fix(tui): always position cursor for IME

# Conflicts:
#	packages/coding-agent/CHANGELOG.md
This commit is contained in:
Mario Zechner 2026-01-17 11:38:46 +01:00
parent cd43b8a9ca
commit 673916f63c
7 changed files with 87 additions and 11 deletions

View file

@ -71,6 +71,7 @@ export interface Settings {
doubleEscapeAction?: "fork" | "tree"; // Action for double-escape with empty editor (default: "tree")
thinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels
editorPaddingX?: number; // Horizontal padding for input editor (default: 0)
showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME
}
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
@ -482,6 +483,15 @@ export class SettingsManager {
this.save();
}
getShowHardwareCursor(): boolean {
return this.settings.showHardwareCursor ?? process.env.PI_HARDWARE_CURSOR === "1";
}
setShowHardwareCursor(enabled: boolean): void {
this.globalSettings.showHardwareCursor = enabled;
this.save();
}
getEditorPaddingX(): number {
return this.settings.editorPaddingX ?? 0;
}

View file

@ -36,6 +36,7 @@ export interface SettingsConfig {
hideThinkingBlock: boolean;
collapseChangelog: boolean;
doubleEscapeAction: "fork" | "tree";
showHardwareCursor: boolean;
editorPaddingX: number;
}
@ -53,6 +54,7 @@ export interface SettingsCallbacks {
onHideThinkingBlockChange: (hidden: boolean) => void;
onCollapseChangelogChange: (collapsed: boolean) => void;
onDoubleEscapeActionChange: (action: "fork" | "tree") => void;
onShowHardwareCursorChange: (enabled: boolean) => void;
onEditorPaddingXChange: (padding: number) => void;
onCancel: () => void;
}
@ -269,9 +271,19 @@ export class SettingsSelectorComponent extends Container {
values: ["true", "false"],
});
// Editor padding toggle (insert after skill-commands)
// Hardware cursor toggle (insert after skill-commands)
const skillCommandsIndex = items.findIndex((item) => item.id === "skill-commands");
items.splice(skillCommandsIndex + 1, 0, {
id: "show-hardware-cursor",
label: "Show hardware cursor",
description: "Show the terminal cursor while still positioning it for IME support",
currentValue: config.showHardwareCursor ? "true" : "false",
values: ["true", "false"],
});
// Editor padding toggle (insert after show-hardware-cursor)
const hardwareCursorIndex = items.findIndex((item) => item.id === "show-hardware-cursor");
items.splice(hardwareCursorIndex + 1, 0, {
id: "editor-padding",
label: "Editor padding",
description: "Horizontal padding for input editor (0-3)",
@ -318,6 +330,9 @@ export class SettingsSelectorComponent extends Container {
case "double-escape-action":
callbacks.onDoubleEscapeActionChange(newValue as "fork" | "tree");
break;
case "show-hardware-cursor":
callbacks.onShowHardwareCursorChange(newValue === "true");
break;
case "editor-padding":
callbacks.onEditorPaddingXChange(parseInt(newValue, 10));
break;

View file

@ -235,7 +235,7 @@ export class InteractiveMode {
) {
this.session = session;
this.version = VERSION;
this.ui = new TUI(new ProcessTerminal());
this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
this.chatContainer = new Container();
this.pendingMessagesContainer = new Container();
this.statusContainer = new Container();
@ -689,6 +689,18 @@ export class InteractiveMode {
shutdown: () => {
this.shutdownRequested = true;
},
getContextUsage: () => this.session.getContextUsage(),
compact: (options) => {
void (async () => {
try {
const result = await this.session.compact(options?.customInstructions);
options?.onComplete?.(result);
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
options?.onError?.(err);
}
})();
},
},
// ExtensionCommandContextActions - for ctx.* in command handlers
{
@ -813,6 +825,18 @@ export class InteractiveMode {
shutdown: () => {
this.shutdownRequested = true;
},
getContextUsage: () => this.session.getContextUsage(),
compact: (options) => {
void (async () => {
try {
const result = await this.session.compact(options?.customInstructions);
options?.onComplete?.(result);
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
options?.onError?.(err);
}
})();
},
});
// Set up the extension shortcut handler on the default editor
@ -2521,6 +2545,7 @@ export class InteractiveMode {
hideThinkingBlock: this.hideThinkingBlock,
collapseChangelog: this.settingsManager.getCollapseChangelog(),
doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
editorPaddingX: this.settingsManager.getEditorPaddingX(),
},
{
@ -2589,6 +2614,10 @@ export class InteractiveMode {
onDoubleEscapeActionChange: (action) => {
this.settingsManager.setDoubleEscapeAction(action);
},
onShowHardwareCursorChange: (enabled) => {
this.settingsManager.setShowHardwareCursor(enabled);
this.ui.setShowHardwareCursor(enabled);
},
onEditorPaddingXChange: (padding) => {
this.settingsManager.setEditorPaddingX(padding);
this.defaultEditor.setPaddingX(padding);