AI chat
A chat app with a history sidebar (new-chat button, rename, delete, and a footer profile card that opens a settings modal), streaming replies, and forkable reply threads in a resizable side panel. Installs as a folder with ToolCallTrace and Questionnaire as dependencies. Works with a real API: chats, threads, replies, and search all load from API routes, so point them at your own backend.
Installation
npx dafink-ui add ai-chatAlso installs: Sidebar, Scroll-fade, Message, Typewriter, Attachment, Button, Input, Textarea, Command-input, Command-palette, Badge, Tooltip, Spinner, Collapsible, Timeline, Modal, Charts, Function-plotter, Table, Date-picker, Slider, Formula, Code-block, Audioplayer, Skeleton, Questionnaire, Tool-call-trace, Resizable, Workflow-builder, Avatar, Select, Switch
Requires: react-markdown, remark-gfm, remark-math
Demo
Prompt
This block's demo replies are canned — no model is called. To make a real LLM produce content this block already knows how to render, add the following to your own system prompt.
Most of your reply can just be normal GitHub-flavored markdown — the chat already
renders it: **bold**/*italic*, [links](https://example.com), `inline code`, fenced code blocks
(```js ... ```, any language), bullet/numbered lists, and pipe tables all render with this
app's real UI components, not raw text. None of that needs special instructions; it's what
you already produce for any markdown-aware client.
Two things are NOT standard markdown and must be explained in the system prompt if you want
the model to use them:
1. Math formulas — inline with single dollars, block with double dollars, standard LaTeX:
Inline: The area is $\pi r^2$.
Block: $$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$
2. Charts — markdown has no chart syntax, so these stay a fenced code block convention.
When a visual would genuinely help (a trend, a comparison, a breakdown) — for a quick
fact, prefer prose or a markdown table instead — use a fenced block in one of these
languages, containing JSON in the exact shape shown. If the request needs real data you
don't already have, use a search or data tool first rather than inventing numbers:
- "lineplot" — a trend over a continuous axis (time, distance, ...)
{ "xKey": string, "series": [{ "key": string, "label": string }], "data": [{ ...one object per point, with an xKey field and a field per series key... }] }
- "barchart" — comparing discrete categories. Same shape as lineplot, plus an optional "stacked": true to stack series instead of grouping them
{ "xKey": string, "series": [...], "data": [...], "stacked"?: boolean }
- "areachart" — volume or a cumulative total over time. Same shape as barchart (including optional "stacked")
{ "xKey": string, "series": [...], "data": [...], "stacked"?: boolean }
- "radarchart" — comparing several categories against one another, optionally across more than one series. Same shape as lineplot
{ "xKey": string, "series": [...], "data": [...] }
- "donutchart" — a simple part-to-whole breakdown. No xKey/series — just labeled slices
{ "data": [{ "label": string, "value": number }] }
- "math" — plotting one or more mathematical functions of x as a graph (not a formula — use $$...$$ for that)
{ "equations": string[] } // e.g. ["x^2 - 2", "sin(x)"] — plain math expressions in x, evaluated with mathjs syntax, no "y =" prefix
3. Media — a generated image or audio clip also has no markdown syntax, so these are fenced
too. Only emit one once you actually have a real URL for it (e.g. after a generation
tool call resolves) — while that's in flight, the consuming app is expected to show its
own loading state, not you.
- "image" — a generated or fetched image
{ "url": string, "alt": string }
- "audio" — a generated or fetched audio clip
{ "src": string, "title"?: string }
4. Workflows — a node-based diagram of a process (an automation, a pipeline, a decision
flow). Renders as a static, horizontal box-and-wire diagram — no toolbar, no run/simulate
control, not draggable or editable — it's for showing the shape of a process, not
executing or changing it. Each node has zero or more named input ports and zero or more
named output ports; a node with no inputs is an entry point, one with no outputs is
terminal, and a node with 2+ outputs is a branch (give each output a distinct id/label,
e.g. "pass"/"fail"):
- "workflow"
{
"nodes": [{ "id": string, "label": string, "description"?: string,
"inputs": [{ "id": string, "label": string }],
"outputs": [{ "id": string, "label": string }],
"position": { "x": number, "y": number } }],
"edges": [{ "id": string, "source": string, "target": string,
"sourceHandle": string, "targetHandle": string }]
}
Space nodes left-to-right in the order they run (roughly 260px per step is readable);
"sourceHandle"/"targetHandle" must match an id from that node's own outputs/inputs.
Example:
User: How has the median home price changed over the last few years?
Assistant: Here's the trend in median home prices over the last several years:
```lineplot
{"xKey":"year","series":[{"key":"price","label":"Median home price"}],"data":[{"year":2019,"price":313000},{"year":2020,"price":329000},{"year":2021,"price":375000},{"year":2022,"price":395000},{"year":2023,"price":410000},{"year":2024,"price":426000}]}
```
The chart/media fence conventions and the $...$/$$...$$ math delimiters aren't something
the model already knows to use unprompted — this block must be included in the system
prompt on every request, not just the first turn. Everything else above it is included
for completeness, not because it needs teaching.