@cimplify/sdk 0.98.0 → 0.99.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js CHANGED
@@ -1287,10 +1287,10 @@ function CimplifyProvider({
1287
1287
  }, [resolvedClient, baseCurrency, displayCurrency]);
1288
1288
  const convertPrice = React14.useCallback(
1289
1289
  (amount) => {
1290
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
1291
- if (isNaN(num)) return 0;
1292
- if (!fxRate || displayCurrency === baseCurrency) return num;
1293
- return Math.round(num * fxRate * 100) / 100;
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[tone]), children: [
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(text) {
17141
- const cached = cache.get(text);
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(text), {
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(text, html);
17152
+ cache.set(text2, html);
17153
17153
  return html;
17154
17154
  }
17155
- function SettledMarkdown({ text, className }) {
17156
- const html = React14.useMemo(() => renderMarkdown(text), [text]);
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: text }) })
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 num = toNumber2(n);
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(num * factor) / factor;
17951
+ return Math.round(num2 * factor) / factor;
18889
17952
  }
18890
17953
  },
18891
17954
  Abs: {
@@ -19901,556 +18964,1841 @@ function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat
19901
18964
  );
19902
18965
  const orphaned = [...unreached];
19903
18966
  return {
19904
- root,
19905
- meta: {
19906
- incomplete: wasIncomplete,
19907
- unresolved: unres,
19908
- orphaned,
19909
- statementCount: stmtCount,
19910
- errors
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;
19005
+ }
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++;
19012
+ }
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;
19020
+ }
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;
19029
+ }
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;
19034
+ }
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;
19068
+ }
19069
+ if (c === inStr) inStr = false;
19070
+ continue;
19071
+ }
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();
19081
+ }
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;
19102
+ }
19103
+ const typedStmts = [...stmtMap.values()];
19104
+ return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
19105
+ }
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
+ }
19168
+ }
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
+ );
19186
+ }
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
+ );
19200
+ }
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);
19206
+ }
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
+ );
19218
+ }
19219
+ function reset() {
19220
+ buf = "";
19221
+ cleaned = "";
19222
+ completedEnd = 0;
19223
+ completedStmtMap.clear();
19224
+ firstId = "";
19225
+ }
19226
+ return {
19227
+ push(chunk) {
19228
+ buf += chunk;
19229
+ return currentResult();
19911
19230
  },
19912
- stateDeclarations,
19913
- queryStatements,
19914
- mutationStatements
19231
+ set(fullText) {
19232
+ if (fullText.length < buf.length || !fullText.startsWith(buf)) {
19233
+ reset();
19234
+ }
19235
+ const delta = fullText.slice(buf.length);
19236
+ if (delta) buf += delta;
19237
+ return currentResult();
19238
+ },
19239
+ getResult: currentResult
19915
19240
  };
19916
19241
  }
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++;
19242
+
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" }]
19350
+ },
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" }]
19928
19430
  }
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
+ }))
19444
+ }
19445
+ ])
19446
+ );
19447
+ }
19448
+ var noop = () => {
19449
+ };
19450
+ function childKey(value, index) {
19451
+ if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
19452
+ return `i:${index}`;
19453
+ }
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;
19929
19467
  }
