diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 10e340d7f8f..cc8d227519b 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -136,7 +136,9 @@ export function Session() { }) const dimensions = useTerminalDimensions() - const [sidebar, setSidebar] = kv.signal<"auto" | "hide">("sidebar", "hide") + const validSidebar = (v: unknown): v is "auto" | "hide" => v === "auto" || v === "hide" + const sidebarDefault = validSidebar(sync.data.config.tui?.sidebar) ? sync.data.config.tui.sidebar : "hide" + const [sidebar, setSidebar] = kv.signal<"auto" | "hide">("sidebar", sidebarDefault) const [sidebarOpen, setSidebarOpen] = createSignal(false) const [conceal, setConceal] = createSignal(true) const [showThinking, setShowThinking] = kv.signal("thinking_visibility", true) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index ead3a0149b4..24ae05bf358 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -770,6 +770,10 @@ export namespace Config { .enum(["auto", "stacked"]) .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), + sidebar: z + .enum(["auto", "show", "hide"]) + .optional() + .describe("Sidebar visibility: 'auto' shows on wide terminals, 'show' always visible, 'hide' always hidden"), }) export const Server = z diff --git a/packages/opencode/test/cli/tui/sidebar-config.test.ts b/packages/opencode/test/cli/tui/sidebar-config.test.ts new file mode 100644 index 00000000000..86dcd421ccb --- /dev/null +++ b/packages/opencode/test/cli/tui/sidebar-config.test.ts @@ -0,0 +1,49 @@ +import { test, expect, describe } from "bun:test" + +// Tests for TUI sidebar config behavior +// The sidebar uses: sync.data.config.tui?.sidebar ?? kv.get("sidebar", "auto") +// Priority: config.tui.sidebar > KV store > "auto" +// This allows per-workspace config to override global KV preferences + +describe("TUI Sidebar Config Behavior", () => { + test("config takes precedence over KV store for per-workspace settings", () => { + // Workspace config set to "hide" overrides global KV preference of "show" + const configValue = "hide" + const kvValue = "show" + const defaultValue = "auto" + + const result = configValue ?? kvValue ?? defaultValue + + expect(result).toBe("hide") + }) + + test("KV store is used when no workspace config is set", () => { + // No workspace config, use global KV preference + const configValue = undefined + const kvValue = "show" + const defaultValue = "auto" + + const result = configValue ?? kvValue ?? defaultValue + + expect(result).toBe("show") + }) + + test("default is used when neither KV nor config is set", () => { + const kvValue = undefined + const configValue = undefined + const defaultValue = "auto" + + const result = kvValue ?? configValue ?? defaultValue + + expect(result).toBe("auto") + }) + + test("all valid sidebar values are accepted", () => { + const validValues = ["auto", "show", "hide"] as const + + for (const value of validValues) { + const result = value + expect(["auto", "show", "hide"]).toContain(result) + } + }) +}) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 087eb0c628c..e7caed8605c 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -870,6 +870,80 @@ test("merges legacy tools with existing permission config", async () => { }) }) +// TUI sidebar config tests +// The TUI uses: sync.data.config.tui?.sidebar ?? kv.get("sidebar", "auto") +// Priority: Config > KV store > "auto" +// Config provides per-workspace default, KV provides global fallback + +test("sidebar is undefined when not set in config", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: {}, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.sidebar).toBeUndefined() + }, + }) +}) + +test("loads TUI sidebar config with custom value", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + sidebar: "hide", + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.sidebar).toBe("hide") + }, + }) +}) + +test("TUI sidebar config accepts all valid values", async () => { + for (const value of ["auto", "show", "hide"] as const) { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + tui: { + sidebar: value, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.tui?.sidebar).toBe(value) + }, + }) + } +}) + test("permission config preserves key order", async () => { await using tmp = await tmpdir({ init: async (dir) => { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 9cb7222aa5f..70de34eb792 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1563,6 +1563,10 @@ export type Config = { * Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column */ diff_style?: "auto" | "stacked" + /** + * Default sidebar visibility: 'auto' shows on wide terminals, 'show' always visible, 'hide' always hidden + */ + sidebar?: "auto" | "show" | "hide" } server?: ServerConfig /** diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 1bc7bd5c2c8..6d11469a34e 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -9112,6 +9112,11 @@ "description": "Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column", "type": "string", "enum": ["auto", "stacked"] + }, + "sidebar": { + "description": "Control sidebar visibility: 'show' always shows sidebar, 'hide' always hides it, 'auto' adapts to terminal width", + "type": "string", + "enum": ["show", "hide", "auto"] } } }, diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index a5931b6fcf5..f8addee5630 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -160,16 +160,24 @@ You can configure TUI-specific settings through the `tui` option. "scroll_acceleration": { "enabled": true }, - "diff_style": "auto" + "diff_style": "auto", + "sidebar": "hide" } } ``` +Set `sidebar` to control the initial state of the session sidebar: + +- `"auto"` (default) shows the sidebar when there is enough horizontal space. +- `"show"` always shows the sidebar on launch. +- `"hide"` keeps the sidebar hidden until toggled. + Available options: - `scroll_acceleration.enabled` - Enable macOS-style scroll acceleration. **Takes precedence over `scroll_speed`.** - `scroll_speed` - Custom scroll speed multiplier (default: `1`, minimum: `1`). Ignored if `scroll_acceleration.enabled` is `true`. - `diff_style` - Control diff rendering. `"auto"` adapts to terminal width, `"stacked"` always shows single column. +- `sidebar` - Initial sidebar visibility. `"auto"` shows on wide terminals, `"show"` always visible, `"hide"` always hidden. [Learn more about using the TUI here](/docs/tui). diff --git a/packages/web/src/content/docs/tui.mdx b/packages/web/src/content/docs/tui.mdx index a92da6a0226..7fd5d8d0363 100644 --- a/packages/web/src/content/docs/tui.mdx +++ b/packages/web/src/content/docs/tui.mdx @@ -350,7 +350,8 @@ You can customize TUI behavior through your OpenCode config file. "scroll_speed": 3, "scroll_acceleration": { "enabled": true - } + }, + "sidebar": "hide" } } ``` @@ -359,6 +360,7 @@ You can customize TUI behavior through your OpenCode config file. - `scroll_acceleration` - Enable macOS-style scroll acceleration for smooth, natural scrolling. When enabled, scroll speed increases with rapid scrolling gestures and stays precise for slower movements. **This setting takes precedence over `scroll_speed` and overrides it when enabled.** - `scroll_speed` - Controls how fast the TUI scrolls when using scroll commands (minimum: `1`). Defaults to `1` on Unix and `3` on Windows. **Note: This is ignored if `scroll_acceleration.enabled` is set to `true`.** +- `sidebar` - Sets the initial sidebar behavior (default: `"auto"` uses the responsive layout, `"show"` forces it visible, `"hide"` keeps it hidden until toggled) ---