Skip to content

zidane-ui/chat/rich-composer

import { … } from 'zidane-ui/chat/rich-composer'ui/src/chat/rich-composer/index.ts

Components#

PlusButtonComponent#

The + button: attach files.

TypeScript
function PlusButton(props: PlusButtonProps): Element
Parameters
NameTypeDescription
propsPlusButtonProps

The click handler.

Element
PlusButton props
NameTypeDescription
onClick() => void

Opens the file picker.

ui/src/chat/rich-composer/composer-controls.tsx:29

RichComposerComponent#

zidane-ui/chat/rich-composer: the desktop app's Tiptap composer (mentions, commands, ghost text, shared drafts, prompt history). Its own entry, so an app that does not render it never loads it.

TypeScript
function RichComposer(props: RichComposerProps): Element
Parameters
NameTypeDescription
propsRichComposerProps

What the composer edits and sends, and its slots.

Element
RichComposer props
NameTypeDescription
activeSessionId?string | null

The session the composer writes to (null: a new chat).

attachmentLabel?AttachmentLabelFn

The icon and name an attachment tile shows.

attachmentsEnabled?boolean

Pasted and dropped files become attachments (off: images and long pastes stay inline, drops are ignored). Default true.

badges?ReactNode

The badge strip (ConversationBadges, the kit's ComposerBadges): the band at the top of the tray ring, above the card, the git bar's mirror. Built by ConversationComposer, so a keystroke here never re-renders it.

className?string

Classes merged on the root.

commandArgs?ComposerCommandArgs

The commands' fixed argument values (the command badge, the / popover's arguments).

disabledboolean

No folder picked: read-only, with its own placeholder.

fileRoot?string

Where @ file mentions list from, when it isn't folderPath: a sandbox session's virtual root (SidebarSession.workspace).

folderPathstring | null

The folder the session runs in ('': none; null: none picked yet).

footer?ReactNode

