--- ## PromptInput The chat composer: an auto-growing textarea in a single bordered surface with a send button that flips to a stop button while a response is streaming. `onSubmit({ text, attachments })` fires with trimmed text when there is content (or at least one attachment); `onStop` fires from the stop button. Attachment intake (paperclip picker, clipboard paste, drag-and-drop, chip strip) reuses the shared `file-intake` core — the same accept/size/count validation and preview-URL lifecycle as FileUpload — and is opt-in via `allowAttachments`. **Import:** `import { PromptInput } from '@urbicon-ui/blocks';` ### Examples ```svelte send(text)} onStop={abort} /> ``` ```svelte send(text, attachments)} /> ``` ### Variants - size: md, sm (default: md) ### Api | Prop | Type | Required | Default | Description | | --- | --- | :---: | --- | --- | | onSubmit | `(payload: { text: string; attachments: FileIntakeEntry[] }) => void` | yes | | Fired on send with the trimmed text and the current attachments. Only fires when there is text or at least one attachment, and never while `busy` or `disabled`. | | ...HTMLAttributes | `HTMLAttributes` | no | | HTML attributes (excluding: 'children' | 'class' | 'onsubmit') | | ...PromptInputVariants | `VariantProps` | no | | Styling variants from PromptInputVariants | | accept | `string | string[]` | no | | Accepted MIME types / extensions (e.g. `'image/*'`, `['.pdf', 'image/png']`). | | allowAttachments | `boolean` | no | false | Enable the attachment surface: paperclip picker, clipboard-image paste, drag-and-drop, and the chip strip above the textarea. | | attachLabel | `string` | no | 'Attach file' | Accessible label for the attach button. | | attachments | `FileIntakeEntry[]` | no | | The accepted attachments (bindable). Populated by the picker / paste / drop when `allowAttachments` is set. On submit these are handed to `onSubmit` and — with `clearOnSubmit` — cleared from here **without** revoking their preview object-URLs, because ownership transfers to the consumer's message list. Removing chips through the built-in UI revokes their preview URLs for you. Clearing or splicing this array **externally** (e.g. reassigning `bind:attachments`) bypasses that cleanup — such mutations must revoke the dropped entries' previews themselves via `revokeIntakePreviews` (exported from `$lib/utils/file-intake`) to avoid leaking object-URLs. | | autofocus | `boolean` | no | false | Focus the textarea on mount. | | busy | `boolean` | no | false | A response is in flight: the send button is replaced by a stop button and Enter no longer submits. | | class | `string` | no | | Extra classes merged onto the root element. | | clearOnSubmit | `boolean` | no | true | Clear the text and attachments after a successful submit. Preview URLs of the submitted attachments are intentionally **not** revoked — see `attachments`. | | disabled | `boolean` | no | false | Disables the whole composer (textarea + buttons). | | hint | `Snippet` | no | | Helper line rendered under the composer (e.g. "Enter to send"). | | label | `string` | no | 'Message' | Accessible name for the textarea (rendered as `aria-label`). | | leading | `Snippet` | no | | Content in the leading (left) action zone, after the attach button. | | maxFiles | `number` | no | | Maximum number of attachments across the whole list. | | maxFileSize | `number` | no | | Maximum attachment size in bytes. | | maxRows | `number` | no | 8 | Maximum visible rows before the textarea scrolls internally. | | minRows | `number` | no | 1 | Minimum visible rows of the auto-growing textarea. | | onAttachmentReject | `(rejections: FileIntakeRejection[]) => void` | no | | Called with the rejected files when an add is refused (bad type, too large, over `maxFiles`, duplicate, or custom `validate`). The first rejection's message also surfaces inline; it clears on the next successful add. | | onStop | `() => void` | no | | Fired by the stop button (shown in place of send while `busy`). | | onValueChange | `(value: string) => void` | no | | Called whenever the text changes (including the clear after submit). | | placeholder | `string` | no | | Placeholder text for the textarea. | | preset | `string` | no | | Apply a named preset registered via ``. Prefer this over `class` overrides when the requested look falls outside the semantic intent palette. | | preventDocumentDrop | `boolean` | no | true | Prevent browser navigation when a file is dropped outside the composer. Only takes effect while `allowAttachments` is set. | | removeAttachmentLabel | `(name: string) => string` | no | (name) => `Remove ${name}` | Accessible label factory for a chip's remove button. | | sendLabel | `string` | no | 'Send' | Accessible label for the send button. | | slotClasses | `Partial>` | no | | Per-slot class overrides. Slots: root | attachmentsStrip | attachmentChip | attachmentThumb | attachmentName | attachmentSize | attachmentRemove | textarea | actions | leading | trailing | attachButton | sendButton | stopButton | error | hint | | stopLabel | `string` | no | 'Stop' | Accessible label for the stop button. | | submitOn | `'enter' | 'mod-enter'` | no | 'enter' | Which key gesture sends the message. - `enter` (default) — Enter sends, Shift+Enter inserts a newline. - `mod-enter` — Cmd/Ctrl+Enter sends, Enter inserts a newline. Submission is always suppressed mid-IME-composition. | | trailing | `Snippet` | no | | Content in the trailing (right) action zone, before the send/stop button. | | unstyled | `boolean` | no | | Remove all default tv() classes. | | validate | `(file: File) => FileIntakeEntry['errors'] | null` | no | | Custom per-file validation. Return an errors array or `null`. | | value | `string` | no | | The composer's text (bindable). | | size | `'md' | 'sm'` | no | md | Controls the dimensions, padding, and text size of the PromptInput. Affects the component's physical footprint. Available options: md, sm. | Inherited from: - PromptInputVariants (external) - Omit, 'children' | 'class' | 'onsubmit'> (omit-pattern) ### Types ```ts interface FileIntakeEntry { /** Unique identifier for this file entry. */ id: string; /** The native File object. */ file: File; /** Object URL for image previews (auto-generated, auto-revoked). */ preview?: string; /** Upload progress 0–100. Undefined when not tracking. */ progress?: number; /** Current lifecycle status. */ status: FileIntakeStatus; /** Validation or upload errors. */ errors: FileIntakeError[]; } ``` ```ts interface FileIntakeRejection { /** The rejected file. */ file: File; /** Why it was rejected. */ errors: FileIntakeError[]; } ``` ```ts type FileIntakeStatus = 'pending' | 'uploading' | 'complete' | 'error' ``` ```ts interface FileIntakeError { /** Machine-readable error code. */ code: FileIntakeErrorCode; /** Human-readable error message. */ message: string; } ``` ```ts type SlotNames = keyof ReturnType & string ``` ```ts type VariantProps = Omit< Exclude[0], undefined>, 'class' > ``` ```ts type FileIntakeErrorCode = | 'FILE_INVALID_TYPE' | 'FILE_TOO_LARGE' | 'FILE_TOO_SMALL' | 'TOO_MANY_FILES' | 'FILE_EXISTS' | 'CUSTOM' ``` ### Slots (slotClasses keys) `root`, `attachmentsStrip`, `attachmentChip`, `attachmentThumb`, `attachmentName`, `attachmentSize`, `attachmentRemove`, `textarea`, `actions`, `leading`, `trailing`, `attachButton`, `sendButton`, `stopButton`, `error`, `hint`