# Transcript and tool calls (/cloud/surfaces/transcript)



<HostMatrix
  primitive="{
  label: &#x22;@nyte-ai/ui/collapsible, /button, /dialog&#x22;,
  href: &#x22;/cloud/headless/collapsible&#x22;,
}"
  desktop="{
  label: &#x22;conversation/turn-view.tsx&#x22;,
  note: &#x22;TurnView › UserMessageView | ReasoningBlock | WorkGroupView › ToolCallView; Prose › CodeBlock&#x22;,
}"
  terminal="{ label: &#x22;transcript.ts&#x22;, note: &#x22;TranscriptView (later pass)&#x22; }"
/>

## Parts [#parts]

| Component         | File                               | Pure?            | Purpose                                                                                                                                                                      |
| ----------------- | ---------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TurnView`        | `conversation/turn-view.tsx`       | mostly           | "One transcript item. User prompts are the only ordinary contained message region. Assistant prose stays flat, while reasoning, tools, and system history use compact rows." |
| `UserMessageView` | `turn-view.tsx`                    | yes              | The prompt bubble with edit.                                                                                                                                                 |
| `ReasoningBlock`  | `turn-view.tsx`                    | yes              | Collapsible thinking; open while streaming, `aria-busy`.                                                                                                                     |
| `WorkGroupView`   | `conversation/tool-group.tsx`      | yes              | "Intermediate narration, reasoning, and tool calls form one work episode."                                                                                                   |
| `ToolCallView`    | `conversation/tool-call.tsx`       | yes              | "One tool call: a quiet verb line that expands into its evidence."                                                                                                           |
| `Prose`           | `conversation/prose.tsx`           | link clicks only | Streamdown Markdown with Nyte components.                                                                                                                                    |
| `CodeBlock`       | `conversation/code-block.tsx`      | yes              | Escaped text immediately, worker highlight when near the viewport.                                                                                                           |
| `UserMessageText` | `conversation/message-content.tsx` | yes              | Message text with inline reference chips.                                                                                                                                    |
| `ImagePreview`    | `conversation/image-preview.tsx`   | yes              | Thumbnail opening a Dialog at natural size.                                                                                                                                  |

## `TurnView` [#turnview]

| Prop            | Type                                       | Description                                            |
| --------------- | ------------------------------------------ | ------------------------------------------------------ |
| `turn`          | `Turn`                                     | `turn` / `checkpoint` / `summary` / `config` / `note`. |
| `liveTools`     | `ReadonlyMap<string, LiveToolProgress>`    |                                                        |
| `live`          | `LiveSnapshot`                             | "Only the trailing group carries the run."             |
| `cwd`           | `string \| undefined`                      | Path shortening.                                       |
| `onEditUser`    | `(part, content, choice) => Promise<void>` | Enables editing.                                       |
| `branchModel`   | `BranchModelPicker`                        | Catalog and model state for the edit footer picker.    |
| `onOpenChanges` | `(target: TurnChangesTarget) => void`      | Review / open file.                                    |
| `running`       | `boolean`                                  |                                                        |

"Parts keep core's stable identity so settled and streaming content exchange in place." Two
constraints from the file: "A completion's continuation turn draws nothing until its response
lands; an empty completed turn must not leave a blank row behind." and "an earlier \[group] is
settled history, and the run's indicator belongs below the prose that follows it."

## User message editing [#user-message-editing]

`UserMessageView` is a button until edited. Click or **F2** begins an edit, unless a text
selection exists (so selecting to copy never opens the editor). In edit mode it mounts a
`ComposerEditor` and a `ModelPicker` in the footer:

| Key           | Effect |
| ------------- | ------ |
| Escape        | Cancel |
| Enter, ⌘Enter | Save   |

The Send button is labelled "Send edited message". Sticky behavior is data-attribute driven
(`data-sticky-user-message`, `data-sticky-active`, `data-sticky-visible`) and mutated by
`syncStickyUserMessage` in the thread screen, which "avoids cloning the prompt or rerendering the
transcript on every scroll tick."

## Tool calls: the locked law [#tool-calls-the-locked-law]

From `tool-call.tsx`:

> Its locked law carries over: no status icons on tool calls. The running state is the shimmer,
> failure is the red line, and the chevron is a control, not a status. Neither shimmer nor red
> line survives a screen reader or a colour-blind eye, so the same two states also ship as
> visually hidden text.

| Prop       | Type                        | Default     |
| ---------- | --------------------------- | ----------- |
| `part`     | `ToolTurnPart`              |             |
| `progress` | `ToolProgress \| undefined` |             |
| `cwd`      | `string \| undefined`       |             |
| `density`  | `"compact" \| "detailed"`   | `"compact"` |

Anatomy: `Collapsible.Root` with `render` callbacks ("Open state lives in Base UI, not here: a
controlled `open` would re-render this component (and re-parse the patch) on every toggle."),
`data-tool-status={state}`, `srOnly` "Running" / "Failed", a `Collapsible.Panel role="region" aria-label="{verb} output"` with `data-nyte-scrollport`, and for edits a `keepMounted` panel with
`DiffView variant="inline"`. Closed output shows an `OutputPreview` that is a second trigger
labelled "Show full command output".

## Work groups [#work-groups]

`WorkGroupView` props: `parts`, `live`, `liveTools`, `cwd`, `durationMs`, `running`, `density`.
Summary verbs: Edited / Ran / Read / Listed / Worked / "Work failed"; while running: Working /
Thinking / "Preparing next move" / "This is taking a bit longer" after 15s. Compact mode keeps
the episode in a clipped window that follows new output; scrolling away pauses following for 10s;
click opens. Uses `Spinner`, `AnimatedNumber`, `srOnly`, `focus.ring`.

## Prose and code [#prose-and-code]

`Prose({ markdown, streaming })` runs Streamdown with `animated={false}`, `controls={false}`,
`skipHtml`, link safety off, and maps `pre` → `CodeBlock`, `table` → a scrollable wrapper
(`data-nyte-scrollport`, `data-prose-table`), `img` → alt text, `a` → `preventDefault` +
`nyte.host.openExternal`.

`CodeBlock({ code, lang })`: "Fenced code paints as escaped plain text immediately. Known grammars
upgrade in a bundled worker, keeping grammars and syntax work off the UI thread." Highlight is
requested once within 300px of the viewport (`IntersectionObserver`); LRU of 64; worker replies
are validated with typebox at the boundary. Copy button labels flip "Copy code" → "Code copied".

## Images [#images]

`ImagePreview({ src, name, compact })`: a 80px (64px compact) thumbnail that is a
`Dialog.Trigger`; the popup shows the image at natural size "capped only by the window", on
`t.imageBg` so transparency does not pick up the workspace tint. Close is labelled "Close image
preview".
