@cimplify/sdk 0.88.0 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/{chunk-A7NODD4Q.mjs → chunk-2UDEWILL.mjs} +83 -17
  4. package/dist/{chunk-W5BVGAO6.js → chunk-BN4V4ONE.js} +83 -17
  5. package/dist/{chunk-6JA4HBBD.js → chunk-JVWFIFQ7.js} +4 -4
  6. package/dist/{chunk-ROBTTS3Q.js → chunk-KJP4U6L3.js} +28 -1
  7. package/dist/{chunk-VU5LFNEX.mjs → chunk-NGE37REW.mjs} +2 -2
  8. package/dist/{chunk-APBLFZ2I.mjs → chunk-NNGKC3MM.mjs} +28 -1
  9. package/dist/chunk-U4MW6PYU.mjs +23 -0
  10. package/dist/chunk-XREIZ56K.js +26 -0
  11. package/dist/{client-D0PrO1-Q.d.ts → client-CR1f94wY.d.ts} +97 -7
  12. package/dist/{client-B3FBDfTg.d.ts → client-CqpOcbin.d.ts} +2 -2
  13. package/dist/{client-B-8ceVyt.d.mts → client-DMttx1xk.d.mts} +2 -2
  14. package/dist/{client-r5VSGFRU.d.mts → client-YiSelM7x.d.mts} +97 -7
  15. package/dist/{index-C-Pxrzna.d.ts → index-CxTVu0HZ.d.ts} +3 -3
  16. package/dist/{index-DLulxmF1.d.mts → index-DD9z396M.d.mts} +3 -3
  17. package/dist/index.d.mts +6 -6
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +17 -10
  20. package/dist/index.mjs +8 -2
  21. package/dist/mock/cli.mjs +49 -1
  22. package/dist/mock/library.js +49 -1
  23. package/dist/mock/library.mjs +49 -1
  24. package/dist/mock/msw.js +49 -1
  25. package/dist/mock/msw.mjs +49 -1
  26. package/dist/{payment-TnJUoHqL.d.mts → payment-5d8FTA1q.d.mts} +1 -1
  27. package/dist/{payment-Cfoo9MT7.d.ts → payment-BX7ccIKY.d.ts} +1 -1
  28. package/dist/{price-CCDSrIsc.d.ts → price-DtfP_Xfq.d.ts} +1 -1
  29. package/dist/{price-CM0zVSyM.d.mts → price-arl_4Rzn.d.mts} +1 -1
  30. package/dist/{product-DoA36Eqh.d.mts → product-DZX4QuKS.d.mts} +68 -4
  31. package/dist/{product-DoA36Eqh.d.ts → product-DZX4QuKS.d.ts} +68 -4
  32. package/dist/react.d.mts +405 -7
  33. package/dist/react.d.ts +405 -7
  34. package/dist/react.js +2345 -74
  35. package/dist/react.mjs +2323 -72
  36. package/dist/{server-CI1CUFVu.d.mts → server-BceZMxcK.d.mts} +8 -1
  37. package/dist/{server-CHfYeTAa.d.ts → server-DoJGSC7L.d.ts} +8 -1
  38. package/dist/server.d.mts +4 -4
  39. package/dist/server.d.ts +4 -4
  40. package/dist/server.js +2 -2
  41. package/dist/server.mjs +1 -1
  42. package/dist/styles.css +1 -1
  43. package/dist/testing/msw.d.mts +2 -2
  44. package/dist/testing/msw.d.ts +2 -2
  45. package/dist/testing/msw.js +3 -2
  46. package/dist/testing/msw.mjs +2 -1
  47. package/dist/testing/suite.d.mts +6 -6
  48. package/dist/testing/suite.d.ts +6 -6
  49. package/dist/testing/suite.js +23 -22
  50. package/dist/testing/suite.mjs +4 -3
  51. package/dist/testing.d.mts +5 -5
  52. package/dist/testing.d.ts +5 -5
  53. package/dist/testing.js +79 -78
  54. package/dist/testing.mjs +5 -4
  55. package/dist/utils.d.mts +4 -4
  56. package/dist/utils.d.ts +4 -4
  57. package/package.json +4 -1
  58. package/registry/chat-widget.json +1 -1
  59. package/registry/product-customizer.json +1 -1
package/dist/react.mjs CHANGED
@@ -1,12 +1,14 @@
1
1
  "use client";
2
2
  import { DURATION_UNIT, getVariantDisplayName, INPUT_FIELD_TYPE, PRODUCT_TYPE, BOUNDARY_KIND, resolveGallery, RENDER_HINT, DEFAULT_CDN_BASE_URL } from './chunk-OZDDHSIB.mjs';
3
3
  export { getVariantDisplayName } from './chunk-OZDDHSIB.mjs';
4
- import { createCimplifyClient } from './chunk-A7NODD4Q.mjs';
4
+ import { messagePreview } from './chunk-U4MW6PYU.mjs';
5
+ export { messagePreview, splitGenUIContent } from './chunk-U4MW6PYU.mjs';
6
+ import { createCimplifyClient } from './chunk-2UDEWILL.mjs';
5
7
  import { ELEMENT_TYPES, EVENT_TYPES, startSignIn } from './chunk-ZU4ZRYUR.mjs';
6
8
  import { parsePrice, formatMoney, formatPrice, getProductCurrency, getUnitPriceAtQuantity, isOnSale, getDiscountPercentage, getBasePrice, getPriceRange, formatPriceRange } from './chunk-KFLGQGMC.mjs';
7
9
  import { isSupportedCurrency } from './chunk-K5EN2W2Q.mjs';
8
10
  import './chunk-3G6RQLXK.mjs';
9
- import React14, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useId, useSyncExternalStore } from 'react';
11
+ import React14, { createContext, lazy, memo, Suspense, useContext, useMemo, useRef, useState, useCallback, useEffect, useId, useSyncExternalStore } from 'react';
10
12
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
11
13
  import * as ReactDOM from 'react-dom';
12
14
  import { clsx } from 'clsx';
@@ -24,6 +26,8 @@ import { FilterXSS, safeAttrValue as safeAttrValue$1 } from 'xss';
24
26
  import { Dialog } from '@base-ui/react/dialog';
25
27
  import { Field } from '@base-ui/react/field';
26
28
  import { Input } from '@base-ui/react/input';
29
+ import DOMPurify from 'dompurify';
30
+ import { marked } from 'marked';
27
31
 
28
32
  // src/ads/identity.ts
29
33
  var COOKIE_NAME = "_cimplify_uid";
@@ -766,15 +770,15 @@ function deriveView(base, ops) {
766
770
  };
767
771
  }
768
772
  async function syncFromServer(entry) {
769
- if (entry.syncing) return;
770
- entry.syncing = true;
771
- try {
773
+ const run = async () => {
772
774
  const result = await entry.client.cart.get();
773
775
  if (!result.ok) throw result.error;
774
776
  entry.collection.setBase(mapServerCart(result.value, entry.currency));
775
- } finally {
776
- entry.syncing = false;
777
- }
777
+ };
778
+ const next = entry.syncTail.then(run, run);
779
+ entry.syncTail = next.catch(() => {
780
+ });
781
+ return next;
778
782
  }
