A chat with an AI agent beside what it's making, a document, a table or code, in versions: ask for a change and the new version writes itself line by line, see what changed, or bring an older one back. On motion.
Installation
When to use
- An agent that makes something people keep working on: a brief, a report, a table of numbers, a piece of code.
- Products where people ask for changes in words and want to compare versions or go back to one.
When not to use
- Questions with a quick answer and nothing to keep: AI assistant.
- Editing the document by hand: pair the panel with your own editor instead of this view.
Usage
Agent workspace with your own data: every prop has the sample as its default, so pass only what you have.
"use client"
import { AgentWorkspace } from "@/components/dashral/blocks/agent-workspace"
export default function Workspace() {
return (
<AgentWorkspace
title="Release notes"
messages={[]}
versions={[]}
suggestions={["Draft this week's release notes"]}
onSend={async (prompt, run, current) => {
// Stream your model here: run.reply goes into the chat, run.write into a new version.
run.reply("Writing it now.")
for (const line of ["# Release notes", "", `- ${prompt}`]) {
if (run.signal.aborted) return
run.write(line + "\n")
await new Promise((resolve) => setTimeout(resolve, 120))
}
run.label(current ? "Updated" : "First draft")
}}
onRestore={(version) => console.log("restored", version.id)}
/>
)
}API reference
AgentWorkspace
A conversation with an AI agent beside what it's making: a document, a table or code, in versions. Ask for a change and the answer streams into the chat while the new version writes itself into the panel. Pick a version to read it, turn on Changes to see what's added and removed since the one before, and Restore to bring an older one back as the newest. Each answer that made a version links to it. The panel closes to give the chat the room, and on a phone it covers the chat.
| Prop | Type | Default |
|---|---|---|
title | stringThe thing being made, at the top of the panel: "Launch brief". | "Launch brief" |
messages | WorkspaceMessage[]The conversation so far. | sample |
versions | WorkspaceVersion[]Its versions, oldest first. | sample |
suggestions | PromptSuggestion[]Ideas for what to ask next, under the chat while it's quiet. | sample |
onSend | (prompt: string, run: WorkspaceRun, current: WorkspaceVersion | undefined) => Promise<void> | voidYour model answering: stream into | — |
renderContent | (content: Extract<WorkspaceContent, { kind: "custom" }>, state: { writing: boolean }) => React.ReactNodeDraws a | — |
onRestore | (version: WorkspaceVersion) => Promise<void> | voidA version brought back: it comes again as the newest one. | — |
className | string | — |
StreamWords
Text as it streams in: each new word arrives from a soft blur the moment it comes, as TextReveal's blur variant has its words arrive, without waiting for the rest. Words already there stay still. With reduced motion, plain text.
| Prop | Type | Default |
|---|---|---|
text | stringRequired. | — |
WorkspaceView
A version's content: a document, code, a table or your own, with what changed marked when asked.
| Prop | Type | Default |
|---|---|---|
version | WorkspaceVersionRequired. | — |
previous | WorkspaceVersionThe version before it: with | — |
changes | boolean | false |
writing | booleanStill being written: its lines grow in, with a caret at the end. | false |
renderContent | AgentWorkspaceProps["renderContent"]Draws a | — |
type WorkspaceContent
= | { kind: "document"; text: string } | { kind: "code"; language: string; text: string } /** * Anything else, drawn by your `renderContent`: a chart, an image, a page preview, a form. `type` says which; `text` * is what Copy copies and what Changes compares (left out, Copy copies `data` as JSON and Changes is off). */ | { kind: "custom"; type: string; data: unknown; text?: string } | { kind: "table"; columns: string[]; rows: string[][] }type WorkspaceVersion
| Prop | Type | Default |
|---|---|---|
id | string | — |
label | stringWhat changed, in a few words: "Shorter intro". | — |
time | stringWhen, as shown: "2 min ago". | — |
content | WorkspaceContent | — |
type WorkspaceMessage
| Prop | Type | Default |
|---|---|---|
id | string | — |
from | "user" | "assistant" | — |
text | string | — |
version | stringThe version this answer made: a card in the chat that opens it. | — |
error | stringWhy the answer didn't come. | — |
type WorkspaceRun
What your model streams into, for one answer.
| Prop | Type | Default |
|---|---|---|
reply | (text: string) => voidAdds text to the agent's answer in the chat. | — |
write | (text: string) => voidAdds text to a new version of the document or code, which starts with the first call. | — |
replace | (content: WorkspaceContent) => voidSets the new version's whole content, e.g. a table. | — |
label | (text: string) => voidThe new version's few words: what changed. | — |
signal | AbortSignalAborted when the person presses stop: stop streaming. What came so far is kept. | — |
type DiffLine
| Prop | Type | Default |
|---|---|---|
text | string | — |
change | "same" | "added" | "removed" | — |
index | number | — |