Inline rich tool rendering in chat + flush-right resizable file sidebar (desktop) #154
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#154
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
The desktop tool panel from #145/#147 (chips open a right sidebar) turned out not to match how the chat is read: tool detail belongs inline in the conversation flow. We want the pre-#147 inline shape back — but keeping the rich renderers from #146 — plus a new, properly-docked sidebar reserved for file content.
Spec
1. Inline rich tool rendering in chat (desktop ≥1024px)
Restore the collapsed-chip shape removed in
f7d3fbd, upgraded with the rich views:web/src/components/ToolPanel.tsx:386-435): diff, write, command, plus the newreadkind (§3), falling back to raw input/output<pre>when no view exists. Extract these renderers into a shared component so inline, sidebar, and mobile sheet use one implementation.white-space: pre-wrap; overflow-wrap: anywhere) in diff/command/read/write views and the raw fallback<pre>blocks. Apply the same wrapping in the sidebar and the mobile sheet.2. Flush-right resizable file sidebar (desktop)
A right sidebar exclusively for file views (
read,diff,write):.chat-pagecarries.page(max-width: 760px + panel; margin: 0 auto,web/src/base.css:157-161, 3801-3805) so the whole row centers. Change: when the sidebar is open, the page row goes full-width, the sidebar sits at the true right edge (no auto-margin, no right padding gap), and the chat column stays capped at 760px, centered in the remaining space to the left.sheetGesture.ts/drawerGesture.tsare dismiss gestures, not resize). Clamp width to min 320px, max 60vw. Persist width inlocalStorage(single global key); default remainsclamp(320px, 30vw, 480px).3. Server: new
readToolView kindToolViewRead = "read"to the view union ininternal/provider/provider.go(fields:Path,Text— the file excerpt from the tool result,DetailTruncateLimitapplies).internal/provider/claudecode/chat_types.gofor theReadtool. Codex has no distinct read tool (reads happen via shell → alreadycommand), so no codex mapping.web/src/api.ts:546-549with{ kind: 'read'; path: string; text: string }and render it like a file view (path header + content, no +/- coloring).internal/chat/redact.go) must cover the new kind'sPath/Text(verify it already masks allViewfields generically).4. Mobile (<1024px): unchanged flow
readview, and line wrapping.Out of scope
Touch points
web/src/routes/RunChat.tsx(ToolChip/ToolGroupView at 1423-1468, panelSel wiring at 163-165, 706-753)web/src/components/ToolPanel.tsx(extract renderers; desktop becomes file-detail-only)web/src/base.css(chat-page cap at 157-161 / 3801-3805,.tool-panel-sideat 3641-3648, wrap rules)internal/provider/provider.go,internal/provider/claudecode/chat_types.go,internal/chat/redact.go,web/src/api.tsLanding audit — PASS
Verification signal relied on: Forgejo Actions
ci / native— success (5m3s, run 168). Not re-run. That job covers the whole surface of this diff: SPA lint/format/vitest/build, thengo build/go test(tagui) andgolangci-lint. No flake/dependency files are touched, so nothing here sits behind the conditional nix gate.Merge state: no conflict —
afk/154is a single commit on the currentorigin/maintip,merge-treeclean.Conventions: title is Conventional Commits (
feat:); body carries a workingCloses #154; head branch is the run'safk/154claim.Diff vs. issue #154: all four sections implemented as specified.
seq(Set<number>, decision-12 keying), so it survives an SSE refetch and resets with the stream inresetStream. Renderers extracted toToolViews.tsx, and the liveness invariant holds:ToolViewBodyreads everything throughprops.message, never a captured snapshot.isFileView(diff/write/read) and on desktop; the guard effect clears a group or non-file selection after a breakpoint crossing, so the sidebar can't be asked to render what it doesn't show.readview kind —ToolViewReadin the union, mapped for claude-code only;readExcerptis conservative (passes text through unchanged unless every non-empty line carries acat -nprefix),TruncateDetailapplied, view cleared on an errored read. Redaction masksPath/Textgenerically, pinned by a new test.desktopPanel()..chat-tool/.chat-tool-groupare plain block frames (border/radius only, nodisplay:flex), so moving them to the wrapperdivwhile.tool-summary/.tool-group-summary(the flex rows) stay on the button preserves the collapsed look.Non-blocking nits (not fixed here)
internal/provider/claudecode/chat_types.go— insidecase "Read",path := str(in["file_path"])shadows the importedpathpackage. Compiles and lints clean (the case body never uses the package), but it is a trap for the next editor of that clause.web/src/components/AppShell.tsx:144— the drawer-gesture comment still lists.tool-bodyamong the horizontal scrollers..tool-bodyis nowpre-wrapwith nooverflow-x, so it no longer is one. Behaviour is unaffected (the check reads computedoverflow-x, it is not a hard-coded selector list), but the comment is stale.ToolChipsetsaria-expanded={props.desktop && props.expanded}, which rendersaria-expanded="false"on mobile chips, where the chip opens a sheet rather than expanding. Previously the attribute was absent there. Harmless, slightly inaccurate semantics.Verdict: PASS. Merging.