Skip to content

Commit bf080dc

Browse files
MarkShawn2020claude
andcommitted
perf(terminal): 优化 xterm.js 集成,修复闪烁和宽度问题
- 新增 Unicode11 addon 支持 CJK 字符正确宽度计算 - 新增 WebGL addon 实现 GPU 加速渲染 - Tab 切换改用 invisible 替代 hidden 避免布局重算 - PTY resize 前使用双 rAF 确保 DOM ready - 添加尺寸有效性检查 (cols >= 10, rows >= 2) - 调整 terminal 黑色为更浅色以保持可见性 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 40fe615 commit bf080dc

5 files changed

Lines changed: 64 additions & 16 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
"@tauri-apps/plugin-global-shortcut": "~2.3.1",
3434
"@tauri-apps/plugin-opener": "^2",
3535
"@xterm/addon-fit": "^0.11.0",
36+
"@xterm/addon-unicode11": "^0.9.0",
3637
"@xterm/addon-web-links": "^0.12.0",
38+
"@xterm/addon-webgl": "^0.19.0",
3739
"@xterm/xterm": "^6.0.0",
3840
"allotment": "^1.20.5",
3941
"class-variance-authority": "^0.7.1",

pnpm-lock.yaml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/PanelGrid/SessionPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export const SessionPanel = memo(function SessionPanel({
221221
key={session.id}
222222
value={session.id}
223223
forceMount
224-
className="absolute inset-0 m-0 data-[state=inactive]:hidden"
224+
className="absolute inset-0 m-0 data-[state=inactive]:invisible data-[state=inactive]:pointer-events-none"
225225
>
226226
<TerminalPane
227227
ptyId={session.ptyId}

src/components/Terminal/TerminalPane.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -154,24 +154,35 @@ export function TerminalPane({
154154

155155
if (!mountState.isMounted) return;
156156

157-
await invoke("pty_resize", {
158-
id: sessionId,
159-
cols: term.cols,
160-
rows: term.rows,
161-
}).catch(() => {});
162-
163-
// Focus if autoFocus is true when PTY becomes ready
164-
if (autoFocusRef.current) {
165-
// Use double rAF to ensure DOM is fully painted before focus
157+
// Ensure terminal is properly sized before sending resize to PTY
158+
// This must happen AFTER DOM is ready, so use double rAF
159+
await new Promise<void>((resolve) => {
166160
requestAnimationFrame(() => {
167-
fitAddon.fit();
168161
requestAnimationFrame(() => {
169-
// Don't steal focus from input/textarea
170-
const active = document.activeElement;
171-
if (active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA') return;
172-
term.focus();
162+
fitAddon.fit();
163+
resolve();
173164
});
174165
});
166+
});
167+
168+
// Only send resize if dimensions are valid (container must be visible)
169+
const cols = term.cols;
170+
const rows = term.rows;
171+
if (cols >= 10 && rows >= 2) {
172+
await invoke("pty_resize", {
173+
id: sessionId,
174+
cols,
175+
rows,
176+
}).catch(() => {});
177+
}
178+
179+
// Focus if autoFocus is true when PTY becomes ready
180+
if (autoFocusRef.current) {
181+
// Don't steal focus from input/textarea
182+
const active = document.activeElement;
183+
if (active?.tagName !== 'INPUT' && active?.tagName !== 'TEXTAREA') {
184+
term.focus();
185+
}
175186
}
176187
onReadyRef.current?.();
177188
} catch (err) {

src/components/Terminal/terminalPool.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Terminal } from "@xterm/xterm";
22
import { FitAddon } from "@xterm/addon-fit";
33
import { WebLinksAddon } from "@xterm/addon-web-links";
4+
import { WebglAddon } from "@xterm/addon-webgl";
5+
import { Unicode11Addon } from "@xterm/addon-unicode11";
46
import { openUrl } from "@tauri-apps/plugin-opener";
57

68
interface PooledTerminal {
@@ -50,7 +52,7 @@ const TERMINAL_THEME = {
5052
cursor: "#CC785C",
5153
cursorAccent: "#1a1a1a",
5254
selectionBackground: "#CC785C40",
53-
black: "#1a1a1a",
55+
black: "#4a4a4a", // Lighter than background to remain visible
5456
red: "#e06c75",
5557
green: "#98c379",
5658
yellow: "#d19a66",
@@ -97,6 +99,11 @@ export function getOrCreateTerminal(sessionId: string): PooledTerminal {
9799
openUrl(uri).catch(console.error);
98100
}));
99101

102+
// Unicode11 addon for proper CJK character width calculation
103+
const unicode11Addon = new Unicode11Addon();
104+
term.loadAddon(unicode11Addon);
105+
term.unicode.activeVersion = "11";
106+
100107
// Create a detached container for the terminal
101108
const container = document.createElement("div");
102109
container.style.width = "100%";
@@ -105,6 +112,18 @@ export function getOrCreateTerminal(sessionId: string): PooledTerminal {
105112
// Open terminal in the detached container
106113
term.open(container);
107114

115+
// WebGL addon for GPU-accelerated rendering (prevents flicker on high-throughput)
116+
// Must be loaded AFTER term.open() - falls back to DOM renderer if WebGL unavailable
117+
try {
118+
const webglAddon = new WebglAddon();
119+
webglAddon.onContextLoss(() => {
120+
webglAddon.dispose();
121+
});
122+
term.loadAddon(webglAddon);
123+
} catch {
124+
// WebGL not available, use default DOM renderer
125+
}
126+
108127
// Note: macOS keyboard shortcuts (Cmd+Arrow, Cmd+Backspace, Option+Arrow, Option+Backspace)
109128
// are handled in TerminalPane.tsx using invoke("pty_write") for direct PTY communication
110129

0 commit comments

Comments
 (0)