fix(tui): restored compact editor border by default

- Restored the compact two-row `Editor` layout by default and gated the dedicated IME-safe bottom border behind `setImeSafeCursorLayout()`.
- Added `tui.imeSafeCursor` as an opt-in appearance setting and applied it to initial and replacement editors.
- Added regression coverage for compact default rendering while retaining terminal-local IME preedit protection.
- Updated the TUI changelog for the opt-in compatibility layout.
This commit is contained in:
can1357
2026-07-15 13:16:07 +02:00
parent 28d4e71b0a
commit f3aad14ee9
5 changed files with 38 additions and 9 deletions
@@ -982,6 +982,17 @@ export const SETTINGS_SCHEMA = {
},
},
"tui.imeSafeCursor": {
type: "boolean",
default: false,
ui: {
tab: "appearance",
group: "Display",
label: "IME-Safe Prompt Layout",
description: "Move the prompt's bottom border to a separate row so macOS IME preedit cannot displace it",
},
},
// ────────────────────────────────────────────────────────────────────────
// Model
// ────────────────────────────────────────────────────────────────────────
@@ -680,6 +680,7 @@ export class InteractiveMode implements InteractiveModeContext {
this.errorBannerContainer = new AnchoredLiveContainer();
this.modelCycleContainer = new AnchoredLiveContainer();
this.editor = new CustomEditor(getEditorTheme());
this.editor.setImeSafeCursorLayout(settings.get("tui.imeSafeCursor"));
this.editor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
this.editor.setAutocompleteMaxVisible(settings.get("autocompleteMaxVisible"));
this.editor.onAutocompleteCancel = () => {
@@ -3669,6 +3670,7 @@ export class InteractiveMode implements InteractiveModeContext {
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined,
): void {
const previousEditor = this.editor;
nextEditor.setImeSafeCursorLayout(this.settings.get("tui.imeSafeCursor"));
const previousText = previousEditor.getText();
const nextEditor = factory
? factory(this.ui, getEditorTheme(), this.keybindings)
+1 -4
View File
@@ -6,10 +6,7 @@
- Display LaTeX now renders `\underbrace`/`\overbrace` (and the bracket/paren variants) as drawn horizontal braces with centered labels, and stacks `\overset`/`\underset`/`\stackrel` annotations above/below the base instead of falling back to flat inline glyphs.
- Display LaTeX renders multi-letter script words (`N_{turns}`) as raised/lowered blocks instead of ragged per-character Unicode sub/superscript glyphs; single letters and digits keep the compact Unicode forms.
### Fixed
- Fixed macOS IME marked text displacing the prompt editor border when entering fullwidth CJK punctuation ([#5563](https://github.com/can1357/oh-my-pi/issues/5563)).
- Added opt-in `Editor.setImeSafeCursorLayout()` protection for macOS IME preedit while retaining the compact bordered layout by default ([#5563](https://github.com/can1357/oh-my-pi/issues/5563)).
## [16.5.2] - 2026-07-14
+13 -5
View File
@@ -382,6 +382,7 @@ export class Editor implements Component, Focusable {
#theme: EditorTheme;
#useTerminalCursor = false;
#imeSafeCursorLayout = false;
/** When set, replaces the normal cursor glyph at end-of-text with this ANSI-styled string. */
cursorOverride: string | undefined;
@@ -539,6 +540,11 @@ export class Editor implements Component, Focusable {
this.#useTerminalCursor = useTerminalCursor;
}
/** Render a dedicated bottom border so terminal-local IME preedit cannot shift editor chrome. */
setImeSafeCursorLayout(enabled: boolean): void {
this.#imeSafeCursorLayout = enabled;
}
getUseTerminalCursor(): boolean {
return this.#useTerminalCursor;
}
@@ -923,7 +929,13 @@ export class Editor implements Component, Focusable {
if (marker) {
const before = displayText.slice(0, layoutLine.cursorPos);
const after = displayText.slice(layoutLine.cursorPos);
if (after.length === 0 && inlineHint) {
if (this.#imeSafeCursorLayout && after.length === 0 && borderVisible) {
// Terminal frontends render IME marked text locally before committed bytes
// reach the application. Keep the end-of-input cursor row empty to its
// right so that insertion cannot shift box chrome onto the next row.
displayText = before + marker;
imeSafeCursorTail = true;
} else if (after.length === 0 && inlineHint) {
const availWidth = Math.max(0, lineContentWidth - displayWidth);
const hintText = hintStyle(truncateToWidth(inlineHint, availWidth));
displayText = before + marker + hintText;
@@ -932,10 +944,6 @@ export class Editor implements Component, Focusable {
displayText = this.#renderTerminalCursorMarker(before, marker, lineContentWidth);
} else {
displayText = before + marker + after;
// Terminal frontends render IME marked text locally before committed bytes
// reach the application. Keep the end-of-input cursor row empty to its
// right so that insertion cannot shift box chrome onto the next row.
imeSafeCursorTail = after.length === 0 && borderVisible;
}
}
} else if (hasCursor && !this.#useTerminalCursor) {
+11
View File
@@ -853,11 +853,22 @@ describe("Editor component", () => {
}
});
it("keeps the terminal-cursor editor compact by default", () => {
const editor = new Editor(defaultEditorTheme);
editor.focused = true;
editor.setUseTerminalCursor(true);
editor.setText("ast");
const lines = editor.render(20).map(line => stripVTControlCharacters(line.replaceAll(CURSOR_MARKER, "")));
expect(lines).toEqual(["+------------------+", "+- ast -+"]);
});
it("keeps terminal-local IME preedit from displacing the editor border (#5563)", async () => {
const width = 20;
const terminal = new VirtualTerminal(width, 6, 1_000);
const tui = new TUI(terminal, true);
const editor = new Editor(defaultEditorTheme);
editor.setImeSafeCursorLayout(true);
tui.addChild(editor);
tui.setFocus(editor);