779
783
  function emitMutationError(entry, operation, opId, idempotencyKey, error) {
780
784
  entry.client.events.emit("mutation:error", {
@@ -814,7 +818,7 @@ function getOrCreateStore(params) {
814
818
  optimisticEnabled,
815
819
  initialized: false,
816
820
  initializePromise: null,
817
- syncing: false,
821
+ syncTail: Promise.resolve(),
818
822
  failedOpRemovers: /* @__PURE__ */ new Set()
819
823
  };
820
824
  cartStores.set(storeKey, entry);
@@ -4279,7 +4283,7 @@ function Price({
4279
4283
  ] });
4280
4284
  }
4281
4285
  function createCachedQuery(config) {
4282
- const cache = /* @__PURE__ */ new Map();
4286
+ const cache2 = /* @__PURE__ */ new Map();
4283
4287
  const inflight = /* @__PURE__ */ new Map();
4284
4288
  const gcMaxAge = config.gcMaxAge ?? 3e5;
4285
4289
  let lastSweep = 0;
@@ -4288,8 +4292,8 @@ function createCachedQuery(config) {
4288
4292
  if (now - lastSweep < 6e4) return;
4289
4293
  lastSweep = now;
4290
4294
  const cutoff = now - gcMaxAge;
4291
- for (const [key, entry] of cache) {
4292
- if (entry.ts < cutoff) cache.delete(key);
4295
+ for (const [key, entry] of cache2) {
4296
+ if (entry.ts < cutoff) cache2.delete(key);
4293
4297
  }
4294
4298
  }
4295
4299
  function useCachedQuery(options = {}) {
@@ -4307,13 +4311,13 @@ function createCachedQuery(config) {
4307
4311
  const optionsRef = useRef(options);
4308
4312
  optionsRef.current = options;
4309
4313
  if (config.locationSensitive && previousLocationRef.current !== locationId) {
4310
- cache.clear();
4314
+ cache2.clear();
4311
4315
  inflight.clear();
4312
4316
  previousLocationRef.current = locationId;
4313
4317
  }
4314
4318
  sweep();
4315
4319
  const cacheKey2 = config.key(client, options);
4316
- const cached = cache.get(cacheKey2);
4320
+ const cached = cache2.get(cacheKey2);
4317
4321
  const [data, setData] = useState(cached?.data ?? null);
4318
4322
  const [isLoading, setIsLoading] = useState(enabled && !cached);
4319
4323
  const [error, setError] = useState(null);
@@ -4326,7 +4330,7 @@ function createCachedQuery(config) {
4326
4330
  const nextRequestId = ++requestIdRef.current;
4327
4331
  setError(null);
4328
4332
  if (!force) {
4329
- const hit = cache.get(cacheKey2);
4333
+ const hit = cache2.get(cacheKey2);
4330
4334
  if (hit) {
4331
4335
  setData(hit.data);
4332
4336
  setIsLoading(false);
@@ -4343,7 +4347,7 @@ function createCachedQuery(config) {
4343
4347
  });
4344
4348
  }
4345
4349
  const value = await promise;
4346
- cache.set(cacheKey2, { data: value, ts: Date.now() });
4350
+ cache2.set(cacheKey2, { data: value, ts: Date.now() });
4347
4351
  if (nextRequestId === requestIdRef.current) {
4348
4352
  setData(value);
4349
4353
  setError(null);
@@ -4364,23 +4368,23 @@ function createCachedQuery(config) {
4364
4368
  void load(false);
4365
4369
  }, [load]);
4366
4370
  const refetch = useCallback(async () => {
4367
- cache.delete(cacheKey2);
4371
+ cache2.delete(cacheKey2);
4368
4372
  await load(true);
4369
4373
  }, [cacheKey2, load]);
4370
4374
  return { data, isLoading, error, refetch };
4371
4375
  }
4372
4376
  useCachedQuery.prefetch = (client, options) => {
4373
4377
  const key = config.key(client, options);
4374
- if (cache.has(key) || inflight.has(key)) return void 0;
4378
+ if (cache2.has(key) || inflight.has(key)) return void 0;
4375
4379
  const promise = config.fetch(client, options);
4376
4380
  inflight.set(key, promise);
4377
4381
  return promise.then((value) => {
4378
- cache.set(key, { data: value, ts: Date.now() });
4382
+ cache2.set(key, { data: value, ts: Date.now() });
4379
4383
  }).catch(() => {
4380
4384
  }).finally(() => inflight.delete(key));
4381
4385
  };
4382
4386
  useCachedQuery.clear = () => {
4383
- cache.clear();
4387
+ cache2.clear();
4384
4388
  inflight.clear();
4385
4389
  };
4386
4390
  return useCachedQuery;
@@ -4508,6 +4512,9 @@ function useCategories(options = {}) {
4508
4512
  return { categories: data ?? [], isLoading, error, refetch };
4509
4513
  }
4510
4514
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "refunded"]);
4515
+ function isTerminalOrderStatus(status) {
4516
+ return TERMINAL_STATUSES.has(status);
4517
+ }
4511
4518
  var orderCache = /* @__PURE__ */ new Map();
4512
4519
  var orderInflight = /* @__PURE__ */ new Map();
4513
4520
  function buildOrderCacheKey(client, orderId) {
@@ -5349,6 +5356,9 @@ function useOrders(options = {}) {
5349
5356
  const { data, isLoading, error, refetch } = ordersQuery(options);
5350
5357
  return { orders: data ?? [], isLoading, error, refetch };
5351
5358
  }
5359
+ function clearOrdersCache() {
5360
+ ordersQuery.clear();
5361
+ }
5352
5362
  function useSubscriptions(options = {}) {
5353
5363
  const { client } = useCimplifyClient();
5354
5364
  const { status, enabled = true } = options;
@@ -5854,6 +5864,7 @@ var POLL_IDLE_INTERVAL_MS = 15e3;
5854
5864
  var IDLE_TIMEOUT_MS = 6e4;
5855
5865
  var WS_MAX_RECONNECT_ATTEMPTS = 10;
5856
5866
  var WS_PATH = "/api/v1/support/conversation/ws";
5867
+ var AWAIT_REPLY_TIMEOUT_MS = 3e4;
5857
5868
  function useChat(options = {}) {
5858
5869
  const context = useOptionalCimplifyClient();
5859
5870
  const client = options.client ?? context?.client;
@@ -5862,6 +5873,10 @@ function useChat(options = {}) {
5862
5873
  const [isSending, setIsSending] = useState(false);
5863
5874
  const [error, setError] = useState(null);
5864
5875
  const [conversationOpened, setConversationOpened] = useState(false);
5876
+ const [humanRequested, setHumanRequested] = useState(false);
5877
+ const [awaitingReply, setAwaitingReply] = useState(false);
5878
+ const [seenAt, setSeenAt] = useState(null);
5879
+ const [mayHaveOlder, setMayHaveOlder] = useState(false);
5865
5880
  const pollRef = useRef(null);
5866
5881
  const lastActivityRef = useRef(Date.now());
5867
5882
  const openedRef = useRef(false);
@@ -5870,21 +5885,45 @@ function useChat(options = {}) {
5870
5885
  const wsRef = useRef(null);
5871
5886
  const wsReconnectAttemptRef = useRef(0);
5872
5887
  const wsReconnectTimerRef = useRef(null);
5888
+ const aiEnabledRef = useRef(false);
5889
+ const humanRequestedRef = useRef(false);
5890
+ const awaitReplyTimerRef = useRef(null);
5873
5891
  messagesRef.current = messages;
5874
5892
  clientRef.current = client;
5893
+ humanRequestedRef.current = humanRequested;
5875
5894
  const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
5876
5895
  const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
5877
- const appendMessages = useCallback((incoming) => {
5878
- setMessages((prev) => {
5879
- const ids = new Set(prev.map((m) => m.id));
5880
- const fresh = incoming.filter((m) => !ids.has(m.id));
5881
- return fresh.length > 0 ? [...prev, ...fresh] : prev;
5882
- });
5896
+ const stopAwaitingReply = useCallback(() => {
5897
+ if (awaitReplyTimerRef.current) {
5898
+ clearTimeout(awaitReplyTimerRef.current);
5899
+ awaitReplyTimerRef.current = null;
5900
+ }
5901
+ setAwaitingReply(false);
5883
5902
  }, []);
5884
- const pollMessages = useCallback(async () => {
5903
+ const startAwaitingReply = useCallback(() => {
5904
+ setAwaitingReply(true);
5905
+ if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
5906
+ awaitReplyTimerRef.current = setTimeout(() => {
5907
+ awaitReplyTimerRef.current = null;
5908
+ setAwaitingReply(false);
5909
+ }, AWAIT_REPLY_TIMEOUT_MS);
5910
+ }, []);
5911
+ const appendMessages = useCallback(
5912
+ (incoming) => {
5913
+ if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
5914
+ stopAwaitingReply();
5915
+ }
5916
+ setMessages((prev) => {
5917
+ const ids = new Set(prev.map((m) => m.id));
5918
+ const fresh = incoming.filter((m) => !ids.has(m.id));
5919
+ return fresh.length > 0 ? [...prev, ...fresh] : prev;
5920
+ });
5921
+ },
5922
+ [stopAwaitingReply]
5923
+ );
5924
+ const fetchNewMessages = useCallback(async () => {
5885
5925
  const c = clientRef.current;
5886
5926
  if (!c) return;
5887
- if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
5888
5927
  const last = messagesRef.current[messagesRef.current.length - 1];
5889
5928
  const result = await c.support.getMessages({
5890
5929
  after: last?.created_at,
@@ -5894,6 +5933,10 @@ function useChat(options = {}) {
5894
5933
  appendMessages(result.value);
5895
5934
  }
5896
5935
  }, [appendMessages]);
5936
+ const pollMessages = useCallback(async () => {
5937
+ if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
5938
+ await fetchNewMessages();
5939
+ }, [fetchNewMessages]);
5897
5940
  const startPolling = useCallback(() => {
5898
5941
  if (pollRef.current) clearInterval(pollRef.current);
5899
5942
  const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
@@ -5926,6 +5969,19 @@ function useChat(options = {}) {
5926
5969
  const envelope = JSON.parse(event.data);
5927
5970
  if (envelope.type === "message.created" && envelope.data) {
5928
5971
  appendMessages([envelope.data]);
5972
+ } else if (envelope.type === "thread.read") {
5973
+ const at = envelope.data?.last_read_at;
5974
+ if (at) setSeenAt((prev) => !prev || prev < at ? at : prev);
5975
+ } else if (envelope.type === "thread.responding") {
5976
+ const active = envelope.data?.active;
5977
+ if (active) startAwaitingReply();
5978
+ else stopAwaitingReply();
5979
+ } else if (envelope.type === "resync") {
5980
+ const latest = envelope.latest_message_at;
5981
+ const last = messagesRef.current[messagesRef.current.length - 1];
5982
+ if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
5983
+ fetchNewMessages();
5984
+ }
5929
5985
  }
5930
5986
  } catch {
5931
5987
  }
@@ -5933,7 +5989,7 @@ function useChat(options = {}) {
5933
5989
  ws.onclose = () => {
5934
5990
  wsRef.current = null;
5935
5991
  if (!openedRef.current) return;
5936
- pollMessages();
5992
+ fetchNewMessages();
5937
5993
  if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
5938
5994
  const attempt = wsReconnectAttemptRef.current++;
5939
5995
  const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
@@ -5945,11 +6001,12 @@ function useChat(options = {}) {
5945
6001
  ws.onerror = () => {
5946
6002
  };
5947
6003
  wsRef.current = ws;
5948
- }, [appendMessages, startPolling, stopPolling, pollMessages]);
6004
+ }, [appendMessages, startPolling, stopPolling, fetchNewMessages]);
5949
6005
  useEffect(() => {
5950
6006
  return () => {
5951
6007
  if (pollRef.current) clearInterval(pollRef.current);
5952
6008
  if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
6009
+ if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
5953
6010
  if (wsRef.current) {
5954
6011
  wsRef.current.onclose = null;
5955
6012
  wsRef.current.close();
@@ -5965,6 +6022,10 @@ function useChat(options = {}) {
5965
6022
  if (result.ok) {
5966
6023
  setMessages(result.value.messages);
5967
6024
  setConversationOpened(true);
6025
+ aiEnabledRef.current = result.value.conversation.ai_enabled;
6026
+ setHumanRequested(result.value.conversation.needs_human_review ?? false);
6027
+ setSeenAt(result.value.conversation.team_read_at ?? null);
6028
+ setMayHaveOlder(result.value.messages.length >= 50);
5968
6029
  connectWebSocket();
5969
6030
  } else {
5970
6031
  openedRef.current = false;
@@ -5973,38 +6034,60 @@ function useChat(options = {}) {
5973
6034
  setIsLoading(false);
5974
6035
  }, [client, connectWebSocket]);
5975
6036
  const send = useCallback(
5976
- async (content) => {
5977
- if (!client || !content.trim()) return;
6037
+ async (content, opts) => {
6038
+ const attachments = opts?.attachments ?? [];
6039
+ if (!client || !content.trim() && attachments.length === 0) return false;
5978
6040
  setError(null);
5979
6041
  lastActivityRef.current = Date.now();
5980
6042
  if (!openedRef.current) {
5981
6043
  await openConversation();
5982
- if (!openedRef.current) return;
6044
+ if (!openedRef.current) return false;
5983
6045
  }
6046
+ const replyTo = opts?.replyTo;
5984
6047
  const optimisticId = `opt_${Date.now()}`;
5985
6048
  const optimistic = {
5986
6049
  id: optimisticId,
5987
6050
  sender_type: "customer",
5988
6051
  content: content.trim(),
5989
- content_type: "text",
5990
- attachments: [],
6052
+ content_type: attachments.length > 0 ? "image" : "text",
6053
+ attachments,
5991
6054
  metadata: {},
6055
+ // Same shape the server echoes back, so the bubble doesn't reflow
6056
+ // when the confirmed message replaces it.
6057
+ reply_to: replyTo ? {
6058
+ id: replyTo.id,
6059
+ sender_type: replyTo.sender_type,
6060
+ preview: messagePreview(replyTo.content)
6061
+ } : void 0,
5992
6062
  created_at: (/* @__PURE__ */ new Date()).toISOString()
5993
6063
  };
5994
6064
  setMessages((prev) => [...prev, optimistic]);
5995
6065
  setIsSending(true);
5996
- const result = await client.support.sendMessage(content.trim());
6066
+ const result = await client.support.sendMessage(content.trim(), {
6067
+ replyToMessageId: replyTo?.id,
6068
+ attachments: attachments.length > 0 ? attachments : void 0
6069
+ });
5997
6070
  if (result.ok) {
6071
+ const confirmed = result.value;
5998
6072
  setMessages(
5999
- (prev) => prev.map((m) => m.id === optimisticId ? result.value : m)
6073
+ (prev) => (
6074
+ // The websocket may have already streamed the confirmed message
6075
+ // in while the POST was in flight; drop the optimistic bubble
6076
+ // instead of inserting a duplicate.
6077
+ prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== optimisticId) : prev.map((m) => m.id === optimisticId ? confirmed : m)
6078
+ )
6000
6079
  );
6080
+ if (aiEnabledRef.current && !humanRequestedRef.current) {
6081
+ startAwaitingReply();
6082
+ }
6001
6083
  } else {
6002
6084
  setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
6003
6085
  setError(result.error);
6004
6086
  }
6005
6087
  setIsSending(false);
6088
+ return result.ok;
6006
6089
  },
6007
- [client, openConversation]
6090
+ [client, openConversation, startAwaitingReply]
6008
6091
  );
6009
6092
  const startConversation = useCallback(
6010
6093
  async (starterText) => {
@@ -6015,14 +6098,65 @@ function useChat(options = {}) {
6015
6098
  },
6016
6099
  [openConversation, send]
6017
6100
  );
6101
+ const react = useCallback(
6102
+ async (message, emoji) => {
6103
+ if (!client || message.id.startsWith("opt_")) return false;
6104
+ const result = await client.support.reactToMessage(message.id, emoji);
6105
+ if (result.ok) appendMessages([result.value]);
6106
+ return result.ok;
6107
+ },
6108
+ [client, appendMessages]
6109
+ );
6110
+ const requestHuman = useCallback(async () => {
6111
+ if (!client) return false;
6112
+ setError(null);
6113
+ if (!openedRef.current) {
6114
+ await openConversation();
6115
+ if (!openedRef.current) return false;
6116
+ }
6117
+ const result = await client.support.requestHuman();
6118
+ if (result.ok) {
6119
+ setHumanRequested(true);
6120
+ return true;
6121
+ }
6122
+ setError(result.error);
6123
+ return false;
6124
+ }, [client, openConversation]);
6125
+ const loadOlder = useCallback(async () => {
6126
+ const c = clientRef.current;
6127
+ const earliest = messagesRef.current[0];
6128
+ if (!c || !earliest) return false;
6129
+ const result = await c.support.getMessages({ before: earliest.created_at, limit: 50 });
6130
+ if (!result.ok) {
6131
+ setError(result.error);
6132
+ return false;
6133
+ }
6134
+ const older = result.value;
6135
+ setMayHaveOlder(older.length >= 50);
6136
+ if (older.length === 0) return false;
6137
+ setMessages((prev) => {
6138
+ const ids = new Set(prev.map((m) => m.id));
6139
+ const fresh = older.filter((m) => !ids.has(m.id));
6140
+ return fresh.length > 0 ? [...fresh, ...prev] : prev;
6141
+ });
6142
+ return true;
6143
+ }, []);
6144
+ const agentArrived = messages.some((m) => m.sender_type === "agent");
6018
6145
  return {
6019
6146
  messages,
6020
6147
  isLoading,
6021
6148
  isSending,
6022
6149
  error,
6023
6150
  isActive: conversationOpened && messages.length > 0,
6151
+ humanRequested: humanRequested && !agentArrived,
6152
+ awaitingReply,
6153
+ seenAt,
6154
+ mayHaveOlder,
6024
6155
  send,
6025
- startConversation
6156
+ startConversation,
6157
+ react,
6158
+ requestHuman,
6159
+ loadOlder
6026
6160
  };
6027
6161
  }
6028
6162
  var ElementsContext = createContext({
@@ -9320,6 +9454,7 @@ function ProductCustomizer({
9320
9454
  onAddToCart,
9321
9455
  onVariantChange,
9322
9456
  showSpecialInstructions = true,
9457
+ submitLabel,
9323
9458
  className,
9324
9459
  classNames
9325
9460
  }) {
@@ -9708,8 +9843,10 @@ function ProductCustomizer({
9708
9843
  isAdded && classNames?.submitButtonAdded,
9709
9844
  classNames?.submitButton
9710
9845
  ),
9711
- children: isAdded ? "Added to Cart" : /* @__PURE__ */ jsxs(Fragment, { children: [
9712
- "Add to Cart \xB7 ",
9846
+ children: isAdded ? "Added" : /* @__PURE__ */ jsxs(Fragment, { children: [
9847
+ submitLabel ?? "Add to Cart",
9848
+ " \xB7",
9849
+ " ",
9713
9850
  /* @__PURE__ */ jsx(Price, { amount: displayTotalPrice, currency })
9714
9851
  ] })
9715
9852
  }
@@ -9869,13 +10006,13 @@ function StoreVideo({
9869
10006
  autoplay = true,
9870
10007
  loop = true,
9871
10008
  muted = true,
9872
- lazy = true,
10009
+ lazy: lazy2 = true,
9873
10010
  className
9874
10011
  }) {
9875
10012
  const ref = useRef(null);
9876
- const [inView, setInView] = useState(!lazy);
10013
+ const [inView, setInView] = useState(!lazy2);
9877
10014
  useEffect(() => {
9878
- if (!lazy || inView) return;
10015
+ if (!lazy2 || inView) return;
9879
10016
  const node = ref.current;
9880
10017
  if (!node || typeof IntersectionObserver === "undefined") {
9881
10018
  setInView(true);
@@ -9892,7 +10029,7 @@ function StoreVideo({
9892
10029
  );
9893
10030
  observer.observe(node);
9894
10031
  return () => observer.disconnect();
9895
- }, [lazy, inView]);
10032
+ }, [lazy2, inView]);
9896
10033
  return /* @__PURE__ */ jsx(
9897
10034
  "div",
9898
10035
  {
@@ -9910,7 +10047,7 @@ function StoreVideo({
9910
10047
  muted,
9911
10048
  playsInline: true,
9912
10049
  controls,
9913
- preload: lazy ? "metadata" : "auto",
10050
+ preload: lazy2 ? "metadata" : "auto",
9914
10051
  "aria-label": alt,
9915
10052
  style: { width: "100%", height: "100%", objectFit: "cover", display: "block" },
9916
10053
  children: poster ? /* @__PURE__ */ jsx(
@@ -16712,6 +16849,112 @@ function CurrencySelector({
16712
16849
  );
16713
16850
  }) });
16714
16851
  }
16852
+ var Streamdown = lazy(() => import('streamdown').then((m) => ({ default: m.Streamdown })));
16853
+ function detectLookbehindSupport() {
16854
+ try {
16855
+ new RegExp("(?<=a)b");
16856
+ return true;
16857
+ } catch {
16858
+ return false;
16859
+ }
16860
+ }
16861
+ var supportsRegExpLookbehind = detectLookbehindSupport();
16862
+ marked.setOptions({
16863
+ gfm: true,
16864
+ breaks: true
16865
+ });
16866
+ var ALLOWED_TAGS = [
16867
+ "a",
16868
+ "b",
16869
+ "blockquote",
16870
+ "br",
16871
+ "code",
16872
+ "del",
16873
+ "em",
16874
+ "h1",
16875
+ "h2",
16876
+ "h3",
16877
+ "h4",
16878
+ "hr",
16879
+ "i",
16880
+ "li",
16881
+ "ol",
16882
+ "p",
16883
+ "pre",
16884
+ "strong",
16885
+ "table",
16886
+ "tbody",
16887
+ "td",
16888
+ "th",
16889
+ "thead",
16890
+ "tr",
16891
+ "ul"
16892
+ ];
16893
+ var ALLOWED_ATTR = ["href", "rel", "target", "title", "start"];
16894
+ var hooksInstalled = false;
16895
+ function installHooks() {
16896
+ if (hooksInstalled) return;
16897
+ hooksInstalled = true;
16898
+ DOMPurify.addHook("afterSanitizeAttributes", (node) => {
16899
+ if (!(node instanceof HTMLAnchorElement)) return;
16900
+ if (!node.getAttribute("href")) return;
16901
+ node.setAttribute("rel", "noreferrer noopener");
16902
+ node.setAttribute("target", "_blank");
16903
+ });
16904
+ }
16905
+ var CACHE_LIMIT = 300;
16906
+ var cache = /* @__PURE__ */ new Map();
16907
+ function renderMarkdown(text) {
16908
+ const cached = cache.get(text);
16909
+ if (cached !== void 0) return cached;
16910
+ installHooks();
16911
+ const html = DOMPurify.sanitize(marked.parse(text), {
16912
+ ALLOWED_TAGS,
16913
+ ALLOWED_ATTR
16914
+ });
16915
+ if (cache.size >= CACHE_LIMIT) {
16916
+ const oldest = cache.keys().next().value;
16917
+ if (oldest !== void 0) cache.delete(oldest);
16918
+ }
16919
+ cache.set(text, html);
16920
+ return html;
16921
+ }
16922
+ function SettledMarkdown({ text, className }) {
16923
+ const html = useMemo(() => renderMarkdown(text), [text]);
16924
+ return /* @__PURE__ */ jsx("div", { className, dangerouslySetInnerHTML: { __html: html } });
16925
+ }
16926
+ var ChatMarkdown = memo(function ChatMarkdown2({
16927
+ text,
16928
+ className,
16929
+ streaming = false
16930
+ }) {
16931
+ if (streaming && supportsRegExpLookbehind) {
16932
+ return (
16933
+ // While the streamdown chunk loads, the settled renderer keeps the
16934
+ // text on screen — no blank frame at stream start.
16935
+ /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(SettledMarkdown, { text, className }), children: /* @__PURE__ */ jsx(Streamdown, { className, children: text }) })
16936
+ );
16937
+ }
16938
+ return /* @__PURE__ */ jsx(SettledMarkdown, { text, className });
16939
+ });
16940
+
16941
+ // src/react/chat-model.ts
16942
+ function partitionMessages(messages) {
16943
+ const bubbles = [];
16944
+ const reactionsByTarget = /* @__PURE__ */ new Map();
16945
+ for (const message of messages) {
16946
+ if (message.content_type === "reaction") {
16947
+ const targetId = message.reply_to?.id;
16948
+ if (!targetId) continue;
16949
+ const list = reactionsByTarget.get(targetId);
16950
+ if (list) list.push(message);
16951
+ else reactionsByTarget.set(targetId, [message]);
16952
+ continue;
16953
+ }
16954
+ bubbles.push(message);
16955
+ }
16956
+ return { bubbles, reactionsByTarget };
16957
+ }
16715
16958
  function ChatWidget({
16716
16959
  client: clientProp,
16717
16960
  businessName = "Support",
@@ -16729,7 +16972,35 @@ function ChatWidget({
16729
16972
  const client = clientProp ?? context?.client;
16730
16973
  const [open, setOpen] = useState(defaultOpen);
16731
16974
  const [unread, setUnread] = useState(0);
16732
- const { messages, isLoading, isSending, error, isActive, send, startConversation } = useChat({ client, pollInterval });
16975
+ const {
16976
+ messages,
16977
+ isLoading,
16978
+ isSending,
16979
+ awaitingReply,
16980
+ seenAt,
16981
+ error,
16982
+ isActive,
16983
+ send,
16984
+ react,
16985
+ startConversation
16986
+ } = useChat({ client, pollInterval });
16987
+ const showTyping = isSending || awaitingReply;
16988
+ const attachPhoto = useCallback(
16989
+ async (file) => {
16990
+ if (!client || !file.type.startsWith("image/") || file.size > 5 * 1024 * 1024) return;
16991
+ const uploaded = await client.uploads.upload(file);
16992
+ if (!uploaded.ok) return;
16993
+ const attachment = {
16994
+ upload_id: uploaded.value.id,
16995
+ url: uploaded.value.url,
16996
+ mime_type: uploaded.value.content_type,
16997
+ filename: uploaded.value.filename,
16998
+ size: uploaded.value.size_bytes
16999
+ };
17000
+ await send("", { attachments: [attachment] });
17001
+ },
17002
+ [client, send]
17003
+ );
16733
17004
  const prevCountRef = useRef(messages.length);
16734
17005
  useEffect(() => {
16735
17006
  if (!open && messages.length > prevCountRef.current) {
@@ -16776,11 +17047,15 @@ function ChatWidget({
16776
17047
  messages,
16777
17048
  isLoading,
16778
17049
  isSending,
17050
+ isTyping: showTyping,
17051
+ seenAt,
16779
17052
  error,
16780
17053
  isActive,
16781
17054
  isLeft,
16782
17055
  onClose: handleClose,
16783
17056
  onSend: send,
17057
+ onReact: react,
17058
+ onAttach: attachPhoto,
16784
17059
  onStartConversation: startConversation,
16785
17060
  classNames
16786
17061
  }
@@ -16833,11 +17108,15 @@ function Panel({
16833
17108
  messages,
16834
17109
  isLoading,
16835
17110
  isSending,
17111
+ isTyping,
17112
+ seenAt,
16836
17113
  error,
16837
17114
  isActive,
16838
17115
  isLeft,
16839
17116
  onClose,
16840
17117
  onSend,
17118
+ onReact,
17119
+ onAttach,
16841
17120
  onStartConversation,
16842
17121
  classNames
16843
17122
  }) {
@@ -16875,7 +17154,9 @@ function Panel({
16875
17154
  {
16876
17155
  messages,
16877
17156
  isLoading,
16878
- isSending,
17157
+ isSending: isTyping,
17158
+ seenAt,
17159
+ onReact,
16879
17160
  className: classNames?.messages
16880
17161
  }
16881
17162
  ),
@@ -16886,6 +17167,7 @@ function Panel({
16886
17167
  placeholder,
16887
17168
  isSending,
16888
17169
  onSend,
17170
+ onAttach,
16889
17171
  className: classNames?.input
16890
17172
  }
16891
17173
  ),
@@ -16981,16 +17263,29 @@ function Welcome({
16981
17263
  }
16982
17264
  );
16983
17265
  }
17266
+ var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
17267
+ function lastSeenCustomerMessageId(bubbles, seenAt) {
17268
+ if (!seenAt || !bubbles.some((m) => m.sender_type === "agent")) return null;
17269
+ for (let i = bubbles.length - 1; i >= 0; i--) {
17270
+ if (bubbles[i].sender_type !== "customer") continue;
17271
+ return bubbles[i].created_at <= seenAt ? bubbles[i].id : null;
17272
+ }
17273
+ return null;
17274
+ }
16984
17275
  function MessageList({
16985
17276
  messages,
16986
17277
  isLoading,
16987
17278
  isSending,
17279
+ seenAt,
17280
+ onReact,
16988
17281
  className
16989
17282
  }) {
16990
17283
  const bottomRef = useRef(null);
16991
17284
  useEffect(() => {
16992
17285
  bottomRef.current?.scrollIntoView({ behavior: "smooth" });
16993
17286
  }, [messages.length, isSending]);
17287
+ const { bubbles, reactionsByTarget } = partitionMessages(messages);
17288
+ const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
16994
17289
  return /* @__PURE__ */ jsxs(
16995
17290
  "div",
16996
17291
  {
@@ -17000,40 +17295,111 @@ function MessageList({
17000
17295
  ),
17001
17296
  children: [
17002
17297
  isLoading && messages.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
17003
- messages.map((msg, i) => {
17298
+ bubbles.map((msg, i) => {
17004
17299
  const isCustomer = msg.sender_type === "customer";
17005
- const prev = messages[i - 1];
17300
+ const prev = bubbles[i - 1];
17006
17301
  const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
17007
17302
  const isOptimistic = msg.id.startsWith("opt_");
17303
+ const reactions = reactionsByTarget.get(msg.id);
17008
17304
  return /* @__PURE__ */ jsxs("div", { children: [
17009
17305
  !sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsx("div", { className: "h-3" }),
17010
- /* @__PURE__ */ jsx(
17306
+ /* @__PURE__ */ jsxs(
17011
17307
  "div",
17012
17308
  {
17013
17309
  className: cn(
17014
- "flex max-w-[85%]",
17310
+ "group relative flex max-w-[85%]",
17015
17311
  isCustomer ? "ml-auto" : "mr-auto"
17016
17312
  ),
17017
- children: /* @__PURE__ */ jsxs(
17018
- "div",
17019
- {
17020
- className: cn(
17021
- "rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
17022
- isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
17023
- isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
17024
- isOptimistic && "opacity-70"
17025
- ),
17026
- children: [
17027
- !isCustomer && !sameSenderAsPrev && /* @__PURE__ */ jsxs("span", { className: "mb-1 inline-flex items-center gap-1 rounded bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold text-violet-600", children: [
17028
- /* @__PURE__ */ jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
17029
- "AI"
17030
- ] }),
17031
- /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content })
17032
- ]
17033
- }
17034
- )
17313
+ children: [
17314
+ !isCustomer && !isOptimistic && /* @__PURE__ */ jsx("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: QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
17315
+ "button",
17316
+ {
17317
+ type: "button",
17318
+ "aria-label": `React ${emoji}`,
17319
+ onClick: () => void onReact(msg, emoji),
17320
+ className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
17321
+ children: emoji
17322
+ },
17323
+ emoji
17324
+ )) }),
17325
+ /* @__PURE__ */ jsxs(
17326
+ "div",
17327
+ {
17328
+ className: cn(
17329
+ "rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
17330
+ isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
17331
+ isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
17332
+ isOptimistic && "opacity-70"
17333
+ ),
17334
+ children: [
17335
+ !isCustomer && !sameSenderAsPrev && /* @__PURE__ */ jsxs("span", { className: "mb-1 inline-flex items-center gap-1 rounded bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold text-violet-600", children: [
17336
+ /* @__PURE__ */ jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
17337
+ "AI"
17338
+ ] }),
17339
+ msg.reply_to && /* @__PURE__ */ jsxs(
17340
+ "div",
17341
+ {
17342
+ className: cn(
17343
+ "mb-1.5 flex gap-1.5 rounded-lg px-2 py-1 text-xs",
17344
+ isCustomer ? "bg-background/15" : "bg-muted"
17345
+ ),
17346
+ children: [
17347
+ /* @__PURE__ */ jsx(
17348
+ "span",
17349
+ {
17350
+ className: cn(
17351
+ "w-[3px] shrink-0 self-stretch rounded-full",
17352
+ isCustomer ? "bg-background/40" : "bg-muted-foreground/40"
17353
+ )
17354
+ }
17355
+ ),
17356
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate opacity-80", children: msg.reply_to.preview })
17357
+ ]
17358
+ }
17359
+ ),
17360
+ msg.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map(
17361
+ (attachment, index) => attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: attachment.url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx(
17362
+ "img",
17363
+ {
17364
+ src: attachment.url,
17365
+ alt: attachment.filename ?? "Photo",
17366
+ loading: "lazy",
17367
+ className: "max-h-48 max-w-full rounded-lg object-cover"
17368
+ }
17369
+ ) }, index) : /* @__PURE__ */ jsx(
17370
+ "a",
17371
+ {
17372
+ href: attachment.url,
17373
+ target: "_blank",
17374
+ rel: "noreferrer",
17375
+ className: "truncate text-xs underline",
17376
+ children: attachment.filename ?? "Attachment"
17377
+ },
17378
+ index
17379
+ )
17380
+ ) }),
17381
+ isCustomer ? msg.content && /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsx(
17382
+ ChatMarkdown,
17383
+ {
17384
+ text: msg.content,
17385
+ className: "[&_p]:my-0 [&_p+p]:mt-1.5 [&_ul]:my-1 [&_ul]:list-disc [&_ul]:pl-4 [&_ol]:my-1 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:my-0.5 [&_h1]:text-sm [&_h1]:font-bold [&_h2]:text-sm [&_h2]:font-bold [&_h3]:text-sm [&_h3]:font-semibold [&_hr]:my-2 [&_table]:my-1 [&_table]:w-full [&_th]:pb-1 [&_th]:text-left [&_th]:text-[10px] [&_th]:font-semibold [&_th]:uppercase [&_td]:py-1 [&_td:last-child]:text-right [&_td:last-child]:font-semibold [&_td:last-child]:tabular-nums [&_a]:underline"
17386
+ }
17387
+ )
17388
+ ]
17389
+ }
17390
+ )
17391
+ ]
17035
17392
  }
17036
- )
17393
+ ),
17394
+ reactions && reactions.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("mt-0.5 flex gap-1", isCustomer ? "justify-end" : "justify-start"), children: reactions.map((reaction) => /* @__PURE__ */ jsx(
17395
+ "span",
17396
+ {
17397
+ className: "rounded-full border border-border bg-background px-1.5 py-0.5 text-xs shadow-sm",
17398
+ children: reaction.content
17399
+ },
17400
+ reaction.id
17401
+ )) }),
17402
+ msg.id === seenMessageId && /* @__PURE__ */ jsx("div", { className: "mt-0.5 text-right text-[10px] font-medium text-muted-foreground", children: "Seen" })
17037
17403
  ] }, msg.id);
17038
17404
  }),
17039
17405
  isSending && /* @__PURE__ */ jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsx(TypingDots, {}) }),
@@ -17046,10 +17412,24 @@ function ChatInput({
17046
17412
  placeholder,
17047
17413
  isSending,
17048
17414
  onSend,
17415
+ onAttach,
17049
17416
  className
17050
17417
  }) {
17051
17418
  const [value, setValue] = useState("");
17419
+ const [uploading, setUploading] = useState(false);
17052
17420
  const textareaRef = useRef(null);
17421
+ const fileRef = useRef(null);
17422
+ const handleAttach = useCallback(
17423
+ async (file) => {
17424
+ setUploading(true);
17425
+ try {
17426
+ await onAttach(file);
17427
+ } finally {
17428
+ setUploading(false);
17429
+ }
17430
+ },
17431
+ [onAttach]
17432
+ );
17053
17433
  const handleSend = useCallback(() => {
17054
17434
  const trimmed = value.trim();
17055
17435
  if (!trimmed || isSending) return;
@@ -17083,6 +17463,37 @@ function ChatInput({
17083
17463
  className
17084
17464
  ),
17085
17465
  children: [
17466
+ /* @__PURE__ */ jsx(
17467
+ "input",
17468
+ {
17469
+ ref: fileRef,
17470
+ type: "file",
17471
+ accept: "image/*",
17472
+ className: "hidden",
17473
+ "aria-hidden": "true",
17474
+ tabIndex: -1,
17475
+ onChange: (e) => {
17476
+ const file = e.target.files?.[0];
17477
+ e.target.value = "";
17478
+ if (file) void handleAttach(file);
17479
+ }
17480
+ }
17481
+ ),
17482
+ /* @__PURE__ */ jsx(
17483
+ "button",
17484
+ {
17485
+ type: "button",
17486
+ "aria-label": "Attach a photo",
17487
+ disabled: uploading,
17488
+ onClick: () => fileRef.current?.click(),
17489
+ className: cn(
17490
+ "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
17491
+ "bg-muted text-muted-foreground transition-colors hover:text-foreground",
17492
+ uploading && "animate-pulse"
17493
+ ),
17494
+ children: /* @__PURE__ */ jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
17495
+ }
17496
+ ),
17086
17497
  /* @__PURE__ */ jsx(
17087
17498
  "div",
17088
17499
  {
@@ -17185,6 +17596,25 @@ function SendIcon({ className }) {
17185
17596
  }
17186
17597
  );
17187
17598
  }
17599
+ function ImageIcon({ className }) {
17600
+ return /* @__PURE__ */ jsxs(
17601
+ "svg",
17602
+ {
17603
+ className,
17604
+ viewBox: "0 0 24 24",
17605
+ fill: "none",
17606
+ stroke: "currentColor",
17607
+ strokeWidth: 2,
17608
+ strokeLinecap: "round",
17609
+ strokeLinejoin: "round",
17610
+ children: [
17611
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
17612
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
17613
+ /* @__PURE__ */ jsx("polyline", { points: "21 15 16 10 5 21" })
17614
+ ]
17615
+ }
17616
+ );
17617
+ }
17188
17618
  function SparkleIcon({ className }) {
17189
17619
  return /* @__PURE__ */ jsx(
17190
17620
  "svg",
@@ -17199,4 +17629,1825 @@ function SparkleIcon({ className }) {
17199
17629
  );
17200
17630
  }
17201
17631
 
17202
- export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, cn, defaultBoundaryKind, getBoundaryLabels, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };
17632
+ // src/react/genui/catalog.ts
17633
+ function genUICatalog(registry) {
17634
+ return new Map(
17635
+ registry.components.map((component) => [
17636
+ component.name,
17637
+ {
17638
+ params: component.params.map((param) => ({
17639
+ name: param.name,
17640
+ required: param.required ?? false
17641
+ }))
17642
+ }
17643
+ ])
17644
+ );
17645
+ }
17646
+
17647
+ // src/genui/lang/parser/ast.ts
17648
+ function isRuntimeExpr(node) {
17649
+ switch (node.k) {
17650
+ case "StateRef":
17651
+ case "RuntimeRef":
17652
+ case "BinOp":
17653
+ case "UnaryOp":
17654
+ case "Ternary":
17655
+ case "Member":
17656
+ case "Index":
17657
+ case "Assign":
17658
+ return true;
17659
+ default:
17660
+ return false;
17661
+ }
17662
+ }
17663
+ var AST_KINDS = /* @__PURE__ */ new Set([
17664
+ "Comp",
17665
+ "Ref",
17666
+ "StateRef",
17667
+ "RuntimeRef",
17668
+ "BinOp",
17669
+ "UnaryOp",
17670
+ "Ternary",
17671
+ "Member",
17672
+ "Index",
17673
+ "Assign",
17674
+ "Str",
17675
+ "Num",
17676
+ "Bool",
17677
+ "Null",
17678
+ "Arr",
17679
+ "Obj",
17680
+ "Ph"
17681
+ ]);
17682
+ function isASTNode(value) {
17683
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
17684
+ return AST_KINDS.has(value.k);
17685
+ }
17686
+ function walkAST(node, visit) {
17687
+ const walk = (current) => {
17688
+ visit(current);
17689
+ switch (current.k) {
17690
+ case "Comp":
17691
+ current.args.forEach(walk);
17692
+ Object.values(current.mappedProps ?? {}).forEach(walk);
17693
+ break;
17694
+ case "Arr":
17695
+ current.els.forEach(walk);
17696
+ break;
17697
+ case "Obj":
17698
+ current.entries.forEach(([, value]) => walk(value));
17699
+ break;
17700
+ case "BinOp":
17701
+ walk(current.left);
17702
+ walk(current.right);
17703
+ break;
17704
+ case "UnaryOp":
17705
+ walk(current.operand);
17706
+ break;
17707
+ case "Ternary":
17708
+ walk(current.cond);
17709
+ walk(current.then);
17710
+ walk(current.else);
17711
+ break;
17712
+ case "Member":
17713
+ walk(current.obj);
17714
+ break;
17715
+ case "Index":
17716
+ walk(current.obj);
17717
+ walk(current.index);
17718
+ break;
17719
+ case "Assign":
17720
+ walk(current.value);
17721
+ break;
17722
+ }
17723
+ };
17724
+ walk(node);
17725
+ }
17726
+
17727
+ // src/genui/lang/parser/builtins.ts
17728
+ function resolveField(obj, path) {
17729
+ if (!path || obj == null) return void 0;
17730
+ if (!path.includes(".")) return obj[path];
17731
+ let cur = obj;
17732
+ for (const p of path.split(".")) {
17733
+ if (cur == null) return void 0;
17734
+ cur = cur[p];
17735
+ }
17736
+ return cur;
17737
+ }
17738
+ function toNumber2(val) {
17739
+ if (typeof val === "number") return val;
17740
+ if (typeof val === "string") {
17741
+ const n = Number(val);
17742
+ return isNaN(n) ? 0 : n;
17743
+ }
17744
+ if (typeof val === "boolean") return val ? 1 : 0;
17745
+ return 0;
17746
+ }
17747
+ var BUILTINS = {
17748
+ Count: {
17749
+ name: "Count",
17750
+ signature: "Count(array) \u2192 number",
17751
+ description: "Returns array length",
17752
+ fn: (arr) => Array.isArray(arr) ? arr.length : 0
17753
+ },
17754
+ First: {
17755
+ name: "First",
17756
+ signature: "First(array) \u2192 element",
17757
+ description: "Returns first element of array",
17758
+ fn: (arr) => Array.isArray(arr) ? arr[0] ?? null : null
17759
+ },
17760
+ Last: {
17761
+ name: "Last",
17762
+ signature: "Last(array) \u2192 element",
17763
+ description: "Returns last element of array",
17764
+ fn: (arr) => Array.isArray(arr) ? arr[arr.length - 1] ?? null : null
17765
+ },
17766
+ Sum: {
17767
+ name: "Sum",
17768
+ signature: "Sum(numbers[]) \u2192 number",
17769
+ description: "Sum of numeric array",
17770
+ fn: (arr) => Array.isArray(arr) ? arr.reduce((a, b) => a + toNumber2(b), 0) : 0
17771
+ },
17772
+ Avg: {
17773
+ name: "Avg",
17774
+ signature: "Avg(numbers[]) \u2192 number",
17775
+ description: "Average of numeric array",
17776
+ fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((a, b) => a + toNumber2(b), 0) / arr.length : 0
17777
+ },
17778
+ Min: {
17779
+ name: "Min",
17780
+ signature: "Min(numbers[]) \u2192 number",
17781
+ description: "Minimum value in array",
17782
+ fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.min(acc, toNumber2(b)), toNumber2(arr[0])) : 0
17783
+ },
17784
+ Max: {
17785
+ name: "Max",
17786
+ signature: "Max(numbers[]) \u2192 number",
17787
+ description: "Maximum value in array",
17788
+ fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.max(acc, toNumber2(b)), toNumber2(arr[0])) : 0
17789
+ },
17790
+ Sort: {
17791
+ name: "Sort",
17792
+ signature: "Sort(array, field, direction?) \u2192 sorted array",
17793
+ description: 'Sort array by field. Direction: "asc" (default) or "desc"',
17794
+ fn: (arr, field, dir) => {
17795
+ if (!Array.isArray(arr)) return arr;
17796
+ const f = String(field ?? "");
17797
+ const desc = String(dir ?? "asc") === "desc";
17798
+ return [...arr].sort((a, b) => {
17799
+ const av = f ? resolveField(a, f) : a;
17800
+ const bv = f ? resolveField(b, f) : b;
17801
+ const aIsNumeric = typeof av === "number" || typeof av === "string" && !isNaN(Number(av)) && av !== "";
17802
+ const bIsNumeric = typeof bv === "number" || typeof bv === "string" && !isNaN(Number(bv)) && bv !== "";
17803
+ if (aIsNumeric && bIsNumeric) {
17804
+ const diff = toNumber2(av) - toNumber2(bv);
17805
+ return desc ? -diff : diff;
17806
+ }
17807
+ const cmp = String(av ?? "").localeCompare(String(bv ?? ""));
17808
+ return desc ? -cmp : cmp;
17809
+ });
17810
+ }
17811
+ },
17812
+ Filter: {
17813
+ name: "Filter",
17814
+ signature: 'Filter(array, field, operator: "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains", value) \u2192 filtered array',
17815
+ description: "Filter array by field value",
17816
+ fn: (arr, field, op, value) => {
17817
+ if (!Array.isArray(arr)) return [];
17818
+ const f = String(field ?? "");
17819
+ const o = String(op ?? "==");
17820
+ return arr.filter((item) => {
17821
+ const v = f ? resolveField(item, f) : item;
17822
+ switch (o) {
17823
+ case "==":
17824
+ return v == value;
17825
+ case "!=":
17826
+ return v != value;
17827
+ case ">":
17828
+ return toNumber2(v) > toNumber2(value);
17829
+ case "<":
17830
+ return toNumber2(v) < toNumber2(value);
17831
+ case ">=":
17832
+ return toNumber2(v) >= toNumber2(value);
17833
+ case "<=":
17834
+ return toNumber2(v) <= toNumber2(value);
17835
+ case "contains":
17836
+ return String(v ?? "").includes(String(value ?? ""));
17837
+ default:
17838
+ return false;
17839
+ }
17840
+ });
17841
+ }
17842
+ },
17843
+ Round: {
17844
+ name: "Round",
17845
+ signature: "Round(number, decimals?) \u2192 number",
17846
+ description: "Round to N decimal places (default 0)",
17847
+ fn: (n, decimals) => {
17848
+ const num = toNumber2(n);
17849
+ const d = decimals != null ? toNumber2(decimals) : 0;
17850
+ const factor = Math.pow(10, d);
17851
+ return Math.round(num * factor) / factor;
17852
+ }
17853
+ },
17854
+ Abs: {
17855
+ name: "Abs",
17856
+ signature: "Abs(number) \u2192 number",
17857
+ description: "Absolute value",
17858
+ fn: (n) => Math.abs(toNumber2(n))
17859
+ },
17860
+ Floor: {
17861
+ name: "Floor",
17862
+ signature: "Floor(number) \u2192 number",
17863
+ description: "Round down to nearest integer",
17864
+ fn: (n) => Math.floor(toNumber2(n))
17865
+ },
17866
+ Ceil: {
17867
+ name: "Ceil",
17868
+ signature: "Ceil(number) \u2192 number",
17869
+ description: "Round up to nearest integer",
17870
+ fn: (n) => Math.ceil(toNumber2(n))
17871
+ }
17872
+ };
17873
+ var LAZY_BUILTINS = /* @__PURE__ */ new Set(["Each"]);
17874
+ var ACTION_STEPS = {
17875
+ Run: "run",
17876
+ ToAssistant: "continue_conversation",
17877
+ OpenUrl: "open_url",
17878
+ Set: "set",
17879
+ Reset: "reset"
17880
+ };
17881
+ var ACTION_NAMES = /* @__PURE__ */ new Set(["Action", ...Object.keys(ACTION_STEPS)]);
17882
+ var BUILTIN_NAMES = /* @__PURE__ */ new Set([
17883
+ ...Object.keys(BUILTINS),
17884
+ ...LAZY_BUILTINS,
17885
+ ...ACTION_NAMES
17886
+ ]);
17887
+ function isBuiltin(name) {
17888
+ return BUILTIN_NAMES.has(name);
17889
+ }
17890
+ var RESERVED_CALLS = { Query: "Query", Mutation: "Mutation" };
17891
+ function isReservedCall(name) {
17892
+ return name in RESERVED_CALLS;
17893
+ }
17894
+
17895
+ // src/genui/lang/parser/expressions.ts
17896
+ var PREC_TERNARY = 1;
17897
+ var PREC_OR = 2;
17898
+ var PREC_AND = 3;
17899
+ var PREC_EQ = 4;
17900
+ var PREC_CMP = 5;
17901
+ var PREC_ADD = 6;
17902
+ var PREC_MUL = 7;
17903
+ var PREC_UNARY = 8;
17904
+ var PREC_MEMBER = 9;
17905
+ function parseExpression(tokens) {
17906
+ let pos = 0;
17907
+ const cur = () => tokens[pos] ?? { t: 13 /* EOF */ };
17908
+ const adv = () => {
17909
+ const tok = cur();
17910
+ pos++;
17911
+ return tok;
17912
+ };
17913
+ const eat = (kind) => {
17914
+ if (cur().t === kind) pos++;
17915
+ };
17916
+ function getInfixPrec(tok) {
17917
+ switch (tok.t) {
17918
+ case 34 /* Question */:
17919
+ return PREC_TERNARY;
17920
+ case 32 /* Or */:
17921
+ return PREC_OR;
17922
+ case 31 /* And */:
17923
+ return PREC_AND;
17924
+ case 25 /* EqEq */:
17925
+ case 26 /* NotEq */:
17926
+ return PREC_EQ;
17927
+ case 27 /* Greater */:
17928
+ case 28 /* Less */:
17929
+ case 29 /* GreaterEq */:
17930
+ case 30 /* LessEq */:
17931
+ return PREC_CMP;
17932
+ case 20 /* Plus */:
17933
+ case 21 /* Minus */:
17934
+ return PREC_ADD;
17935
+ case 22 /* Star */:
17936
+ case 23 /* Slash */:
17937
+ case 24 /* Percent */:
17938
+ return PREC_MUL;
17939
+ case 19 /* Dot */:
17940
+ case 3 /* LBrack */:
17941
+ return PREC_MEMBER;
17942
+ default:
17943
+ return 0;
17944
+ }
17945
+ }
17946
+ function parseExpr(minPrec = 0) {
17947
+ let left = parsePrefix();
17948
+ while (getInfixPrec(cur()) > minPrec) {
17949
+ left = parseInfix(left);
17950
+ }
17951
+ return left;
17952
+ }
17953
+ function parsePrefix() {
17954
+ const tok = cur();
17955
+ if (tok.t === 14 /* Str */) {
17956
+ adv();
17957
+ return { k: "Str", v: tok.v };
17958
+ }
17959
+ if (tok.t === 15 /* Num */) {
17960
+ adv();
17961
+ return { k: "Num", v: tok.v };
17962
+ }
17963
+ if (tok.t === 10 /* True */) {
17964
+ adv();
17965
+ return { k: "Bool", v: true };
17966
+ }
17967
+ if (tok.t === 11 /* False */) {
17968
+ adv();
17969
+ return { k: "Bool", v: false };
17970
+ }
17971
+ if (tok.t === 12 /* Null */) {
17972
+ adv();
17973
+ return { k: "Null" };
17974
+ }
17975
+ if (tok.t === 3 /* LBrack */) return parseArr();
17976
+ if (tok.t === 5 /* LBrace */) return parseObj();
17977
+ if (tok.t === 18 /* StateVar */) {
17978
+ const name = tok.v;
17979
+ adv();
17980
+ if (cur().t === 9 /* Equals */) {
17981
+ adv();
17982
+ const value = parseExpr(0);
17983
+ return { k: "Assign", target: name, value };
17984
+ }
17985
+ return { k: "StateRef", n: name };
17986
+ }
17987
+ if (tok.t === 17 /* Type */) {
17988
+ const name = tok.v;
17989
+ if (tokens[pos + 1]?.t === 1 /* LParen */ && (!isBuiltin(name) || name === "Action"))
17990
+ return parseComp();
17991
+ adv();
17992
+ return { k: "Ref", n: name };
17993
+ }
17994
+ if (tok.t === 35 /* BuiltinCall */) {
17995
+ if (tokens[pos + 1]?.t === 1 /* LParen */) return parseComp();
17996
+ adv();
17997
+ return { k: "Ref", n: tok.v };
17998
+ }
17999
+ if (tok.t === 16 /* Ident */) {
18000
+ adv();
18001
+ return { k: "Ref", n: tok.v };
18002
+ }
18003
+ if (tok.t === 33 /* Not */) {
18004
+ adv();
18005
+ return { k: "UnaryOp", op: "!", operand: parseExpr(PREC_UNARY) };
18006
+ }
18007
+ if (tok.t === 21 /* Minus */) {
18008
+ adv();
18009
+ return { k: "UnaryOp", op: "-", operand: parseExpr(PREC_UNARY) };
18010
+ }
18011
+ if (tok.t === 1 /* LParen */) {
18012
+ adv();
18013
+ const inner = parseExpr(0);
18014
+ eat(2 /* RParen */);
18015
+ return inner;
18016
+ }
18017
+ adv();
18018
+ return { k: "Null" };
18019
+ }
18020
+ function parseInfix(left) {
18021
+ const tok = cur();
18022
+ if (tok.t === 20 /* Plus */) {
18023
+ adv();
18024
+ return { k: "BinOp", op: "+", left, right: parseExpr(PREC_ADD) };
18025
+ }
18026
+ if (tok.t === 21 /* Minus */) {
18027
+ adv();
18028
+ return { k: "BinOp", op: "-", left, right: parseExpr(PREC_ADD) };
18029
+ }
18030
+ if (tok.t === 22 /* Star */) {
18031
+ adv();
18032
+ return { k: "BinOp", op: "*", left, right: parseExpr(PREC_MUL) };
18033
+ }
18034
+ if (tok.t === 23 /* Slash */) {
18035
+ adv();
18036
+ return { k: "BinOp", op: "/", left, right: parseExpr(PREC_MUL) };
18037
+ }
18038
+ if (tok.t === 24 /* Percent */) {
18039
+ adv();
18040
+ return { k: "BinOp", op: "%", left, right: parseExpr(PREC_MUL) };
18041
+ }
18042
+ if (tok.t === 25 /* EqEq */) {
18043
+ adv();
18044
+ return { k: "BinOp", op: "==", left, right: parseExpr(PREC_EQ) };
18045
+ }
18046
+ if (tok.t === 26 /* NotEq */) {
18047
+ adv();
18048
+ return { k: "BinOp", op: "!=", left, right: parseExpr(PREC_EQ) };
18049
+ }
18050
+ if (tok.t === 27 /* Greater */) {
18051
+ adv();
18052
+ return { k: "BinOp", op: ">", left, right: parseExpr(PREC_CMP) };
18053
+ }
18054
+ if (tok.t === 28 /* Less */) {
18055
+ adv();
18056
+ return { k: "BinOp", op: "<", left, right: parseExpr(PREC_CMP) };
18057
+ }
18058
+ if (tok.t === 29 /* GreaterEq */) {
18059
+ adv();
18060
+ return { k: "BinOp", op: ">=", left, right: parseExpr(PREC_CMP) };
18061
+ }
18062
+ if (tok.t === 30 /* LessEq */) {
18063
+ adv();
18064
+ return { k: "BinOp", op: "<=", left, right: parseExpr(PREC_CMP) };
18065
+ }
18066
+ if (tok.t === 31 /* And */) {
18067
+ adv();
18068
+ return { k: "BinOp", op: "&&", left, right: parseExpr(PREC_AND) };
18069
+ }
18070
+ if (tok.t === 32 /* Or */) {
18071
+ adv();
18072
+ return { k: "BinOp", op: "||", left, right: parseExpr(PREC_OR) };
18073
+ }
18074
+ if (tok.t === 34 /* Question */) {
18075
+ adv();
18076
+ const then = parseExpr(0);
18077
+ eat(8 /* Colon */);
18078
+ const els = parseExpr(0);
18079
+ return { k: "Ternary", cond: left, then, else: els };
18080
+ }
18081
+ if (tok.t === 19 /* Dot */) {
18082
+ adv();
18083
+ const fieldTok = cur();
18084
+ const field = fieldTok.t === 16 /* Ident */ || fieldTok.t === 17 /* Type */ || fieldTok.t === 14 /* Str */ || fieldTok.t === 15 /* Num */ ? (adv(), String(fieldTok.v)) : fieldTok.t === 18 /* StateVar */ ? (adv(), fieldTok.v.replace(/^\$/, "")) : (adv(), "?");
18085
+ return { k: "Member", obj: left, field };
18086
+ }
18087
+ if (tok.t === 3 /* LBrack */) {
18088
+ adv();
18089
+ const index = parseExpr(0);
18090
+ eat(4 /* RBrack */);
18091
+ return { k: "Index", obj: left, index };
18092
+ }
18093
+ return left;
18094
+ }
18095
+ function parseComp() {
18096
+ const name = cur().v;
18097
+ adv();
18098
+ eat(1 /* LParen */);
18099
+ const args = [];
18100
+ while (cur().t !== 2 /* RParen */ && cur().t !== 13 /* EOF */) {
18101
+ args.push(parseExpr(0));
18102
+ if (cur().t === 7 /* Comma */) adv();
18103
+ }
18104
+ eat(2 /* RParen */);
18105
+ return { k: "Comp", name, args };
18106
+ }
18107
+ function parseArr() {
18108
+ adv();
18109
+ const els = [];
18110
+ while (cur().t !== 4 /* RBrack */ && cur().t !== 13 /* EOF */) {
18111
+ els.push(parseExpr(0));
18112
+ if (cur().t === 7 /* Comma */) adv();
18113
+ }
18114
+ eat(4 /* RBrack */);
18115
+ return { k: "Arr", els };
18116
+ }
18117
+ function parseObj() {
18118
+ adv();
18119
+ const entries = [];
18120
+ while (cur().t !== 6 /* RBrace */ && cur().t !== 13 /* EOF */) {
18121
+ const kt = cur();
18122
+ const key = kt.t === 16 /* Ident */ || kt.t === 14 /* Str */ || kt.t === 17 /* Type */ || kt.t === 15 /* Num */ ? (adv(), String(kt.v)) : kt.t === 18 /* StateVar */ ? (adv(), kt.v.replace(/^\$/, "")) : (adv(), "?");
18123
+ eat(8 /* Colon */);
18124
+ entries.push([key, parseExpr(0)]);
18125
+ if (cur().t === 7 /* Comma */) adv();
18126
+ }
18127
+ eat(6 /* RBrace */);
18128
+ return { k: "Obj", entries };
18129
+ }
18130
+ return parseExpr(0);
18131
+ }
18132
+
18133
+ // src/genui/lang/parser/lexer.ts
18134
+ function tokenize(src) {
18135
+ const tokens = [];
18136
+ let i = 0;
18137
+ const n = src.length;
18138
+ while (i < n) {
18139
+ while (i < n && (src[i] === " " || src[i] === " " || src[i] === "\r")) i++;
18140
+ if (i >= n) break;
18141
+ const c = src[i];
18142
+ if (c === "\n") {
18143
+ tokens.push({ t: 0 /* Newline */ });
18144
+ i++;
18145
+ continue;
18146
+ }
18147
+ if (c === "(") {
18148
+ tokens.push({ t: 1 /* LParen */ });
18149
+ i++;
18150
+ continue;
18151
+ }
18152
+ if (c === ")") {
18153
+ tokens.push({ t: 2 /* RParen */ });
18154
+ i++;
18155
+ continue;
18156
+ }
18157
+ if (c === "[") {
18158
+ tokens.push({ t: 3 /* LBrack */ });
18159
+ i++;
18160
+ continue;
18161
+ }
18162
+ if (c === "]") {
18163
+ tokens.push({ t: 4 /* RBrack */ });
18164
+ i++;
18165
+ continue;
18166
+ }
18167
+ if (c === "{") {
18168
+ tokens.push({ t: 5 /* LBrace */ });
18169
+ i++;
18170
+ continue;
18171
+ }
18172
+ if (c === "}") {
18173
+ tokens.push({ t: 6 /* RBrace */ });
18174
+ i++;
18175
+ continue;
18176
+ }
18177
+ if (c === ",") {
18178
+ tokens.push({ t: 7 /* Comma */ });
18179
+ i++;
18180
+ continue;
18181
+ }
18182
+ if (c === ":") {
18183
+ tokens.push({ t: 8 /* Colon */ });
18184
+ i++;
18185
+ continue;
18186
+ }
18187
+ if (c === "=") {
18188
+ if (i + 1 < n && src[i + 1] === "=") {
18189
+ tokens.push({ t: 25 /* EqEq */ });
18190
+ i += 2;
18191
+ } else {
18192
+ tokens.push({ t: 9 /* Equals */ });
18193
+ i++;
18194
+ }
18195
+ continue;
18196
+ }
18197
+ if (c === "!") {
18198
+ if (i + 1 < n && src[i + 1] === "=") {
18199
+ tokens.push({ t: 26 /* NotEq */ });
18200
+ i += 2;
18201
+ } else {
18202
+ tokens.push({ t: 33 /* Not */ });
18203
+ i++;
18204
+ }
18205
+ continue;
18206
+ }
18207
+ if (c === ">") {
18208
+ if (i + 1 < n && src[i + 1] === "=") {
18209
+ tokens.push({ t: 29 /* GreaterEq */ });
18210
+ i += 2;
18211
+ } else {
18212
+ tokens.push({ t: 27 /* Greater */ });
18213
+ i++;
18214
+ }
18215
+ continue;
18216
+ }
18217
+ if (c === "<") {
18218
+ if (i + 1 < n && src[i + 1] === "=") {
18219
+ tokens.push({ t: 30 /* LessEq */ });
18220
+ i += 2;
18221
+ } else {
18222
+ tokens.push({ t: 28 /* Less */ });
18223
+ i++;
18224
+ }
18225
+ continue;
18226
+ }
18227
+ if (c === "&") {
18228
+ if (i + 1 < n && src[i + 1] === "&") {
18229
+ tokens.push({ t: 31 /* And */ });
18230
+ i += 2;
18231
+ } else {
18232
+ tokens.push({ t: 31 /* And */ });
18233
+ i++;
18234
+ }
18235
+ continue;
18236
+ }
18237
+ if (c === "|") {
18238
+ if (i + 1 < n && src[i + 1] === "|") {
18239
+ tokens.push({ t: 32 /* Or */ });
18240
+ i += 2;
18241
+ } else {
18242
+ tokens.push({ t: 32 /* Or */ });
18243
+ i++;
18244
+ }
18245
+ continue;
18246
+ }
18247
+ if (c === ".") {
18248
+ tokens.push({ t: 19 /* Dot */ });
18249
+ i++;
18250
+ continue;
18251
+ }
18252
+ if (c === "?") {
18253
+ tokens.push({ t: 34 /* Question */ });
18254
+ i++;
18255
+ continue;
18256
+ }
18257
+ if (c === "+") {
18258
+ tokens.push({ t: 20 /* Plus */ });
18259
+ i++;
18260
+ continue;
18261
+ }
18262
+ if (c === "*") {
18263
+ tokens.push({ t: 22 /* Star */ });
18264
+ i++;
18265
+ continue;
18266
+ }
18267
+ if (c === "/") {
18268
+ tokens.push({ t: 23 /* Slash */ });
18269
+ i++;
18270
+ continue;
18271
+ }
18272
+ if (c === "%") {
18273
+ tokens.push({ t: 24 /* Percent */ });
18274
+ i++;
18275
+ continue;
18276
+ }
18277
+ if (c === '"') {
18278
+ const start = i;
18279
+ i++;
18280
+ let isClosed = false;
18281
+ while (i < n) {
18282
+ if (src[i] === "\\") {
18283
+ i += 2;
18284
+ } else if (src[i] === '"') {
18285
+ i++;
18286
+ isClosed = true;
18287
+ break;
18288
+ } else {
18289
+ i++;
18290
+ }
18291
+ }
18292
+ const rawString = src.slice(start, i);
18293
+ try {
18294
+ const validJsonString = isClosed ? rawString : rawString + '"';
18295
+ tokens.push({ t: 14 /* Str */, v: JSON.parse(validJsonString) });
18296
+ } catch {
18297
+ const stripped = rawString.replace(/^"|"$/g, "");
18298
+ tokens.push({ t: 14 /* Str */, v: stripped });
18299
+ }
18300
+ continue;
18301
+ }
18302
+ if (c === "'") {
18303
+ i++;
18304
+ let result = "";
18305
+ while (i < n) {
18306
+ if (src[i] === "\\") {
18307
+ i++;
18308
+ if (i < n) {
18309
+ const esc = src[i];
18310
+ if (esc === "'") result += "'";
18311
+ else if (esc === "\\") result += "\\";
18312
+ else if (esc === "n") result += "\n";
18313
+ else if (esc === "t") result += " ";
18314
+ else result += esc;
18315
+ i++;
18316
+ }
18317
+ } else if (src[i] === "'") {
18318
+ i++;
18319
+ break;
18320
+ } else {
18321
+ result += src[i];
18322
+ i++;
18323
+ }
18324
+ }
18325
+ tokens.push({ t: 14 /* Str */, v: result });
18326
+ continue;
18327
+ }
18328
+ if (c === "-") {
18329
+ const prev = tokens.length > 0 ? tokens[tokens.length - 1] : null;
18330
+ const afterValue = prev != null && (prev.t === 15 /* Num */ || prev.t === 14 /* Str */ || prev.t === 16 /* Ident */ || prev.t === 17 /* Type */ || prev.t === 2 /* RParen */ || prev.t === 4 /* RBrack */ || prev.t === 10 /* True */ || prev.t === 11 /* False */ || prev.t === 12 /* Null */ || prev.t === 18 /* StateVar */ || prev.t === 35 /* BuiltinCall */);
18331
+ if (!afterValue && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") ; else {
18332
+ tokens.push({ t: 21 /* Minus */ });
18333
+ i++;
18334
+ continue;
18335
+ }
18336
+ }
18337
+ const isDigit = c >= "0" && c <= "9";
18338
+ const isNegDigit = c === "-" && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9";
18339
+ if (isDigit || isNegDigit) {
18340
+ const start = i;
18341
+ if (src[i] === "-") i++;
18342
+ while (i < n && src[i] >= "0" && src[i] <= "9") i++;
18343
+ if (i < n && src[i] === "." && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") {
18344
+ i++;
18345
+ while (i < n && src[i] >= "0" && src[i] <= "9") i++;
18346
+ }
18347
+ if (i < n && (src[i] === "e" || src[i] === "E")) {
18348
+ i++;
18349
+ if (i < n && (src[i] === "+" || src[i] === "-")) i++;
18350
+ while (i < n && src[i] >= "0" && src[i] <= "9") i++;
18351
+ }
18352
+ tokens.push({ t: 15 /* Num */, v: +src.slice(start, i) });
18353
+ continue;
18354
+ }
18355
+ if (c === "$" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
18356
+ const start = i;
18357
+ i++;
18358
+ while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
18359
+ i++;
18360
+ tokens.push({ t: 18 /* StateVar */, v: src.slice(start, i) });
18361
+ continue;
18362
+ }
18363
+ const isAlpha = c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
18364
+ if (isAlpha) {
18365
+ const start = i;
18366
+ while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
18367
+ i++;
18368
+ const word = src.slice(start, i);
18369
+ if (word === "true") {
18370
+ tokens.push({ t: 10 /* True */ });
18371
+ continue;
18372
+ }
18373
+ if (word === "false") {
18374
+ tokens.push({ t: 11 /* False */ });
18375
+ continue;
18376
+ }
18377
+ if (word === "null") {
18378
+ tokens.push({ t: 12 /* Null */ });
18379
+ continue;
18380
+ }
18381
+ const kind = c >= "A" && c <= "Z" ? 17 /* Type */ : 16 /* Ident */;
18382
+ tokens.push({ t: kind, v: word });
18383
+ continue;
18384
+ }
18385
+ if (c === "@" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
18386
+ i++;
18387
+ const start = i;
18388
+ while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
18389
+ i++;
18390
+ tokens.push({ t: 35 /* BuiltinCall */, v: src.slice(start, i) });
18391
+ continue;
18392
+ }
18393
+ i++;
18394
+ }
18395
+ tokens.push({ t: 13 /* EOF */ });
18396
+ return tokens;
18397
+ }
18398
+
18399
+ // src/genui/lang/parser/types.ts
18400
+ function isElementNode(value) {
18401
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
18402
+ const node = value;
18403
+ return node.type === "element" && typeof node.typeName === "string" && typeof node.props === "object" && node.props !== null && typeof node.partial === "boolean";
18404
+ }
18405
+
18406
+ // src/genui/lang/parser/materialize.ts
18407
+ function containsDynamicValue(v) {
18408
+ if (v == null || typeof v !== "object") return false;
18409
+ if (isASTNode(v)) return true;
18410
+ if (Array.isArray(v)) return v.some(containsDynamicValue);
18411
+ if (isElementNode(v)) {
18412
+ return Object.values(v.props).some(containsDynamicValue);
18413
+ }
18414
+ const obj = v;
18415
+ return Object.values(obj).some(containsDynamicValue);
18416
+ }
18417
+ function resolveRef(name, ctx, mode) {
18418
+ if (ctx.visited.has(name)) {
18419
+ ctx.unres.push(name);
18420
+ return mode === "expr" ? { k: "Ph", n: name } : null;
18421
+ }
18422
+ if (!ctx.syms.has(name)) {
18423
+ ctx.unres.push(name);
18424
+ return mode === "expr" ? { k: "Ph", n: name } : null;
18425
+ }
18426
+ const target = ctx.syms.get(name);
18427
+ ctx.unreached?.delete(name);
18428
+ if (target.k === "Comp" && isReservedCall(target.name)) {
18429
+ const refType = target.name === RESERVED_CALLS.Mutation ? "mutation" : "query";
18430
+ return { k: "RuntimeRef", n: name, refType };
18431
+ }
18432
+ ctx.visited.add(name);
18433
+ const prevStatementId = ctx.currentStatementId;
18434
+ ctx.currentStatementId = name;
18435
+ try {
18436
+ const result = mode === "value" ? materializeValue(target, ctx) : materializeExpr(target, ctx);
18437
+ if (mode === "value" && isElementNode(result)) {
18438
+ result.statementId = name;
18439
+ }
18440
+ return result;
18441
+ } finally {
18442
+ ctx.currentStatementId = prevStatementId;
18443
+ ctx.visited.delete(name);
18444
+ }
18445
+ }
18446
+ function materializeLazyBuiltin(node, ctx, scopedRefs) {
18447
+ if (!LAZY_BUILTINS.has(node.name) || node.args.length < 3) return null;
18448
+ const varArg = node.args[1];
18449
+ const varName = varArg.k === "Ref" ? varArg.n : varArg.k === "Str" ? varArg.v : null;
18450
+ if (!varName) return null;
18451
+ const nextScopedRefs = new Set(scopedRefs);
18452
+ nextScopedRefs.add(varName);
18453
+ const recursedArgs = node.args.map(
18454
+ (a, i) => i === 1 ? a : materializeExprInternal(a, ctx, nextScopedRefs)
18455
+ );
18456
+ return { ...node, args: recursedArgs };
18457
+ }
18458
+ function materializeExprInternal(node, ctx, scopedRefs) {
18459
+ switch (node.k) {
18460
+ case "Ref":
18461
+ return scopedRefs.has(node.n) ? node : resolveRef(node.n, ctx, "expr");
18462
+ case "Ph":
18463
+ return node;
18464
+ case "Comp": {
18465
+ const lazy2 = materializeLazyBuiltin(node, ctx, scopedRefs);
18466
+ if (lazy2) return lazy2;
18467
+ const recursedArgs = node.args.map((a) => materializeExprInternal(a, ctx, scopedRefs));
18468
+ if (isBuiltin(node.name) || isReservedCall(node.name)) {
18469
+ return { ...node, args: recursedArgs };
18470
+ }
18471
+ const def = ctx.cat?.get(node.name);
18472
+ if (def) {
18473
+ const mappedProps = {};
18474
+ for (let i = 0; i < def.params.length && i < recursedArgs.length; i++) {
18475
+ mappedProps[def.params[i].name] = recursedArgs[i];
18476
+ }
18477
+ return { ...node, args: recursedArgs, mappedProps };
18478
+ }
18479
+ ctx.errors.push({
18480
+ code: "unknown-component",
18481
+ component: node.name,
18482
+ path: "",
18483
+ message: `Unknown component "${node.name}" \u2014 not found in catalog or builtins`,
18484
+ statementId: ctx.currentStatementId
18485
+ });
18486
+ return { ...node, args: recursedArgs };
18487
+ }
18488
+ case "Arr":
18489
+ return { ...node, els: node.els.map((e) => materializeExprInternal(e, ctx, scopedRefs)) };
18490
+ case "Obj":
18491
+ return {
18492
+ ...node,
18493
+ entries: node.entries.map(
18494
+ ([k, v]) => [k, materializeExprInternal(v, ctx, scopedRefs)]
18495
+ )
18496
+ };
18497
+ case "BinOp":
18498
+ return {
18499
+ ...node,
18500
+ left: materializeExprInternal(node.left, ctx, scopedRefs),
18501
+ right: materializeExprInternal(node.right, ctx, scopedRefs)
18502
+ };
18503
+ case "UnaryOp":
18504
+ return { ...node, operand: materializeExprInternal(node.operand, ctx, scopedRefs) };
18505
+ case "Ternary":
18506
+ return {
18507
+ ...node,
18508
+ cond: materializeExprInternal(node.cond, ctx, scopedRefs),
18509
+ then: materializeExprInternal(node.then, ctx, scopedRefs),
18510
+ else: materializeExprInternal(node.else, ctx, scopedRefs)
18511
+ };
18512
+ case "Member":
18513
+ return { ...node, obj: materializeExprInternal(node.obj, ctx, scopedRefs) };
18514
+ case "Index":
18515
+ return {
18516
+ ...node,
18517
+ obj: materializeExprInternal(node.obj, ctx, scopedRefs),
18518
+ index: materializeExprInternal(node.index, ctx, scopedRefs)
18519
+ };
18520
+ case "Assign":
18521
+ return { ...node, value: materializeExprInternal(node.value, ctx, scopedRefs) };
18522
+ // Literals, StateRef, RuntimeRef — pass through unchanged
18523
+ default:
18524
+ return node;
18525
+ }
18526
+ }
18527
+ function materializeExpr(node, ctx) {
18528
+ return materializeExprInternal(node, ctx, /* @__PURE__ */ new Set());
18529
+ }
18530
+ function materializeValue(node, ctx) {
18531
+ switch (node.k) {
18532
+ // ── Ref resolution ───────────────────────────────────────────────────
18533
+ case "Ref":
18534
+ return resolveRef(node.n, ctx, "value");
18535
+ // ── Literals → plain values ──────────────────────────────────────────
18536
+ case "Str":
18537
+ return node.v;
18538
+ case "Num":
18539
+ return node.v;
18540
+ case "Bool":
18541
+ return node.v;
18542
+ case "Null":
18543
+ return null;
18544
+ case "Ph":
18545
+ return null;
18546
+ // ── Collections ──────────────────────────────────────────────────────
18547
+ case "Arr": {
18548
+ const items = [];
18549
+ for (const e of node.els) {
18550
+ if (e.k === "Ph") continue;
18551
+ const value = materializeValue(e, ctx);
18552
+ if (value === null && (e.k === "Comp" || e.k === "Ref")) continue;
18553
+ items.push(value);
18554
+ }
18555
+ return items;
18556
+ }
18557
+ case "Obj": {
18558
+ const o = {};
18559
+ for (const [k, v] of node.entries) o[k] = materializeValue(v, ctx);
18560
+ return o;
18561
+ }
18562
+ // ── Component nodes ──────────────────────────────────────────────────
18563
+ case "Comp": {
18564
+ const { name, args } = node;
18565
+ if (isBuiltin(name)) {
18566
+ const lazy2 = materializeLazyBuiltin(node, ctx, /* @__PURE__ */ new Set());
18567
+ if (lazy2) return lazy2;
18568
+ return { ...node, args: args.map((a) => materializeExpr(a, ctx)) };
18569
+ }
18570
+ if (isReservedCall(name)) {
18571
+ ctx.errors.push({
18572
+ code: "inline-reserved",
18573
+ component: name,
18574
+ path: "",
18575
+ message: `${name}() must be declared as a top-level statement, not used inline as a value`,
18576
+ statementId: ctx.currentStatementId
18577
+ });
18578
+ return null;
18579
+ }
18580
+ const def = ctx.cat?.get(name);
18581
+ const props = {};
18582
+ if (def) {
18583
+ for (let i = 0; i < def.params.length && i < args.length; i++) {
18584
+ props[def.params[i].name] = materializeValue(args[i], ctx);
18585
+ }
18586
+ if (args.length > def.params.length) {
18587
+ const excessCount = args.length - def.params.length;
18588
+ ctx.errors.push({
18589
+ code: "excess-args",
18590
+ component: name,
18591
+ path: "",
18592
+ message: `${name} takes ${def.params.length} arg(s), got ${args.length} (${excessCount} excess dropped)`,
18593
+ statementId: ctx.currentStatementId
18594
+ });
18595
+ }
18596
+ const missingRequired = def.params.filter(
18597
+ (p) => p.required && (!(p.name in props) || props[p.name] === null)
18598
+ );
18599
+ if (missingRequired.length) {
18600
+ const stillInvalid = missingRequired.filter((p) => {
18601
+ if (p.defaultValue !== void 0) {
18602
+ props[p.name] = p.defaultValue;
18603
+ return false;
18604
+ }
18605
+ return true;
18606
+ });
18607
+ if (stillInvalid.length) {
18608
+ for (const p of stillInvalid) {
18609
+ const isNull = p.name in props;
18610
+ ctx.errors.push({
18611
+ code: isNull ? "null-required" : "missing-required",
18612
+ component: name,
18613
+ path: `/${p.name}`,
18614
+ message: isNull ? `required field "${p.name}" cannot be null` : `missing required field "${p.name}"`,
18615
+ statementId: ctx.currentStatementId
18616
+ });
18617
+ }
18618
+ return null;
18619
+ }
18620
+ }
18621
+ } else if (!isBuiltin(name) && !isReservedCall(name)) {
18622
+ ctx.errors.push({
18623
+ code: "unknown-component",
18624
+ component: name,
18625
+ path: "",
18626
+ message: `Unknown component "${name}" \u2014 not found in catalog or builtins`,
18627
+ statementId: ctx.currentStatementId
18628
+ });
18629
+ return null;
18630
+ }
18631
+ const hasDynamicProps = Object.values(props).some((v) => containsDynamicValue(v));
18632
+ return { type: "element", typeName: name, props, partial: ctx.partial, hasDynamicProps };
18633
+ }
18634
+ // ── Runtime expression nodes → preserve as ASTNode, normalize children ─
18635
+ default: {
18636
+ if (isRuntimeExpr(node)) {
18637
+ return materializeExpr(node, ctx);
18638
+ }
18639
+ return node;
18640
+ }
18641
+ }
18642
+ }
18643
+
18644
+ // src/genui/lang/parser/statements.ts
18645
+ function autoClose(input) {
18646
+ const stack = [];
18647
+ let inStr = false;
18648
+ let esc = false;
18649
+ for (let i = 0; i < input.length; i++) {
18650
+ const c = input[i];
18651
+ if (esc) {
18652
+ esc = false;
18653
+ continue;
18654
+ }
18655
+ if (c === "\\" && inStr) {
18656
+ esc = true;
18657
+ continue;
18658
+ }
18659
+ if (inStr) {
18660
+ if (c === inStr) inStr = false;
18661
+ continue;
18662
+ }
18663
+ if (c === '"' || c === "'") {
18664
+ inStr = c;
18665
+ continue;
18666
+ }
18667
+ if (c === "(" || c === "[" || c === "{") stack.push(c);
18668
+ else if (c === ")" && stack[stack.length - 1] === "(") stack.pop();
18669
+ else if (c === "]" && stack[stack.length - 1] === "[") stack.pop();
18670
+ else if (c === "}" && stack[stack.length - 1] === "{") stack.pop();
18671
+ }
18672
+ const wasIncomplete = !!inStr || stack.length > 0;
18673
+ if (!wasIncomplete) return { text: input, wasIncomplete: false };
18674
+ let out = input;
18675
+ if (inStr) {
18676
+ if (esc) out += "\\";
18677
+ out += inStr;
18678
+ }
18679
+ for (let j = stack.length - 1; j >= 0; j--)
18680
+ out += stack[j] === "(" ? ")" : stack[j] === "[" ? "]" : "}";
18681
+ return { text: out, wasIncomplete: true };
18682
+ }
18683
+ function split(tokens) {
18684
+ const stmts = [];
18685
+ let pos = 0;
18686
+ while (pos < tokens.length) {
18687
+ while (pos < tokens.length && tokens[pos].t === 0 /* Newline */) pos++;
18688
+ if (pos >= tokens.length || tokens[pos].t === 13 /* EOF */) break;
18689
+ const tok = tokens[pos];
18690
+ if (tok.t !== 16 /* Ident */ && tok.t !== 17 /* Type */ && tok.t !== 18 /* StateVar */) {
18691
+ while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
18692
+ continue;
18693
+ }
18694
+ const id = tok.v;
18695
+ const idTokenType = tok.t;
18696
+ pos++;
18697
+ if (pos >= tokens.length || tokens[pos].t !== 9 /* Equals */) {
18698
+ while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
18699
+ continue;
18700
+ }
18701
+ pos++;
18702
+ const expr = [];
18703
+ let depth = 0;
18704
+ let ternaryDepth = 0;
18705
+ while (pos < tokens.length && tokens[pos].t !== 13 /* EOF */) {
18706
+ const tt = tokens[pos].t;
18707
+ if (tt === 0 /* Newline */ && depth <= 0 && ternaryDepth <= 0) {
18708
+ let peek = pos + 1;
18709
+ while (peek < tokens.length && tokens[peek].t === 0 /* Newline */) peek++;
18710
+ const nextT = peek < tokens.length ? tokens[peek].t : 13 /* EOF */;
18711
+ if (nextT === 34 /* Question */ || nextT === 8 /* Colon */ && ternaryDepth > 0) {
18712
+ pos++;
18713
+ continue;
18714
+ }
18715
+ break;
18716
+ }
18717
+ if (tt === 0 /* Newline */) {
18718
+ pos++;
18719
+ continue;
18720
+ }
18721
+ if (tt === 1 /* LParen */ || tt === 3 /* LBrack */ || tt === 5 /* LBrace */) depth++;
18722
+ else if ((tt === 2 /* RParen */ || tt === 4 /* RBrack */ || tt === 6 /* RBrace */) && depth > 0) depth--;
18723
+ else if (tt === 34 /* Question */ && depth === 0) ternaryDepth++;
18724
+ else if (tt === 8 /* Colon */ && depth === 0 && ternaryDepth > 0) ternaryDepth--;
18725
+ expr.push(tokens[pos++]);
18726
+ }
18727
+ if (expr.length) stmts.push({ id, idTokenType, tokens: expr });
18728
+ }
18729
+ return stmts;
18730
+ }
18731
+
18732
+ // src/genui/lang/parser/parser.ts
18733
+ function emptyResult(incomplete = true) {
18734
+ return {
18735
+ root: null,
18736
+ meta: {
18737
+ incomplete,
18738
+ unresolved: [],
18739
+ orphaned: [],
18740
+ statementCount: 0,
18741
+ errors: []
18742
+ },
18743
+ stateDeclarations: {},
18744
+ queryStatements: [],
18745
+ mutationStatements: []
18746
+ };
18747
+ }
18748
+ function collectQueryDeps(node) {
18749
+ if (!isASTNode(node)) return [];
18750
+ const refs = /* @__PURE__ */ new Set();
18751
+ walkAST(node, (current) => {
18752
+ if (current.k === "StateRef") refs.add(current.n);
18753
+ });
18754
+ return [...refs];
18755
+ }
18756
+ function classifyStatement(raw, expr) {
18757
+ if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Query) {
18758
+ const deps = collectQueryDeps(expr.args[1]);
18759
+ return {
18760
+ kind: "query",
18761
+ id: raw.id,
18762
+ call: { callee: RESERVED_CALLS.Query, args: expr.args },
18763
+ expr,
18764
+ deps: deps.length > 0 ? deps : void 0
18765
+ };
18766
+ }
18767
+ if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Mutation) {
18768
+ return {
18769
+ kind: "mutation",
18770
+ id: raw.id,
18771
+ call: { callee: RESERVED_CALLS.Mutation, args: expr.args },
18772
+ expr
18773
+ };
18774
+ }
18775
+ if (raw.idTokenType === 18 /* StateVar */) {
18776
+ return { kind: "state", id: raw.id, init: expr };
18777
+ }
18778
+ return { kind: "value", id: raw.id, expr };
18779
+ }
18780
+ function extractStatements(stmts, ctx) {
18781
+ const stateDeclarations = {};
18782
+ const queryStatements = [];
18783
+ const mutationStatements = [];
18784
+ for (const stmt of stmts) {
18785
+ switch (stmt.kind) {
18786
+ case "state":
18787
+ stateDeclarations[stmt.id] = materializeValue(stmt.init, ctx);
18788
+ break;
18789
+ case "query":
18790
+ queryStatements.push({
18791
+ statementId: stmt.id,
18792
+ toolAST: stmt.call.args[0] ?? null,
18793
+ argsAST: stmt.call.args[1] ?? null,
18794
+ defaultsAST: stmt.call.args[2] ?? null,
18795
+ refreshAST: stmt.call.args[3] ?? null,
18796
+ deps: stmt.deps,
18797
+ complete: true
18798
+ });
18799
+ break;
18800
+ case "mutation":
18801
+ mutationStatements.push({
18802
+ statementId: stmt.id,
18803
+ toolAST: stmt.call.args[0] ?? null,
18804
+ argsAST: stmt.call.args[1] ?? null
18805
+ });
18806
+ break;
18807
+ }
18808
+ }
18809
+ for (const stmt of stmts) {
18810
+ const nodes = stmt.kind === "state" ? [stmt.init] : stmt.kind === "value" ? [stmt.expr] : stmt.kind === "query" || stmt.kind === "mutation" ? stmt.call.args : [];
18811
+ for (const node of nodes) {
18812
+ for (const dep of collectQueryDeps(node)) {
18813
+ if (!(dep in stateDeclarations)) {
18814
+ stateDeclarations[dep] = null;
18815
+ }
18816
+ }
18817
+ }
18818
+ }
18819
+ return { stateDeclarations, queryStatements, mutationStatements };
18820
+ }
18821
+ var DEFAULT_ROOT_STATEMENT_ID = "root";
18822
+ function isComponentStatement(stmt) {
18823
+ return stmt.kind === "value" && stmt.expr.k === "Comp" && !isBuiltin(stmt.expr.name) && stmt.expr.name !== RESERVED_CALLS.Query && stmt.expr.name !== RESERVED_CALLS.Mutation;
18824
+ }
18825
+ function pickEntryId(stmtMap, typedStmts, firstId, rootName) {
18826
+ if (stmtMap.has(DEFAULT_ROOT_STATEMENT_ID)) return DEFAULT_ROOT_STATEMENT_ID;
18827
+ if (rootName && stmtMap.has(rootName)) return rootName;
18828
+ const preferredComponent = rootName ? typedStmts.find((stmt) => isComponentStatement(stmt) && stmt.expr.name === rootName) : void 0;
18829
+ if (preferredComponent) return preferredComponent.id;
18830
+ const firstComponent = typedStmts.find(isComponentStatement);
18831
+ return firstComponent?.id ?? firstId;
18832
+ }
18833
+ function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat, rootName) {
18834
+ const entryId = pickEntryId(stmtMap, typedStmts, firstId, rootName);
18835
+ if (!stmtMap.has(entryId)) return emptyResult(wasIncomplete);
18836
+ const syms = /* @__PURE__ */ new Map();
18837
+ for (const [id, stmt] of stmtMap) {
18838
+ syms.set(id, stmt.kind === "state" ? stmt.init : stmt.expr);
18839
+ }
18840
+ const unres = [];
18841
+ const errors = [];
18842
+ const unreached = /* @__PURE__ */ new Set();
18843
+ for (const [id, stmt] of stmtMap) {
18844
+ if (id === entryId) continue;
18845
+ if (stmt.kind === "state" || stmt.kind === "query" || stmt.kind === "mutation") continue;
18846
+ unreached.add(id);
18847
+ }
18848
+ const ctx = {
18849
+ syms,
18850
+ cat,
18851
+ errors,
18852
+ unres,
18853
+ visited: /* @__PURE__ */ new Set(),
18854
+ partial: wasIncomplete,
18855
+ currentStatementId: entryId,
18856
+ unreached
18857
+ };
18858
+ const materialized = materializeValue(syms.get(entryId), ctx);
18859
+ const root = isElementNode(materialized) ? materialized : null;
18860
+ if (root) root.statementId = entryId;
18861
+ const { stateDeclarations, queryStatements, mutationStatements } = extractStatements(
18862
+ typedStmts,
18863
+ ctx
18864
+ );
18865
+ const orphaned = [...unreached];
18866
+ return {
18867
+ root,
18868
+ meta: {
18869
+ incomplete: wasIncomplete,
18870
+ unresolved: unres,
18871
+ orphaned,
18872
+ statementCount: stmtCount,
18873
+ errors
18874
+ },
18875
+ stateDeclarations,
18876
+ queryStatements,
18877
+ mutationStatements
18878
+ };
18879
+ }
18880
+ function skipString(input, start) {
18881
+ if (input[start] !== '"') return start;
18882
+ let i = start + 1;
18883
+ while (i < input.length) {
18884
+ const c = input[i];
18885
+ if (c === "\\") {
18886
+ i += 2;
18887
+ } else if (c === '"') {
18888
+ return i + 1;
18889
+ } else {
18890
+ i++;
18891
+ }
18892
+ }
18893
+ return i;
18894
+ }
18895
+ function stripFences(input) {
18896
+ const blocks = [];
18897
+ let i = 0;
18898
+ while (i < input.length) {
18899
+ let fenceStart = -1;
18900
+ while (i < input.length) {
18901
+ const nextI = skipString(input, i);
18902
+ if (nextI > i) {
18903
+ i = nextI;
18904
+ continue;
18905
+ }
18906
+ const c = input[i];
18907
+ if (c === "`" && i + 1 < input.length && input[i + 1] === "`" && i + 2 < input.length && input[i + 2] === "`") {
18908
+ fenceStart = i;
18909
+ break;
18910
+ }
18911
+ i++;
18912
+ }
18913
+ if (fenceStart === -1) break;
18914
+ let j = fenceStart + 3;
18915
+ while (j < input.length && input[j] !== "\n") j++;
18916
+ if (j >= input.length) {
18917
+ blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
18918
+ i = input.length;
18919
+ break;
18920
+ }
18921
+ j++;
18922
+ let closePos = -1;
18923
+ let k = j;
18924
+ while (k < input.length) {
18925
+ const nextK = skipString(input, k);
18926
+ if (nextK > k) {
18927
+ k = nextK;
18928
+ continue;
18929
+ }
18930
+ const c = input[k];
18931
+ if (c === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
18932
+ closePos = k;
18933
+ break;
18934
+ }
18935
+ k++;
18936
+ }
18937
+ if (closePos !== -1) {
18938
+ blocks.push(input.slice(j, closePos));
18939
+ i = closePos + 3;
18940
+ } else {
18941
+ blocks.push(input.slice(j));
18942
+ i = input.length;
18943
+ }
18944
+ }
18945
+ if (blocks.length > 0) return blocks.join("\n");
18946
+ if (input.startsWith("```")) {
18947
+ let j = 3;
18948
+ while (j < input.length && input[j] !== "\n") j++;
18949
+ const start = j < input.length ? j + 1 : 3;
18950
+ const body = input.slice(start);
18951
+ const trailingFence = body.lastIndexOf("```");
18952
+ if (trailingFence !== -1) {
18953
+ return body.slice(0, trailingFence);
18954
+ }
18955
+ return body;
18956
+ }
18957
+ return input;
18958
+ }
18959
+ function stripComments(input) {
18960
+ let inStr = false;
18961
+ return input.split("\n").map((line) => {
18962
+ for (let i = 0; i < line.length; i++) {
18963
+ const c = line[i];
18964
+ if (inStr) {
18965
+ if (c === "\\" && i + 1 < line.length) {
18966
+ i++;
18967
+ continue;
18968
+ }
18969
+ if (c === inStr) inStr = false;
18970
+ continue;
18971
+ }
18972
+ if (c === '"' || c === "'") {
18973
+ inStr = c;
18974
+ continue;
18975
+ }
18976
+ if (c === "/" && line[i + 1] === "/") {
18977
+ return line.substring(0, i).trimEnd();
18978
+ }
18979
+ if (c === "#") {
18980
+ return line.substring(0, i).trimEnd();
18981
+ }
18982
+ }
18983
+ return line;
18984
+ }).join("\n");
18985
+ }
18986
+ function preprocess(input) {
18987
+ return stripComments(stripFences(input.trim())).trim();
18988
+ }
18989
+ function parse(input, cat, rootName) {
18990
+ const trimmed = preprocess(input);
18991
+ if (!trimmed) return emptyResult();
18992
+ const { text, wasIncomplete } = autoClose(trimmed);
18993
+ const stmts = split(tokenize(text));
18994
+ if (!stmts.length) return emptyResult(wasIncomplete);
18995
+ const stmtMap = /* @__PURE__ */ new Map();
18996
+ let firstId = "";
18997
+ for (const s of stmts) {
18998
+ const expr = parseExpression(s.tokens);
18999
+ const stmt = classifyStatement(s, expr);
19000
+ stmtMap.set(s.id, stmt);
19001
+ if (!firstId) firstId = s.id;
19002
+ }
19003
+ const typedStmts = [...stmtMap.values()];
19004
+ return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
19005
+ }
19006
+ function createStreamParser(cat, rootName) {
19007
+ let buf = "";
19008
+ let cleaned = "";
19009
+ let completedEnd = 0;
19010
+ const completedStmtMap = /* @__PURE__ */ new Map();
19011
+ let firstId = "";
19012
+ function addStmt(text) {
19013
+ const t = text.trim();
19014
+ if (!t) return;
19015
+ for (const s of split(tokenize(t))) {
19016
+ const expr = parseExpression(s.tokens);
19017
+ const stmt = classifyStatement(s, expr);
19018
+ completedStmtMap.set(s.id, stmt);
19019
+ if (!firstId) firstId = s.id;
19020
+ }
19021
+ }
19022
+ function refreshCleaned() {
19023
+ const next = preprocess(buf);
19024
+ if (!next.startsWith(cleaned.slice(0, completedEnd))) {
19025
+ completedEnd = 0;
19026
+ completedStmtMap.clear();
19027
+ firstId = "";
19028
+ }
19029
+ cleaned = next;
19030
+ }
19031
+ function scanNewCompleted() {
19032
+ let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
19033
+ let stmtStart = completedEnd;
19034
+ for (let i = completedEnd; i < cleaned.length; i++) {
19035
+ const c = cleaned[i];
19036
+ if (esc) {
19037
+ esc = false;
19038
+ continue;
19039
+ }
19040
+ if (c === "\\" && inStr) {
19041
+ esc = true;
19042
+ continue;
19043
+ }
19044
+ if (inStr) {
19045
+ if (c === inStr) inStr = false;
19046
+ continue;
19047
+ }
19048
+ if (c === '"' || c === "'") {
19049
+ inStr = c;
19050
+ continue;
19051
+ }
19052
+ if (c === "(" || c === "[" || c === "{") depth++;
19053
+ else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
19054
+ else if (c === "?" && depth === 0) ternaryDepth++;
19055
+ else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
19056
+ else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
19057
+ let peek = i + 1;
19058
+ while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n"))
19059
+ peek++;
19060
+ if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) {
19061
+ continue;
19062
+ }
19063
+ const t = cleaned.slice(stmtStart, i).trim();
19064
+ if (t) addStmt(t);
19065
+ stmtStart = i + 1;
19066
+ completedEnd = i + 1;
19067
+ }
19068
+ }
19069
+ return stmtStart;
19070
+ }
19071
+ function currentResult() {
19072
+ refreshCleaned();
19073
+ const pendingStart = scanNewCompleted();
19074
+ const pendingText = cleaned.slice(pendingStart).trim();
19075
+ if (!pendingText) {
19076
+ if (completedStmtMap.size === 0) return emptyResult();
19077
+ return buildResult(
19078
+ completedStmtMap,
19079
+ [...completedStmtMap.values()],
19080
+ firstId,
19081
+ false,
19082
+ completedStmtMap.size,
19083
+ cat,
19084
+ rootName
19085
+ );
19086
+ }
19087
+ const { text: closed, wasIncomplete } = autoClose(pendingText);
19088
+ const stmts = split(tokenize(closed));
19089
+ if (!stmts.length) {
19090
+ if (completedStmtMap.size === 0) return emptyResult(wasIncomplete);
19091
+ return buildResult(
19092
+ completedStmtMap,
19093
+ [...completedStmtMap.values()],
19094
+ firstId,
19095
+ wasIncomplete,
19096
+ completedStmtMap.size,
19097
+ cat,
19098
+ rootName
19099
+ );
19100
+ }
19101
+ const allStmtMap = new Map(completedStmtMap);
19102
+ for (const s of stmts) {
19103
+ const expr = parseExpression(s.tokens);
19104
+ const stmt = classifyStatement(s, expr);
19105
+ allStmtMap.set(s.id, stmt);
19106
+ }
19107
+ const allTypedStmts = [...allStmtMap.values()];
19108
+ const fid = firstId || stmts[0].id;
19109
+ return buildResult(
19110
+ allStmtMap,
19111
+ allTypedStmts,
19112
+ fid,
19113
+ wasIncomplete,
19114
+ allStmtMap.size,
19115
+ cat,
19116
+ rootName
19117
+ );
19118
+ }
19119
+ function reset() {
19120
+ buf = "";
19121
+ cleaned = "";
19122
+ completedEnd = 0;
19123
+ completedStmtMap.clear();
19124
+ firstId = "";
19125
+ }
19126
+ return {
19127
+ push(chunk) {
19128
+ buf += chunk;
19129
+ return currentResult();
19130
+ },
19131
+ set(fullText) {
19132
+ if (fullText.length < buf.length || !fullText.startsWith(buf)) {
19133
+ reset();
19134
+ }
19135
+ const delta = fullText.slice(buf.length);
19136
+ if (delta) buf += delta;
19137
+ return currentResult();
19138
+ },
19139
+ getResult: currentResult
19140
+ };
19141
+ }
19142
+ function useGenUIProgram(text, catalog) {
19143
+ const parserRef = useRef(
19144
+ null
19145
+ );
19146
+ if (!parserRef.current || parserRef.current.catalog !== catalog) {
19147
+ parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
19148
+ }
19149
+ const lastGoodRef = useRef(null);
19150
+ return useMemo(() => {
19151
+ const result = parserRef.current.parser.set(text);
19152
+ if (result.root) {
19153
+ lastGoodRef.current = result.root;
19154
+ }
19155
+ return { result, root: result.root ?? lastGoodRef.current };
19156
+ }, [text, catalog]);
19157
+ }
19158
+ var noop = () => {
19159
+ };
19160
+ function childKey(value, index) {
19161
+ if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
19162
+ return `i:${index}`;
19163
+ }
19164
+ var RenderDepthContext = createContext(0);
19165
+ var MAX_DEPTH = 64;
19166
+ var GenUINode = memo(function GenUINode2({
19167
+ node,
19168
+ components,
19169
+ onAction,
19170
+ isStreaming,
19171
+ onTelemetry
19172
+ }) {
19173
+ const depth = useContext(RenderDepthContext);
19174
+ if (depth > MAX_DEPTH) {
19175
+ onTelemetry?.({ kind: "dropped-node", typeName: node.typeName });
19176
+ return null;
19177
+ }
19178
+ const renderer = components[node.typeName];
19179
+ if (!renderer) {
19180
+ onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
19181
+ return null;
19182
+ }
19183
+ const render = (value) => {
19184
+ if (value == null) return null;
19185
+ if (isElementNode(value)) {
19186
+ return /* @__PURE__ */ jsx(RenderDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsx(
19187
+ GenUINode2,
19188
+ {
19189
+ node: value,
19190
+ components,
19191
+ onAction,
19192
+ isStreaming,
19193
+ onTelemetry
19194
+ }
19195
+ ) });
19196
+ }
19197
+ if (Array.isArray(value)) {
19198
+ return value.map((item, index) => /* @__PURE__ */ jsx(React14.Fragment, { children: render(item) }, childKey(item, index)));
19199
+ }
19200
+ if (typeof value === "object" && isRuntimeExpr(value)) return null;
19201
+ if (typeof value === "string" || typeof value === "number") return String(value);
19202
+ return null;
19203
+ };
19204
+ return renderer({ node, ctx: { render, onAction, isStreaming } });
19205
+ });
19206
+ function GenUIView({
19207
+ root,
19208
+ components,
19209
+ onAction = noop,
19210
+ isStreaming = false,
19211
+ onTelemetry,
19212
+ placeholder = null
19213
+ }) {
19214
+ if (!root) return placeholder;
19215
+ return /* @__PURE__ */ jsx(
19216
+ GenUINode,
19217
+ {
19218
+ node: root,
19219
+ components,
19220
+ onAction,
19221
+ isStreaming,
19222
+ onTelemetry
19223
+ }
19224
+ );
19225
+ }
19226
+
19227
+ // src/genui/registry.ts
19228
+ var CIMPLIFY_GENUI_REGISTRY = {
19229
+ version: "3",
19230
+ components: [
19231
+ {
19232
+ name: "Stack",
19233
+ description: 'Layout container. direction: "column" (default) or "row"; gap in points (default 12)',
19234
+ params: [{ name: "children", required: true }, { name: "direction" }, { name: "gap" }]
19235
+ },
19236
+ {
19237
+ name: "Card",
19238
+ description: "Elevated surface grouping related content, with an optional title",
19239
+ params: [{ name: "children", required: true }, { name: "title" }]
19240
+ },
19241
+ {
19242
+ name: "TextContent",
19243
+ description: 'Text block. size: "display", "heading", "title", "body" (default), "label", "caption"',
19244
+ params: [{ name: "text", required: true }, { name: "size" }]
19245
+ },
19246
+ {
19247
+ name: "Stat",
19248
+ description: "Compact stat: small label over a bold value, optional sub line and tone",
19249
+ params: [
19250
+ { name: "label", required: true },
19251
+ { name: "value", required: true },
19252
+ { name: "sub" },
19253
+ { name: "tone" }
19254
+ ]
19255
+ },
19256
+ {
19257
+ name: "Metric",
19258
+ description: "KPI card with label, value, optional percent change and SF Symbol icon",
19259
+ params: [
19260
+ { name: "label", required: true },
19261
+ { name: "value", required: true },
19262
+ { name: "change" },
19263
+ { name: "icon" }
19264
+ ]
19265
+ },
19266
+ {
19267
+ name: "KeyValue",
19268
+ description: "Label/value row; set mono true for numeric values",
19269
+ params: [
19270
+ { name: "label", required: true },
19271
+ { name: "value", required: true },
19272
+ { name: "mono" }
19273
+ ]
19274
+ },
19275
+ {
19276
+ name: "Badge",
19277
+ description: "Small status chip. tone: neutral, positive, info, indigo, violet, warning, danger",
19278
+ params: [{ name: "label", required: true }, { name: "tone" }]
19279
+ },
19280
+ {
19281
+ name: "Callout",
19282
+ description: "Inline alert with title, optional message. tone: info (default), success, warning, danger, accent, neutral",
19283
+ params: [{ name: "title", required: true }, { name: "message" }, { name: "tone" }]
19284
+ },
19285
+ {
19286
+ name: "Button",
19287
+ description: 'Action button. action: "action:name" string or Action([...]) steps. variant: primary, cta, secondary (default), ghost, destructive',
19288
+ params: [
19289
+ { name: "label", required: true },
19290
+ { name: "action", required: true },
19291
+ { name: "variant" }
19292
+ ]
19293
+ },
19294
+ {
19295
+ name: "Buttons",
19296
+ description: 'Row (default) or column of Buttons; direction: "row" or "column"',
19297
+ params: [{ name: "children", required: true }, { name: "direction" }]
19298
+ },
19299
+ {
19300
+ name: "Divider",
19301
+ description: "Hairline separator",
19302
+ params: []
19303
+ },
19304
+ {
19305
+ name: "Table",
19306
+ description: "Data table: headers is an array of strings, rows an array of cell arrays",
19307
+ params: [
19308
+ { name: "headers", required: true },
19309
+ { name: "rows", required: true }
19310
+ ]
19311
+ },
19312
+ {
19313
+ name: "BarChart",
19314
+ description: "Bar chart: labels (x axis strings) and values (numbers), optional title",
19315
+ params: [
19316
+ { name: "labels", required: true },
19317
+ { name: "values", required: true },
19318
+ { name: "title" }
19319
+ ]
19320
+ },
19321
+ {
19322
+ name: "LineChart",
19323
+ description: "Line chart with area fill: labels and values, optional title",
19324
+ params: [
19325
+ { name: "labels", required: true },
19326
+ { name: "values", required: true },
19327
+ { name: "title" }
19328
+ ]
19329
+ },
19330
+ {
19331
+ name: "Money",
19332
+ description: "Formatted money amount, never abbreviated. currency: ISO code (default USD)",
19333
+ params: [{ name: "amount", required: true }, { name: "currency" }]
19334
+ },
19335
+ {
19336
+ name: "Image",
19337
+ description: "Remote image by URL with optional caption",
19338
+ params: [{ name: "url", required: true }, { name: "caption" }]
19339
+ },
19340
+ {
19341
+ name: "ListRow",
19342
+ description: "Entity row for orders/customers/products/invoices: initials avatar (or SF Symbol tile via symbol), title + subtitle, trailing value and badge with tone",
19343
+ params: [
19344
+ { name: "title", required: true },
19345
+ { name: "subtitle" },
19346
+ { name: "value" },
19347
+ { name: "badge" },
19348
+ { name: "tone" },
19349
+ { name: "symbol" }
19350
+ ]
19351
+ },
19352
+ {
19353
+ name: "Timeline",
19354
+ description: "Event history (order journey, payment trail): events is an array of {title, detail?, time?, tone?} objects, newest first",
19355
+ params: [{ name: "events", required: true }]
19356
+ },
19357
+ {
19358
+ name: "Progress",
19359
+ description: "Amount toward a bound (goal, stock cover, capacity): numeric value and target (default 100), optional detail caption and tone",
19360
+ params: [
19361
+ { name: "label", required: true },
19362
+ { name: "value", required: true },
19363
+ { name: "target" },
19364
+ { name: "detail" },
19365
+ { name: "tone" }
19366
+ ]
19367
+ },
19368
+ {
19369
+ name: "DonutChart",
19370
+ description: "Share of a whole (sales by channel, revenue by category): labels + values, optional title and center display string (e.g. the formatted total)",
19371
+ params: [
19372
+ { name: "labels", required: true },
19373
+ { name: "values", required: true },
19374
+ { name: "title" },
19375
+ { name: "center" }
19376
+ ]
19377
+ },
19378
+ {
19379
+ name: "StatusTrack",
19380
+ description: "Stage tracker (order fulfilment, invoice lifecycle): steps are stage names, current is the active step name or index",
19381
+ params: [
19382
+ { name: "steps", required: true },
19383
+ { name: "current", required: true }
19384
+ ]
19385
+ },
19386
+ {
19387
+ name: "LineItems",
19388
+ 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",
19389
+ params: [
19390
+ { name: "items", required: true },
19391
+ { name: "total", required: true },
19392
+ { name: "adjustments" }
19393
+ ]
19394
+ },
19395
+ {
19396
+ name: "Grid",
19397
+ description: "Tile layout for Stat/Metric children, columns default 2",
19398
+ params: [{ name: "children", required: true }, { name: "columns" }]
19399
+ },
19400
+ {
19401
+ name: "Banner",
19402
+ description: "Hero-stat moment on a tonal wash: big value, label, optional percent delta. tone: positive (default), warning, danger, info, neutral",
19403
+ params: [
19404
+ { name: "value", required: true },
19405
+ { name: "label", required: true },
19406
+ { name: "delta" },
19407
+ { name: "tone" }
19408
+ ]
19409
+ },
19410
+ {
19411
+ name: "EmptyState",
19412
+ description: "Nothing-to-show state with an SF Symbol and optional message",
19413
+ params: [{ name: "title", required: true }, { name: "message" }, { name: "symbol" }]
19414
+ }
19415
+ ]
19416
+ };
19417
+
19418
+ // src/genui/manifest.ts
19419
+ function genUIManifest(registry) {
19420
+ return {
19421
+ version: registry.version,
19422
+ components: registry.components.map((component) => ({
19423
+ name: component.name,
19424
+ description: component.description,
19425
+ params: component.params.map((param) => ({
19426
+ name: param.name,
19427
+ required: param.required ?? false
19428
+ }))
19429
+ }))
19430
+ };
19431
+ }
19432
+ function encodeString(value) {
19433
+ return JSON.stringify(value).replace(/\//g, "\\/");
19434
+ }
19435
+ function canonicalize(value) {
19436
+ if (value === null) return "null";
19437
+ if (typeof value === "string") return encodeString(value);
19438
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
19439
+ if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
19440
+ const keys = Object.keys(value).sort();
19441
+ const entries = keys.map((key) => `${encodeString(key)}:${canonicalize(value[key])}`);
19442
+ return `{${entries.join(",")}}`;
19443
+ }
19444
+ function genUIManifestJson(manifest) {
19445
+ return canonicalize(manifest);
19446
+ }
19447
+ async function genUIFingerprint(manifest) {
19448
+ const bytes = new TextEncoder().encode(genUIManifestJson(manifest));
19449
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
19450
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
19451
+ }
19452
+
19453
+ export { AccommodationCard, AccountContent, AccountDashboardPage, AccountHero, AccountIdentityStrip, AccountLineItem, AccountLineItems, AccountNav, AccountOrderDetailPage, AccountOrderView, AccountOrdersPage, AccountShell, AccountSidebar, AccountSignedOutPrompt, AccountSubscriptionDetailPage, AccountSubscriptionsPage, Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, AvailabilityBadge, BackLink, BillingPlanSelector, BookingCard, BookingList, BookingPage, BookingSummary, BookingsPage, BoxIcon24, BundleProductCard, BundleProductLayout, BundleSelector, Button, CIMPLIFY_GENUI_REGISTRY, CardImage, CardShell, CardVariant, CartDrawer, CartDrawerProvider, CartPage, CartSummary, CartTemplate, CatalogueCollectionLayout, CataloguePage, CatalogueTemplate, CategoryFilter, CategoryGrid, ChatMarkdown, ChatWidget, CheckIcon, CheckoutPage, CimplifyCheckout, CimplifyProvider, CimplifySignInButton, CimplifyUserButton, CollectionPage, CollectionTemplate, CompactCartLayout, CompactCatalogueLayout, CompactSearchLayout, CompactServiceCard, CompositeProductCard, CompositeProductLayout, CompositeSelector, ContextCard, ContinuityCard, CurrencySelector, CustomAttributesTable, CustomerInputFields, DashboardIcon, DashboardIcon20, DatePicker, DateSlotPicker, DealBanner, DealsPage, DefaultCartLayout, DefaultCatalogueLayout, DefaultCollectionLayout, DefaultProductLayout, DefaultSearchLayout, DeliveryEstimate, DeliveryTracker, DeliveryTruckIcon, DigitalProductCard, DigitalProductLayout, DiscountInput, ElementsProvider, EmptyState, ErrorState, FeaturedCollectionLayout, FilterTabs, FoodProductCard, FoodProductLayout, GenUIView, HappeningCard, IdentityIcon, IdentityOfframp, InventoryBadge, ItemsCard, LeaseServiceCard, LineItemDetails, LocationPicker, MediaGallery, MetadataStringList, MobileAccountShell, MobileBackButton, MobileMoreButton, MobileTabBar, NextDeliveryCard, OrderDetailHeader, OrderDetailMetaPipe, OrderDetailPage, OrderHistory, OrderHistoryPage, OrderList, OrderRow, OrderSummary, OrderTimeline, OrderTotals, OrdersIcon, OrdersIcon20, OvenIcon, PackageIcon, PaymentElement, Price, PriceRange, ProductCard, ProductCustomizer, ProductGrid, ProductImageGallery, ProductModel3D, ProductPage, ProductSheet, ProductTemplate, PropertiesTable, QuantitySelector, QuickAddButton, RecentlyViewed, RecommendationCarousel, RelatedProductsSection, RentalServiceCard, ResourcePicker, RetailProductCard, RichText, SaleBadge, SalePrice, SaleTeaser, ScheduleServiceCard, SearchInput, SearchPage, SearchTemplate, Section, SectionMore, ServiceProductLayout, SessionMessageBanner, SignOutIcon, Skeleton, SlotPicker, SoldOutOverlay, StaffPicker, StandardServiceCard, Status, StoreNav, StoreVideo, SubscriptionCard, SubscriptionList, SubscriptionRow, SubscriptionsIcon, SubscriptionsIcon20, TagPills, TimePicker, Toast, TwoColumnGrid, VariantSelector, VolumePricing, WelcomeCard, WholesaleProductCard, WholesaleProductLayout, WishlistButton, clearOrdersCache, cn, defaultBoundaryKind, genUICatalog, genUIFingerprint, genUIManifest, genUIManifestJson, getBoundaryLabels, isTerminalOrderStatus, parse as parseGenUIProgram, partitionMessages, prefetchProduct, prefetchProducts, resolveBoundaryKind, roomToResource, sanitizeRichTextHtml, stripHtmlToText, toAccountLineItem, toAccountLineItems, toAccountOrderView, useActivityState, useAds, useAttributeDefinitions, useAvailableSlots, useBillingPlans, useBookings, useBootstrap, useBundles, useCart, useCartDrawer, useCategories, useChat, useCheckout, useCimplify, useCimplifyClient, useCimplifySession, useCollection, useCollections, useDeals, useDeliveryFee, useDeliveryOptions, useElements, useElementsReady, useFxRate, useGenUIProgram, useLocations, useOptionalCimplify, useOrder, useOrders, useProduct, useProductAvailability, useProductDeals, useProductPrice, useProductSchedules, useProducts, useProductsOnSale, usePropertyFacets, useQuote, useRecommendations, useSearch, useServiceAvailability, useServices, useStoreCredit, useSubscription, useSubscriptions, useTaxonomies, useTaxonomy, useTaxonomyPath, useValidateDiscount, useVariantSelector };