KQ-751 · PROMPT/COMPOSER
Prompt Composer
A composer that grows with the thought. A hidden mirror carries the draft in the field's own type and padding, so its measured height — read by a ResizeObserver — is the height the field needs, and the wrapper glides there on glide, holding at maxRows while the textarea scrolls inside. The footer's token estimate rolls its digits on snap, and the send control is one button whose shape says what it does: round with an arrow while idle, square with a stop mark while a run is live, the corners and glyph swapping on snap so focus never moves. Enter sends, Shift+Enter breaks the line, and while live Enter is held so the draft stays.
Idle · ≈ 0 tokens
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/prompt-composer
Props
- value / defaultValue
- string
- default ""
- Controlled or initial draft.
- onValueChange
- (value: string) => void
- default —
- Fires from every edit.
- onSend
- (value: string) => void
- default —
- Fires from Enter or the send button with the trimmed draft; the parent clears or keeps it.
- onStop
- () => void
- default —
- Fires from the stop button while live.
- live
- boolean
- default false
- A run is in flight: the send button becomes stop and Enter is held.
- model
- string
- default —
- Printed in the footer beside the estimate.
- estimateTokens
- (value: string) => number
- default Math.ceil(chars / 4)
- The estimate shown in the footer.
- maxRows
- number
- default 6
- Lines the field grows to before it scrolls.
- placeholder
- string
- default "Ask anything"
- Placeholder while the draft is empty.
- label
- string
- default —
- Names the textarea for assistive technology.
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue | string | "" | Controlled or initial draft. |
| onValueChange | (value: string) => void | — | Fires from every edit. |
| onSend | (value: string) => void | — | Fires from Enter or the send button with the trimmed draft; the parent clears or keeps it. |
| onStop | () => void | — | Fires from the stop button while live. |
| live | boolean | false | A run is in flight: the send button becomes stop and Enter is held. |
| model | string | — | Printed in the footer beside the estimate. |
| estimateTokens | (value: string) => number | Math.ceil(chars / 4) | The estimate shown in the footer. |
| maxRows | number | 6 | Lines the field grows to before it scrolls. |
| placeholder | string | "Ask anything" | Placeholder while the draft is empty. |
| label | string | — | Names the textarea for assistive technology. |