summaryrefslogtreecommitdiff
path: root/src/renderer
diff options
context:
space:
mode:
authorhaoyuren <13851610112@163.com>2026-07-28 06:29:04 +0800
committerhaoyuren <13851610112@163.com>2026-07-28 06:29:04 +0800
commitd35a6a0ef366f86aeba28c06f40e44cd29cf1f07 (patch)
tree1f9e08527da6720722dde971636273d859af89d6 /src/renderer
parent1ab00142191f4e3b94ba36d2a33914697864c892 (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.tsx34
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)