From 0f56a119383e403ef01cc84989a39f6eab3afe3f Mon Sep 17 00:00:00 2001 From: kahlos Date: Fri, 14 Aug 2026 11:42:43 +0100 Subject: [PATCH] feat(tui): add session.tool_details option to collapse write/edit/patch content Adds a session.tool_details setting (default "show") that collapses the write, edit, and patch tool blocks to a clickable header by default, hiding the file content and diffs from the transcript. Clicking the header expands or collapses the content again. - Config: new session.tool_details field ("show" | "collapse") in the V2 TUI config schema - Settings: new "Tool details" entry in the /settings dialog - Rendering: Write, Edit, and ApplyPatch wrap their content in a Show block and toggle expansion through BlockTool's onClick, matching the existing Shell and GenericTool collapse pattern - Tests: schema validation and settings default coverage --- packages/tui/src/component/dialog-config.tsx | 9 ++ packages/tui/src/config/index.tsx | 3 + packages/tui/src/routes/session/index.tsx | 150 +++++++++++-------- packages/tui/test/config-v2.test.tsx | 10 ++ 4 files changed, 108 insertions(+), 64 deletions(-) diff --git a/packages/tui/src/component/dialog-config.tsx b/packages/tui/src/component/dialog-config.tsx index ced25dd183c5..54778bf549e1 100644 --- a/packages/tui/src/component/dialog-config.tsx +++ b/packages/tui/src/component/dialog-config.tsx @@ -68,6 +68,15 @@ export const settings: Setting[] = [ values: ["hide", "show"], keywords: ["reasoning", "chain of thought"], }, + { + title: "Tool details", + category: "Session", + path: ["session", "tool_details"], + default: "show", + values: ["show", "collapse"], + labels: ["show", "collapse"], + keywords: ["tool output", "write", "edit", "patch", "diff", "code changes"], + }, { title: "Markdown", category: "Session", diff --git a/packages/tui/src/config/index.tsx b/packages/tui/src/config/index.tsx index 2cd49a3f642a..7f0eafb731c7 100644 --- a/packages/tui/src/config/index.tsx +++ b/packages/tui/src/config/index.tsx @@ -128,6 +128,9 @@ export const Info = Schema.Struct({ thinking: Schema.optional(Schema.Literals(["show", "hide"])).annotate({ description: "Show or hide model reasoning by default", }), + tool_details: Schema.optional(Schema.Literals(["show", "collapse"])).annotate({ + description: "Show write, edit, and patch content inline or collapse it behind a clickable header", + }), grouping: Schema.optional(Schema.Literals(["auto", "none"])).annotate({ description: "Group related transcript items automatically or render each item separately", }), diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index e90da6f0830d..d8686d814406 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -126,6 +126,7 @@ const context = createContext<{ sessionID: string thinkingMode: () => ThinkingMode showThinking: () => boolean + toolDetailsMode: () => "show" | "collapse" markdownMode: () => "source" | "rendered" groupExploration: () => boolean diffWrapMode: () => "word" | "none" @@ -223,6 +224,7 @@ export function Session() { const [sidebarOpen, setSidebarOpen] = createSignal(false) const thinkingMode = createMemo(() => config.session?.thinking ?? "hide") const showThinking = createMemo(() => true) + const toolDetailsMode = createMemo<"show" | "collapse">(() => config.session?.tool_details ?? "show") const showScrollbar = createMemo(() => config.session?.scrollbar ?? false) const markdownMode = createMemo(() => config.session?.markdown ?? "rendered") const diffWrapMode = createMemo(() => config.diffs?.wrap ?? "word") @@ -1138,6 +1140,7 @@ export function Session() { sessionID: route.sessionID, thinkingMode, showThinking, + toolDetailsMode, markdownMode, groupExploration, diffWrapMode, @@ -3060,9 +3063,12 @@ function Shell(props: ToolProps) { } function Write(props: ToolProps) { + const ctx = use() const theme = useTheme() const { currentSyntax: syntax } = useThemes() const pathFormatter = usePathFormatter() + const [expanded, setExpanded] = createSignal(false) + const collapsed = createMemo(() => ctx.toolDetailsMode() === "collapse") const code = createMemo(() => { return stringValue(props.input.content) ?? "" }) @@ -3073,17 +3079,20 @@ function Write(props: ToolProps) { setExpanded((value) => !value) : undefined} > - - - - + + + + + + @@ -3325,6 +3334,8 @@ function Edit(props: ToolProps) { const theme = useTheme() const { currentSyntax: syntax } = useThemes() const pathFormatter = usePathFormatter() + const [expanded, setExpanded] = createSignal(false) + const collapsed = createMemo(() => ctx.toolDetailsMode() === "collapse") const view = createMemo(() => { const diffView = ctx.config.diffs?.view @@ -3341,30 +3352,36 @@ function Edit(props: ToolProps) { {(item) => ( - - - - - + setExpanded((value) => !value) : undefined} + > + + + + + + )} @@ -3389,6 +3406,8 @@ function ApplyPatch(props: ToolProps) { const theme = useTheme() const { currentSyntax: syntax } = useThemes() const pathFormatter = usePathFormatter() + const [expanded, setExpanded] = createSignal(false) + const collapsed = createMemo(() => ctx.toolDetailsMode() === "collapse") const files = createMemo(() => parseApplyPatchFiles(props.metadata.files)) const targets = createMemo(() => { const patch = stringValue(props.input.patchText) @@ -3423,37 +3442,40 @@ function ApplyPatch(props: ToolProps) { value: pathFormatter.format(file.relativePath), }} part={props.part} + onClick={collapsed() ? () => setExpanded((value) => !value) : undefined} > - - -{file.deletions} line{file.deletions !== 1 ? "s" : ""} - - } - > - - - + + + -{file.deletions} line{file.deletions !== 1 ? "s" : ""} + + } + > + + + + )} diff --git a/packages/tui/test/config-v2.test.tsx b/packages/tui/test/config-v2.test.tsx index 23017e09f43b..3c6542eab5ad 100644 --- a/packages/tui/test/config-v2.test.tsx +++ b/packages/tui/test/config-v2.test.tsx @@ -31,6 +31,16 @@ test("validates the session tabs setting", () => { expect(() => decode({ session: { new_location: "current" } })).toThrow() }) +test("validates the session tool details setting", () => { + const config = resolve( + decodeInfo({ session: { tool_details: "collapse" } }), + { terminalSuspend: true }, + ) + expect(config.session.tool_details).toBe("collapse") + expect(() => decodeInfo({ session: { tool_details: "hidden" } })).toThrow() + expect(settings.find((setting) => setting.path.join(".") === "session.tool_details")?.default).toBe("show") +}) + test("resolves nested config and keybind defaults", () => { const config = resolve( {