Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions packages/tui/src/component/dialog-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions packages/tui/src/component/session-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions packages/tui/src/config/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -236,7 +236,7 @@ export type Resolved = Omit<Info, "attention" | "cursor" | "keybinds" | "leader"

export function resolve(input: Info, options: { terminalSuspend: boolean }): Resolved {
const keybinds: TuiKeybind.KeybindOverrides = { ...input.keybinds }
if (input.terminal?.enabled) {
if (input.session?.terminal) {
if (input.keybinds?.["terminal.toggle"] === undefined && input.keybinds?.["theme.switch"] === undefined) {
keybinds["terminal.toggle"] = "<leader>t"
keybinds["theme.switch"] = "none"
Expand Down
2 changes: 1 addition & 1 deletion packages/tui/src/context/session-terminals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const { use: useSessionTerminals, provider: SessionTerminalsProvider } =
for (const type of ["persistent-pty.added", "persistent-pty.removed"] as const) {
onCleanup(
event.on(type, (evt) => {
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),
)
Expand Down
2 changes: 1 addition & 1 deletion packages/tui/src/routes/session/composer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export function Composer(props: ComposerProps) {
</box>
<SubagentsTab sessionID={props.sessionID} />
<ShellTab sessionID={props.sessionID} />
<Show when={config.terminal?.enabled}>
<Show when={config.session.terminal}>
<TerminalsTab sessionID={props.sessionID} visibleTerminalID={props.visibleTerminalID} />
</Show>
<box flexDirection="row" gap={2} paddingLeft={1} flexShrink={0}>
Expand Down
2 changes: 1 addition & 1 deletion packages/tui/src/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 10 additions & 5 deletions packages/tui/test/config-v2.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: "<leader>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: "<leader>t" }])
expect(enabled.keybinds.get("theme.switch")).toEqual([])

const customized = resolve(
{ terminal: { enabled: true }, keybinds: { "theme.switch": "<leader>t", "terminal.toggle": "<leader>p" } },
{ session: { terminal: true }, keybinds: { "theme.switch": "<leader>t", "terminal.toggle": "<leader>p" } },
{ terminalSuspend: true },
)
expect(customized.keybinds.get("theme.switch")).toMatchObject([{ key: "<leader>t" }])
Expand Down
Loading