Skip to main content
Alpha

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:

The ai copy-registry collection
ItemRole
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 verbatim text part.
  • blockToText(part: RenderPart): string — the exact inverse projection (canonical markdown / plain-text fallback). parseBlocks is 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.

Presentation RenderPart (chat-render) — one variant per rendered block
NameTypeDescription
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:

  • markdown renders sanitized streaming markdown — safe to feed partial, mid-stream assistant text as chunks arrive, without waiting for a complete message.
  • prompt-input is 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.

Reference