Chat UI
The AI chat interface ships as a copy-registry collection in
@netscript/fresh-ui: a composer, a message thread, tool-call cards, a model picker,
and a generative-UI block renderer.
alpha
These are not package subpath imports. The published @netscript/fresh-ui subpath
exports (., ./primitives, ./interactive, ./registry) carry no chat symbols. The
chat surface is a set of files the NetScript CLI copies into your app, where you own
and edit them like any other source file. This is the same copy-source model as the rest
of the Fresh UI — see Fresh UI & design and
Customize Fresh UI.
The ai collection
The collection is described as the "AI / chat surface seams: grounded-agent citations, message thread, composer, model picker, and tool-call disclosure." Its items:
| Item | Role |
|---|---|
prompt-input |
Chat composer: auto-grow textarea plus a toolbar (research / grounding pills, model picker, attach / screenshot / voice, send). |
message |
A chat message: author + time, inline-markup body (bold / code / [n] citations), tool-call and chart / code blocks, follow-up chips, typing indicator. Exports renderInline and TypingIndicator. |
markdown |
Sanitized streaming-markdown component plus its rendering pipeline. |
chat-render |
The generative-UI block parser (a lib item): copies parse-blocks.ts into your app. |
tool-call-card |
Tool-call disclosure card for an agent tool invocation. |
model-selector |
Model picker used by the composer. |
citation-chip |
Grounded-agent citation chip. |
code-block |
Rendered code block. |
chart-block |
Rendered chart block (a target of the generative-UI renderer). |
avatar |
Author avatar. |
command-palette |
Command palette surface. |
search |
Search surface. |
theme-seed |
Theme token seed for the chat surface. |
The generative-UI renderer — chat-render / parse-blocks
The chat-render item copies parse-blocks.ts into your app (@lib/chat/parse-blocks.ts).
It is the parser that turns streamed markdown into typed, renderable blocks — the seam
that lets an agent emit a chart or a table inside its answer.
parseBlocks(input: string): RenderPart[]— markdown → typed parts. It never throws; an unrecognized fenced block falls through to a verbatimtextpart.blockToText(part: RenderPart): string— the exact inverse projection (canonical markdown / plain-text fallback).parseBlocksis a boundary-stable fixed point, which is what gives the transcript reload fidelity.
The presentation RenderPart
The renderer's RenderPart is a rich presentation union — distinct from the
transport RenderPart in @netscript/fresh/ai (which is only
text | tool). Keep the two separate: one describes how a chunk streams, the other
describes how a block is drawn.
| Name | Type | Description |
|---|---|---|
chart |
ChartRenderPart |
A chart block (payload ChartDatum[]). |
donut |
DonutRenderPart |
A donut block (payload DonutDatum[]). |
table |
TableRenderPart |
A table block (TableColumn / TableRow, with TableAlign). |
stats |
StatsRenderPart |
A stats grid (payload StatsEntry[]). |
line |
LineRenderPart |
A line chart (payload LinePoint[]). |
text |
TextRenderPart |
Verbatim text — the fallback for any unrecognized fence. |
The fenced-block grammar
parseBlocks reads a curated set of fenced-code info-strings —
chart, donut, table, stats, line — and accepts either canonical JSON or a
minimal DSL (label: value @tone, plus markdown pipe tables). A RenderTone colors a
value; a fence whose info-string is not in the set is left as a verbatim text part, so
ordinary code blocks pass through untouched.
```chart
[{ "label": "Q1", "value": 42 }, { "label": "Q2", "value": 58 }]
```
```stats
Revenue: 1.2M @positive
Churn: 3% @negative
```
parse-blocks.ts is self-contained — it does not import @netscript/fresh/ai. The two
RenderPart types are intentionally separate (presentation vs transport); reconciling
them is a downstream concern, not something you wire by hand.
Streaming markdown and the composer
Two components carry most of the interactive weight:
markdownrenders sanitized streaming markdown — safe to feed partial, mid-stream assistant text as chunks arrive, without waiting for a complete message.prompt-inputis the composer: an auto-grow textarea that expands with the input, plus a toolbar for research/grounding pills, the model picker, attachment / screenshot / voice affordances, and send.
Because every item is copied into your workspace, restyling the composer, swapping the chart renderer, or removing the citation chip is a normal source edit — there is no package boundary to work around.