From 21980a4448d013e6f88381984b8e42bbc8dfd643 Mon Sep 17 00:00:00 2001 From: James Long Date: Wed, 26 Aug 2026 09:46:33 -0400 Subject: [PATCH] fix(tui): restore terminal settings and move pane toggle to sessions (#45271) --- packages/tui/src/component/dialog-config.tsx | 25 ++++++++++++++++--- packages/tui/src/component/session-frame.tsx | 6 ++--- packages/tui/src/config/index.tsx | 4 +-- .../tui/src/context/session-terminals.tsx | 2 +- .../tui/src/routes/session/composer/index.tsx | 2 +- packages/tui/src/routes/session/index.tsx | 2 +- packages/tui/test/config-v2.test.tsx | 15 +++++++---- 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/tui/src/component/dialog-config.tsx b/packages/tui/src/component/dialog-config.tsx index dba2401b8e2..ab83654ce1c 100644 --- a/packages/tui/src/component/dialog-config.tsx +++ b/packages/tui/src/component/dialog-config.tsx @@ -51,6 +51,15 @@ export const settings: Setting[] = [ values: ["hide", "auto"], keywords: ["side panel"], }, + { + title: "Terminal", + category: "Session", + path: ["session", "terminal"], + default: false, + values: [false, true], + labels: ["off", "on"], + keywords: ["pty", "shell", "terminal pane"], + }, { title: "Scrollbar", category: "Session", @@ -274,13 +283,21 @@ export const settings: Setting[] = [ keywords: ["sound volume", "audio volume"], }, { - title: "Enabled", + title: "Window title", category: "Terminal", - path: ["terminal", "enabled"], - default: false, + path: ["terminal", "title"], + default: true, values: [false, true], labels: ["off", "on"], - keywords: ["pty", "shell", "terminal pane"], + keywords: ["terminal title", "tab title"], + }, + { + title: "Copy behavior", + category: "Terminal", + path: ["terminal", "copy"], + default: process.platform === "win32" ? "manual" : "select", + values: ["manual", "select"], + keywords: ["selection", "clipboard"], }, { title: "Developer tools", diff --git a/packages/tui/src/component/session-frame.tsx b/packages/tui/src/component/session-frame.tsx index 1d59682adfa..5c92c6ed418 100644 --- a/packages/tui/src/component/session-frame.tsx +++ b/packages/tui/src/component/session-frame.tsx @@ -25,13 +25,13 @@ export function SessionFrame(props: { sessionID: string; verticalTabsWidth: numb const [restoreTerminalFocus, setRestoreTerminalFocus] = createSignal(false) let focusTerminal: (() => void) | undefined createResource( - () => (config.data.terminal?.enabled ? props.sessionID : undefined), + () => (config.data.session.terminal ? props.sessionID : undefined), (sessionID) => sessions.refresh(sessionID).catch(() => undefined), ) const session = () => sessions.get(props.sessionID) const terminals = () => session()?.terminals ?? [] const selectedTerminal = () => { - if (!config.data.terminal?.enabled) return + if (!config.data.session.terminal) return const value = session() if (value?.hidden) return return value?.terminals.find((terminal) => terminal.id === value.selectedTerminalID) ?? value?.terminals.at(-1) @@ -74,7 +74,7 @@ export function SessionFrame(props: { sessionID: string; verticalTabsWidth: numb prompt.current?.focus() }) Keymap.createLayer(() => ({ - enabled: () => config.data.terminal?.enabled === true, + enabled: () => config.data.session.terminal === true, commands: [ { id: "pane.focus.left", diff --git a/packages/tui/src/config/index.tsx b/packages/tui/src/config/index.tsx index 94e8e7e8057..ec7edfd557d 100644 --- a/packages/tui/src/config/index.tsx +++ b/packages/tui/src/config/index.tsx @@ -102,7 +102,6 @@ export const Info = Schema.Struct({ ).annotate({ description: "Diff presentation settings" }), terminal: Schema.optional( Schema.Struct({ - enabled: Schema.optional(Schema.Boolean).annotate({ description: "Enable persistent terminal panes" }), title: Schema.optional(Schema.Boolean).annotate({ description: "Update the terminal window title" }), copy: Schema.optional(Schema.Literals(["manual", "select"])).annotate({ description: "Copy text manually or immediately after selecting it", @@ -127,6 +126,7 @@ export const Info = Schema.Struct({ sidebar: Schema.optional(Schema.Literals(["auto", "hide"])).annotate({ description: "Session sidebar visibility; 'auto' shows it when space permits", }), + terminal: Schema.optional(Schema.Boolean).annotate({ description: "Enable persistent session terminal panes" }), scrollbar: Schema.optional(Schema.Boolean).annotate({ description: "Show the session transcript scrollbar" }), thinking: Schema.optional(Schema.Literals(["show", "hide"])).annotate({ description: "Show or hide model reasoning by default", @@ -236,7 +236,7 @@ export type Resolved = Omit { - if (!config.terminal?.enabled || !store.sessions[evt.data.sessionID]) return + if (!config.session.terminal || !store.sessions[evt.data.sessionID]) return void refresh(evt.data.sessionID).catch((error) => console.error("Failed to refresh persistent terminal panes", error), ) diff --git a/packages/tui/src/routes/session/composer/index.tsx b/packages/tui/src/routes/session/composer/index.tsx index 4ca00551fff..c9970536acf 100644 --- a/packages/tui/src/routes/session/composer/index.tsx +++ b/packages/tui/src/routes/session/composer/index.tsx @@ -152,7 +152,7 @@ export function Composer(props: ComposerProps) { - + diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 9a0d0a78152..ea9539720bd 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -895,7 +895,7 @@ export function Session(props: { dialog.clear() }, }, - ...(config.terminal?.enabled + ...(config.session.terminal ? [ { title: props.visibleTerminalID ? "Hide terminal pane" : "Show terminal pane", diff --git a/packages/tui/test/config-v2.test.tsx b/packages/tui/test/config-v2.test.tsx index 1fd403d9fac..19685310418 100644 --- a/packages/tui/test/config-v2.test.tsx +++ b/packages/tui/test/config-v2.test.tsx @@ -74,24 +74,29 @@ test("validates terminal copy behavior", () => { expect(decodeInfo({ terminal: { copy: "manual" } })).toEqual({ terminal: { copy: "manual" } }) expect(decodeInfo({ terminal: { copy: "select" } })).toEqual({ terminal: { copy: "select" } }) expect(() => decodeInfo({ terminal: { copy: "always" } })).toThrow() + + const setting = settings.find((setting) => setting.path.join(".") === "terminal.copy") + expect(setting?.values).toEqual(["manual", "select"]) + expect(setting?.default).toBe(process.platform === "win32" ? "manual" : "select") }) test("keeps persistent terminals disabled until explicitly enabled", () => { const disabled = resolve({}, { terminalSuspend: true }) - expect(disabled.terminal?.enabled ?? false).toBe(false) + expect(disabled.session.terminal ?? false).toBe(false) expect(disabled.keybinds.get("theme.switch")).toMatchObject([{ key: "t" }]) expect(disabled.keybinds.get("terminal.toggle")).toEqual([]) - expect(settings.find((setting) => setting.path.join(".") === "terminal.enabled")?.default).toBe(false) + expect(settings.find((setting) => setting.path.join(".") === "session.terminal")?.default).toBe(false) expect(settings.filter((setting) => setting.category === "Terminal").map((setting) => setting.title)).toEqual([ - "Enabled", + "Window title", + "Copy behavior", ]) - const enabled = resolve({ terminal: { enabled: true } }, { terminalSuspend: true }) + const enabled = resolve({ session: { terminal: true } }, { terminalSuspend: true }) expect(enabled.keybinds.get("terminal.toggle")).toMatchObject([{ key: "t" }]) expect(enabled.keybinds.get("theme.switch")).toEqual([]) const customized = resolve( - { terminal: { enabled: true }, keybinds: { "theme.switch": "t", "terminal.toggle": "p" } }, + { session: { terminal: true }, keybinds: { "theme.switch": "t", "terminal.toggle": "p" } }, { terminalSuspend: true }, ) expect(customized.keybinds.get("theme.switch")).toMatchObject([{ key: "t" }])