composeai 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/dist/index.cjs +4750 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +896 -0
- package/dist/index.d.ts +896 -0
- package/dist/index.js +4747 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
- package/src/composer.css +481 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ComponentType, SVGProps, ReactNode, CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Built-in icon set for the composer.
|
|
6
|
+
*
|
|
7
|
+
* Why inline instead of `lucide-react`?
|
|
8
|
+
* - Removes the runtime dependency entirely.
|
|
9
|
+
* - Lets consumers override any icon with their own component via the
|
|
10
|
+
* `icons` prop on `<Composer />` — useful for matching a brand kit
|
|
11
|
+
* (Heroicons, Phosphor, Material, etc).
|
|
12
|
+
*
|
|
13
|
+
* The defaults below are 24x24 stroke SVGs drawn in the same visual idiom as
|
|
14
|
+
* lucide so the out-of-the-box composer looks the same as before. Each icon
|
|
15
|
+
* forwards arbitrary SVGProps so callers can pass `className`, `style`,
|
|
16
|
+
* `aria-*`, etc.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
type IconProps = SVGProps<SVGSVGElement>;
|
|
20
|
+
type IconComponent = ComponentType<IconProps>;
|
|
21
|
+
/**
|
|
22
|
+
* Public icon slot map. Consumers can override any of these via the
|
|
23
|
+
* `icons` prop on `<Composer />`. Anything left unspecified falls back to
|
|
24
|
+
* the built-in lucide-style default above.
|
|
25
|
+
*/
|
|
26
|
+
interface ComposerIcons {
|
|
27
|
+
/** Submit / send the message. */
|
|
28
|
+
send: IconComponent;
|
|
29
|
+
/** Stop an in-flight streaming response. */
|
|
30
|
+
stop: IconComponent;
|
|
31
|
+
/** Toolbar: attach any file. */
|
|
32
|
+
attach: IconComponent;
|
|
33
|
+
/** Toolbar: pick an image. */
|
|
34
|
+
image: IconComponent;
|
|
35
|
+
/** Voice plugin: start recording. */
|
|
36
|
+
voice: IconComponent;
|
|
37
|
+
/** Voice plugin: transcription in progress. */
|
|
38
|
+
voiceRecording: IconComponent;
|
|
39
|
+
/** Toolbar: enable web search. */
|
|
40
|
+
web: IconComponent;
|
|
41
|
+
/** Close / dismiss (e.g. lightbox, chip remove). */
|
|
42
|
+
close: IconComponent;
|
|
43
|
+
/** Zoom into an attachment / diagram. */
|
|
44
|
+
zoom: IconComponent;
|
|
45
|
+
/** Generic file (PDF, doc, etc) attachment. */
|
|
46
|
+
file: IconComponent;
|
|
47
|
+
/** Audio attachment. */
|
|
48
|
+
audio: IconComponent;
|
|
49
|
+
/** Decorative sparkle (suggestions, diagram preview header). */
|
|
50
|
+
sparkle: IconComponent;
|
|
51
|
+
/** Generic loading spinner (used on attachment chips during upload). */
|
|
52
|
+
spinner: IconComponent;
|
|
53
|
+
/** Generic warning / failure badge (used on failed-upload chips). */
|
|
54
|
+
warning: IconComponent;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Stylable surfaces inside the composer. Every entry corresponds to a real
|
|
59
|
+
* DOM node the package owns. `classNames` and `sx` props are keyed by these
|
|
60
|
+
* slot names so consumers can reskin a single piece without forking the
|
|
61
|
+
* component.
|
|
62
|
+
*/
|
|
63
|
+
type ComposerSlot = "root" | "card" | "editor" | "placeholder" | "toolbar" | "toolbarButton" | "sendButton" | "stopButton" | "hint" | "attachmentTray" | "attachmentChip" | "mention" | "mentionMenu" | "mentionItem" | "slashMenu" | "slashItem" | "mermaidPreview";
|
|
64
|
+
/**
|
|
65
|
+
* Per-slot className overrides. Strings are merged after the built-in
|
|
66
|
+
* classes (last-wins via standard CSS cascade), so consumers can layer
|
|
67
|
+
* Tailwind utilities or their own classes on top of the defaults.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <Composer
|
|
72
|
+
* classNames={{
|
|
73
|
+
* card: "bg-gradient-to-br from-violet-500/10 to-pink-500/10",
|
|
74
|
+
* sendButton: "bg-violet-600 hover:bg-violet-700",
|
|
75
|
+
* }}
|
|
76
|
+
* />
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
type ComposerSlotClassNames = Partial<Record<ComposerSlot, string>>;
|
|
80
|
+
/**
|
|
81
|
+
* Lightweight `sx` value: a plain CSS object merged onto a slot's `style`
|
|
82
|
+
* with a few token-aware shortcuts.
|
|
83
|
+
*
|
|
84
|
+
* Token-aware keys (`color`, `bg`, `backgroundColor`, `borderColor`,
|
|
85
|
+
* `outlineColor`, `fill`, `stroke`) accept either a real CSS value
|
|
86
|
+
* (`"#fff"`, `"rgb(...)"`, `"red"`) or one of the composer's design tokens
|
|
87
|
+
* (`"primary"`, `"accent"`, `"border"`, `"card"`, `"muted"`,
|
|
88
|
+
* `"foreground"`, `"background"`, `"destructive"`, `"success"`,
|
|
89
|
+
* `"warning"`), in which case it expands to `hsl(var(--<token>))`.
|
|
90
|
+
*
|
|
91
|
+
* Sizing/spacing keys accept numbers (React adds `px`) or any CSS length
|
|
92
|
+
* string (`"200px"`, `"50%"`, `"clamp(...)"`).
|
|
93
|
+
*
|
|
94
|
+
* No pseudo selectors, no media queries, no responsive arrays — reach for
|
|
95
|
+
* `classNames` when you need those.
|
|
96
|
+
*/
|
|
97
|
+
type ComposerSxValue = CSSProperties & {
|
|
98
|
+
/** Shortcut for `backgroundColor`; accepts a token name. */
|
|
99
|
+
bg?: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Per-slot sx overrides. Each value follows {@link ComposerSxValue}.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```tsx
|
|
106
|
+
* <Composer
|
|
107
|
+
* sx={{
|
|
108
|
+
* card: { bg: "card", borderColor: "primary", borderRadius: 12 },
|
|
109
|
+
* editor: { minHeight: 80, fontFamily: "JetBrains Mono, monospace" },
|
|
110
|
+
* sendButton: { bg: "primary", color: "primary-foreground" },
|
|
111
|
+
* }}
|
|
112
|
+
* />
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
type ComposerSxMap = Partial<Record<ComposerSlot, ComposerSxValue>>;
|
|
116
|
+
/**
|
|
117
|
+
* Design tokens applied as inline CSS custom properties on the composer
|
|
118
|
+
* root, so they cascade into every slot — including the package's built-in
|
|
119
|
+
* CSS — without overriding the consumer app's global theme.
|
|
120
|
+
*
|
|
121
|
+
* Color tokens are HSL components (e.g. `"258 90% 62%"`) so they compose
|
|
122
|
+
* cleanly with opacities (`hsl(var(--primary) / 0.1)`), matching the
|
|
123
|
+
* convention used internally.
|
|
124
|
+
*
|
|
125
|
+
* Sizing tokens (`radius`, `fontSize`) accept a number (treated as `px`)
|
|
126
|
+
* or any CSS length string.
|
|
127
|
+
*/
|
|
128
|
+
interface ComposerTokens {
|
|
129
|
+
primary?: string;
|
|
130
|
+
primaryForeground?: string;
|
|
131
|
+
accent?: string;
|
|
132
|
+
accentForeground?: string;
|
|
133
|
+
background?: string;
|
|
134
|
+
foreground?: string;
|
|
135
|
+
card?: string;
|
|
136
|
+
cardForeground?: string;
|
|
137
|
+
popover?: string;
|
|
138
|
+
popoverForeground?: string;
|
|
139
|
+
muted?: string;
|
|
140
|
+
mutedForeground?: string;
|
|
141
|
+
border?: string;
|
|
142
|
+
ring?: string;
|
|
143
|
+
input?: string;
|
|
144
|
+
destructive?: string;
|
|
145
|
+
destructiveForeground?: string;
|
|
146
|
+
success?: string;
|
|
147
|
+
successForeground?: string;
|
|
148
|
+
warning?: string;
|
|
149
|
+
warningForeground?: string;
|
|
150
|
+
/** Outer card corner radius. Number → px. Default `28px`. */
|
|
151
|
+
radius?: number | string;
|
|
152
|
+
/** Editor base font size. Number → px. Default `15px`. */
|
|
153
|
+
fontSize?: number | string;
|
|
154
|
+
/** Font family for the editor and chips. Defaults to inherit. */
|
|
155
|
+
fontFamily?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Consumer-supplied renderer for fenced code blocks the composer treats as
|
|
159
|
+
* diagrams (currently `mermaid`). When provided, the composer will NOT try to
|
|
160
|
+
* dynamically import the `mermaid` package — your renderer is fully in charge.
|
|
161
|
+
* When omitted, the composer falls back to a lazy `import("mermaid")`; if
|
|
162
|
+
* `mermaid` isn't installed, the diagram is silently skipped.
|
|
163
|
+
*
|
|
164
|
+
* Future fence languages may be routed here too; inspect `language` to
|
|
165
|
+
* decide what to do.
|
|
166
|
+
*/
|
|
167
|
+
type DiagramRenderer = (params: {
|
|
168
|
+
/** Raw code inside the fence, with the surrounding ``` stripped. */
|
|
169
|
+
code: string;
|
|
170
|
+
/** Language tag from the opening fence — e.g. "mermaid". */
|
|
171
|
+
language: string;
|
|
172
|
+
}) => ReactNode;
|
|
173
|
+
/**
|
|
174
|
+
* How a click on a quick-prompt chip is interpreted.
|
|
175
|
+
* - `"sendValue"` (default): drop the prompt into the editor and submit
|
|
176
|
+
* immediately. The user sees their `onSend` payload fire as if they
|
|
177
|
+
* typed it and pressed Enter.
|
|
178
|
+
* - `"initValue"`: drop the prompt into the editor but do NOT submit —
|
|
179
|
+
* the user can edit it further before sending.
|
|
180
|
+
*/
|
|
181
|
+
type ComposerPromptBehavior = "sendValue" | "initValue";
|
|
182
|
+
interface ComposerPromptsConfig {
|
|
183
|
+
/**
|
|
184
|
+
* Full list of prompts the consumer wants to expose. Order is preserved
|
|
185
|
+
* unless `randomize` is `true` (the default).
|
|
186
|
+
*/
|
|
187
|
+
items: string[];
|
|
188
|
+
/**
|
|
189
|
+
* What clicking a chip should do. Defaults to `"sendValue"` — clicking
|
|
190
|
+
* a prompt fills the editor and submits immediately, which matches the
|
|
191
|
+
* "starter prompts" UX users see in most AI chat surfaces.
|
|
192
|
+
*/
|
|
193
|
+
behavior?: ComposerPromptBehavior;
|
|
194
|
+
/**
|
|
195
|
+
* Optional notification fired whenever the user picks a prompt — useful
|
|
196
|
+
* for analytics. Fires regardless of `behavior`.
|
|
197
|
+
*/
|
|
198
|
+
onSelect?: (prompt: string) => void;
|
|
199
|
+
/**
|
|
200
|
+
* Maximum number of chips rendered at once. Defaults to `3`, hard-capped
|
|
201
|
+
* at `5` to keep the chip row from dominating the composer.
|
|
202
|
+
*/
|
|
203
|
+
maxToShow?: number;
|
|
204
|
+
/**
|
|
205
|
+
* When `items.length > maxToShow`, pick a random subset on each mount
|
|
206
|
+
* so different sessions see different suggestions. Defaults to `true`.
|
|
207
|
+
* Set `false` to always show the first N items in order.
|
|
208
|
+
*/
|
|
209
|
+
randomize?: boolean;
|
|
210
|
+
}
|
|
211
|
+
type AttachmentKind = "image" | "audio" | "file";
|
|
212
|
+
/**
|
|
213
|
+
* Upload lifecycle for an attachment. Only meaningful when
|
|
214
|
+
* `attachmentOptions.uploadFirst` is enabled.
|
|
215
|
+
* - `undefined` / `"ready"`: no upload in flight (default; same as today).
|
|
216
|
+
* - `"uploading"`: `onUpload(file)` is running — chip shows a spinner.
|
|
217
|
+
* - `"uploaded"`: `onUpload` resolved truthy — chip looks normal again.
|
|
218
|
+
* - `"failed"`: `onUpload` resolved falsy or threw — chip shows a warning.
|
|
219
|
+
* The user can dismiss the chip and re-attach to retry.
|
|
220
|
+
*/
|
|
221
|
+
type AttachmentStatus = "ready" | "uploading" | "uploaded" | "failed";
|
|
222
|
+
interface Attachment {
|
|
223
|
+
id: string;
|
|
224
|
+
kind: AttachmentKind;
|
|
225
|
+
name: string;
|
|
226
|
+
mimeType: string;
|
|
227
|
+
size: number;
|
|
228
|
+
file: File;
|
|
229
|
+
/** Object URL for previews (revoked when removed). */
|
|
230
|
+
previewUrl?: string;
|
|
231
|
+
/** Optional natural width/height for images. */
|
|
232
|
+
width?: number;
|
|
233
|
+
height?: number;
|
|
234
|
+
/** Optional duration (seconds) for audio. */
|
|
235
|
+
duration?: number;
|
|
236
|
+
/**
|
|
237
|
+
* Upload lifecycle. `undefined` is equivalent to `"ready"` and means
|
|
238
|
+
* "no upload pipeline in play" — the historical default. Populated only
|
|
239
|
+
* when `attachmentOptions.uploadFirst` is on.
|
|
240
|
+
*/
|
|
241
|
+
status?: AttachmentStatus;
|
|
242
|
+
/** Optional message surfaced when `status === "failed"`. */
|
|
243
|
+
error?: string;
|
|
244
|
+
}
|
|
245
|
+
interface MentionItem {
|
|
246
|
+
id: string;
|
|
247
|
+
label: string;
|
|
248
|
+
description?: string;
|
|
249
|
+
avatarUrl?: string;
|
|
250
|
+
icon?: ReactNode;
|
|
251
|
+
}
|
|
252
|
+
interface MentionRef {
|
|
253
|
+
id: string;
|
|
254
|
+
label: string;
|
|
255
|
+
}
|
|
256
|
+
interface SlashCommand {
|
|
257
|
+
id: string;
|
|
258
|
+
label: string;
|
|
259
|
+
description?: string;
|
|
260
|
+
group?: string;
|
|
261
|
+
icon?: ReactNode;
|
|
262
|
+
shortcut?: string;
|
|
263
|
+
/**
|
|
264
|
+
* Called when the command is chosen. Receive helpers to mutate the editor.
|
|
265
|
+
* If omitted, the slash text is simply removed.
|
|
266
|
+
*/
|
|
267
|
+
onSelect?: (ctx: SlashCommandContext) => void;
|
|
268
|
+
}
|
|
269
|
+
interface SlashCommandContext {
|
|
270
|
+
/** Inserts text at the current selection (after removing the slash trigger). */
|
|
271
|
+
insertText: (text: string) => void;
|
|
272
|
+
/** Replaces the slash trigger with raw markdown content. */
|
|
273
|
+
insertMarkdown: (markdown: string) => void;
|
|
274
|
+
/** Closes the menu without changing the editor. */
|
|
275
|
+
cancel: () => void;
|
|
276
|
+
/** Submits the composer immediately. */
|
|
277
|
+
submit: () => void;
|
|
278
|
+
}
|
|
279
|
+
interface ComposerSubmitPayload {
|
|
280
|
+
/** Plain text (chips collapsed to their labels). */
|
|
281
|
+
text: string;
|
|
282
|
+
/** Serialized markdown including chips as `@label`. */
|
|
283
|
+
markdown: string;
|
|
284
|
+
attachments: Attachment[];
|
|
285
|
+
mentions: MentionRef[];
|
|
286
|
+
}
|
|
287
|
+
interface MentionConfig {
|
|
288
|
+
/** Static list, or async resolver that receives the query (without `@`). */
|
|
289
|
+
items: MentionItem[] | ((query: string) => MentionItem[] | Promise<MentionItem[]>);
|
|
290
|
+
/** Visual character used to render the trigger. Defaults to "@". */
|
|
291
|
+
trigger?: string;
|
|
292
|
+
/** Limit suggestion count. Defaults to 8. */
|
|
293
|
+
maxItems?: number;
|
|
294
|
+
}
|
|
295
|
+
interface SlashConfig {
|
|
296
|
+
items: SlashCommand[] | ((query: string) => SlashCommand[] | Promise<SlashCommand[]>);
|
|
297
|
+
trigger?: string;
|
|
298
|
+
maxItems?: number;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* One entry in the paperclip's type-picker menu. When `AttachmentsConfig.types`
|
|
302
|
+
* is supplied, clicking the paperclip opens a small popover listing these
|
|
303
|
+
* options; picking one opens the OS file dialog already scoped to that
|
|
304
|
+
* entry's `accept` string.
|
|
305
|
+
*/
|
|
306
|
+
interface AttachmentTypeOption {
|
|
307
|
+
/** Stable identifier (used as React key). */
|
|
308
|
+
id: string;
|
|
309
|
+
/** Display label, e.g. "PDF", "Word", "Spreadsheet". */
|
|
310
|
+
label: string;
|
|
311
|
+
/**
|
|
312
|
+
* `accept` attribute passed to the file input when this option is
|
|
313
|
+
* picked. Examples: `".pdf"`, `".docx,.doc"`, `"image/*"`,
|
|
314
|
+
* `".xlsx,.xls,application/vnd.ms-excel"`.
|
|
315
|
+
*/
|
|
316
|
+
accept: string;
|
|
317
|
+
/** Optional second line, e.g. `".pdf"` or `"Word document"`. */
|
|
318
|
+
description?: string;
|
|
319
|
+
/** Optional leading icon, rendered to the start of the label. */
|
|
320
|
+
icon?: ReactNode;
|
|
321
|
+
}
|
|
322
|
+
interface AttachmentsConfig {
|
|
323
|
+
/**
|
|
324
|
+
* Show the generic "Attach file" picker button (paperclip icon). Honours
|
|
325
|
+
* `accept` so the OS dialog can include any allowed mime — images, PDFs,
|
|
326
|
+
* docs, audio, video, etc. Defaults to `true`.
|
|
327
|
+
*/
|
|
328
|
+
file?: boolean;
|
|
329
|
+
/**
|
|
330
|
+
* Show the dedicated "Add image" picker button (image icon). Forces
|
|
331
|
+
* `accept="image/*"` on the underlying input so iOS / Android open the
|
|
332
|
+
* camera-roll picker directly instead of the generic Files app — a UX win
|
|
333
|
+
* on mobile chat apps. **Defaults to `false`** because the paperclip
|
|
334
|
+
* already accepts images via its `accept` string; opt in when the second
|
|
335
|
+
* tap-target is worth it.
|
|
336
|
+
*/
|
|
337
|
+
image?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* Accept attribute string passed to the generic "Attach file" picker.
|
|
340
|
+
* Has no effect on the image-only picker (which is always `image/*`) or
|
|
341
|
+
* on `types` entries (which carry their own `accept`).
|
|
342
|
+
* Defaults to `"image/*,application/pdf,text/*,audio/*,video/*"`.
|
|
343
|
+
*/
|
|
344
|
+
accept?: string;
|
|
345
|
+
/**
|
|
346
|
+
* Pre-defined attachment types. When **omitted or empty** (the default),
|
|
347
|
+
* clicking the paperclip opens the OS file picker directly with
|
|
348
|
+
* `accept`. When **non-empty**, the paperclip becomes a popover trigger
|
|
349
|
+
* — clicking it shows the list, and picking an entry opens the dialog
|
|
350
|
+
* scoped to that entry's `accept`. Great for apps that want to nudge
|
|
351
|
+
* users toward specific formats (e.g. "PDF or Word only").
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```tsx
|
|
355
|
+
* features={{
|
|
356
|
+
* attachments: {
|
|
357
|
+
* types: [
|
|
358
|
+
* { id: "pdf", label: "PDF", accept: ".pdf", description: ".pdf" },
|
|
359
|
+
* { id: "word", label: "Word", accept: ".docx,.doc", description: ".docx, .doc" },
|
|
360
|
+
* { id: "image", label: "Image", accept: "image/*", description: "PNG, JPG, …" },
|
|
361
|
+
* ],
|
|
362
|
+
* },
|
|
363
|
+
* }}
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
types?: AttachmentTypeOption[];
|
|
367
|
+
/** Max bytes per file. Default 25 MiB. */
|
|
368
|
+
maxSize?: number;
|
|
369
|
+
/** Maximum total attachments. Default 10. */
|
|
370
|
+
maxCount?: number;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Behavioural switches for attachment lifecycle and submission rules.
|
|
374
|
+
* Lives at the top level (not under `features.attachments`) so the
|
|
375
|
+
* "is the feature on / what file types" config stays cleanly separated
|
|
376
|
+
* from the "what happens when the user attaches / sends" callbacks.
|
|
377
|
+
*/
|
|
378
|
+
interface AttachmentOptions {
|
|
379
|
+
/**
|
|
380
|
+
* When `true`, every newly-attached file is immediately handed to
|
|
381
|
+
* `onUpload` in the background. The attachment chip shows a spinner
|
|
382
|
+
* while the upload is in flight and a warning badge if it fails.
|
|
383
|
+
* Sending is blocked until every attachment is either `"uploaded"`
|
|
384
|
+
* or removed. Defaults to `false` — consumers receive raw `File`s in
|
|
385
|
+
* `onSend` and upload themselves.
|
|
386
|
+
*
|
|
387
|
+
* Has no effect unless `onUpload` is also provided.
|
|
388
|
+
*/
|
|
389
|
+
uploadFirst?: boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Upload handler invoked once per attached `File` when `uploadFirst`
|
|
392
|
+
* is on. Resolve `true` for success, `false` (or throw) for failure.
|
|
393
|
+
* For richer feedback (URL, server id, …), keep a side map in your app
|
|
394
|
+
* keyed by `file.name` + `file.size` — the composer will hand the
|
|
395
|
+
* same `File` instance back in `onSend`'s payload.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```tsx
|
|
399
|
+
* attachmentOptions={{
|
|
400
|
+
* uploadFirst: true,
|
|
401
|
+
* onUpload: async (file) => {
|
|
402
|
+
* const res = await fetch("/api/upload", { method: "POST", body: file });
|
|
403
|
+
* return res.ok;
|
|
404
|
+
* },
|
|
405
|
+
* }}
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
onUpload?: (file: File) => Promise<boolean> | boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Whether the user can submit a message that has attachments but no
|
|
411
|
+
* text. Defaults to `true` — matches Slack / Discord / WhatsApp where
|
|
412
|
+
* dropping a file is itself a valid message. Set to `false` to force
|
|
413
|
+
* users to write at least one character alongside any attachment.
|
|
414
|
+
*/
|
|
415
|
+
canSendOnlyAttachment?: boolean;
|
|
416
|
+
}
|
|
417
|
+
interface MermaidConfig {
|
|
418
|
+
/**
|
|
419
|
+
* Keep the raw ```mermaid source visible in the editor while the preview
|
|
420
|
+
* renders below. Defaults to `true` — the user sees both the code they
|
|
421
|
+
* wrote and the live diagram. Set to `false` to hide the source block
|
|
422
|
+
* once it parses (the diagram preview is still shown), useful when you
|
|
423
|
+
* only want the rendered output in the conversation surface.
|
|
424
|
+
*/
|
|
425
|
+
keepSource?: boolean;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Two visual contracts for inline / block markdown styling. Default is
|
|
429
|
+
* `"hybrid"` because it preserves the source byte-for-byte and is what
|
|
430
|
+
* the composer originally shipped with; consumers who want the more
|
|
431
|
+
* conventional "WYSIWYG-ish" feel opt into `"live"`.
|
|
432
|
+
*
|
|
433
|
+
* - `"hybrid"` — markers stay visible (rendered in a muted style) AND the
|
|
434
|
+
* inner text receives the matching format. Typing `**a**`
|
|
435
|
+
* leaves you with `**a**` on screen, the `**` dimmed and
|
|
436
|
+
* the `a` bold. This is what Slack / Discord / iMessage do.
|
|
437
|
+
* The document IS the markdown source.
|
|
438
|
+
*
|
|
439
|
+
* - `"live"` — markers are consumed as soon as the closing one is typed.
|
|
440
|
+
* `**a**` collapses to bold **a** with no asterisks left on
|
|
441
|
+
* screen; `# Title` becomes a styled paragraph with no
|
|
442
|
+
* leading `#`. This is what Notion / Tiptap / Lexical's
|
|
443
|
+
* own `MarkdownShortcutPlugin` do. The document is the
|
|
444
|
+
* *rendered* state; we reconstruct markdown only at submit.
|
|
445
|
+
*
|
|
446
|
+
* Both modes produce the same `markdown` payload on submit — the
|
|
447
|
+
* difference is purely how the editor LOOKS while you're typing.
|
|
448
|
+
*/
|
|
449
|
+
type MarkdownMode = "hybrid" | "live";
|
|
450
|
+
/**
|
|
451
|
+
* Fine-grained markdown configuration. Pass `markdown: true` (or omit it)
|
|
452
|
+
* to keep the default behaviour, or supply this object to switch modes /
|
|
453
|
+
* scope the change.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```tsx
|
|
457
|
+
* // Notion-like: markers vanish once matched.
|
|
458
|
+
* <Composer features={{ markdown: { mode: "live" } }} />
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
interface MarkdownConfig {
|
|
462
|
+
/** Visual mode shared by inline and block constructs. Default: `"hybrid"`. */
|
|
463
|
+
mode?: MarkdownMode;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Inline ghost-text autocomplete configuration. When the user's current
|
|
467
|
+
* text is a prefix of one of the suggestions, the remaining characters
|
|
468
|
+
* are rendered after the caret in a muted style. Pressing **Tab** accepts
|
|
469
|
+
* the suggestion (the remainder is inserted into the editor); Escape, a
|
|
470
|
+
* non-matching keystroke, or moving the caret away dismisses it.
|
|
471
|
+
*
|
|
472
|
+
* Operates on the editor's plain-text content (chips collapsed to their
|
|
473
|
+
* labels) and matches against the start of the document — perfect for
|
|
474
|
+
* search-bar–style inputs, command palettes, or templated prompts.
|
|
475
|
+
*
|
|
476
|
+
* Pass an array for the simplest case (`ghostedAutoComplete: [...]`) or
|
|
477
|
+
* a config object for finer control.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```tsx
|
|
481
|
+
* <Composer features={{ ghostedAutoComplete: ["My cat is playing", "Hello world"] }} />
|
|
482
|
+
* ```
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```tsx
|
|
486
|
+
* <Composer
|
|
487
|
+
* features={{
|
|
488
|
+
* ghostedAutoComplete: {
|
|
489
|
+
* suggestions: ["Summarize this thread", "Translate to English"],
|
|
490
|
+
* caseSensitive: false,
|
|
491
|
+
* minLength: 2,
|
|
492
|
+
* },
|
|
493
|
+
* }}
|
|
494
|
+
* />
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
interface GhostedAutoCompleteConfig {
|
|
498
|
+
/**
|
|
499
|
+
* Static list of completion suggestions. The first entry whose prefix
|
|
500
|
+
* matches the editor's current text wins — so order this list by
|
|
501
|
+
* priority / likelihood.
|
|
502
|
+
*/
|
|
503
|
+
suggestions: string[];
|
|
504
|
+
/**
|
|
505
|
+
* When `false` (default), matching is case-insensitive — `"my cat"`
|
|
506
|
+
* matches `"My cat is playing"`. Set to `true` to require an exact
|
|
507
|
+
* case match.
|
|
508
|
+
*/
|
|
509
|
+
caseSensitive?: boolean;
|
|
510
|
+
/**
|
|
511
|
+
* Minimum number of characters the user must have typed before a
|
|
512
|
+
* ghost suggestion is shown. Defaults to `1` — the ghost never
|
|
513
|
+
* appears on an empty editor (that's what `placeholder` is for).
|
|
514
|
+
*/
|
|
515
|
+
minLength?: number;
|
|
516
|
+
}
|
|
517
|
+
interface ComposerFeatures {
|
|
518
|
+
/** `true` (default) → hybrid mode. Pass a {@link MarkdownConfig} to opt
|
|
519
|
+
* into `"live"` (Notion-style) or otherwise customise behaviour. */
|
|
520
|
+
markdown?: boolean | MarkdownConfig;
|
|
521
|
+
attachments?: boolean | AttachmentsConfig;
|
|
522
|
+
mentions?: false | MentionConfig;
|
|
523
|
+
slashCommands?: false | SlashConfig;
|
|
524
|
+
voice?: boolean;
|
|
525
|
+
mermaid?: boolean | MermaidConfig;
|
|
526
|
+
web?: boolean;
|
|
527
|
+
/**
|
|
528
|
+
* Inline ghost-text autocomplete suggested from a list. Accepts a plain
|
|
529
|
+
* `string[]` shorthand or a {@link GhostedAutoCompleteConfig} for
|
|
530
|
+
* case-sensitivity / minimum-length tuning. Press **Tab** to accept the
|
|
531
|
+
* suggestion. Disabled by default.
|
|
532
|
+
*/
|
|
533
|
+
ghostedAutoComplete?: false | string[] | GhostedAutoCompleteConfig;
|
|
534
|
+
}
|
|
535
|
+
interface ComposerHandle {
|
|
536
|
+
focus(): void;
|
|
537
|
+
clear(): void;
|
|
538
|
+
insert(text: string): void;
|
|
539
|
+
submit(): void;
|
|
540
|
+
addAttachments(files: File[]): void;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Render-props received by a custom send-button slot. Returned by the
|
|
544
|
+
* library so consumer components can be completely chrome-free while still
|
|
545
|
+
* inheriting the gating logic (uploads pending, empty editor, streaming).
|
|
546
|
+
*
|
|
547
|
+
* `className` / `style` carry the resolved values from
|
|
548
|
+
* `classNames.sendButton` and `sx.sendButton`, so a consumer who wants
|
|
549
|
+
* "their button but with my colours" can just spread them; a consumer
|
|
550
|
+
* doing a from-scratch design can ignore both.
|
|
551
|
+
*/
|
|
552
|
+
interface SendButtonRenderProps {
|
|
553
|
+
/** True when the editor has something sendable (text, or attachments
|
|
554
|
+
* if `canSendOnlyAttachment` is true) AND no uploads are pending or
|
|
555
|
+
* failed. Same gate the default button uses for its disabled state. */
|
|
556
|
+
canSend: boolean;
|
|
557
|
+
/** Invoke this to send. Matches the internal submit pipeline 1:1 —
|
|
558
|
+
* serializes editor state, fires `onSend`, clears, etc. */
|
|
559
|
+
onSend: () => void;
|
|
560
|
+
/** Resolved className from `classNames.sendButton` + sx-derived classes. */
|
|
561
|
+
className?: string;
|
|
562
|
+
/** Resolved inline styles from `sx.sendButton` tokens. */
|
|
563
|
+
style?: CSSProperties;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Render-props received by a custom stop-button slot. Only mounted while
|
|
567
|
+
* `isStreaming` is true on `<Composer />`.
|
|
568
|
+
*/
|
|
569
|
+
interface StopButtonRenderProps {
|
|
570
|
+
/** Invoke this to ask the host to stop generation; fires the
|
|
571
|
+
* `onStop` prop on `<Composer />`. */
|
|
572
|
+
onStop: () => void;
|
|
573
|
+
/** Resolved className from `classNames.stopButton` + sx-derived classes. */
|
|
574
|
+
className?: string;
|
|
575
|
+
/** Resolved inline styles from `sx.stopButton` tokens. */
|
|
576
|
+
style?: CSSProperties;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Component-level escape hatches. When a slot is provided the library skips
|
|
580
|
+
* rendering its own element and renders yours instead, passing the same
|
|
581
|
+
* runtime data it would have used internally (callbacks, gating flags,
|
|
582
|
+
* resolved styles). Slots compose with `icons`, `classNames`, and `sx` —
|
|
583
|
+
* use whichever level matches the depth of customization you need:
|
|
584
|
+
*
|
|
585
|
+
* icons — swap the SVG inside the default button
|
|
586
|
+
* classNames — append classes to the default button
|
|
587
|
+
* sx — token-driven inline styles on the default button
|
|
588
|
+
* slots — replace the entire button (or other slot) wholesale
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```tsx
|
|
592
|
+
* <Composer
|
|
593
|
+
* slots={{
|
|
594
|
+
* sendButton: ({ canSend, onSend, className }) => (
|
|
595
|
+
* <button
|
|
596
|
+
* type="button"
|
|
597
|
+
* disabled={!canSend}
|
|
598
|
+
* onClick={onSend}
|
|
599
|
+
* className={`flex items-center gap-2 rounded-xl bg-emerald-600 px-3 py-1.5 text-white disabled:opacity-40 ${className ?? ""}`}
|
|
600
|
+
* >
|
|
601
|
+
* <span>Send</span>
|
|
602
|
+
* <kbd className="text-xs opacity-75">⏎</kbd>
|
|
603
|
+
* </button>
|
|
604
|
+
* ),
|
|
605
|
+
* }}
|
|
606
|
+
* />
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
interface ComposerSlots {
|
|
610
|
+
/** Replace the send button. Receives {@link SendButtonRenderProps}. */
|
|
611
|
+
sendButton?: ComponentType<SendButtonRenderProps>;
|
|
612
|
+
/** Replace the stop button (rendered while `isStreaming`).
|
|
613
|
+
* Receives {@link StopButtonRenderProps}. */
|
|
614
|
+
stopButton?: ComponentType<StopButtonRenderProps>;
|
|
615
|
+
}
|
|
616
|
+
interface ComposerProps {
|
|
617
|
+
/** Initial markdown to seed the editor. */
|
|
618
|
+
initialValue?: string;
|
|
619
|
+
/** Called when the user submits. */
|
|
620
|
+
onSend?: (payload: ComposerSubmitPayload) => void;
|
|
621
|
+
/** Called when the stop button is clicked while `isStreaming`. */
|
|
622
|
+
onStop?: () => void;
|
|
623
|
+
isStreaming?: boolean;
|
|
624
|
+
/** Focus the editor on mount. Defaults to `false`. */
|
|
625
|
+
autoFocus?: boolean;
|
|
626
|
+
/**
|
|
627
|
+
* Return focus to the editor after a successful send. Defaults to `true`.
|
|
628
|
+
*
|
|
629
|
+
* Sends triggered by the keyboard (Enter, Cmd/Ctrl+Enter) already keep
|
|
630
|
+
* focus naturally; this prop guarantees the same after Send-button
|
|
631
|
+
* clicks, quick-prompt `sendValue`, or imperative `ref.submit()` so the
|
|
632
|
+
* user can keep typing without a second click. Set to `false` if your
|
|
633
|
+
* UX moves focus elsewhere on send (e.g. a confirmation modal).
|
|
634
|
+
*/
|
|
635
|
+
refocusOnSubmit?: boolean;
|
|
636
|
+
/**
|
|
637
|
+
* Global keyboard shortcut that focuses the composer from anywhere on
|
|
638
|
+
* the page. Defaults to `"mod+/"` — `mod` resolves to ⌘ on macOS and
|
|
639
|
+
* Ctrl on Windows/Linux. Pass any combo of `mod` / `cmd` / `meta` /
|
|
640
|
+
* `ctrl` / `alt` / `option` / `shift` plus a single key, separated by
|
|
641
|
+
* `+` (e.g. `"mod+k"`, `"shift+mod+/"`, `"alt+l"`).
|
|
642
|
+
*
|
|
643
|
+
* Set to `false` (or `null`) to disable. The listener is registered on
|
|
644
|
+
* `window` and ignored while a popover / mention / slash menu intercepts
|
|
645
|
+
* the key first.
|
|
646
|
+
*/
|
|
647
|
+
focusShortcut?: string | false | null;
|
|
648
|
+
placeholder?: string;
|
|
649
|
+
/**
|
|
650
|
+
* Shorthand for `classNames.root`. Kept for back-compat; if both are set,
|
|
651
|
+
* the two are merged (`className` first, then `classNames.root`).
|
|
652
|
+
*/
|
|
653
|
+
className?: string;
|
|
654
|
+
/**
|
|
655
|
+
* Per-slot className overrides. See {@link ComposerSlotClassNames}.
|
|
656
|
+
*/
|
|
657
|
+
classNames?: ComposerSlotClassNames;
|
|
658
|
+
/**
|
|
659
|
+
* Per-slot inline-style overrides converted by the lightweight sx engine.
|
|
660
|
+
* See {@link ComposerSxMap}.
|
|
661
|
+
*/
|
|
662
|
+
sx?: ComposerSxMap;
|
|
663
|
+
/**
|
|
664
|
+
* Standard React `style` applied to the outer root in addition to (and
|
|
665
|
+
* after) any `sx.root` resolved values. Use for dimensions/positioning of
|
|
666
|
+
* the composer as a whole.
|
|
667
|
+
*/
|
|
668
|
+
style?: CSSProperties;
|
|
669
|
+
/**
|
|
670
|
+
* Design tokens applied as inline CSS custom properties on the root, so
|
|
671
|
+
* they cascade into every slot — including the package's built-in CSS —
|
|
672
|
+
* without leaking to the rest of the app. See {@link ComposerTokens}.
|
|
673
|
+
*/
|
|
674
|
+
tokens?: ComposerTokens;
|
|
675
|
+
/**
|
|
676
|
+
* Single brand color shorthand. Accepts any of:
|
|
677
|
+
* - HSL components (preferred): `"258 90% 62%"`
|
|
678
|
+
* - hex: `"#7c3aed"` / `"#abc"`
|
|
679
|
+
* - `rgb(...)` / `rgba(...)`
|
|
680
|
+
* - `hsl(...)` / `hsla(...)`
|
|
681
|
+
*
|
|
682
|
+
* Internally derives `--primary`, `--primary-foreground`, `--accent`,
|
|
683
|
+
* `--accent-foreground`, and `--ring` from this one value. That re-tints
|
|
684
|
+
* every "hot" surface — hover backgrounds, selected menu rows, mention
|
|
685
|
+
* chips, the mention-list avatar, the Web pill, focus ring — without
|
|
686
|
+
* touching the neutral chrome (card, border, foreground text).
|
|
687
|
+
*
|
|
688
|
+
* Anything you also pass via `tokens` overrides the derived value, so
|
|
689
|
+
* `color` is just a sensible default you can fine-tune.
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
692
|
+
* ```tsx
|
|
693
|
+
* <Composer color="#7c3aed" />
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
color?: string;
|
|
697
|
+
/** Hint text under the composer. `false` hides; `true` shows default. */
|
|
698
|
+
hint?: boolean | ReactNode;
|
|
699
|
+
/**
|
|
700
|
+
* Editor mode.
|
|
701
|
+
* - `"markdown"` (default): rich-text editor that understands markdown.
|
|
702
|
+
* Enables block shortcuts (`# `, `- `, `> `, ```` ``` ````), inline
|
|
703
|
+
* live styling (`**bold**`, `*italic*`, etc.), mermaid previews, and
|
|
704
|
+
* paste-as-rich-text. The serialized payload contains markdown.
|
|
705
|
+
* - `"text"`: plain-text editor. No markdown styling, no block shortcuts,
|
|
706
|
+
* no mermaid. Pasted content is reduced to plain text. The serialized
|
|
707
|
+
* `markdown` field of `onSend` simply equals the text.
|
|
708
|
+
*
|
|
709
|
+
* Mentions, slash commands, attachments and voice are independent of
|
|
710
|
+
* `mode` and work in both.
|
|
711
|
+
*/
|
|
712
|
+
mode?: "markdown" | "text";
|
|
713
|
+
/** Toggle / configure built-in plugins. */
|
|
714
|
+
features?: ComposerFeatures;
|
|
715
|
+
/** Extra controls rendered after the built-in toolbar buttons. */
|
|
716
|
+
toolbarExtras?: ReactNode;
|
|
717
|
+
/**
|
|
718
|
+
* Close any open typeahead menu (slash, mentions) when the user clicks
|
|
719
|
+
* or taps outside the composer. Defaults to `true`. Set `false` to keep
|
|
720
|
+
* the menu open until the user dismisses it explicitly (Escape, selecting
|
|
721
|
+
* an item, or moving the caret past the trigger).
|
|
722
|
+
*/
|
|
723
|
+
closeMenusOnOutsideClick?: boolean;
|
|
724
|
+
/**
|
|
725
|
+
* Whether the editor is allowed to hold more than one line of content.
|
|
726
|
+
* Defaults to `true`. When `false`, the composer behaves like a
|
|
727
|
+
* single-line input: Enter never inserts a newline (Shift+Enter is also
|
|
728
|
+
* suppressed), and `smartNewline` is implicitly disabled.
|
|
729
|
+
*/
|
|
730
|
+
multiline?: boolean;
|
|
731
|
+
/**
|
|
732
|
+
* Whether pressing Enter (no modifiers) submits the message. Defaults to
|
|
733
|
+
* `true`. When `false`, Enter only inserts a newline (assuming
|
|
734
|
+
* `multiline` is `true`); the user can still submit with
|
|
735
|
+
* Cmd/Ctrl+Enter or via the Send button / imperative `ref.submit()`.
|
|
736
|
+
*/
|
|
737
|
+
submitOnEnter?: boolean;
|
|
738
|
+
/**
|
|
739
|
+
* Smart "context-aware" Enter behavior. Defaults to `true`. Only takes
|
|
740
|
+
* effect when `multiline` is `true`.
|
|
741
|
+
*
|
|
742
|
+
* - While the editor holds a single line, Enter submits as usual
|
|
743
|
+
* (subject to `submitOnEnter`).
|
|
744
|
+
* - As soon as the editor contains more than one line, Enter inserts
|
|
745
|
+
* a newline instead of submitting, and the user must press
|
|
746
|
+
* Cmd/Ctrl+Enter (or click Send) to submit. This prevents
|
|
747
|
+
* accidental sends while composing longer messages.
|
|
748
|
+
* - In markdown mode, when the cursor sits inside a list paragraph
|
|
749
|
+
* (`- `, `* `, `+ `, or `N. `), Enter continues the list with the
|
|
750
|
+
* next marker (bullet character preserved, numbers
|
|
751
|
+
* auto-incremented). Pressing Enter on an empty list item exits
|
|
752
|
+
* the list — the marker is cleared and the cursor stays on the
|
|
753
|
+
* now-plain line.
|
|
754
|
+
*
|
|
755
|
+
* Set to `false` to keep Enter's behavior fixed regardless of how many
|
|
756
|
+
* lines the user has typed or whether they're inside a list.
|
|
757
|
+
*/
|
|
758
|
+
smartNewline?: boolean;
|
|
759
|
+
/**
|
|
760
|
+
* Override any built-in icon with your own React component. The library
|
|
761
|
+
* ships small lucide-style SVGs by default; provide your own to match
|
|
762
|
+
* your design system (Heroicons, Phosphor, Material, etc). Any slot you
|
|
763
|
+
* leave out keeps its default.
|
|
764
|
+
*
|
|
765
|
+
* @example
|
|
766
|
+
* ```tsx
|
|
767
|
+
* <Composer icons={{ send: MyArrowIcon, voice: MyMicIcon }} />
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
icons?: Partial<ComposerIcons>;
|
|
771
|
+
/**
|
|
772
|
+
* Component-level overrides for chrome pieces (currently `sendButton`
|
|
773
|
+
* and `stopButton`). Each entry, when provided, REPLACES the library's
|
|
774
|
+
* default element while still receiving the runtime data it would have
|
|
775
|
+
* used (callbacks, gating flags, resolved styles). See {@link ComposerSlots}
|
|
776
|
+
* and the per-slot render-prop interfaces ({@link SendButtonRenderProps},
|
|
777
|
+
* {@link StopButtonRenderProps}) for the contract.
|
|
778
|
+
*
|
|
779
|
+
* Composes with `icons` / `classNames` / `sx`: use those for cosmetic
|
|
780
|
+
* tweaks, reach for `slots` only when you need different DOM, behaviour,
|
|
781
|
+
* or accessibility wrapping (e.g. a tooltip-wrapped button, a split
|
|
782
|
+
* "Send / Send & schedule" dropdown, a labelled pill).
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```tsx
|
|
786
|
+
* <Composer
|
|
787
|
+
* slots={{
|
|
788
|
+
* sendButton: ({ canSend, onSend }) => (
|
|
789
|
+
* <MyBrandButton disabled={!canSend} onClick={onSend}>
|
|
790
|
+
* Send <kbd>⏎</kbd>
|
|
791
|
+
* </MyBrandButton>
|
|
792
|
+
* ),
|
|
793
|
+
* }}
|
|
794
|
+
* />
|
|
795
|
+
* ```
|
|
796
|
+
*/
|
|
797
|
+
slots?: ComposerSlots;
|
|
798
|
+
/**
|
|
799
|
+
* Render mermaid (or other future diagram-language) fences yourself instead
|
|
800
|
+
* of relying on the lazy-imported `mermaid` package. Recommended for
|
|
801
|
+
* production apps that already have a diagram pipeline — set this and you
|
|
802
|
+
* can omit `mermaid` from your install entirely.
|
|
803
|
+
*
|
|
804
|
+
* Receives the raw fenced code and the language tag, returns any ReactNode.
|
|
805
|
+
*
|
|
806
|
+
* @example
|
|
807
|
+
* ```tsx
|
|
808
|
+
* <Composer
|
|
809
|
+
* renderDiagram={({ code }) => <MyDiagram source={code} />}
|
|
810
|
+
* />
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
813
|
+
renderDiagram?: DiagramRenderer;
|
|
814
|
+
/**
|
|
815
|
+
* "Starter" prompts shown as a clickable chip row above the composer.
|
|
816
|
+
* Clicking a chip either fills the editor (`behavior: "initValue"`) or
|
|
817
|
+
* fills + submits (`behavior: "sendValue"`, default). Useful as a
|
|
818
|
+
* zero-typing entry point on an empty chat surface.
|
|
819
|
+
*
|
|
820
|
+
* @example
|
|
821
|
+
* ```tsx
|
|
822
|
+
* <Composer
|
|
823
|
+
* prompts={{
|
|
824
|
+
* items: [
|
|
825
|
+
* "Summarize today's stand-up",
|
|
826
|
+
* "Draft a release email",
|
|
827
|
+
* "Find bugs in this snippet",
|
|
828
|
+
* "Brainstorm names for my project",
|
|
829
|
+
* "Plan next sprint",
|
|
830
|
+
* ],
|
|
831
|
+
* maxToShow: 3, // show 3 chips out of 5
|
|
832
|
+
* randomize: true, // pick a different 3 each mount
|
|
833
|
+
* behavior: "sendValue", // click → fill + submit
|
|
834
|
+
* onSelect: (p) => track("prompt_picked", { p }),
|
|
835
|
+
* }}
|
|
836
|
+
* />
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
prompts?: ComposerPromptsConfig;
|
|
840
|
+
/**
|
|
841
|
+
* Attachment lifecycle and submission rules — `uploadFirst` for
|
|
842
|
+
* server-side upload pipelines (spinner + warning chip states),
|
|
843
|
+
* `onUpload` for the callback, `canSendOnlyAttachment` to require
|
|
844
|
+
* text alongside attachments. See {@link AttachmentOptions}.
|
|
845
|
+
*
|
|
846
|
+
* @example
|
|
847
|
+
* ```tsx
|
|
848
|
+
* <Composer
|
|
849
|
+
* features={{ attachments: true }}
|
|
850
|
+
* attachmentOptions={{
|
|
851
|
+
* uploadFirst: true,
|
|
852
|
+
* onUpload: async (file) => (await fetch("/api/upload", { method: "POST", body: file })).ok,
|
|
853
|
+
* canSendOnlyAttachment: false,
|
|
854
|
+
* }}
|
|
855
|
+
* />
|
|
856
|
+
* ```
|
|
857
|
+
*/
|
|
858
|
+
attachmentOptions?: AttachmentOptions;
|
|
859
|
+
/**
|
|
860
|
+
* Writing direction. The composer's chrome (toolbar, attachment tray,
|
|
861
|
+
* popovers, hint, mention chips, send button) is laid out with CSS
|
|
862
|
+
* logical properties so a single attribute flips left↔right correctly.
|
|
863
|
+
*
|
|
864
|
+
* - `"ltr"` (default behaviour when omitted): left-to-right.
|
|
865
|
+
* - `"rtl"`: right-to-left — Arabic, Hebrew, Persian, Urdu, …
|
|
866
|
+
* - `"auto"`: the browser picks per-paragraph based on the first
|
|
867
|
+
* strong character. Useful for chat surfaces that mix scripts.
|
|
868
|
+
*
|
|
869
|
+
* Applied to the outer composer root *and* the Lexical contenteditable so
|
|
870
|
+
* caret movement, text alignment, and per-line direction (in `"auto"`
|
|
871
|
+
* mode) all behave correctly.
|
|
872
|
+
*
|
|
873
|
+
* @example
|
|
874
|
+
* ```tsx
|
|
875
|
+
* <Composer dir="rtl" placeholder="اكتب رسالة..." onSend={...} />
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
878
|
+
dir?: "ltr" | "rtl" | "auto";
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* ComposeAI — a Lexical-powered, plugin-driven rich input designed for
|
|
883
|
+
* chat / AI assistant interfaces. Internally stateful: parents only listen via
|
|
884
|
+
* `onSend` and (optionally) hold an imperative `ref`.
|
|
885
|
+
*/
|
|
886
|
+
declare const Composer: react.ForwardRefExoticComponent<ComposerProps & react.RefAttributes<ComposerHandle>>;
|
|
887
|
+
|
|
888
|
+
interface SuggestionRowProps {
|
|
889
|
+
/** Suggestion labels. Clicking one calls `onSelect` with that string. */
|
|
890
|
+
items: string[];
|
|
891
|
+
onSelect: (value: string) => void;
|
|
892
|
+
className?: string;
|
|
893
|
+
}
|
|
894
|
+
declare function SuggestionRow({ items, onSelect, className }: SuggestionRowProps): react.JSX.Element;
|
|
895
|
+
|
|
896
|
+
export { type Attachment, type AttachmentKind, type AttachmentOptions, type AttachmentStatus, type AttachmentTypeOption, type AttachmentsConfig, Composer, type ComposerFeatures, type ComposerHandle, type ComposerIcons, type ComposerPromptBehavior, type ComposerPromptsConfig, type ComposerProps, type ComposerSlot, type ComposerSlotClassNames, type ComposerSlots, type ComposerSubmitPayload, type ComposerSxMap, type ComposerSxValue, type ComposerTokens, type DiagramRenderer, type GhostedAutoCompleteConfig, type IconComponent, type IconProps, type MarkdownConfig, type MarkdownMode, type MentionConfig, type MentionItem, type MentionRef, type MermaidConfig, type SendButtonRenderProps, type SlashCommand, type SlashCommandContext, type SlashConfig, type StopButtonRenderProps, SuggestionRow, type SuggestionRowProps };
|