Composer Panel
The two-row composer: a radius-24 card with the prompt on top and the controls underneath, the add menu, a permission picker with Auto, Manual, Plan mode and Bypass all, a model picker with a scrollable provider rail and an effort popover on the chosen model, mic and send, plus a status tab with branch, project folder and context meter hanging off the card's top edge. The single-line Composer pill stays as it is; this is the taller layout for agent surfaces.
Installation
Dark mode ready - this component follows BoardUI semantic tokens automatically. Set up theme switching.
Permissions
The picker paints its surface only on hover, press, or while the menu is open, so at rest it reads as a label next to the plus button. The menu lists the four modes with a one-line description each and a Learn more link button in the header (learnMoreHref or onLearnMore). Auto is the default; set defaultPermission to start elsewhere, or pass permission and onPermissionChange to own it. The trigger, the menu and the option list also ship on their own as PermissionMenu and COMPOSER_PERMISSIONS.
{/* Auto is the default; start on another mode */}<ComposerPanel defaultPermission="plan" />
{/* Or own the mode yourself */}const [permission, setPermission] = useState<ComposerPermission>("auto");<ComposerPanel permission={permission} onPermissionChange={setPermission} />
{/* The picker on its own; "Learn more" is a Link Button, a link with href or a button with onLearnMore */}<PermissionMenu value={permission} onChange={setPermission} learnMoreHref="/docs/permissions" />Model picker
A 341px panel: a scrollable rail of provider marks down the left, and the chosen provider's lineup as radio rows on the right under a Models header with the Quick Search hint. MODEL_PROVIDERSships eighteen realistic lineups, the eight marks the design carries plus ten more from LobeHub's icon set, from OpenAI and Anthropic to Google, Mistral, xAI and Amazon. Only the selected row carries the effort chip; pressing it opens the effort popover with the same six-stop Faster to Smarter slider the pill composer uses. Quick Search, or /, turns the header into a field that filters every lineup at once, each match tagged with its provider; a pick lands you on that provider's list. Every list scrolls. Pass providers for your own catalogue.
{/* Start on a model and effort stop; MODEL_PROVIDERS ships eighteen realistic lineups */}<ComposerPanel defaultModel="anthropic/fable-5.1" defaultEffort={3} />
{/* Or own both */}const [model, setModel] = useState("openai/gpt-5.6-mini");const [effort, setEffort] = useState(1); // 0 Low … 5 Max, see EFFORT_LEVELS<ComposerPanel model={model} onModelChange={setModel} effort={effort} onEffortChange={setEffort} />
{/* Your own catalogue, and the picker on its own */}<ModelPicker providers={[{ id: "acme", name: "Acme", logo: "/logos/acme.svg", models: [{ id: "acme/acme-1", name: "Acme 1" }] }]} value={model} onChange={setModel}/>Status tab
The tab is a 34px grey strip inset 28px on each side, rounded at the top only, carrying the branch, the project folder and the 16px context ring. It is the status slot: render ComposerStatusTab with your own values, drop in anything else, or pass null for the bare card.
{/* Branch, project folder and context meter */}<ComposerPanel status={<ComposerStatusTab branch="release/2.0" project="boardui" context={82} />}/>
{/* No tab at all */}<ComposerPanel status={null} />Wiring it up
The prompt is a textarea that grows a line at a time up to ten lines. Enter sends and Shift+Enter breaks the line; onSubmit receives the text and the field clears itself when uncontrolled. disabled greys out send while a turn is in flight, and inputRef reaches the field for focus.
const [draft, setDraft] = useState("");const inputRef = useRef<HTMLTextAreaElement>(null);
<ComposerPanel value={draft} onValueChange={setDraft} onSubmit={(message) => send(message)} // Enter sends, Shift+Enter breaks the line disabled={waiting} placeholder="Hi, what do you need today?" inputRef={inputRef}/>Attachments
The same card carries a strip of 56px file tiles above the prompt, each drawing the upload ring while its file lands. The tiles, the strip and the sequential upload queue have their own page.