Hooks & Helpers
The developer-experience layer: a state hook, trigger preset factories, and segment helpers that remove boilerplate.
usePromptAreaState()
Manages the segment value, ref, and derived state. Call it once and spread bind onto the component.
const { bind, plainText, isEmpty, hasChips, chips, clear, focus } =
usePromptAreaState()
<PromptArea {...bind} />| Prop | Type | Default | Description |
|---|---|---|---|
bind | { ref, value, onChange } | — | Spread onto <PromptArea {...bind} />. |
plainText | string | — | Derived plain text of the current value. |
isEmpty | boolean | — | True when empty or whitespace-only. |
hasChips | boolean | — | True when the value contains a chip. |
chips | ChipSegment[] | — | All chip segments in the value. |
clear() | () => void | — | Clear all content. |
focus() / blur() | () => void | — | Focus or blur the input. |
insertChip() | (chip: Omit<ChipSegment, 'type'>) => void | — | Insert a chip imperatively. |
useMarkdownMode()
Owns the markdown / plain-text variant as a named mode and returns a markdown boolean to spread onto the component, plus toggle. Switching is non-destructive — the text is kept, only its rendering changes. The a-large-small (ALargeSmall) icon is the conventional toggle control.
const { bind } = usePromptAreaState()
const { markdown, mode, toggle } = useMarkdownMode()
<PromptArea {...bind} markdown={markdown} />
<button onClick={toggle} aria-pressed={markdown}>
<ALargeSmall className="size-4" />
</button>| Prop | Type | Default | Description |
|---|---|---|---|
mode | 'markdown' | 'plain' | — | The active variant. |
markdown | boolean | — | True in markdown mode — spread as the markdown prop. |
isPlainText | boolean | — | True in plain-text mode (inverse of markdown). |
toggle() | () => void | — | Flip between markdown and plain text. |
setMode(mode) | (mode: 'markdown' | 'plain') => void | — | Switch to an explicit mode. |
Options: initialMode (default 'markdown'), mode for controlled use, and onModeChange. The pure oppositeMode(mode) helper is exported for custom toggles.
Trigger presets
Each returns a TriggerConfig for the triggers prop. Pass onSearch to supply items, plus optional styling.
| Prop | Type | Default | Description |
|---|---|---|---|
mentionTrigger(opts?) | (opts) => TriggerConfig | — | @ dropdown for users, agents, or documents. |
commandTrigger(opts?) | (opts) => TriggerConfig | — | / dropdown anywhere in the input. Pass position: "start" to limit it to the start of a line. |
hashtagTrigger(opts?) | (opts) => TriggerConfig | — | # tags that auto-resolve on space. |
callbackTrigger(opts) | (opts) => TriggerConfig | — | Fire a callback (e.g. file picker) instead of a dropdown. |
Segment helpers
Pure functions for building and reading segment arrays:
| Prop | Type | Default | Description |
|---|---|---|---|
text(value) | (string) => TextSegment | — | Create a text segment. |
chip(opts) | (Omit<ChipSegment,'type'>) => ChipSegment | — | Create a chip segment. |
getChips(segments) | (Segment[]) => ChipSegment[] | — | All chips in a segment array. |
getChipsByTrigger(segments, trigger) | (Segment[], string) => ChipSegment[] | — | Chips for one trigger, e.g. '@' or '/'. |
isSegmentsEmpty(segments) | (Segment[]) => boolean | — | True when empty or whitespace-only. |
hasChips(segments) | (Segment[]) => boolean | — | True when at least one chip is present. |
import { getChipsByTrigger } from '@/components/segment-helpers'
const mentions = getChipsByTrigger(bind.value, '@').map((c) => c.value)