Chat composer card with optional context, attachments, editor content, and actions.
Installation
import {
Composer,
useComposerEditorSubmit,
} from "@o/pipeline/experimental/components/composer";
Usage
Editor
Composers pair with Editor: Editor.Root wraps the composer — outside Composer.Root, so drafts survive content swaps — and Editor.Content plus Editor.Bubble render inside Composer.EditorBody. The external Editor component and Composer.Editor are intentionally adjacent: Composer.Editor is the composer’s elevated card, while Editor.Root is the editor state owner.
useComposerEditorSubmit adds submission behavior to Composer editors without changing the generic Editor.Root: Enter and ⌘↵ submit when there is content, while Shift+Enter inserts a new paragraph. When the editor is empty or submission is disabled, the keymap yields so the editor handles the key normally. Set preventEmptySubmit when a parent composition has its own Enter behavior that should not run for an empty editor. Use the hook’s submit/canSubmit values to wire Composer.EditorSubmit; the keymap is removed when the calling component unmounts.
const { submit, canSubmit } = useComposerEditorSubmit({ onSubmit, disabled });
Header
Context
Resume
Attachments
Notice
Composer.Notice is a header row for a sentence about the composer’s state, with an optional leading icon. Unlike Composer.Header it wraps, because a notice must be read whole. It renders role="status", a polite live region: notices explain the composer’s state rather than demand attention, so it is not an alert. Put secondary text in Composer.NoticeDescription.
While the composer is getting ready, the notice sits above a live Composer.Editor, so people can still draft. Pass a Spinner as its icon to show it is working.
When the viewer can’t post, the notice replaces Composer.Editor instead of sitting above a disabled one.
A problem takes a warning glyph in the danger colour.
Dynamic content
Dynamic content swaps happen directly under Composer.Root, as keyed siblings of the persistent Composer.Header. Composer.Editor is the editor card and contains the EditorAttachments/EditorBody/EditorFooter arrangement. Non-editor views bring their own card surface and padding; there is no generic body slot by design. Use Questionnaire for question views. Composer.Action is generic and can be used in the header, editor footer, or a custom view. Keep dismissal in state too: Esc or a dismiss action simply switches back.
Click the button on the bottom right
Composition
Editor.Root
└── Composer.Root
├── Composer.Header
│ ├── Glyph
│ ├── Composer.HeaderTitle
│ ├── Composer.HeaderDescription
│ └── Composer.Action
├── Composer.Notice
│ └── Composer.NoticeDescription
└── Composer.Editor
├── Composer.EditorAttachments
│ └── Attachment.Root
├── Composer.EditorBody
│ ├── Editor.Content
│ └── Editor.Bubble
└── Composer.EditorFooter
├── Composer.EditorActions
│ └── Composer.Action
└── Composer.EditorSubmit