feat(web): unify tool-row expand interaction with IN/OUT card and trajectory Inspect

Every expandable tool row shares one interaction (whole-row toggle,
icon-to-chevron hover preview) and one expanded body: an IN/OUT
gutter-labeled card with per-section 150px scroll caps and sticky labels.
toolRowModel derives result output and the error first line, terminalFailed
surfaces a failing exit as the collapsed row's red dot, a hover Inspect
pill jumps to the call's trajectory record through a one-shot store
handoff, and the chat view keeps its scroll offset across view switches.
This commit is contained in:
Yif
2026-07-30 21:21:29 +08:00
parent 86fa88a012
commit 78e4d36214
38 changed files with 1241 additions and 268 deletions
@@ -203,7 +203,7 @@ describe('run_code sub-calls through the real chat machinery', () => {
expect(nest.querySelector('[data-tool="cordis_unmount"]')?.textContent)
.toContain('Unmount temporary Plugindyn-2')
fireEvent.click(mounted!.querySelector('button[aria-expanded]')!)
fireEvent.click(mounted!.querySelector('[data-expandable]')!)
expect(mounted!.querySelector('pre.shiki')?.textContent).toBe(code)
})
@@ -211,8 +211,8 @@ describe('run_code sub-calls through the real chat machinery', () => {
const parent = 'call-64'
const b = await bench(snapshotWith([codeResult(10, parent)], new Map()))
const view = mountApp(b.slots)
// The code row is expandable via its leading control (body = the program).
const toggle = view.container.querySelector('[data-variant="code"] button[aria-expanded]')
// The code row is expandable via the whole summary row (body = the program).
const toggle = view.container.querySelector('[data-variant="code"] [data-expandable]')
expect(toggle).not.toBeNull()
fireEvent.click(toggle!)
// Shiki splits the program into token spans inside one <pre class="shiki">: