fix(tui): improve Terminal.app compatibility with CJK input

- Detect Terminal.app via $TERM_PROGRAM and lower Ink render FPS from
  30 to 15 to reduce ANSI redraw pressure during CJK IME composition
- Use Intl.Segmenter for grapheme-aware backspace handling (correctly
  deletes CJK characters, emoji with ZWJ, surrogate pairs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
fanghanjun
2026-04-13 01:20:29 -07:00
parent 432283a256
commit 2b6ddf277e
3 changed files with 16 additions and 2 deletions
+7 -1
View File
@@ -12,6 +12,7 @@ import { formatModeLabel, getTuiCopy, normalizeStageLabel, resolveTuiLocale, typ
import { formatTuiResult } from "./output.js";
import { loadProjectSession, persistProjectSession } from "./session-store.js";
import { detectModelInfo, detectProjectLanguage, ensureProject, interactiveLlmSetup } from "./setup.js";
import { isAppleTerminal } from "./theme.js";
import { createInteractionTools } from "./tools.js";
import { animateStartup } from "./effects.js";
@@ -172,7 +173,12 @@ export async function launchTui(
tools,
chatStreamBridge,
}),
{ exitOnCtrlC: true },
{
exitOnCtrlC: true,
// Terminal.app crashes with rapid ANSI redraws + CJK IME input;
// halve the frame rate to reduce rendering pressure.
...(isAppleTerminal && { maxFps: 15 }),
},
);
await app.waitUntilExit();
} finally {
+6 -1
View File
@@ -230,7 +230,12 @@ export function InkTuiApp(props: InkTuiAppProps): React.JSX.Element {
}
if (key.backspace || key.delete) {
setInputValue((current) => current.slice(0, -1));
setInputValue((current) => {
// Use Intl.Segmenter for grapheme-aware backspace (handles CJK, emoji, etc.)
const segments = [...new Intl.Segmenter().segment(current)];
segments.pop();
return segments.map((s) => s.segment).join("");
});
setSelectedSlashIndex(0);
return;
}
+3
View File
@@ -2,6 +2,9 @@ import { execSync } from "node:child_process";
export type TerminalTheme = "dark" | "light";
/** Whether the terminal is macOS Terminal.app (limited ANSI support). */
export const isAppleTerminal = process.env.TERM_PROGRAM === "Apple_Terminal";
/**
* Detect terminal background color.
*