19930
- return i;
19931
- }
19932
- function stripFences(input) {
19933
- const blocks = [];
19934
- let i = 0;
19935
- while (i < input.length) {
19936
- let fenceStart = -1;
19937
- while (i < input.length) {
19938
- const nextI = skipString(input, i);
19939
- if (nextI > i) {
19940
- i = nextI;
19941
- continue;
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;
19468
+ const renderer = components[node.typeName];
19469
+ if (!renderer) {
19470
+ onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
19471
+ return null;
19472
+ }
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
- j++;
19959
- let closePos = -1;
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 (closePos !== -1) {
19975
- blocks.push(input.slice(j, closePos));
19976
- i = closePos + 3;
19977
- } else {
19978
- blocks.push(input.slice(j));
19979
- i = input.length;
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
- if (blocks.length > 0) return blocks.join("\n");
19983
- if (input.startsWith("```")) {
19984
- let j = 3;
19985
- while (j < input.length && input[j] !== "\n") j++;
19986
- const start = j < input.length ? j + 1 : 3;
19987
- const body = input.slice(start);
19988
- const trailingFence = body.lastIndexOf("```");
19989
- if (trailingFence !== -1) {
19990
- return body.slice(0, trailingFence);
19991
- }
19992
- return body;
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 stripComments(input) {
19997
- let inStr = false;
19998
- return input.split("\n").map((line) => {
19999
- for (let i = 0; i < line.length; i++) {
20000
- const c = line[i];
20001
- if (inStr) {
20002
- if (c === "\\" && i + 1 < line.length) {
20003
- i++;
20004
- continue;
20005
- }
20006
- if (c === inStr) inStr = false;
20007
- continue;
20008
- }
20009
- if (c === '"' || c === "'") {
20010
- inStr = c;
20011
- continue;
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
- if (c === "/" && line[i + 1] === "/") {
20014
- return line.substring(0, i).trimEnd();
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
- if (c === "#") {
20017
- return line.substring(0, i).trimEnd();
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
- return line;
20021
- }).join("\n");
20022
- }
20023
- function preprocess(input) {
20024
- return stripComments(stripFences(input.trim())).trim();
20025
- }
20026
- function parse(input, cat, rootName) {
20027
- const trimmed = preprocess(input);
20028
- if (!trimmed) return emptyResult();
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
- function refreshCleaned() {
20060
- const next = preprocess(buf);
20061
- if (!next.startsWith(cleaned.slice(0, completedEnd))) {
20062
- completedEnd = 0;
20063
- completedStmtMap.clear();
20064
- firstId = "";
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
- cleaned = next;
20067
- }
20068
- function scanNewCompleted() {
20069
- let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
20070
- let stmtStart = completedEnd;
20071
- for (let i = completedEnd; i < cleaned.length; i++) {
20072
- const c = cleaned[i];
20073
- if (esc) {
20074
- esc = false;
20075
- continue;
20076
- }
20077
- if (c === "\\" && inStr) {
20078
- esc = true;
20079
- continue;
20080
- }
20081
- if (inStr) {
20082
- if (c === inStr) inStr = false;
20083
- continue;
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: businessNameProp,
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: currencyProp,
19864
+ onGenUIAction,
19865
+ className,
19866
+ classNames
19867
+ }) {
19868
+ const context = useOptionalCimplifyClient();
19869
+ const client = clientProp ?? context?.client;
19870
+ const businessName = businessNameProp ?? context?.business?.name ?? "Support";
19871
+ const currency = currencyProp ?? context?.baseCurrency;
19872
+ const [open, setOpen] = React14.useState(defaultOpen);
19873
+ const [readAt, setReadAt] = React14.useState(null);
19874
+ const [attachError, setAttachError] = React14.useState(null);
19875
+ const [replyTo, setReplyTo] = React14.useState(null);
19876
+ const {
19877
+ messages,
19878
+ isLoading,
19879
+ isSending,
19880
+ awaitingReply,
19881
+ seenAt,
19882
+ error,
19883
+ isActive,
19884
+ humanRequested,
19885
+ claimedAs,
19886
+ signOut,
19887
+ send,
19888
+ react,
19889
+ requestHuman,
19890
+ startConversation,
19891
+ loadOlder,
19892
+ mayHaveOlder,
19893
+ openIfExisting
19894
+ } = useChat({ client, pollInterval });
19895
+ const showTyping = isSending || awaitingReply;
19896
+ const attachPhoto = React14.useCallback(
19897
+ async (file) => {
19898
+ if (!client) return;
19899
+ setAttachError(null);
19900
+ if (!file.type.startsWith("image/")) {
19901
+ setAttachError(new chunkAETVRBKI_js.CimplifyError("invalid_attachment", "Only photos can be attached."));
19902
+ return;
20084
19903
  }
20085
- if (c === '"' || c === "'") {
20086
- inStr = c;
20087
- continue;
19904
+ if (file.size > MAX_ATTACHMENT_BYTES) {
19905
+ setAttachError(new chunkAETVRBKI_js.CimplifyError("attachment_too_large", "Photos must be under 5MB."));
19906
+ return;
20088
19907
  }
20089
- if (c === "(" || c === "[" || c === "{") depth++;
20090
- else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
20091
- else if (c === "?" && depth === 0) ternaryDepth++;
20092
- else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
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;
19908
+ const uploaded = await client.uploads.upload(file);
19909
+ if (!uploaded.ok) {
19910
+ setAttachError(uploaded.error);
19911
+ return;
20104
19912
  }
19913
+ const attachment = {
19914
+ upload_id: uploaded.value.id,
19915
+ url: uploaded.value.url,
19916
+ mime_type: uploaded.value.content_type,
19917
+ filename: uploaded.value.filename,
19918
+ size: uploaded.value.size_bytes
19919
+ };
19920
+ await send("", { attachments: [attachment] });
19921
+ },
19922
+ [client, send]
19923
+ );
19924
+ const handleSend = React14.useCallback(
19925
+ (content) => {
19926
+ setAttachError(null);
19927
+ const target = replyTo;
19928
+ setReplyTo(null);
19929
+ return send(content, target ? { replyTo: target } : void 0);
19930
+ },
19931
+ [send, replyTo]
19932
+ );
19933
+ const unread = open ? 0 : messages.filter(
19934
+ (m) => m.sender_type !== "customer" && (!readAt || m.created_at > readAt)
19935
+ ).length;
19936
+ React14.useState(() => {
19937
+ if (defaultOpen) void openIfExisting();
19938
+ });
19939
+ const handleOpen = React14.useCallback(() => {
19940
+ setOpen(true);
19941
+ void openIfExisting();
19942
+ }, [openIfExisting]);
19943
+ const handleClose = React14.useCallback(() => {
19944
+ const newest = messages[messages.length - 1];
19945
+ setReadAt(newest?.created_at ?? null);
19946
+ setOpen(false);
19947
+ }, [messages]);
19948
+ const isLeft = position === "bottom-left";
19949
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19950
+ "div",
19951
+ {
19952
+ className: cn(
19953
+ "fixed bottom-6 z-[9999]",
19954
+ isLeft ? "left-6" : "right-6",
19955
+ className,
19956
+ classNames?.root
19957
+ ),
19958
+ children: [
19959
+ !open && /* @__PURE__ */ jsxRuntime.jsx(
19960
+ Bubble,
19961
+ {
19962
+ unread,
19963
+ onClick: handleOpen,
19964
+ className: classNames?.bubble
19965
+ }
19966
+ ),
19967
+ open && /* @__PURE__ */ jsxRuntime.jsx(
19968
+ Panel,
19969
+ {
19970
+ client,
19971
+ businessName,
19972
+ greeting,
19973
+ subtitle,
19974
+ starters,
19975
+ placeholder,
19976
+ messages,
19977
+ isLoading,
19978
+ isSending,
19979
+ isTyping: showTyping,
19980
+ seenAt,
19981
+ readAt,
19982
+ error: error ?? attachError,
19983
+ isActive,
19984
+ isLeft,
19985
+ humanRequested,
19986
+ mayHaveOlder,
19987
+ replyTo,
19988
+ claimedAs,
19989
+ onSignOut: () => void signOut(),
19990
+ onClose: handleClose,
19991
+ onSend: handleSend,
19992
+ onReact: react,
19993
+ onReply: setReplyTo,
19994
+ onCancelReply: () => setReplyTo(null),
19995
+ onRequestHuman: () => void requestHuman(),
19996
+ onLoadOlder: () => void loadOlder(),
19997
+ onAttach: attachPhoto,
19998
+ onStartConversation: startConversation,
19999
+ currency,
20000
+ onGenUIAction,
20001
+ classNames
20002
+ }
20003
+ )
20004
+ ]
20105
20005
  }
20106
- return stmtStart;
20107
- }
20108
- function currentResult() {
20109
- refreshCleaned();
20110
- const pendingStart = scanNewCompleted();
20111
- const pendingText = cleaned.slice(pendingStart).trim();
20112
- if (!pendingText) {
20113
- if (completedStmtMap.size === 0) return emptyResult();
20114
- return buildResult(
20115
- completedStmtMap,
20116
- [...completedStmtMap.values()],
20117
- firstId,
20118
- false,
20119
- completedStmtMap.size,
20120
- cat,
20121
- rootName
20122
- );
20123
- }
20124
- const { text: closed, wasIncomplete } = autoClose(pendingText);
20125
- const stmts = split(tokenize(closed));
20126
- if (!stmts.length) {
20127
- if (completedStmtMap.size === 0) return emptyResult(wasIncomplete);
20128
- return buildResult(
20129
- completedStmtMap,
20130
- [...completedStmtMap.values()],
20131
- firstId,
20132
- wasIncomplete,
20133
- completedStmtMap.size,
20134
- cat,
20135
- rootName
20136
- );
20006
+ );
20007
+ }
20008
+ function Bubble({
20009
+ unread,
20010
+ onClick,
20011
+ className
20012
+ }) {
20013
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20014
+ "button",
20015
+ {
20016
+ type: "button",
20017
+ onClick,
20018
+ "aria-label": "Open chat",
20019
+ className: cn(
20020
+ "relative flex h-14 w-14 items-center justify-center rounded-full",
20021
+ "bg-foreground text-background shadow-lg",
20022
+ "transition-transform duration-300 ease-out",
20023
+ "hover:scale-[1.08] active:scale-95",
20024
+ className
20025
+ ),
20026
+ children: [
20027
+ /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-6 w-6" }),
20028
+ unread > 0 && /* @__PURE__ */ jsxRuntime.jsx(
20029
+ "span",
20030
+ {
20031
+ className: cn(
20032
+ "absolute -right-0.5 -top-0.5 flex h-5 w-5 items-center justify-center",
20033
+ "rounded-full bg-destructive text-[10px] font-bold text-white",
20034
+ "ring-2 ring-background"
20035
+ ),
20036
+ children: unread > 9 ? "9+" : unread
20037
+ }
20038
+ )
20039
+ ]
20137
20040
  }
20138
- const allStmtMap = new Map(completedStmtMap);
20139
- for (const s of stmts) {
20140
- const expr = parseExpression(s.tokens);
20141
- const stmt = classifyStatement(s, expr);
20142
- allStmtMap.set(s.id, stmt);
20041
+ );
20042
+ }
20043
+ function Panel({
20044
+ client,
20045
+ businessName,
20046
+ greeting,
20047
+ subtitle,
20048
+ starters,
20049
+ placeholder,
20050
+ messages,
20051
+ isLoading,
20052
+ isSending,
20053
+ isTyping,
20054
+ seenAt,
20055
+ readAt,
20056
+ error,
20057
+ isActive,
20058
+ isLeft,
20059
+ humanRequested,
20060
+ mayHaveOlder,
20061
+ replyTo,
20062
+ claimedAs,
20063
+ onSignOut,
20064
+ onClose,
20065
+ onSend,
20066
+ onReact,
20067
+ onReply,
20068
+ onCancelReply,
20069
+ onRequestHuman,
20070
+ onLoadOlder,
20071
+ onAttach,
20072
+ onStartConversation,
20073
+ currency,
20074
+ onGenUIAction,
20075
+ classNames
20076
+ }) {
20077
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20078
+ "div",
20079
+ {
20080
+ className: cn(
20081
+ "flex flex-col overflow-hidden rounded-2xl bg-background shadow-2xl",
20082
+ "w-[400px] max-w-[calc(100vw-3rem)]",
20083
+ "h-[min(600px,calc(100vh-6rem))]",
20084
+ "animate-in fade-in slide-in-from-bottom-4 duration-300",
20085
+ isLeft ? "origin-bottom-left" : "origin-bottom-right",
20086
+ classNames?.panel
20087
+ ),
20088
+ children: [
20089
+ /* @__PURE__ */ jsxRuntime.jsx(
20090
+ Header,
20091
+ {
20092
+ businessName,
20093
+ claimedAs,
20094
+ onSignOut,
20095
+ onClose,
20096
+ className: classNames?.header
20097
+ }
20098
+ ),
20099
+ !isActive && !isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
20100
+ Welcome,
20101
+ {
20102
+ greeting,
20103
+ subtitle,
20104
+ starters,
20105
+ onStarter: onStartConversation,
20106
+ className: classNames?.welcome
20107
+ }
20108
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
20109
+ MessageList,
20110
+ {
20111
+ messages,
20112
+ businessName,
20113
+ isLoading,
20114
+ isSending: isTyping,
20115
+ seenAt,
20116
+ readAt,
20117
+ humanRequested,
20118
+ mayHaveOlder,
20119
+ onReact,
20120
+ onReply,
20121
+ onRequestHuman,
20122
+ onLoadOlder,
20123
+ currency,
20124
+ onGenUIAction,
20125
+ className: classNames?.messages
20126
+ }
20127
+ ),
20128
+ isActive && !claimedAs && client && /* @__PURE__ */ jsxRuntime.jsx(KeepConversation, { client }),
20129
+ 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: [
20130
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 flex-shrink-0 animate-pulse rounded-full bg-amber-500" }),
20131
+ "Waiting for ",
20132
+ businessName,
20133
+ " \u2014 usually replies in minutes"
20134
+ ] }),
20135
+ 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." }),
20136
+ replyTo && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-t border-border bg-muted/50 px-4 py-2", children: [
20137
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-[3px] shrink-0 self-stretch rounded-full bg-foreground/40" }),
20138
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
20139
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] font-semibold text-foreground", children: [
20140
+ "Replying to ",
20141
+ replyTo.sender_type === "customer" ? "yourself" : businessName
20142
+ ] }),
20143
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[11px] text-muted-foreground", children: proseOf(replyTo.content) || "Photo" })
20144
+ ] }),
20145
+ /* @__PURE__ */ jsxRuntime.jsx(
20146
+ "button",
20147
+ {
20148
+ type: "button",
20149
+ "aria-label": "Cancel reply",
20150
+ onClick: onCancelReply,
20151
+ className: "flex-shrink-0 p-0.5 text-muted-foreground hover:text-foreground",
20152
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-3.5 w-3.5" })
20153
+ }
20154
+ )
20155
+ ] }),
20156
+ /* @__PURE__ */ jsxRuntime.jsx(
20157
+ ChatInput,
20158
+ {
20159
+ placeholder,
20160
+ isSending,
20161
+ onSend: isActive ? onSend : async (text2) => {
20162
+ await onStartConversation(text2);
20163
+ return true;
20164
+ },
20165
+ onAttach,
20166
+ className: classNames?.input
20167
+ }
20168
+ ),
20169
+ /* @__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: [
20170
+ "Powered by",
20171
+ " ",
20172
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Cimplify" })
20173
+ ] })
20174
+ ]
20143
20175
  }
20144
- const allTypedStmts = [...allStmtMap.values()];
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
- };
20176
+ );
20178
20177
  }
20179
- function useGenUIProgram(text, catalog) {
20180
- const parserRef = React14.useRef(
20181
- null
20178
+ function Header({
20179
+ businessName,
20180
+ claimedAs,
20181
+ onSignOut,
20182
+ onClose,
20183
+ className
20184
+ }) {
20185
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20186
+ "div",
20187
+ {
20188
+ className: cn(
20189
+ "flex flex-shrink-0 items-center gap-3 bg-foreground px-5 py-4 text-background",
20190
+ className
20191
+ ),
20192
+ children: [
20193
+ /* @__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" }) }),
20194
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
20195
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
20196
+ claimedAs ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-background/60", children: [
20197
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
20198
+ "Chatting as ",
20199
+ claimedAs
20200
+ ] }),
20201
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }),
20202
+ /* @__PURE__ */ jsxRuntime.jsx(
20203
+ "button",
20204
+ {
20205
+ type: "button",
20206
+ onClick: onSignOut,
20207
+ className: "flex-shrink-0 underline underline-offset-2 hover:text-background",
20208
+ children: "Not you?"
20209
+ }
20210
+ )
20211
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-background/60", children: [
20212
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
20213
+ "Online"
20214
+ ] })
20215
+ ] }),
20216
+ /* @__PURE__ */ jsxRuntime.jsx(
20217
+ "button",
20218
+ {
20219
+ type: "button",
20220
+ onClick: onClose,
20221
+ "aria-label": "Close chat",
20222
+ className: cn(
20223
+ "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",
20224
+ "bg-background/10 text-background/70 transition-colors hover:bg-background/20 hover:text-background"
20225
+ ),
20226
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-[18px] w-[18px]" })
20227
+ }
20228
+ )
20229
+ ]
20230
+ }
20182
20231
  );
