@epicbagel/saas-kit 1.6.0 → 1.7.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 +11 -1
- package/dist/primitives/attachment.d.cts +1 -1
- package/dist/primitives/attachment.d.ts +1 -1
- package/dist/primitives/badge.d.cts +1 -1
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/bubble.d.cts +1 -1
- package/dist/primitives/bubble.d.ts +1 -1
- package/dist/primitives/button.d.cts +3 -3
- package/dist/primitives/button.d.ts +3 -3
- package/dist/primitives/chat.cjs +72 -24
- package/dist/primitives/chat.d.cts +19 -2
- package/dist/primitives/chat.d.ts +19 -2
- package/dist/primitives/chat.js +70 -22
- package/dist/primitives/item.d.cts +2 -2
- package/dist/primitives/item.d.ts +2 -2
- package/dist/primitives/section-header.d.cts +1 -1
- package/dist/primitives/section-header.d.ts +1 -1
- package/dist/primitives/toggle-group.d.cts +2 -2
- package/dist/primitives/toggle-group.d.ts +2 -2
- package/dist/primitives/toggle.d.cts +2 -2
- package/dist/primitives/toggle.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,7 +94,8 @@ public in 1.1.0, and these were the decisions about its props then:
|
|
|
94
94
|
("Thinking…"), `scrollButtonLabel` ("Scroll to latest message"),
|
|
95
95
|
`uploadingLabel` ("Uploading…"), `voiceHeroLabel` ("Tap to speak"),
|
|
96
96
|
`voiceHeroSwitchLabel` ("or type instead"), `examplePromptsLabel` ("Try"),
|
|
97
|
-
`examplePromptsLoadingLabel` ("Loading suggestions…"),
|
|
97
|
+
`examplePromptsLoadingLabel` ("Loading suggestions…"),
|
|
98
|
+
`replySuggestionsLabel` ("Suggested replies"), `addFilesLabel`,
|
|
98
99
|
`placeholder` and `activePlaceholder`. The defaults are plain English with
|
|
99
100
|
no product voice, and the empty state has no title unless you pass
|
|
100
101
|
`emptyTitle`. The icon buttons' accessible names ("Send", "Add to
|
|
@@ -114,6 +115,15 @@ public in 1.1.0, and these were the decisions about its props then:
|
|
|
114
115
|
- **Example prompts.** `examplePromptsVariant` is `chips` (the default: chips
|
|
115
116
|
that wrap onto as many lines as they need), `row` (one line that scrolls
|
|
116
117
|
sideways) or `cards` (a two-column grid).
|
|
118
|
+
- **Reply suggestions (1.7.0).** `replySuggestions` are the replies the
|
|
119
|
+
conversation offers next. They show once the conversation has started, at
|
|
120
|
+
the composer, directly above the input, as the example prompts' chips (one
|
|
121
|
+
implementation draws both), and wrap rather than cover the input on a
|
|
122
|
+
phone. `replySuggestionsLabel` ("Suggested replies") names the group.
|
|
123
|
+
Picking a suggestion of either kind sends only its words, `onSend(text,
|
|
124
|
+
[])`: the draft and staged files stay, and a failure is reported without
|
|
125
|
+
touching them. Suggestions are disabled while `pending`; pass none once
|
|
126
|
+
they no longer apply.
|
|
117
127
|
- **Failed sends.** When `onSend` rejects, Chat puts the text back in the
|
|
118
128
|
composer and calls `onSendError`; without one it reports the rejection
|
|
119
129
|
through `reportError`, so a failure is never silent.
|
|
@@ -12,7 +12,7 @@ declare function Attachment({ className, state, size, orientation, ...props }: R
|
|
|
12
12
|
state?: "idle" | "uploading" | "processing" | "error" | "done";
|
|
13
13
|
}): react_jsx_runtime.JSX.Element;
|
|
14
14
|
declare const attachmentMediaVariants: (props?: ({
|
|
15
|
-
variant?: "
|
|
15
|
+
variant?: "icon" | "image" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
17
|
declare function AttachmentMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof attachmentMediaVariants>): react_jsx_runtime.JSX.Element;
|
|
18
18
|
declare function AttachmentContent({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
@@ -12,7 +12,7 @@ declare function Attachment({ className, state, size, orientation, ...props }: R
|
|
|
12
12
|
state?: "idle" | "uploading" | "processing" | "error" | "done";
|
|
13
13
|
}): react_jsx_runtime.JSX.Element;
|
|
14
14
|
declare const attachmentMediaVariants: (props?: ({
|
|
15
|
-
variant?: "
|
|
15
|
+
variant?: "icon" | "image" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
17
|
declare function AttachmentMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof attachmentMediaVariants>): react_jsx_runtime.JSX.Element;
|
|
18
18
|
declare function AttachmentContent({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import { ElementType } from 'react';
|
|
5
5
|
|
|
6
6
|
declare const badgeVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "error" | "success" | "default" | "secondary" | "destructive" | "outline" | "warning" | "info" | "neutral" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
10
10
|
weight?: "normal" | "semibold" | "bold";
|
|
@@ -4,7 +4,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
4
4
|
import { ElementType } from 'react';
|
|
5
5
|
|
|
6
6
|
declare const badgeVariants: (props?: ({
|
|
7
|
-
variant?: "
|
|
7
|
+
variant?: "error" | "success" | "default" | "secondary" | "destructive" | "outline" | "warning" | "info" | "neutral" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
10
10
|
weight?: "normal" | "semibold" | "bold";
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare function BubbleGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
7
7
|
declare const bubbleVariants: (props?: ({
|
|
8
|
-
variant?: "
|
|
8
|
+
variant?: "muted" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "tinted" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function Bubble({ variant, align, className, ...props }: React.ComponentProps<"div"> & VariantProps<typeof bubbleVariants> & {
|
|
11
11
|
align?: "start" | "end";
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare function BubbleGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
7
7
|
declare const bubbleVariants: (props?: ({
|
|
8
|
-
variant?: "
|
|
8
|
+
variant?: "muted" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "tinted" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function Bubble({ variant, align, className, ...props }: React.ComponentProps<"div"> & VariantProps<typeof bubbleVariants> & {
|
|
11
11
|
align?: "start" | "end";
|
|
@@ -4,13 +4,13 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const buttonVariants: (props?: ({
|
|
7
|
-
variant?: "link" | "default" | "
|
|
8
|
-
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "
|
|
7
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "outline-muted" | "ghost" | "quiet" | "ghost-destructive" | "link-muted" | "inverted" | "status-error" | "status-warning" | "status-muted" | null | undefined;
|
|
8
|
+
size?: "filter" | "status" | "icon" | "toolbar" | "sm" | "lg" | "default" | "micro" | "compact" | "hero" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" | "tone" | null | undefined;
|
|
9
9
|
width?: "auto" | "full" | "responsive" | null | undefined;
|
|
10
10
|
align?: "center" | "start" | null | undefined;
|
|
11
11
|
shrink?: boolean | null | undefined;
|
|
12
12
|
shape?: "circle" | "default" | "pill" | null | undefined;
|
|
13
|
-
ring?: "
|
|
13
|
+
ring?: "surface" | "none" | null | undefined;
|
|
14
14
|
grow?: boolean | null | undefined;
|
|
15
15
|
minWidth?: "auto" | "zero" | null | undefined;
|
|
16
16
|
textAlign?: "center" | "start" | null | undefined;
|
|
@@ -4,13 +4,13 @@ import * as class_variance_authority_types from 'class-variance-authority/types'
|
|
|
4
4
|
import { VariantProps } from 'class-variance-authority';
|
|
5
5
|
|
|
6
6
|
declare const buttonVariants: (props?: ({
|
|
7
|
-
variant?: "link" | "default" | "
|
|
8
|
-
size?: "filter" | "status" | "toolbar" | "sm" | "lg" | "default" | "micro" | "
|
|
7
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "outline-muted" | "ghost" | "quiet" | "ghost-destructive" | "link-muted" | "inverted" | "status-error" | "status-warning" | "status-muted" | null | undefined;
|
|
8
|
+
size?: "filter" | "status" | "icon" | "toolbar" | "sm" | "lg" | "default" | "micro" | "compact" | "hero" | "icon-lg" | "icon-sm" | "icon-md" | "icon-xs" | "icon-compact" | "icon-micro" | "bare" | "voice-sm" | "voice" | "suggestion" | "tone" | null | undefined;
|
|
9
9
|
width?: "auto" | "full" | "responsive" | null | undefined;
|
|
10
10
|
align?: "center" | "start" | null | undefined;
|
|
11
11
|
shrink?: boolean | null | undefined;
|
|
12
12
|
shape?: "circle" | "default" | "pill" | null | undefined;
|
|
13
|
-
ring?: "
|
|
13
|
+
ring?: "surface" | "none" | null | undefined;
|
|
14
14
|
grow?: boolean | null | undefined;
|
|
15
15
|
minWidth?: "auto" | "zero" | null | undefined;
|
|
16
16
|
textAlign?: "center" | "start" | null | undefined;
|
package/dist/primitives/chat.cjs
CHANGED
|
@@ -629,27 +629,40 @@ var EXAMPLE_PROMPT_LAYOUTS = {
|
|
|
629
629
|
row: "row",
|
|
630
630
|
cards: "grid"
|
|
631
631
|
};
|
|
632
|
-
function
|
|
633
|
-
|
|
632
|
+
function ChatSuggestions({
|
|
633
|
+
slot,
|
|
634
|
+
items,
|
|
634
635
|
onSelect,
|
|
635
|
-
label
|
|
636
|
+
label,
|
|
637
|
+
showLabel = true,
|
|
636
638
|
variant = "chips",
|
|
639
|
+
disabled = false,
|
|
637
640
|
className
|
|
638
641
|
}) {
|
|
639
642
|
const labelId = _react.useId.call(void 0, );
|
|
640
|
-
if (
|
|
643
|
+
if (items.length === 0) return null;
|
|
641
644
|
const cards = variant === "cards";
|
|
642
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot":
|
|
643
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { id: labelId, "data-slot":
|
|
644
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
645
|
-
_chunkM4V7OJNFcjs.
|
|
645
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { "data-slot": slot, "data-variant": variant, className, children: [
|
|
646
|
+
showLabel && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { id: labelId, "data-slot": `${slot}-label`, className: "mb-2 text-left text-xs text-muted-foreground", children: label }),
|
|
647
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
648
|
+
_chunkM4V7OJNFcjs.Suggestions,
|
|
646
649
|
{
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
650
|
+
role: "group",
|
|
651
|
+
"aria-labelledby": showLabel ? labelId : void 0,
|
|
652
|
+
"aria-label": showLabel ? void 0 : label,
|
|
653
|
+
layout: EXAMPLE_PROMPT_LAYOUTS[variant],
|
|
654
|
+
children: items.map((item) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
655
|
+
_chunkM4V7OJNFcjs.Suggestion,
|
|
656
|
+
{
|
|
657
|
+
suggestion: item,
|
|
658
|
+
onClick: onSelect,
|
|
659
|
+
disabled,
|
|
660
|
+
className: cards ? "h-auto min-h-12 px-3 py-2.5 text-xs" : void 0
|
|
661
|
+
},
|
|
662
|
+
item
|
|
663
|
+
))
|
|
664
|
+
}
|
|
665
|
+
)
|
|
653
666
|
] });
|
|
654
667
|
}
|
|
655
668
|
function ExamplePromptsLoading({ label, className }) {
|
|
@@ -693,6 +706,8 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
693
706
|
examplePromptsVariant = "chips",
|
|
694
707
|
examplePromptsLoading = false,
|
|
695
708
|
examplePromptsLoadingLabel = "Loading suggestions\u2026",
|
|
709
|
+
replySuggestions = [],
|
|
710
|
+
replySuggestionsLabel = "Suggested replies",
|
|
696
711
|
compactEmptyState = false,
|
|
697
712
|
defaultMode,
|
|
698
713
|
placeholder = "Type your message\u2026",
|
|
@@ -739,6 +754,14 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
739
754
|
const conversationStarted = messages.length > 0;
|
|
740
755
|
const [userMode, setUserMode] = _react.useState.call(void 0, null);
|
|
741
756
|
const mode = _nullishCoalesce(_nullishCoalesce(userMode, () => ( defaultMode)), () => ( (isMobile ? "voice" : "text")));
|
|
757
|
+
const reportSendError = _react.useCallback.call(void 0,
|
|
758
|
+
(cause) => {
|
|
759
|
+
if (onSendError) onSendError(cause);
|
|
760
|
+
else if (typeof globalThis.reportError === "function") globalThis.reportError(cause);
|
|
761
|
+
else console.error(cause);
|
|
762
|
+
},
|
|
763
|
+
[onSendError]
|
|
764
|
+
);
|
|
742
765
|
const send = _react.useCallback.call(void 0,
|
|
743
766
|
async (text, sentAttachments = attachments) => {
|
|
744
767
|
const trimmed = text.trim();
|
|
@@ -751,18 +774,29 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
751
774
|
setInput((current) => current ? current : trimmed);
|
|
752
775
|
if (mode === "voice") setUserMode("text");
|
|
753
776
|
}
|
|
754
|
-
|
|
755
|
-
else if (typeof globalThis.reportError === "function") globalThis.reportError(cause);
|
|
756
|
-
else console.error(cause);
|
|
777
|
+
reportSendError(cause);
|
|
757
778
|
}
|
|
758
779
|
window.setTimeout(() => _optionalChain([inputRef, 'access', _9 => _9.current, 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]), 0);
|
|
759
780
|
},
|
|
760
|
-
[attachments, mode, onSend,
|
|
781
|
+
[attachments, mode, onSend, pending, reportSendError]
|
|
782
|
+
);
|
|
783
|
+
const sendSuggestion = _react.useCallback.call(void 0,
|
|
784
|
+
async (text) => {
|
|
785
|
+
const trimmed = text.trim();
|
|
786
|
+
if (!trimmed || pending) return;
|
|
787
|
+
try {
|
|
788
|
+
await onSend(trimmed, []);
|
|
789
|
+
} catch (cause) {
|
|
790
|
+
reportSendError(cause);
|
|
791
|
+
}
|
|
792
|
+
window.setTimeout(() => _optionalChain([inputRef, 'access', _12 => _12.current, 'optionalAccess', _13 => _13.focus, 'call', _14 => _14()]), 0);
|
|
793
|
+
},
|
|
794
|
+
[onSend, pending, reportSendError]
|
|
761
795
|
);
|
|
762
796
|
_react.useImperativeHandle.call(void 0,
|
|
763
797
|
ref,
|
|
764
798
|
() => ({
|
|
765
|
-
focusInput: () => _optionalChain([inputRef, 'access',
|
|
799
|
+
focusInput: () => _optionalChain([inputRef, 'access', _15 => _15.current, 'optionalAccess', _16 => _16.focus, 'call', _17 => _17()]),
|
|
766
800
|
clearInput: () => setInput("")
|
|
767
801
|
}),
|
|
768
802
|
[]
|
|
@@ -770,7 +804,7 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
770
804
|
const dropFiles = _react.useCallback.call(void 0,
|
|
771
805
|
(files) => {
|
|
772
806
|
if (!conversationStarted && mode === "voice") setUserMode("text");
|
|
773
|
-
return _optionalChain([onFilesSelected, 'optionalCall',
|
|
807
|
+
return _optionalChain([onFilesSelected, 'optionalCall', _18 => _18(files)]);
|
|
774
808
|
},
|
|
775
809
|
[conversationStarted, mode, onFilesSelected]
|
|
776
810
|
);
|
|
@@ -898,12 +932,14 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
898
932
|
error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ChatError, { children: error }),
|
|
899
933
|
!pending && examplePromptsLoading && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ExamplePromptsLoading, { label: examplePromptsLoadingLabel }),
|
|
900
934
|
!pending && !examplePromptsLoading && examplePrompts.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
901
|
-
|
|
935
|
+
ChatSuggestions,
|
|
902
936
|
{
|
|
903
|
-
|
|
904
|
-
|
|
937
|
+
slot: "chat-examples",
|
|
938
|
+
items: examplePrompts,
|
|
939
|
+
onSelect: (prompt) => void sendSuggestion(prompt),
|
|
905
940
|
label: examplePromptsLabel,
|
|
906
|
-
variant: examplePromptsVariant
|
|
941
|
+
variant: examplePromptsVariant,
|
|
942
|
+
className: examplePromptsVariant === "cards" ? "mt-4" : "mt-3"
|
|
907
943
|
}
|
|
908
944
|
),
|
|
909
945
|
emptyFooter && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { "data-slot": "chat-empty-footer", className: "mt-5", children: emptyFooter })
|
|
@@ -946,6 +982,18 @@ var Chat = _react.forwardRef.call(void 0, function Chat2({
|
|
|
946
982
|
workspace && mobileCompactComposer && "max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))] max-md:pt-3"
|
|
947
983
|
),
|
|
948
984
|
children: [
|
|
985
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
986
|
+
ChatSuggestions,
|
|
987
|
+
{
|
|
988
|
+
slot: "chat-replies",
|
|
989
|
+
items: replySuggestions,
|
|
990
|
+
onSelect: (reply) => void sendSuggestion(reply),
|
|
991
|
+
label: replySuggestionsLabel,
|
|
992
|
+
showLabel: false,
|
|
993
|
+
disabled: pending,
|
|
994
|
+
className: _chunkKWLSBTB2cjs.cn.call(void 0, "mb-3", workspace && "mx-auto max-w-3xl")
|
|
995
|
+
}
|
|
996
|
+
),
|
|
949
997
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
950
998
|
ChatInput,
|
|
951
999
|
{
|
|
@@ -249,7 +249,10 @@ interface ChatProps {
|
|
|
249
249
|
emptyTitle?: ReactNode;
|
|
250
250
|
/** Subheadline shown under the headline. */
|
|
251
251
|
emptySubtitle?: ReactNode;
|
|
252
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Optional list of suggested starter prompts, under the empty-state
|
|
254
|
+
* composer. Picking one sends it as `replySuggestions` do.
|
|
255
|
+
*/
|
|
253
256
|
examplePrompts?: ReadonlyArray<string>;
|
|
254
257
|
/** Label above the example prompts. Defaults to "Try". */
|
|
255
258
|
examplePromptsLabel?: string;
|
|
@@ -303,6 +306,19 @@ interface ChatProps {
|
|
|
303
306
|
addFilesLabel?: string;
|
|
304
307
|
/** Shown on a file, staged or on a message, while it is still uploading. Defaults to "Uploading…". */
|
|
305
308
|
uploadingLabel?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Replies the conversation offers next, such as the choices the latest
|
|
311
|
+
* assistant turn asked for. Once the conversation has started they show
|
|
312
|
+
* at the composer, directly above the input and at its width, as the same
|
|
313
|
+
* suggestion chips as `examplePrompts`, wrapping onto as many lines as
|
|
314
|
+
* they need. Picking one sends only its words: the draft and any staged
|
|
315
|
+
* files stay in the composer, and a failed send is reported
|
|
316
|
+
* (`onSendError`) without touching them. Disabled while `pending`. Pass
|
|
317
|
+
* none (the default) to show none; drop them once they no longer apply.
|
|
318
|
+
*/
|
|
319
|
+
replySuggestions?: ReadonlyArray<string>;
|
|
320
|
+
/** The reply suggestions' accessible name. Defaults to "Suggested replies". */
|
|
321
|
+
replySuggestionsLabel?: string;
|
|
306
322
|
/** Visual treatment for both empty and active text composers. */
|
|
307
323
|
composerAppearance?: ChatInputProps["appearance"];
|
|
308
324
|
/**
|
|
@@ -406,7 +422,8 @@ interface ChatHandle {
|
|
|
406
422
|
* - Optional example prompts render as suggestions below the input.
|
|
407
423
|
*
|
|
408
424
|
* **Active state** (messages exist) — always the compact text input;
|
|
409
|
-
* the mic stays as an accessory icon.
|
|
425
|
+
* the mic stays as an accessory icon. Optional reply suggestions render as
|
|
426
|
+
* the same suggestions, directly above the input.
|
|
410
427
|
*
|
|
411
428
|
* **Files** — with `onFilesSelected`, the composer's menu picks files and a
|
|
412
429
|
* file dragged anywhere on the conversation can be dropped (`fileDrop`);
|
|
@@ -249,7 +249,10 @@ interface ChatProps {
|
|
|
249
249
|
emptyTitle?: ReactNode;
|
|
250
250
|
/** Subheadline shown under the headline. */
|
|
251
251
|
emptySubtitle?: ReactNode;
|
|
252
|
-
/**
|
|
252
|
+
/**
|
|
253
|
+
* Optional list of suggested starter prompts, under the empty-state
|
|
254
|
+
* composer. Picking one sends it as `replySuggestions` do.
|
|
255
|
+
*/
|
|
253
256
|
examplePrompts?: ReadonlyArray<string>;
|
|
254
257
|
/** Label above the example prompts. Defaults to "Try". */
|
|
255
258
|
examplePromptsLabel?: string;
|
|
@@ -303,6 +306,19 @@ interface ChatProps {
|
|
|
303
306
|
addFilesLabel?: string;
|
|
304
307
|
/** Shown on a file, staged or on a message, while it is still uploading. Defaults to "Uploading…". */
|
|
305
308
|
uploadingLabel?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Replies the conversation offers next, such as the choices the latest
|
|
311
|
+
* assistant turn asked for. Once the conversation has started they show
|
|
312
|
+
* at the composer, directly above the input and at its width, as the same
|
|
313
|
+
* suggestion chips as `examplePrompts`, wrapping onto as many lines as
|
|
314
|
+
* they need. Picking one sends only its words: the draft and any staged
|
|
315
|
+
* files stay in the composer, and a failed send is reported
|
|
316
|
+
* (`onSendError`) without touching them. Disabled while `pending`. Pass
|
|
317
|
+
* none (the default) to show none; drop them once they no longer apply.
|
|
318
|
+
*/
|
|
319
|
+
replySuggestions?: ReadonlyArray<string>;
|
|
320
|
+
/** The reply suggestions' accessible name. Defaults to "Suggested replies". */
|
|
321
|
+
replySuggestionsLabel?: string;
|
|
306
322
|
/** Visual treatment for both empty and active text composers. */
|
|
307
323
|
composerAppearance?: ChatInputProps["appearance"];
|
|
308
324
|
/**
|
|
@@ -406,7 +422,8 @@ interface ChatHandle {
|
|
|
406
422
|
* - Optional example prompts render as suggestions below the input.
|
|
407
423
|
*
|
|
408
424
|
* **Active state** (messages exist) — always the compact text input;
|
|
409
|
-
* the mic stays as an accessory icon.
|
|
425
|
+
* the mic stays as an accessory icon. Optional reply suggestions render as
|
|
426
|
+
* the same suggestions, directly above the input.
|
|
410
427
|
*
|
|
411
428
|
* **Files** — with `onFilesSelected`, the composer's menu picks files and a
|
|
412
429
|
* file dragged anywhere on the conversation can be dropped (`fileDrop`);
|
package/dist/primitives/chat.js
CHANGED
|
@@ -629,27 +629,40 @@ var EXAMPLE_PROMPT_LAYOUTS = {
|
|
|
629
629
|
row: "row",
|
|
630
630
|
cards: "grid"
|
|
631
631
|
};
|
|
632
|
-
function
|
|
633
|
-
|
|
632
|
+
function ChatSuggestions({
|
|
633
|
+
slot,
|
|
634
|
+
items,
|
|
634
635
|
onSelect,
|
|
635
|
-
label
|
|
636
|
+
label,
|
|
637
|
+
showLabel = true,
|
|
636
638
|
variant = "chips",
|
|
639
|
+
disabled = false,
|
|
637
640
|
className
|
|
638
641
|
}) {
|
|
639
642
|
const labelId = useId();
|
|
640
|
-
if (
|
|
643
|
+
if (items.length === 0) return null;
|
|
641
644
|
const cards = variant === "cards";
|
|
642
|
-
return /* @__PURE__ */ jsxs("div", { "data-slot":
|
|
643
|
-
/* @__PURE__ */ jsx("p", { id: labelId, "data-slot":
|
|
644
|
-
/* @__PURE__ */ jsx(
|
|
645
|
-
|
|
645
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": slot, "data-variant": variant, className, children: [
|
|
646
|
+
showLabel && /* @__PURE__ */ jsx("p", { id: labelId, "data-slot": `${slot}-label`, className: "mb-2 text-left text-xs text-muted-foreground", children: label }),
|
|
647
|
+
/* @__PURE__ */ jsx(
|
|
648
|
+
Suggestions,
|
|
646
649
|
{
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
650
|
+
role: "group",
|
|
651
|
+
"aria-labelledby": showLabel ? labelId : void 0,
|
|
652
|
+
"aria-label": showLabel ? void 0 : label,
|
|
653
|
+
layout: EXAMPLE_PROMPT_LAYOUTS[variant],
|
|
654
|
+
children: items.map((item) => /* @__PURE__ */ jsx(
|
|
655
|
+
Suggestion,
|
|
656
|
+
{
|
|
657
|
+
suggestion: item,
|
|
658
|
+
onClick: onSelect,
|
|
659
|
+
disabled,
|
|
660
|
+
className: cards ? "h-auto min-h-12 px-3 py-2.5 text-xs" : void 0
|
|
661
|
+
},
|
|
662
|
+
item
|
|
663
|
+
))
|
|
664
|
+
}
|
|
665
|
+
)
|
|
653
666
|
] });
|
|
654
667
|
}
|
|
655
668
|
function ExamplePromptsLoading({ label, className }) {
|
|
@@ -693,6 +706,8 @@ var Chat = forwardRef(function Chat2({
|
|
|
693
706
|
examplePromptsVariant = "chips",
|
|
694
707
|
examplePromptsLoading = false,
|
|
695
708
|
examplePromptsLoadingLabel = "Loading suggestions\u2026",
|
|
709
|
+
replySuggestions = [],
|
|
710
|
+
replySuggestionsLabel = "Suggested replies",
|
|
696
711
|
compactEmptyState = false,
|
|
697
712
|
defaultMode,
|
|
698
713
|
placeholder = "Type your message\u2026",
|
|
@@ -739,6 +754,14 @@ var Chat = forwardRef(function Chat2({
|
|
|
739
754
|
const conversationStarted = messages.length > 0;
|
|
740
755
|
const [userMode, setUserMode] = useState(null);
|
|
741
756
|
const mode = userMode ?? defaultMode ?? (isMobile ? "voice" : "text");
|
|
757
|
+
const reportSendError = useCallback(
|
|
758
|
+
(cause) => {
|
|
759
|
+
if (onSendError) onSendError(cause);
|
|
760
|
+
else if (typeof globalThis.reportError === "function") globalThis.reportError(cause);
|
|
761
|
+
else console.error(cause);
|
|
762
|
+
},
|
|
763
|
+
[onSendError]
|
|
764
|
+
);
|
|
742
765
|
const send = useCallback(
|
|
743
766
|
async (text, sentAttachments = attachments) => {
|
|
744
767
|
const trimmed = text.trim();
|
|
@@ -751,13 +774,24 @@ var Chat = forwardRef(function Chat2({
|
|
|
751
774
|
setInput((current) => current ? current : trimmed);
|
|
752
775
|
if (mode === "voice") setUserMode("text");
|
|
753
776
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
777
|
+
reportSendError(cause);
|
|
778
|
+
}
|
|
779
|
+
window.setTimeout(() => inputRef.current?.focus(), 0);
|
|
780
|
+
},
|
|
781
|
+
[attachments, mode, onSend, pending, reportSendError]
|
|
782
|
+
);
|
|
783
|
+
const sendSuggestion = useCallback(
|
|
784
|
+
async (text) => {
|
|
785
|
+
const trimmed = text.trim();
|
|
786
|
+
if (!trimmed || pending) return;
|
|
787
|
+
try {
|
|
788
|
+
await onSend(trimmed, []);
|
|
789
|
+
} catch (cause) {
|
|
790
|
+
reportSendError(cause);
|
|
757
791
|
}
|
|
758
792
|
window.setTimeout(() => inputRef.current?.focus(), 0);
|
|
759
793
|
},
|
|
760
|
-
[
|
|
794
|
+
[onSend, pending, reportSendError]
|
|
761
795
|
);
|
|
762
796
|
useImperativeHandle(
|
|
763
797
|
ref,
|
|
@@ -898,12 +932,14 @@ var Chat = forwardRef(function Chat2({
|
|
|
898
932
|
error && /* @__PURE__ */ jsx(ChatError, { children: error }),
|
|
899
933
|
!pending && examplePromptsLoading && /* @__PURE__ */ jsx(ExamplePromptsLoading, { label: examplePromptsLoadingLabel }),
|
|
900
934
|
!pending && !examplePromptsLoading && examplePrompts.length > 0 && /* @__PURE__ */ jsx(
|
|
901
|
-
|
|
935
|
+
ChatSuggestions,
|
|
902
936
|
{
|
|
903
|
-
|
|
904
|
-
|
|
937
|
+
slot: "chat-examples",
|
|
938
|
+
items: examplePrompts,
|
|
939
|
+
onSelect: (prompt) => void sendSuggestion(prompt),
|
|
905
940
|
label: examplePromptsLabel,
|
|
906
|
-
variant: examplePromptsVariant
|
|
941
|
+
variant: examplePromptsVariant,
|
|
942
|
+
className: examplePromptsVariant === "cards" ? "mt-4" : "mt-3"
|
|
907
943
|
}
|
|
908
944
|
),
|
|
909
945
|
emptyFooter && /* @__PURE__ */ jsx("div", { "data-slot": "chat-empty-footer", className: "mt-5", children: emptyFooter })
|
|
@@ -946,6 +982,18 @@ var Chat = forwardRef(function Chat2({
|
|
|
946
982
|
workspace && mobileCompactComposer && "max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))] max-md:pt-3"
|
|
947
983
|
),
|
|
948
984
|
children: [
|
|
985
|
+
/* @__PURE__ */ jsx(
|
|
986
|
+
ChatSuggestions,
|
|
987
|
+
{
|
|
988
|
+
slot: "chat-replies",
|
|
989
|
+
items: replySuggestions,
|
|
990
|
+
onSelect: (reply) => void sendSuggestion(reply),
|
|
991
|
+
label: replySuggestionsLabel,
|
|
992
|
+
showLabel: false,
|
|
993
|
+
disabled: pending,
|
|
994
|
+
className: cn("mb-3", workspace && "mx-auto max-w-3xl")
|
|
995
|
+
}
|
|
996
|
+
),
|
|
949
997
|
/* @__PURE__ */ jsx(
|
|
950
998
|
ChatInput,
|
|
951
999
|
{
|
|
@@ -8,14 +8,14 @@ import '@radix-ui/react-separator';
|
|
|
8
8
|
declare function ItemGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
9
9
|
declare function ItemSeparator({ className, ...props }: React.ComponentProps<typeof Separator>): react_jsx_runtime.JSX.Element;
|
|
10
10
|
declare const itemVariants: (props?: ({
|
|
11
|
-
variant?: "
|
|
11
|
+
variant?: "muted" | "default" | "outline" | null | undefined;
|
|
12
12
|
size?: "xs" | "sm" | "default" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
14
14
|
declare function Item({ className, variant, size, asChild, ...props }: React.ComponentProps<"div"> & VariantProps<typeof itemVariants> & {
|
|
15
15
|
asChild?: boolean;
|
|
16
16
|
}): react_jsx_runtime.JSX.Element;
|
|
17
17
|
declare const itemMediaVariants: (props?: ({
|
|
18
|
-
variant?: "
|
|
18
|
+
variant?: "icon" | "image" | "default" | null | undefined;
|
|
19
19
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
20
20
|
declare function ItemMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>): react_jsx_runtime.JSX.Element;
|
|
21
21
|
declare function ItemContent({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
@@ -8,14 +8,14 @@ import '@radix-ui/react-separator';
|
|
|
8
8
|
declare function ItemGroup({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
9
9
|
declare function ItemSeparator({ className, ...props }: React.ComponentProps<typeof Separator>): react_jsx_runtime.JSX.Element;
|
|
10
10
|
declare const itemVariants: (props?: ({
|
|
11
|
-
variant?: "
|
|
11
|
+
variant?: "muted" | "default" | "outline" | null | undefined;
|
|
12
12
|
size?: "xs" | "sm" | "default" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
14
14
|
declare function Item({ className, variant, size, asChild, ...props }: React.ComponentProps<"div"> & VariantProps<typeof itemVariants> & {
|
|
15
15
|
asChild?: boolean;
|
|
16
16
|
}): react_jsx_runtime.JSX.Element;
|
|
17
17
|
declare const itemMediaVariants: (props?: ({
|
|
18
|
-
variant?: "
|
|
18
|
+
variant?: "icon" | "image" | "default" | null | undefined;
|
|
19
19
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
20
20
|
declare function ItemMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>): react_jsx_runtime.JSX.Element;
|
|
21
21
|
declare function ItemContent({ className, ...props }: React.ComponentProps<"div">): react_jsx_runtime.JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare const sectionHeaderVariants: (props?: ({
|
|
7
7
|
variant?: "muted" | "plain" | null | undefined;
|
|
8
|
-
spacing?: "
|
|
8
|
+
spacing?: "tight" | "loose" | "none" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
interface SectionHeaderProps extends Omit<HTMLAttributes<HTMLHeadingElement>, "children">, VariantProps<typeof sectionHeaderVariants> {
|
|
11
11
|
/** Pre-rendered icon node. Sized to match the header text. */
|
|
@@ -5,7 +5,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare const sectionHeaderVariants: (props?: ({
|
|
7
7
|
variant?: "muted" | "plain" | null | undefined;
|
|
8
|
-
spacing?: "
|
|
8
|
+
spacing?: "tight" | "loose" | "none" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
interface SectionHeaderProps extends Omit<HTMLAttributes<HTMLHeadingElement>, "children">, VariantProps<typeof sectionHeaderVariants> {
|
|
11
11
|
/** Pre-rendered icon node. Sized to match the header text. */
|
|
@@ -5,11 +5,11 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare const ToggleGroup: React.ForwardRefExoticComponent<((Omit<ToggleGroupPrimitive.ToggleGroupSingleProps & React.RefAttributes<HTMLDivElement>, "ref"> | Omit<ToggleGroupPrimitive.ToggleGroupMultipleProps & React.RefAttributes<HTMLDivElement>, "ref">) & VariantProps<(props?: ({
|
|
7
7
|
variant?: "default" | "outline" | null | undefined;
|
|
8
|
-
size?: "
|
|
8
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string>) & React.RefAttributes<HTMLDivElement>>;
|
|
10
10
|
declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<ToggleGroupPrimitive.ToggleGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
11
11
|
variant?: "default" | "outline" | null | undefined;
|
|
12
|
-
size?: "
|
|
12
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
14
14
|
grow?: boolean;
|
|
15
15
|
textSize?: "inherit" | "xs";
|
|
@@ -5,11 +5,11 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
5
5
|
|
|
6
6
|
declare const ToggleGroup: React.ForwardRefExoticComponent<((Omit<ToggleGroupPrimitive.ToggleGroupSingleProps & React.RefAttributes<HTMLDivElement>, "ref"> | Omit<ToggleGroupPrimitive.ToggleGroupMultipleProps & React.RefAttributes<HTMLDivElement>, "ref">) & VariantProps<(props?: ({
|
|
7
7
|
variant?: "default" | "outline" | null | undefined;
|
|
8
|
-
size?: "
|
|
8
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string>) & React.RefAttributes<HTMLDivElement>>;
|
|
10
10
|
declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<ToggleGroupPrimitive.ToggleGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
11
11
|
variant?: "default" | "outline" | null | undefined;
|
|
12
|
-
size?: "
|
|
12
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
14
14
|
grow?: boolean;
|
|
15
15
|
textSize?: "inherit" | "xs";
|
|
@@ -5,11 +5,11 @@ import * as React from 'react';
|
|
|
5
5
|
|
|
6
6
|
declare const toggleVariants: (props?: ({
|
|
7
7
|
variant?: "default" | "outline" | null | undefined;
|
|
8
|
-
size?: "
|
|
8
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare const Toggle: React.ForwardRefExoticComponent<Omit<TogglePrimitive.ToggleProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
11
11
|
variant?: "default" | "outline" | null | undefined;
|
|
12
|
-
size?: "
|
|
12
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
14
14
|
|
|
15
15
|
export { Toggle, toggleVariants };
|
|
@@ -5,11 +5,11 @@ import * as React from 'react';
|
|
|
5
5
|
|
|
6
6
|
declare const toggleVariants: (props?: ({
|
|
7
7
|
variant?: "default" | "outline" | null | undefined;
|
|
8
|
-
size?: "
|
|
8
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare const Toggle: React.ForwardRefExoticComponent<Omit<TogglePrimitive.ToggleProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
11
11
|
variant?: "default" | "outline" | null | undefined;
|
|
12
|
-
size?: "
|
|
12
|
+
size?: "icon" | "sm" | "lg" | "default" | "icon-lg" | "avatar" | "chip" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLButtonElement>>;
|
|
14
14
|
|
|
15
15
|
export { Toggle, toggleVariants };
|
package/package.json
CHANGED