AI interfaces and text editor
The pieces of an assistant interface come from the chat of the AMSET assistant, which uses them every day: the input box, the messages, the reasoning, the tool calls, the log of what an agent does, the files it generates and the model picker. There are 16, with no dependencies, one CSS file per component, and everything conveyed with text as well as colour.
The components
Activity log
ActivityLog
Agent workflow
AgentWorkflowEditor
AI usage
AIUsageDashboard
Approve an agent action
AgentApprovalDialog
Artifact card
ArtifactCard
Attachment
Attachment
Chat message
ChatMessage
Engine selector
ModelSelector
Image viewer
ImageViewer
Prompt input
PromptInput
Reasoning
Reasoning
Search the conversation
FindBar
Suggestions
Suggestions
Template editor
PromptTemplateEditor
Tool call
ToolCall
Usage and cost
UsageBadge
PromptInputgrows with the text up to a limit, sends with Enter and adds a new line with Shift+Enter (without sending while composing with an IME), recalls the last message with the up arrow and accepts files that are pasted, dragged or picked. WithbusyModesyou can keep typing while it answers: add to the turn, stop and correct, or queue.ChatMessageincludes copy, regenerate and edit in the same bubble, plus custom actions.ActivityLogfollows the end only if you were already at the bottom and groups repeated events; withgroup, a history of several tasks.FindBarsearches within a long conversation, where the browser's find cannot see what has not been loaded.ArtifactFrameisolates the HTML an assistant generates withsandbox="allow-scripts", never withallow-same-origin.
Rich text editor
The library's text editor is RichTextEditor, from @amset/ui/rich-text: our own, with no third-party dependencies, with review, version history and formulas. It has its own page, with demos. The toolbar, RichTextToolbar, can also be used on its own with whatever editing engine you already have; this is what talks to it:
| Direction | Prop | What it carries |
|---|---|---|
| editor → toolbar | state | What is applied where the cursor is (active), what cannot be used right now (unavailable) and the link at the cursor (link). |
| toolbar → editor | onCommand | The command: 'bold', 'heading2', 'bulletList', 'undo', 'clearFormatting'... |
| toolbar → editor | onLinkChange | { href, newTab } already checked, or null to remove it. |
| toolbar → editor | onImageInsert | { src, alt } already checked; alt is only empty if the image is marked as decorative. |
| application → toolbar | shortcuts | The editor's shortcuts (Mod+B), which the toolbar announces and shows in its help. The editor listens for them, not the toolbar. |
- A single tab stop for the whole toolbar, arrow keys between buttons and focus memory when you come back.
- Each format is a
ToggleButtonwitharia-pressed; clicking it with the mouse does not take focus away from the editor, so the selection stays where it was. - The link and image panels are only mounted when opened and are not a
<form>: the toolbar works inside your application's form. - Addresses are checked with
normalizeLinkHrefandnormalizeImageSrc(an allowlist of schemes: ajavascript:or adata:does not get through). The toolbar does not clean what the editor stores: that HTML is sanitised on the server before storing or rendering it.
