@cimplify/sdk 0.98.0 → 0.99.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/dist/react.d.mts +128 -92
- package/dist/react.d.ts +128 -92
- package/dist/react.js +1807 -1453
- package/dist/react.mjs +1802 -1454
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/chat-widget.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -1263,10 +1263,10 @@ function CimplifyProvider({
|
|
|
1263
1263
|
}, [resolvedClient, baseCurrency, displayCurrency]);
|
|
1264
1264
|
const convertPrice = useCallback(
|
|
1265
1265
|
(amount) => {
|
|
1266
|
-
const
|
|
1267
|
-
if (isNaN(
|
|
1268
|
-
if (!fxRate || displayCurrency === baseCurrency) return
|
|
1269
|
-
return Math.round(
|
|
1266
|
+
const num2 = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
1267
|
+
if (isNaN(num2)) return 0;
|
|
1268
|
+
if (!fxRate || displayCurrency === baseCurrency) return num2;
|
|
1269
|
+
return Math.round(num2 * fxRate * 100) / 100;
|
|
1270
1270
|
},
|
|
1271
1271
|
[fxRate, displayCurrency, baseCurrency]
|
|
1272
1272
|
);
|
|
@@ -2714,8 +2714,8 @@ var STATE_TONE = {
|
|
|
2714
2714
|
cancelled: "text-destructive",
|
|
2715
2715
|
default: "text-foreground/55"
|
|
2716
2716
|
};
|
|
2717
|
-
function StateDot({ label, tone = "default" }) {
|
|
2718
|
-
return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-1.5 text-xs font-medium", STATE_TONE[
|
|
2717
|
+
function StateDot({ label, tone: tone2 = "default" }) {
|
|
2718
|
+
return /* @__PURE__ */ jsxs("span", { className: cn("inline-flex items-center gap-1.5 text-xs font-medium", STATE_TONE[tone2]), children: [
|
|
2719
2719
|
/* @__PURE__ */ jsx("span", { className: "size-1.5 rounded-full bg-current" }),
|
|
2720
2720
|
label
|
|
2721
2721
|
] });
|
|
@@ -17113,11 +17113,11 @@ function installHooks() {
|
|
|
17113
17113
|
}
|
|
17114
17114
|
var CACHE_LIMIT = 300;
|
|
17115
17115
|
var cache = /* @__PURE__ */ new Map();
|
|
17116
|
-
function renderMarkdown(
|
|
17117
|
-
const cached = cache.get(
|
|
17116
|
+
function renderMarkdown(text2) {
|
|
17117
|
+
const cached = cache.get(text2);
|
|
17118
17118
|
if (cached !== void 0) return cached;
|
|
17119
17119
|
installHooks();
|
|
17120
|
-
const html = DOMPurify.sanitize(marked.parse(
|
|
17120
|
+
const html = DOMPurify.sanitize(marked.parse(text2), {
|
|
17121
17121
|
ALLOWED_TAGS,
|
|
17122
17122
|
ALLOWED_ATTR
|
|
17123
17123
|
});
|
|
@@ -17125,15 +17125,15 @@ function renderMarkdown(text) {
|
|
|
17125
17125
|
const oldest = cache.keys().next().value;
|
|
17126
17126
|
if (oldest !== void 0) cache.delete(oldest);
|
|
17127
17127
|
}
|
|
17128
|
-
cache.set(
|
|
17128
|
+
cache.set(text2, html);
|
|
17129
17129
|
return html;
|
|
17130
17130
|
}
|
|
17131
|
-
function SettledMarkdown({ text, className }) {
|
|
17132
|
-
const html = useMemo(() => renderMarkdown(
|
|
17131
|
+
function SettledMarkdown({ text: text2, className }) {
|
|
17132
|
+
const html = useMemo(() => renderMarkdown(text2), [text2]);
|
|
17133
17133
|
return /* @__PURE__ */ jsx("div", { className, dangerouslySetInnerHTML: { __html: html } });
|
|
17134
17134
|
}
|
|
17135
17135
|
var ChatMarkdown = memo(function ChatMarkdown2({
|
|
17136
|
-
text,
|
|
17136
|
+
text: text2,
|
|
17137
17137
|
className,
|
|
17138
17138
|
streaming = false
|
|
17139
17139
|
}) {
|
|
@@ -17141,10 +17141,10 @@ var ChatMarkdown = memo(function ChatMarkdown2({
|
|
|
17141
17141
|
return (
|
|
17142
17142
|
// While the streamdown chunk loads, the settled renderer keeps the
|
|
17143
17143
|
// text on screen — no blank frame at stream start.
|
|
17144
|
-
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(SettledMarkdown, { text, className }), children: /* @__PURE__ */ jsx(Streamdown, { className, children:
|
|
17144
|
+
/* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(SettledMarkdown, { text: text2, className }), children: /* @__PURE__ */ jsx(Streamdown, { className, children: text2 }) })
|
|
17145
17145
|
);
|
|
17146
17146
|
}
|
|
17147
|
-
return /* @__PURE__ */ jsx(SettledMarkdown, { text, className });
|
|
17147
|
+
return /* @__PURE__ */ jsx(SettledMarkdown, { text: text2, className });
|
|
17148
17148
|
});
|
|
17149
17149
|
|
|
17150
17150
|
// src/react/chat-kit/motion.ts
|
|
@@ -17719,943 +17719,6 @@ function deliveryState(message, timeline, seenAt, isOptimistic2) {
|
|
|
17719
17719
|
const seenCutoff = timeline.firstAgentMessageId ? seenAt : null;
|
|
17720
17720
|
return seenCutoff && message.created_at <= seenCutoff ? "seen" : "delivered";
|
|
17721
17721
|
}
|
|
17722
|
-
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
17723
|
-
var KEEP_DISMISSED_KEY = "cimplify_keep_chat_dismissed";
|
|
17724
|
-
var AT_BOTTOM_SLACK_PX = 48;
|
|
17725
|
-
function ChatWidget({
|
|
17726
|
-
client: clientProp,
|
|
17727
|
-
businessName = "Support",
|
|
17728
|
-
greeting = "Hi there!",
|
|
17729
|
-
subtitle = "Ask us anything \u2014 we typically reply in a few seconds.",
|
|
17730
|
-
starters,
|
|
17731
|
-
placeholder = "Type a message\u2026",
|
|
17732
|
-
position = "bottom-right",
|
|
17733
|
-
defaultOpen = false,
|
|
17734
|
-
pollInterval,
|
|
17735
|
-
className,
|
|
17736
|
-
classNames
|
|
17737
|
-
}) {
|
|
17738
|
-
const context = useOptionalCimplifyClient();
|
|
17739
|
-
const client = clientProp ?? context?.client;
|
|
17740
|
-
const [open, setOpen] = useState(defaultOpen);
|
|
17741
|
-
const [readAt, setReadAt] = useState(null);
|
|
17742
|
-
const [attachError, setAttachError] = useState(null);
|
|
17743
|
-
const [replyTo, setReplyTo] = useState(null);
|
|
17744
|
-
const {
|
|
17745
|
-
messages,
|
|
17746
|
-
isLoading,
|
|
17747
|
-
isSending,
|
|
17748
|
-
awaitingReply,
|
|
17749
|
-
seenAt,
|
|
17750
|
-
error,
|
|
17751
|
-
isActive,
|
|
17752
|
-
humanRequested,
|
|
17753
|
-
claimedAs,
|
|
17754
|
-
signOut,
|
|
17755
|
-
send,
|
|
17756
|
-
react,
|
|
17757
|
-
requestHuman,
|
|
17758
|
-
startConversation,
|
|
17759
|
-
loadOlder,
|
|
17760
|
-
mayHaveOlder,
|
|
17761
|
-
openIfExisting
|
|
17762
|
-
} = useChat({ client, pollInterval });
|
|
17763
|
-
const showTyping = isSending || awaitingReply;
|
|
17764
|
-
const attachPhoto = useCallback(
|
|
17765
|
-
async (file) => {
|
|
17766
|
-
if (!client) return;
|
|
17767
|
-
setAttachError(null);
|
|
17768
|
-
if (!file.type.startsWith("image/")) {
|
|
17769
|
-
setAttachError(new CimplifyError("invalid_attachment", "Only photos can be attached."));
|
|
17770
|
-
return;
|
|
17771
|
-
}
|
|
17772
|
-
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
17773
|
-
setAttachError(new CimplifyError("attachment_too_large", "Photos must be under 5MB."));
|
|
17774
|
-
return;
|
|
17775
|
-
}
|
|
17776
|
-
const uploaded = await client.uploads.upload(file);
|
|
17777
|
-
if (!uploaded.ok) {
|
|
17778
|
-
setAttachError(uploaded.error);
|
|
17779
|
-
return;
|
|
17780
|
-
}
|
|
17781
|
-
const attachment = {
|
|
17782
|
-
upload_id: uploaded.value.id,
|
|
17783
|
-
url: uploaded.value.url,
|
|
17784
|
-
mime_type: uploaded.value.content_type,
|
|
17785
|
-
filename: uploaded.value.filename,
|
|
17786
|
-
size: uploaded.value.size_bytes
|
|
17787
|
-
};
|
|
17788
|
-
await send("", { attachments: [attachment] });
|
|
17789
|
-
},
|
|
17790
|
-
[client, send]
|
|
17791
|
-
);
|
|
17792
|
-
const handleSend = useCallback(
|
|
17793
|
-
(content) => {
|
|
17794
|
-
setAttachError(null);
|
|
17795
|
-
const target = replyTo;
|
|
17796
|
-
setReplyTo(null);
|
|
17797
|
-
return send(content, target ? { replyTo: target } : void 0);
|
|
17798
|
-
},
|
|
17799
|
-
[send, replyTo]
|
|
17800
|
-
);
|
|
17801
|
-
const unread = open ? 0 : messages.filter(
|
|
17802
|
-
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
17803
|
-
).length;
|
|
17804
|
-
useState(() => {
|
|
17805
|
-
if (defaultOpen) void openIfExisting();
|
|
17806
|
-
});
|
|
17807
|
-
const handleOpen = useCallback(() => {
|
|
17808
|
-
setOpen(true);
|
|
17809
|
-
void openIfExisting();
|
|
17810
|
-
}, [openIfExisting]);
|
|
17811
|
-
const handleClose = useCallback(() => {
|
|
17812
|
-
const newest = messages[messages.length - 1];
|
|
17813
|
-
setReadAt(newest?.created_at ?? null);
|
|
17814
|
-
setOpen(false);
|
|
17815
|
-
}, [messages]);
|
|
17816
|
-
const isLeft = position === "bottom-left";
|
|
17817
|
-
return /* @__PURE__ */ jsxs(
|
|
17818
|
-
"div",
|
|
17819
|
-
{
|
|
17820
|
-
className: cn(
|
|
17821
|
-
"fixed bottom-6 z-[9999]",
|
|
17822
|
-
isLeft ? "left-6" : "right-6",
|
|
17823
|
-
className,
|
|
17824
|
-
classNames?.root
|
|
17825
|
-
),
|
|
17826
|
-
children: [
|
|
17827
|
-
!open && /* @__PURE__ */ jsx(
|
|
17828
|
-
Bubble,
|
|
17829
|
-
{
|
|
17830
|
-
unread,
|
|
17831
|
-
onClick: handleOpen,
|
|
17832
|
-
className: classNames?.bubble
|
|
17833
|
-
}
|
|
17834
|
-
),
|
|
17835
|
-
open && /* @__PURE__ */ jsx(
|
|
17836
|
-
Panel,
|
|
17837
|
-
{
|
|
17838
|
-
client,
|
|
17839
|
-
businessName,
|
|
17840
|
-
greeting,
|
|
17841
|
-
subtitle,
|
|
17842
|
-
starters,
|
|
17843
|
-
placeholder,
|
|
17844
|
-
messages,
|
|
17845
|
-
isLoading,
|
|
17846
|
-
isSending,
|
|
17847
|
-
isTyping: showTyping,
|
|
17848
|
-
seenAt,
|
|
17849
|
-
readAt,
|
|
17850
|
-
error: error ?? attachError,
|
|
17851
|
-
isActive,
|
|
17852
|
-
isLeft,
|
|
17853
|
-
humanRequested,
|
|
17854
|
-
mayHaveOlder,
|
|
17855
|
-
replyTo,
|
|
17856
|
-
claimedAs,
|
|
17857
|
-
onSignOut: () => void signOut(),
|
|
17858
|
-
onClose: handleClose,
|
|
17859
|
-
onSend: handleSend,
|
|
17860
|
-
onReact: react,
|
|
17861
|
-
onReply: setReplyTo,
|
|
17862
|
-
onCancelReply: () => setReplyTo(null),
|
|
17863
|
-
onRequestHuman: () => void requestHuman(),
|
|
17864
|
-
onLoadOlder: () => void loadOlder(),
|
|
17865
|
-
onAttach: attachPhoto,
|
|
17866
|
-
onStartConversation: startConversation,
|
|
17867
|
-
classNames
|
|
17868
|
-
}
|
|
17869
|
-
)
|
|
17870
|
-
]
|
|
17871
|
-
}
|
|
17872
|
-
);
|
|
17873
|
-
}
|
|
17874
|
-
function Bubble({
|
|
17875
|
-
unread,
|
|
17876
|
-
onClick,
|
|
17877
|
-
className
|
|
17878
|
-
}) {
|
|
17879
|
-
return /* @__PURE__ */ jsxs(
|
|
17880
|
-
"button",
|
|
17881
|
-
{
|
|
17882
|
-
type: "button",
|
|
17883
|
-
onClick,
|
|
17884
|
-
"aria-label": "Open chat",
|
|
17885
|
-
className: cn(
|
|
17886
|
-
"relative flex h-14 w-14 items-center justify-center rounded-full",
|
|
17887
|
-
"bg-foreground text-background shadow-lg",
|
|
17888
|
-
"transition-transform duration-300 ease-out",
|
|
17889
|
-
"hover:scale-[1.08] active:scale-95",
|
|
17890
|
-
className
|
|
17891
|
-
),
|
|
17892
|
-
children: [
|
|
17893
|
-
/* @__PURE__ */ jsx(ChatIcon, { className: "h-6 w-6" }),
|
|
17894
|
-
unread > 0 && /* @__PURE__ */ jsx(
|
|
17895
|
-
"span",
|
|
17896
|
-
{
|
|
17897
|
-
className: cn(
|
|
17898
|
-
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
17899
|
-
"rounded-full bg-destructive text-[10px] font-bold text-white",
|
|
17900
|
-
"ring-2 ring-background"
|
|
17901
|
-
),
|
|
17902
|
-
children: unread > 9 ? "9+" : unread
|
|
17903
|
-
}
|
|
17904
|
-
)
|
|
17905
|
-
]
|
|
17906
|
-
}
|
|
17907
|
-
);
|
|
17908
|
-
}
|
|
17909
|
-
function Panel({
|
|
17910
|
-
client,
|
|
17911
|
-
businessName,
|
|
17912
|
-
greeting,
|
|
17913
|
-
subtitle,
|
|
17914
|
-
starters,
|
|
17915
|
-
placeholder,
|
|
17916
|
-
messages,
|
|
17917
|
-
isLoading,
|
|
17918
|
-
isSending,
|
|
17919
|
-
isTyping,
|
|
17920
|
-
seenAt,
|
|
17921
|
-
readAt,
|
|
17922
|
-
error,
|
|
17923
|
-
isActive,
|
|
17924
|
-
isLeft,
|
|
17925
|
-
humanRequested,
|
|
17926
|
-
mayHaveOlder,
|
|
17927
|
-
replyTo,
|
|
17928
|
-
claimedAs,
|
|
17929
|
-
onSignOut,
|
|
17930
|
-
onClose,
|
|
17931
|
-
onSend,
|
|
17932
|
-
onReact,
|
|
17933
|
-
onReply,
|
|
17934
|
-
onCancelReply,
|
|
17935
|
-
onRequestHuman,
|
|
17936
|
-
onLoadOlder,
|
|
17937
|
-
onAttach,
|
|
17938
|
-
onStartConversation,
|
|
17939
|
-
classNames
|
|
17940
|
-
}) {
|
|
17941
|
-
return /* @__PURE__ */ jsxs(
|
|
17942
|
-
"div",
|
|
17943
|
-
{
|
|
17944
|
-
className: cn(
|
|
17945
|
-
"flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
|
|
17946
|
-
"w-[400px] max-w-[calc(100vw-3rem)]",
|
|
17947
|
-
"h-[min(600px,calc(100vh-6rem))]",
|
|
17948
|
-
"animate-in fade-in slide-in-from-bottom-4 duration-300",
|
|
17949
|
-
isLeft ? "origin-bottom-left" : "origin-bottom-right",
|
|
17950
|
-
classNames?.panel
|
|
17951
|
-
),
|
|
17952
|
-
children: [
|
|
17953
|
-
/* @__PURE__ */ jsx(
|
|
17954
|
-
Header,
|
|
17955
|
-
{
|
|
17956
|
-
businessName,
|
|
17957
|
-
claimedAs,
|
|
17958
|
-
onSignOut,
|
|
17959
|
-
onClose,
|
|
17960
|
-
className: classNames?.header
|
|
17961
|
-
}
|
|
17962
|
-
),
|
|
17963
|
-
!isActive && !isLoading ? /* @__PURE__ */ jsx(
|
|
17964
|
-
Welcome,
|
|
17965
|
-
{
|
|
17966
|
-
greeting,
|
|
17967
|
-
subtitle,
|
|
17968
|
-
starters,
|
|
17969
|
-
onStarter: onStartConversation,
|
|
17970
|
-
className: classNames?.welcome
|
|
17971
|
-
}
|
|
17972
|
-
) : /* @__PURE__ */ jsx(
|
|
17973
|
-
MessageList,
|
|
17974
|
-
{
|
|
17975
|
-
messages,
|
|
17976
|
-
businessName,
|
|
17977
|
-
isLoading,
|
|
17978
|
-
isSending: isTyping,
|
|
17979
|
-
seenAt,
|
|
17980
|
-
readAt,
|
|
17981
|
-
humanRequested,
|
|
17982
|
-
mayHaveOlder,
|
|
17983
|
-
onReact,
|
|
17984
|
-
onReply,
|
|
17985
|
-
onRequestHuman,
|
|
17986
|
-
onLoadOlder,
|
|
17987
|
-
className: classNames?.messages
|
|
17988
|
-
}
|
|
17989
|
-
),
|
|
17990
|
-
isActive && !claimedAs && client && /* @__PURE__ */ jsx(KeepConversation, { client }),
|
|
17991
|
-
humanRequested && isActive && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-t border-border bg-muted/50 px-4 py-2 text-[11px] text-muted-foreground", children: [
|
|
17992
|
-
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 flex-shrink-0 animate-pulse rounded-full bg-amber-500" }),
|
|
17993
|
-
"Waiting for ",
|
|
17994
|
-
businessName,
|
|
17995
|
-
" \u2014 usually replies in minutes"
|
|
17996
|
-
] }),
|
|
17997
|
-
error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-destructive/20 bg-destructive/10 px-4 py-2 text-xs text-destructive", children: error.userMessage || "Something went wrong. Please try again." }),
|
|
17998
|
-
replyTo && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 border-t border-border bg-muted/50 px-4 py-2", children: [
|
|
17999
|
-
/* @__PURE__ */ jsx("span", { className: "w-[3px] shrink-0 self-stretch rounded-full bg-foreground/40" }),
|
|
18000
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
18001
|
-
/* @__PURE__ */ jsxs("div", { className: "text-[11px] font-semibold text-foreground", children: [
|
|
18002
|
-
"Replying to ",
|
|
18003
|
-
replyTo.sender_type === "customer" ? "yourself" : businessName
|
|
18004
|
-
] }),
|
|
18005
|
-
/* @__PURE__ */ jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: proseOf(replyTo.content) || "Photo" })
|
|
18006
|
-
] }),
|
|
18007
|
-
/* @__PURE__ */ jsx(
|
|
18008
|
-
"button",
|
|
18009
|
-
{
|
|
18010
|
-
type: "button",
|
|
18011
|
-
"aria-label": "Cancel reply",
|
|
18012
|
-
onClick: onCancelReply,
|
|
18013
|
-
className: "flex-shrink-0 p-0.5 text-muted-foreground hover:text-foreground",
|
|
18014
|
-
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
18015
|
-
}
|
|
18016
|
-
)
|
|
18017
|
-
] }),
|
|
18018
|
-
/* @__PURE__ */ jsx(
|
|
18019
|
-
ChatInput,
|
|
18020
|
-
{
|
|
18021
|
-
placeholder,
|
|
18022
|
-
isSending,
|
|
18023
|
-
onSend: isActive ? onSend : async (text) => {
|
|
18024
|
-
await onStartConversation(text);
|
|
18025
|
-
return true;
|
|
18026
|
-
},
|
|
18027
|
-
onAttach,
|
|
18028
|
-
className: classNames?.input
|
|
18029
|
-
}
|
|
18030
|
-
),
|
|
18031
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-background px-4 py-1.5 text-center text-[10px] text-muted-foreground", children: [
|
|
18032
|
-
"Powered by",
|
|
18033
|
-
" ",
|
|
18034
|
-
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
|
|
18035
|
-
] })
|
|
18036
|
-
]
|
|
18037
|
-
}
|
|
18038
|
-
);
|
|
18039
|
-
}
|
|
18040
|
-
function Header({
|
|
18041
|
-
businessName,
|
|
18042
|
-
claimedAs,
|
|
18043
|
-
onSignOut,
|
|
18044
|
-
onClose,
|
|
18045
|
-
className
|
|
18046
|
-
}) {
|
|
18047
|
-
return /* @__PURE__ */ jsxs(
|
|
18048
|
-
"div",
|
|
18049
|
-
{
|
|
18050
|
-
className: cn(
|
|
18051
|
-
"flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
|
|
18052
|
-
className
|
|
18053
|
-
),
|
|
18054
|
-
children: [
|
|
18055
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-background/15", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
18056
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
18057
|
-
/* @__PURE__ */ jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
18058
|
-
claimedAs ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-background/60", children: [
|
|
18059
|
-
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
18060
|
-
"Chatting as ",
|
|
18061
|
-
claimedAs
|
|
18062
|
-
] }),
|
|
18063
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
18064
|
-
/* @__PURE__ */ jsx(
|
|
18065
|
-
"button",
|
|
18066
|
-
{
|
|
18067
|
-
type: "button",
|
|
18068
|
-
onClick: onSignOut,
|
|
18069
|
-
className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
|
|
18070
|
-
children: "Not you?"
|
|
18071
|
-
}
|
|
18072
|
-
)
|
|
18073
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-background/60", children: [
|
|
18074
|
-
/* @__PURE__ */ jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
18075
|
-
"Online"
|
|
18076
|
-
] })
|
|
18077
|
-
] }),
|
|
18078
|
-
/* @__PURE__ */ jsx(
|
|
18079
|
-
"button",
|
|
18080
|
-
{
|
|
18081
|
-
type: "button",
|
|
18082
|
-
onClick: onClose,
|
|
18083
|
-
"aria-label": "Close chat",
|
|
18084
|
-
className: cn(
|
|
18085
|
-
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
18086
|
-
"bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
|
|
18087
|
-
),
|
|
18088
|
-
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
18089
|
-
}
|
|
18090
|
-
)
|
|
18091
|
-
]
|
|
18092
|
-
}
|
|
18093
|
-
);
|
|
18094
|
-
}
|
|
18095
|
-
function Welcome({
|
|
18096
|
-
greeting,
|
|
18097
|
-
subtitle,
|
|
18098
|
-
starters,
|
|
18099
|
-
onStarter,
|
|
18100
|
-
className
|
|
18101
|
-
}) {
|
|
18102
|
-
const defaultStarters = [
|
|
18103
|
-
{ icon: "\u{1F50D}", text: "Help me find a product" },
|
|
18104
|
-
{ icon: "\u{1F6D2}", text: "Check my cart" },
|
|
18105
|
-
{ icon: "\u{1F4E6}", text: "Track my order" }
|
|
18106
|
-
];
|
|
18107
|
-
const items = starters ?? defaultStarters;
|
|
18108
|
-
return /* @__PURE__ */ jsxs(
|
|
18109
|
-
"div",
|
|
18110
|
-
{
|
|
18111
|
-
className: cn(
|
|
18112
|
-
"flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
|
|
18113
|
-
className
|
|
18114
|
-
),
|
|
18115
|
-
children: [
|
|
18116
|
-
/* @__PURE__ */ jsx("div", { className: "mb-5 flex h-16 w-16 items-center justify-center rounded-2xl bg-muted", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-7 w-7 text-muted-foreground" }) }),
|
|
18117
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
|
|
18118
|
-
/* @__PURE__ */ jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
|
|
18119
|
-
/* @__PURE__ */ jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxs(
|
|
18120
|
-
"button",
|
|
18121
|
-
{
|
|
18122
|
-
type: "button",
|
|
18123
|
-
onClick: () => onStarter(s.text),
|
|
18124
|
-
className: cn(
|
|
18125
|
-
"flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
|
|
18126
|
-
"text-left text-[13px] font-medium text-foreground",
|
|
18127
|
-
"transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
|
|
18128
|
-
),
|
|
18129
|
-
children: [
|
|
18130
|
-
s.icon && /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-muted text-[15px]", children: s.icon }),
|
|
18131
|
-
/* @__PURE__ */ jsx("span", { children: s.text })
|
|
18132
|
-
]
|
|
18133
|
-
},
|
|
18134
|
-
s.text
|
|
18135
|
-
)) })
|
|
18136
|
-
]
|
|
18137
|
-
}
|
|
18138
|
-
);
|
|
18139
|
-
}
|
|
18140
|
-
var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
|
|
18141
|
-
function LetterAvatar({ name }) {
|
|
18142
|
-
return /* @__PURE__ */ jsx("span", { className: "flex size-6 items-center justify-center rounded-full bg-foreground text-[10px] font-bold text-background", children: name.charAt(0).toUpperCase() });
|
|
18143
|
-
}
|
|
18144
|
-
function MessageList({
|
|
18145
|
-
messages,
|
|
18146
|
-
businessName,
|
|
18147
|
-
isLoading,
|
|
18148
|
-
isSending,
|
|
18149
|
-
seenAt,
|
|
18150
|
-
readAt,
|
|
18151
|
-
humanRequested,
|
|
18152
|
-
mayHaveOlder,
|
|
18153
|
-
onReact,
|
|
18154
|
-
onReply,
|
|
18155
|
-
onRequestHuman,
|
|
18156
|
-
onLoadOlder,
|
|
18157
|
-
className
|
|
18158
|
-
}) {
|
|
18159
|
-
const scrolledUpRef = useRef(false);
|
|
18160
|
-
const timeline = chatTimeline(messages, { readAt });
|
|
18161
|
-
const { bubbles, reactionsByTarget, firstUnreadId, unreadOnOpen, firstAgentMessageId } = timeline;
|
|
18162
|
-
const lastBubble = bubbles[bubbles.length - 1];
|
|
18163
|
-
const offerHuman = !humanRequested && lastBubble?.sender_type === "bot" && !isSending;
|
|
18164
|
-
const handleScroll = (event) => {
|
|
18165
|
-
const node = event.currentTarget;
|
|
18166
|
-
scrolledUpRef.current = node.scrollHeight - node.scrollTop - node.clientHeight >= AT_BOTTOM_SLACK_PX;
|
|
18167
|
-
};
|
|
18168
|
-
return /* @__PURE__ */ jsxs(
|
|
18169
|
-
"div",
|
|
18170
|
-
{
|
|
18171
|
-
onScroll: handleScroll,
|
|
18172
|
-
style: { background: "var(--color-chat-canvas, var(--color-muted, #f3f5f3))" },
|
|
18173
|
-
className: cn("flex flex-1 flex-col gap-0.5 overflow-y-auto px-3.5 py-3.5", className),
|
|
18174
|
-
children: [
|
|
18175
|
-
isLoading && messages.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
18176
|
-
mayHaveOlder && /* @__PURE__ */ jsx("div", { className: "flex justify-center pb-1", children: /* @__PURE__ */ jsx(
|
|
18177
|
-
"button",
|
|
18178
|
-
{
|
|
18179
|
-
type: "button",
|
|
18180
|
-
onClick: onLoadOlder,
|
|
18181
|
-
className: "rounded-full border border-foreground/[0.06] bg-background px-3.5 py-[7px] text-xs font-semibold text-muted-foreground shadow-[0_1px_2px_rgba(15,23,42,0.04)]",
|
|
18182
|
-
children: "Show earlier messages"
|
|
18183
|
-
}
|
|
18184
|
-
) }),
|
|
18185
|
-
bubbles.map((msg, i) => {
|
|
18186
|
-
const prev = bubbles[i - 1];
|
|
18187
|
-
const newDay = !prev || !sameDay(prev.created_at, msg.created_at);
|
|
18188
|
-
const group = groupPosition(timeline, i);
|
|
18189
|
-
const startsRun = group === "single" || group === "first";
|
|
18190
|
-
const delivery = deliveryState(msg, timeline, seenAt, isOptimistic);
|
|
18191
|
-
return /* @__PURE__ */ jsxs(
|
|
18192
|
-
"div",
|
|
18193
|
-
{
|
|
18194
|
-
className: cn("flex flex-col gap-1.5", startsRun && i > 0 && "mt-2.5"),
|
|
18195
|
-
children: [
|
|
18196
|
-
newDay && /* @__PURE__ */ jsx(Seam, { variant: "chip", label: dayLabel(msg.created_at) }),
|
|
18197
|
-
msg.id === firstUnreadId && /* @__PURE__ */ jsx(
|
|
18198
|
-
Seam,
|
|
18199
|
-
{
|
|
18200
|
-
variant: "unread",
|
|
18201
|
-
label: `${unreadOnOpen} new message${unreadOnOpen === 1 ? "" : "s"}`
|
|
18202
|
-
}
|
|
18203
|
-
),
|
|
18204
|
-
msg.id === firstAgentMessageId && /* @__PURE__ */ jsx(Seam, { label: "A person joined" }),
|
|
18205
|
-
msg.sender_type === "system" ? /* @__PURE__ */ jsx(Seam, { label: "Passing you to the team" }) : /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
|
|
18206
|
-
msg.sender_type !== "customer" && !isOptimistic(msg) && /* @__PURE__ */ jsx(HoverRail, { message: msg, onReact, onReply }),
|
|
18207
|
-
/* @__PURE__ */ jsx(
|
|
18208
|
-
MessageBubble,
|
|
18209
|
-
{
|
|
18210
|
-
message: msg,
|
|
18211
|
-
reactions: reactionsByTarget.get(msg.id),
|
|
18212
|
-
businessName,
|
|
18213
|
-
group,
|
|
18214
|
-
delivery,
|
|
18215
|
-
onReply,
|
|
18216
|
-
avatar: /* @__PURE__ */ jsx(LetterAvatar, { name: businessName }),
|
|
18217
|
-
onQuoteMiss: async () => {
|
|
18218
|
-
onLoadOlder();
|
|
18219
|
-
return true;
|
|
18220
|
-
}
|
|
18221
|
-
}
|
|
18222
|
-
)
|
|
18223
|
-
] })
|
|
18224
|
-
]
|
|
18225
|
-
},
|
|
18226
|
-
msg.id
|
|
18227
|
-
);
|
|
18228
|
-
}),
|
|
18229
|
-
isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
18230
|
-
offerHuman && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
|
|
18231
|
-
"button",
|
|
18232
|
-
{
|
|
18233
|
-
type: "button",
|
|
18234
|
-
onClick: onRequestHuman,
|
|
18235
|
-
className: "rounded-full border border-border bg-background px-3 py-1.5 text-xs font-semibold text-foreground hover:border-foreground",
|
|
18236
|
-
children: "Talk to a person"
|
|
18237
|
-
}
|
|
18238
|
-
) }),
|
|
18239
|
-
/* @__PURE__ */ jsx(
|
|
18240
|
-
"div",
|
|
18241
|
-
{
|
|
18242
|
-
ref: (node) => {
|
|
18243
|
-
if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
|
|
18244
|
-
}
|
|
18245
|
-
},
|
|
18246
|
-
`${messages.length}:${isSending}`
|
|
18247
|
-
)
|
|
18248
|
-
]
|
|
18249
|
-
}
|
|
18250
|
-
);
|
|
18251
|
-
}
|
|
18252
|
-
function HoverRail({
|
|
18253
|
-
message,
|
|
18254
|
-
onReact,
|
|
18255
|
-
onReply
|
|
18256
|
-
}) {
|
|
18257
|
-
return /* @__PURE__ */ jsxs("div", { className: "absolute -top-3 right-0 z-10 hidden gap-0.5 rounded-full border border-border bg-background px-1 py-0.5 shadow-sm group-hover:flex", children: [
|
|
18258
|
-
QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
18259
|
-
"button",
|
|
18260
|
-
{
|
|
18261
|
-
type: "button",
|
|
18262
|
-
"aria-label": `React ${emoji}`,
|
|
18263
|
-
onClick: () => void onReact(message, emoji),
|
|
18264
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
18265
|
-
children: emoji
|
|
18266
|
-
},
|
|
18267
|
-
emoji
|
|
18268
|
-
)),
|
|
18269
|
-
/* @__PURE__ */ jsx(
|
|
18270
|
-
"button",
|
|
18271
|
-
{
|
|
18272
|
-
type: "button",
|
|
18273
|
-
"aria-label": "Reply",
|
|
18274
|
-
onClick: () => onReply(message),
|
|
18275
|
-
className: "flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
18276
|
-
children: /* @__PURE__ */ jsx(ReplyIcon, { className: "h-3.5 w-3.5" })
|
|
18277
|
-
}
|
|
18278
|
-
)
|
|
18279
|
-
] });
|
|
18280
|
-
}
|
|
18281
|
-
function readKeepDismissed() {
|
|
18282
|
-
try {
|
|
18283
|
-
return typeof window !== "undefined" && window.localStorage.getItem(KEEP_DISMISSED_KEY) === "1";
|
|
18284
|
-
} catch {
|
|
18285
|
-
return false;
|
|
18286
|
-
}
|
|
18287
|
-
}
|
|
18288
|
-
function writeKeepDismissed() {
|
|
18289
|
-
try {
|
|
18290
|
-
window.localStorage.setItem(KEEP_DISMISSED_KEY, "1");
|
|
18291
|
-
} catch {
|
|
18292
|
-
}
|
|
18293
|
-
}
|
|
18294
|
-
function KeepConversation({ client }) {
|
|
18295
|
-
const [step, setStep] = useState("row");
|
|
18296
|
-
const [dismissed, setDismissed] = useState(readKeepDismissed);
|
|
18297
|
-
const [contact, setContact] = useState("");
|
|
18298
|
-
const [code, setCode] = useState("");
|
|
18299
|
-
const [busy, setBusy] = useState(false);
|
|
18300
|
-
const [verifyError, setVerifyError] = useState(null);
|
|
18301
|
-
if (dismissed) return null;
|
|
18302
|
-
const contactType = contact.includes("@") ? "email" : "phone";
|
|
18303
|
-
const requestCode = async () => {
|
|
18304
|
-
const trimmed = contact.trim();
|
|
18305
|
-
if (!trimmed || busy) return;
|
|
18306
|
-
setBusy(true);
|
|
18307
|
-
setVerifyError(null);
|
|
18308
|
-
const result = await client.link.requestOtp({ contact: trimmed, contact_type: contactType });
|
|
18309
|
-
setBusy(false);
|
|
18310
|
-
if (result.ok) {
|
|
18311
|
-
setCode("");
|
|
18312
|
-
setStep("code");
|
|
18313
|
-
} else {
|
|
18314
|
-
setVerifyError(result.error.userMessage || "Couldn't send a code \u2014 try again.");
|
|
18315
|
-
}
|
|
18316
|
-
};
|
|
18317
|
-
const verify = async (value) => {
|
|
18318
|
-
if (value.length !== 6 || busy) return;
|
|
18319
|
-
setBusy(true);
|
|
18320
|
-
setVerifyError(null);
|
|
18321
|
-
const result = await client.link.verifyOtp({
|
|
18322
|
-
contact: contact.trim(),
|
|
18323
|
-
contact_type: contactType,
|
|
18324
|
-
otp_code: value
|
|
18325
|
-
});
|
|
18326
|
-
setBusy(false);
|
|
18327
|
-
if (!result.ok) {
|
|
18328
|
-
setCode("");
|
|
18329
|
-
setVerifyError(result.error.userMessage || "Invalid or expired code.");
|
|
18330
|
-
}
|
|
18331
|
-
};
|
|
18332
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-muted/50 px-4 py-2.5", children: [
|
|
18333
|
-
step === "row" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
18334
|
-
/* @__PURE__ */ jsxs(
|
|
18335
|
-
"button",
|
|
18336
|
-
{
|
|
18337
|
-
type: "button",
|
|
18338
|
-
onClick: () => setStep("contact"),
|
|
18339
|
-
className: "flex min-w-0 flex-1 flex-col text-left",
|
|
18340
|
-
children: [
|
|
18341
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: "Keep this conversation" }),
|
|
18342
|
-
/* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: "Verify your number and it follows you \u2014 any device" })
|
|
18343
|
-
]
|
|
18344
|
-
}
|
|
18345
|
-
),
|
|
18346
|
-
/* @__PURE__ */ jsx(
|
|
18347
|
-
"button",
|
|
18348
|
-
{
|
|
18349
|
-
type: "button",
|
|
18350
|
-
"aria-label": "Dismiss",
|
|
18351
|
-
onClick: () => {
|
|
18352
|
-
writeKeepDismissed();
|
|
18353
|
-
setDismissed(true);
|
|
18354
|
-
},
|
|
18355
|
-
className: "flex-shrink-0 p-1 text-muted-foreground hover:text-foreground",
|
|
18356
|
-
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
18357
|
-
}
|
|
18358
|
-
)
|
|
18359
|
-
] }),
|
|
18360
|
-
step === "contact" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
18361
|
-
/* @__PURE__ */ jsx(
|
|
18362
|
-
"input",
|
|
18363
|
-
{
|
|
18364
|
-
type: "text",
|
|
18365
|
-
inputMode: "email",
|
|
18366
|
-
value: contact,
|
|
18367
|
-
onChange: (e) => setContact(e.target.value),
|
|
18368
|
-
onKeyDown: (e) => e.key === "Enter" && void requestCode(),
|
|
18369
|
-
placeholder: "Phone or email",
|
|
18370
|
-
autoFocus: true,
|
|
18371
|
-
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:border-foreground"
|
|
18372
|
-
}
|
|
18373
|
-
),
|
|
18374
|
-
/* @__PURE__ */ jsx(
|
|
18375
|
-
"button",
|
|
18376
|
-
{
|
|
18377
|
-
type: "button",
|
|
18378
|
-
onClick: () => void requestCode(),
|
|
18379
|
-
disabled: !contact.trim() || busy,
|
|
18380
|
-
className: "h-9 flex-shrink-0 rounded-lg bg-foreground px-3 text-xs font-semibold text-background disabled:opacity-50",
|
|
18381
|
-
children: busy ? "Sending\u2026" : "Send code"
|
|
18382
|
-
}
|
|
18383
|
-
)
|
|
18384
|
-
] }),
|
|
18385
|
-
step === "code" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
18386
|
-
/* @__PURE__ */ jsx(
|
|
18387
|
-
"input",
|
|
18388
|
-
{
|
|
18389
|
-
type: "text",
|
|
18390
|
-
inputMode: "numeric",
|
|
18391
|
-
autoComplete: "one-time-code",
|
|
18392
|
-
maxLength: 6,
|
|
18393
|
-
value: code,
|
|
18394
|
-
onChange: (e) => {
|
|
18395
|
-
const digits = e.target.value.replace(/\D/g, "").slice(0, 6);
|
|
18396
|
-
setCode(digits);
|
|
18397
|
-
if (digits.length === 6) void verify(digits);
|
|
18398
|
-
},
|
|
18399
|
-
placeholder: "6-digit code",
|
|
18400
|
-
autoFocus: true,
|
|
18401
|
-
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs tracking-[0.2em] text-foreground outline-none placeholder:tracking-normal placeholder:text-muted-foreground focus:border-foreground"
|
|
18402
|
-
}
|
|
18403
|
-
),
|
|
18404
|
-
/* @__PURE__ */ jsx(
|
|
18405
|
-
"button",
|
|
18406
|
-
{
|
|
18407
|
-
type: "button",
|
|
18408
|
-
onClick: () => void requestCode(),
|
|
18409
|
-
disabled: busy,
|
|
18410
|
-
className: "h-9 flex-shrink-0 rounded-lg px-2 text-[11px] font-medium text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
18411
|
-
children: "Resend"
|
|
18412
|
-
}
|
|
18413
|
-
)
|
|
18414
|
-
] }),
|
|
18415
|
-
verifyError && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-[11px] text-destructive", children: verifyError })
|
|
18416
|
-
] });
|
|
18417
|
-
}
|
|
18418
|
-
function ChatInput({
|
|
18419
|
-
placeholder,
|
|
18420
|
-
isSending,
|
|
18421
|
-
onSend,
|
|
18422
|
-
onAttach,
|
|
18423
|
-
className
|
|
18424
|
-
}) {
|
|
18425
|
-
const [value, setValue] = useState("");
|
|
18426
|
-
const [uploading, setUploading] = useState(false);
|
|
18427
|
-
const textareaRef = useRef(null);
|
|
18428
|
-
const fileRef = useRef(null);
|
|
18429
|
-
const handleAttach = useCallback(
|
|
18430
|
-
async (file) => {
|
|
18431
|
-
setUploading(true);
|
|
18432
|
-
try {
|
|
18433
|
-
await onAttach(file);
|
|
18434
|
-
} finally {
|
|
18435
|
-
setUploading(false);
|
|
18436
|
-
}
|
|
18437
|
-
},
|
|
18438
|
-
[onAttach]
|
|
18439
|
-
);
|
|
18440
|
-
const handleSend = useCallback(async () => {
|
|
18441
|
-
const trimmed = value.trim();
|
|
18442
|
-
if (!trimmed || isSending) return;
|
|
18443
|
-
setValue("");
|
|
18444
|
-
if (textareaRef.current) {
|
|
18445
|
-
textareaRef.current.style.height = "auto";
|
|
18446
|
-
}
|
|
18447
|
-
const sent = await onSend(trimmed);
|
|
18448
|
-
if (!sent) setValue((current) => current || trimmed);
|
|
18449
|
-
}, [value, isSending, onSend]);
|
|
18450
|
-
const handleKeyDown = useCallback(
|
|
18451
|
-
(e) => {
|
|
18452
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
18453
|
-
e.preventDefault();
|
|
18454
|
-
handleSend();
|
|
18455
|
-
}
|
|
18456
|
-
},
|
|
18457
|
-
[handleSend]
|
|
18458
|
-
);
|
|
18459
|
-
const handleInput = useCallback(() => {
|
|
18460
|
-
const el = textareaRef.current;
|
|
18461
|
-
if (!el) return;
|
|
18462
|
-
el.style.height = "auto";
|
|
18463
|
-
el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
|
|
18464
|
-
}, []);
|
|
18465
|
-
const hasContent = value.trim().length > 0;
|
|
18466
|
-
return /* @__PURE__ */ jsxs(
|
|
18467
|
-
"div",
|
|
18468
|
-
{
|
|
18469
|
-
className: cn(
|
|
18470
|
-
"flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
|
|
18471
|
-
className
|
|
18472
|
-
),
|
|
18473
|
-
children: [
|
|
18474
|
-
/* @__PURE__ */ jsx(
|
|
18475
|
-
"input",
|
|
18476
|
-
{
|
|
18477
|
-
ref: fileRef,
|
|
18478
|
-
type: "file",
|
|
18479
|
-
accept: "image/*",
|
|
18480
|
-
className: "hidden",
|
|
18481
|
-
"aria-hidden": "true",
|
|
18482
|
-
tabIndex: -1,
|
|
18483
|
-
onChange: (e) => {
|
|
18484
|
-
const file = e.target.files?.[0];
|
|
18485
|
-
e.target.value = "";
|
|
18486
|
-
if (file) void handleAttach(file);
|
|
18487
|
-
}
|
|
18488
|
-
}
|
|
18489
|
-
),
|
|
18490
|
-
/* @__PURE__ */ jsx(
|
|
18491
|
-
"button",
|
|
18492
|
-
{
|
|
18493
|
-
type: "button",
|
|
18494
|
-
"aria-label": "Attach a photo",
|
|
18495
|
-
disabled: uploading,
|
|
18496
|
-
onClick: () => fileRef.current?.click(),
|
|
18497
|
-
className: cn(
|
|
18498
|
-
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
18499
|
-
"bg-muted text-muted-foreground transition-colors hover:text-foreground",
|
|
18500
|
-
uploading && "animate-pulse"
|
|
18501
|
-
),
|
|
18502
|
-
children: /* @__PURE__ */ jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
|
|
18503
|
-
}
|
|
18504
|
-
),
|
|
18505
|
-
/* @__PURE__ */ jsx(
|
|
18506
|
-
"div",
|
|
18507
|
-
{
|
|
18508
|
-
className: cn(
|
|
18509
|
-
"flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
|
|
18510
|
-
"transition-colors focus-within:border-foreground focus-within:bg-background"
|
|
18511
|
-
),
|
|
18512
|
-
children: /* @__PURE__ */ jsx(
|
|
18513
|
-
"textarea",
|
|
18514
|
-
{
|
|
18515
|
-
ref: textareaRef,
|
|
18516
|
-
value,
|
|
18517
|
-
onChange: (e) => setValue(e.target.value),
|
|
18518
|
-
onKeyDown: handleKeyDown,
|
|
18519
|
-
onInput: handleInput,
|
|
18520
|
-
placeholder,
|
|
18521
|
-
rows: 1,
|
|
18522
|
-
className: cn(
|
|
18523
|
-
"max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
|
|
18524
|
-
"text-sm text-foreground outline-none placeholder:text-muted-foreground"
|
|
18525
|
-
)
|
|
18526
|
-
}
|
|
18527
|
-
)
|
|
18528
|
-
}
|
|
18529
|
-
),
|
|
18530
|
-
/* @__PURE__ */ jsx(
|
|
18531
|
-
"button",
|
|
18532
|
-
{
|
|
18533
|
-
type: "button",
|
|
18534
|
-
onClick: handleSend,
|
|
18535
|
-
disabled: !hasContent || isSending,
|
|
18536
|
-
"aria-label": "Send message",
|
|
18537
|
-
className: cn(
|
|
18538
|
-
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
18539
|
-
"transition-all duration-200",
|
|
18540
|
-
hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
|
|
18541
|
-
),
|
|
18542
|
-
children: /* @__PURE__ */ jsx(SendIcon, { className: "h-[18px] w-[18px]" })
|
|
18543
|
-
}
|
|
18544
|
-
)
|
|
18545
|
-
]
|
|
18546
|
-
}
|
|
18547
|
-
);
|
|
18548
|
-
}
|
|
18549
|
-
function TypingDots() {
|
|
18550
|
-
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
|
|
18551
|
-
"span",
|
|
18552
|
-
{
|
|
18553
|
-
className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
|
|
18554
|
-
style: { animationDelay: `${i * 160}ms` }
|
|
18555
|
-
},
|
|
18556
|
-
i
|
|
18557
|
-
)) });
|
|
18558
|
-
}
|
|
18559
|
-
function ChatIcon({ className }) {
|
|
18560
|
-
return /* @__PURE__ */ jsx(
|
|
18561
|
-
"svg",
|
|
18562
|
-
{
|
|
18563
|
-
className,
|
|
18564
|
-
viewBox: "0 0 24 24",
|
|
18565
|
-
fill: "none",
|
|
18566
|
-
stroke: "currentColor",
|
|
18567
|
-
strokeWidth: 2,
|
|
18568
|
-
strokeLinecap: "round",
|
|
18569
|
-
strokeLinejoin: "round",
|
|
18570
|
-
children: /* @__PURE__ */ jsx("path", { d: "M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" })
|
|
18571
|
-
}
|
|
18572
|
-
);
|
|
18573
|
-
}
|
|
18574
|
-
function CloseIcon({ className }) {
|
|
18575
|
-
return /* @__PURE__ */ jsx(
|
|
18576
|
-
"svg",
|
|
18577
|
-
{
|
|
18578
|
-
className,
|
|
18579
|
-
viewBox: "0 0 24 24",
|
|
18580
|
-
fill: "none",
|
|
18581
|
-
stroke: "currentColor",
|
|
18582
|
-
strokeWidth: 2,
|
|
18583
|
-
strokeLinecap: "round",
|
|
18584
|
-
strokeLinejoin: "round",
|
|
18585
|
-
children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
|
|
18586
|
-
}
|
|
18587
|
-
);
|
|
18588
|
-
}
|
|
18589
|
-
function SendIcon({ className }) {
|
|
18590
|
-
return /* @__PURE__ */ jsxs(
|
|
18591
|
-
"svg",
|
|
18592
|
-
{
|
|
18593
|
-
className,
|
|
18594
|
-
viewBox: "0 0 24 24",
|
|
18595
|
-
fill: "none",
|
|
18596
|
-
stroke: "currentColor",
|
|
18597
|
-
strokeWidth: 2,
|
|
18598
|
-
strokeLinecap: "round",
|
|
18599
|
-
strokeLinejoin: "round",
|
|
18600
|
-
children: [
|
|
18601
|
-
/* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
18602
|
-
/* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
18603
|
-
]
|
|
18604
|
-
}
|
|
18605
|
-
);
|
|
18606
|
-
}
|
|
18607
|
-
function ImageIcon({ className }) {
|
|
18608
|
-
return /* @__PURE__ */ jsxs(
|
|
18609
|
-
"svg",
|
|
18610
|
-
{
|
|
18611
|
-
className,
|
|
18612
|
-
viewBox: "0 0 24 24",
|
|
18613
|
-
fill: "none",
|
|
18614
|
-
stroke: "currentColor",
|
|
18615
|
-
strokeWidth: 2,
|
|
18616
|
-
strokeLinecap: "round",
|
|
18617
|
-
strokeLinejoin: "round",
|
|
18618
|
-
children: [
|
|
18619
|
-
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
18620
|
-
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
18621
|
-
/* @__PURE__ */ jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
18622
|
-
]
|
|
18623
|
-
}
|
|
18624
|
-
);
|
|
18625
|
-
}
|
|
18626
|
-
function ReplyIcon({ className }) {
|
|
18627
|
-
return /* @__PURE__ */ jsxs(
|
|
18628
|
-
"svg",
|
|
18629
|
-
{
|
|
18630
|
-
className,
|
|
18631
|
-
viewBox: "0 0 24 24",
|
|
18632
|
-
fill: "none",
|
|
18633
|
-
stroke: "currentColor",
|
|
18634
|
-
strokeWidth: 2,
|
|
18635
|
-
strokeLinecap: "round",
|
|
18636
|
-
strokeLinejoin: "round",
|
|
18637
|
-
children: [
|
|
18638
|
-
/* @__PURE__ */ jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
18639
|
-
/* @__PURE__ */ jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
18640
|
-
]
|
|
18641
|
-
}
|
|
18642
|
-
);
|
|
18643
|
-
}
|
|
18644
|
-
|
|
18645
|
-
// src/react/genui/catalog.ts
|
|
18646
|
-
function genUICatalog(registry) {
|
|
18647
|
-
return new Map(
|
|
18648
|
-
registry.components.map((component) => [
|
|
18649
|
-
component.name,
|
|
18650
|
-
{
|
|
18651
|
-
params: component.params.map((param) => ({
|
|
18652
|
-
name: param.name,
|
|
18653
|
-
required: param.required ?? false
|
|
18654
|
-
}))
|
|
18655
|
-
}
|
|
18656
|
-
])
|
|
18657
|
-
);
|
|
18658
|
-
}
|
|
18659
17722
|
|
|
18660
17723
|
// src/genui/lang/parser/ast.ts
|
|
18661
17724
|
function isRuntimeExpr(node) {
|
|
@@ -18858,10 +17921,10 @@ var BUILTINS = {
|
|
|
18858
17921
|
signature: "Round(number, decimals?) \u2192 number",
|
|
18859
17922
|
description: "Round to N decimal places (default 0)",
|
|
18860
17923
|
fn: (n, decimals) => {
|
|
18861
|
-
const
|
|
17924
|
+
const num2 = toNumber2(n);
|
|
18862
17925
|
const d = decimals != null ? toNumber2(decimals) : 0;
|
|
18863
17926
|
const factor = Math.pow(10, d);
|
|
18864
|
-
return Math.round(
|
|
17927
|
+
return Math.round(num2 * factor) / factor;
|
|
18865
17928
|
}
|
|
18866
17929
|
},
|
|
18867
17930
|
Abs: {
|
|
@@ -19877,556 +18940,1841 @@ function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat
|
|
|
19877
18940
|
);
|
|
19878
18941
|
const orphaned = [...unreached];
|
|
19879
18942
|
return {
|
|
19880
|
-
root,
|
|
19881
|
-
meta: {
|
|
19882
|
-
incomplete: wasIncomplete,
|
|
19883
|
-
unresolved: unres,
|
|
19884
|
-
orphaned,
|
|
19885
|
-
statementCount: stmtCount,
|
|
19886
|
-
errors
|
|
18943
|
+
root,
|
|
18944
|
+
meta: {
|
|
18945
|
+
incomplete: wasIncomplete,
|
|
18946
|
+
unresolved: unres,
|
|
18947
|
+
orphaned,
|
|
18948
|
+
statementCount: stmtCount,
|
|
18949
|
+
errors
|
|
18950
|
+
},
|
|
18951
|
+
stateDeclarations,
|
|
18952
|
+
queryStatements,
|
|
18953
|
+
mutationStatements
|
|
18954
|
+
};
|
|
18955
|
+
}
|
|
18956
|
+
function skipString(input, start) {
|
|
18957
|
+
if (input[start] !== '"') return start;
|
|
18958
|
+
let i = start + 1;
|
|
18959
|
+
while (i < input.length) {
|
|
18960
|
+
const c = input[i];
|
|
18961
|
+
if (c === "\\") {
|
|
18962
|
+
i += 2;
|
|
18963
|
+
} else if (c === '"') {
|
|
18964
|
+
return i + 1;
|
|
18965
|
+
} else {
|
|
18966
|
+
i++;
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18969
|
+
return i;
|
|
18970
|
+
}
|
|
18971
|
+
function stripFences(input) {
|
|
18972
|
+
const blocks = [];
|
|
18973
|
+
let i = 0;
|
|
18974
|
+
while (i < input.length) {
|
|
18975
|
+
let fenceStart = -1;
|
|
18976
|
+
while (i < input.length) {
|
|
18977
|
+
const nextI = skipString(input, i);
|
|
18978
|
+
if (nextI > i) {
|
|
18979
|
+
i = nextI;
|
|
18980
|
+
continue;
|
|
18981
|
+
}
|
|
18982
|
+
const c = input[i];
|
|
18983
|
+
if (c === "`" && i + 1 < input.length && input[i + 1] === "`" && i + 2 < input.length && input[i + 2] === "`") {
|
|
18984
|
+
fenceStart = i;
|
|
18985
|
+
break;
|
|
18986
|
+
}
|
|
18987
|
+
i++;
|
|
18988
|
+
}
|
|
18989
|
+
if (fenceStart === -1) break;
|
|
18990
|
+
let j = fenceStart + 3;
|
|
18991
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
18992
|
+
if (j >= input.length) {
|
|
18993
|
+
blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
|
|
18994
|
+
i = input.length;
|
|
18995
|
+
break;
|
|
18996
|
+
}
|
|
18997
|
+
j++;
|
|
18998
|
+
let closePos = -1;
|
|
18999
|
+
let k = j;
|
|
19000
|
+
while (k < input.length) {
|
|
19001
|
+
const nextK = skipString(input, k);
|
|
19002
|
+
if (nextK > k) {
|
|
19003
|
+
k = nextK;
|
|
19004
|
+
continue;
|
|
19005
|
+
}
|
|
19006
|
+
const c = input[k];
|
|
19007
|
+
if (c === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
|
|
19008
|
+
closePos = k;
|
|
19009
|
+
break;
|
|
19010
|
+
}
|
|
19011
|
+
k++;
|
|
19012
|
+
}
|
|
19013
|
+
if (closePos !== -1) {
|
|
19014
|
+
blocks.push(input.slice(j, closePos));
|
|
19015
|
+
i = closePos + 3;
|
|
19016
|
+
} else {
|
|
19017
|
+
blocks.push(input.slice(j));
|
|
19018
|
+
i = input.length;
|
|
19019
|
+
}
|
|
19020
|
+
}
|
|
19021
|
+
if (blocks.length > 0) return blocks.join("\n");
|
|
19022
|
+
if (input.startsWith("```")) {
|
|
19023
|
+
let j = 3;
|
|
19024
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
19025
|
+
const start = j < input.length ? j + 1 : 3;
|
|
19026
|
+
const body = input.slice(start);
|
|
19027
|
+
const trailingFence = body.lastIndexOf("```");
|
|
19028
|
+
if (trailingFence !== -1) {
|
|
19029
|
+
return body.slice(0, trailingFence);
|
|
19030
|
+
}
|
|
19031
|
+
return body;
|
|
19032
|
+
}
|
|
19033
|
+
return input;
|
|
19034
|
+
}
|
|
19035
|
+
function stripComments(input) {
|
|
19036
|
+
let inStr = false;
|
|
19037
|
+
return input.split("\n").map((line) => {
|
|
19038
|
+
for (let i = 0; i < line.length; i++) {
|
|
19039
|
+
const c = line[i];
|
|
19040
|
+
if (inStr) {
|
|
19041
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
19042
|
+
i++;
|
|
19043
|
+
continue;
|
|
19044
|
+
}
|
|
19045
|
+
if (c === inStr) inStr = false;
|
|
19046
|
+
continue;
|
|
19047
|
+
}
|
|
19048
|
+
if (c === '"' || c === "'") {
|
|
19049
|
+
inStr = c;
|
|
19050
|
+
continue;
|
|
19051
|
+
}
|
|
19052
|
+
if (c === "/" && line[i + 1] === "/") {
|
|
19053
|
+
return line.substring(0, i).trimEnd();
|
|
19054
|
+
}
|
|
19055
|
+
if (c === "#") {
|
|
19056
|
+
return line.substring(0, i).trimEnd();
|
|
19057
|
+
}
|
|
19058
|
+
}
|
|
19059
|
+
return line;
|
|
19060
|
+
}).join("\n");
|
|
19061
|
+
}
|
|
19062
|
+
function preprocess(input) {
|
|
19063
|
+
return stripComments(stripFences(input.trim())).trim();
|
|
19064
|
+
}
|
|
19065
|
+
function parse(input, cat, rootName) {
|
|
19066
|
+
const trimmed = preprocess(input);
|
|
19067
|
+
if (!trimmed) return emptyResult();
|
|
19068
|
+
const { text: text2, wasIncomplete } = autoClose(trimmed);
|
|
19069
|
+
const stmts = split(tokenize(text2));
|
|
19070
|
+
if (!stmts.length) return emptyResult(wasIncomplete);
|
|
19071
|
+
const stmtMap = /* @__PURE__ */ new Map();
|
|
19072
|
+
let firstId = "";
|
|
19073
|
+
for (const s of stmts) {
|
|
19074
|
+
const expr = parseExpression(s.tokens);
|
|
19075
|
+
const stmt = classifyStatement(s, expr);
|
|
19076
|
+
stmtMap.set(s.id, stmt);
|
|
19077
|
+
if (!firstId) firstId = s.id;
|
|
19078
|
+
}
|
|
19079
|
+
const typedStmts = [...stmtMap.values()];
|
|
19080
|
+
return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
|
|
19081
|
+
}
|
|
19082
|
+
function createStreamParser(cat, rootName) {
|
|
19083
|
+
let buf = "";
|
|
19084
|
+
let cleaned = "";
|
|
19085
|
+
let completedEnd = 0;
|
|
19086
|
+
const completedStmtMap = /* @__PURE__ */ new Map();
|
|
19087
|
+
let firstId = "";
|
|
19088
|
+
function addStmt(text2) {
|
|
19089
|
+
const t = text2.trim();
|
|
19090
|
+
if (!t) return;
|
|
19091
|
+
for (const s of split(tokenize(t))) {
|
|
19092
|
+
const expr = parseExpression(s.tokens);
|
|
19093
|
+
const stmt = classifyStatement(s, expr);
|
|
19094
|
+
completedStmtMap.set(s.id, stmt);
|
|
19095
|
+
if (!firstId) firstId = s.id;
|
|
19096
|
+
}
|
|
19097
|
+
}
|
|
19098
|
+
function refreshCleaned() {
|
|
19099
|
+
const next = preprocess(buf);
|
|
19100
|
+
if (!next.startsWith(cleaned.slice(0, completedEnd))) {
|
|
19101
|
+
completedEnd = 0;
|
|
19102
|
+
completedStmtMap.clear();
|
|
19103
|
+
firstId = "";
|
|
19104
|
+
}
|
|
19105
|
+
cleaned = next;
|
|
19106
|
+
}
|
|
19107
|
+
function scanNewCompleted() {
|
|
19108
|
+
let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
|
|
19109
|
+
let stmtStart = completedEnd;
|
|
19110
|
+
for (let i = completedEnd; i < cleaned.length; i++) {
|
|
19111
|
+
const c = cleaned[i];
|
|
19112
|
+
if (esc) {
|
|
19113
|
+
esc = false;
|
|
19114
|
+
continue;
|
|
19115
|
+
}
|
|
19116
|
+
if (c === "\\" && inStr) {
|
|
19117
|
+
esc = true;
|
|
19118
|
+
continue;
|
|
19119
|
+
}
|
|
19120
|
+
if (inStr) {
|
|
19121
|
+
if (c === inStr) inStr = false;
|
|
19122
|
+
continue;
|
|
19123
|
+
}
|
|
19124
|
+
if (c === '"' || c === "'") {
|
|
19125
|
+
inStr = c;
|
|
19126
|
+
continue;
|
|
19127
|
+
}
|
|
19128
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
19129
|
+
else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
19130
|
+
else if (c === "?" && depth === 0) ternaryDepth++;
|
|
19131
|
+
else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
19132
|
+
else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
|
|
19133
|
+
let peek = i + 1;
|
|
19134
|
+
while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n"))
|
|
19135
|
+
peek++;
|
|
19136
|
+
if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) {
|
|
19137
|
+
continue;
|
|
19138
|
+
}
|
|
19139
|
+
const t = cleaned.slice(stmtStart, i).trim();
|
|
19140
|
+
if (t) addStmt(t);
|
|
19141
|
+
stmtStart = i + 1;
|
|
19142
|
+
completedEnd = i + 1;
|
|
19143
|
+
}
|
|
19144
|
+
}
|
|
19145
|
+
return stmtStart;
|
|
19146
|
+
}
|
|
19147
|
+
function currentResult() {
|
|
19148
|
+
refreshCleaned();
|
|
19149
|
+
const pendingStart = scanNewCompleted();
|
|
19150
|
+
const pendingText = cleaned.slice(pendingStart).trim();
|
|
19151
|
+
if (!pendingText) {
|
|
19152
|
+
if (completedStmtMap.size === 0) return emptyResult();
|
|
19153
|
+
return buildResult(
|
|
19154
|
+
completedStmtMap,
|
|
19155
|
+
[...completedStmtMap.values()],
|
|
19156
|
+
firstId,
|
|
19157
|
+
false,
|
|
19158
|
+
completedStmtMap.size,
|
|
19159
|
+
cat,
|
|
19160
|
+
rootName
|
|
19161
|
+
);
|
|
19162
|
+
}
|
|
19163
|
+
const { text: closed, wasIncomplete } = autoClose(pendingText);
|
|
19164
|
+
const stmts = split(tokenize(closed));
|
|
19165
|
+
if (!stmts.length) {
|
|
19166
|
+
if (completedStmtMap.size === 0) return emptyResult(wasIncomplete);
|
|
19167
|
+
return buildResult(
|
|
19168
|
+
completedStmtMap,
|
|
19169
|
+
[...completedStmtMap.values()],
|
|
19170
|
+
firstId,
|
|
19171
|
+
wasIncomplete,
|
|
19172
|
+
completedStmtMap.size,
|
|
19173
|
+
cat,
|
|
19174
|
+
rootName
|
|
19175
|
+
);
|
|
19176
|
+
}
|
|
19177
|
+
const allStmtMap = new Map(completedStmtMap);
|
|
19178
|
+
for (const s of stmts) {
|
|
19179
|
+
const expr = parseExpression(s.tokens);
|
|
19180
|
+
const stmt = classifyStatement(s, expr);
|
|
19181
|
+
allStmtMap.set(s.id, stmt);
|
|
19182
|
+
}
|
|
19183
|
+
const allTypedStmts = [...allStmtMap.values()];
|
|
19184
|
+
const fid = firstId || stmts[0].id;
|
|
19185
|
+
return buildResult(
|
|
19186
|
+
allStmtMap,
|
|
19187
|
+
allTypedStmts,
|
|
19188
|
+
fid,
|
|
19189
|
+
wasIncomplete,
|
|
19190
|
+
allStmtMap.size,
|
|
19191
|
+
cat,
|
|
19192
|
+
rootName
|
|
19193
|
+
);
|
|
19194
|
+
}
|
|
19195
|
+
function reset() {
|
|
19196
|
+
buf = "";
|
|
19197
|
+
cleaned = "";
|
|
19198
|
+
completedEnd = 0;
|
|
19199
|
+
completedStmtMap.clear();
|
|
19200
|
+
firstId = "";
|
|
19201
|
+
}
|
|
19202
|
+
return {
|
|
19203
|
+
push(chunk) {
|
|
19204
|
+
buf += chunk;
|
|
19205
|
+
return currentResult();
|
|
19887
19206
|
},
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19207
|
+
set(fullText) {
|
|
19208
|
+
if (fullText.length < buf.length || !fullText.startsWith(buf)) {
|
|
19209
|
+
reset();
|
|
19210
|
+
}
|
|
19211
|
+
const delta = fullText.slice(buf.length);
|
|
19212
|
+
if (delta) buf += delta;
|
|
19213
|
+
return currentResult();
|
|
19214
|
+
},
|
|
19215
|
+
getResult: currentResult
|
|
19891
19216
|
};
|
|
19892
19217
|
}
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
}
|
|
19903
|
-
|
|
19218
|
+
|
|
19219
|
+
// src/genui/registry.ts
|
|
19220
|
+
var CIMPLIFY_GENUI_REGISTRY = {
|
|
19221
|
+
version: "3",
|
|
19222
|
+
components: [
|
|
19223
|
+
{
|
|
19224
|
+
name: "Stack",
|
|
19225
|
+
description: 'Layout container. direction: "column" (default) or "row"; gap in points (default 12)',
|
|
19226
|
+
params: [{ name: "children", required: true }, { name: "direction" }, { name: "gap" }]
|
|
19227
|
+
},
|
|
19228
|
+
{
|
|
19229
|
+
name: "Card",
|
|
19230
|
+
description: "Elevated surface grouping related content, with an optional title",
|
|
19231
|
+
params: [{ name: "children", required: true }, { name: "title" }]
|
|
19232
|
+
},
|
|
19233
|
+
{
|
|
19234
|
+
name: "TextContent",
|
|
19235
|
+
description: 'Text block. size: "display", "heading", "title", "body" (default), "label", "caption"',
|
|
19236
|
+
params: [{ name: "text", required: true }, { name: "size" }]
|
|
19237
|
+
},
|
|
19238
|
+
{
|
|
19239
|
+
name: "Stat",
|
|
19240
|
+
description: "Compact stat: small label over a bold value, optional sub line and tone",
|
|
19241
|
+
params: [
|
|
19242
|
+
{ name: "label", required: true },
|
|
19243
|
+
{ name: "value", required: true },
|
|
19244
|
+
{ name: "sub" },
|
|
19245
|
+
{ name: "tone" }
|
|
19246
|
+
]
|
|
19247
|
+
},
|
|
19248
|
+
{
|
|
19249
|
+
name: "Metric",
|
|
19250
|
+
description: "KPI card with label, value, optional percent change and SF Symbol icon",
|
|
19251
|
+
params: [
|
|
19252
|
+
{ name: "label", required: true },
|
|
19253
|
+
{ name: "value", required: true },
|
|
19254
|
+
{ name: "change" },
|
|
19255
|
+
{ name: "icon" }
|
|
19256
|
+
]
|
|
19257
|
+
},
|
|
19258
|
+
{
|
|
19259
|
+
name: "KeyValue",
|
|
19260
|
+
description: "Label/value row; set mono true for numeric values",
|
|
19261
|
+
params: [
|
|
19262
|
+
{ name: "label", required: true },
|
|
19263
|
+
{ name: "value", required: true },
|
|
19264
|
+
{ name: "mono" }
|
|
19265
|
+
]
|
|
19266
|
+
},
|
|
19267
|
+
{
|
|
19268
|
+
name: "Badge",
|
|
19269
|
+
description: "Small status chip. tone: neutral, positive, info, indigo, violet, warning, danger",
|
|
19270
|
+
params: [{ name: "label", required: true }, { name: "tone" }]
|
|
19271
|
+
},
|
|
19272
|
+
{
|
|
19273
|
+
name: "Callout",
|
|
19274
|
+
description: "Inline alert with title, optional message. tone: info (default), success, warning, danger, accent, neutral",
|
|
19275
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "tone" }]
|
|
19276
|
+
},
|
|
19277
|
+
{
|
|
19278
|
+
name: "Button",
|
|
19279
|
+
description: 'Action button. action: "action:name" string or Action([...]) steps. variant: primary, cta, secondary (default), ghost, destructive',
|
|
19280
|
+
params: [
|
|
19281
|
+
{ name: "label", required: true },
|
|
19282
|
+
{ name: "action", required: true },
|
|
19283
|
+
{ name: "variant" }
|
|
19284
|
+
]
|
|
19285
|
+
},
|
|
19286
|
+
{
|
|
19287
|
+
name: "Buttons",
|
|
19288
|
+
description: 'Row (default) or column of Buttons; direction: "row" or "column"',
|
|
19289
|
+
params: [{ name: "children", required: true }, { name: "direction" }]
|
|
19290
|
+
},
|
|
19291
|
+
{
|
|
19292
|
+
name: "Divider",
|
|
19293
|
+
description: "Hairline separator",
|
|
19294
|
+
params: []
|
|
19295
|
+
},
|
|
19296
|
+
{
|
|
19297
|
+
name: "Table",
|
|
19298
|
+
description: "Data table: headers is an array of strings, rows an array of cell arrays",
|
|
19299
|
+
params: [
|
|
19300
|
+
{ name: "headers", required: true },
|
|
19301
|
+
{ name: "rows", required: true }
|
|
19302
|
+
]
|
|
19303
|
+
},
|
|
19304
|
+
{
|
|
19305
|
+
name: "BarChart",
|
|
19306
|
+
description: "Bar chart: labels (x axis strings) and values (numbers), optional title",
|
|
19307
|
+
params: [
|
|
19308
|
+
{ name: "labels", required: true },
|
|
19309
|
+
{ name: "values", required: true },
|
|
19310
|
+
{ name: "title" }
|
|
19311
|
+
]
|
|
19312
|
+
},
|
|
19313
|
+
{
|
|
19314
|
+
name: "LineChart",
|
|
19315
|
+
description: "Line chart with area fill: labels and values, optional title",
|
|
19316
|
+
params: [
|
|
19317
|
+
{ name: "labels", required: true },
|
|
19318
|
+
{ name: "values", required: true },
|
|
19319
|
+
{ name: "title" }
|
|
19320
|
+
]
|
|
19321
|
+
},
|
|
19322
|
+
{
|
|
19323
|
+
name: "Money",
|
|
19324
|
+
description: "Formatted money amount, never abbreviated. currency: ISO code (default USD)",
|
|
19325
|
+
params: [{ name: "amount", required: true }, { name: "currency" }]
|
|
19326
|
+
},
|
|
19327
|
+
{
|
|
19328
|
+
name: "Image",
|
|
19329
|
+
description: "Remote image by URL with optional caption",
|
|
19330
|
+
params: [{ name: "url", required: true }, { name: "caption" }]
|
|
19331
|
+
},
|
|
19332
|
+
{
|
|
19333
|
+
name: "ListRow",
|
|
19334
|
+
description: "Entity row for orders/customers/products/invoices: initials avatar (or SF Symbol tile via symbol), title + subtitle, trailing value and badge with tone",
|
|
19335
|
+
params: [
|
|
19336
|
+
{ name: "title", required: true },
|
|
19337
|
+
{ name: "subtitle" },
|
|
19338
|
+
{ name: "value" },
|
|
19339
|
+
{ name: "badge" },
|
|
19340
|
+
{ name: "tone" },
|
|
19341
|
+
{ name: "symbol" }
|
|
19342
|
+
]
|
|
19343
|
+
},
|
|
19344
|
+
{
|
|
19345
|
+
name: "Timeline",
|
|
19346
|
+
description: "Event history (order journey, payment trail): events is an array of {title, detail?, time?, tone?} objects, newest first",
|
|
19347
|
+
params: [{ name: "events", required: true }]
|
|
19348
|
+
},
|
|
19349
|
+
{
|
|
19350
|
+
name: "Progress",
|
|
19351
|
+
description: "Amount toward a bound (goal, stock cover, capacity): numeric value and target (default 100), optional detail caption and tone",
|
|
19352
|
+
params: [
|
|
19353
|
+
{ name: "label", required: true },
|
|
19354
|
+
{ name: "value", required: true },
|
|
19355
|
+
{ name: "target" },
|
|
19356
|
+
{ name: "detail" },
|
|
19357
|
+
{ name: "tone" }
|
|
19358
|
+
]
|
|
19359
|
+
},
|
|
19360
|
+
{
|
|
19361
|
+
name: "DonutChart",
|
|
19362
|
+
description: "Share of a whole (sales by channel, revenue by category): labels + values, optional title and center display string (e.g. the formatted total)",
|
|
19363
|
+
params: [
|
|
19364
|
+
{ name: "labels", required: true },
|
|
19365
|
+
{ name: "values", required: true },
|
|
19366
|
+
{ name: "title" },
|
|
19367
|
+
{ name: "center" }
|
|
19368
|
+
]
|
|
19369
|
+
},
|
|
19370
|
+
{
|
|
19371
|
+
name: "StatusTrack",
|
|
19372
|
+
description: "Stage tracker (order fulfilment, invoice lifecycle): steps are stage names, current is the active step name or index",
|
|
19373
|
+
params: [
|
|
19374
|
+
{ name: "steps", required: true },
|
|
19375
|
+
{ name: "current", required: true }
|
|
19376
|
+
]
|
|
19377
|
+
},
|
|
19378
|
+
{
|
|
19379
|
+
name: "LineItems",
|
|
19380
|
+
description: "Order/quote/invoice money block: items is an array of {qty?, name, amount} with display-ready amount strings, adjustments an array of {label, amount, tone?}, total the anchored display string",
|
|
19381
|
+
params: [
|
|
19382
|
+
{ name: "items", required: true },
|
|
19383
|
+
{ name: "total", required: true },
|
|
19384
|
+
{ name: "adjustments" }
|
|
19385
|
+
]
|
|
19386
|
+
},
|
|
19387
|
+
{
|
|
19388
|
+
name: "Grid",
|
|
19389
|
+
description: "Tile layout for Stat/Metric children, columns default 2",
|
|
19390
|
+
params: [{ name: "children", required: true }, { name: "columns" }]
|
|
19391
|
+
},
|
|
19392
|
+
{
|
|
19393
|
+
name: "Banner",
|
|
19394
|
+
description: "Hero-stat moment on a tonal wash: big value, label, optional percent delta. tone: positive (default), warning, danger, info, neutral",
|
|
19395
|
+
params: [
|
|
19396
|
+
{ name: "value", required: true },
|
|
19397
|
+
{ name: "label", required: true },
|
|
19398
|
+
{ name: "delta" },
|
|
19399
|
+
{ name: "tone" }
|
|
19400
|
+
]
|
|
19401
|
+
},
|
|
19402
|
+
{
|
|
19403
|
+
name: "EmptyState",
|
|
19404
|
+
description: "Nothing-to-show state with an SF Symbol and optional message",
|
|
19405
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "symbol" }]
|
|
19904
19406
|
}
|
|
19407
|
+
]
|
|
19408
|
+
};
|
|
19409
|
+
|
|
19410
|
+
// src/react/genui/catalog.ts
|
|
19411
|
+
function genUICatalog(registry) {
|
|
19412
|
+
return new Map(
|
|
19413
|
+
registry.components.map((component) => [
|
|
19414
|
+
component.name,
|
|
19415
|
+
{
|
|
19416
|
+
params: component.params.map((param) => ({
|
|
19417
|
+
name: param.name,
|
|
19418
|
+
required: param.required ?? false
|
|
19419
|
+
}))
|
|
19420
|
+
}
|
|
19421
|
+
])
|
|
19422
|
+
);
|
|
19423
|
+
}
|
|
19424
|
+
var noop = () => {
|
|
19425
|
+
};
|
|
19426
|
+
function childKey(value, index) {
|
|
19427
|
+
if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
|
|
19428
|
+
return `i:${index}`;
|
|
19429
|
+
}
|
|
19430
|
+
var RenderDepthContext = createContext(0);
|
|
19431
|
+
var MAX_DEPTH = 64;
|
|
19432
|
+
var GenUINode = memo(function GenUINode2({
|
|
19433
|
+
node,
|
|
19434
|
+
components,
|
|
19435
|
+
onAction,
|
|
19436
|
+
isStreaming,
|
|
19437
|
+
onTelemetry
|
|
19438
|
+
}) {
|
|
19439
|
+
const depth = useContext(RenderDepthContext);
|
|
19440
|
+
if (depth > MAX_DEPTH) {
|
|
19441
|
+
onTelemetry?.({ kind: "dropped-node", typeName: node.typeName });
|
|
19442
|
+
return null;
|
|
19905
19443
|
}
|
|
19906
|
-
|
|
19907
|
-
|
|
19908
|
-
|
|
19909
|
-
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
-
|
|
19917
|
-
|
|
19918
|
-
|
|
19919
|
-
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
}
|
|
19924
|
-
i++;
|
|
19925
|
-
}
|
|
19926
|
-
if (fenceStart === -1) break;
|
|
19927
|
-
let j = fenceStart + 3;
|
|
19928
|
-
while (j < input.length && input[j] !== "\n") j++;
|
|
19929
|
-
if (j >= input.length) {
|
|
19930
|
-
blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
|
|
19931
|
-
i = input.length;
|
|
19932
|
-
break;
|
|
19444
|
+
const renderer = components[node.typeName];
|
|
19445
|
+
if (!renderer) {
|
|
19446
|
+
onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
|
|
19447
|
+
return null;
|
|
19448
|
+
}
|
|
19449
|
+
const render = (value) => {
|
|
19450
|
+
if (value == null) return null;
|
|
19451
|
+
if (isElementNode(value)) {
|
|
19452
|
+
return /* @__PURE__ */ jsx(RenderDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsx(
|
|
19453
|
+
GenUINode2,
|
|
19454
|
+
{
|
|
19455
|
+
node: value,
|
|
19456
|
+
components,
|
|
19457
|
+
onAction,
|
|
19458
|
+
isStreaming,
|
|
19459
|
+
onTelemetry
|
|
19460
|
+
}
|
|
19461
|
+
) });
|
|
19933
19462
|
}
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
let k = j;
|
|
19937
|
-
while (k < input.length) {
|
|
19938
|
-
const nextK = skipString(input, k);
|
|
19939
|
-
if (nextK > k) {
|
|
19940
|
-
k = nextK;
|
|
19941
|
-
continue;
|
|
19942
|
-
}
|
|
19943
|
-
const c = input[k];
|
|
19944
|
-
if (c === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
|
|
19945
|
-
closePos = k;
|
|
19946
|
-
break;
|
|
19947
|
-
}
|
|
19948
|
-
k++;
|
|
19463
|
+
if (Array.isArray(value)) {
|
|
19464
|
+
return value.map((item, index) => /* @__PURE__ */ jsx(React14.Fragment, { children: render(item) }, childKey(item, index)));
|
|
19949
19465
|
}
|
|
19950
|
-
if (
|
|
19951
|
-
|
|
19952
|
-
|
|
19953
|
-
|
|
19954
|
-
|
|
19955
|
-
|
|
19466
|
+
if (typeof value === "object" && isRuntimeExpr(value)) return null;
|
|
19467
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
19468
|
+
return null;
|
|
19469
|
+
};
|
|
19470
|
+
return renderer({ node, ctx: { render, onAction, isStreaming } });
|
|
19471
|
+
});
|
|
19472
|
+
function GenUIView({
|
|
19473
|
+
root,
|
|
19474
|
+
components,
|
|
19475
|
+
onAction = noop,
|
|
19476
|
+
isStreaming = false,
|
|
19477
|
+
onTelemetry,
|
|
19478
|
+
placeholder = null
|
|
19479
|
+
}) {
|
|
19480
|
+
if (!root) return placeholder;
|
|
19481
|
+
return /* @__PURE__ */ jsx(
|
|
19482
|
+
GenUINode,
|
|
19483
|
+
{
|
|
19484
|
+
node: root,
|
|
19485
|
+
components,
|
|
19486
|
+
onAction,
|
|
19487
|
+
isStreaming,
|
|
19488
|
+
onTelemetry
|
|
19956
19489
|
}
|
|
19490
|
+
);
|
|
19491
|
+
}
|
|
19492
|
+
var CHAT_ACTION_VIEW_PRODUCT = "view_product";
|
|
19493
|
+
var CHAT_ACTION_VIEW_ORDER = "view_order";
|
|
19494
|
+
function chatAction(verb, id) {
|
|
19495
|
+
return `chat:${verb}:${id}`;
|
|
19496
|
+
}
|
|
19497
|
+
function parseChatAction(action) {
|
|
19498
|
+
const [, verb, id] = action.split(":");
|
|
19499
|
+
return verb && id ? { verb, id } : null;
|
|
19500
|
+
}
|
|
19501
|
+
function text(value) {
|
|
19502
|
+
return typeof value === "string" ? value : typeof value === "number" ? String(value) : void 0;
|
|
19503
|
+
}
|
|
19504
|
+
function num(value) {
|
|
19505
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
19506
|
+
if (typeof value === "string" && Number.isFinite(Number(value))) return Number(value);
|
|
19507
|
+
return void 0;
|
|
19508
|
+
}
|
|
19509
|
+
function rowsOf(value) {
|
|
19510
|
+
if (!Array.isArray(value)) return [];
|
|
19511
|
+
return value.filter(
|
|
19512
|
+
(item) => typeof item === "object" && item !== null && !Array.isArray(item)
|
|
19513
|
+
);
|
|
19514
|
+
}
|
|
19515
|
+
function tone(value) {
|
|
19516
|
+
switch (value) {
|
|
19517
|
+
case "positive":
|
|
19518
|
+
case "success":
|
|
19519
|
+
return "positive";
|
|
19520
|
+
case "warning":
|
|
19521
|
+
return "warning";
|
|
19522
|
+
case "danger":
|
|
19523
|
+
case "negative":
|
|
19524
|
+
return "danger";
|
|
19525
|
+
default:
|
|
19526
|
+
return "neutral";
|
|
19957
19527
|
}
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
return
|
|
19967
|
-
|
|
19968
|
-
|
|
19528
|
+
}
|
|
19529
|
+
var WARNING_CHIP_STYLE = {
|
|
19530
|
+
color: "var(--color-pending, #B45309)",
|
|
19531
|
+
background: "var(--color-pending-wash, #FEF3C7)"
|
|
19532
|
+
};
|
|
19533
|
+
function toneChip(value) {
|
|
19534
|
+
switch (tone(value)) {
|
|
19535
|
+
case "positive":
|
|
19536
|
+
return { className: "bg-primary/10 text-primary" };
|
|
19537
|
+
case "warning":
|
|
19538
|
+
return { className: "", style: WARNING_CHIP_STYLE };
|
|
19539
|
+
case "danger":
|
|
19540
|
+
return { className: "bg-destructive/10 text-destructive" };
|
|
19541
|
+
default:
|
|
19542
|
+
return { className: "bg-muted text-muted-foreground" };
|
|
19969
19543
|
}
|
|
19970
|
-
return input;
|
|
19971
19544
|
}
|
|
19972
|
-
function
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
19976
|
-
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19545
|
+
function money(amount, currency) {
|
|
19546
|
+
return formatPrice(parsePrice(num(amount) ?? 0), text(currency));
|
|
19547
|
+
}
|
|
19548
|
+
var CHAT_GENUI_COMPONENTS = {
|
|
19549
|
+
Stack: ({ node, ctx }) => /* @__PURE__ */ jsx(
|
|
19550
|
+
"div",
|
|
19551
|
+
{
|
|
19552
|
+
className: cn("flex w-full", node.props.direction === "row" ? "flex-row" : "flex-col"),
|
|
19553
|
+
style: { gap: num(node.props.gap) ?? 10 },
|
|
19554
|
+
children: ctx.render(node.props.children)
|
|
19555
|
+
}
|
|
19556
|
+
),
|
|
19557
|
+
Card: ({ node, ctx }) => /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-2 rounded-2xl border border-border bg-background p-3.5", children: [
|
|
19558
|
+
text(node.props.title) && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: text(node.props.title) }),
|
|
19559
|
+
ctx.render(node.props.children)
|
|
19560
|
+
] }),
|
|
19561
|
+
TextContent: ({ node }) => {
|
|
19562
|
+
const size = text(node.props.size);
|
|
19563
|
+
return /* @__PURE__ */ jsx(
|
|
19564
|
+
"p",
|
|
19565
|
+
{
|
|
19566
|
+
className: cn(
|
|
19567
|
+
"break-words text-foreground",
|
|
19568
|
+
size === "display" || size === "heading" ? "text-[17px] font-bold leading-6" : size === "title" ? "text-[15px] font-semibold leading-5" : size === "label" || size === "caption" ? "text-xs text-muted-foreground" : "text-sm leading-5"
|
|
19569
|
+
),
|
|
19570
|
+
children: text(node.props.text) ?? ""
|
|
19988
19571
|
}
|
|
19989
|
-
|
|
19990
|
-
|
|
19572
|
+
);
|
|
19573
|
+
},
|
|
19574
|
+
Stat: ({ node }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
19575
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(node.props.label) }),
|
|
19576
|
+
/* @__PURE__ */ jsx("span", { className: "text-[17px] font-bold text-foreground tabular-nums", children: text(node.props.value) }),
|
|
19577
|
+
text(node.props.sub) && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(node.props.sub) })
|
|
19578
|
+
] }),
|
|
19579
|
+
KeyValue: ({ node }) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [
|
|
19580
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: text(node.props.label) }),
|
|
19581
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground tabular-nums", children: text(node.props.value) })
|
|
19582
|
+
] }),
|
|
19583
|
+
Badge: ({ node }) => {
|
|
19584
|
+
const chip = toneChip(node.props.tone);
|
|
19585
|
+
return /* @__PURE__ */ jsx(
|
|
19586
|
+
"span",
|
|
19587
|
+
{
|
|
19588
|
+
style: chip.style,
|
|
19589
|
+
className: cn("w-fit rounded-full px-2 py-0.5 text-[11px] font-semibold", chip.className),
|
|
19590
|
+
children: text(node.props.label)
|
|
19991
19591
|
}
|
|
19992
|
-
|
|
19993
|
-
|
|
19592
|
+
);
|
|
19593
|
+
},
|
|
19594
|
+
Callout: ({ node }) => {
|
|
19595
|
+
const chip = toneChip(node.props.tone);
|
|
19596
|
+
return /* @__PURE__ */ jsxs(
|
|
19597
|
+
"div",
|
|
19598
|
+
{
|
|
19599
|
+
style: chip.style,
|
|
19600
|
+
className: cn("flex flex-col gap-0.5 rounded-xl px-3 py-2.5", chip.className),
|
|
19601
|
+
children: [
|
|
19602
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold", children: text(node.props.title) }),
|
|
19603
|
+
text(node.props.message) && /* @__PURE__ */ jsx("span", { className: "text-xs leading-4 opacity-90", children: text(node.props.message) })
|
|
19604
|
+
]
|
|
19994
19605
|
}
|
|
19606
|
+
);
|
|
19607
|
+
},
|
|
19608
|
+
Button: ({ node, ctx }) => /* @__PURE__ */ jsx(
|
|
19609
|
+
"button",
|
|
19610
|
+
{
|
|
19611
|
+
type: "button",
|
|
19612
|
+
disabled: ctx.isStreaming,
|
|
19613
|
+
onClick: () => {
|
|
19614
|
+
const action = node.props.action;
|
|
19615
|
+
if (typeof action === "string") {
|
|
19616
|
+
ctx.onAction({ action, label: text(node.props.label) ?? "" });
|
|
19617
|
+
}
|
|
19618
|
+
},
|
|
19619
|
+
className: "w-fit rounded-full bg-foreground px-4 py-2 text-[13px] font-semibold text-background active:opacity-80",
|
|
19620
|
+
children: text(node.props.label)
|
|
19995
19621
|
}
|
|
19996
|
-
|
|
19997
|
-
})
|
|
19998
|
-
|
|
19999
|
-
|
|
20000
|
-
|
|
20001
|
-
|
|
20002
|
-
|
|
20003
|
-
|
|
20004
|
-
|
|
20005
|
-
const { text, wasIncomplete } = autoClose(trimmed);
|
|
20006
|
-
const stmts = split(tokenize(text));
|
|
20007
|
-
if (!stmts.length) return emptyResult(wasIncomplete);
|
|
20008
|
-
const stmtMap = /* @__PURE__ */ new Map();
|
|
20009
|
-
let firstId = "";
|
|
20010
|
-
for (const s of stmts) {
|
|
20011
|
-
const expr = parseExpression(s.tokens);
|
|
20012
|
-
const stmt = classifyStatement(s, expr);
|
|
20013
|
-
stmtMap.set(s.id, stmt);
|
|
20014
|
-
if (!firstId) firstId = s.id;
|
|
20015
|
-
}
|
|
20016
|
-
const typedStmts = [...stmtMap.values()];
|
|
20017
|
-
return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
|
|
20018
|
-
}
|
|
20019
|
-
function createStreamParser(cat, rootName) {
|
|
20020
|
-
let buf = "";
|
|
20021
|
-
let cleaned = "";
|
|
20022
|
-
let completedEnd = 0;
|
|
20023
|
-
const completedStmtMap = /* @__PURE__ */ new Map();
|
|
20024
|
-
let firstId = "";
|
|
20025
|
-
function addStmt(text) {
|
|
20026
|
-
const t = text.trim();
|
|
20027
|
-
if (!t) return;
|
|
20028
|
-
for (const s of split(tokenize(t))) {
|
|
20029
|
-
const expr = parseExpression(s.tokens);
|
|
20030
|
-
const stmt = classifyStatement(s, expr);
|
|
20031
|
-
completedStmtMap.set(s.id, stmt);
|
|
20032
|
-
if (!firstId) firstId = s.id;
|
|
19622
|
+
),
|
|
19623
|
+
Buttons: ({ node, ctx }) => /* @__PURE__ */ jsx(
|
|
19624
|
+
"div",
|
|
19625
|
+
{
|
|
19626
|
+
className: cn(
|
|
19627
|
+
"flex gap-2",
|
|
19628
|
+
node.props.direction === "column" ? "flex-col items-start" : "flex-row flex-wrap"
|
|
19629
|
+
),
|
|
19630
|
+
children: ctx.render(node.props.children)
|
|
20033
19631
|
}
|
|
19632
|
+
),
|
|
19633
|
+
Divider: () => /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-border" }),
|
|
19634
|
+
Table: ({ node }) => {
|
|
19635
|
+
const headers = Array.isArray(node.props.headers) ? node.props.headers.map(String) : [];
|
|
19636
|
+
const rows = Array.isArray(node.props.rows) ? node.props.rows : [];
|
|
19637
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col", children: [
|
|
19638
|
+
headers.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-3 border-b border-border pb-1", children: headers.map((header, index) => /* @__PURE__ */ jsx(
|
|
19639
|
+
"span",
|
|
19640
|
+
{
|
|
19641
|
+
className: "min-w-0 flex-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase",
|
|
19642
|
+
children: header
|
|
19643
|
+
},
|
|
19644
|
+
index
|
|
19645
|
+
)) }),
|
|
19646
|
+
rows.map((row, rowIndex) => /* @__PURE__ */ jsx("div", { className: "flex gap-3 border-b border-border py-1.5 last:border-b-0", children: (Array.isArray(row) ? row : [row]).map((cell, cellIndex) => /* @__PURE__ */ jsx(
|
|
19647
|
+
"span",
|
|
19648
|
+
{
|
|
19649
|
+
className: "min-w-0 flex-1 text-[13px] text-foreground tabular-nums",
|
|
19650
|
+
children: text(cell) ?? ""
|
|
19651
|
+
},
|
|
19652
|
+
cellIndex
|
|
19653
|
+
)) }, rowIndex))
|
|
19654
|
+
] });
|
|
19655
|
+
},
|
|
19656
|
+
Money: ({ node }) => /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground tabular-nums", children: money(node.props.amount, node.props.currency) }),
|
|
19657
|
+
Image: ({ node }) => text(node.props.url) ? /* @__PURE__ */ jsx(
|
|
19658
|
+
"img",
|
|
19659
|
+
{
|
|
19660
|
+
src: text(node.props.url),
|
|
19661
|
+
alt: text(node.props.caption) ?? "",
|
|
19662
|
+
loading: "lazy",
|
|
19663
|
+
className: "max-h-40 w-full rounded-xl object-cover"
|
|
19664
|
+
}
|
|
19665
|
+
) : null,
|
|
19666
|
+
ListRow: ({ node, ctx }) => {
|
|
19667
|
+
const action = text(node.props.action);
|
|
19668
|
+
const title = text(node.props.title) ?? "";
|
|
19669
|
+
const chip = toneChip(node.props.tone);
|
|
19670
|
+
return /* @__PURE__ */ jsxs(
|
|
19671
|
+
"div",
|
|
19672
|
+
{
|
|
19673
|
+
role: action ? "button" : void 0,
|
|
19674
|
+
onClick: action ? () => ctx.onAction({ action, label: title }) : void 0,
|
|
19675
|
+
className: cn("flex items-center gap-2.5 py-1", action && "cursor-pointer active:opacity-70"),
|
|
19676
|
+
children: [
|
|
19677
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 grow flex-col", children: [
|
|
19678
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
|
|
19679
|
+
text(node.props.subtitle) && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: text(node.props.subtitle) })
|
|
19680
|
+
] }),
|
|
19681
|
+
text(node.props.value) && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold text-foreground tabular-nums", children: text(node.props.value) }),
|
|
19682
|
+
text(node.props.badge) && /* @__PURE__ */ jsx(
|
|
19683
|
+
"span",
|
|
19684
|
+
{
|
|
19685
|
+
style: chip.style,
|
|
19686
|
+
className: cn(
|
|
19687
|
+
"shrink-0 rounded-full px-2 py-0.5 text-[10.5px] font-semibold",
|
|
19688
|
+
chip.className
|
|
19689
|
+
),
|
|
19690
|
+
children: text(node.props.badge)
|
|
19691
|
+
}
|
|
19692
|
+
)
|
|
19693
|
+
]
|
|
19694
|
+
}
|
|
19695
|
+
);
|
|
19696
|
+
},
|
|
19697
|
+
Timeline: ({ node }) => /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: rowsOf(node.props.events).map((event, index, events) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5", children: [
|
|
19698
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-2.5 flex-col items-center", children: [
|
|
19699
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1.5 size-1.5 rounded-full bg-primary" }),
|
|
19700
|
+
index < events.length - 1 && /* @__PURE__ */ jsx("span", { className: "w-px grow bg-border" })
|
|
19701
|
+
] }),
|
|
19702
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col pb-2.5", children: [
|
|
19703
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-foreground", children: text(event.title) }),
|
|
19704
|
+
text(event.detail) && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(event.detail) }),
|
|
19705
|
+
text(event.time) && /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: text(event.time) })
|
|
19706
|
+
] })
|
|
19707
|
+
] }, index)) }),
|
|
19708
|
+
Progress: ({ node }) => {
|
|
19709
|
+
const value = num(node.props.value) ?? 0;
|
|
19710
|
+
const target = num(node.props.target) || 100;
|
|
19711
|
+
const ratio = Math.min(1, Math.max(0, target === 0 ? 0 : value / target));
|
|
19712
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-1", children: [
|
|
19713
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
19714
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(node.props.label) }),
|
|
19715
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground tabular-nums", children: text(node.props.value) })
|
|
19716
|
+
] }),
|
|
19717
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-primary", style: { width: `${ratio * 100}%` } }) })
|
|
19718
|
+
] });
|
|
19719
|
+
},
|
|
19720
|
+
StatusTrack: ({ node }) => {
|
|
19721
|
+
const steps = Array.isArray(node.props.steps) ? node.props.steps.map(String) : [];
|
|
19722
|
+
const current = node.props.current;
|
|
19723
|
+
const currentIndex = typeof current === "number" ? Math.min(Math.max(0, Math.trunc(current)), Math.max(0, steps.length - 1)) : Math.max(0, steps.indexOf(String(current ?? "")));
|
|
19724
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col gap-1", children: [
|
|
19725
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: steps.map((_, index) => /* @__PURE__ */ jsx(
|
|
19726
|
+
"span",
|
|
19727
|
+
{
|
|
19728
|
+
className: cn(
|
|
19729
|
+
"h-1 grow rounded-full",
|
|
19730
|
+
index <= currentIndex ? "bg-primary" : "bg-muted"
|
|
19731
|
+
)
|
|
19732
|
+
},
|
|
19733
|
+
index
|
|
19734
|
+
)) }),
|
|
19735
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: steps[currentIndex] })
|
|
19736
|
+
] });
|
|
19737
|
+
},
|
|
19738
|
+
LineItems: ({ node }) => /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col", children: [
|
|
19739
|
+
rowsOf(node.props.items).map((item, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 py-0.5", children: [
|
|
19740
|
+
text(item.qty) && /* @__PURE__ */ jsxs("span", { className: "w-5 shrink-0 text-[13px] text-muted-foreground tabular-nums", children: [
|
|
19741
|
+
text(item.qty),
|
|
19742
|
+
"\xD7"
|
|
19743
|
+
] }),
|
|
19744
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 grow truncate text-sm text-foreground", children: text(item.name) }),
|
|
19745
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold text-foreground tabular-nums", children: text(item.amount) })
|
|
19746
|
+
] }, index)),
|
|
19747
|
+
rowsOf(node.props.adjustments).map((adjustment, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between py-0.5", children: [
|
|
19748
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(adjustment.label) }),
|
|
19749
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground tabular-nums", children: text(adjustment.amount) })
|
|
19750
|
+
] }, `adj-${index}`)),
|
|
19751
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-baseline justify-between border-t border-border pt-1.5", children: [
|
|
19752
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Total" }),
|
|
19753
|
+
/* @__PURE__ */ jsx("span", { className: "text-[15px] font-bold text-foreground tabular-nums", children: text(node.props.total) })
|
|
19754
|
+
] })
|
|
19755
|
+
] }),
|
|
19756
|
+
Grid: ({ node, ctx }) => /* @__PURE__ */ jsx("div", { className: "grid w-full grid-cols-2 gap-2", children: ctx.render(node.props.children) }),
|
|
19757
|
+
EmptyState: ({ node }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0.5 py-4 text-center", children: [
|
|
19758
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: text(node.props.title) }),
|
|
19759
|
+
text(node.props.message) && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: text(node.props.message) })
|
|
19760
|
+
] })
|
|
19761
|
+
};
|
|
19762
|
+
var CATALOG = genUICatalog(CIMPLIFY_GENUI_REGISTRY);
|
|
19763
|
+
function ChatKitProgram({ source, onAction }) {
|
|
19764
|
+
const parsed = useMemo(() => parse(source, CATALOG, "root"), [source]);
|
|
19765
|
+
if (!parsed.root) return null;
|
|
19766
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full max-w-[290px]", children: /* @__PURE__ */ jsx(GenUIView, { root: parsed.root, components: CHAT_GENUI_COMPONENTS, onAction }) });
|
|
19767
|
+
}
|
|
19768
|
+
function ChatReplyCards({ message, currency, onAction }) {
|
|
19769
|
+
const cards = chatMessageCards(message);
|
|
19770
|
+
if (cards.length === 0) return null;
|
|
19771
|
+
return /* @__PURE__ */ jsx("div", { className: "scrollbar-hide flex max-w-full gap-2 overflow-x-auto pl-[30px]", children: cards.map((card, index) => /* @__PURE__ */ jsx(ReplyCardView, { card, currency, onAction }, index)) });
|
|
19772
|
+
}
|
|
19773
|
+
function ReplyCardView({
|
|
19774
|
+
card,
|
|
19775
|
+
currency,
|
|
19776
|
+
onAction
|
|
19777
|
+
}) {
|
|
19778
|
+
if (card.kind === "product") {
|
|
19779
|
+
const fire2 = onAction ? () => onAction({ action: chatAction(CHAT_ACTION_VIEW_PRODUCT, card.id), label: card.name }) : void 0;
|
|
19780
|
+
return /* @__PURE__ */ jsxs(
|
|
19781
|
+
"button",
|
|
19782
|
+
{
|
|
19783
|
+
type: "button",
|
|
19784
|
+
onClick: fire2,
|
|
19785
|
+
disabled: !fire2,
|
|
19786
|
+
className: cn(
|
|
19787
|
+
"flex w-[230px] shrink-0 items-center gap-3 rounded-2xl border border-border bg-background p-2.5 text-left",
|
|
19788
|
+
fire2 && "active:opacity-80"
|
|
19789
|
+
),
|
|
19790
|
+
children: [
|
|
19791
|
+
card.image_url && /* @__PURE__ */ jsx("img", { src: card.image_url, alt: "", className: "size-12 shrink-0 rounded-xl object-cover" }),
|
|
19792
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
19793
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: card.name }),
|
|
19794
|
+
card.price !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-foreground tabular-nums", children: formatPrice(parsePrice(card.price), currency ?? void 0) }),
|
|
19795
|
+
fire2 && /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-primary", children: "View" })
|
|
19796
|
+
] })
|
|
19797
|
+
]
|
|
19798
|
+
}
|
|
19799
|
+
);
|
|
20034
19800
|
}
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20039
|
-
|
|
20040
|
-
|
|
19801
|
+
const fire = onAction ? () => onAction({
|
|
19802
|
+
action: chatAction(CHAT_ACTION_VIEW_ORDER, card.order_id),
|
|
19803
|
+
label: card.order_number ?? card.order_id
|
|
19804
|
+
}) : void 0;
|
|
19805
|
+
return /* @__PURE__ */ jsxs(
|
|
19806
|
+
"button",
|
|
19807
|
+
{
|
|
19808
|
+
type: "button",
|
|
19809
|
+
onClick: fire,
|
|
19810
|
+
disabled: !fire,
|
|
19811
|
+
className: cn(
|
|
19812
|
+
"flex w-[230px] shrink-0 flex-col gap-1 rounded-2xl border border-border bg-background p-3 text-left",
|
|
19813
|
+
fire && "active:opacity-80"
|
|
19814
|
+
),
|
|
19815
|
+
children: [
|
|
19816
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-foreground", children: [
|
|
19817
|
+
"Order ",
|
|
19818
|
+
card.order_number ? `#${card.order_number}` : ""
|
|
19819
|
+
] }),
|
|
19820
|
+
card.status && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: card.status }),
|
|
19821
|
+
fire && /* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-primary", children: "View order" })
|
|
19822
|
+
]
|
|
20041
19823
|
}
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20046
|
-
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
20053
|
-
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
|
|
20059
|
-
|
|
19824
|
+
);
|
|
19825
|
+
}
|
|
19826
|
+
var MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;
|
|
19827
|
+
var KEEP_DISMISSED_KEY = "cimplify_keep_chat_dismissed";
|
|
19828
|
+
var AT_BOTTOM_SLACK_PX = 48;
|
|
19829
|
+
function ChatWidget({
|
|
19830
|
+
client: clientProp,
|
|
19831
|
+
businessName: businessNameProp,
|
|
19832
|
+
greeting = "Hi there!",
|
|
19833
|
+
subtitle = "Ask us anything \u2014 we typically reply in a few seconds.",
|
|
19834
|
+
starters,
|
|
19835
|
+
placeholder = "Type a message\u2026",
|
|
19836
|
+
position = "bottom-right",
|
|
19837
|
+
defaultOpen = false,
|
|
19838
|
+
pollInterval,
|
|
19839
|
+
currency: currencyProp,
|
|
19840
|
+
onGenUIAction,
|
|
19841
|
+
className,
|
|
19842
|
+
classNames
|
|
19843
|
+
}) {
|
|
19844
|
+
const context = useOptionalCimplifyClient();
|
|
19845
|
+
const client = clientProp ?? context?.client;
|
|
19846
|
+
const businessName = businessNameProp ?? context?.business?.name ?? "Support";
|
|
19847
|
+
const currency = currencyProp ?? context?.baseCurrency;
|
|
19848
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
19849
|
+
const [readAt, setReadAt] = useState(null);
|
|
19850
|
+
const [attachError, setAttachError] = useState(null);
|
|
19851
|
+
const [replyTo, setReplyTo] = useState(null);
|
|
19852
|
+
const {
|
|
19853
|
+
messages,
|
|
19854
|
+
isLoading,
|
|
19855
|
+
isSending,
|
|
19856
|
+
awaitingReply,
|
|
19857
|
+
seenAt,
|
|
19858
|
+
error,
|
|
19859
|
+
isActive,
|
|
19860
|
+
humanRequested,
|
|
19861
|
+
claimedAs,
|
|
19862
|
+
signOut,
|
|
19863
|
+
send,
|
|
19864
|
+
react,
|
|
19865
|
+
requestHuman,
|
|
19866
|
+
startConversation,
|
|
19867
|
+
loadOlder,
|
|
19868
|
+
mayHaveOlder,
|
|
19869
|
+
openIfExisting
|
|
19870
|
+
} = useChat({ client, pollInterval });
|
|
19871
|
+
const showTyping = isSending || awaitingReply;
|
|
19872
|
+
const attachPhoto = useCallback(
|
|
19873
|
+
async (file) => {
|
|
19874
|
+
if (!client) return;
|
|
19875
|
+
setAttachError(null);
|
|
19876
|
+
if (!file.type.startsWith("image/")) {
|
|
19877
|
+
setAttachError(new CimplifyError("invalid_attachment", "Only photos can be attached."));
|
|
19878
|
+
return;
|
|
20060
19879
|
}
|
|
20061
|
-
if (
|
|
20062
|
-
|
|
20063
|
-
|
|
19880
|
+
if (file.size > MAX_ATTACHMENT_BYTES) {
|
|
19881
|
+
setAttachError(new CimplifyError("attachment_too_large", "Photos must be under 5MB."));
|
|
19882
|
+
return;
|
|
20064
19883
|
}
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
|
|
20070
|
-
let peek = i + 1;
|
|
20071
|
-
while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n"))
|
|
20072
|
-
peek++;
|
|
20073
|
-
if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) {
|
|
20074
|
-
continue;
|
|
20075
|
-
}
|
|
20076
|
-
const t = cleaned.slice(stmtStart, i).trim();
|
|
20077
|
-
if (t) addStmt(t);
|
|
20078
|
-
stmtStart = i + 1;
|
|
20079
|
-
completedEnd = i + 1;
|
|
19884
|
+
const uploaded = await client.uploads.upload(file);
|
|
19885
|
+
if (!uploaded.ok) {
|
|
19886
|
+
setAttachError(uploaded.error);
|
|
19887
|
+
return;
|
|
20080
19888
|
}
|
|
19889
|
+
const attachment = {
|
|
19890
|
+
upload_id: uploaded.value.id,
|
|
19891
|
+
url: uploaded.value.url,
|
|
19892
|
+
mime_type: uploaded.value.content_type,
|
|
19893
|
+
filename: uploaded.value.filename,
|
|
19894
|
+
size: uploaded.value.size_bytes
|
|
19895
|
+
};
|
|
19896
|
+
await send("", { attachments: [attachment] });
|
|
19897
|
+
},
|
|
19898
|
+
[client, send]
|
|
19899
|
+
);
|
|
19900
|
+
const handleSend = useCallback(
|
|
19901
|
+
(content) => {
|
|
19902
|
+
setAttachError(null);
|
|
19903
|
+
const target = replyTo;
|
|
19904
|
+
setReplyTo(null);
|
|
19905
|
+
return send(content, target ? { replyTo: target } : void 0);
|
|
19906
|
+
},
|
|
19907
|
+
[send, replyTo]
|
|
19908
|
+
);
|
|
19909
|
+
const unread = open ? 0 : messages.filter(
|
|
19910
|
+
(m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
|
|
19911
|
+
).length;
|
|
19912
|
+
useState(() => {
|
|
19913
|
+
if (defaultOpen) void openIfExisting();
|
|
19914
|
+
});
|
|
19915
|
+
const handleOpen = useCallback(() => {
|
|
19916
|
+
setOpen(true);
|
|
19917
|
+
void openIfExisting();
|
|
19918
|
+
}, [openIfExisting]);
|
|
19919
|
+
const handleClose = useCallback(() => {
|
|
19920
|
+
const newest = messages[messages.length - 1];
|
|
19921
|
+
setReadAt(newest?.created_at ?? null);
|
|
19922
|
+
setOpen(false);
|
|
19923
|
+
}, [messages]);
|
|
19924
|
+
const isLeft = position === "bottom-left";
|
|
19925
|
+
return /* @__PURE__ */ jsxs(
|
|
19926
|
+
"div",
|
|
19927
|
+
{
|
|
19928
|
+
className: cn(
|
|
19929
|
+
"fixed bottom-6 z-[9999]",
|
|
19930
|
+
isLeft ? "left-6" : "right-6",
|
|
19931
|
+
className,
|
|
19932
|
+
classNames?.root
|
|
19933
|
+
),
|
|
19934
|
+
children: [
|
|
19935
|
+
!open && /* @__PURE__ */ jsx(
|
|
19936
|
+
Bubble,
|
|
19937
|
+
{
|
|
19938
|
+
unread,
|
|
19939
|
+
onClick: handleOpen,
|
|
19940
|
+
className: classNames?.bubble
|
|
19941
|
+
}
|
|
19942
|
+
),
|
|
19943
|
+
open && /* @__PURE__ */ jsx(
|
|
19944
|
+
Panel,
|
|
19945
|
+
{
|
|
19946
|
+
client,
|
|
19947
|
+
businessName,
|
|
19948
|
+
greeting,
|
|
19949
|
+
subtitle,
|
|
19950
|
+
starters,
|
|
19951
|
+
placeholder,
|
|
19952
|
+
messages,
|
|
19953
|
+
isLoading,
|
|
19954
|
+
isSending,
|
|
19955
|
+
isTyping: showTyping,
|
|
19956
|
+
seenAt,
|
|
19957
|
+
readAt,
|
|
19958
|
+
error: error ?? attachError,
|
|
19959
|
+
isActive,
|
|
19960
|
+
isLeft,
|
|
19961
|
+
humanRequested,
|
|
19962
|
+
mayHaveOlder,
|
|
19963
|
+
replyTo,
|
|
19964
|
+
claimedAs,
|
|
19965
|
+
onSignOut: () => void signOut(),
|
|
19966
|
+
onClose: handleClose,
|
|
19967
|
+
onSend: handleSend,
|
|
19968
|
+
onReact: react,
|
|
19969
|
+
onReply: setReplyTo,
|
|
19970
|
+
onCancelReply: () => setReplyTo(null),
|
|
19971
|
+
onRequestHuman: () => void requestHuman(),
|
|
19972
|
+
onLoadOlder: () => void loadOlder(),
|
|
19973
|
+
onAttach: attachPhoto,
|
|
19974
|
+
onStartConversation: startConversation,
|
|
19975
|
+
currency,
|
|
19976
|
+
onGenUIAction,
|
|
19977
|
+
classNames
|
|
19978
|
+
}
|
|
19979
|
+
)
|
|
19980
|
+
]
|
|
20081
19981
|
}
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
|
|
20102
|
-
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
19982
|
+
);
|
|
19983
|
+
}
|
|
19984
|
+
function Bubble({
|
|
19985
|
+
unread,
|
|
19986
|
+
onClick,
|
|
19987
|
+
className
|
|
19988
|
+
}) {
|
|
19989
|
+
return /* @__PURE__ */ jsxs(
|
|
19990
|
+
"button",
|
|
19991
|
+
{
|
|
19992
|
+
type: "button",
|
|
19993
|
+
onClick,
|
|
19994
|
+
"aria-label": "Open chat",
|
|
19995
|
+
className: cn(
|
|
19996
|
+
"relative flex h-14 w-14 items-center justify-center rounded-full",
|
|
19997
|
+
"bg-foreground text-background shadow-lg",
|
|
19998
|
+
"transition-transform duration-300 ease-out",
|
|
19999
|
+
"hover:scale-[1.08] active:scale-95",
|
|
20000
|
+
className
|
|
20001
|
+
),
|
|
20002
|
+
children: [
|
|
20003
|
+
/* @__PURE__ */ jsx(ChatIcon, { className: "h-6 w-6" }),
|
|
20004
|
+
unread > 0 && /* @__PURE__ */ jsx(
|
|
20005
|
+
"span",
|
|
20006
|
+
{
|
|
20007
|
+
className: cn(
|
|
20008
|
+
"absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
|
|
20009
|
+
"rounded-full bg-destructive text-[10px] font-bold text-white",
|
|
20010
|
+
"ring-2 ring-background"
|
|
20011
|
+
),
|
|
20012
|
+
children: unread > 9 ? "9+" : unread
|
|
20013
|
+
}
|
|
20014
|
+
)
|
|
20015
|
+
]
|
|
20113
20016
|
}
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20017
|
+
);
|
|
20018
|
+
}
|
|
20019
|
+
function Panel({
|
|
20020
|
+
client,
|
|
20021
|
+
businessName,
|
|
20022
|
+
greeting,
|
|
20023
|
+
subtitle,
|
|
20024
|
+
starters,
|
|
20025
|
+
placeholder,
|
|
20026
|
+
messages,
|
|
20027
|
+
isLoading,
|
|
20028
|
+
isSending,
|
|
20029
|
+
isTyping,
|
|
20030
|
+
seenAt,
|
|
20031
|
+
readAt,
|
|
20032
|
+
error,
|
|
20033
|
+
isActive,
|
|
20034
|
+
isLeft,
|
|
20035
|
+
humanRequested,
|
|
20036
|
+
mayHaveOlder,
|
|
20037
|
+
replyTo,
|
|
20038
|
+
claimedAs,
|
|
20039
|
+
onSignOut,
|
|
20040
|
+
onClose,
|
|
20041
|
+
onSend,
|
|
20042
|
+
onReact,
|
|
20043
|
+
onReply,
|
|
20044
|
+
onCancelReply,
|
|
20045
|
+
onRequestHuman,
|
|
20046
|
+
onLoadOlder,
|
|
20047
|
+
onAttach,
|
|
20048
|
+
onStartConversation,
|
|
20049
|
+
currency,
|
|
20050
|
+
onGenUIAction,
|
|
20051
|
+
classNames
|
|
20052
|
+
}) {
|
|
20053
|
+
return /* @__PURE__ */ jsxs(
|
|
20054
|
+
"div",
|
|
20055
|
+
{
|
|
20056
|
+
className: cn(
|
|
20057
|
+
"flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
|
|
20058
|
+
"w-[400px] max-w-[calc(100vw-3rem)]",
|
|
20059
|
+
"h-[min(600px,calc(100vh-6rem))]",
|
|
20060
|
+
"animate-in fade-in slide-in-from-bottom-4 duration-300",
|
|
20061
|
+
isLeft ? "origin-bottom-left" : "origin-bottom-right",
|
|
20062
|
+
classNames?.panel
|
|
20063
|
+
),
|
|
20064
|
+
children: [
|
|
20065
|
+
/* @__PURE__ */ jsx(
|
|
20066
|
+
Header,
|
|
20067
|
+
{
|
|
20068
|
+
businessName,
|
|
20069
|
+
claimedAs,
|
|
20070
|
+
onSignOut,
|
|
20071
|
+
onClose,
|
|
20072
|
+
className: classNames?.header
|
|
20073
|
+
}
|
|
20074
|
+
),
|
|
20075
|
+
!isActive && !isLoading ? /* @__PURE__ */ jsx(
|
|
20076
|
+
Welcome,
|
|
20077
|
+
{
|
|
20078
|
+
greeting,
|
|
20079
|
+
subtitle,
|
|
20080
|
+
starters,
|
|
20081
|
+
onStarter: onStartConversation,
|
|
20082
|
+
className: classNames?.welcome
|
|
20083
|
+
}
|
|
20084
|
+
) : /* @__PURE__ */ jsx(
|
|
20085
|
+
MessageList,
|
|
20086
|
+
{
|
|
20087
|
+
messages,
|
|
20088
|
+
businessName,
|
|
20089
|
+
isLoading,
|
|
20090
|
+
isSending: isTyping,
|
|
20091
|
+
seenAt,
|
|
20092
|
+
readAt,
|
|
20093
|
+
humanRequested,
|
|
20094
|
+
mayHaveOlder,
|
|
20095
|
+
onReact,
|
|
20096
|
+
onReply,
|
|
20097
|
+
onRequestHuman,
|
|
20098
|
+
onLoadOlder,
|
|
20099
|
+
currency,
|
|
20100
|
+
onGenUIAction,
|
|
20101
|
+
className: classNames?.messages
|
|
20102
|
+
}
|
|
20103
|
+
),
|
|
20104
|
+
isActive && !claimedAs && client && /* @__PURE__ */ jsx(KeepConversation, { client }),
|
|
20105
|
+
humanRequested && isActive && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-t border-border bg-muted/50 px-4 py-2 text-[11px] text-muted-foreground", children: [
|
|
20106
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 flex-shrink-0 animate-pulse rounded-full bg-amber-500" }),
|
|
20107
|
+
"Waiting for ",
|
|
20108
|
+
businessName,
|
|
20109
|
+
" \u2014 usually replies in minutes"
|
|
20110
|
+
] }),
|
|
20111
|
+
error != null && /* @__PURE__ */ jsx("div", { className: "border-t border-destructive/20 bg-destructive/10 px-4 py-2 text-xs text-destructive", children: error.userMessage || "Something went wrong. Please try again." }),
|
|
20112
|
+
replyTo && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 border-t border-border bg-muted/50 px-4 py-2", children: [
|
|
20113
|
+
/* @__PURE__ */ jsx("span", { className: "w-[3px] shrink-0 self-stretch rounded-full bg-foreground/40" }),
|
|
20114
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
20115
|
+
/* @__PURE__ */ jsxs("div", { className: "text-[11px] font-semibold text-foreground", children: [
|
|
20116
|
+
"Replying to ",
|
|
20117
|
+
replyTo.sender_type === "customer" ? "yourself" : businessName
|
|
20118
|
+
] }),
|
|
20119
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: proseOf(replyTo.content) || "Photo" })
|
|
20120
|
+
] }),
|
|
20121
|
+
/* @__PURE__ */ jsx(
|
|
20122
|
+
"button",
|
|
20123
|
+
{
|
|
20124
|
+
type: "button",
|
|
20125
|
+
"aria-label": "Cancel reply",
|
|
20126
|
+
onClick: onCancelReply,
|
|
20127
|
+
className: "flex-shrink-0 p-0.5 text-muted-foreground hover:text-foreground",
|
|
20128
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
20129
|
+
}
|
|
20130
|
+
)
|
|
20131
|
+
] }),
|
|
20132
|
+
/* @__PURE__ */ jsx(
|
|
20133
|
+
ChatInput,
|
|
20134
|
+
{
|
|
20135
|
+
placeholder,
|
|
20136
|
+
isSending,
|
|
20137
|
+
onSend: isActive ? onSend : async (text2) => {
|
|
20138
|
+
await onStartConversation(text2);
|
|
20139
|
+
return true;
|
|
20140
|
+
},
|
|
20141
|
+
onAttach,
|
|
20142
|
+
className: classNames?.input
|
|
20143
|
+
}
|
|
20144
|
+
),
|
|
20145
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-background px-4 py-1.5 text-center text-[10px] text-muted-foreground", children: [
|
|
20146
|
+
"Powered by",
|
|
20147
|
+
" ",
|
|
20148
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
|
|
20149
|
+
] })
|
|
20150
|
+
]
|
|
20119
20151
|
}
|
|
20120
|
-
|
|
20121
|
-
const fid = firstId || stmts[0].id;
|
|
20122
|
-
return buildResult(
|
|
20123
|
-
allStmtMap,
|
|
20124
|
-
allTypedStmts,
|
|
20125
|
-
fid,
|
|
20126
|
-
wasIncomplete,
|
|
20127
|
-
allStmtMap.size,
|
|
20128
|
-
cat,
|
|
20129
|
-
rootName
|
|
20130
|
-
);
|
|
20131
|
-
}
|
|
20132
|
-
function reset() {
|
|
20133
|
-
buf = "";
|
|
20134
|
-
cleaned = "";
|
|
20135
|
-
completedEnd = 0;
|
|
20136
|
-
completedStmtMap.clear();
|
|
20137
|
-
firstId = "";
|
|
20138
|
-
}
|
|
20139
|
-
return {
|
|
20140
|
-
push(chunk) {
|
|
20141
|
-
buf += chunk;
|
|
20142
|
-
return currentResult();
|
|
20143
|
-
},
|
|
20144
|
-
set(fullText) {
|
|
20145
|
-
if (fullText.length < buf.length || !fullText.startsWith(buf)) {
|
|
20146
|
-
reset();
|
|
20147
|
-
}
|
|
20148
|
-
const delta = fullText.slice(buf.length);
|
|
20149
|
-
if (delta) buf += delta;
|
|
20150
|
-
return currentResult();
|
|
20151
|
-
},
|
|
20152
|
-
getResult: currentResult
|
|
20153
|
-
};
|
|
20152
|
+
);
|
|
20154
20153
|
}
|
|
20155
|
-
function
|
|
20156
|
-
|
|
20157
|
-
|
|
20154
|
+
function Header({
|
|
20155
|
+
businessName,
|
|
20156
|
+
claimedAs,
|
|
20157
|
+
onSignOut,
|
|
20158
|
+
onClose,
|
|
20159
|
+
className
|
|
20160
|
+
}) {
|
|
20161
|
+
return /* @__PURE__ */ jsxs(
|
|
20162
|
+
"div",
|
|
20163
|
+
{
|
|
20164
|
+
className: cn(
|
|
20165
|
+
"flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
|
|
20166
|
+
className
|
|
20167
|
+
),
|
|
20168
|
+
children: [
|
|
20169
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-background/15", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
20170
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
20171
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
20172
|
+
claimedAs ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-background/60", children: [
|
|
20173
|
+
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
20174
|
+
"Chatting as ",
|
|
20175
|
+
claimedAs
|
|
20176
|
+
] }),
|
|
20177
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
20178
|
+
/* @__PURE__ */ jsx(
|
|
20179
|
+
"button",
|
|
20180
|
+
{
|
|
20181
|
+
type: "button",
|
|
20182
|
+
onClick: onSignOut,
|
|
20183
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
|
|
20184
|
+
children: "Not you?"
|
|
20185
|
+
}
|
|
20186
|
+
)
|
|
20187
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-background/60", children: [
|
|
20188
|
+
/* @__PURE__ */ jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
20189
|
+
"Online"
|
|
20190
|
+
] })
|
|
20191
|
+
] }),
|
|
20192
|
+
/* @__PURE__ */ jsx(
|
|
20193
|
+
"button",
|
|
20194
|
+
{
|
|
20195
|
+
type: "button",
|
|
20196
|
+
onClick: onClose,
|
|
20197
|
+
"aria-label": "Close chat",
|
|
20198
|
+
className: cn(
|
|
20199
|
+
"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
|
|
20200
|
+
"bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
|
|
20201
|
+
),
|
|
20202
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
|
|
20203
|
+
}
|
|
20204
|
+
)
|
|
20205
|
+
]
|
|
20206
|
+
}
|
|
20158
20207
|
);
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20208
|
+
}
|
|
20209
|
+
function Welcome({
|
|
20210
|
+
greeting,
|
|
20211
|
+
subtitle,
|
|
20212
|
+
starters,
|
|
20213
|
+
onStarter,
|
|
20214
|
+
className
|
|
20215
|
+
}) {
|
|
20216
|
+
const defaultStarters = [
|
|
20217
|
+
{ icon: "\u{1F50D}", text: "Help me find a product" },
|
|
20218
|
+
{ icon: "\u{1F6D2}", text: "Check my cart" },
|
|
20219
|
+
{ icon: "\u{1F4E6}", text: "Track my order" }
|
|
20220
|
+
];
|
|
20221
|
+
const items = starters ?? defaultStarters;
|
|
20222
|
+
return /* @__PURE__ */ jsxs(
|
|
20223
|
+
"div",
|
|
20224
|
+
{
|
|
20225
|
+
className: cn(
|
|
20226
|
+
"flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
|
|
20227
|
+
className
|
|
20228
|
+
),
|
|
20229
|
+
children: [
|
|
20230
|
+
/* @__PURE__ */ jsx("div", { className: "mb-5 flex h-16 w-16 items-center justify-center rounded-2xl bg-muted", children: /* @__PURE__ */ jsx(ChatIcon, { className: "h-7 w-7 text-muted-foreground" }) }),
|
|
20231
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
|
|
20232
|
+
/* @__PURE__ */ jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
|
|
20233
|
+
/* @__PURE__ */ jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxs(
|
|
20234
|
+
"button",
|
|
20235
|
+
{
|
|
20236
|
+
type: "button",
|
|
20237
|
+
onClick: () => onStarter(s.text),
|
|
20238
|
+
className: cn(
|
|
20239
|
+
"flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
|
|
20240
|
+
"text-left text-[13px] font-medium text-foreground",
|
|
20241
|
+
"transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
|
|
20242
|
+
),
|
|
20243
|
+
children: [
|
|
20244
|
+
s.icon && /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-muted text-[15px]", children: s.icon }),
|
|
20245
|
+
/* @__PURE__ */ jsx("span", { children: s.text })
|
|
20246
|
+
]
|
|
20247
|
+
},
|
|
20248
|
+
s.text
|
|
20249
|
+
)) })
|
|
20250
|
+
]
|
|
20167
20251
|
}
|
|
20168
|
-
|
|
20169
|
-
}, [text, catalog]);
|
|
20252
|
+
);
|
|
20170
20253
|
}
|
|
20171
|
-
var
|
|
20172
|
-
}
|
|
20173
|
-
|
|
20174
|
-
if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
|
|
20175
|
-
return `i:${index}`;
|
|
20254
|
+
var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
|
|
20255
|
+
function LetterAvatar({ name }) {
|
|
20256
|
+
return /* @__PURE__ */ jsx("span", { className: "flex size-6 items-center justify-center rounded-full bg-foreground text-[10px] font-bold text-background", children: name.charAt(0).toUpperCase() });
|
|
20176
20257
|
}
|
|
20177
|
-
|
|
20178
|
-
|
|
20179
|
-
|
|
20180
|
-
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
20184
|
-
|
|
20258
|
+
function MessageList({
|
|
20259
|
+
messages,
|
|
20260
|
+
businessName,
|
|
20261
|
+
isLoading,
|
|
20262
|
+
isSending,
|
|
20263
|
+
seenAt,
|
|
20264
|
+
readAt,
|
|
20265
|
+
humanRequested,
|
|
20266
|
+
mayHaveOlder,
|
|
20267
|
+
onReact,
|
|
20268
|
+
onReply,
|
|
20269
|
+
onRequestHuman,
|
|
20270
|
+
onLoadOlder,
|
|
20271
|
+
currency,
|
|
20272
|
+
onGenUIAction,
|
|
20273
|
+
className
|
|
20185
20274
|
}) {
|
|
20186
|
-
const
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20275
|
+
const scrolledUpRef = useRef(false);
|
|
20276
|
+
const timeline = chatTimeline(messages, { readAt, rendersPrograms: true });
|
|
20277
|
+
const { bubbles, reactionsByTarget, firstUnreadId, unreadOnOpen, firstAgentMessageId } = timeline;
|
|
20278
|
+
const lastBubble = bubbles[bubbles.length - 1];
|
|
20279
|
+
const offerHuman = !humanRequested && lastBubble?.sender_type === "bot" && !isSending;
|
|
20280
|
+
const handleScroll = (event) => {
|
|
20281
|
+
const node = event.currentTarget;
|
|
20282
|
+
scrolledUpRef.current = node.scrollHeight - node.scrollTop - node.clientHeight >= AT_BOTTOM_SLACK_PX;
|
|
20283
|
+
};
|
|
20284
|
+
return /* @__PURE__ */ jsxs(
|
|
20285
|
+
"div",
|
|
20286
|
+
{
|
|
20287
|
+
onScroll: handleScroll,
|
|
20288
|
+
style: { background: "var(--color-chat-canvas, var(--color-muted, #f3f5f3))" },
|
|
20289
|
+
className: cn("flex flex-1 flex-col gap-0.5 overflow-y-auto px-3.5 py-3.5", className),
|
|
20290
|
+
children: [
|
|
20291
|
+
isLoading && messages.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
20292
|
+
mayHaveOlder && /* @__PURE__ */ jsx("div", { className: "flex justify-center pb-1", children: /* @__PURE__ */ jsx(
|
|
20293
|
+
"button",
|
|
20294
|
+
{
|
|
20295
|
+
type: "button",
|
|
20296
|
+
onClick: onLoadOlder,
|
|
20297
|
+
className: "rounded-full border border-foreground/[0.06] bg-background px-3.5 py-[7px] text-xs font-semibold text-muted-foreground shadow-[0_1px_2px_rgba(15,23,42,0.04)]",
|
|
20298
|
+
children: "Show earlier messages"
|
|
20299
|
+
}
|
|
20300
|
+
) }),
|
|
20301
|
+
bubbles.map((msg, i) => {
|
|
20302
|
+
const prev = bubbles[i - 1];
|
|
20303
|
+
const newDay = !prev || !sameDay(prev.created_at, msg.created_at);
|
|
20304
|
+
const group = groupPosition(timeline, i);
|
|
20305
|
+
const startsRun = group === "single" || group === "first";
|
|
20306
|
+
const delivery = deliveryState(msg, timeline, seenAt, isOptimistic);
|
|
20307
|
+
return /* @__PURE__ */ jsxs(
|
|
20308
|
+
"div",
|
|
20309
|
+
{
|
|
20310
|
+
className: cn("flex flex-col gap-1.5", startsRun && i > 0 && "mt-2.5"),
|
|
20311
|
+
children: [
|
|
20312
|
+
newDay && /* @__PURE__ */ jsx(Seam, { variant: "chip", label: dayLabel(msg.created_at) }),
|
|
20313
|
+
msg.id === firstUnreadId && /* @__PURE__ */ jsx(
|
|
20314
|
+
Seam,
|
|
20315
|
+
{
|
|
20316
|
+
variant: "unread",
|
|
20317
|
+
label: `${unreadOnOpen} new message${unreadOnOpen === 1 ? "" : "s"}`
|
|
20318
|
+
}
|
|
20319
|
+
),
|
|
20320
|
+
msg.id === firstAgentMessageId && /* @__PURE__ */ jsx(Seam, { label: "A person joined" }),
|
|
20321
|
+
msg.sender_type === "system" ? /* @__PURE__ */ jsx(Seam, { label: "Passing you to the team" }) : /* @__PURE__ */ jsxs("div", { className: "group relative", children: [
|
|
20322
|
+
msg.sender_type !== "customer" && !isOptimistic(msg) && /* @__PURE__ */ jsx(HoverRail, { message: msg, onReact, onReply }),
|
|
20323
|
+
/* @__PURE__ */ jsx(
|
|
20324
|
+
MessageBubble,
|
|
20325
|
+
{
|
|
20326
|
+
message: msg,
|
|
20327
|
+
reactions: reactionsByTarget.get(msg.id),
|
|
20328
|
+
businessName,
|
|
20329
|
+
group,
|
|
20330
|
+
delivery,
|
|
20331
|
+
onReply,
|
|
20332
|
+
avatar: /* @__PURE__ */ jsx(LetterAvatar, { name: businessName }),
|
|
20333
|
+
renderProgram: (source) => /* @__PURE__ */ jsx(ChatKitProgram, { source, onAction: onGenUIAction }),
|
|
20334
|
+
renderCards: (target) => /* @__PURE__ */ jsx(ChatReplyCards, { message: target, currency, onAction: onGenUIAction }),
|
|
20335
|
+
onQuoteMiss: async () => {
|
|
20336
|
+
onLoadOlder();
|
|
20337
|
+
return true;
|
|
20338
|
+
}
|
|
20339
|
+
}
|
|
20340
|
+
)
|
|
20341
|
+
] })
|
|
20342
|
+
]
|
|
20343
|
+
},
|
|
20344
|
+
msg.id
|
|
20345
|
+
);
|
|
20346
|
+
}),
|
|
20347
|
+
isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
|
|
20348
|
+
offerHuman && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
|
|
20349
|
+
"button",
|
|
20350
|
+
{
|
|
20351
|
+
type: "button",
|
|
20352
|
+
onClick: onRequestHuman,
|
|
20353
|
+
className: "rounded-full border border-border bg-background px-3 py-1.5 text-xs font-semibold text-foreground hover:border-foreground",
|
|
20354
|
+
children: "Talk to a person"
|
|
20355
|
+
}
|
|
20356
|
+
) }),
|
|
20357
|
+
/* @__PURE__ */ jsx(
|
|
20358
|
+
"div",
|
|
20359
|
+
{
|
|
20360
|
+
ref: (node) => {
|
|
20361
|
+
if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
|
|
20362
|
+
}
|
|
20363
|
+
},
|
|
20364
|
+
`${messages.length}:${isSending}`
|
|
20365
|
+
)
|
|
20366
|
+
]
|
|
20367
|
+
}
|
|
20368
|
+
);
|
|
20369
|
+
}
|
|
20370
|
+
function HoverRail({
|
|
20371
|
+
message,
|
|
20372
|
+
onReact,
|
|
20373
|
+
onReply
|
|
20374
|
+
}) {
|
|
20375
|
+
return /* @__PURE__ */ jsxs("div", { className: "absolute -top-3 right-0 z-10 hidden gap-0.5 rounded-full border border-border bg-background px-1 py-0.5 shadow-sm group-hover:flex", children: [
|
|
20376
|
+
QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
20377
|
+
"button",
|
|
20378
|
+
{
|
|
20379
|
+
type: "button",
|
|
20380
|
+
"aria-label": `React ${emoji}`,
|
|
20381
|
+
onClick: () => void onReact(message, emoji),
|
|
20382
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
20383
|
+
children: emoji
|
|
20384
|
+
},
|
|
20385
|
+
emoji
|
|
20386
|
+
)),
|
|
20387
|
+
/* @__PURE__ */ jsx(
|
|
20388
|
+
"button",
|
|
20389
|
+
{
|
|
20390
|
+
type: "button",
|
|
20391
|
+
"aria-label": "Reply",
|
|
20392
|
+
onClick: () => onReply(message),
|
|
20393
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
20394
|
+
children: /* @__PURE__ */ jsx(ReplyIcon, { className: "h-3.5 w-3.5" })
|
|
20395
|
+
}
|
|
20396
|
+
)
|
|
20397
|
+
] });
|
|
20398
|
+
}
|
|
20399
|
+
function readKeepDismissed() {
|
|
20400
|
+
try {
|
|
20401
|
+
return typeof window !== "undefined" && window.localStorage.getItem(KEEP_DISMISSED_KEY) === "1";
|
|
20402
|
+
} catch {
|
|
20403
|
+
return false;
|
|
20190
20404
|
}
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
|
|
20194
|
-
|
|
20405
|
+
}
|
|
20406
|
+
function writeKeepDismissed() {
|
|
20407
|
+
try {
|
|
20408
|
+
window.localStorage.setItem(KEEP_DISMISSED_KEY, "1");
|
|
20409
|
+
} catch {
|
|
20195
20410
|
}
|
|
20196
|
-
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
|
|
20204
|
-
|
|
20205
|
-
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
|
|
20411
|
+
}
|
|
20412
|
+
function KeepConversation({ client }) {
|
|
20413
|
+
const [step, setStep] = useState("row");
|
|
20414
|
+
const [dismissed, setDismissed] = useState(readKeepDismissed);
|
|
20415
|
+
const [contact, setContact] = useState("");
|
|
20416
|
+
const [code, setCode] = useState("");
|
|
20417
|
+
const [busy, setBusy] = useState(false);
|
|
20418
|
+
const [verifyError, setVerifyError] = useState(null);
|
|
20419
|
+
if (dismissed) return null;
|
|
20420
|
+
const contactType = contact.includes("@") ? "email" : "phone";
|
|
20421
|
+
const requestCode = async () => {
|
|
20422
|
+
const trimmed = contact.trim();
|
|
20423
|
+
if (!trimmed || busy) return;
|
|
20424
|
+
setBusy(true);
|
|
20425
|
+
setVerifyError(null);
|
|
20426
|
+
const result = await client.link.requestOtp({ contact: trimmed, contact_type: contactType });
|
|
20427
|
+
setBusy(false);
|
|
20428
|
+
if (result.ok) {
|
|
20429
|
+
setCode("");
|
|
20430
|
+
setStep("code");
|
|
20431
|
+
} else {
|
|
20432
|
+
setVerifyError(result.error.userMessage || "Couldn't send a code \u2014 try again.");
|
|
20209
20433
|
}
|
|
20210
|
-
|
|
20211
|
-
|
|
20434
|
+
};
|
|
20435
|
+
const verify = async (value) => {
|
|
20436
|
+
if (value.length !== 6 || busy) return;
|
|
20437
|
+
setBusy(true);
|
|
20438
|
+
setVerifyError(null);
|
|
20439
|
+
const result = await client.link.verifyOtp({
|
|
20440
|
+
contact: contact.trim(),
|
|
20441
|
+
contact_type: contactType,
|
|
20442
|
+
otp_code: value
|
|
20443
|
+
});
|
|
20444
|
+
setBusy(false);
|
|
20445
|
+
if (!result.ok) {
|
|
20446
|
+
setCode("");
|
|
20447
|
+
setVerifyError(result.error.userMessage || "Invalid or expired code.");
|
|
20212
20448
|
}
|
|
20213
|
-
if (typeof value === "object" && isRuntimeExpr(value)) return null;
|
|
20214
|
-
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
20215
|
-
return null;
|
|
20216
20449
|
};
|
|
20217
|
-
return
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
})
|
|
20227
|
-
|
|
20228
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20450
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-border bg-muted/50 px-4 py-2.5", children: [
|
|
20451
|
+
step === "row" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
20452
|
+
/* @__PURE__ */ jsxs(
|
|
20453
|
+
"button",
|
|
20454
|
+
{
|
|
20455
|
+
type: "button",
|
|
20456
|
+
onClick: () => setStep("contact"),
|
|
20457
|
+
className: "flex min-w-0 flex-1 flex-col text-left",
|
|
20458
|
+
children: [
|
|
20459
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-foreground", children: "Keep this conversation" }),
|
|
20460
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: "Verify your number and it follows you \u2014 any device" })
|
|
20461
|
+
]
|
|
20462
|
+
}
|
|
20463
|
+
),
|
|
20464
|
+
/* @__PURE__ */ jsx(
|
|
20465
|
+
"button",
|
|
20466
|
+
{
|
|
20467
|
+
type: "button",
|
|
20468
|
+
"aria-label": "Dismiss",
|
|
20469
|
+
onClick: () => {
|
|
20470
|
+
writeKeepDismissed();
|
|
20471
|
+
setDismissed(true);
|
|
20472
|
+
},
|
|
20473
|
+
className: "flex-shrink-0 p-1 text-muted-foreground hover:text-foreground",
|
|
20474
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "h-3.5 w-3.5" })
|
|
20475
|
+
}
|
|
20476
|
+
)
|
|
20477
|
+
] }),
|
|
20478
|
+
step === "contact" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
20479
|
+
/* @__PURE__ */ jsx(
|
|
20480
|
+
"input",
|
|
20481
|
+
{
|
|
20482
|
+
type: "text",
|
|
20483
|
+
inputMode: "email",
|
|
20484
|
+
value: contact,
|
|
20485
|
+
onChange: (e) => setContact(e.target.value),
|
|
20486
|
+
onKeyDown: (e) => e.key === "Enter" && void requestCode(),
|
|
20487
|
+
placeholder: "Phone or email",
|
|
20488
|
+
autoFocus: true,
|
|
20489
|
+
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:border-foreground"
|
|
20490
|
+
}
|
|
20491
|
+
),
|
|
20492
|
+
/* @__PURE__ */ jsx(
|
|
20493
|
+
"button",
|
|
20494
|
+
{
|
|
20495
|
+
type: "button",
|
|
20496
|
+
onClick: () => void requestCode(),
|
|
20497
|
+
disabled: !contact.trim() || busy,
|
|
20498
|
+
className: "h-9 flex-shrink-0 rounded-lg bg-foreground px-3 text-xs font-semibold text-background disabled:opacity-50",
|
|
20499
|
+
children: busy ? "Sending\u2026" : "Send code"
|
|
20500
|
+
}
|
|
20501
|
+
)
|
|
20502
|
+
] }),
|
|
20503
|
+
step === "code" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
20504
|
+
/* @__PURE__ */ jsx(
|
|
20505
|
+
"input",
|
|
20506
|
+
{
|
|
20507
|
+
type: "text",
|
|
20508
|
+
inputMode: "numeric",
|
|
20509
|
+
autoComplete: "one-time-code",
|
|
20510
|
+
maxLength: 6,
|
|
20511
|
+
value: code,
|
|
20512
|
+
onChange: (e) => {
|
|
20513
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 6);
|
|
20514
|
+
setCode(digits);
|
|
20515
|
+
if (digits.length === 6) void verify(digits);
|
|
20516
|
+
},
|
|
20517
|
+
placeholder: "6-digit code",
|
|
20518
|
+
autoFocus: true,
|
|
20519
|
+
className: "h-9 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-xs tracking-[0.2em] text-foreground outline-none placeholder:tracking-normal placeholder:text-muted-foreground focus:border-foreground"
|
|
20520
|
+
}
|
|
20521
|
+
),
|
|
20522
|
+
/* @__PURE__ */ jsx(
|
|
20523
|
+
"button",
|
|
20524
|
+
{
|
|
20525
|
+
type: "button",
|
|
20526
|
+
onClick: () => void requestCode(),
|
|
20527
|
+
disabled: busy,
|
|
20528
|
+
className: "h-9 flex-shrink-0 rounded-lg px-2 text-[11px] font-medium text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
20529
|
+
children: "Resend"
|
|
20530
|
+
}
|
|
20531
|
+
)
|
|
20532
|
+
] }),
|
|
20533
|
+
verifyError && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-[11px] text-destructive", children: verifyError })
|
|
20534
|
+
] });
|
|
20238
20535
|
}
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
20242
|
-
|
|
20243
|
-
|
|
20244
|
-
|
|
20245
|
-
|
|
20246
|
-
|
|
20247
|
-
|
|
20248
|
-
|
|
20249
|
-
|
|
20250
|
-
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20257
|
-
|
|
20258
|
-
},
|
|
20259
|
-
{
|
|
20260
|
-
name: "Stat",
|
|
20261
|
-
description: "Compact stat: small label over a bold value, optional sub line and tone",
|
|
20262
|
-
params: [
|
|
20263
|
-
{ name: "label", required: true },
|
|
20264
|
-
{ name: "value", required: true },
|
|
20265
|
-
{ name: "sub" },
|
|
20266
|
-
{ name: "tone" }
|
|
20267
|
-
]
|
|
20268
|
-
},
|
|
20269
|
-
{
|
|
20270
|
-
name: "Metric",
|
|
20271
|
-
description: "KPI card with label, value, optional percent change and SF Symbol icon",
|
|
20272
|
-
params: [
|
|
20273
|
-
{ name: "label", required: true },
|
|
20274
|
-
{ name: "value", required: true },
|
|
20275
|
-
{ name: "change" },
|
|
20276
|
-
{ name: "icon" }
|
|
20277
|
-
]
|
|
20278
|
-
},
|
|
20279
|
-
{
|
|
20280
|
-
name: "KeyValue",
|
|
20281
|
-
description: "Label/value row; set mono true for numeric values",
|
|
20282
|
-
params: [
|
|
20283
|
-
{ name: "label", required: true },
|
|
20284
|
-
{ name: "value", required: true },
|
|
20285
|
-
{ name: "mono" }
|
|
20286
|
-
]
|
|
20287
|
-
},
|
|
20288
|
-
{
|
|
20289
|
-
name: "Badge",
|
|
20290
|
-
description: "Small status chip. tone: neutral, positive, info, indigo, violet, warning, danger",
|
|
20291
|
-
params: [{ name: "label", required: true }, { name: "tone" }]
|
|
20292
|
-
},
|
|
20293
|
-
{
|
|
20294
|
-
name: "Callout",
|
|
20295
|
-
description: "Inline alert with title, optional message. tone: info (default), success, warning, danger, accent, neutral",
|
|
20296
|
-
params: [{ name: "title", required: true }, { name: "message" }, { name: "tone" }]
|
|
20297
|
-
},
|
|
20298
|
-
{
|
|
20299
|
-
name: "Button",
|
|
20300
|
-
description: 'Action button. action: "action:name" string or Action([...]) steps. variant: primary, cta, secondary (default), ghost, destructive',
|
|
20301
|
-
params: [
|
|
20302
|
-
{ name: "label", required: true },
|
|
20303
|
-
{ name: "action", required: true },
|
|
20304
|
-
{ name: "variant" }
|
|
20305
|
-
]
|
|
20306
|
-
},
|
|
20307
|
-
{
|
|
20308
|
-
name: "Buttons",
|
|
20309
|
-
description: 'Row (default) or column of Buttons; direction: "row" or "column"',
|
|
20310
|
-
params: [{ name: "children", required: true }, { name: "direction" }]
|
|
20311
|
-
},
|
|
20312
|
-
{
|
|
20313
|
-
name: "Divider",
|
|
20314
|
-
description: "Hairline separator",
|
|
20315
|
-
params: []
|
|
20316
|
-
},
|
|
20317
|
-
{
|
|
20318
|
-
name: "Table",
|
|
20319
|
-
description: "Data table: headers is an array of strings, rows an array of cell arrays",
|
|
20320
|
-
params: [
|
|
20321
|
-
{ name: "headers", required: true },
|
|
20322
|
-
{ name: "rows", required: true }
|
|
20323
|
-
]
|
|
20324
|
-
},
|
|
20325
|
-
{
|
|
20326
|
-
name: "BarChart",
|
|
20327
|
-
description: "Bar chart: labels (x axis strings) and values (numbers), optional title",
|
|
20328
|
-
params: [
|
|
20329
|
-
{ name: "labels", required: true },
|
|
20330
|
-
{ name: "values", required: true },
|
|
20331
|
-
{ name: "title" }
|
|
20332
|
-
]
|
|
20333
|
-
},
|
|
20334
|
-
{
|
|
20335
|
-
name: "LineChart",
|
|
20336
|
-
description: "Line chart with area fill: labels and values, optional title",
|
|
20337
|
-
params: [
|
|
20338
|
-
{ name: "labels", required: true },
|
|
20339
|
-
{ name: "values", required: true },
|
|
20340
|
-
{ name: "title" }
|
|
20341
|
-
]
|
|
20342
|
-
},
|
|
20343
|
-
{
|
|
20344
|
-
name: "Money",
|
|
20345
|
-
description: "Formatted money amount, never abbreviated. currency: ISO code (default USD)",
|
|
20346
|
-
params: [{ name: "amount", required: true }, { name: "currency" }]
|
|
20536
|
+
function ChatInput({
|
|
20537
|
+
placeholder,
|
|
20538
|
+
isSending,
|
|
20539
|
+
onSend,
|
|
20540
|
+
onAttach,
|
|
20541
|
+
className
|
|
20542
|
+
}) {
|
|
20543
|
+
const [value, setValue] = useState("");
|
|
20544
|
+
const [uploading, setUploading] = useState(false);
|
|
20545
|
+
const textareaRef = useRef(null);
|
|
20546
|
+
const fileRef = useRef(null);
|
|
20547
|
+
const handleAttach = useCallback(
|
|
20548
|
+
async (file) => {
|
|
20549
|
+
setUploading(true);
|
|
20550
|
+
try {
|
|
20551
|
+
await onAttach(file);
|
|
20552
|
+
} finally {
|
|
20553
|
+
setUploading(false);
|
|
20554
|
+
}
|
|
20347
20555
|
},
|
|
20348
|
-
|
|
20349
|
-
|
|
20350
|
-
|
|
20351
|
-
|
|
20556
|
+
[onAttach]
|
|
20557
|
+
);
|
|
20558
|
+
const handleSend = useCallback(async () => {
|
|
20559
|
+
const trimmed = value.trim();
|
|
20560
|
+
if (!trimmed || isSending) return;
|
|
20561
|
+
setValue("");
|
|
20562
|
+
if (textareaRef.current) {
|
|
20563
|
+
textareaRef.current.style.height = "auto";
|
|
20564
|
+
}
|
|
20565
|
+
const sent = await onSend(trimmed);
|
|
20566
|
+
if (!sent) setValue((current) => current || trimmed);
|
|
20567
|
+
}, [value, isSending, onSend]);
|
|
20568
|
+
const handleKeyDown = useCallback(
|
|
20569
|
+
(e) => {
|
|
20570
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
20571
|
+
e.preventDefault();
|
|
20572
|
+
handleSend();
|
|
20573
|
+
}
|
|
20352
20574
|
},
|
|
20575
|
+
[handleSend]
|
|
20576
|
+
);
|
|
20577
|
+
const handleInput = useCallback(() => {
|
|
20578
|
+
const el = textareaRef.current;
|
|
20579
|
+
if (!el) return;
|
|
20580
|
+
el.style.height = "auto";
|
|
20581
|
+
el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
|
|
20582
|
+
}, []);
|
|
20583
|
+
const hasContent = value.trim().length > 0;
|
|
20584
|
+
return /* @__PURE__ */ jsxs(
|
|
20585
|
+
"div",
|
|
20353
20586
|
{
|
|
20354
|
-
|
|
20355
|
-
|
|
20356
|
-
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
|
|
20360
|
-
|
|
20361
|
-
|
|
20362
|
-
|
|
20587
|
+
className: cn(
|
|
20588
|
+
"flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
|
|
20589
|
+
className
|
|
20590
|
+
),
|
|
20591
|
+
children: [
|
|
20592
|
+
/* @__PURE__ */ jsx(
|
|
20593
|
+
"input",
|
|
20594
|
+
{
|
|
20595
|
+
ref: fileRef,
|
|
20596
|
+
type: "file",
|
|
20597
|
+
accept: "image/*",
|
|
20598
|
+
className: "hidden",
|
|
20599
|
+
"aria-hidden": "true",
|
|
20600
|
+
tabIndex: -1,
|
|
20601
|
+
onChange: (e) => {
|
|
20602
|
+
const file = e.target.files?.[0];
|
|
20603
|
+
e.target.value = "";
|
|
20604
|
+
if (file) void handleAttach(file);
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
),
|
|
20608
|
+
/* @__PURE__ */ jsx(
|
|
20609
|
+
"button",
|
|
20610
|
+
{
|
|
20611
|
+
type: "button",
|
|
20612
|
+
"aria-label": "Attach a photo",
|
|
20613
|
+
disabled: uploading,
|
|
20614
|
+
onClick: () => fileRef.current?.click(),
|
|
20615
|
+
className: cn(
|
|
20616
|
+
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
20617
|
+
"bg-muted text-muted-foreground transition-colors hover:text-foreground",
|
|
20618
|
+
uploading && "animate-pulse"
|
|
20619
|
+
),
|
|
20620
|
+
children: /* @__PURE__ */ jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
|
|
20621
|
+
}
|
|
20622
|
+
),
|
|
20623
|
+
/* @__PURE__ */ jsx(
|
|
20624
|
+
"div",
|
|
20625
|
+
{
|
|
20626
|
+
className: cn(
|
|
20627
|
+
"flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
|
|
20628
|
+
"transition-colors focus-within:border-foreground focus-within:bg-background"
|
|
20629
|
+
),
|
|
20630
|
+
children: /* @__PURE__ */ jsx(
|
|
20631
|
+
"textarea",
|
|
20632
|
+
{
|
|
20633
|
+
ref: textareaRef,
|
|
20634
|
+
value,
|
|
20635
|
+
onChange: (e) => setValue(e.target.value),
|
|
20636
|
+
onKeyDown: handleKeyDown,
|
|
20637
|
+
onInput: handleInput,
|
|
20638
|
+
placeholder,
|
|
20639
|
+
rows: 1,
|
|
20640
|
+
className: cn(
|
|
20641
|
+
"max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
|
|
20642
|
+
"text-sm text-foreground outline-none placeholder:text-muted-foreground"
|
|
20643
|
+
)
|
|
20644
|
+
}
|
|
20645
|
+
)
|
|
20646
|
+
}
|
|
20647
|
+
),
|
|
20648
|
+
/* @__PURE__ */ jsx(
|
|
20649
|
+
"button",
|
|
20650
|
+
{
|
|
20651
|
+
type: "button",
|
|
20652
|
+
onClick: handleSend,
|
|
20653
|
+
disabled: !hasContent || isSending,
|
|
20654
|
+
"aria-label": "Send message",
|
|
20655
|
+
className: cn(
|
|
20656
|
+
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
20657
|
+
"transition-all duration-200",
|
|
20658
|
+
hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
|
|
20659
|
+
),
|
|
20660
|
+
children: /* @__PURE__ */ jsx(SendIcon, { className: "h-[18px] w-[18px]" })
|
|
20661
|
+
}
|
|
20662
|
+
)
|
|
20363
20663
|
]
|
|
20364
|
-
}
|
|
20664
|
+
}
|
|
20665
|
+
);
|
|
20666
|
+
}
|
|
20667
|
+
function TypingDots() {
|
|
20668
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
|
|
20669
|
+
"span",
|
|
20365
20670
|
{
|
|
20366
|
-
|
|
20367
|
-
|
|
20368
|
-
params: [{ name: "events", required: true }]
|
|
20671
|
+
className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
|
|
20672
|
+
style: { animationDelay: `${i * 160}ms` }
|
|
20369
20673
|
},
|
|
20674
|
+
i
|
|
20675
|
+
)) });
|
|
20676
|
+
}
|
|
20677
|
+
function ChatIcon({ className }) {
|
|
20678
|
+
return /* @__PURE__ */ jsx(
|
|
20679
|
+
"svg",
|
|
20370
20680
|
{
|
|
20371
|
-
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
|
|
20681
|
+
className,
|
|
20682
|
+
viewBox: "0 0 24 24",
|
|
20683
|
+
fill: "none",
|
|
20684
|
+
stroke: "currentColor",
|
|
20685
|
+
strokeWidth: 2,
|
|
20686
|
+
strokeLinecap: "round",
|
|
20687
|
+
strokeLinejoin: "round",
|
|
20688
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z" })
|
|
20689
|
+
}
|
|
20690
|
+
);
|
|
20691
|
+
}
|
|
20692
|
+
function CloseIcon({ className }) {
|
|
20693
|
+
return /* @__PURE__ */ jsx(
|
|
20694
|
+
"svg",
|
|
20381
20695
|
{
|
|
20382
|
-
|
|
20383
|
-
|
|
20384
|
-
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
|
|
20390
|
-
}
|
|
20696
|
+
className,
|
|
20697
|
+
viewBox: "0 0 24 24",
|
|
20698
|
+
fill: "none",
|
|
20699
|
+
stroke: "currentColor",
|
|
20700
|
+
strokeWidth: 2,
|
|
20701
|
+
strokeLinecap: "round",
|
|
20702
|
+
strokeLinejoin: "round",
|
|
20703
|
+
children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
|
|
20704
|
+
}
|
|
20705
|
+
);
|
|
20706
|
+
}
|
|
20707
|
+
function SendIcon({ className }) {
|
|
20708
|
+
return /* @__PURE__ */ jsxs(
|
|
20709
|
+
"svg",
|
|
20391
20710
|
{
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
20396
|
-
|
|
20711
|
+
className,
|
|
20712
|
+
viewBox: "0 0 24 24",
|
|
20713
|
+
fill: "none",
|
|
20714
|
+
stroke: "currentColor",
|
|
20715
|
+
strokeWidth: 2,
|
|
20716
|
+
strokeLinecap: "round",
|
|
20717
|
+
strokeLinejoin: "round",
|
|
20718
|
+
children: [
|
|
20719
|
+
/* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
20720
|
+
/* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
20397
20721
|
]
|
|
20398
|
-
}
|
|
20722
|
+
}
|
|
20723
|
+
);
|
|
20724
|
+
}
|
|
20725
|
+
function ImageIcon({ className }) {
|
|
20726
|
+
return /* @__PURE__ */ jsxs(
|
|
20727
|
+
"svg",
|
|
20399
20728
|
{
|
|
20400
|
-
|
|
20401
|
-
|
|
20402
|
-
|
|
20403
|
-
|
|
20404
|
-
|
|
20405
|
-
|
|
20729
|
+
className,
|
|
20730
|
+
viewBox: "0 0 24 24",
|
|
20731
|
+
fill: "none",
|
|
20732
|
+
stroke: "currentColor",
|
|
20733
|
+
strokeWidth: 2,
|
|
20734
|
+
strokeLinecap: "round",
|
|
20735
|
+
strokeLinejoin: "round",
|
|
20736
|
+
children: [
|
|
20737
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
20738
|
+
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
20739
|
+
/* @__PURE__ */ jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
20406
20740
|
]
|
|
20407
|
-
}
|
|
20408
|
-
|
|
20409
|
-
|
|
20410
|
-
|
|
20411
|
-
|
|
20412
|
-
|
|
20741
|
+
}
|
|
20742
|
+
);
|
|
20743
|
+
}
|
|
20744
|
+
function ReplyIcon({ className }) {
|
|
20745
|
+
return /* @__PURE__ */ jsxs(
|
|
20746
|
+
"svg",
|
|
20413
20747
|
{
|
|
20414
|
-
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20748
|
+
className,
|
|
20749
|
+
viewBox: "0 0 24 24",
|
|
20750
|
+
fill: "none",
|
|
20751
|
+
stroke: "currentColor",
|
|
20752
|
+
strokeWidth: 2,
|
|
20753
|
+
strokeLinecap: "round",
|
|
20754
|
+
strokeLinejoin: "round",
|
|
20755
|
+
children: [
|
|
20756
|
+
/* @__PURE__ */ jsx("polyline", { points: "9 17 4 12 9 7" }),
|
|
20757
|
+
/* @__PURE__ */ jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
|
|
20421
20758
|
]
|
|
20422
|
-
},
|
|
20423
|
-
{
|
|
20424
|
-
name: "EmptyState",
|
|
20425
|
-
description: "Nothing-to-show state with an SF Symbol and optional message",
|
|
20426
|
-
params: [{ name: "title", required: true }, { name: "message" }, { name: "symbol" }]
|
|
20427
20759
|
}
|
|
20428
|
-
|
|
20429
|
-
}
|
|
20760
|
+
);
|
|
20761
|
+
}
|
|
20762
|
+
function useGenUIProgram(text2, catalog) {
|
|
20763
|
+
const parserRef = useRef(
|
|
20764
|
+
null
|
|
20765
|
+
);
|
|
20766
|
+
if (!parserRef.current || parserRef.current.catalog !== catalog) {
|
|
20767
|
+
parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
|
|
20768
|
+
}
|
|
20769
|
+
const lastGoodRef = useRef(null);
|
|
20770
|
+
return useMemo(() => {
|
|
20771
|
+
const result = parserRef.current.parser.set(text2);
|
|
20772
|
+
if (result.root) {
|
|
20773
|
+
lastGoodRef.current = result.root;
|
|
20774
|
+
}
|
|
20775
|
+
return { result, root: result.root ?? lastGoodRef.current };
|
|
20776
|
+
}, [text2, catalog]);
|
|
20777
|
+
}
|
|
20430
20778
|
|
|
20431
20779
|
// src/genui/manifest.ts
|
|
20432
20780
|
function genUIManifest(registry) {
|
|
@@ -20463,4 +20811,4 @@ async function genUIFingerprint(manifest) {
|
|
|
20463
20811
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
20464
20812
|
}
|
|
20465
20813
|
|
|
20466
|
-
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BubbleTail, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MessageBubble, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, QuoteBlock, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SWIPE_COMMIT_PX, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, Seam, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, chatTimeline, clearOrdersCache, cn, createPlacesSessionToken, dayLabel, defaultBoundaryKind, deliveryState, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, groupPosition, isImageAttachment, isOptimistic, isTerminalOrderStatus, optimisticId, parse as parseGenUIProgram, partitionMessages, prefersReducedMotion, prefetchProduct, prefetchProducts, proseOf, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sameDay, sanitizeRichTextHtml, scrollBehavior, stripHtmlToText, swipeCommitted, swipeProgress, swipeTravel, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useSwipeReply, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|
|
20814
|
+
export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressAutocomplete, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BubbleTail, BundleProductCard, BundleProductLayout, BundleSelector, Button, CHAT_ACTION_VIEW_ORDER, CHAT_ACTION_VIEW_PRODUCT, CHAT_GENUI_COMPONENTS, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatKitProgram, ChatMarkdown, ChatReplyCards, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MessageBubble, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, QuoteBlock, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SWIPE_COMMIT_PX, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, Seam, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, chatTimeline, clearOrdersCache, cn, createPlacesSessionToken, dayLabel, defaultBoundaryKind, deliveryState, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, groupPosition, isImageAttachment, isOptimistic, isTerminalOrderStatus, optimisticId, parseChatAction, parse as parseGenUIProgram, partitionMessages, prefersReducedMotion, prefetchProduct, prefetchProducts, proseOf, resolveBoundaryKind, roomToResource, safeAttachmentUrl, sameDay, sanitizeRichTextHtml, scrollBehavior, stripHtmlToText, swipeCommitted, swipeProgress, swipeTravel, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useSwipeReply, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
|