Transcript and tool calls
Turns, user messages, reasoning, work groups, tool calls, prose, and code: how a conversation is drawn
| Host | Implementation | Note |
|---|---|---|
| Primitive | @nyte-ai/ui/collapsible, /button, /dialog | |
| Desktop | conversation/turn-view.tsx | TurnView › UserMessageView | ReasoningBlock | WorkGroupView › ToolCallView; Prose › CodeBlock |
| Terminal | transcript.ts | TranscriptView (later pass) |
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
| 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
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
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
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({ 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
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".