feat: inline rich tool rendering in chat + flush-right resizable file sidebar #155

Merged
dominik.polakovics merged 1 commit from afk/154 into main 2026-07-13 11:39:04 +02:00

Implements issue #154: desktop tool detail moves back inline into the conversation flow (keeping the #146 rich renderers), and the desktop panel is reborn as a flush-right, drag-resizable sidebar reserved for file views.

What changed

1. Inline rich tool rendering (desktop >=1024px)

  • Chips and group rows toggle expansion in place; groups reveal their member chips, each independently expandable. Expansion state is controlled (Set<seq>, decision-12 keying) so it survives SSE refetches, and resets with the stream.
  • The expanded body renders the shared rich views (diff / write / command / read, raw <pre> fallback) inside a .tool-inline-body capped at 45vh with internal scroll.
  • No horizontal scrolling anywhere: .tool-body, diff line spans, and path headers soft-wrap (pre-wrap + overflow-wrap: anywhere) across inline, sidebar, and sheet.

2. Flush-right resizable file sidebar (desktop)

  • File chips (view kind read/diff/write) carry an "open in sidebar" icon button on the collapsed row and next to the path header of the expanded inline view; command/fallback tools get none. The shown chip keeps its selected state.
  • Sidebar is file-detail-only (no list page on desktop; a guard clears group or non-file selections, e.g. after a mobile->desktop breakpoint crossing). Opening another file replaces the content.
  • Docked flush right: with the sidebar open the page row goes full-width (no right padding, no auto-margin), the chat column stays capped at 760px centered in the remaining space.
  • Drag-to-resize via pointer events on a left-edge handle, clamped 320px-60vw, width persisted under lab.tool-panel-width (re-clamped against the viewport at render); default stays clamp(320px, 30vw, 480px). Escape still closes; open state remains an in-memory signal (no history entry).

3. Server: new read ToolView kind

  • ToolViewRead in the provider view union; claudecode maps the Read tool (Path at tool_use time, Text filled from the tool_result with cat -n prefixes and trailing system-reminder stripped, TruncateDetail applied; view cleared on an errored read). No codex mapping (reads there are shell commands).
  • Client union extended; redaction already masks all view fields generically — pinned by a new test.

4. Mobile (<1024px): unchanged

  • Chips/groups keep opening the bottom sheet (list -> detail) exactly as before; the sheet picks up the shared renderers, the read view, and line wrapping.

Renderer extraction

ToolPanel.tsx's view switch moved to web/src/components/ToolViews.tsx (ToolViewBody), preserving the liveness invariant (all reads through props.message, resolved live by seq). Inline, sidebar, and sheet all render through it.

Verification

  • go build ./..., go vet ./..., go test ./... - all green (new tests for the read mapping, excerpt cleaning, truncation, error clearing, redaction).
  • web: tsc --noEmit, vitest 762/762, eslint, prettier --check, production vite build - all green. New tests cover inline expand/collapse, group bodies, refetch survival, affordance gating, sidebar open/replace/close, resize clamping + persistence, breakpoint-crossing guards, and mobile pins (no inline expansion, no affordances).

Closes #154

🤖 Generated with Claude Code

https://claude.ai/code/session_011iVKMijBLcAySTFm3AyWRh

Implements issue #154: desktop tool detail moves back inline into the conversation flow (keeping the #146 rich renderers), and the desktop panel is reborn as a flush-right, drag-resizable sidebar reserved for file views. ## What changed ### 1. Inline rich tool rendering (desktop >=1024px) - Chips and group rows toggle expansion in place; groups reveal their member chips, each independently expandable. Expansion state is controlled (`Set<seq>`, decision-12 keying) so it survives SSE refetches, and resets with the stream. - The expanded body renders the shared rich views (diff / write / command / read, raw `<pre>` fallback) inside a `.tool-inline-body` capped at 45vh with internal scroll. - No horizontal scrolling anywhere: `.tool-body`, diff line spans, and path headers soft-wrap (`pre-wrap` + `overflow-wrap: anywhere`) across inline, sidebar, and sheet. ### 2. Flush-right resizable file sidebar (desktop) - File chips (view kind `read`/`diff`/`write`) carry an "open in sidebar" icon button on the collapsed row and next to the path header of the expanded inline view; command/fallback tools get none. The shown chip keeps its selected state. - Sidebar is file-detail-only (no list page on desktop; a guard clears group or non-file selections, e.g. after a mobile->desktop breakpoint crossing). Opening another file replaces the content. - Docked flush right: with the sidebar open the page row goes full-width (no right padding, no auto-margin), the chat column stays capped at 760px centered in the remaining space. - Drag-to-resize via pointer events on a left-edge handle, clamped 320px-60vw, width persisted under `lab.tool-panel-width` (re-clamped against the viewport at render); default stays `clamp(320px, 30vw, 480px)`. Escape still closes; open state remains an in-memory signal (no history entry). ### 3. Server: new `read` ToolView kind - `ToolViewRead` in the provider view union; claudecode maps the `Read` tool (Path at tool_use time, Text filled from the tool_result with cat -n prefixes and trailing system-reminder stripped, `TruncateDetail` applied; view cleared on an errored read). No codex mapping (reads there are shell commands). - Client union extended; redaction already masks all view fields generically — pinned by a new test. ### 4. Mobile (<1024px): unchanged - Chips/groups keep opening the bottom sheet (list -> detail) exactly as before; the sheet picks up the shared renderers, the read view, and line wrapping. ## Renderer extraction `ToolPanel.tsx`'s view switch moved to `web/src/components/ToolViews.tsx` (`ToolViewBody`), preserving the liveness invariant (all reads through `props.message`, resolved live by seq). Inline, sidebar, and sheet all render through it. ## Verification - `go build ./...`, `go vet ./...`, `go test ./...` - all green (new tests for the read mapping, excerpt cleaning, truncation, error clearing, redaction). - web: `tsc --noEmit`, `vitest` 762/762, `eslint`, `prettier --check`, production `vite build` - all green. New tests cover inline expand/collapse, group bodies, refetch survival, affordance gating, sidebar open/replace/close, resize clamping + persistence, breakpoint-crossing guards, and mobile pins (no inline expansion, no affordances). Closes #154 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011iVKMijBLcAySTFm3AyWRh
feat: inline rich tool rendering in chat + flush-right resizable file sidebar
All checks were successful
ci / native (pull_request) Successful in 5m3s
d267853142
Desktop (>=1024px) tool chips and group summaries toggle rich inline
expansion in place again (the pre-#147 shape, upgraded with the #146
rich views); mobile keeps the bottom sheet flow unchanged.

- Extract the rich view renderers into a shared ToolViewBody so inline,
  sidebar, and mobile sheet render from one implementation; long lines
  soft-wrap in every view and raw <pre> — no horizontal scrolling.
- New server-side `read` ToolView kind (Path + Text): mapped for the
  claudecode Read tool with cat -n prefixes and trailing system-reminder
  stripped, cleared on an errored read; redaction coverage pinned.
- The desktop panel becomes a file-only (read/diff/write) sidebar docked
  flush to the right window edge: file chips carry an "open in sidebar"
  affordance, the page row goes full-width while the chat column stays
  capped at 760px centered to its left, and a left-edge drag handle
  resizes it (clamped 320px-60vw, width persisted in localStorage).

Closes #154

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iVKMijBLcAySTFm3AyWRh
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab!155
No description provided.