20183
- if (!parserRef.current || parserRef.current.catalog !== catalog) {
20184
- parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
20185
- }
20186
- const lastGoodRef = React14.useRef(null);
20187
- return React14.useMemo(() => {
20188
- const result = parserRef.current.parser.set(text);
20189
- if (result.root) {
20190
- lastGoodRef.current = result.root;
20232
+ }
20233
+ function Welcome({
20234
+ greeting,
20235
+ subtitle,
20236
+ starters,
20237
+ onStarter,
20238
+ className
20239
+ }) {
20240
+ const defaultStarters = [
20241
+ { icon: "\u{1F50D}", text: "Help me find a product" },
20242
+ { icon: "\u{1F6D2}", text: "Check my cart" },
20243
+ { icon: "\u{1F4E6}", text: "Track my order" }
20244
+ ];
20245
+ const items = starters ?? defaultStarters;
20246
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20247
+ "div",
20248
+ {
20249
+ className: cn(
20250
+ "flex flex-1 flex-col items-center justify-center overflow-y-auto bg-muted/50 px-8 py-10 text-center",
20251
+ className
20252
+ ),
20253
+ children: [
20254
+ /* @__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" }) }),
20255
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-2 text-xl font-bold text-foreground", children: greeting }),
20256
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 max-w-[280px] text-sm leading-relaxed text-muted-foreground", children: subtitle }),
20257
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full max-w-[300px] flex-col gap-2", children: items.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(
20258
+ "button",
20259
+ {
20260
+ type: "button",
20261
+ onClick: () => onStarter(s.text),
20262
+ className: cn(
20263
+ "flex items-center gap-3 rounded-xl border border-border bg-background px-4 py-3",
20264
+ "text-left text-[13px] font-medium text-foreground",
20265
+ "transition-all hover:-translate-y-px hover:border-foreground hover:shadow-sm"
20266
+ ),
20267
+ children: [
20268
+ 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 }),
20269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: s.text })
20270
+ ]
20271
+ },
20272
+ s.text
20273
+ )) })
20274
+ ]
20191
20275
  }
20192
- return { result, root: result.root ?? lastGoodRef.current };
20193
- }, [text, catalog]);
20276
+ );
20194
20277
  }
20195
- var noop = () => {
20196
- };
20197
- function childKey(value, index) {
20198
- if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
20199
- return `i:${index}`;
20278
+ var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
20279
+ function LetterAvatar({ name }) {
20280
+ 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
20281
  }
20201
- var RenderDepthContext = React14.createContext(0);
20202
- var MAX_DEPTH = 64;
20203
- var GenUINode = React14.memo(function GenUINode2({
20204
- node,
20205
- components,
20206
- onAction,
20207
- isStreaming,
20208
- onTelemetry
20282
+ function MessageList({
20283
+ messages,
20284
+ businessName,
20285
+ isLoading,
20286
+ isSending,
20287
+ seenAt,
20288
+ readAt,
20289
+ humanRequested,
20290
+ mayHaveOlder,
20291
+ onReact,
20292
+ onReply,
20293
+ onRequestHuman,
20294
+ onLoadOlder,
20295
+ currency,
20296
+ onGenUIAction,
20297
+ className
20209
20298
  }) {
20210
- const depth = React14.useContext(RenderDepthContext);
20211
- if (depth > MAX_DEPTH) {
20212
- onTelemetry?.({ kind: "dropped-node", typeName: node.typeName });
20213
- return null;
20299
+ const scrolledUpRef = React14.useRef(false);
20300
+ const timeline = chatTimeline(messages, { readAt, rendersPrograms: true });
20301
+ const { bubbles, reactionsByTarget, firstUnreadId, unreadOnOpen, firstAgentMessageId } = timeline;
20302
+ const lastBubble = bubbles[bubbles.length - 1];
20303
+ const offerHuman = !humanRequested && lastBubble?.sender_type === "bot" && !isSending;
20304
+ const handleScroll = (event) => {
20305
+ const node = event.currentTarget;
20306
+ scrolledUpRef.current = node.scrollHeight - node.scrollTop - node.clientHeight >= AT_BOTTOM_SLACK_PX;
20307
+ };
20308
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20309
+ "div",
20310
+ {
20311
+ onScroll: handleScroll,
20312
+ style: { background: "var(--color-chat-canvas, var(--color-muted, #f3f5f3))" },
20313
+ className: cn("flex flex-1 flex-col gap-0.5 overflow-y-auto px-3.5 py-3.5", className),
20314
+ children: [
20315
+ isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
20316
+ mayHaveOlder && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
20317
+ "button",
20318
+ {
20319
+ type: "button",
20320
+ onClick: onLoadOlder,
20321
+ 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)]",
20322
+ children: "Show earlier messages"
20323
+ }
20324
+ ) }),
20325
+ bubbles.map((msg, i) => {
20326
+ const prev = bubbles[i - 1];
20327
+ const newDay = !prev || !sameDay(prev.created_at, msg.created_at);
20328
+ const group = groupPosition(timeline, i);
20329
+ const startsRun = group === "single" || group === "first";
20330
+ const delivery = deliveryState(msg, timeline, seenAt, isOptimistic);
20331
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20332
+ "div",
20333
+ {
20334
+ className: cn("flex flex-col gap-1.5", startsRun && i > 0 && "mt-2.5"),
20335
+ children: [
20336
+ newDay && /* @__PURE__ */ jsxRuntime.jsx(Seam, { variant: "chip", label: dayLabel(msg.created_at) }),
20337
+ msg.id === firstUnreadId && /* @__PURE__ */ jsxRuntime.jsx(
20338
+ Seam,
20339
+ {
20340
+ variant: "unread",
20341
+ label: `${unreadOnOpen} new message${unreadOnOpen === 1 ? "" : "s"}`
20342
+ }
20343
+ ),
20344
+ msg.id === firstAgentMessageId && /* @__PURE__ */ jsxRuntime.jsx(Seam, { label: "A person joined" }),
20345
+ msg.sender_type === "system" ? /* @__PURE__ */ jsxRuntime.jsx(Seam, { label: "Passing you to the team" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group relative", children: [
20346
+ msg.sender_type !== "customer" && !isOptimistic(msg) && /* @__PURE__ */ jsxRuntime.jsx(HoverRail, { message: msg, onReact, onReply }),
20347
+ /* @__PURE__ */ jsxRuntime.jsx(
20348
+ MessageBubble,
20349
+ {
20350
+ message: msg,
20351
+ reactions: reactionsByTarget.get(msg.id),
20352
+ businessName,
20353
+ group,
20354
+ delivery,
20355
+ onReply,
20356
+ avatar: /* @__PURE__ */ jsxRuntime.jsx(LetterAvatar, { name: businessName }),
20357
+ renderProgram: (source) => /* @__PURE__ */ jsxRuntime.jsx(ChatKitProgram, { source, onAction: onGenUIAction }),
20358
+ renderCards: (target) => /* @__PURE__ */ jsxRuntime.jsx(ChatReplyCards, { message: target, currency, onAction: onGenUIAction }),
20359
+ onQuoteMiss: async () => {
20360
+ onLoadOlder();
20361
+ return true;
20362
+ }
20363
+ }
20364
+ )
20365
+ ] })
20366
+ ]
20367
+ },
20368
+ msg.id
20369
+ );
20370
+ }),
20371
+ isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
20372
+ offerHuman && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
20373
+ "button",
20374
+ {
20375
+ type: "button",
20376
+ onClick: onRequestHuman,
20377
+ className: "rounded-full border border-border bg-background px-3 py-1.5 text-xs font-semibold text-foreground hover:border-foreground",
20378
+ children: "Talk to a person"
20379
+ }
20380
+ ) }),
20381
+ /* @__PURE__ */ jsxRuntime.jsx(
20382
+ "div",
20383
+ {
20384
+ ref: (node) => {
20385
+ if (node && !scrolledUpRef.current) node.scrollIntoView({ behavior: "smooth" });
20386
+ }
20387
+ },
20388
+ `${messages.length}:${isSending}`
20389
+ )
20390
+ ]
20391
+ }
20392
+ );
20393
+ }
20394
+ function HoverRail({
20395
+ message,
20396
+ onReact,
20397
+ onReply
20398
+ }) {
20399
+ 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: [
20400
+ QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsxRuntime.jsx(
20401
+ "button",
20402
+ {
20403
+ type: "button",
20404
+ "aria-label": `React ${emoji}`,
20405
+ onClick: () => void onReact(message, emoji),
20406
+ className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
20407
+ children: emoji
20408
+ },
20409
+ emoji
20410
+ )),
20411
+ /* @__PURE__ */ jsxRuntime.jsx(
20412
+ "button",
20413
+ {
20414
+ type: "button",
20415
+ "aria-label": "Reply",
20416
+ onClick: () => onReply(message),
20417
+ className: "flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground hover:bg-muted hover:text-foreground",
20418
+ children: /* @__PURE__ */ jsxRuntime.jsx(ReplyIcon, { className: "h-3.5 w-3.5" })
20419
+ }
20420
+ )
20421
+ ] });
20422
+ }
20423
+ function readKeepDismissed() {
20424
+ try {
20425
+ return typeof window !== "undefined" && window.localStorage.getItem(KEEP_DISMISSED_KEY) === "1";
20426
+ } catch {
20427
+ return false;
20214
20428
  }
20215
- const renderer = components[node.typeName];
20216
- if (!renderer) {
20217
- onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
20218
- return null;
20429
+ }
20430
+ function writeKeepDismissed() {
20431
+ try {
20432
+ window.localStorage.setItem(KEEP_DISMISSED_KEY, "1");
20433
+ } catch {
20219
20434
  }
20220
- const render = (value) => {
20221
- if (value == null) return null;
20222
- if (isElementNode(value)) {
20223
- return /* @__PURE__ */ jsxRuntime.jsx(RenderDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsxRuntime.jsx(
20224
- GenUINode2,
20225
- {
20226
- node: value,
20227
- components,
20228
- onAction,
20229
- isStreaming,
20230
- onTelemetry
20231
- }
20232
- ) });
20435
+ }
20436
+ function KeepConversation({ client }) {
20437
+ const [step, setStep] = React14.useState("row");
20438
+ const [dismissed, setDismissed] = React14.useState(readKeepDismissed);
20439
+ const [contact, setContact] = React14.useState("");
20440
+ const [code, setCode] = React14.useState("");
20441
+ const [busy, setBusy] = React14.useState(false);
20442
+ const [verifyError, setVerifyError] = React14.useState(null);
20443
+ if (dismissed) return null;
20444
+ const contactType = contact.includes("@") ? "email" : "phone";
20445
+ const requestCode = async () => {
20446
+ const trimmed = contact.trim();
20447
+ if (!trimmed || busy) return;
20448
+ setBusy(true);
20449
+ setVerifyError(null);
20450
+ const result = await client.link.requestOtp({ contact: trimmed, contact_type: contactType });
20451
+ setBusy(false);
20452
+ if (result.ok) {
20453
+ setCode("");
20454
+ setStep("code");
20455
+ } else {
20456
+ setVerifyError(result.error.userMessage || "Couldn't send a code \u2014 try again.");
20233
20457
  }
20234
- if (Array.isArray(value)) {
20235
- return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React14__default.default.Fragment, { children: render(item) }, childKey(item, index)));
20458
+ };
20459
+ const verify = async (value) => {
20460
+ if (value.length !== 6 || busy) return;
20461
+ setBusy(true);
20462
+ setVerifyError(null);
20463
+ const result = await client.link.verifyOtp({
20464
+ contact: contact.trim(),
20465
+ contact_type: contactType,
20466
+ otp_code: value
20467
+ });
20468
+ setBusy(false);
20469
+ if (!result.ok) {
20470
+ setCode("");
20471
+ setVerifyError(result.error.userMessage || "Invalid or expired code.");
20236
20472
  }
20237
- if (typeof value === "object" && isRuntimeExpr(value)) return null;
20238
- if (typeof value === "string" || typeof value === "number") return String(value);
20239
- return null;
20240
20473
  };
20241
- return renderer({ node, ctx: { render, onAction, isStreaming } });
20242
- });
20243
- function GenUIView({
20244
- root,
20245
- components,
20246
- onAction = noop,
20247
- isStreaming = false,
20248
- onTelemetry,
20249
- placeholder = null
20250
- }) {
20251
- if (!root) return placeholder;
20252
- return /* @__PURE__ */ jsxRuntime.jsx(
20253
- GenUINode,
20254
- {
20255
- node: root,
20256
- components,
20257
- onAction,
20258
- isStreaming,
20259
- onTelemetry
20260
- }
20261
- );
20474
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 border-t border-border bg-muted/50 px-4 py-2.5", children: [
20475
+ step === "row" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
20476
+ /* @__PURE__ */ jsxRuntime.jsxs(
20477
+ "button",
20478
+ {
20479
+ type: "button",
20480
+ onClick: () => setStep("contact"),
20481
+ className: "flex min-w-0 flex-1 flex-col text-left",
20482
+ children: [
20483
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-foreground", children: "Keep this conversation" }),
20484
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: "Verify your number and it follows you \u2014 any device" })
20485
+ ]
20486
+ }
20487
+ ),
20488
+ /* @__PURE__ */ jsxRuntime.jsx(
20489
+ "button",
20490
+ {
20491
+ type: "button",
20492
+ "aria-label": "Dismiss",
20493
+ onClick: () => {
20494
+ writeKeepDismissed();
20495
+ setDismissed(true);
20496
+ },
20497
+ className: "flex-shrink-0 p-1 text-muted-foreground hover:text-foreground",
20498
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, { className: "h-3.5 w-3.5" })
20499
+ }
20500
+ )
20501
+ ] }),
20502
+ step === "contact" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
20503
+ /* @__PURE__ */ jsxRuntime.jsx(
20504
+ "input",
20505
+ {
20506
+ type: "text",
20507
+ inputMode: "email",
20508
+ value: contact,
20509
+ onChange: (e) => setContact(e.target.value),
20510
+ onKeyDown: (e) => e.key === "Enter" && void requestCode(),
20511
+ placeholder: "Phone or email",
20512
+ autoFocus: true,
20513
+ 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"
20514
+ }
20515
+ ),
20516
+ /* @__PURE__ */ jsxRuntime.jsx(
20517
+ "button",
20518
+ {
20519
+ type: "button",
20520
+ onClick: () => void requestCode(),
20521
+ disabled: !contact.trim() || busy,
20522
+ className: "h-9 flex-shrink-0 rounded-lg bg-foreground px-3 text-xs font-semibold text-background disabled:opacity-50",
20523
+ children: busy ? "Sending\u2026" : "Send code"
20524
+ }
20525
+ )
20526
+ ] }),
20527
+ step === "code" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
20528
+ /* @__PURE__ */ jsxRuntime.jsx(
20529
+ "input",
20530
+ {
20531
+ type: "text",
20532
+ inputMode: "numeric",
20533
+ autoComplete: "one-time-code",
20534
+ maxLength: 6,
20535
+ value: code,
20536
+ onChange: (e) => {
20537
+ const digits = e.target.value.replace(/\D/g, "").slice(0, 6);
20538
+ setCode(digits);
20539
+ if (digits.length === 6) void verify(digits);
20540
+ },
20541
+ placeholder: "6-digit code",
20542
+ autoFocus: true,
20543
+ 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"
20544
+ }
20545
+ ),
20546
+ /* @__PURE__ */ jsxRuntime.jsx(
20547
+ "button",
20548
+ {
20549
+ type: "button",
20550
+ onClick: () => void requestCode(),
20551
+ disabled: busy,
20552
+ className: "h-9 flex-shrink-0 rounded-lg px-2 text-[11px] font-medium text-muted-foreground hover:text-foreground disabled:opacity-50",
20553
+ children: "Resend"
20554
+ }
20555
+ )
20556
+ ] }),
20557
+ verifyError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-[11px] text-destructive", children: verifyError })
20558
+ ] });
20262
20559
  }
