diff options
| author | haoyuren <13851610112@163.com> | 2026-07-28 06:29:04 +0800 |
|---|---|---|
| committer | haoyuren <13851610112@163.com> | 2026-07-28 06:29:04 +0800 |
| commit | d35a6a0ef366f86aeba28c06f40e44cd29cf1f07 (patch) | |
| tree | 1f9e08527da6720722dde971636273d859af89d6 /src/renderer | |
| parent | 1ab00142191f4e3b94ba36d2a33914697864c892 (diff) | |
Codex/agent-neutral setup, terminal IME fix, hide own overlay cursor
- Write AGENTS.md (the cross-tool agent-instructions standard) at the
project root alongside .claude/CLAUDE.md, generated from one guide;
includes an Agent Setup section with the project-specific Codex MCP
registration command. AGENTS.md is excluded from Overleaf sync.
- MCP server resolves the project directory from its own script location
when cwd doesn't contain the state file, so clients that launch MCP
servers from a global config (Codex) work without special setup
- Terminal: swallow the duplicate compositionend commit when switching
input sources mid-composition (xterm finalizes on the keydown and then
again on compositionend, sending pinyin text twice)
- Don't render our own colored overlay cursor — the native caret already
marks our position; overlay cursors are collaborators-only (web parity)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'src/renderer')
| -rw-r--r-- | src/renderer/src/components/Terminal.tsx | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/src/renderer/src/components/Terminal.tsx b/src/renderer/src/components/Terminal.tsx index ea54343..26b0270 100644 --- a/src/renderer/src/components/Terminal.tsx +++ b/src/renderer/src/components/Terminal.tsx @@ -66,6 +66,37 @@ function TerminalInstance({ id, cwd, cmd, args, visible }: { xterm.loadAddon(fitAddon) xterm.open(termRef.current) + // ── IME double-input workaround ───────────────────────────── + // + // Switching the input source mid-composition (e.g. CapsLock with a + // Chinese IME, committing the raw pinyin as ASCII) makes xterm send the + // text twice: its CompositionHelper.keydown() finalizes and sends + // synchronously, then the browser's compositionend fires and the helper + // sends the same textarea content again. Intercept in the capture phase + // on the container (runs before xterm's textarea listeners): when a + // non-IME keydown just force-finalized a composition, swallow the + // compositionend that follows so the text is only sent once. + const container = termRef.current + let imeComposing = false + let keydownFinalizedAt = 0 + const onCompStart = () => { imeComposing = true } + const onKeyDown = (e: KeyboardEvent) => { + if (imeComposing && e.keyCode !== 229 && e.keyCode !== 16 && e.keyCode !== 17 && e.keyCode !== 18) { + // xterm's CompositionHelper will finalize + send synchronously now + keydownFinalizedAt = Date.now() + imeComposing = false + } + } + const onCompEnd = (e: Event) => { + imeComposing = false + if (Date.now() - keydownFinalizedAt < 100) { + e.stopPropagation() // duplicate commit of already-sent text + } + } + container.addEventListener('compositionstart', onCompStart, true) + container.addEventListener('keydown', onKeyDown, true) + container.addEventListener('compositionend', onCompEnd, true) + setTimeout(() => fitAddon.fit(), 100) xtermRef.current = xterm @@ -101,6 +132,9 @@ function TerminalInstance({ id, cwd, cmd, args, visible }: { return () => { initializedRef.current = false resizeObserver.disconnect() + container.removeEventListener('compositionstart', onCompStart, true) + container.removeEventListener('keydown', onKeyDown, true) + container.removeEventListener('compositionend', onCompEnd, true) unsubData() unsubExit() window.api.ptyKill(id) |
