From 8783cd9b595481f3c5c2438d7fa10e45d95305ed Mon Sep 17 00:00:00 2001 From: thzxx Date: Wed, 8 Apr 2026 16:29:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B7=A5=E4=BD=9C=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E5=AE=9A=E4=BD=8D=E3=80=81=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E4=B9=B1=E7=A0=81=E3=80=81=E5=BF=AB=E6=8D=B7=E9=94=AE=E3=80=81?= =?UTF-8?q?=E5=B8=AE=E5=8A=A9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 工作空间面板 top 改用 CSS 变量 --nav-height,JS 动态计算 header+model-bar 实际高度 - Windows 下 spawn 子进程前加 chcp 65001 切换 UTF-8 代码页,修复中文乱码 - 补全 Ctrl+K(知识库)、Ctrl+M(记忆面板)快捷键,防止 Ctrl+M 默认行为冲突 - 帮助弹框内容全面更新,修正工作空间为常驻显示,补充 Ctrl+N、布局说明等 - Token 统计图标从 emoji 🪙 改为内联 SVG,兼容所有 Windows 字体 --- src/main/tool-handlers.ts | 15 +++++++++++---- src/main/workspace.ts | 32 ++++++++++++++++++++++++-------- src/renderer/index.html | 19 +++++++++---------- src/renderer/main.ts | 29 +++++++++++++++++++++++++++++ src/renderer/styles/style.css | 9 ++++++++- 5 files changed, 81 insertions(+), 23 deletions(-) diff --git a/src/main/tool-handlers.ts b/src/main/tool-handlers.ts index 5720906..0b27a34 100644 --- a/src/main/tool-handlers.ts +++ b/src/main/tool-handlers.ts @@ -298,12 +298,19 @@ export async function handleRunCommand(params: { command: string; cwd?: string; return new Promise((resolve) => { const isWin = process.platform === 'win32'; const shell = isWin ? 'cmd.exe' : 'bash'; - const shellArgs = isWin ? ['/c', params.command] : ['-c', params.command]; + const actualCmd = isWin ? `chcp 65001 >nul && ${params.command}` : params.command; + const shellArgs = isWin ? ['/c', actualCmd] : ['-c', actualCmd]; const startTime = Date.now(); _toolProc = spawn(shell, shellArgs, { cwd, - env: { ...process.env, ...(isWin ? {} : { TERM: 'xterm-256color' }) }, + env: { + ...process.env, + ...(isWin ? {} : { TERM: 'xterm-256color' }), + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US.UTF-8', + PYTHONIOENCODING: 'utf-8' + }, stdio: ['pipe', 'pipe', 'pipe'] }); @@ -312,13 +319,13 @@ export async function handleRunCommand(params: { command: string; cwd?: string; // 实时推送到工作空间终端 _toolProc.stdout?.on('data', (data: Buffer) => { - const str = data.toString(); + const str = data.toString('utf-8'); stdout += str; mainWindow?.webContents.send('cmd:output', { command: params.command, type: 'stdout', data: str }); }); _toolProc.stderr?.on('data', (data: Buffer) => { - const str = data.toString(); + const str = data.toString('utf-8'); stderr += str; mainWindow?.webContents.send('cmd:output', { command: params.command, type: 'stderr', data: str }); }); diff --git a/src/main/workspace.ts b/src/main/workspace.ts index c8c11eb..5b432ab 100644 --- a/src/main/workspace.ts +++ b/src/main/workspace.ts @@ -88,21 +88,30 @@ export function startProcess( try { const isWin = process.platform === 'win32'; const shell = isWin ? 'cmd.exe' : 'bash'; - const shellArgs = isWin ? ['/c', command] : ['-c', command]; + // Windows cmd 默认代码页为 GBK/CP936,中文输出会乱码 + // chcp 65001 切换到 UTF-8 代码页,>nul 抑制 chcp 自身的输出 + const actualCmd = isWin ? `chcp 65001 >nul && ${command}` : command; + const shellArgs = isWin ? ['/c', actualCmd] : ['-c', actualCmd]; const proc = spawn(shell, shellArgs, { cwd: workDir, - env: { ...process.env, ...(isWin ? {} : { TERM: 'xterm-256color' }) }, + env: { + ...process.env, + ...(isWin ? {} : { TERM: 'xterm-256color' }), + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US.UTF-8', + PYTHONIOENCODING: 'utf-8' + }, stdio: ['pipe', 'pipe', 'pipe'] }); activeProcesses.set(id, proc); proc.stdout.on('data', (data: Buffer) => { - onOutput('stdout', data.toString()); + onOutput('stdout', data.toString('utf-8')); }); proc.stderr.on('data', (data: Buffer) => { - onOutput('stderr', data.toString()); + onOutput('stderr', data.toString('utf-8')); }); proc.on('close', (code) => { @@ -246,17 +255,24 @@ export function execWorkspaceCommand( try { const isWin = process.platform === 'win32'; const shell = isWin ? 'cmd.exe' : 'bash'; - const shellArgs = isWin ? ['/c', command] : ['-c', command]; + const actualCmd = isWin ? `chcp 65001 >nul && ${command}` : command; + const shellArgs = isWin ? ['/c', actualCmd] : ['-c', actualCmd]; const proc = spawn(shell, shellArgs, { cwd: workDir, - env: { ...process.env, ...(isWin ? {} : { TERM: 'xterm-256color' }) }, + env: { + ...process.env, + ...(isWin ? {} : { TERM: 'xterm-256color' }), + LANG: 'en_US.UTF-8', + LC_ALL: 'en_US.UTF-8', + PYTHONIOENCODING: 'utf-8' + }, stdio: ['pipe', 'pipe', 'pipe'] }); activeProcesses.set(cmdId, proc); proc.stdout.on('data', (data: Buffer) => { - const str = data.toString(); + const str = data.toString('utf-8'); if (stdoutBuf.length < MAX_OUTPUT) { stdoutBuf += str; if (stdoutBuf.length > MAX_OUTPUT) { @@ -271,7 +287,7 @@ export function execWorkspaceCommand( }); proc.stderr.on('data', (data: Buffer) => { - const str = data.toString(); + const str = data.toString('utf-8'); if (stderrBuf.length < MAX_OUTPUT) { stderrBuf += str; if (stderrBuf.length > MAX_OUTPUT) { diff --git a/src/renderer/index.html b/src/renderer/index.html index 65185e2..7c320d8 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -34,7 +34,7 @@ - 🪙 0 + T 0
@@ -387,15 +387,14 @@
diff --git a/src/renderer/main.ts b/src/renderer/main.ts index a3c57cc..795157b 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -212,6 +212,8 @@ async function init(): Promise { setupDesktopIntegration(); bindGlobalEvents(); + updateNavHeight(); + window.addEventListener('resize', updateNavHeight); await checkConnection(); await loadModels(); @@ -283,10 +285,25 @@ async function init(): Promise { initSettingsModal(); initHistoryModal(); setupDesktopIntegration(); + updateNavHeight(); + window.addEventListener('resize', updateNavHeight); checkConnection().then(loadModels); } } +/** + * 动态计算顶部导航高度(header + model-bar), + * 设为 CSS 变量 --nav-height,供 workspace-panel 引用 + */ +function updateNavHeight(): void { + const header = document.querySelector('.app-header'); + const modelBar = document.querySelector('.model-bar'); + const headerH = header?.getBoundingClientRect().height ?? 0; + const modelBarH = modelBar?.getBoundingClientRect().height ?? 0; + const total = Math.ceil(headerH + modelBarH); + document.documentElement.style.setProperty('--nav-height', `${total}px`); +} + function bindGlobalEvents(): void { document.querySelector('#btnNewChat')?.addEventListener('click', startNewSession); @@ -298,6 +315,18 @@ function bindGlobalEvents(): void { (document.querySelector('#helpModal') as HTMLElement).style.display = 'none'; (document.querySelector('#kbModal') as HTMLElement).style.display = 'none'; } + + // Ctrl+K — 知识库管理 + if (e.ctrlKey && e.key === 'k') { + e.preventDefault(); + document.querySelector('#btnKB')?.dispatchEvent(new Event('click')); + } + + // Ctrl+M — 记忆面板 + if (e.ctrlKey && e.key === 'm') { + e.preventDefault(); + document.querySelector('#btnMemory')?.dispatchEvent(new Event('click')); + } }); window.addEventListener('error', (e) => { diff --git a/src/renderer/styles/style.css b/src/renderer/styles/style.css index 4a9368c..20f69ef 100644 --- a/src/renderer/styles/style.css +++ b/src/renderer/styles/style.css @@ -383,6 +383,13 @@ html, body { user-select: none; } +.token-icon { + width: 14px; + height: 14px; + flex-shrink: 0; + opacity: 0.8; +} + /* ═══ 模型选择栏 ═══ */ .model-bar { display: flex; @@ -2667,7 +2674,7 @@ html, body { .workspace-panel { position: fixed; - top: 92px; /* header(44px) + border(1px) + model-bar(46px) + border(1px) */ + top: var(--nav-height, 92px); /* JS 动态计算 header + model-bar 实际高度 */ right: 0; bottom: 0; width: 480px;