Q3 summary
Here is where the quarter landed, ahead of Thursday's review.
The rest of the document carries on below, exactly where it was.
Generated text block inside a document
Here is where the quarter landed, ahead of Thursday's review.
The rest of the document carries on below, exactly where it was.
A passage of generated prose that sits inside a document as a real node — a paragraph, a heading, a list — wearing a thin frame, an attribution line and the four approval verbs until someone decides what to do with it. It streams in place, opens for editing in place, and can be re-prompted in place; what it never does is float over the page.
This is the shape Notion AI, Manus reports, Spellbook drafting and Claude artifacts all converged on, and the reason is the same in each: generated text has to survive save, reload and export as ordinary content. A block that is really an overlay looks identical in a screenshot and disappears the moment the document is serialised. Keeping the passage in the document also keeps the approval contract honest — F7 `approval-card`'s four verbs applied to prose, so the muscle memory built on approvals elsewhere in the product transfers to the middle of a paragraph.
Observed in: Notion AI · Manus · Spellbook · Claude artifacts
ai-doc-blockThe document node itself. Same element in every state — it never unmounts and never moves.ai-doc-block-headerAttribution row: the generated-by label, plus the streaming indicator when text is arriving.ai-doc-block-labelSays in words that the passage was generated. Not a colour, not a border.ai-doc-block-streamingSpinner and the word "Streaming", shown only while text is arriving.ai-doc-block-contentThe prose. Your markup, unwrapped — this is the part a document model saves and exports.ai-doc-block-editorThe textarea that replaces the prose in the editable state.ai-doc-block-verbsKeep · Edit · Regenerate · Discard, in that order, always.ai-doc-block-repromptThe re-prompt affordance: a labelled instruction field with its own Regenerate and Cancel.ai-doc-block-promptThe instruction field inside that affordance.ai-doc-block-statusPersistent live region that announces each state change.Reach for it whenever a model writes into a document the user already owns — a summary dropped under a heading, a clause drafted into a contract, a section expanded in a report. Pass the prose as children in ordinary markup and keep it in your document model; the component only supplies the frame, the attribution and the verbs. Drive `state` from your host: `streaming` while tokens arrive, `approval-verbs` once they stop, `editable` when Edit is pressed, `re-promptable` when Regenerate is. Every verb is optional and shows up only when you supply its handler, but the order is not yours to choose. For a generation that has not landed anywhere yet, use K2 `inline-generate-popup` — the popover generates, this block commits.
Do
Keep the passage in the flow of the document, with real paragraphs above and below it, so it saves and exports like any other content.
The paragraph above the generated passage.
The paragraph below it, which never moves.
Leave the verbs on screen but disabled while text is streaming — present and un-clickable, so the footer does not shrink and regrow when generation lands.
Don't
Don't render the block as a floating layer over the paragraph it replaces. It looks the same and serialises to nothing.
The paragraph underneath is the only thing the document model knows about. The generated text sits in a layer above it, so it cannot be saved, reloaded or exported with the rest of the page.
Support volume rose 30% this quarter against a headcount that did not move.
Don't rebuild the verb row by hand to reorder it. Keep · Edit · Regenerate · Discard is the contract, and the component fixes it whatever order you pass the handlers in.
Don't tint Discard with a translucent destructive background and destructive text — that pairing measures under 4.5:1. The component ships it solid instead.
state and swap children; the same DOM node stays between the same two paragraphs.value undefined in the editable state. The edit field is controlled; pair value with onValueChange and keep it in step with the prose you pass as children.npx shadcn@latest add https://super-ai-components.vercel.app/r/ai-doc-block.jsonRequires a shadcn app on Base UI (style base-nova). Radix-based styles such as new-york fail to typecheck: these components use Base UI's render prop and callbacks like onOpenChangeComplete, which Radix primitives do not have.