20263
-
20264
- // src/genui/registry.ts
20265
- var CIMPLIFY_GENUI_REGISTRY = {
20266
- version: "3",
20267
- components: [
20268
- {
20269
- name: "Stack",
20270
- description: 'Layout container. direction: "column" (default) or "row"; gap in points (default 12)',
20271
- params: [{ name: "children", required: true }, { name: "direction" }, { name: "gap" }]
20272
- },
20273
- {
20274
- name: "Card",
20275
- description: "Elevated surface grouping related content, with an optional title",
20276
- params: [{ name: "children", required: true }, { name: "title" }]
20277
- },
20278
- {
20279
- name: "TextContent",
20280
- description: 'Text block. size: "display", "heading", "title", "body" (default), "label", "caption"',
20281
- params: [{ name: "text", required: true }, { name: "size" }]
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" }]
20560
+ function ChatInput({
20561
+ placeholder,
20562
+ isSending,
20563
+ onSend,
20564
+ onAttach,
20565
+ className
20566
+ }) {
20567
+ const [value, setValue] = React14.useState("");
20568
+ const [uploading, setUploading] = React14.useState(false);
20569
+ const textareaRef = React14.useRef(null);
20570
+ const fileRef = React14.useRef(null);
20571
+ const handleAttach = React14.useCallback(
20572
+ async (file) => {
20573
+ setUploading(true);
20574
+ try {
20575
+ await onAttach(file);
20576
+ } finally {
20577
+ setUploading(false);
20578
+ }
20371
20579
  },
20372
- {
20373
- name: "Image",
20374
- description: "Remote image by URL with optional caption",
20375
- params: [{ name: "url", required: true }, { name: "caption" }]
20580
+ [onAttach]
20581
+ );
20582
+ const handleSend = React14.useCallback(async () => {
20583
+ const trimmed = value.trim();
20584
+ if (!trimmed || isSending) return;
20585
+ setValue("");
20586
+ if (textareaRef.current) {
20587
+ textareaRef.current.style.height = "auto";
20588
+ }
20589
+ const sent = await onSend(trimmed);
20590
+ if (!sent) setValue((current) => current || trimmed);
20591
+ }, [value, isSending, onSend]);
20592
+ const handleKeyDown = React14.useCallback(
20593
+ (e) => {
20594
+ if (e.key === "Enter" && !e.shiftKey) {
20595
+ e.preventDefault();
20596
+ handleSend();
20597
+ }
20376
20598
  },
20599
+ [handleSend]
20600
+ );
20601
+ const handleInput = React14.useCallback(() => {
20602
+ const el = textareaRef.current;
20603
+ if (!el) return;
20604
+ el.style.height = "auto";
20605
+ el.style.height = `${Math.min(el.scrollHeight, 100)}px`;
20606
+ }, []);
20607
+ const hasContent = value.trim().length > 0;
20608
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20609
+ "div",
20377
20610
  {
20378
- name: "ListRow",
20379
- description: "Entity row for orders/customers/products/invoices: initials avatar (or SF Symbol tile via symbol), title + subtitle, trailing value and badge with tone",
20380
- params: [
20381
- { name: "title", required: true },
20382
- { name: "subtitle" },
20383
- { name: "value" },
20384
- { name: "badge" },
20385
- { name: "tone" },
20386
- { name: "symbol" }
20611
+ className: cn(
20612
+ "flex flex-shrink-0 items-end gap-2 border-t border-border bg-background px-4 py-3",
20613
+ className
20614
+ ),
20615
+ children: [
20616
+ /* @__PURE__ */ jsxRuntime.jsx(
20617
+ "input",
20618
+ {
20619
+ ref: fileRef,
20620
+ type: "file",
20621
+ accept: "image/*",
20622
+ className: "hidden",
20623
+ "aria-hidden": "true",
20624
+ tabIndex: -1,
20625
+ onChange: (e) => {
20626
+ const file = e.target.files?.[0];
20627
+ e.target.value = "";
20628
+ if (file) void handleAttach(file);
20629
+ }
20630
+ }
20631
+ ),
20632
+ /* @__PURE__ */ jsxRuntime.jsx(
20633
+ "button",
20634
+ {
20635
+ type: "button",
20636
+ "aria-label": "Attach a photo",
20637
+ disabled: uploading,
20638
+ onClick: () => fileRef.current?.click(),
20639
+ className: cn(
20640
+ "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
20641
+ "bg-muted text-muted-foreground transition-colors hover:text-foreground",
20642
+ uploading && "animate-pulse"
20643
+ ),
20644
+ children: /* @__PURE__ */ jsxRuntime.jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
20645
+ }
20646
+ ),
20647
+ /* @__PURE__ */ jsxRuntime.jsx(
20648
+ "div",
20649
+ {
20650
+ className: cn(
20651
+ "flex flex-1 items-end rounded-xl border-[1.5px] border-transparent bg-muted px-1",
20652
+ "transition-colors focus-within:border-foreground focus-within:bg-background"
20653
+ ),
20654
+ children: /* @__PURE__ */ jsxRuntime.jsx(
20655
+ "textarea",
20656
+ {
20657
+ ref: textareaRef,
20658
+ value,
20659
+ onChange: (e) => setValue(e.target.value),
20660
+ onKeyDown: handleKeyDown,
20661
+ onInput: handleInput,
20662
+ placeholder,
20663
+ rows: 1,
20664
+ className: cn(
20665
+ "max-h-[100px] min-h-[20px] flex-1 resize-none bg-transparent px-2.5 py-2",
20666
+ "text-sm text-foreground outline-none placeholder:text-muted-foreground"
20667
+ )
20668
+ }
20669
+ )
20670
+ }
20671
+ ),
20672
+ /* @__PURE__ */ jsxRuntime.jsx(
20673
+ "button",
20674
+ {
20675
+ type: "button",
20676
+ onClick: handleSend,
20677
+ disabled: !hasContent || isSending,
20678
+ "aria-label": "Send message",
20679
+ className: cn(
20680
+ "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
20681
+ "transition-all duration-200",
20682
+ hasContent && !isSending ? "bg-foreground text-background hover:scale-105 active:scale-95" : "bg-muted text-muted-foreground"
20683
+ ),
20684
+ children: /* @__PURE__ */ jsxRuntime.jsx(SendIcon, { className: "h-[18px] w-[18px]" })
20685
+ }
20686
+ )
20387
20687
  ]
20388
- },
20688
+ }
20689
+ );
20690
+ }
20691
+ function TypingDots() {
20692
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 px-4 py-3", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
20693
+ "span",
20389
20694
  {
20390
- name: "Timeline",
20391
- description: "Event history (order journey, payment trail): events is an array of {title, detail?, time?, tone?} objects, newest first",
20392
- params: [{ name: "events", required: true }]
20695
+ className: "h-[7px] w-[7px] animate-bounce rounded-full bg-muted-foreground/40",
20696
+ style: { animationDelay: `${i * 160}ms` }
20393
20697
  },
20698
+ i
20699
+ )) });
20700
+ }
20701
+ function ChatIcon({ className }) {
20702
+ return /* @__PURE__ */ jsxRuntime.jsx(
20703
+ "svg",
20394
20704
  {
20395
- name: "Progress",
20396
- description: "Amount toward a bound (goal, stock cover, capacity): numeric value and target (default 100), optional detail caption and tone",
20397
- params: [
20398
- { name: "label", required: true },
20399
- { name: "value", required: true },
20400
- { name: "target" },
20401
- { name: "detail" },
20402
- { name: "tone" }
20403
- ]
20404
- },
20705
+ className,
20706
+ viewBox: "0 0 24 24",
20707
+ fill: "none",
20708
+ stroke: "currentColor",
20709
+ strokeWidth: 2,
20710
+ strokeLinecap: "round",
20711
+ strokeLinejoin: "round",
20712
+ 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" })
20713
+ }
20714
+ );
20715
+ }
20716
+ function CloseIcon({ className }) {
20717
+ return /* @__PURE__ */ jsxRuntime.jsx(
20718
+ "svg",
20405
20719
  {
20406
- name: "DonutChart",
20407
- description: "Share of a whole (sales by channel, revenue by category): labels + values, optional title and center display string (e.g. the formatted total)",
20408
- params: [
20409
- { name: "labels", required: true },
20410
- { name: "values", required: true },
20411
- { name: "title" },
20412
- { name: "center" }
20413
- ]
20414
- },
20720
+ className,
20721
+ viewBox: "0 0 24 24",
20722
+ fill: "none",
20723
+ stroke: "currentColor",
20724
+ strokeWidth: 2,
20725
+ strokeLinecap: "round",
20726
+ strokeLinejoin: "round",
20727
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" })
20728
+ }
20729
+ );
20730
+ }
20731
+ function SendIcon({ className }) {
20732
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20733
+ "svg",
20415
20734
  {
20416
- name: "StatusTrack",
20417
- description: "Stage tracker (order fulfilment, invoice lifecycle): steps are stage names, current is the active step name or index",
20418
- params: [
20419
- { name: "steps", required: true },
20420
- { name: "current", required: true }
20735
+ className,
20736
+ viewBox: "0 0 24 24",
20737
+ fill: "none",
20738
+ stroke: "currentColor",
20739
+ strokeWidth: 2,
20740
+ strokeLinecap: "round",
20741
+ strokeLinejoin: "round",
20742
+ children: [
20743
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
20744
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
20421
20745
  ]
20422
- },
20746
+ }
20747
+ );
20748
+ }
20749
+ function ImageIcon({ className }) {
20750
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20751
+ "svg",
20423
20752
  {
20424
- name: "LineItems",
20425
- 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",
20426
- params: [
20427
- { name: "items", required: true },
20428
- { name: "total", required: true },
20429
- { name: "adjustments" }
20753
+ className,
20754
+ viewBox: "0 0 24 24",
20755
+ fill: "none",
20756
+ stroke: "currentColor",
20757
+ strokeWidth: 2,
20758
+ strokeLinecap: "round",
20759
+ strokeLinejoin: "round",
20760
+ children: [
20761
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
20762
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
20763
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "21 15 16 10 5 21" })
20430
20764
  ]
20431
- },
20432
- {
20433
- name: "Grid",
20434
- description: "Tile layout for Stat/Metric children, columns default 2",
20435
- params: [{ name: "children", required: true }, { name: "columns" }]
20436
- },
20765
+ }
20766
+ );
20767
+ }
20768
+ function ReplyIcon({ className }) {
20769
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20770
+ "svg",
20437
20771
  {
20438
- name: "Banner",
20439
- description: "Hero-stat moment on a tonal wash: big value, label, optional percent delta. tone: positive (default), warning, danger, info, neutral",
20440
- params: [
20441
- { name: "value", required: true },
20442
- { name: "label", required: true },
20443
- { name: "delta" },
20444
- { name: "tone" }
20772
+ className,
20773
+ viewBox: "0 0 24 24",
20774
+ fill: "none",
20775
+ stroke: "currentColor",
20776
+ strokeWidth: 2,
20777
+ strokeLinecap: "round",
20778
+ strokeLinejoin: "round",
20779
+ children: [
20780
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 17 4 12 9 7" }),
20781
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 18v-2a4 4 0 0 0-4-4H4" })
20445
20782
  ]
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
20783
  }
20452
- ]
20453
- };
20784
+ );
20785
+ }
20786
+ function useGenUIProgram(text2, catalog) {
20787
+ const parserRef = React14.useRef(
20788
+ null
20789
+ );
20790
+ if (!parserRef.current || parserRef.current.catalog !== catalog) {
20791
+ parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
20792
+ }
20793
+ const lastGoodRef = React14.useRef(null);
20794
+ return React14.useMemo(() => {
20795
+ const result = parserRef.current.parser.set(text2);
20796
+ if (result.root) {
20797
+ lastGoodRef.current = result.root;
20798
+ }
20799
+ return { result, root: result.root ?? lastGoodRef.current };
20800
+ }, [text2, catalog]);
20801
+ }
20454
20802
 