Rendered in the tray under the card (the desktop app's git bar).

hostId?string | null

The server the session (or the draft) runs on: / commands, their arguments and the ghost text come from it; this window's extension popups don't apply there. Absent = This computer.

inlineAutocomplete?boolean

Inline ghost-text autocomplete (ComposerHost.autocomplete). Default false.

leadingActions?ReactNode

Rendered next to the + attachment button — typically the inline AgentPicker. Kept as a slot so the Chatbox doesn't need to know which app-level controls the user wants inline.

messageDelivery?MessageDelivery

What Enter does with a prompt while running: steer it into the run or queue it for when the run ends. Alt+Enter does the other. Pass the user's "Messages during a run" setting. Default queue.

onSearchNavigate?(direction: -1 | 1) => void

Hops to the next (1) or previous (-1) match.

onSearchQueryChange?(query: string | null) => void

The search query as typed (null: search mode exited).

onSend(text: string, attachments: ComposerAttachment[], skills: string[], opts?: { delivery?: MessageDelivery; keepDraft?: boolean }) => void | Promise<void>

Sends a prompt. opts.delivery is set while running. Rejecting puts the prompt back in an empty composer.

onStop?() => void

Stops the run.

promptSuggestions?boolean

The next-prompt suggestion as a ghost while the composer is empty (ComposerHost.suggestions). Default false.

remoteSearchApply?{ active: boolean; query: string; seq: number } | null

Shared-search application (daemon mode) — Center threads the active session's search:changed signals here so the composer's ? mode mirrors a search driven from another window. seq forces the apply effect to re-fire on every signal (values alone can repeat).

running?boolean

When true, the send button is replaced by a stop button that calls onStop.

scopeKey?string | null

The composer's scope on the composer bus (composer-bus.ts): pushes aimed at it land here.

searchOverlayOpen?boolean

True while the ⌘F overlay is open. The overlay and the composer mode drive the same document-wide highlight registry, so opening the overlay force-exits the composer's search mode.

searchStatus?{ activeIndex: number; capped: boolean; count: number; hasNeedle: boolean } | null

The search engine's counter while in search mode: matches, whether capped, the active one, whether a query is typed.

siteIcons?ChatSiteIcons

Site icons for link badges. Absent: links show the globe.

style?CSSProperties

Styles on the root.

textPreview?ComponentType<TextPreviewSlotProps>

What a text attachment opens as in the lightbox. Default: the kit's TextPreview.

trailingActions?ReactNode

Rendered on the right side of the controls row, immediately before the Send / Stop button — the per-session context gauge + cost pill cluster lives here. Slot keeps Chatbox agnostic of the actual pills (App.tsx owns the state, Center.tsx threads the node in). Children are responsible for their own collapse behavior; the Chatbox surface declares a CSS container so container queries on descendants can react to the prompt's inline-size.

ui/src/chat/rich-composer/RichComposer.tsx:209

SendStopButtonComponent#

Unified Send / Stop control — one button element that never unmounts, so the idle→streaming→hover-stop states crossfade in place (no layout jump; constant 32×32 footprint). Three visual states, all CSS-driven via the zg-sendstop classes in rich-composer.css:

  • idle (data-state="send") — a lit accent sphere (the daemon orb's drawing) + send glyph; asleep while the prompt is empty.
  • streaming (data-state="run") — the same lit sphere, its glyph turned into a thin spinner ring: "the agent is working", not an alarm.
  • streaming + hover/focus-visible — the stop affordance surfaces: the sphere eases to the danger colour and a square stop glyph fades in.

Clicking while streaming always stops (the hover state is a reveal, not a gate — touch/keyboard users get the same action without hover).

TypeScript
function SendStopButton(props: SendStopButtonProps): Element
Parameters
NameTypeDescription
propsSendStopButtonProps

The run and draft state, and the handlers.

Element
SendStopButton props
NameTypeDescription
canSendboolean

The draft can be sent.

onSend() => void

Sends the draft.

onStop?() => void

Stops the run.

runningboolean

A run streams: the button stops it.

ui/src/chat/rich-composer/composer-controls.tsx:75

Interfaces#

PlusButtonPropsInterface#

Props for PlusButton.

TypeScript
interface PlusButtonProps
PlusButtonProps properties
NameTypeDescription
onClick() => void

Opens the file picker.

ui/src/chat/rich-composer/composer-controls.tsx:19

RichComposerPropsInterface#

Props for RichComposer.

TypeScript
interface RichComposerProps
RichComposerProps properties
NameTypeDescription
activeSessionId?string | null

The session the composer writes to (null: a new chat).

attachmentLabel?AttachmentLabelFn

The icon and name an attachment tile shows.

attachmentsEnabled?boolean

Pasted and dropped files become attachments (off: images and long pastes stay inline, drops are ignored). Default true.

badges?ReactNode

The badge strip (ConversationBadges, the kit's ComposerBadges): the band at the top of the tray ring, above the card, the git bar's mirror. Built by ConversationComposer, so a keystroke here never re-renders it.

className?string

Classes merged on the root.

commandArgs?ComposerCommandArgs

The commands' fixed argument values (the command badge, the / popover's arguments).

disabledboolean

No folder picked: read-only, with its own placeholder.

fileRoot?string

Where @ file mentions list from, when it isn't folderPath: a sandbox session's virtual root (SidebarSession.workspace).

folderPathstring | null

The folder the session runs in ('': none; null: none picked yet).

footer?ReactNode

Rendered in the tray under the card (the desktop app's git bar).

hostId?string | null

The server the session (or the draft) runs on: / commands, their arguments and the ghost text come from it; this window's extension popups don't apply there. Absent = This computer.

inlineAutocomplete?boolean

Inline ghost-text autocomplete (ComposerHost.autocomplete). Default false.

leadingActions?ReactNode

Rendered next to the + attachment button — typically the inline AgentPicker. Kept as a slot so the Chatbox doesn't need to know which app-level controls the user wants inline.

messageDelivery?MessageDelivery

What Enter does with a prompt while running: steer it into the run or queue it for when the run ends. Alt+Enter does the other. Pass the user's "Messages during a run" setting. Default queue.

onSearchNavigate?(direction: -1 | 1) => void

Hops to the next (1) or previous (-1) match.

onSearchQueryChange?(query: string | null) => void

The search query as typed (null: search mode exited).

onSend(text: string, attachments: ComposerAttachment[], skills: string[], opts?: { delivery?: MessageDelivery; keepDraft?: boolean }) => void | Promise<void>

Sends a prompt. opts.delivery is set while running. Rejecting puts the prompt back in an empty composer.

onStop?() => void

Stops the run.

promptSuggestions?boolean

The next-prompt suggestion as a ghost while the composer is empty (ComposerHost.suggestions). Default false.

remoteSearchApply?{ active: boolean; query: string; seq: number } | null

Shared-search application (daemon mode) — Center threads the active session's search:changed signals here so the composer's ? mode mirrors a search driven from another window. seq forces the apply effect to re-fire on every signal (values alone can repeat).

running?boolean

When true, the send button is replaced by a stop button that calls onStop.

scopeKey?string | null

The composer's scope on the composer bus (composer-bus.ts): pushes aimed at it land here.

searchOverlayOpen?boolean

True while the ⌘F overlay is open. The overlay and the composer mode drive the same document-wide highlight registry, so opening the overlay force-exits the composer's search mode.

searchStatus?{ activeIndex: number; capped: boolean; count: number; hasNeedle: boolean } | null

The search engine's counter while in search mode: matches, whether capped, the active one, whether a query is typed.

siteIcons?ChatSiteIcons

Site icons for link badges. Absent: links show the globe.

style?CSSProperties

Styles on the root.

textPreview?ComponentType<TextPreviewSlotProps>

What a text attachment opens as in the lightbox. Default: the kit's TextPreview.

trailingActions?ReactNode

Rendered on the right side of the controls row, immediately before the Send / Stop button — the per-session context gauge + cost pill cluster lives here. Slot keeps Chatbox agnostic of the actual pills (App.tsx owns the state, Center.tsx threads the node in). Children are responsible for their own collapse behavior; the Chatbox surface declares a CSS container so container queries on descendants can react to the prompt's inline-size.

ui/src/chat/rich-composer/RichComposer.tsx:75

SendStopButtonPropsInterface#

Props for SendStopButton.

TypeScript
interface SendStopButtonProps
SendStopButtonProps properties
NameTypeDescription
canSendboolean

The draft can be sent.

onSend() => void

Sends the draft.

onStop?() => void

Stops the run.

runningboolean

A run streams: the button stops it.

ui/src/chat/rich-composer/composer-controls.tsx:46