@vvedantb/ui 0.17.1 → 0.18.0
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 +251 -3
- package/package.json +13 -5
- package/shell/src/SidebarResize.tsx +1 -4
- package/src/ai-elements/__snapshots__/queue.test.tsx.snap +11 -0
- package/src/ai-elements/activity-question-utils.ts +26 -0
- package/src/ai-elements/activity-question.tsx +159 -0
- package/src/ai-elements/activity-shared.tsx +459 -0
- package/src/ai-elements/activity-step-detail.tsx +115 -0
- package/src/ai-elements/activity-step-label.ts +981 -0
- package/src/ai-elements/activity-tasks-utils.ts +199 -0
- package/src/ai-elements/activity-tasks.tsx +770 -0
- package/src/ai-elements/artifact.tsx +162 -52
- package/src/ai-elements/attachments.tsx +62 -32
- package/src/ai-elements/code-block.tsx +76 -22
- package/src/ai-elements/context.tsx +370 -0
- package/src/ai-elements/conversation.tsx +186 -62
- package/src/ai-elements/file-chip.tsx +53 -0
- package/src/ai-elements/message.tsx +161 -50
- package/src/ai-elements/model-picker-content.tsx +505 -0
- package/src/ai-elements/model-picker-types.ts +56 -0
- package/src/ai-elements/model-picker.tsx +301 -0
- package/src/ai-elements/model-selector.tsx +102 -21
- package/src/ai-elements/plan.tsx +130 -0
- package/src/ai-elements/prompt-input-settings.tsx +144 -0
- package/src/ai-elements/prompt-input-speech.tsx +189 -0
- package/src/ai-elements/prompt-input-traits.tsx +281 -0
- package/src/ai-elements/prompt-input.tsx +320 -134
- package/src/ai-elements/provider-icon.tsx +133 -0
- package/src/ai-elements/queue.tsx +293 -9
- package/src/ai-elements/shimmer.tsx +72 -1
- package/src/ai-elements/simple-model-ladder.tsx +108 -0
- package/src/ai-elements/test-results.tsx +510 -0
- package/src/ai-elements/trace.tsx +243 -0
- package/src/ai-elements/use-streaming-disclosure.ts +50 -0
- package/src/ai-elements/web-preview.tsx +349 -0
- package/src/composer/input-area/ComposerSlashMenu.tsx +1 -1
- package/src/index.ts +66 -12
- package/src/kibo/avatar-stack.tsx +40 -0
- package/src/kibo/contribution-graph.tsx +539 -0
- package/src/kibo/cursor.tsx +72 -0
- package/src/kibo/gantt-features.tsx +439 -0
- package/src/kibo/gantt-header.tsx +181 -0
- package/src/kibo/gantt-provider.tsx +657 -0
- package/src/kibo/gantt-sidebar.tsx +157 -0
- package/src/kibo/gantt-timeline.tsx +154 -0
- package/src/kibo/index.ts +151 -0
- package/src/kibo/kanban.tsx +338 -0
- package/src/kibo/kanbanDropTarget.ts +25 -0
- package/src/kibo/list.tsx +162 -0
- package/src/kibo/theme-switcher.tsx +109 -0
- package/src/kibo/video-player.tsx +237 -0
- package/src/markdown/Markdown.tsx +112 -0
- package/src/markdown/MarkdownBlockquote.tsx +45 -0
- package/src/markdown/MarkdownCodeBlock.tsx +197 -0
- package/src/markdown/MarkdownInlineCode.tsx +40 -0
- package/src/markdown/MarkdownLink.tsx +92 -0
- package/src/markdown/codeFence.ts +57 -0
- package/src/markdown/codeHighlight.ts +138 -0
- package/src/markdown/incrementalBlocks.ts +182 -0
- package/src/markdown/index.ts +3 -0
- package/src/markdown/markdownClipboard.ts +178 -0
- package/src/markdown/mermaid.ts +155 -0
- package/src/markdown/rehypeGithubAlerts.ts +80 -0
- package/src/motion/presets.ts +24 -0
- package/src/ui/__snapshots__/alt-wrappers.test.tsx.snap +19 -0
- package/src/ui/__snapshots__/sonner-dom.test.tsx.snap +5 -0
- package/src/ui/_menu-parts.tsx +29 -0
- package/src/ui/accordion.alt.tsx +46 -53
- package/src/ui/accordion.tsx +59 -10
- package/src/ui/avatar.tsx +56 -0
- package/src/ui/badge.tsx +45 -21
- package/src/ui/border-beam.tsx +97 -0
- package/src/ui/button-group.tsx +50 -21
- package/src/ui/button.tsx +67 -45
- package/src/ui/calendar.tsx +48 -5
- package/src/ui/card.tsx +77 -8
- package/src/ui/carousel.tsx +60 -0
- package/src/ui/centered-spinner.tsx +30 -0
- package/src/ui/checkbox.tsx +34 -16
- package/src/ui/clear-input.alt.tsx +33 -75
- package/src/ui/clear-input.tsx +140 -15
- package/src/ui/collapsible.tsx +40 -2
- package/src/ui/command.tsx +143 -70
- package/src/ui/context-menu.tsx +159 -93
- package/src/ui/crossfade-icon.tsx +123 -0
- package/src/ui/dialog.tsx +154 -70
- package/src/ui/dropdown-menu.tsx +177 -117
- package/src/ui/hover-card.tsx +59 -17
- package/src/ui/input-group.alt.tsx +83 -83
- package/src/ui/input-group.tsx +129 -58
- package/src/ui/input.tsx +13 -4
- package/src/ui/label.tsx +11 -8
- package/src/ui/list-row.tsx +232 -0
- package/src/ui/loading-state.tsx +215 -0
- package/src/ui/popover.tsx +56 -23
- package/src/ui/progress.tsx +28 -16
- package/src/ui/refresh-spin-icon.tsx +41 -0
- package/src/ui/search-input.tsx +66 -15
- package/src/ui/select.tsx +171 -100
- package/src/ui/separator.tsx +23 -14
- package/src/ui/slider.tsx +21 -7
- package/src/ui/sonner.tsx +68 -30
- package/src/ui/spinner.tsx +229 -11
- package/src/ui/status-dot.tsx +84 -0
- package/src/ui/surface.tsx +49 -0
- package/src/ui/switch.tsx +29 -15
- package/src/ui/table.tsx +38 -16
- package/src/ui/tabs.tsx +123 -35
- package/src/ui/tabsSliding.ts +33 -1
- package/src/ui/textarea.tsx +13 -4
- package/src/ui/tooltip.tsx +50 -14
- package/src/ui/ui-config.tsx +149 -0
- package/src/utils/cat-color.ts +123 -0
- package/src/utils/dayjs.ts +6 -0
- package/src/utils/gesture.ts +59 -0
- package/src/utils/runtimeVisibility.ts +167 -0
- package/src/utils/sharedClock.ts +96 -0
- package/src/utils/surface-radius.ts +16 -0
- package/src/utils/useDragSensors.ts +58 -0
- package/tokens/examples/tokens-moved/Fixture.tsx +283 -0
- package/tokens/examples/tokens-moved/README.md +20 -0
- package/tokens/examples/tokens-moved/index.html +12 -0
- package/tokens/examples/tokens-moved/main.tsx +6 -0
- package/tokens/examples/tokens-moved/package.json +14 -0
- package/tokens/examples/tokens-moved/styles.css +12 -0
- package/tokens/examples/tokens-moved/tsconfig.json +14 -0
- package/tokens/examples/tokens-moved/vite.config.ts +11 -0
- package/tokens/src/css/border-beam.css +116 -0
- package/tokens/src/css/markdown.css +318 -0
|
@@ -47,9 +47,10 @@ import {
|
|
|
47
47
|
SelectTrigger,
|
|
48
48
|
SelectValue,
|
|
49
49
|
} from "../ui/radix-select";
|
|
50
|
+
import { CrossfadeIconSlot } from "../ui/crossfade-icon";
|
|
50
51
|
import { Spinner } from "../ui/spinner";
|
|
51
52
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
|
52
|
-
import {
|
|
53
|
+
import { useCn, useUiConfig } from "../ui/ui-config";
|
|
53
54
|
import { segmentExpandedStateClass } from "../ui/surface-classes";
|
|
54
55
|
import {
|
|
55
56
|
IconCornerDownLeft,
|
|
@@ -67,6 +68,21 @@ import {
|
|
|
67
68
|
useRef,
|
|
68
69
|
useState,
|
|
69
70
|
} from "react";
|
|
71
|
+
/** `existing` with `file` in its place when the ids match (old blob URL revoked). */
|
|
72
|
+
const replaceFile = (
|
|
73
|
+
existing: FileUIPart & { id: string },
|
|
74
|
+
id: string,
|
|
75
|
+
file: File,
|
|
76
|
+
): FileUIPart & { id: string } => {
|
|
77
|
+
if (existing.id !== id) return existing;
|
|
78
|
+
if (existing.url) URL.revokeObjectURL(existing.url);
|
|
79
|
+
return {
|
|
80
|
+
...existing,
|
|
81
|
+
filename: file.name,
|
|
82
|
+
mediaType: file.type,
|
|
83
|
+
url: URL.createObjectURL(file),
|
|
84
|
+
};
|
|
85
|
+
};
|
|
70
86
|
const convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {
|
|
71
87
|
try {
|
|
72
88
|
const response = await fetch(url);
|
|
@@ -81,12 +97,39 @@ const convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {
|
|
|
81
97
|
return null;
|
|
82
98
|
}
|
|
83
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* `UiConfig.PromptInput`. `classes` replace built-in classes: `group` (the
|
|
102
|
+
* input group, default `overflow-hidden`), `groupIdle` / `groupDragOver`
|
|
103
|
+
* (added while no drag / an accepted drag is over the form), `groupDefault`
|
|
104
|
+
* (added when no `containerClassName` is given), `textarea`, `footer`,
|
|
105
|
+
* `submit` and `tabItem`. `submitIcons` replace the submit
|
|
106
|
+
* button's icon per status; `submitCrossfade` cross-fades between them.
|
|
107
|
+
* `menuTriggerAsChild` makes `PromptInputActionMenuTrigger` the trigger
|
|
108
|
+
* itself rather than a button inside the menu's trigger button.
|
|
109
|
+
*/
|
|
110
|
+
export interface PromptInputConfig {
|
|
111
|
+
classes?: {
|
|
112
|
+
group?: string;
|
|
113
|
+
groupIdle?: string;
|
|
114
|
+
groupDragOver?: string;
|
|
115
|
+
groupDefault?: string;
|
|
116
|
+
textarea?: string;
|
|
117
|
+
footer?: string;
|
|
118
|
+
submit?: string;
|
|
119
|
+
tabItem?: string;
|
|
120
|
+
};
|
|
121
|
+
submitIcons?: Partial<Record<"send" | "submitted" | "streaming" | "error", ReactNode>>;
|
|
122
|
+
submitCrossfade?: boolean;
|
|
123
|
+
menuTriggerAsChild?: boolean;
|
|
124
|
+
}
|
|
84
125
|
export interface AttachmentsContext {
|
|
85
126
|
files: (FileUIPart & {
|
|
86
127
|
id: string;
|
|
87
128
|
})[];
|
|
88
129
|
add: (files: File[] | FileList) => void;
|
|
89
130
|
remove: (id: string) => void;
|
|
131
|
+
/** Replace one attachment in place (same id and position). No re-validation. */
|
|
132
|
+
replace: (id: string, file: File) => void;
|
|
90
133
|
clear: () => void;
|
|
91
134
|
openFileDialog: () => void;
|
|
92
135
|
fileInputRef: RefObject<HTMLInputElement | null>;
|
|
@@ -119,7 +162,8 @@ export const usePromptInputController = () => {
|
|
|
119
162
|
}
|
|
120
163
|
return ctx;
|
|
121
164
|
};
|
|
122
|
-
|
|
165
|
+
/** The provider's controller, or `null` outside `PromptInputProvider`. */
|
|
166
|
+
export const useOptionalPromptInputController = () =>
|
|
123
167
|
useContext(PromptInputController);
|
|
124
168
|
export const useProviderAttachments = () => {
|
|
125
169
|
const ctx = useContext(ProviderAttachmentsContext);
|
|
@@ -169,6 +213,9 @@ export const PromptInputProvider = ({
|
|
|
169
213
|
return prev.filter((f) => f.id !== id);
|
|
170
214
|
});
|
|
171
215
|
};
|
|
216
|
+
const replace = (id: string, file: File) => {
|
|
217
|
+
setAttachmentFiles((prev) => prev.map((existing) => replaceFile(existing, id, file)));
|
|
218
|
+
};
|
|
172
219
|
const clear = () => {
|
|
173
220
|
setAttachmentFiles((prev) => {
|
|
174
221
|
for (const f of prev) {
|
|
@@ -199,6 +246,7 @@ export const PromptInputProvider = ({
|
|
|
199
246
|
files: attachmentFiles,
|
|
200
247
|
openFileDialog,
|
|
201
248
|
remove,
|
|
249
|
+
replace,
|
|
202
250
|
};
|
|
203
251
|
const __registerFileInput = (
|
|
204
252
|
ref: RefObject<HTMLInputElement | null>,
|
|
@@ -289,7 +337,16 @@ export type PromptInputProps = Omit<
|
|
|
289
337
|
syncHiddenInput?: boolean;
|
|
290
338
|
maxFiles?: number;
|
|
291
339
|
maxFileSize?: number;
|
|
340
|
+
/** Merged onto the inner `InputGroup`. */
|
|
292
341
|
containerClassName?: string;
|
|
342
|
+
/** Same as `containerClassName`. */
|
|
343
|
+
inputGroupClassName?: string;
|
|
344
|
+
/**
|
|
345
|
+
* Accepts a non-file drag of this MIME type: it counts as a drag over the
|
|
346
|
+
* form (the `groupDragOver` classes) and `onDrop` receives its string
|
|
347
|
+
* payload. Other types keep the browser's default drop.
|
|
348
|
+
*/
|
|
349
|
+
dataDrop?: { type: string; onDrop: (payload: string) => void };
|
|
293
350
|
onError?: (err: {
|
|
294
351
|
code: "max_files" | "max_file_size" | "accept";
|
|
295
352
|
message: string;
|
|
@@ -308,15 +365,23 @@ export const PromptInput = ({
|
|
|
308
365
|
maxFiles,
|
|
309
366
|
maxFileSize,
|
|
310
367
|
containerClassName,
|
|
368
|
+
inputGroupClassName,
|
|
369
|
+
dataDrop,
|
|
311
370
|
onError,
|
|
312
371
|
onSubmit,
|
|
313
372
|
children,
|
|
314
373
|
...props
|
|
315
374
|
}: PromptInputProps) => {
|
|
375
|
+
const classes = useUiConfig("PromptInput")?.classes;
|
|
376
|
+
const merge = useCn();
|
|
316
377
|
const controller = useOptionalPromptInputController();
|
|
317
378
|
const usingProvider = !!controller;
|
|
318
379
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
319
380
|
const formRef = useRef<HTMLFormElement | null>(null);
|
|
381
|
+
// Enter/leave pairs, not a boolean: `dragleave` also fires when the pointer
|
|
382
|
+
// moves onto a child of the form.
|
|
383
|
+
const dragEnterDepth = useRef(0);
|
|
384
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
320
385
|
const [items, setItems] = useState<
|
|
321
386
|
(FileUIPart & {
|
|
322
387
|
id: string;
|
|
@@ -346,6 +411,10 @@ export const PromptInput = ({
|
|
|
346
411
|
const prefix = pattern.slice(0, -1);
|
|
347
412
|
return f.type.startsWith(prefix);
|
|
348
413
|
}
|
|
414
|
+
// Extension patterns (`.html`): browsers often omit the MIME type.
|
|
415
|
+
if (pattern.startsWith(".")) {
|
|
416
|
+
return f.name.toLowerCase().endsWith(pattern.toLowerCase());
|
|
417
|
+
}
|
|
349
418
|
return f.type === pattern;
|
|
350
419
|
});
|
|
351
420
|
};
|
|
@@ -403,6 +472,8 @@ export const PromptInput = ({
|
|
|
403
472
|
if (found?.url) URL.revokeObjectURL(found.url);
|
|
404
473
|
return prev.filter((file) => file.id !== id);
|
|
405
474
|
});
|
|
475
|
+
const replaceLocal = (id: string, file: File) =>
|
|
476
|
+
setItems((prev) => prev.map((existing) => replaceFile(existing, id, file)));
|
|
406
477
|
const addWithProviderValidation = (fileList: File[] | FileList) => {
|
|
407
478
|
const incoming = [...fileList];
|
|
408
479
|
const accepted = incoming.filter((f) => matchesAccept(f));
|
|
@@ -452,6 +523,7 @@ export const PromptInput = ({
|
|
|
452
523
|
const clearReferencedSources = () => setReferencedSources([]);
|
|
453
524
|
const add = usingProvider ? addWithProviderValidation : addLocal;
|
|
454
525
|
const remove = usingProvider ? controller.attachments.remove : removeLocal;
|
|
526
|
+
const replace = usingProvider ? controller.attachments.replace : replaceLocal;
|
|
455
527
|
const openFileDialog = usingProvider
|
|
456
528
|
? controller.attachments.openFileDialog
|
|
457
529
|
: openFileDialogLocal;
|
|
@@ -468,44 +540,54 @@ export const PromptInput = ({
|
|
|
468
540
|
inputRef.current.value = "";
|
|
469
541
|
}
|
|
470
542
|
}, [files, syncHiddenInput]);
|
|
543
|
+
// Drops land on the form, or anywhere in the document with `globalDrop`.
|
|
471
544
|
useEffect(() => {
|
|
472
|
-
const
|
|
473
|
-
if (!
|
|
474
|
-
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
545
|
+
const target: EventTarget | null = globalDrop ? document : formRef.current;
|
|
546
|
+
if (!target) return;
|
|
547
|
+
// Types are readable during the drag; the payload only on drop.
|
|
548
|
+
const carriesData = (event: DragEvent) =>
|
|
549
|
+
dataDrop !== undefined &&
|
|
550
|
+
event.dataTransfer?.types?.includes(dataDrop.type) === true;
|
|
551
|
+
const isAcceptedDrag = (event: DragEvent) =>
|
|
552
|
+
event.dataTransfer?.types?.includes("Files") === true || carriesData(event);
|
|
553
|
+
const onDragEnter = (event: Event) => {
|
|
554
|
+
if (!(event instanceof DragEvent) || !isAcceptedDrag(event)) return;
|
|
555
|
+
dragEnterDepth.current += 1;
|
|
556
|
+
setIsDragOver(true);
|
|
483
557
|
};
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
return () => {
|
|
487
|
-
form.removeEventListener("dragover", onDragOver);
|
|
488
|
-
form.removeEventListener("drop", onDrop);
|
|
558
|
+
const onDragOver = (event: Event) => {
|
|
559
|
+
if (event instanceof DragEvent && isAcceptedDrag(event)) event.preventDefault();
|
|
489
560
|
};
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
|
|
561
|
+
const onDragLeave = (event: Event) => {
|
|
562
|
+
if (!(event instanceof DragEvent) || !isAcceptedDrag(event)) return;
|
|
563
|
+
dragEnterDepth.current = Math.max(0, dragEnterDepth.current - 1);
|
|
564
|
+
if (dragEnterDepth.current === 0) setIsDragOver(false);
|
|
495
565
|
};
|
|
496
|
-
const onDrop = (
|
|
497
|
-
if (
|
|
498
|
-
if (
|
|
499
|
-
|
|
566
|
+
const onDrop = (event: Event) => {
|
|
567
|
+
if (!(event instanceof DragEvent)) return;
|
|
568
|
+
if (isAcceptedDrag(event)) event.preventDefault();
|
|
569
|
+
dragEnterDepth.current = 0;
|
|
570
|
+
setIsDragOver(false);
|
|
571
|
+
if (dataDrop !== undefined && carriesData(event)) {
|
|
572
|
+
const payload = event.dataTransfer?.getData(dataDrop.type);
|
|
573
|
+
if (payload) dataDrop.onDrop(payload);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (event.dataTransfer?.files && event.dataTransfer.files.length > 0) {
|
|
577
|
+
add(event.dataTransfer.files);
|
|
500
578
|
}
|
|
501
579
|
};
|
|
502
|
-
|
|
503
|
-
|
|
580
|
+
target.addEventListener("dragenter", onDragEnter);
|
|
581
|
+
target.addEventListener("dragover", onDragOver);
|
|
582
|
+
target.addEventListener("dragleave", onDragLeave);
|
|
583
|
+
target.addEventListener("drop", onDrop);
|
|
504
584
|
return () => {
|
|
505
|
-
|
|
506
|
-
|
|
585
|
+
target.removeEventListener("dragenter", onDragEnter);
|
|
586
|
+
target.removeEventListener("dragover", onDragOver);
|
|
587
|
+
target.removeEventListener("dragleave", onDragLeave);
|
|
588
|
+
target.removeEventListener("drop", onDrop);
|
|
507
589
|
};
|
|
508
|
-
}, [add, globalDrop]);
|
|
590
|
+
}, [add, globalDrop, dataDrop]);
|
|
509
591
|
useEffect(
|
|
510
592
|
() => () => {
|
|
511
593
|
if (!usingProvider) {
|
|
@@ -527,6 +609,7 @@ export const PromptInput = ({
|
|
|
527
609
|
files: files.map((item) => ({ ...item, id: item.id })),
|
|
528
610
|
openFileDialog,
|
|
529
611
|
remove,
|
|
612
|
+
replace,
|
|
530
613
|
};
|
|
531
614
|
const refsCtx = {
|
|
532
615
|
add: (incoming: SourceDocumentUIPart[] | SourceDocumentUIPart) => {
|
|
@@ -592,12 +675,18 @@ export const PromptInput = ({
|
|
|
592
675
|
type="file"
|
|
593
676
|
/>
|
|
594
677
|
<form
|
|
595
|
-
className={
|
|
678
|
+
className={merge("w-full", className)}
|
|
596
679
|
onSubmit={handleSubmit}
|
|
597
680
|
ref={formRef}
|
|
598
681
|
{...props}
|
|
599
682
|
>
|
|
600
|
-
<InputGroup
|
|
683
|
+
<InputGroup
|
|
684
|
+
className={merge(
|
|
685
|
+
classes?.group ?? "overflow-hidden",
|
|
686
|
+
isDragOver ? classes?.groupDragOver : classes?.groupIdle,
|
|
687
|
+
containerClassName ?? inputGroupClassName ?? classes?.groupDefault,
|
|
688
|
+
)}
|
|
689
|
+
>
|
|
601
690
|
{children}
|
|
602
691
|
</InputGroup>
|
|
603
692
|
</form>
|
|
@@ -618,9 +707,12 @@ export type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;
|
|
|
618
707
|
export const PromptInputBody = ({
|
|
619
708
|
className,
|
|
620
709
|
...props
|
|
621
|
-
}: PromptInputBodyProps) =>
|
|
622
|
-
|
|
623
|
-
|
|
710
|
+
}: PromptInputBodyProps) => {
|
|
711
|
+
const merge = useCn();
|
|
712
|
+
return (
|
|
713
|
+
<div className={merge("contents", className)} {...props} />
|
|
714
|
+
);
|
|
715
|
+
};
|
|
624
716
|
export type PromptInputTextareaProps = ComponentProps<
|
|
625
717
|
typeof InputGroupTextarea
|
|
626
718
|
>;
|
|
@@ -633,6 +725,8 @@ export const PromptInputTextarea = ({
|
|
|
633
725
|
}: PromptInputTextareaProps) => {
|
|
634
726
|
const controller = useOptionalPromptInputController();
|
|
635
727
|
const attachments = usePromptInputAttachments();
|
|
728
|
+
const textarea = useUiConfig("PromptInput")?.classes?.textarea;
|
|
729
|
+
const merge = useCn();
|
|
636
730
|
const [isComposing, setIsComposing] = useState(false);
|
|
637
731
|
const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (e) => {
|
|
638
732
|
onKeyDown?.(e);
|
|
@@ -686,7 +780,7 @@ export const PromptInputTextarea = ({
|
|
|
686
780
|
: { onChange };
|
|
687
781
|
return (
|
|
688
782
|
<InputGroupTextarea
|
|
689
|
-
className={
|
|
783
|
+
className={merge(textarea ?? "field-sizing-content max-h-48 min-h-16", className)}
|
|
690
784
|
name="message"
|
|
691
785
|
onCompositionEnd={handleCompositionEnd}
|
|
692
786
|
onCompositionStart={handleCompositionStart}
|
|
@@ -705,13 +799,16 @@ export type PromptInputHeaderProps = Omit<
|
|
|
705
799
|
export const PromptInputHeader = ({
|
|
706
800
|
className,
|
|
707
801
|
...props
|
|
708
|
-
}: PromptInputHeaderProps) =>
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
802
|
+
}: PromptInputHeaderProps) => {
|
|
803
|
+
const merge = useCn();
|
|
804
|
+
return (
|
|
805
|
+
<InputGroupAddon
|
|
806
|
+
align="block-end"
|
|
807
|
+
className={merge("order-first flex-wrap gap-1", className)}
|
|
808
|
+
{...props}
|
|
809
|
+
/>
|
|
810
|
+
);
|
|
811
|
+
};
|
|
715
812
|
export type PromptInputFooterProps = Omit<
|
|
716
813
|
ComponentProps<typeof InputGroupAddon>,
|
|
717
814
|
"align"
|
|
@@ -719,23 +816,30 @@ export type PromptInputFooterProps = Omit<
|
|
|
719
816
|
export const PromptInputFooter = ({
|
|
720
817
|
className,
|
|
721
818
|
...props
|
|
722
|
-
}: PromptInputFooterProps) =>
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
)
|
|
819
|
+
}: PromptInputFooterProps) => {
|
|
820
|
+
const footer = useUiConfig("PromptInput")?.classes?.footer;
|
|
821
|
+
const merge = useCn();
|
|
822
|
+
return (
|
|
823
|
+
<InputGroupAddon
|
|
824
|
+
align="block-end"
|
|
825
|
+
className={merge(footer ?? "justify-between gap-1", className)}
|
|
826
|
+
{...props}
|
|
827
|
+
/>
|
|
828
|
+
);
|
|
829
|
+
};
|
|
729
830
|
export type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;
|
|
730
831
|
export const PromptInputTools = ({
|
|
731
832
|
className,
|
|
732
833
|
...props
|
|
733
|
-
}: PromptInputToolsProps) =>
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
834
|
+
}: PromptInputToolsProps) => {
|
|
835
|
+
const merge = useCn();
|
|
836
|
+
return (
|
|
837
|
+
<div
|
|
838
|
+
className={merge("flex min-w-0 items-center gap-1", className)}
|
|
839
|
+
{...props}
|
|
840
|
+
/>
|
|
841
|
+
);
|
|
842
|
+
};
|
|
739
843
|
export type PromptInputButtonTooltip =
|
|
740
844
|
| string
|
|
741
845
|
| {
|
|
@@ -753,11 +857,12 @@ export const PromptInputButton = ({
|
|
|
753
857
|
tooltip,
|
|
754
858
|
...props
|
|
755
859
|
}: PromptInputButtonProps) => {
|
|
860
|
+
const merge = useCn();
|
|
756
861
|
const newSize =
|
|
757
862
|
size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
|
|
758
863
|
const button = (
|
|
759
864
|
<InputGroupButton
|
|
760
|
-
className={
|
|
865
|
+
className={merge(className)}
|
|
761
866
|
size={newSize}
|
|
762
867
|
type="button"
|
|
763
868
|
variant={variant}
|
|
@@ -788,31 +893,39 @@ export const PromptInputActionMenuTrigger = ({
|
|
|
788
893
|
className,
|
|
789
894
|
children,
|
|
790
895
|
...props
|
|
791
|
-
}: PromptInputActionMenuTriggerProps) =>
|
|
792
|
-
|
|
896
|
+
}: PromptInputActionMenuTriggerProps) => {
|
|
897
|
+
const asChild = useUiConfig("PromptInput")?.menuTriggerAsChild;
|
|
898
|
+
const button = (
|
|
793
899
|
<PromptInputButton className={className} {...props}>
|
|
794
900
|
{children ?? <IconPlus className="size-4" />}
|
|
795
901
|
</PromptInputButton>
|
|
796
|
-
|
|
797
|
-
|
|
902
|
+
);
|
|
903
|
+
return asChild ? <DropdownTrigger asChild>{button}</DropdownTrigger> : <DropdownTrigger>{button}</DropdownTrigger>;
|
|
904
|
+
};
|
|
798
905
|
export type PromptInputActionMenuContentProps = ComponentProps<
|
|
799
906
|
typeof DropdownMenu
|
|
800
907
|
>;
|
|
801
908
|
export const PromptInputActionMenuContent = ({
|
|
802
909
|
className,
|
|
803
910
|
...props
|
|
804
|
-
}: PromptInputActionMenuContentProps) =>
|
|
805
|
-
|
|
806
|
-
|
|
911
|
+
}: PromptInputActionMenuContentProps) => {
|
|
912
|
+
const merge = useCn();
|
|
913
|
+
return (
|
|
914
|
+
<DropdownMenu align="start" className={merge(className)} {...props} />
|
|
915
|
+
);
|
|
916
|
+
};
|
|
807
917
|
export type PromptInputActionMenuItemProps = ComponentProps<
|
|
808
918
|
typeof DropdownItem
|
|
809
919
|
>;
|
|
810
920
|
export const PromptInputActionMenuItem = ({
|
|
811
921
|
className,
|
|
812
922
|
...props
|
|
813
|
-
}: PromptInputActionMenuItemProps) =>
|
|
814
|
-
|
|
815
|
-
|
|
923
|
+
}: PromptInputActionMenuItemProps) => {
|
|
924
|
+
const merge = useCn();
|
|
925
|
+
return (
|
|
926
|
+
<DropdownItem className={merge(className)} {...props} />
|
|
927
|
+
);
|
|
928
|
+
};
|
|
816
929
|
export type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {
|
|
817
930
|
status?: ChatStatus;
|
|
818
931
|
onStop?: () => void;
|
|
@@ -827,14 +940,21 @@ export const PromptInputSubmit = ({
|
|
|
827
940
|
children,
|
|
828
941
|
...props
|
|
829
942
|
}: PromptInputSubmitProps) => {
|
|
943
|
+
const config = useUiConfig("PromptInput");
|
|
944
|
+
const icons = config?.submitIcons;
|
|
945
|
+
const merge = useCn();
|
|
830
946
|
const isGenerating = status === "submitted" || status === "streaming";
|
|
831
|
-
|
|
947
|
+
const iconKey =
|
|
948
|
+
status === "submitted" || status === "streaming" || status === "error"
|
|
949
|
+
? status
|
|
950
|
+
: "send";
|
|
951
|
+
let Icon = icons?.send ?? <IconCornerDownLeft className="size-4" />;
|
|
832
952
|
if (status === "submitted") {
|
|
833
|
-
Icon = <Spinner size="sm" />;
|
|
953
|
+
Icon = icons?.submitted ?? <Spinner size="sm" />;
|
|
834
954
|
} else if (status === "streaming") {
|
|
835
|
-
Icon = <IconSquare className="size-4" />;
|
|
955
|
+
Icon = icons?.streaming ?? <IconSquare className="size-4" />;
|
|
836
956
|
} else if (status === "error") {
|
|
837
|
-
Icon = <IconX className="size-4" />;
|
|
957
|
+
Icon = icons?.error ?? <IconX className="size-4" />;
|
|
838
958
|
}
|
|
839
959
|
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
840
960
|
if (isGenerating && onStop) {
|
|
@@ -847,63 +967,91 @@ export const PromptInputSubmit = ({
|
|
|
847
967
|
return (
|
|
848
968
|
<InputGroupButton
|
|
849
969
|
aria-label={isGenerating ? "Stop" : "Submit"}
|
|
850
|
-
className={
|
|
970
|
+
className={merge(config?.classes?.submit, className)}
|
|
851
971
|
onClick={handleClick}
|
|
852
972
|
size={size}
|
|
853
973
|
type={isGenerating && onStop ? "button" : "submit"}
|
|
854
974
|
variant={variant}
|
|
855
975
|
{...props}
|
|
856
976
|
>
|
|
857
|
-
{children ??
|
|
977
|
+
{children ??
|
|
978
|
+
(config?.submitCrossfade ? (
|
|
979
|
+
// Keyed by status, so send, spinner, stop and error cross-fade.
|
|
980
|
+
<CrossfadeIconSlot iconKey={iconKey}>{Icon}</CrossfadeIconSlot>
|
|
981
|
+
) : (
|
|
982
|
+
Icon
|
|
983
|
+
))}
|
|
858
984
|
</InputGroupButton>
|
|
859
985
|
);
|
|
860
986
|
};
|
|
987
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
861
988
|
export type PromptInputModelSelectProps = ComponentProps<typeof Select>;
|
|
989
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
862
990
|
export const PromptInputModelSelect = (props: PromptInputModelSelectProps) => (
|
|
863
991
|
<Select {...props} />
|
|
864
992
|
);
|
|
993
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
865
994
|
export type PromptInputModelSelectTriggerProps = ComponentProps<
|
|
866
995
|
typeof SelectTrigger
|
|
867
996
|
>;
|
|
997
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
868
998
|
export const PromptInputModelSelectTrigger = ({
|
|
869
999
|
className,
|
|
870
1000
|
...props
|
|
871
|
-
}: PromptInputModelSelectTriggerProps) =>
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1001
|
+
}: PromptInputModelSelectTriggerProps) => {
|
|
1002
|
+
const merge = useCn();
|
|
1003
|
+
return (
|
|
1004
|
+
<SelectTrigger
|
|
1005
|
+
className={merge(
|
|
1006
|
+
"border-none bg-transparent font-medium text-muted-foreground shadow-none transition-[color,transform] duration-150 ease-out active:scale-[0.97]",
|
|
1007
|
+
"hover:bg-surface-secondary hover:text-foreground",
|
|
1008
|
+
segmentExpandedStateClass,
|
|
1009
|
+
className,
|
|
1010
|
+
)}
|
|
1011
|
+
{...props}
|
|
1012
|
+
/>
|
|
1013
|
+
);
|
|
1014
|
+
};
|
|
1015
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
882
1016
|
export type PromptInputModelSelectContentProps = ComponentProps<
|
|
883
1017
|
typeof SelectContent
|
|
884
1018
|
>;
|
|
1019
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
885
1020
|
export const PromptInputModelSelectContent = ({
|
|
886
1021
|
className,
|
|
887
1022
|
...props
|
|
888
|
-
}: PromptInputModelSelectContentProps) =>
|
|
889
|
-
|
|
890
|
-
|
|
1023
|
+
}: PromptInputModelSelectContentProps) => {
|
|
1024
|
+
const merge = useCn();
|
|
1025
|
+
return (
|
|
1026
|
+
<SelectContent className={merge(className)} {...props} />
|
|
1027
|
+
);
|
|
1028
|
+
};
|
|
1029
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
891
1030
|
export type PromptInputModelSelectItemProps = ComponentProps<typeof SelectItem>;
|
|
1031
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
892
1032
|
export const PromptInputModelSelectItem = ({
|
|
893
1033
|
className,
|
|
894
1034
|
...props
|
|
895
|
-
}: PromptInputModelSelectItemProps) =>
|
|
896
|
-
|
|
897
|
-
|
|
1035
|
+
}: PromptInputModelSelectItemProps) => {
|
|
1036
|
+
const merge = useCn();
|
|
1037
|
+
return (
|
|
1038
|
+
<SelectItem className={merge(className)} {...props} />
|
|
1039
|
+
);
|
|
1040
|
+
};
|
|
1041
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
898
1042
|
export type PromptInputModelSelectValueProps = ComponentProps<
|
|
899
1043
|
typeof SelectValue
|
|
900
1044
|
>;
|
|
1045
|
+
/** @deprecated Use `ModelPicker`. Removed in the next minor release. */
|
|
901
1046
|
export const PromptInputModelSelectValue = ({
|
|
902
1047
|
className,
|
|
903
1048
|
...props
|
|
904
|
-
}: PromptInputModelSelectValueProps) =>
|
|
905
|
-
|
|
906
|
-
|
|
1049
|
+
}: PromptInputModelSelectValueProps) => {
|
|
1050
|
+
const merge = useCn();
|
|
1051
|
+
return (
|
|
1052
|
+
<SelectValue className={merge(className)} {...props} />
|
|
1053
|
+
);
|
|
1054
|
+
};
|
|
907
1055
|
export type PromptInputHoverCardProps = ComponentProps<typeof HoverCard>;
|
|
908
1056
|
export const PromptInputHoverCard = ({
|
|
909
1057
|
openDelay = 0,
|
|
@@ -931,88 +1079,126 @@ export type PromptInputTabsListProps = HTMLAttributes<HTMLDivElement>;
|
|
|
931
1079
|
export const PromptInputTabsList = ({
|
|
932
1080
|
className,
|
|
933
1081
|
...props
|
|
934
|
-
}: PromptInputTabsListProps) =>
|
|
1082
|
+
}: PromptInputTabsListProps) => {
|
|
1083
|
+
const merge = useCn();
|
|
1084
|
+
return <div className={merge(className)} {...props} />;
|
|
1085
|
+
};
|
|
935
1086
|
export type PromptInputTabProps = HTMLAttributes<HTMLDivElement>;
|
|
936
1087
|
export const PromptInputTab = ({
|
|
937
1088
|
className,
|
|
938
1089
|
...props
|
|
939
|
-
}: PromptInputTabProps) =>
|
|
1090
|
+
}: PromptInputTabProps) => {
|
|
1091
|
+
const merge = useCn();
|
|
1092
|
+
return <div className={merge(className)} {...props} />;
|
|
1093
|
+
};
|
|
940
1094
|
export type PromptInputTabLabelProps = HTMLAttributes<HTMLHeadingElement>;
|
|
941
1095
|
export const PromptInputTabLabel = ({
|
|
942
1096
|
className,
|
|
943
1097
|
...props
|
|
944
|
-
}: PromptInputTabLabelProps) =>
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1098
|
+
}: PromptInputTabLabelProps) => {
|
|
1099
|
+
const merge = useCn();
|
|
1100
|
+
return (
|
|
1101
|
+
<h3
|
|
1102
|
+
className={merge("mb-2 px-3 font-medium text-muted-foreground text-xs", className)}
|
|
1103
|
+
{...props}
|
|
1104
|
+
/>
|
|
1105
|
+
);
|
|
1106
|
+
};
|
|
950
1107
|
export type PromptInputTabBodyProps = HTMLAttributes<HTMLDivElement>;
|
|
951
1108
|
export const PromptInputTabBody = ({
|
|
952
1109
|
className,
|
|
953
1110
|
...props
|
|
954
|
-
}: PromptInputTabBodyProps) =>
|
|
955
|
-
|
|
956
|
-
|
|
1111
|
+
}: PromptInputTabBodyProps) => {
|
|
1112
|
+
const merge = useCn();
|
|
1113
|
+
return (
|
|
1114
|
+
<div className={merge("space-y-1", className)} {...props} />
|
|
1115
|
+
);
|
|
1116
|
+
};
|
|
957
1117
|
export type PromptInputTabItemProps = HTMLAttributes<HTMLDivElement>;
|
|
958
1118
|
export const PromptInputTabItem = ({
|
|
959
1119
|
className,
|
|
960
1120
|
...props
|
|
961
|
-
}: PromptInputTabItemProps) =>
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1121
|
+
}: PromptInputTabItemProps) => {
|
|
1122
|
+
const tabItem = useUiConfig("PromptInput")?.classes?.tabItem;
|
|
1123
|
+
const merge = useCn();
|
|
1124
|
+
return (
|
|
1125
|
+
<div
|
|
1126
|
+
className={merge(
|
|
1127
|
+
tabItem ??
|
|
1128
|
+
"flex items-center gap-2 rounded-md px-3 py-2 text-xs transition-[background-color,transform] duration-150 ease-out active:scale-[0.99] hover:bg-surface-secondary",
|
|
1129
|
+
className,
|
|
1130
|
+
)}
|
|
1131
|
+
{...props}
|
|
1132
|
+
/>
|
|
1133
|
+
);
|
|
1134
|
+
};
|
|
970
1135
|
export type PromptInputCommandProps = ComponentProps<typeof Command>;
|
|
971
1136
|
export const PromptInputCommand = ({
|
|
972
1137
|
className,
|
|
973
1138
|
...props
|
|
974
|
-
}: PromptInputCommandProps) =>
|
|
1139
|
+
}: PromptInputCommandProps) => {
|
|
1140
|
+
const merge = useCn();
|
|
1141
|
+
return <Command className={merge(className)} {...props} />;
|
|
1142
|
+
};
|
|
975
1143
|
export type PromptInputCommandInputProps = ComponentProps<typeof CommandInput>;
|
|
976
1144
|
export const PromptInputCommandInput = ({
|
|
977
1145
|
className,
|
|
978
1146
|
...props
|
|
979
|
-
}: PromptInputCommandInputProps) =>
|
|
980
|
-
|
|
981
|
-
|
|
1147
|
+
}: PromptInputCommandInputProps) => {
|
|
1148
|
+
const merge = useCn();
|
|
1149
|
+
return (
|
|
1150
|
+
<CommandInput className={merge(className)} {...props} />
|
|
1151
|
+
);
|
|
1152
|
+
};
|
|
982
1153
|
export type PromptInputCommandListProps = ComponentProps<typeof CommandList>;
|
|
983
1154
|
export const PromptInputCommandList = ({
|
|
984
1155
|
className,
|
|
985
1156
|
...props
|
|
986
|
-
}: PromptInputCommandListProps) =>
|
|
987
|
-
|
|
988
|
-
|
|
1157
|
+
}: PromptInputCommandListProps) => {
|
|
1158
|
+
const merge = useCn();
|
|
1159
|
+
return (
|
|
1160
|
+
<CommandList className={merge(className)} {...props} />
|
|
1161
|
+
);
|
|
1162
|
+
};
|
|
989
1163
|
export type PromptInputCommandEmptyProps = ComponentProps<typeof CommandEmpty>;
|
|
990
1164
|
export const PromptInputCommandEmpty = ({
|
|
991
1165
|
className,
|
|
992
1166
|
...props
|
|
993
|
-
}: PromptInputCommandEmptyProps) =>
|
|
994
|
-
|
|
995
|
-
|
|
1167
|
+
}: PromptInputCommandEmptyProps) => {
|
|
1168
|
+
const merge = useCn();
|
|
1169
|
+
return (
|
|
1170
|
+
<CommandEmpty className={merge(className)} {...props} />
|
|
1171
|
+
);
|
|
1172
|
+
};
|
|
996
1173
|
export type PromptInputCommandGroupProps = ComponentProps<typeof CommandGroup>;
|
|
997
1174
|
export const PromptInputCommandGroup = ({
|
|
998
1175
|
className,
|
|
999
1176
|
...props
|
|
1000
|
-
}: PromptInputCommandGroupProps) =>
|
|
1001
|
-
|
|
1002
|
-
|
|
1177
|
+
}: PromptInputCommandGroupProps) => {
|
|
1178
|
+
const merge = useCn();
|
|
1179
|
+
return (
|
|
1180
|
+
<CommandGroup className={merge(className)} {...props} />
|
|
1181
|
+
);
|
|
1182
|
+
};
|
|
1003
1183
|
export type PromptInputCommandItemProps = ComponentProps<typeof CommandItem>;
|
|
1004
1184
|
export const PromptInputCommandItem = ({
|
|
1005
1185
|
className,
|
|
1006
1186
|
...props
|
|
1007
|
-
}: PromptInputCommandItemProps) =>
|
|
1008
|
-
|
|
1009
|
-
|
|
1187
|
+
}: PromptInputCommandItemProps) => {
|
|
1188
|
+
const merge = useCn();
|
|
1189
|
+
return (
|
|
1190
|
+
<CommandItem className={merge(className)} {...props} />
|
|
1191
|
+
);
|
|
1192
|
+
};
|
|
1010
1193
|
export type PromptInputCommandSeparatorProps = ComponentProps<
|
|
1011
1194
|
typeof CommandSeparator
|
|
1012
1195
|
>;
|
|
1013
1196
|
export const PromptInputCommandSeparator = ({
|
|
1014
1197
|
className,
|
|
1015
1198
|
...props
|
|
1016
|
-
}: PromptInputCommandSeparatorProps) =>
|
|
1017
|
-
|
|
1018
|
-
|
|
1199
|
+
}: PromptInputCommandSeparatorProps) => {
|
|
1200
|
+
const merge = useCn();
|
|
1201
|
+
return (
|
|
1202
|
+
<CommandSeparator className={merge(className)} {...props} />
|
|
1203
|
+
);
|
|
1204
|
+
};
|