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