20455
20803
  // src/genui/manifest.ts
20456
20804
  function genUIManifest(registry) {
@@ -20535,6 +20883,9 @@ exports.BundleProductCard = BundleProductCard;
20535
20883
  exports.BundleProductLayout = BundleProductLayout;
20536
20884
  exports.BundleSelector = BundleSelector;
20537
20885
  exports.Button = Button;
20886
+ exports.CHAT_ACTION_VIEW_ORDER = CHAT_ACTION_VIEW_ORDER;
20887
+ exports.CHAT_ACTION_VIEW_PRODUCT = CHAT_ACTION_VIEW_PRODUCT;
20888
+ exports.CHAT_GENUI_COMPONENTS = CHAT_GENUI_COMPONENTS;
20538
20889
  exports.CIMPLIFY_GENUI_REGISTRY = CIMPLIFY_GENUI_REGISTRY;
20539
20890
  exports.CardImage = CardImage;
20540
20891
  exports.CardShell = CardShell;
@@ -20549,7 +20900,9 @@ exports.CataloguePage = CataloguePage;
20549
20900
  exports.CatalogueTemplate = CatalogueTemplate;
20550
20901
  exports.CategoryFilter = CategoryFilter;
20551
20902
  exports.CategoryGrid = CategoryGrid;
20903
+ exports.ChatKitProgram = ChatKitProgram;
20552
20904
  exports.ChatMarkdown = ChatMarkdown;
20905
+ exports.ChatReplyCards = ChatReplyCards;
20553
20906
  exports.ChatWidget = ChatWidget;
20554
20907
  exports.CheckIcon = CheckIcon;
20555
20908
  exports.CheckoutPage = CheckoutPage;
@@ -20702,6 +21055,7 @@ exports.isImageAttachment = isImageAttachment;
20702
21055
  exports.isOptimistic = isOptimistic;
20703
21056
  exports.isTerminalOrderStatus = isTerminalOrderStatus;
20704
21057
  exports.optimisticId = optimisticId;
21058
+ exports.parseChatAction = parseChatAction;
20705
21059
  exports.parseGenUIProgram = parse;
20706
21060
  exports.partitionMessages = partitionMessages;
20707
21061
  exports.prefersReducedMotion = prefersReducedMotion;