@cimplify/sdk 0.89.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.
- package/dist/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/{chunk-DENWOZEA.mjs → chunk-2UDEWILL.mjs} +46 -8
- package/dist/{chunk-T2CJ67QV.js → chunk-BN4V4ONE.js} +46 -8
- package/dist/{chunk-JHJTXAPS.js → chunk-JVWFIFQ7.js} +4 -4
- package/dist/{chunk-ROBTTS3Q.js → chunk-KJP4U6L3.js} +28 -1
- package/dist/{chunk-M4ALCELT.mjs → chunk-NGE37REW.mjs} +2 -2
- package/dist/{chunk-APBLFZ2I.mjs → chunk-NNGKC3MM.mjs} +28 -1
- package/dist/chunk-U4MW6PYU.mjs +23 -0
- package/dist/chunk-XREIZ56K.js +26 -0
- package/dist/{client-CqZgdUtw.d.ts → client-CR1f94wY.d.ts} +81 -6
- package/dist/{client-CUbwb6Om.d.ts → client-CqpOcbin.d.ts} +2 -2
- package/dist/{client-CAY2NbRG.d.mts → client-DMttx1xk.d.mts} +2 -2
- package/dist/{client-D8ZFs-sq.d.mts → client-YiSelM7x.d.mts} +81 -6
- package/dist/{index-Bc5qUxAt.d.ts → index-CxTVu0HZ.d.ts} +3 -3
- package/dist/{index-CmWOz9bi.d.mts → index-DD9z396M.d.mts} +3 -3
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +17 -10
- package/dist/index.mjs +8 -2
- package/dist/mock/cli.mjs +49 -1
- package/dist/mock/library.js +49 -1
- package/dist/mock/library.mjs +49 -1
- package/dist/mock/msw.js +49 -1
- package/dist/mock/msw.mjs +49 -1
- package/dist/{payment-Dcgb9n1n.d.mts → payment-5d8FTA1q.d.mts} +1 -1
- package/dist/{payment-Xq0WDZXh.d.ts → payment-BX7ccIKY.d.ts} +1 -1
- package/dist/{price-DAAjKgAp.d.ts → price-DtfP_Xfq.d.ts} +1 -1
- package/dist/{price--Ma_WO5_.d.mts → price-arl_4Rzn.d.mts} +1 -1
- package/dist/{product-BakHcsP2.d.mts → product-DZX4QuKS.d.mts} +57 -2
- package/dist/{product-BakHcsP2.d.ts → product-DZX4QuKS.d.ts} +57 -2
- package/dist/react.d.mts +405 -7
- package/dist/react.d.ts +405 -7
- package/dist/react.js +2325 -69
- package/dist/react.mjs +2303 -67
- package/dist/{server-Bk_hlefX.d.mts → server-BceZMxcK.d.mts} +8 -1
- package/dist/{server-Bei5lBrc.d.ts → server-DoJGSC7L.d.ts} +8 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/server.mjs +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +3 -2
- package/dist/testing/msw.mjs +2 -1
- package/dist/testing/suite.d.mts +6 -6
- package/dist/testing/suite.d.ts +6 -6
- package/dist/testing/suite.js +23 -22
- package/dist/testing/suite.mjs +4 -3
- package/dist/testing.d.mts +5 -5
- package/dist/testing.d.ts +5 -5
- package/dist/testing.js +79 -78
- package/dist/testing.mjs +5 -4
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/package.json +4 -1
- package/registry/chat-widget.json +1 -1
- package/registry/product-customizer.json +1 -1
package/dist/react.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var chunkT7Z25EY4_js = require('./chunk-T7Z25EY4.js');
|
|
5
|
-
var
|
|
5
|
+
var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
|
|
6
|
+
var chunkBN4V4ONE_js = require('./chunk-BN4V4ONE.js');
|
|
6
7
|
var chunkGHFDYM5K_js = require('./chunk-GHFDYM5K.js');
|
|
7
8
|
var chunkPTPOQVXS_js = require('./chunk-PTPOQVXS.js');
|
|
8
9
|
var chunkAETVRBKI_js = require('./chunk-AETVRBKI.js');
|
|
@@ -25,6 +26,8 @@ var xss = require('xss');
|
|
|
25
26
|
var dialog = require('@base-ui/react/dialog');
|
|
26
27
|
var field = require('@base-ui/react/field');
|
|
27
28
|
var input = require('@base-ui/react/input');
|
|
29
|
+
var DOMPurify = require('dompurify');
|
|
30
|
+
var marked = require('marked');
|
|
28
31
|
|
|
29
32
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
30
33
|
|
|
@@ -48,6 +51,7 @@ function _interopNamespace(e) {
|
|
|
48
51
|
|
|
49
52
|
var React14__default = /*#__PURE__*/_interopDefault(React14);
|
|
50
53
|
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
54
|
+
var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
|
|
51
55
|
|
|
52
56
|
// src/ads/identity.ts
|
|
53
57
|
var COOKIE_NAME = "_cimplify_uid";
|
|
@@ -790,15 +794,15 @@ function deriveView(base, ops) {
|
|
|
790
794
|
};
|
|
791
795
|
}
|
|
792
796
|
async function syncFromServer(entry) {
|
|
793
|
-
|
|
794
|
-
entry.syncing = true;
|
|
795
|
-
try {
|
|
797
|
+
const run = async () => {
|
|
796
798
|
const result = await entry.client.cart.get();
|
|
797
799
|
if (!result.ok) throw result.error;
|
|
798
800
|
entry.collection.setBase(mapServerCart(result.value, entry.currency));
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
|
|
801
|
+
};
|
|
802
|
+
const next = entry.syncTail.then(run, run);
|
|
803
|
+
entry.syncTail = next.catch(() => {
|
|
804
|
+
});
|
|
805
|
+
return next;
|
|
802
806
|
}
|
|
803
807
|
function emitMutationError(entry, operation, opId, idempotencyKey, error) {
|
|
804
808
|
entry.client.events.emit("mutation:error", {
|
|
@@ -838,7 +842,7 @@ function getOrCreateStore(params) {
|
|
|
838
842
|
optimisticEnabled,
|
|
839
843
|
initialized: false,
|
|
840
844
|
initializePromise: null,
|
|
841
|
-
|
|
845
|
+
syncTail: Promise.resolve(),
|
|
842
846
|
failedOpRemovers: /* @__PURE__ */ new Set()
|
|
843
847
|
};
|
|
844
848
|
cartStores.set(storeKey, entry);
|
|
@@ -1132,7 +1136,7 @@ var DEFAULT_COUNTRY = "US";
|
|
|
1132
1136
|
function createDefaultClient() {
|
|
1133
1137
|
const processRef = globalThis.process;
|
|
1134
1138
|
const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
|
|
1135
|
-
return
|
|
1139
|
+
return chunkBN4V4ONE_js.createCimplifyClient({ publicKey: envPublicKey });
|
|
1136
1140
|
}
|
|
1137
1141
|
function getStoredLocationId() {
|
|
1138
1142
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -4303,7 +4307,7 @@ function Price({
|
|
|
4303
4307
|
] });
|
|
4304
4308
|
}
|
|
4305
4309
|
function createCachedQuery(config) {
|
|
4306
|
-
const
|
|
4310
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
4307
4311
|
const inflight = /* @__PURE__ */ new Map();
|
|
4308
4312
|
const gcMaxAge = config.gcMaxAge ?? 3e5;
|
|
4309
4313
|
let lastSweep = 0;
|
|
@@ -4312,8 +4316,8 @@ function createCachedQuery(config) {
|
|
|
4312
4316
|
if (now - lastSweep < 6e4) return;
|
|
4313
4317
|
lastSweep = now;
|
|
4314
4318
|
const cutoff = now - gcMaxAge;
|
|
4315
|
-
for (const [key, entry] of
|
|
4316
|
-
if (entry.ts < cutoff)
|
|
4319
|
+
for (const [key, entry] of cache2) {
|
|
4320
|
+
if (entry.ts < cutoff) cache2.delete(key);
|
|
4317
4321
|
}
|
|
4318
4322
|
}
|
|
4319
4323
|
function useCachedQuery(options = {}) {
|
|
@@ -4331,13 +4335,13 @@ function createCachedQuery(config) {
|
|
|
4331
4335
|
const optionsRef = React14.useRef(options);
|
|
4332
4336
|
optionsRef.current = options;
|
|
4333
4337
|
if (config.locationSensitive && previousLocationRef.current !== locationId) {
|
|
4334
|
-
|
|
4338
|
+
cache2.clear();
|
|
4335
4339
|
inflight.clear();
|
|
4336
4340
|
previousLocationRef.current = locationId;
|
|
4337
4341
|
}
|
|
4338
4342
|
sweep();
|
|
4339
4343
|
const cacheKey2 = config.key(client, options);
|
|
4340
|
-
const cached =
|
|
4344
|
+
const cached = cache2.get(cacheKey2);
|
|
4341
4345
|
const [data, setData] = React14.useState(cached?.data ?? null);
|
|
4342
4346
|
const [isLoading, setIsLoading] = React14.useState(enabled && !cached);
|
|
4343
4347
|
const [error, setError] = React14.useState(null);
|
|
@@ -4350,7 +4354,7 @@ function createCachedQuery(config) {
|
|
|
4350
4354
|
const nextRequestId = ++requestIdRef.current;
|
|
4351
4355
|
setError(null);
|
|
4352
4356
|
if (!force) {
|
|
4353
|
-
const hit =
|
|
4357
|
+
const hit = cache2.get(cacheKey2);
|
|
4354
4358
|
if (hit) {
|
|
4355
4359
|
setData(hit.data);
|
|
4356
4360
|
setIsLoading(false);
|
|
@@ -4367,7 +4371,7 @@ function createCachedQuery(config) {
|
|
|
4367
4371
|
});
|
|
4368
4372
|
}
|
|
4369
4373
|
const value = await promise;
|
|
4370
|
-
|
|
4374
|
+
cache2.set(cacheKey2, { data: value, ts: Date.now() });
|
|
4371
4375
|
if (nextRequestId === requestIdRef.current) {
|
|
4372
4376
|
setData(value);
|
|
4373
4377
|
setError(null);
|
|
@@ -4388,23 +4392,23 @@ function createCachedQuery(config) {
|
|
|
4388
4392
|
void load(false);
|
|
4389
4393
|
}, [load]);
|
|
4390
4394
|
const refetch = React14.useCallback(async () => {
|
|
4391
|
-
|
|
4395
|
+
cache2.delete(cacheKey2);
|
|
4392
4396
|
await load(true);
|
|
4393
4397
|
}, [cacheKey2, load]);
|
|
4394
4398
|
return { data, isLoading, error, refetch };
|
|
4395
4399
|
}
|
|
4396
4400
|
useCachedQuery.prefetch = (client, options) => {
|
|
4397
4401
|
const key = config.key(client, options);
|
|
4398
|
-
if (
|
|
4402
|
+
if (cache2.has(key) || inflight.has(key)) return void 0;
|
|
4399
4403
|
const promise = config.fetch(client, options);
|
|
4400
4404
|
inflight.set(key, promise);
|
|
4401
4405
|
return promise.then((value) => {
|
|
4402
|
-
|
|
4406
|
+
cache2.set(key, { data: value, ts: Date.now() });
|
|
4403
4407
|
}).catch(() => {
|
|
4404
4408
|
}).finally(() => inflight.delete(key));
|
|
4405
4409
|
};
|
|
4406
4410
|
useCachedQuery.clear = () => {
|
|
4407
|
-
|
|
4411
|
+
cache2.clear();
|
|
4408
4412
|
inflight.clear();
|
|
4409
4413
|
};
|
|
4410
4414
|
return useCachedQuery;
|
|
@@ -4532,6 +4536,9 @@ function useCategories(options = {}) {
|
|
|
4532
4536
|
return { categories: data ?? [], isLoading, error, refetch };
|
|
4533
4537
|
}
|
|
4534
4538
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "refunded"]);
|
|
4539
|
+
function isTerminalOrderStatus(status) {
|
|
4540
|
+
return TERMINAL_STATUSES.has(status);
|
|
4541
|
+
}
|
|
4535
4542
|
var orderCache = /* @__PURE__ */ new Map();
|
|
4536
4543
|
var orderInflight = /* @__PURE__ */ new Map();
|
|
4537
4544
|
function buildOrderCacheKey(client, orderId) {
|
|
@@ -5373,6 +5380,9 @@ function useOrders(options = {}) {
|
|
|
5373
5380
|
const { data, isLoading, error, refetch } = ordersQuery(options);
|
|
5374
5381
|
return { orders: data ?? [], isLoading, error, refetch };
|
|
5375
5382
|
}
|
|
5383
|
+
function clearOrdersCache() {
|
|
5384
|
+
ordersQuery.clear();
|
|
5385
|
+
}
|
|
5376
5386
|
function useSubscriptions(options = {}) {
|
|
5377
5387
|
const { client } = useCimplifyClient();
|
|
5378
5388
|
const { status, enabled = true } = options;
|
|
@@ -5878,6 +5888,7 @@ var POLL_IDLE_INTERVAL_MS = 15e3;
|
|
|
5878
5888
|
var IDLE_TIMEOUT_MS = 6e4;
|
|
5879
5889
|
var WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
5880
5890
|
var WS_PATH = "/api/v1/support/conversation/ws";
|
|
5891
|
+
var AWAIT_REPLY_TIMEOUT_MS = 3e4;
|
|
5881
5892
|
function useChat(options = {}) {
|
|
5882
5893
|
const context = useOptionalCimplifyClient();
|
|
5883
5894
|
const client = options.client ?? context?.client;
|
|
@@ -5886,6 +5897,10 @@ function useChat(options = {}) {
|
|
|
5886
5897
|
const [isSending, setIsSending] = React14.useState(false);
|
|
5887
5898
|
const [error, setError] = React14.useState(null);
|
|
5888
5899
|
const [conversationOpened, setConversationOpened] = React14.useState(false);
|
|
5900
|
+
const [humanRequested, setHumanRequested] = React14.useState(false);
|
|
5901
|
+
const [awaitingReply, setAwaitingReply] = React14.useState(false);
|
|
5902
|
+
const [seenAt, setSeenAt] = React14.useState(null);
|
|
5903
|
+
const [mayHaveOlder, setMayHaveOlder] = React14.useState(false);
|
|
5889
5904
|
const pollRef = React14.useRef(null);
|
|
5890
5905
|
const lastActivityRef = React14.useRef(Date.now());
|
|
5891
5906
|
const openedRef = React14.useRef(false);
|
|
@@ -5894,17 +5909,42 @@ function useChat(options = {}) {
|
|
|
5894
5909
|
const wsRef = React14.useRef(null);
|
|
5895
5910
|
const wsReconnectAttemptRef = React14.useRef(0);
|
|
5896
5911
|
const wsReconnectTimerRef = React14.useRef(null);
|
|
5912
|
+
const aiEnabledRef = React14.useRef(false);
|
|
5913
|
+
const humanRequestedRef = React14.useRef(false);
|
|
5914
|
+
const awaitReplyTimerRef = React14.useRef(null);
|
|
5897
5915
|
messagesRef.current = messages;
|
|
5898
5916
|
clientRef.current = client;
|
|
5917
|
+
humanRequestedRef.current = humanRequested;
|
|
5899
5918
|
const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
|
|
5900
5919
|
const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
|
|
5901
|
-
const
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5920
|
+
const stopAwaitingReply = React14.useCallback(() => {
|
|
5921
|
+
if (awaitReplyTimerRef.current) {
|
|
5922
|
+
clearTimeout(awaitReplyTimerRef.current);
|
|
5923
|
+
awaitReplyTimerRef.current = null;
|
|
5924
|
+
}
|
|
5925
|
+
setAwaitingReply(false);
|
|
5926
|
+
}, []);
|
|
5927
|
+
const startAwaitingReply = React14.useCallback(() => {
|
|
5928
|
+
setAwaitingReply(true);
|
|
5929
|
+
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
5930
|
+
awaitReplyTimerRef.current = setTimeout(() => {
|
|
5931
|
+
awaitReplyTimerRef.current = null;
|
|
5932
|
+
setAwaitingReply(false);
|
|
5933
|
+
}, AWAIT_REPLY_TIMEOUT_MS);
|
|
5907
5934
|
}, []);
|
|
5935
|
+
const appendMessages = React14.useCallback(
|
|
5936
|
+
(incoming) => {
|
|
5937
|
+
if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
|
|
5938
|
+
stopAwaitingReply();
|
|
5939
|
+
}
|
|
5940
|
+
setMessages((prev) => {
|
|
5941
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
5942
|
+
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5943
|
+
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
5944
|
+
});
|
|
5945
|
+
},
|
|
5946
|
+
[stopAwaitingReply]
|
|
5947
|
+
);
|
|
5908
5948
|
const fetchNewMessages = React14.useCallback(async () => {
|
|
5909
5949
|
const c = clientRef.current;
|
|
5910
5950
|
if (!c) return;
|
|
@@ -5953,6 +5993,13 @@ function useChat(options = {}) {
|
|
|
5953
5993
|
const envelope = JSON.parse(event.data);
|
|
5954
5994
|
if (envelope.type === "message.created" && envelope.data) {
|
|
5955
5995
|
appendMessages([envelope.data]);
|
|
5996
|
+
} else if (envelope.type === "thread.read") {
|
|
5997
|
+
const at = envelope.data?.last_read_at;
|
|
5998
|
+
if (at) setSeenAt((prev) => !prev || prev < at ? at : prev);
|
|
5999
|
+
} else if (envelope.type === "thread.responding") {
|
|
6000
|
+
const active = envelope.data?.active;
|
|
6001
|
+
if (active) startAwaitingReply();
|
|
6002
|
+
else stopAwaitingReply();
|
|
5956
6003
|
} else if (envelope.type === "resync") {
|
|
5957
6004
|
const latest = envelope.latest_message_at;
|
|
5958
6005
|
const last = messagesRef.current[messagesRef.current.length - 1];
|
|
@@ -5983,6 +6030,7 @@ function useChat(options = {}) {
|
|
|
5983
6030
|
return () => {
|
|
5984
6031
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5985
6032
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6033
|
+
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
5986
6034
|
if (wsRef.current) {
|
|
5987
6035
|
wsRef.current.onclose = null;
|
|
5988
6036
|
wsRef.current.close();
|
|
@@ -5998,6 +6046,10 @@ function useChat(options = {}) {
|
|
|
5998
6046
|
if (result.ok) {
|
|
5999
6047
|
setMessages(result.value.messages);
|
|
6000
6048
|
setConversationOpened(true);
|
|
6049
|
+
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6050
|
+
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6051
|
+
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6052
|
+
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6001
6053
|
connectWebSocket();
|
|
6002
6054
|
} else {
|
|
6003
6055
|
openedRef.current = false;
|
|
@@ -6006,27 +6058,39 @@ function useChat(options = {}) {
|
|
|
6006
6058
|
setIsLoading(false);
|
|
6007
6059
|
}, [client, connectWebSocket]);
|
|
6008
6060
|
const send = React14.useCallback(
|
|
6009
|
-
async (content) => {
|
|
6010
|
-
|
|
6061
|
+
async (content, opts) => {
|
|
6062
|
+
const attachments = opts?.attachments ?? [];
|
|
6063
|
+
if (!client || !content.trim() && attachments.length === 0) return false;
|
|
6011
6064
|
setError(null);
|
|
6012
6065
|
lastActivityRef.current = Date.now();
|
|
6013
6066
|
if (!openedRef.current) {
|
|
6014
6067
|
await openConversation();
|
|
6015
|
-
if (!openedRef.current) return;
|
|
6068
|
+
if (!openedRef.current) return false;
|
|
6016
6069
|
}
|
|
6070
|
+
const replyTo = opts?.replyTo;
|
|
6017
6071
|
const optimisticId = `opt_${Date.now()}`;
|
|
6018
6072
|
const optimistic = {
|
|
6019
6073
|
id: optimisticId,
|
|
6020
6074
|
sender_type: "customer",
|
|
6021
6075
|
content: content.trim(),
|
|
6022
|
-
content_type: "text",
|
|
6023
|
-
attachments
|
|
6076
|
+
content_type: attachments.length > 0 ? "image" : "text",
|
|
6077
|
+
attachments,
|
|
6024
6078
|
metadata: {},
|
|
6079
|
+
// Same shape the server echoes back, so the bubble doesn't reflow
|
|
6080
|
+
// when the confirmed message replaces it.
|
|
6081
|
+
reply_to: replyTo ? {
|
|
6082
|
+
id: replyTo.id,
|
|
6083
|
+
sender_type: replyTo.sender_type,
|
|
6084
|
+
preview: chunkXREIZ56K_js.messagePreview(replyTo.content)
|
|
6085
|
+
} : void 0,
|
|
6025
6086
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6026
6087
|
};
|
|
6027
6088
|
setMessages((prev) => [...prev, optimistic]);
|
|
6028
6089
|
setIsSending(true);
|
|
6029
|
-
const result = await client.support.sendMessage(content.trim()
|
|
6090
|
+
const result = await client.support.sendMessage(content.trim(), {
|
|
6091
|
+
replyToMessageId: replyTo?.id,
|
|
6092
|
+
attachments: attachments.length > 0 ? attachments : void 0
|
|
6093
|
+
});
|
|
6030
6094
|
if (result.ok) {
|
|
6031
6095
|
const confirmed = result.value;
|
|
6032
6096
|
setMessages(
|
|
@@ -6037,13 +6101,17 @@ function useChat(options = {}) {
|
|
|
6037
6101
|
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== optimisticId) : prev.map((m) => m.id === optimisticId ? confirmed : m)
|
|
6038
6102
|
)
|
|
6039
6103
|
);
|
|
6104
|
+
if (aiEnabledRef.current && !humanRequestedRef.current) {
|
|
6105
|
+
startAwaitingReply();
|
|
6106
|
+
}
|
|
6040
6107
|
} else {
|
|
6041
6108
|
setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
|
|
6042
6109
|
setError(result.error);
|
|
6043
6110
|
}
|
|
6044
6111
|
setIsSending(false);
|
|
6112
|
+
return result.ok;
|
|
6045
6113
|
},
|
|
6046
|
-
[client, openConversation]
|
|
6114
|
+
[client, openConversation, startAwaitingReply]
|
|
6047
6115
|
);
|
|
6048
6116
|
const startConversation = React14.useCallback(
|
|
6049
6117
|
async (starterText) => {
|
|
@@ -6054,14 +6122,65 @@ function useChat(options = {}) {
|
|
|
6054
6122
|
},
|
|
6055
6123
|
[openConversation, send]
|
|
6056
6124
|
);
|
|
6125
|
+
const react = React14.useCallback(
|
|
6126
|
+
async (message, emoji) => {
|
|
6127
|
+
if (!client || message.id.startsWith("opt_")) return false;
|
|
6128
|
+
const result = await client.support.reactToMessage(message.id, emoji);
|
|
6129
|
+
if (result.ok) appendMessages([result.value]);
|
|
6130
|
+
return result.ok;
|
|
6131
|
+
},
|
|
6132
|
+
[client, appendMessages]
|
|
6133
|
+
);
|
|
6134
|
+
const requestHuman = React14.useCallback(async () => {
|
|
6135
|
+
if (!client) return false;
|
|
6136
|
+
setError(null);
|
|
6137
|
+
if (!openedRef.current) {
|
|
6138
|
+
await openConversation();
|
|
6139
|
+
if (!openedRef.current) return false;
|
|
6140
|
+
}
|
|
6141
|
+
const result = await client.support.requestHuman();
|
|
6142
|
+
if (result.ok) {
|
|
6143
|
+
setHumanRequested(true);
|
|
6144
|
+
return true;
|
|
6145
|
+
}
|
|
6146
|
+
setError(result.error);
|
|
6147
|
+
return false;
|
|
6148
|
+
}, [client, openConversation]);
|
|
6149
|
+
const loadOlder = React14.useCallback(async () => {
|
|
6150
|
+
const c = clientRef.current;
|
|
6151
|
+
const earliest = messagesRef.current[0];
|
|
6152
|
+
if (!c || !earliest) return false;
|
|
6153
|
+
const result = await c.support.getMessages({ before: earliest.created_at, limit: 50 });
|
|
6154
|
+
if (!result.ok) {
|
|
6155
|
+
setError(result.error);
|
|
6156
|
+
return false;
|
|
6157
|
+
}
|
|
6158
|
+
const older = result.value;
|
|
6159
|
+
setMayHaveOlder(older.length >= 50);
|
|
6160
|
+
if (older.length === 0) return false;
|
|
6161
|
+
setMessages((prev) => {
|
|
6162
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
6163
|
+
const fresh = older.filter((m) => !ids.has(m.id));
|
|
6164
|
+
return fresh.length > 0 ? [...fresh, ...prev] : prev;
|
|
6165
|
+
});
|
|
6166
|
+
return true;
|
|
6167
|
+
}, []);
|
|
6168
|
+
const agentArrived = messages.some((m) => m.sender_type === "agent");
|
|
6057
6169
|
return {
|
|
6058
6170
|
messages,
|
|
6059
6171
|
isLoading,
|
|
6060
6172
|
isSending,
|
|
6061
6173
|
error,
|
|
6062
6174
|
isActive: conversationOpened && messages.length > 0,
|
|
6175
|
+
humanRequested: humanRequested && !agentArrived,
|
|
6176
|
+
awaitingReply,
|
|
6177
|
+
seenAt,
|
|
6178
|
+
mayHaveOlder,
|
|
6063
6179
|
send,
|
|
6064
|
-
startConversation
|
|
6180
|
+
startConversation,
|
|
6181
|
+
react,
|
|
6182
|
+
requestHuman,
|
|
6183
|
+
loadOlder
|
|
6065
6184
|
};
|
|
6066
6185
|
}
|
|
6067
6186
|
var ElementsContext = React14.createContext({
|
|
@@ -9359,6 +9478,7 @@ function ProductCustomizer({
|
|
|
9359
9478
|
onAddToCart,
|
|
9360
9479
|
onVariantChange,
|
|
9361
9480
|
showSpecialInstructions = true,
|
|
9481
|
+
submitLabel,
|
|
9362
9482
|
className,
|
|
9363
9483
|
classNames
|
|
9364
9484
|
}) {
|
|
@@ -9747,8 +9867,10 @@ function ProductCustomizer({
|
|
|
9747
9867
|
isAdded && classNames?.submitButtonAdded,
|
|
9748
9868
|
classNames?.submitButton
|
|
9749
9869
|
),
|
|
9750
|
-
children: isAdded ? "Added
|
|
9751
|
-
"Add to Cart
|
|
9870
|
+
children: isAdded ? "Added" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9871
|
+
submitLabel ?? "Add to Cart",
|
|
9872
|
+
" \xB7",
|
|
9873
|
+
" ",
|
|
9752
9874
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: displayTotalPrice, currency })
|
|
9753
9875
|
] })
|
|
9754
9876
|
}
|
|
@@ -9908,13 +10030,13 @@ function StoreVideo({
|
|
|
9908
10030
|
autoplay = true,
|
|
9909
10031
|
loop = true,
|
|
9910
10032
|
muted = true,
|
|
9911
|
-
lazy = true,
|
|
10033
|
+
lazy: lazy2 = true,
|
|
9912
10034
|
className
|
|
9913
10035
|
}) {
|
|
9914
10036
|
const ref = React14.useRef(null);
|
|
9915
|
-
const [inView, setInView] = React14.useState(!
|
|
10037
|
+
const [inView, setInView] = React14.useState(!lazy2);
|
|
9916
10038
|
React14.useEffect(() => {
|
|
9917
|
-
if (!
|
|
10039
|
+
if (!lazy2 || inView) return;
|
|
9918
10040
|
const node = ref.current;
|
|
9919
10041
|
if (!node || typeof IntersectionObserver === "undefined") {
|
|
9920
10042
|
setInView(true);
|
|
@@ -9931,7 +10053,7 @@ function StoreVideo({
|
|
|
9931
10053
|
);
|
|
9932
10054
|
observer.observe(node);
|
|
9933
10055
|
return () => observer.disconnect();
|
|
9934
|
-
}, [
|
|
10056
|
+
}, [lazy2, inView]);
|
|
9935
10057
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9936
10058
|
"div",
|
|
9937
10059
|
{
|
|
@@ -9949,7 +10071,7 @@ function StoreVideo({
|
|
|
9949
10071
|
muted,
|
|
9950
10072
|
playsInline: true,
|
|
9951
10073
|
controls,
|
|
9952
|
-
preload:
|
|
10074
|
+
preload: lazy2 ? "metadata" : "auto",
|
|
9953
10075
|
"aria-label": alt,
|
|
9954
10076
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" },
|
|
9955
10077
|
children: poster ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -16751,6 +16873,112 @@ function CurrencySelector({
|
|
|
16751
16873
|
);
|
|
16752
16874
|
}) });
|
|
16753
16875
|
}
|
|
16876
|
+
var Streamdown = React14.lazy(() => import('streamdown').then((m) => ({ default: m.Streamdown })));
|
|
16877
|
+
function detectLookbehindSupport() {
|
|
16878
|
+
try {
|
|
16879
|
+
new RegExp("(?<=a)b");
|
|
16880
|
+
return true;
|
|
16881
|
+
} catch {
|
|
16882
|
+
return false;
|
|
16883
|
+
}
|
|
16884
|
+
}
|
|
16885
|
+
var supportsRegExpLookbehind = detectLookbehindSupport();
|
|
16886
|
+
marked.marked.setOptions({
|
|
16887
|
+
gfm: true,
|
|
16888
|
+
breaks: true
|
|
16889
|
+
});
|
|
16890
|
+
var ALLOWED_TAGS = [
|
|
16891
|
+
"a",
|
|
16892
|
+
"b",
|
|
16893
|
+
"blockquote",
|
|
16894
|
+
"br",
|
|
16895
|
+
"code",
|
|
16896
|
+
"del",
|
|
16897
|
+
"em",
|
|
16898
|
+
"h1",
|
|
16899
|
+
"h2",
|
|
16900
|
+
"h3",
|
|
16901
|
+
"h4",
|
|
16902
|
+
"hr",
|
|
16903
|
+
"i",
|
|
16904
|
+
"li",
|
|
16905
|
+
"ol",
|
|
16906
|
+
"p",
|
|
16907
|
+
"pre",
|
|
16908
|
+
"strong",
|
|
16909
|
+
"table",
|
|
16910
|
+
"tbody",
|
|
16911
|
+
"td",
|
|
16912
|
+
"th",
|
|
16913
|
+
"thead",
|
|
16914
|
+
"tr",
|
|
16915
|
+
"ul"
|
|
16916
|
+
];
|
|
16917
|
+
var ALLOWED_ATTR = ["href", "rel", "target", "title", "start"];
|
|
16918
|
+
var hooksInstalled = false;
|
|
16919
|
+
function installHooks() {
|
|
16920
|
+
if (hooksInstalled) return;
|
|
16921
|
+
hooksInstalled = true;
|
|
16922
|
+
DOMPurify__default.default.addHook("afterSanitizeAttributes", (node) => {
|
|
16923
|
+
if (!(node instanceof HTMLAnchorElement)) return;
|
|
16924
|
+
if (!node.getAttribute("href")) return;
|
|
16925
|
+
node.setAttribute("rel", "noreferrer noopener");
|
|
16926
|
+
node.setAttribute("target", "_blank");
|
|
16927
|
+
});
|
|
16928
|
+
}
|
|
16929
|
+
var CACHE_LIMIT = 300;
|
|
16930
|
+
var cache = /* @__PURE__ */ new Map();
|
|
16931
|
+
function renderMarkdown(text) {
|
|
16932
|
+
const cached = cache.get(text);
|
|
16933
|
+
if (cached !== void 0) return cached;
|
|
16934
|
+
installHooks();
|
|
16935
|
+
const html = DOMPurify__default.default.sanitize(marked.marked.parse(text), {
|
|
16936
|
+
ALLOWED_TAGS,
|
|
16937
|
+
ALLOWED_ATTR
|
|
16938
|
+
});
|
|
16939
|
+
if (cache.size >= CACHE_LIMIT) {
|
|
16940
|
+
const oldest = cache.keys().next().value;
|
|
16941
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
16942
|
+
}
|
|
16943
|
+
cache.set(text, html);
|
|
16944
|
+
return html;
|
|
16945
|
+
}
|
|
16946
|
+
function SettledMarkdown({ text, className }) {
|
|
16947
|
+
const html = React14.useMemo(() => renderMarkdown(text), [text]);
|
|
16948
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, dangerouslySetInnerHTML: { __html: html } });
|
|
16949
|
+
}
|
|
16950
|
+
var ChatMarkdown = React14.memo(function ChatMarkdown2({
|
|
16951
|
+
text,
|
|
16952
|
+
className,
|
|
16953
|
+
streaming = false
|
|
16954
|
+
}) {
|
|
16955
|
+
if (streaming && supportsRegExpLookbehind) {
|
|
16956
|
+
return (
|
|
16957
|
+
// While the streamdown chunk loads, the settled renderer keeps the
|
|
16958
|
+
// text on screen — no blank frame at stream start.
|
|
16959
|
+
/* @__PURE__ */ jsxRuntime.jsx(React14.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className }), children: /* @__PURE__ */ jsxRuntime.jsx(Streamdown, { className, children: text }) })
|
|
16960
|
+
);
|
|
16961
|
+
}
|
|
16962
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className });
|
|
16963
|
+
});
|
|
16964
|
+
|
|
16965
|
+
// src/react/chat-model.ts
|
|
16966
|
+
function partitionMessages(messages) {
|
|
16967
|
+
const bubbles = [];
|
|
16968
|
+
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
16969
|
+
for (const message of messages) {
|
|
16970
|
+
if (message.content_type === "reaction") {
|
|
16971
|
+
const targetId = message.reply_to?.id;
|
|
16972
|
+
if (!targetId) continue;
|
|
16973
|
+
const list = reactionsByTarget.get(targetId);
|
|
16974
|
+
if (list) list.push(message);
|
|
16975
|
+
else reactionsByTarget.set(targetId, [message]);
|
|
16976
|
+
continue;
|
|
16977
|
+
}
|
|
16978
|
+
bubbles.push(message);
|
|
16979
|
+
}
|
|
16980
|
+
return { bubbles, reactionsByTarget };
|
|
16981
|
+
}
|
|
16754
16982
|
function ChatWidget({
|
|
16755
16983
|
client: clientProp,
|
|
16756
16984
|
businessName = "Support",
|
|
@@ -16768,7 +16996,35 @@ function ChatWidget({
|
|
|
16768
16996
|
const client = clientProp ?? context?.client;
|
|
16769
16997
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
16770
16998
|
const [unread, setUnread] = React14.useState(0);
|
|
16771
|
-
const {
|
|
16999
|
+
const {
|
|
17000
|
+
messages,
|
|
17001
|
+
isLoading,
|
|
17002
|
+
isSending,
|
|
17003
|
+
awaitingReply,
|
|
17004
|
+
seenAt,
|
|
17005
|
+
error,
|
|
17006
|
+
isActive,
|
|
17007
|
+
send,
|
|
17008
|
+
react,
|
|
17009
|
+
startConversation
|
|
17010
|
+
} = useChat({ client, pollInterval });
|
|
17011
|
+
const showTyping = isSending || awaitingReply;
|
|
17012
|
+
const attachPhoto = React14.useCallback(
|
|
17013
|
+
async (file) => {
|
|
17014
|
+
if (!client || !file.type.startsWith("image/") || file.size > 5 * 1024 * 1024) return;
|
|
17015
|
+
const uploaded = await client.uploads.upload(file);
|
|
17016
|
+
if (!uploaded.ok) return;
|
|
17017
|
+
const attachment = {
|
|
17018
|
+
upload_id: uploaded.value.id,
|
|
17019
|
+
url: uploaded.value.url,
|
|
17020
|
+
mime_type: uploaded.value.content_type,
|
|
17021
|
+
filename: uploaded.value.filename,
|
|
17022
|
+
size: uploaded.value.size_bytes
|
|
17023
|
+
};
|
|
17024
|
+
await send("", { attachments: [attachment] });
|
|
17025
|
+
},
|
|
17026
|
+
[client, send]
|
|
17027
|
+
);
|
|
16772
17028
|
const prevCountRef = React14.useRef(messages.length);
|
|
16773
17029
|
React14.useEffect(() => {
|
|
16774
17030
|
if (!open && messages.length > prevCountRef.current) {
|
|
@@ -16815,11 +17071,15 @@ function ChatWidget({
|
|
|
16815
17071
|
messages,
|
|
16816
17072
|
isLoading,
|
|
16817
17073
|
isSending,
|
|
17074
|
+
isTyping: showTyping,
|
|
17075
|
+
seenAt,
|
|
16818
17076
|
error,
|
|
16819
17077
|
isActive,
|
|
16820
17078
|
isLeft,
|
|
16821
17079
|
onClose: handleClose,
|
|
16822
17080
|
onSend: send,
|
|
17081
|
+
onReact: react,
|
|
17082
|
+
onAttach: attachPhoto,
|
|
16823
17083
|
onStartConversation: startConversation,
|
|
16824
17084
|
classNames
|
|
16825
17085
|
}
|
|
@@ -16872,11 +17132,15 @@ function Panel({
|
|
|
16872
17132
|
messages,
|
|
16873
17133
|
isLoading,
|
|
16874
17134
|
isSending,
|
|
17135
|
+
isTyping,
|
|
17136
|
+
seenAt,
|
|
16875
17137
|
error,
|
|
16876
17138
|
isActive,
|
|
16877
17139
|
isLeft,
|
|
16878
17140
|
onClose,
|
|
16879
17141
|
onSend,
|
|
17142
|
+
onReact,
|
|
17143
|
+
onAttach,
|
|
16880
17144
|
onStartConversation,
|
|
16881
17145
|
classNames
|
|
16882
17146
|
}) {
|
|
@@ -16914,7 +17178,9 @@ function Panel({
|
|
|
16914
17178
|
{
|
|
16915
17179
|
messages,
|
|
16916
17180
|
isLoading,
|
|
16917
|
-
isSending,
|
|
17181
|
+
isSending: isTyping,
|
|
17182
|
+
seenAt,
|
|
17183
|
+
onReact,
|
|
16918
17184
|
className: classNames?.messages
|
|
16919
17185
|
}
|
|
16920
17186
|
),
|
|
@@ -16925,6 +17191,7 @@ function Panel({
|
|
|
16925
17191
|
placeholder,
|
|
16926
17192
|
isSending,
|
|
16927
17193
|
onSend,
|
|
17194
|
+
onAttach,
|
|
16928
17195
|
className: classNames?.input
|
|
16929
17196
|
}
|
|
16930
17197
|
),
|
|
@@ -17020,16 +17287,29 @@ function Welcome({
|
|
|
17020
17287
|
}
|
|
17021
17288
|
);
|
|
17022
17289
|
}
|
|
17290
|
+
var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
|
|
17291
|
+
function lastSeenCustomerMessageId(bubbles, seenAt) {
|
|
17292
|
+
if (!seenAt || !bubbles.some((m) => m.sender_type === "agent")) return null;
|
|
17293
|
+
for (let i = bubbles.length - 1; i >= 0; i--) {
|
|
17294
|
+
if (bubbles[i].sender_type !== "customer") continue;
|
|
17295
|
+
return bubbles[i].created_at <= seenAt ? bubbles[i].id : null;
|
|
17296
|
+
}
|
|
17297
|
+
return null;
|
|
17298
|
+
}
|
|
17023
17299
|
function MessageList({
|
|
17024
17300
|
messages,
|
|
17025
17301
|
isLoading,
|
|
17026
17302
|
isSending,
|
|
17303
|
+
seenAt,
|
|
17304
|
+
onReact,
|
|
17027
17305
|
className
|
|
17028
17306
|
}) {
|
|
17029
17307
|
const bottomRef = React14.useRef(null);
|
|
17030
17308
|
React14.useEffect(() => {
|
|
17031
17309
|
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
17032
17310
|
}, [messages.length, isSending]);
|
|
17311
|
+
const { bubbles, reactionsByTarget } = partitionMessages(messages);
|
|
17312
|
+
const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
|
|
17033
17313
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17034
17314
|
"div",
|
|
17035
17315
|
{
|
|
@@ -17039,40 +17319,111 @@ function MessageList({
|
|
|
17039
17319
|
),
|
|
17040
17320
|
children: [
|
|
17041
17321
|
isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17042
|
-
|
|
17322
|
+
bubbles.map((msg, i) => {
|
|
17043
17323
|
const isCustomer = msg.sender_type === "customer";
|
|
17044
|
-
const prev =
|
|
17324
|
+
const prev = bubbles[i - 1];
|
|
17045
17325
|
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
17046
17326
|
const isOptimistic = msg.id.startsWith("opt_");
|
|
17327
|
+
const reactions = reactionsByTarget.get(msg.id);
|
|
17047
17328
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
17048
17329
|
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
|
|
17049
|
-
/* @__PURE__ */ jsxRuntime.
|
|
17330
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17050
17331
|
"div",
|
|
17051
17332
|
{
|
|
17052
17333
|
className: cn(
|
|
17053
|
-
"flex max-w-[85%]",
|
|
17334
|
+
"group relative flex max-w-[85%]",
|
|
17054
17335
|
isCustomer ? "ml-auto" : "mr-auto"
|
|
17055
17336
|
),
|
|
17056
|
-
children:
|
|
17057
|
-
"div",
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17337
|
+
children: [
|
|
17338
|
+
!isCustomer && !isOptimistic && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(
|
|
17339
|
+
"button",
|
|
17340
|
+
{
|
|
17341
|
+
type: "button",
|
|
17342
|
+
"aria-label": `React ${emoji}`,
|
|
17343
|
+
onClick: () => void onReact(msg, emoji),
|
|
17344
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17345
|
+
children: emoji
|
|
17346
|
+
},
|
|
17347
|
+
emoji
|
|
17348
|
+
)) }),
|
|
17349
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17350
|
+
"div",
|
|
17351
|
+
{
|
|
17352
|
+
className: cn(
|
|
17353
|
+
"rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
|
|
17354
|
+
isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
|
|
17355
|
+
isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
|
|
17356
|
+
isOptimistic && "opacity-70"
|
|
17357
|
+
),
|
|
17358
|
+
children: [
|
|
17359
|
+
!isCustomer && !sameSenderAsPrev && /* @__PURE__ */ jsxRuntime.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: [
|
|
17360
|
+
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
17361
|
+
"AI"
|
|
17362
|
+
] }),
|
|
17363
|
+
msg.reply_to && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17364
|
+
"div",
|
|
17365
|
+
{
|
|
17366
|
+
className: cn(
|
|
17367
|
+
"mb-1.5 flex gap-1.5 rounded-lg px-2 py-1 text-xs",
|
|
17368
|
+
isCustomer ? "bg-background/15" : "bg-muted"
|
|
17369
|
+
),
|
|
17370
|
+
children: [
|
|
17371
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17372
|
+
"span",
|
|
17373
|
+
{
|
|
17374
|
+
className: cn(
|
|
17375
|
+
"w-[3px] shrink-0 self-stretch rounded-full",
|
|
17376
|
+
isCustomer ? "bg-background/40" : "bg-muted-foreground/40"
|
|
17377
|
+
)
|
|
17378
|
+
}
|
|
17379
|
+
),
|
|
17380
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate opacity-80", children: msg.reply_to.preview })
|
|
17381
|
+
]
|
|
17382
|
+
}
|
|
17383
|
+
),
|
|
17384
|
+
msg.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-wrap gap-1.5", msg.content && "mb-1.5"), children: msg.attachments.map(
|
|
17385
|
+
(attachment, index) => attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: attachment.url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17386
|
+
"img",
|
|
17387
|
+
{
|
|
17388
|
+
src: attachment.url,
|
|
17389
|
+
alt: attachment.filename ?? "Photo",
|
|
17390
|
+
loading: "lazy",
|
|
17391
|
+
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
17392
|
+
}
|
|
17393
|
+
) }, index) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17394
|
+
"a",
|
|
17395
|
+
{
|
|
17396
|
+
href: attachment.url,
|
|
17397
|
+
target: "_blank",
|
|
17398
|
+
rel: "noreferrer",
|
|
17399
|
+
className: "truncate text-xs underline",
|
|
17400
|
+
children: attachment.filename ?? "Attachment"
|
|
17401
|
+
},
|
|
17402
|
+
index
|
|
17403
|
+
)
|
|
17404
|
+
) }),
|
|
17405
|
+
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17406
|
+
ChatMarkdown,
|
|
17407
|
+
{
|
|
17408
|
+
text: msg.content,
|
|
17409
|
+
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"
|
|
17410
|
+
}
|
|
17411
|
+
)
|
|
17412
|
+
]
|
|
17413
|
+
}
|
|
17414
|
+
)
|
|
17415
|
+
]
|
|
17074
17416
|
}
|
|
17075
|
-
)
|
|
17417
|
+
),
|
|
17418
|
+
reactions && reactions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mt-0.5 flex gap-1", isCustomer ? "justify-end" : "justify-start"), children: reactions.map((reaction) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
17419
|
+
"span",
|
|
17420
|
+
{
|
|
17421
|
+
className: "rounded-full border border-border bg-background px-1.5 py-0.5 text-xs shadow-sm",
|
|
17422
|
+
children: reaction.content
|
|
17423
|
+
},
|
|
17424
|
+
reaction.id
|
|
17425
|
+
)) }),
|
|
17426
|
+
msg.id === seenMessageId && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-right text-[10px] font-medium text-muted-foreground", children: "Seen" })
|
|
17076
17427
|
] }, msg.id);
|
|
17077
17428
|
}),
|
|
17078
17429
|
isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
@@ -17085,10 +17436,24 @@ function ChatInput({
|
|
|
17085
17436
|
placeholder,
|
|
17086
17437
|
isSending,
|
|
17087
17438
|
onSend,
|
|
17439
|
+
onAttach,
|
|
17088
17440
|
className
|
|
17089
17441
|
}) {
|
|
17090
17442
|
const [value, setValue] = React14.useState("");
|
|
17443
|
+
const [uploading, setUploading] = React14.useState(false);
|
|
17091
17444
|
const textareaRef = React14.useRef(null);
|
|
17445
|
+
const fileRef = React14.useRef(null);
|
|
17446
|
+
const handleAttach = React14.useCallback(
|
|
17447
|
+
async (file) => {
|
|
17448
|
+
setUploading(true);
|
|
17449
|
+
try {
|
|
17450
|
+
await onAttach(file);
|
|
17451
|
+
} finally {
|
|
17452
|
+
setUploading(false);
|
|
17453
|
+
}
|
|
17454
|
+
},
|
|
17455
|
+
[onAttach]
|
|
17456
|
+
);
|
|
17092
17457
|
const handleSend = React14.useCallback(() => {
|
|
17093
17458
|
const trimmed = value.trim();
|
|
17094
17459
|
if (!trimmed || isSending) return;
|
|
@@ -17122,6 +17487,37 @@ function ChatInput({
|
|
|
17122
17487
|
className
|
|
17123
17488
|
),
|
|
17124
17489
|
children: [
|
|
17490
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17491
|
+
"input",
|
|
17492
|
+
{
|
|
17493
|
+
ref: fileRef,
|
|
17494
|
+
type: "file",
|
|
17495
|
+
accept: "image/*",
|
|
17496
|
+
className: "hidden",
|
|
17497
|
+
"aria-hidden": "true",
|
|
17498
|
+
tabIndex: -1,
|
|
17499
|
+
onChange: (e) => {
|
|
17500
|
+
const file = e.target.files?.[0];
|
|
17501
|
+
e.target.value = "";
|
|
17502
|
+
if (file) void handleAttach(file);
|
|
17503
|
+
}
|
|
17504
|
+
}
|
|
17505
|
+
),
|
|
17506
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17507
|
+
"button",
|
|
17508
|
+
{
|
|
17509
|
+
type: "button",
|
|
17510
|
+
"aria-label": "Attach a photo",
|
|
17511
|
+
disabled: uploading,
|
|
17512
|
+
onClick: () => fileRef.current?.click(),
|
|
17513
|
+
className: cn(
|
|
17514
|
+
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
17515
|
+
"bg-muted text-muted-foreground transition-colors hover:text-foreground",
|
|
17516
|
+
uploading && "animate-pulse"
|
|
17517
|
+
),
|
|
17518
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
|
|
17519
|
+
}
|
|
17520
|
+
),
|
|
17125
17521
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17126
17522
|
"div",
|
|
17127
17523
|
{
|
|
@@ -17224,6 +17620,25 @@ function SendIcon({ className }) {
|
|
|
17224
17620
|
}
|
|
17225
17621
|
);
|
|
17226
17622
|
}
|
|
17623
|
+
function ImageIcon({ className }) {
|
|
17624
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17625
|
+
"svg",
|
|
17626
|
+
{
|
|
17627
|
+
className,
|
|
17628
|
+
viewBox: "0 0 24 24",
|
|
17629
|
+
fill: "none",
|
|
17630
|
+
stroke: "currentColor",
|
|
17631
|
+
strokeWidth: 2,
|
|
17632
|
+
strokeLinecap: "round",
|
|
17633
|
+
strokeLinejoin: "round",
|
|
17634
|
+
children: [
|
|
17635
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
17636
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
17637
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
17638
|
+
]
|
|
17639
|
+
}
|
|
17640
|
+
);
|
|
17641
|
+
}
|
|
17227
17642
|
function SparkleIcon({ className }) {
|
|
17228
17643
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17229
17644
|
"svg",
|
|
@@ -17238,9 +17653,1838 @@ function SparkleIcon({ className }) {
|
|
|
17238
17653
|
);
|
|
17239
17654
|
}
|
|
17240
17655
|
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
|
|
17656
|
+
// src/react/genui/catalog.ts
|
|
17657
|
+
function genUICatalog(registry) {
|
|
17658
|
+
return new Map(
|
|
17659
|
+
registry.components.map((component) => [
|
|
17660
|
+
component.name,
|
|
17661
|
+
{
|
|
17662
|
+
params: component.params.map((param) => ({
|
|
17663
|
+
name: param.name,
|
|
17664
|
+
required: param.required ?? false
|
|
17665
|
+
}))
|
|
17666
|
+
}
|
|
17667
|
+
])
|
|
17668
|
+
);
|
|
17669
|
+
}
|
|
17670
|
+
|
|
17671
|
+
// src/genui/lang/parser/ast.ts
|
|
17672
|
+
function isRuntimeExpr(node) {
|
|
17673
|
+
switch (node.k) {
|
|
17674
|
+
case "StateRef":
|
|
17675
|
+
case "RuntimeRef":
|
|
17676
|
+
case "BinOp":
|
|
17677
|
+
case "UnaryOp":
|
|
17678
|
+
case "Ternary":
|
|
17679
|
+
case "Member":
|
|
17680
|
+
case "Index":
|
|
17681
|
+
case "Assign":
|
|
17682
|
+
return true;
|
|
17683
|
+
default:
|
|
17684
|
+
return false;
|
|
17685
|
+
}
|
|
17686
|
+
}
|
|
17687
|
+
var AST_KINDS = /* @__PURE__ */ new Set([
|
|
17688
|
+
"Comp",
|
|
17689
|
+
"Ref",
|
|
17690
|
+
"StateRef",
|
|
17691
|
+
"RuntimeRef",
|
|
17692
|
+
"BinOp",
|
|
17693
|
+
"UnaryOp",
|
|
17694
|
+
"Ternary",
|
|
17695
|
+
"Member",
|
|
17696
|
+
"Index",
|
|
17697
|
+
"Assign",
|
|
17698
|
+
"Str",
|
|
17699
|
+
"Num",
|
|
17700
|
+
"Bool",
|
|
17701
|
+
"Null",
|
|
17702
|
+
"Arr",
|
|
17703
|
+
"Obj",
|
|
17704
|
+
"Ph"
|
|
17705
|
+
]);
|
|
17706
|
+
function isASTNode(value) {
|
|
17707
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
17708
|
+
return AST_KINDS.has(value.k);
|
|
17709
|
+
}
|
|
17710
|
+
function walkAST(node, visit) {
|
|
17711
|
+
const walk = (current) => {
|
|
17712
|
+
visit(current);
|
|
17713
|
+
switch (current.k) {
|
|
17714
|
+
case "Comp":
|
|
17715
|
+
current.args.forEach(walk);
|
|
17716
|
+
Object.values(current.mappedProps ?? {}).forEach(walk);
|
|
17717
|
+
break;
|
|
17718
|
+
case "Arr":
|
|
17719
|
+
current.els.forEach(walk);
|
|
17720
|
+
break;
|
|
17721
|
+
case "Obj":
|
|
17722
|
+
current.entries.forEach(([, value]) => walk(value));
|
|
17723
|
+
break;
|
|
17724
|
+
case "BinOp":
|
|
17725
|
+
walk(current.left);
|
|
17726
|
+
walk(current.right);
|
|
17727
|
+
break;
|
|
17728
|
+
case "UnaryOp":
|
|
17729
|
+
walk(current.operand);
|
|
17730
|
+
break;
|
|
17731
|
+
case "Ternary":
|
|
17732
|
+
walk(current.cond);
|
|
17733
|
+
walk(current.then);
|
|
17734
|
+
walk(current.else);
|
|
17735
|
+
break;
|
|
17736
|
+
case "Member":
|
|
17737
|
+
walk(current.obj);
|
|
17738
|
+
break;
|
|
17739
|
+
case "Index":
|
|
17740
|
+
walk(current.obj);
|
|
17741
|
+
walk(current.index);
|
|
17742
|
+
break;
|
|
17743
|
+
case "Assign":
|
|
17744
|
+
walk(current.value);
|
|
17745
|
+
break;
|
|
17746
|
+
}
|
|
17747
|
+
};
|
|
17748
|
+
walk(node);
|
|
17749
|
+
}
|
|
17750
|
+
|
|
17751
|
+
// src/genui/lang/parser/builtins.ts
|
|
17752
|
+
function resolveField(obj, path) {
|
|
17753
|
+
if (!path || obj == null) return void 0;
|
|
17754
|
+
if (!path.includes(".")) return obj[path];
|
|
17755
|
+
let cur = obj;
|
|
17756
|
+
for (const p of path.split(".")) {
|
|
17757
|
+
if (cur == null) return void 0;
|
|
17758
|
+
cur = cur[p];
|
|
17759
|
+
}
|
|
17760
|
+
return cur;
|
|
17761
|
+
}
|
|
17762
|
+
function toNumber2(val) {
|
|
17763
|
+
if (typeof val === "number") return val;
|
|
17764
|
+
if (typeof val === "string") {
|
|
17765
|
+
const n = Number(val);
|
|
17766
|
+
return isNaN(n) ? 0 : n;
|
|
17767
|
+
}
|
|
17768
|
+
if (typeof val === "boolean") return val ? 1 : 0;
|
|
17769
|
+
return 0;
|
|
17770
|
+
}
|
|
17771
|
+
var BUILTINS = {
|
|
17772
|
+
Count: {
|
|
17773
|
+
name: "Count",
|
|
17774
|
+
signature: "Count(array) \u2192 number",
|
|
17775
|
+
description: "Returns array length",
|
|
17776
|
+
fn: (arr) => Array.isArray(arr) ? arr.length : 0
|
|
17777
|
+
},
|
|
17778
|
+
First: {
|
|
17779
|
+
name: "First",
|
|
17780
|
+
signature: "First(array) \u2192 element",
|
|
17781
|
+
description: "Returns first element of array",
|
|
17782
|
+
fn: (arr) => Array.isArray(arr) ? arr[0] ?? null : null
|
|
17783
|
+
},
|
|
17784
|
+
Last: {
|
|
17785
|
+
name: "Last",
|
|
17786
|
+
signature: "Last(array) \u2192 element",
|
|
17787
|
+
description: "Returns last element of array",
|
|
17788
|
+
fn: (arr) => Array.isArray(arr) ? arr[arr.length - 1] ?? null : null
|
|
17789
|
+
},
|
|
17790
|
+
Sum: {
|
|
17791
|
+
name: "Sum",
|
|
17792
|
+
signature: "Sum(numbers[]) \u2192 number",
|
|
17793
|
+
description: "Sum of numeric array",
|
|
17794
|
+
fn: (arr) => Array.isArray(arr) ? arr.reduce((a, b) => a + toNumber2(b), 0) : 0
|
|
17795
|
+
},
|
|
17796
|
+
Avg: {
|
|
17797
|
+
name: "Avg",
|
|
17798
|
+
signature: "Avg(numbers[]) \u2192 number",
|
|
17799
|
+
description: "Average of numeric array",
|
|
17800
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((a, b) => a + toNumber2(b), 0) / arr.length : 0
|
|
17801
|
+
},
|
|
17802
|
+
Min: {
|
|
17803
|
+
name: "Min",
|
|
17804
|
+
signature: "Min(numbers[]) \u2192 number",
|
|
17805
|
+
description: "Minimum value in array",
|
|
17806
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.min(acc, toNumber2(b)), toNumber2(arr[0])) : 0
|
|
17807
|
+
},
|
|
17808
|
+
Max: {
|
|
17809
|
+
name: "Max",
|
|
17810
|
+
signature: "Max(numbers[]) \u2192 number",
|
|
17811
|
+
description: "Maximum value in array",
|
|
17812
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.max(acc, toNumber2(b)), toNumber2(arr[0])) : 0
|
|
17813
|
+
},
|
|
17814
|
+
Sort: {
|
|
17815
|
+
name: "Sort",
|
|
17816
|
+
signature: "Sort(array, field, direction?) \u2192 sorted array",
|
|
17817
|
+
description: 'Sort array by field. Direction: "asc" (default) or "desc"',
|
|
17818
|
+
fn: (arr, field, dir) => {
|
|
17819
|
+
if (!Array.isArray(arr)) return arr;
|
|
17820
|
+
const f = String(field ?? "");
|
|
17821
|
+
const desc = String(dir ?? "asc") === "desc";
|
|
17822
|
+
return [...arr].sort((a, b) => {
|
|
17823
|
+
const av = f ? resolveField(a, f) : a;
|
|
17824
|
+
const bv = f ? resolveField(b, f) : b;
|
|
17825
|
+
const aIsNumeric = typeof av === "number" || typeof av === "string" && !isNaN(Number(av)) && av !== "";
|
|
17826
|
+
const bIsNumeric = typeof bv === "number" || typeof bv === "string" && !isNaN(Number(bv)) && bv !== "";
|
|
17827
|
+
if (aIsNumeric && bIsNumeric) {
|
|
17828
|
+
const diff = toNumber2(av) - toNumber2(bv);
|
|
17829
|
+
return desc ? -diff : diff;
|
|
17830
|
+
}
|
|
17831
|
+
const cmp = String(av ?? "").localeCompare(String(bv ?? ""));
|
|
17832
|
+
return desc ? -cmp : cmp;
|
|
17833
|
+
});
|
|
17834
|
+
}
|
|
17835
|
+
},
|
|
17836
|
+
Filter: {
|
|
17837
|
+
name: "Filter",
|
|
17838
|
+
signature: 'Filter(array, field, operator: "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains", value) \u2192 filtered array',
|
|
17839
|
+
description: "Filter array by field value",
|
|
17840
|
+
fn: (arr, field, op, value) => {
|
|
17841
|
+
if (!Array.isArray(arr)) return [];
|
|
17842
|
+
const f = String(field ?? "");
|
|
17843
|
+
const o = String(op ?? "==");
|
|
17844
|
+
return arr.filter((item) => {
|
|
17845
|
+
const v = f ? resolveField(item, f) : item;
|
|
17846
|
+
switch (o) {
|
|
17847
|
+
case "==":
|
|
17848
|
+
return v == value;
|
|
17849
|
+
case "!=":
|
|
17850
|
+
return v != value;
|
|
17851
|
+
case ">":
|
|
17852
|
+
return toNumber2(v) > toNumber2(value);
|
|
17853
|
+
case "<":
|
|
17854
|
+
return toNumber2(v) < toNumber2(value);
|
|
17855
|
+
case ">=":
|
|
17856
|
+
return toNumber2(v) >= toNumber2(value);
|
|
17857
|
+
case "<=":
|
|
17858
|
+
return toNumber2(v) <= toNumber2(value);
|
|
17859
|
+
case "contains":
|
|
17860
|
+
return String(v ?? "").includes(String(value ?? ""));
|
|
17861
|
+
default:
|
|
17862
|
+
return false;
|
|
17863
|
+
}
|
|
17864
|
+
});
|
|
17865
|
+
}
|
|
17866
|
+
},
|
|
17867
|
+
Round: {
|
|
17868
|
+
name: "Round",
|
|
17869
|
+
signature: "Round(number, decimals?) \u2192 number",
|
|
17870
|
+
description: "Round to N decimal places (default 0)",
|
|
17871
|
+
fn: (n, decimals) => {
|
|
17872
|
+
const num = toNumber2(n);
|
|
17873
|
+
const d = decimals != null ? toNumber2(decimals) : 0;
|
|
17874
|
+
const factor = Math.pow(10, d);
|
|
17875
|
+
return Math.round(num * factor) / factor;
|
|
17876
|
+
}
|
|
17877
|
+
},
|
|
17878
|
+
Abs: {
|
|
17879
|
+
name: "Abs",
|
|
17880
|
+
signature: "Abs(number) \u2192 number",
|
|
17881
|
+
description: "Absolute value",
|
|
17882
|
+
fn: (n) => Math.abs(toNumber2(n))
|
|
17883
|
+
},
|
|
17884
|
+
Floor: {
|
|
17885
|
+
name: "Floor",
|
|
17886
|
+
signature: "Floor(number) \u2192 number",
|
|
17887
|
+
description: "Round down to nearest integer",
|
|
17888
|
+
fn: (n) => Math.floor(toNumber2(n))
|
|
17889
|
+
},
|
|
17890
|
+
Ceil: {
|
|
17891
|
+
name: "Ceil",
|
|
17892
|
+
signature: "Ceil(number) \u2192 number",
|
|
17893
|
+
description: "Round up to nearest integer",
|
|
17894
|
+
fn: (n) => Math.ceil(toNumber2(n))
|
|
17895
|
+
}
|
|
17896
|
+
};
|
|
17897
|
+
var LAZY_BUILTINS = /* @__PURE__ */ new Set(["Each"]);
|
|
17898
|
+
var ACTION_STEPS = {
|
|
17899
|
+
Run: "run",
|
|
17900
|
+
ToAssistant: "continue_conversation",
|
|
17901
|
+
OpenUrl: "open_url",
|
|
17902
|
+
Set: "set",
|
|
17903
|
+
Reset: "reset"
|
|
17904
|
+
};
|
|
17905
|
+
var ACTION_NAMES = /* @__PURE__ */ new Set(["Action", ...Object.keys(ACTION_STEPS)]);
|
|
17906
|
+
var BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
17907
|
+
...Object.keys(BUILTINS),
|
|
17908
|
+
...LAZY_BUILTINS,
|
|
17909
|
+
...ACTION_NAMES
|
|
17910
|
+
]);
|
|
17911
|
+
function isBuiltin(name) {
|
|
17912
|
+
return BUILTIN_NAMES.has(name);
|
|
17913
|
+
}
|
|
17914
|
+
var RESERVED_CALLS = { Query: "Query", Mutation: "Mutation" };
|
|
17915
|
+
function isReservedCall(name) {
|
|
17916
|
+
return name in RESERVED_CALLS;
|
|
17917
|
+
}
|
|
17918
|
+
|
|
17919
|
+
// src/genui/lang/parser/expressions.ts
|
|
17920
|
+
var PREC_TERNARY = 1;
|
|
17921
|
+
var PREC_OR = 2;
|
|
17922
|
+
var PREC_AND = 3;
|
|
17923
|
+
var PREC_EQ = 4;
|
|
17924
|
+
var PREC_CMP = 5;
|
|
17925
|
+
var PREC_ADD = 6;
|
|
17926
|
+
var PREC_MUL = 7;
|
|
17927
|
+
var PREC_UNARY = 8;
|
|
17928
|
+
var PREC_MEMBER = 9;
|
|
17929
|
+
function parseExpression(tokens) {
|
|
17930
|
+
let pos = 0;
|
|
17931
|
+
const cur = () => tokens[pos] ?? { t: 13 /* EOF */ };
|
|
17932
|
+
const adv = () => {
|
|
17933
|
+
const tok = cur();
|
|
17934
|
+
pos++;
|
|
17935
|
+
return tok;
|
|
17936
|
+
};
|
|
17937
|
+
const eat = (kind) => {
|
|
17938
|
+
if (cur().t === kind) pos++;
|
|
17939
|
+
};
|
|
17940
|
+
function getInfixPrec(tok) {
|
|
17941
|
+
switch (tok.t) {
|
|
17942
|
+
case 34 /* Question */:
|
|
17943
|
+
return PREC_TERNARY;
|
|
17944
|
+
case 32 /* Or */:
|
|
17945
|
+
return PREC_OR;
|
|
17946
|
+
case 31 /* And */:
|
|
17947
|
+
return PREC_AND;
|
|
17948
|
+
case 25 /* EqEq */:
|
|
17949
|
+
case 26 /* NotEq */:
|
|
17950
|
+
return PREC_EQ;
|
|
17951
|
+
case 27 /* Greater */:
|
|
17952
|
+
case 28 /* Less */:
|
|
17953
|
+
case 29 /* GreaterEq */:
|
|
17954
|
+
case 30 /* LessEq */:
|
|
17955
|
+
return PREC_CMP;
|
|
17956
|
+
case 20 /* Plus */:
|
|
17957
|
+
case 21 /* Minus */:
|
|
17958
|
+
return PREC_ADD;
|
|
17959
|
+
case 22 /* Star */:
|
|
17960
|
+
case 23 /* Slash */:
|
|
17961
|
+
case 24 /* Percent */:
|
|
17962
|
+
return PREC_MUL;
|
|
17963
|
+
case 19 /* Dot */:
|
|
17964
|
+
case 3 /* LBrack */:
|
|
17965
|
+
return PREC_MEMBER;
|
|
17966
|
+
default:
|
|
17967
|
+
return 0;
|
|
17968
|
+
}
|
|
17969
|
+
}
|
|
17970
|
+
function parseExpr(minPrec = 0) {
|
|
17971
|
+
let left = parsePrefix();
|
|
17972
|
+
while (getInfixPrec(cur()) > minPrec) {
|
|
17973
|
+
left = parseInfix(left);
|
|
17974
|
+
}
|
|
17975
|
+
return left;
|
|
17976
|
+
}
|
|
17977
|
+
function parsePrefix() {
|
|
17978
|
+
const tok = cur();
|
|
17979
|
+
if (tok.t === 14 /* Str */) {
|
|
17980
|
+
adv();
|
|
17981
|
+
return { k: "Str", v: tok.v };
|
|
17982
|
+
}
|
|
17983
|
+
if (tok.t === 15 /* Num */) {
|
|
17984
|
+
adv();
|
|
17985
|
+
return { k: "Num", v: tok.v };
|
|
17986
|
+
}
|
|
17987
|
+
if (tok.t === 10 /* True */) {
|
|
17988
|
+
adv();
|
|
17989
|
+
return { k: "Bool", v: true };
|
|
17990
|
+
}
|
|
17991
|
+
if (tok.t === 11 /* False */) {
|
|
17992
|
+
adv();
|
|
17993
|
+
return { k: "Bool", v: false };
|
|
17994
|
+
}
|
|
17995
|
+
if (tok.t === 12 /* Null */) {
|
|
17996
|
+
adv();
|
|
17997
|
+
return { k: "Null" };
|
|
17998
|
+
}
|
|
17999
|
+
if (tok.t === 3 /* LBrack */) return parseArr();
|
|
18000
|
+
if (tok.t === 5 /* LBrace */) return parseObj();
|
|
18001
|
+
if (tok.t === 18 /* StateVar */) {
|
|
18002
|
+
const name = tok.v;
|
|
18003
|
+
adv();
|
|
18004
|
+
if (cur().t === 9 /* Equals */) {
|
|
18005
|
+
adv();
|
|
18006
|
+
const value = parseExpr(0);
|
|
18007
|
+
return { k: "Assign", target: name, value };
|
|
18008
|
+
}
|
|
18009
|
+
return { k: "StateRef", n: name };
|
|
18010
|
+
}
|
|
18011
|
+
if (tok.t === 17 /* Type */) {
|
|
18012
|
+
const name = tok.v;
|
|
18013
|
+
if (tokens[pos + 1]?.t === 1 /* LParen */ && (!isBuiltin(name) || name === "Action"))
|
|
18014
|
+
return parseComp();
|
|
18015
|
+
adv();
|
|
18016
|
+
return { k: "Ref", n: name };
|
|
18017
|
+
}
|
|
18018
|
+
if (tok.t === 35 /* BuiltinCall */) {
|
|
18019
|
+
if (tokens[pos + 1]?.t === 1 /* LParen */) return parseComp();
|
|
18020
|
+
adv();
|
|
18021
|
+
return { k: "Ref", n: tok.v };
|
|
18022
|
+
}
|
|
18023
|
+
if (tok.t === 16 /* Ident */) {
|
|
18024
|
+
adv();
|
|
18025
|
+
return { k: "Ref", n: tok.v };
|
|
18026
|
+
}
|
|
18027
|
+
if (tok.t === 33 /* Not */) {
|
|
18028
|
+
adv();
|
|
18029
|
+
return { k: "UnaryOp", op: "!", operand: parseExpr(PREC_UNARY) };
|
|
18030
|
+
}
|
|
18031
|
+
if (tok.t === 21 /* Minus */) {
|
|
18032
|
+
adv();
|
|
18033
|
+
return { k: "UnaryOp", op: "-", operand: parseExpr(PREC_UNARY) };
|
|
18034
|
+
}
|
|
18035
|
+
if (tok.t === 1 /* LParen */) {
|
|
18036
|
+
adv();
|
|
18037
|
+
const inner = parseExpr(0);
|
|
18038
|
+
eat(2 /* RParen */);
|
|
18039
|
+
return inner;
|
|
18040
|
+
}
|
|
18041
|
+
adv();
|
|
18042
|
+
return { k: "Null" };
|
|
18043
|
+
}
|
|
18044
|
+
function parseInfix(left) {
|
|
18045
|
+
const tok = cur();
|
|
18046
|
+
if (tok.t === 20 /* Plus */) {
|
|
18047
|
+
adv();
|
|
18048
|
+
return { k: "BinOp", op: "+", left, right: parseExpr(PREC_ADD) };
|
|
18049
|
+
}
|
|
18050
|
+
if (tok.t === 21 /* Minus */) {
|
|
18051
|
+
adv();
|
|
18052
|
+
return { k: "BinOp", op: "-", left, right: parseExpr(PREC_ADD) };
|
|
18053
|
+
}
|
|
18054
|
+
if (tok.t === 22 /* Star */) {
|
|
18055
|
+
adv();
|
|
18056
|
+
return { k: "BinOp", op: "*", left, right: parseExpr(PREC_MUL) };
|
|
18057
|
+
}
|
|
18058
|
+
if (tok.t === 23 /* Slash */) {
|
|
18059
|
+
adv();
|
|
18060
|
+
return { k: "BinOp", op: "/", left, right: parseExpr(PREC_MUL) };
|
|
18061
|
+
}
|
|
18062
|
+
if (tok.t === 24 /* Percent */) {
|
|
18063
|
+
adv();
|
|
18064
|
+
return { k: "BinOp", op: "%", left, right: parseExpr(PREC_MUL) };
|
|
18065
|
+
}
|
|
18066
|
+
if (tok.t === 25 /* EqEq */) {
|
|
18067
|
+
adv();
|
|
18068
|
+
return { k: "BinOp", op: "==", left, right: parseExpr(PREC_EQ) };
|
|
18069
|
+
}
|
|
18070
|
+
if (tok.t === 26 /* NotEq */) {
|
|
18071
|
+
adv();
|
|
18072
|
+
return { k: "BinOp", op: "!=", left, right: parseExpr(PREC_EQ) };
|
|
18073
|
+
}
|
|
18074
|
+
if (tok.t === 27 /* Greater */) {
|
|
18075
|
+
adv();
|
|
18076
|
+
return { k: "BinOp", op: ">", left, right: parseExpr(PREC_CMP) };
|
|
18077
|
+
}
|
|
18078
|
+
if (tok.t === 28 /* Less */) {
|
|
18079
|
+
adv();
|
|
18080
|
+
return { k: "BinOp", op: "<", left, right: parseExpr(PREC_CMP) };
|
|
18081
|
+
}
|
|
18082
|
+
if (tok.t === 29 /* GreaterEq */) {
|
|
18083
|
+
adv();
|
|
18084
|
+
return { k: "BinOp", op: ">=", left, right: parseExpr(PREC_CMP) };
|
|
18085
|
+
}
|
|
18086
|
+
if (tok.t === 30 /* LessEq */) {
|
|
18087
|
+
adv();
|
|
18088
|
+
return { k: "BinOp", op: "<=", left, right: parseExpr(PREC_CMP) };
|
|
18089
|
+
}
|
|
18090
|
+
if (tok.t === 31 /* And */) {
|
|
18091
|
+
adv();
|
|
18092
|
+
return { k: "BinOp", op: "&&", left, right: parseExpr(PREC_AND) };
|
|
18093
|
+
}
|
|
18094
|
+
if (tok.t === 32 /* Or */) {
|
|
18095
|
+
adv();
|
|
18096
|
+
return { k: "BinOp", op: "||", left, right: parseExpr(PREC_OR) };
|
|
18097
|
+
}
|
|
18098
|
+
if (tok.t === 34 /* Question */) {
|
|
18099
|
+
adv();
|
|
18100
|
+
const then = parseExpr(0);
|
|
18101
|
+
eat(8 /* Colon */);
|
|
18102
|
+
const els = parseExpr(0);
|
|
18103
|
+
return { k: "Ternary", cond: left, then, else: els };
|
|
18104
|
+
}
|
|
18105
|
+
if (tok.t === 19 /* Dot */) {
|
|
18106
|
+
adv();
|
|
18107
|
+
const fieldTok = cur();
|
|
18108
|
+
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(), "?");
|
|
18109
|
+
return { k: "Member", obj: left, field };
|
|
18110
|
+
}
|
|
18111
|
+
if (tok.t === 3 /* LBrack */) {
|
|
18112
|
+
adv();
|
|
18113
|
+
const index = parseExpr(0);
|
|
18114
|
+
eat(4 /* RBrack */);
|
|
18115
|
+
return { k: "Index", obj: left, index };
|
|
18116
|
+
}
|
|
18117
|
+
return left;
|
|
18118
|
+
}
|
|
18119
|
+
function parseComp() {
|
|
18120
|
+
const name = cur().v;
|
|
18121
|
+
adv();
|
|
18122
|
+
eat(1 /* LParen */);
|
|
18123
|
+
const args = [];
|
|
18124
|
+
while (cur().t !== 2 /* RParen */ && cur().t !== 13 /* EOF */) {
|
|
18125
|
+
args.push(parseExpr(0));
|
|
18126
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18127
|
+
}
|
|
18128
|
+
eat(2 /* RParen */);
|
|
18129
|
+
return { k: "Comp", name, args };
|
|
18130
|
+
}
|
|
18131
|
+
function parseArr() {
|
|
18132
|
+
adv();
|
|
18133
|
+
const els = [];
|
|
18134
|
+
while (cur().t !== 4 /* RBrack */ && cur().t !== 13 /* EOF */) {
|
|
18135
|
+
els.push(parseExpr(0));
|
|
18136
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18137
|
+
}
|
|
18138
|
+
eat(4 /* RBrack */);
|
|
18139
|
+
return { k: "Arr", els };
|
|
18140
|
+
}
|
|
18141
|
+
function parseObj() {
|
|
18142
|
+
adv();
|
|
18143
|
+
const entries = [];
|
|
18144
|
+
while (cur().t !== 6 /* RBrace */ && cur().t !== 13 /* EOF */) {
|
|
18145
|
+
const kt = cur();
|
|
18146
|
+
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(), "?");
|
|
18147
|
+
eat(8 /* Colon */);
|
|
18148
|
+
entries.push([key, parseExpr(0)]);
|
|
18149
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18150
|
+
}
|
|
18151
|
+
eat(6 /* RBrace */);
|
|
18152
|
+
return { k: "Obj", entries };
|
|
18153
|
+
}
|
|
18154
|
+
return parseExpr(0);
|
|
18155
|
+
}
|
|
18156
|
+
|
|
18157
|
+
// src/genui/lang/parser/lexer.ts
|
|
18158
|
+
function tokenize(src) {
|
|
18159
|
+
const tokens = [];
|
|
18160
|
+
let i = 0;
|
|
18161
|
+
const n = src.length;
|
|
18162
|
+
while (i < n) {
|
|
18163
|
+
while (i < n && (src[i] === " " || src[i] === " " || src[i] === "\r")) i++;
|
|
18164
|
+
if (i >= n) break;
|
|
18165
|
+
const c = src[i];
|
|
18166
|
+
if (c === "\n") {
|
|
18167
|
+
tokens.push({ t: 0 /* Newline */ });
|
|
18168
|
+
i++;
|
|
18169
|
+
continue;
|
|
18170
|
+
}
|
|
18171
|
+
if (c === "(") {
|
|
18172
|
+
tokens.push({ t: 1 /* LParen */ });
|
|
18173
|
+
i++;
|
|
18174
|
+
continue;
|
|
18175
|
+
}
|
|
18176
|
+
if (c === ")") {
|
|
18177
|
+
tokens.push({ t: 2 /* RParen */ });
|
|
18178
|
+
i++;
|
|
18179
|
+
continue;
|
|
18180
|
+
}
|
|
18181
|
+
if (c === "[") {
|
|
18182
|
+
tokens.push({ t: 3 /* LBrack */ });
|
|
18183
|
+
i++;
|
|
18184
|
+
continue;
|
|
18185
|
+
}
|
|
18186
|
+
if (c === "]") {
|
|
18187
|
+
tokens.push({ t: 4 /* RBrack */ });
|
|
18188
|
+
i++;
|
|
18189
|
+
continue;
|
|
18190
|
+
}
|
|
18191
|
+
if (c === "{") {
|
|
18192
|
+
tokens.push({ t: 5 /* LBrace */ });
|
|
18193
|
+
i++;
|
|
18194
|
+
continue;
|
|
18195
|
+
}
|
|
18196
|
+
if (c === "}") {
|
|
18197
|
+
tokens.push({ t: 6 /* RBrace */ });
|
|
18198
|
+
i++;
|
|
18199
|
+
continue;
|
|
18200
|
+
}
|
|
18201
|
+
if (c === ",") {
|
|
18202
|
+
tokens.push({ t: 7 /* Comma */ });
|
|
18203
|
+
i++;
|
|
18204
|
+
continue;
|
|
18205
|
+
}
|
|
18206
|
+
if (c === ":") {
|
|
18207
|
+
tokens.push({ t: 8 /* Colon */ });
|
|
18208
|
+
i++;
|
|
18209
|
+
continue;
|
|
18210
|
+
}
|
|
18211
|
+
if (c === "=") {
|
|
18212
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18213
|
+
tokens.push({ t: 25 /* EqEq */ });
|
|
18214
|
+
i += 2;
|
|
18215
|
+
} else {
|
|
18216
|
+
tokens.push({ t: 9 /* Equals */ });
|
|
18217
|
+
i++;
|
|
18218
|
+
}
|
|
18219
|
+
continue;
|
|
18220
|
+
}
|
|
18221
|
+
if (c === "!") {
|
|
18222
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18223
|
+
tokens.push({ t: 26 /* NotEq */ });
|
|
18224
|
+
i += 2;
|
|
18225
|
+
} else {
|
|
18226
|
+
tokens.push({ t: 33 /* Not */ });
|
|
18227
|
+
i++;
|
|
18228
|
+
}
|
|
18229
|
+
continue;
|
|
18230
|
+
}
|
|
18231
|
+
if (c === ">") {
|
|
18232
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18233
|
+
tokens.push({ t: 29 /* GreaterEq */ });
|
|
18234
|
+
i += 2;
|
|
18235
|
+
} else {
|
|
18236
|
+
tokens.push({ t: 27 /* Greater */ });
|
|
18237
|
+
i++;
|
|
18238
|
+
}
|
|
18239
|
+
continue;
|
|
18240
|
+
}
|
|
18241
|
+
if (c === "<") {
|
|
18242
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18243
|
+
tokens.push({ t: 30 /* LessEq */ });
|
|
18244
|
+
i += 2;
|
|
18245
|
+
} else {
|
|
18246
|
+
tokens.push({ t: 28 /* Less */ });
|
|
18247
|
+
i++;
|
|
18248
|
+
}
|
|
18249
|
+
continue;
|
|
18250
|
+
}
|
|
18251
|
+
if (c === "&") {
|
|
18252
|
+
if (i + 1 < n && src[i + 1] === "&") {
|
|
18253
|
+
tokens.push({ t: 31 /* And */ });
|
|
18254
|
+
i += 2;
|
|
18255
|
+
} else {
|
|
18256
|
+
tokens.push({ t: 31 /* And */ });
|
|
18257
|
+
i++;
|
|
18258
|
+
}
|
|
18259
|
+
continue;
|
|
18260
|
+
}
|
|
18261
|
+
if (c === "|") {
|
|
18262
|
+
if (i + 1 < n && src[i + 1] === "|") {
|
|
18263
|
+
tokens.push({ t: 32 /* Or */ });
|
|
18264
|
+
i += 2;
|
|
18265
|
+
} else {
|
|
18266
|
+
tokens.push({ t: 32 /* Or */ });
|
|
18267
|
+
i++;
|
|
18268
|
+
}
|
|
18269
|
+
continue;
|
|
18270
|
+
}
|
|
18271
|
+
if (c === ".") {
|
|
18272
|
+
tokens.push({ t: 19 /* Dot */ });
|
|
18273
|
+
i++;
|
|
18274
|
+
continue;
|
|
18275
|
+
}
|
|
18276
|
+
if (c === "?") {
|
|
18277
|
+
tokens.push({ t: 34 /* Question */ });
|
|
18278
|
+
i++;
|
|
18279
|
+
continue;
|
|
18280
|
+
}
|
|
18281
|
+
if (c === "+") {
|
|
18282
|
+
tokens.push({ t: 20 /* Plus */ });
|
|
18283
|
+
i++;
|
|
18284
|
+
continue;
|
|
18285
|
+
}
|
|
18286
|
+
if (c === "*") {
|
|
18287
|
+
tokens.push({ t: 22 /* Star */ });
|
|
18288
|
+
i++;
|
|
18289
|
+
continue;
|
|
18290
|
+
}
|
|
18291
|
+
if (c === "/") {
|
|
18292
|
+
tokens.push({ t: 23 /* Slash */ });
|
|
18293
|
+
i++;
|
|
18294
|
+
continue;
|
|
18295
|
+
}
|
|
18296
|
+
if (c === "%") {
|
|
18297
|
+
tokens.push({ t: 24 /* Percent */ });
|
|
18298
|
+
i++;
|
|
18299
|
+
continue;
|
|
18300
|
+
}
|
|
18301
|
+
if (c === '"') {
|
|
18302
|
+
const start = i;
|
|
18303
|
+
i++;
|
|
18304
|
+
let isClosed = false;
|
|
18305
|
+
while (i < n) {
|
|
18306
|
+
if (src[i] === "\\") {
|
|
18307
|
+
i += 2;
|
|
18308
|
+
} else if (src[i] === '"') {
|
|
18309
|
+
i++;
|
|
18310
|
+
isClosed = true;
|
|
18311
|
+
break;
|
|
18312
|
+
} else {
|
|
18313
|
+
i++;
|
|
18314
|
+
}
|
|
18315
|
+
}
|
|
18316
|
+
const rawString = src.slice(start, i);
|
|
18317
|
+
try {
|
|
18318
|
+
const validJsonString = isClosed ? rawString : rawString + '"';
|
|
18319
|
+
tokens.push({ t: 14 /* Str */, v: JSON.parse(validJsonString) });
|
|
18320
|
+
} catch {
|
|
18321
|
+
const stripped = rawString.replace(/^"|"$/g, "");
|
|
18322
|
+
tokens.push({ t: 14 /* Str */, v: stripped });
|
|
18323
|
+
}
|
|
18324
|
+
continue;
|
|
18325
|
+
}
|
|
18326
|
+
if (c === "'") {
|
|
18327
|
+
i++;
|
|
18328
|
+
let result = "";
|
|
18329
|
+
while (i < n) {
|
|
18330
|
+
if (src[i] === "\\") {
|
|
18331
|
+
i++;
|
|
18332
|
+
if (i < n) {
|
|
18333
|
+
const esc = src[i];
|
|
18334
|
+
if (esc === "'") result += "'";
|
|
18335
|
+
else if (esc === "\\") result += "\\";
|
|
18336
|
+
else if (esc === "n") result += "\n";
|
|
18337
|
+
else if (esc === "t") result += " ";
|
|
18338
|
+
else result += esc;
|
|
18339
|
+
i++;
|
|
18340
|
+
}
|
|
18341
|
+
} else if (src[i] === "'") {
|
|
18342
|
+
i++;
|
|
18343
|
+
break;
|
|
18344
|
+
} else {
|
|
18345
|
+
result += src[i];
|
|
18346
|
+
i++;
|
|
18347
|
+
}
|
|
18348
|
+
}
|
|
18349
|
+
tokens.push({ t: 14 /* Str */, v: result });
|
|
18350
|
+
continue;
|
|
18351
|
+
}
|
|
18352
|
+
if (c === "-") {
|
|
18353
|
+
const prev = tokens.length > 0 ? tokens[tokens.length - 1] : null;
|
|
18354
|
+
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 */);
|
|
18355
|
+
if (!afterValue && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") ; else {
|
|
18356
|
+
tokens.push({ t: 21 /* Minus */ });
|
|
18357
|
+
i++;
|
|
18358
|
+
continue;
|
|
18359
|
+
}
|
|
18360
|
+
}
|
|
18361
|
+
const isDigit = c >= "0" && c <= "9";
|
|
18362
|
+
const isNegDigit = c === "-" && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9";
|
|
18363
|
+
if (isDigit || isNegDigit) {
|
|
18364
|
+
const start = i;
|
|
18365
|
+
if (src[i] === "-") i++;
|
|
18366
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18367
|
+
if (i < n && src[i] === "." && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") {
|
|
18368
|
+
i++;
|
|
18369
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18370
|
+
}
|
|
18371
|
+
if (i < n && (src[i] === "e" || src[i] === "E")) {
|
|
18372
|
+
i++;
|
|
18373
|
+
if (i < n && (src[i] === "+" || src[i] === "-")) i++;
|
|
18374
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18375
|
+
}
|
|
18376
|
+
tokens.push({ t: 15 /* Num */, v: +src.slice(start, i) });
|
|
18377
|
+
continue;
|
|
18378
|
+
}
|
|
18379
|
+
if (c === "$" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
18380
|
+
const start = i;
|
|
18381
|
+
i++;
|
|
18382
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18383
|
+
i++;
|
|
18384
|
+
tokens.push({ t: 18 /* StateVar */, v: src.slice(start, i) });
|
|
18385
|
+
continue;
|
|
18386
|
+
}
|
|
18387
|
+
const isAlpha = c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
|
|
18388
|
+
if (isAlpha) {
|
|
18389
|
+
const start = i;
|
|
18390
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18391
|
+
i++;
|
|
18392
|
+
const word = src.slice(start, i);
|
|
18393
|
+
if (word === "true") {
|
|
18394
|
+
tokens.push({ t: 10 /* True */ });
|
|
18395
|
+
continue;
|
|
18396
|
+
}
|
|
18397
|
+
if (word === "false") {
|
|
18398
|
+
tokens.push({ t: 11 /* False */ });
|
|
18399
|
+
continue;
|
|
18400
|
+
}
|
|
18401
|
+
if (word === "null") {
|
|
18402
|
+
tokens.push({ t: 12 /* Null */ });
|
|
18403
|
+
continue;
|
|
18404
|
+
}
|
|
18405
|
+
const kind = c >= "A" && c <= "Z" ? 17 /* Type */ : 16 /* Ident */;
|
|
18406
|
+
tokens.push({ t: kind, v: word });
|
|
18407
|
+
continue;
|
|
18408
|
+
}
|
|
18409
|
+
if (c === "@" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
18410
|
+
i++;
|
|
18411
|
+
const start = i;
|
|
18412
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18413
|
+
i++;
|
|
18414
|
+
tokens.push({ t: 35 /* BuiltinCall */, v: src.slice(start, i) });
|
|
18415
|
+
continue;
|
|
18416
|
+
}
|
|
18417
|
+
i++;
|
|
18418
|
+
}
|
|
18419
|
+
tokens.push({ t: 13 /* EOF */ });
|
|
18420
|
+
return tokens;
|
|
18421
|
+
}
|
|
18422
|
+
|
|
18423
|
+
// src/genui/lang/parser/types.ts
|
|
18424
|
+
function isElementNode(value) {
|
|
18425
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
18426
|
+
const node = value;
|
|
18427
|
+
return node.type === "element" && typeof node.typeName === "string" && typeof node.props === "object" && node.props !== null && typeof node.partial === "boolean";
|
|
18428
|
+
}
|
|
18429
|
+
|
|
18430
|
+
// src/genui/lang/parser/materialize.ts
|
|
18431
|
+
function containsDynamicValue(v) {
|
|
18432
|
+
if (v == null || typeof v !== "object") return false;
|
|
18433
|
+
if (isASTNode(v)) return true;
|
|
18434
|
+
if (Array.isArray(v)) return v.some(containsDynamicValue);
|
|
18435
|
+
if (isElementNode(v)) {
|
|
18436
|
+
return Object.values(v.props).some(containsDynamicValue);
|
|
18437
|
+
}
|
|
18438
|
+
const obj = v;
|
|
18439
|
+
return Object.values(obj).some(containsDynamicValue);
|
|
18440
|
+
}
|
|
18441
|
+
function resolveRef(name, ctx, mode) {
|
|
18442
|
+
if (ctx.visited.has(name)) {
|
|
18443
|
+
ctx.unres.push(name);
|
|
18444
|
+
return mode === "expr" ? { k: "Ph", n: name } : null;
|
|
18445
|
+
}
|
|
18446
|
+
if (!ctx.syms.has(name)) {
|
|
18447
|
+
ctx.unres.push(name);
|
|
18448
|
+
return mode === "expr" ? { k: "Ph", n: name } : null;
|
|
18449
|
+
}
|
|
18450
|
+
const target = ctx.syms.get(name);
|
|
18451
|
+
ctx.unreached?.delete(name);
|
|
18452
|
+
if (target.k === "Comp" && isReservedCall(target.name)) {
|
|
18453
|
+
const refType = target.name === RESERVED_CALLS.Mutation ? "mutation" : "query";
|
|
18454
|
+
return { k: "RuntimeRef", n: name, refType };
|
|
18455
|
+
}
|
|
18456
|
+
ctx.visited.add(name);
|
|
18457
|
+
const prevStatementId = ctx.currentStatementId;
|
|
18458
|
+
ctx.currentStatementId = name;
|
|
18459
|
+
try {
|
|
18460
|
+
const result = mode === "value" ? materializeValue(target, ctx) : materializeExpr(target, ctx);
|
|
18461
|
+
if (mode === "value" && isElementNode(result)) {
|
|
18462
|
+
result.statementId = name;
|
|
18463
|
+
}
|
|
18464
|
+
return result;
|
|
18465
|
+
} finally {
|
|
18466
|
+
ctx.currentStatementId = prevStatementId;
|
|
18467
|
+
ctx.visited.delete(name);
|
|
18468
|
+
}
|
|
18469
|
+
}
|
|
18470
|
+
function materializeLazyBuiltin(node, ctx, scopedRefs) {
|
|
18471
|
+
if (!LAZY_BUILTINS.has(node.name) || node.args.length < 3) return null;
|
|
18472
|
+
const varArg = node.args[1];
|
|
18473
|
+
const varName = varArg.k === "Ref" ? varArg.n : varArg.k === "Str" ? varArg.v : null;
|
|
18474
|
+
if (!varName) return null;
|
|
18475
|
+
const nextScopedRefs = new Set(scopedRefs);
|
|
18476
|
+
nextScopedRefs.add(varName);
|
|
18477
|
+
const recursedArgs = node.args.map(
|
|
18478
|
+
(a, i) => i === 1 ? a : materializeExprInternal(a, ctx, nextScopedRefs)
|
|
18479
|
+
);
|
|
18480
|
+
return { ...node, args: recursedArgs };
|
|
18481
|
+
}
|
|
18482
|
+
function materializeExprInternal(node, ctx, scopedRefs) {
|
|
18483
|
+
switch (node.k) {
|
|
18484
|
+
case "Ref":
|
|
18485
|
+
return scopedRefs.has(node.n) ? node : resolveRef(node.n, ctx, "expr");
|
|
18486
|
+
case "Ph":
|
|
18487
|
+
return node;
|
|
18488
|
+
case "Comp": {
|
|
18489
|
+
const lazy2 = materializeLazyBuiltin(node, ctx, scopedRefs);
|
|
18490
|
+
if (lazy2) return lazy2;
|
|
18491
|
+
const recursedArgs = node.args.map((a) => materializeExprInternal(a, ctx, scopedRefs));
|
|
18492
|
+
if (isBuiltin(node.name) || isReservedCall(node.name)) {
|
|
18493
|
+
return { ...node, args: recursedArgs };
|
|
18494
|
+
}
|
|
18495
|
+
const def = ctx.cat?.get(node.name);
|
|
18496
|
+
if (def) {
|
|
18497
|
+
const mappedProps = {};
|
|
18498
|
+
for (let i = 0; i < def.params.length && i < recursedArgs.length; i++) {
|
|
18499
|
+
mappedProps[def.params[i].name] = recursedArgs[i];
|
|
18500
|
+
}
|
|
18501
|
+
return { ...node, args: recursedArgs, mappedProps };
|
|
18502
|
+
}
|
|
18503
|
+
ctx.errors.push({
|
|
18504
|
+
code: "unknown-component",
|
|
18505
|
+
component: node.name,
|
|
18506
|
+
path: "",
|
|
18507
|
+
message: `Unknown component "${node.name}" \u2014 not found in catalog or builtins`,
|
|
18508
|
+
statementId: ctx.currentStatementId
|
|
18509
|
+
});
|
|
18510
|
+
return { ...node, args: recursedArgs };
|
|
18511
|
+
}
|
|
18512
|
+
case "Arr":
|
|
18513
|
+
return { ...node, els: node.els.map((e) => materializeExprInternal(e, ctx, scopedRefs)) };
|
|
18514
|
+
case "Obj":
|
|
18515
|
+
return {
|
|
18516
|
+
...node,
|
|
18517
|
+
entries: node.entries.map(
|
|
18518
|
+
([k, v]) => [k, materializeExprInternal(v, ctx, scopedRefs)]
|
|
18519
|
+
)
|
|
18520
|
+
};
|
|
18521
|
+
case "BinOp":
|
|
18522
|
+
return {
|
|
18523
|
+
...node,
|
|
18524
|
+
left: materializeExprInternal(node.left, ctx, scopedRefs),
|
|
18525
|
+
right: materializeExprInternal(node.right, ctx, scopedRefs)
|
|
18526
|
+
};
|
|
18527
|
+
case "UnaryOp":
|
|
18528
|
+
return { ...node, operand: materializeExprInternal(node.operand, ctx, scopedRefs) };
|
|
18529
|
+
case "Ternary":
|
|
18530
|
+
return {
|
|
18531
|
+
...node,
|
|
18532
|
+
cond: materializeExprInternal(node.cond, ctx, scopedRefs),
|
|
18533
|
+
then: materializeExprInternal(node.then, ctx, scopedRefs),
|
|
18534
|
+
else: materializeExprInternal(node.else, ctx, scopedRefs)
|
|
18535
|
+
};
|
|
18536
|
+
case "Member":
|
|
18537
|
+
return { ...node, obj: materializeExprInternal(node.obj, ctx, scopedRefs) };
|
|
18538
|
+
case "Index":
|
|
18539
|
+
return {
|
|
18540
|
+
...node,
|
|
18541
|
+
obj: materializeExprInternal(node.obj, ctx, scopedRefs),
|
|
18542
|
+
index: materializeExprInternal(node.index, ctx, scopedRefs)
|
|
18543
|
+
};
|
|
18544
|
+
case "Assign":
|
|
18545
|
+
return { ...node, value: materializeExprInternal(node.value, ctx, scopedRefs) };
|
|
18546
|
+
// Literals, StateRef, RuntimeRef — pass through unchanged
|
|
18547
|
+
default:
|
|
18548
|
+
return node;
|
|
18549
|
+
}
|
|
18550
|
+
}
|
|
18551
|
+
function materializeExpr(node, ctx) {
|
|
18552
|
+
return materializeExprInternal(node, ctx, /* @__PURE__ */ new Set());
|
|
18553
|
+
}
|
|
18554
|
+
function materializeValue(node, ctx) {
|
|
18555
|
+
switch (node.k) {
|
|
18556
|
+
// ── Ref resolution ───────────────────────────────────────────────────
|
|
18557
|
+
case "Ref":
|
|
18558
|
+
return resolveRef(node.n, ctx, "value");
|
|
18559
|
+
// ── Literals → plain values ──────────────────────────────────────────
|
|
18560
|
+
case "Str":
|
|
18561
|
+
return node.v;
|
|
18562
|
+
case "Num":
|
|
18563
|
+
return node.v;
|
|
18564
|
+
case "Bool":
|
|
18565
|
+
return node.v;
|
|
18566
|
+
case "Null":
|
|
18567
|
+
return null;
|
|
18568
|
+
case "Ph":
|
|
18569
|
+
return null;
|
|
18570
|
+
// ── Collections ──────────────────────────────────────────────────────
|
|
18571
|
+
case "Arr": {
|
|
18572
|
+
const items = [];
|
|
18573
|
+
for (const e of node.els) {
|
|
18574
|
+
if (e.k === "Ph") continue;
|
|
18575
|
+
const value = materializeValue(e, ctx);
|
|
18576
|
+
if (value === null && (e.k === "Comp" || e.k === "Ref")) continue;
|
|
18577
|
+
items.push(value);
|
|
18578
|
+
}
|
|
18579
|
+
return items;
|
|
18580
|
+
}
|
|
18581
|
+
case "Obj": {
|
|
18582
|
+
const o = {};
|
|
18583
|
+
for (const [k, v] of node.entries) o[k] = materializeValue(v, ctx);
|
|
18584
|
+
return o;
|
|
18585
|
+
}
|
|
18586
|
+
// ── Component nodes ──────────────────────────────────────────────────
|
|
18587
|
+
case "Comp": {
|
|
18588
|
+
const { name, args } = node;
|
|
18589
|
+
if (isBuiltin(name)) {
|
|
18590
|
+
const lazy2 = materializeLazyBuiltin(node, ctx, /* @__PURE__ */ new Set());
|
|
18591
|
+
if (lazy2) return lazy2;
|
|
18592
|
+
return { ...node, args: args.map((a) => materializeExpr(a, ctx)) };
|
|
18593
|
+
}
|
|
18594
|
+
if (isReservedCall(name)) {
|
|
18595
|
+
ctx.errors.push({
|
|
18596
|
+
code: "inline-reserved",
|
|
18597
|
+
component: name,
|
|
18598
|
+
path: "",
|
|
18599
|
+
message: `${name}() must be declared as a top-level statement, not used inline as a value`,
|
|
18600
|
+
statementId: ctx.currentStatementId
|
|
18601
|
+
});
|
|
18602
|
+
return null;
|
|
18603
|
+
}
|
|
18604
|
+
const def = ctx.cat?.get(name);
|
|
18605
|
+
const props = {};
|
|
18606
|
+
if (def) {
|
|
18607
|
+
for (let i = 0; i < def.params.length && i < args.length; i++) {
|
|
18608
|
+
props[def.params[i].name] = materializeValue(args[i], ctx);
|
|
18609
|
+
}
|
|
18610
|
+
if (args.length > def.params.length) {
|
|
18611
|
+
const excessCount = args.length - def.params.length;
|
|
18612
|
+
ctx.errors.push({
|
|
18613
|
+
code: "excess-args",
|
|
18614
|
+
component: name,
|
|
18615
|
+
path: "",
|
|
18616
|
+
message: `${name} takes ${def.params.length} arg(s), got ${args.length} (${excessCount} excess dropped)`,
|
|
18617
|
+
statementId: ctx.currentStatementId
|
|
18618
|
+
});
|
|
18619
|
+
}
|
|
18620
|
+
const missingRequired = def.params.filter(
|
|
18621
|
+
(p) => p.required && (!(p.name in props) || props[p.name] === null)
|
|
18622
|
+
);
|
|
18623
|
+
if (missingRequired.length) {
|
|
18624
|
+
const stillInvalid = missingRequired.filter((p) => {
|
|
18625
|
+
if (p.defaultValue !== void 0) {
|
|
18626
|
+
props[p.name] = p.defaultValue;
|
|
18627
|
+
return false;
|
|
18628
|
+
}
|
|
18629
|
+
return true;
|
|
18630
|
+
});
|
|
18631
|
+
if (stillInvalid.length) {
|
|
18632
|
+
for (const p of stillInvalid) {
|
|
18633
|
+
const isNull = p.name in props;
|
|
18634
|
+
ctx.errors.push({
|
|
18635
|
+
code: isNull ? "null-required" : "missing-required",
|
|
18636
|
+
component: name,
|
|
18637
|
+
path: `/${p.name}`,
|
|
18638
|
+
message: isNull ? `required field "${p.name}" cannot be null` : `missing required field "${p.name}"`,
|
|
18639
|
+
statementId: ctx.currentStatementId
|
|
18640
|
+
});
|
|
18641
|
+
}
|
|
18642
|
+
return null;
|
|
18643
|
+
}
|
|
18644
|
+
}
|
|
18645
|
+
} else if (!isBuiltin(name) && !isReservedCall(name)) {
|
|
18646
|
+
ctx.errors.push({
|
|
18647
|
+
code: "unknown-component",
|
|
18648
|
+
component: name,
|
|
18649
|
+
path: "",
|
|
18650
|
+
message: `Unknown component "${name}" \u2014 not found in catalog or builtins`,
|
|
18651
|
+
statementId: ctx.currentStatementId
|
|
18652
|
+
});
|
|
18653
|
+
return null;
|
|
18654
|
+
}
|
|
18655
|
+
const hasDynamicProps = Object.values(props).some((v) => containsDynamicValue(v));
|
|
18656
|
+
return { type: "element", typeName: name, props, partial: ctx.partial, hasDynamicProps };
|
|
18657
|
+
}
|
|
18658
|
+
// ── Runtime expression nodes → preserve as ASTNode, normalize children ─
|
|
18659
|
+
default: {
|
|
18660
|
+
if (isRuntimeExpr(node)) {
|
|
18661
|
+
return materializeExpr(node, ctx);
|
|
18662
|
+
}
|
|
18663
|
+
return node;
|
|
18664
|
+
}
|
|
18665
|
+
}
|
|
18666
|
+
}
|
|
18667
|
+
|
|
18668
|
+
// src/genui/lang/parser/statements.ts
|
|
18669
|
+
function autoClose(input) {
|
|
18670
|
+
const stack = [];
|
|
18671
|
+
let inStr = false;
|
|
18672
|
+
let esc = false;
|
|
18673
|
+
for (let i = 0; i < input.length; i++) {
|
|
18674
|
+
const c = input[i];
|
|
18675
|
+
if (esc) {
|
|
18676
|
+
esc = false;
|
|
18677
|
+
continue;
|
|
18678
|
+
}
|
|
18679
|
+
if (c === "\\" && inStr) {
|
|
18680
|
+
esc = true;
|
|
18681
|
+
continue;
|
|
18682
|
+
}
|
|
18683
|
+
if (inStr) {
|
|
18684
|
+
if (c === inStr) inStr = false;
|
|
18685
|
+
continue;
|
|
18686
|
+
}
|
|
18687
|
+
if (c === '"' || c === "'") {
|
|
18688
|
+
inStr = c;
|
|
18689
|
+
continue;
|
|
18690
|
+
}
|
|
18691
|
+
if (c === "(" || c === "[" || c === "{") stack.push(c);
|
|
18692
|
+
else if (c === ")" && stack[stack.length - 1] === "(") stack.pop();
|
|
18693
|
+
else if (c === "]" && stack[stack.length - 1] === "[") stack.pop();
|
|
18694
|
+
else if (c === "}" && stack[stack.length - 1] === "{") stack.pop();
|
|
18695
|
+
}
|
|
18696
|
+
const wasIncomplete = !!inStr || stack.length > 0;
|
|
18697
|
+
if (!wasIncomplete) return { text: input, wasIncomplete: false };
|
|
18698
|
+
let out = input;
|
|
18699
|
+
if (inStr) {
|
|
18700
|
+
if (esc) out += "\\";
|
|
18701
|
+
out += inStr;
|
|
18702
|
+
}
|
|
18703
|
+
for (let j = stack.length - 1; j >= 0; j--)
|
|
18704
|
+
out += stack[j] === "(" ? ")" : stack[j] === "[" ? "]" : "}";
|
|
18705
|
+
return { text: out, wasIncomplete: true };
|
|
18706
|
+
}
|
|
18707
|
+
function split(tokens) {
|
|
18708
|
+
const stmts = [];
|
|
18709
|
+
let pos = 0;
|
|
18710
|
+
while (pos < tokens.length) {
|
|
18711
|
+
while (pos < tokens.length && tokens[pos].t === 0 /* Newline */) pos++;
|
|
18712
|
+
if (pos >= tokens.length || tokens[pos].t === 13 /* EOF */) break;
|
|
18713
|
+
const tok = tokens[pos];
|
|
18714
|
+
if (tok.t !== 16 /* Ident */ && tok.t !== 17 /* Type */ && tok.t !== 18 /* StateVar */) {
|
|
18715
|
+
while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
|
|
18716
|
+
continue;
|
|
18717
|
+
}
|
|
18718
|
+
const id = tok.v;
|
|
18719
|
+
const idTokenType = tok.t;
|
|
18720
|
+
pos++;
|
|
18721
|
+
if (pos >= tokens.length || tokens[pos].t !== 9 /* Equals */) {
|
|
18722
|
+
while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
|
|
18723
|
+
continue;
|
|
18724
|
+
}
|
|
18725
|
+
pos++;
|
|
18726
|
+
const expr = [];
|
|
18727
|
+
let depth = 0;
|
|
18728
|
+
let ternaryDepth = 0;
|
|
18729
|
+
while (pos < tokens.length && tokens[pos].t !== 13 /* EOF */) {
|
|
18730
|
+
const tt = tokens[pos].t;
|
|
18731
|
+
if (tt === 0 /* Newline */ && depth <= 0 && ternaryDepth <= 0) {
|
|
18732
|
+
let peek = pos + 1;
|
|
18733
|
+
while (peek < tokens.length && tokens[peek].t === 0 /* Newline */) peek++;
|
|
18734
|
+
const nextT = peek < tokens.length ? tokens[peek].t : 13 /* EOF */;
|
|
18735
|
+
if (nextT === 34 /* Question */ || nextT === 8 /* Colon */ && ternaryDepth > 0) {
|
|
18736
|
+
pos++;
|
|
18737
|
+
continue;
|
|
18738
|
+
}
|
|
18739
|
+
break;
|
|
18740
|
+
}
|
|
18741
|
+
if (tt === 0 /* Newline */) {
|
|
18742
|
+
pos++;
|
|
18743
|
+
continue;
|
|
18744
|
+
}
|
|
18745
|
+
if (tt === 1 /* LParen */ || tt === 3 /* LBrack */ || tt === 5 /* LBrace */) depth++;
|
|
18746
|
+
else if ((tt === 2 /* RParen */ || tt === 4 /* RBrack */ || tt === 6 /* RBrace */) && depth > 0) depth--;
|
|
18747
|
+
else if (tt === 34 /* Question */ && depth === 0) ternaryDepth++;
|
|
18748
|
+
else if (tt === 8 /* Colon */ && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
18749
|
+
expr.push(tokens[pos++]);
|
|
18750
|
+
}
|
|
18751
|
+
if (expr.length) stmts.push({ id, idTokenType, tokens: expr });
|
|
18752
|
+
}
|
|
18753
|
+
return stmts;
|
|
18754
|
+
}
|
|
18755
|
+
|
|
18756
|
+
// src/genui/lang/parser/parser.ts
|
|
18757
|
+
function emptyResult(incomplete = true) {
|
|
18758
|
+
return {
|
|
18759
|
+
root: null,
|
|
18760
|
+
meta: {
|
|
18761
|
+
incomplete,
|
|
18762
|
+
unresolved: [],
|
|
18763
|
+
orphaned: [],
|
|
18764
|
+
statementCount: 0,
|
|
18765
|
+
errors: []
|
|
18766
|
+
},
|
|
18767
|
+
stateDeclarations: {},
|
|
18768
|
+
queryStatements: [],
|
|
18769
|
+
mutationStatements: []
|
|
18770
|
+
};
|
|
18771
|
+
}
|
|
18772
|
+
function collectQueryDeps(node) {
|
|
18773
|
+
if (!isASTNode(node)) return [];
|
|
18774
|
+
const refs = /* @__PURE__ */ new Set();
|
|
18775
|
+
walkAST(node, (current) => {
|
|
18776
|
+
if (current.k === "StateRef") refs.add(current.n);
|
|
18777
|
+
});
|
|
18778
|
+
return [...refs];
|
|
18779
|
+
}
|
|
18780
|
+
function classifyStatement(raw, expr) {
|
|
18781
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Query) {
|
|
18782
|
+
const deps = collectQueryDeps(expr.args[1]);
|
|
18783
|
+
return {
|
|
18784
|
+
kind: "query",
|
|
18785
|
+
id: raw.id,
|
|
18786
|
+
call: { callee: RESERVED_CALLS.Query, args: expr.args },
|
|
18787
|
+
expr,
|
|
18788
|
+
deps: deps.length > 0 ? deps : void 0
|
|
18789
|
+
};
|
|
18790
|
+
}
|
|
18791
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Mutation) {
|
|
18792
|
+
return {
|
|
18793
|
+
kind: "mutation",
|
|
18794
|
+
id: raw.id,
|
|
18795
|
+
call: { callee: RESERVED_CALLS.Mutation, args: expr.args },
|
|
18796
|
+
expr
|
|
18797
|
+
};
|
|
18798
|
+
}
|
|
18799
|
+
if (raw.idTokenType === 18 /* StateVar */) {
|
|
18800
|
+
return { kind: "state", id: raw.id, init: expr };
|
|
18801
|
+
}
|
|
18802
|
+
return { kind: "value", id: raw.id, expr };
|
|
18803
|
+
}
|
|
18804
|
+
function extractStatements(stmts, ctx) {
|
|
18805
|
+
const stateDeclarations = {};
|
|
18806
|
+
const queryStatements = [];
|
|
18807
|
+
const mutationStatements = [];
|
|
18808
|
+
for (const stmt of stmts) {
|
|
18809
|
+
switch (stmt.kind) {
|
|
18810
|
+
case "state":
|
|
18811
|
+
stateDeclarations[stmt.id] = materializeValue(stmt.init, ctx);
|
|
18812
|
+
break;
|
|
18813
|
+
case "query":
|
|
18814
|
+
queryStatements.push({
|
|
18815
|
+
statementId: stmt.id,
|
|
18816
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
18817
|
+
argsAST: stmt.call.args[1] ?? null,
|
|
18818
|
+
defaultsAST: stmt.call.args[2] ?? null,
|
|
18819
|
+
refreshAST: stmt.call.args[3] ?? null,
|
|
18820
|
+
deps: stmt.deps,
|
|
18821
|
+
complete: true
|
|
18822
|
+
});
|
|
18823
|
+
break;
|
|
18824
|
+
case "mutation":
|
|
18825
|
+
mutationStatements.push({
|
|
18826
|
+
statementId: stmt.id,
|
|
18827
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
18828
|
+
argsAST: stmt.call.args[1] ?? null
|
|
18829
|
+
});
|
|
18830
|
+
break;
|
|
18831
|
+
}
|
|
18832
|
+
}
|
|
18833
|
+
for (const stmt of stmts) {
|
|
18834
|
+
const nodes = stmt.kind === "state" ? [stmt.init] : stmt.kind === "value" ? [stmt.expr] : stmt.kind === "query" || stmt.kind === "mutation" ? stmt.call.args : [];
|
|
18835
|
+
for (const node of nodes) {
|
|
18836
|
+
for (const dep of collectQueryDeps(node)) {
|
|
18837
|
+
if (!(dep in stateDeclarations)) {
|
|
18838
|
+
stateDeclarations[dep] = null;
|
|
18839
|
+
}
|
|
18840
|
+
}
|
|
18841
|
+
}
|
|
18842
|
+
}
|
|
18843
|
+
return { stateDeclarations, queryStatements, mutationStatements };
|
|
18844
|
+
}
|
|
18845
|
+
var DEFAULT_ROOT_STATEMENT_ID = "root";
|
|
18846
|
+
function isComponentStatement(stmt) {
|
|
18847
|
+
return stmt.kind === "value" && stmt.expr.k === "Comp" && !isBuiltin(stmt.expr.name) && stmt.expr.name !== RESERVED_CALLS.Query && stmt.expr.name !== RESERVED_CALLS.Mutation;
|
|
18848
|
+
}
|
|
18849
|
+
function pickEntryId(stmtMap, typedStmts, firstId, rootName) {
|
|
18850
|
+
if (stmtMap.has(DEFAULT_ROOT_STATEMENT_ID)) return DEFAULT_ROOT_STATEMENT_ID;
|
|
18851
|
+
if (rootName && stmtMap.has(rootName)) return rootName;
|
|
18852
|
+
const preferredComponent = rootName ? typedStmts.find((stmt) => isComponentStatement(stmt) && stmt.expr.name === rootName) : void 0;
|
|
18853
|
+
if (preferredComponent) return preferredComponent.id;
|
|
18854
|
+
const firstComponent = typedStmts.find(isComponentStatement);
|
|
18855
|
+
return firstComponent?.id ?? firstId;
|
|
18856
|
+
}
|
|
18857
|
+
function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat, rootName) {
|
|
18858
|
+
const entryId = pickEntryId(stmtMap, typedStmts, firstId, rootName);
|
|
18859
|
+
if (!stmtMap.has(entryId)) return emptyResult(wasIncomplete);
|
|
18860
|
+
const syms = /* @__PURE__ */ new Map();
|
|
18861
|
+
for (const [id, stmt] of stmtMap) {
|
|
18862
|
+
syms.set(id, stmt.kind === "state" ? stmt.init : stmt.expr);
|
|
18863
|
+
}
|
|
18864
|
+
const unres = [];
|
|
18865
|
+
const errors = [];
|
|
18866
|
+
const unreached = /* @__PURE__ */ new Set();
|
|
18867
|
+
for (const [id, stmt] of stmtMap) {
|
|
18868
|
+
if (id === entryId) continue;
|
|
18869
|
+
if (stmt.kind === "state" || stmt.kind === "query" || stmt.kind === "mutation") continue;
|
|
18870
|
+
unreached.add(id);
|
|
18871
|
+
}
|
|
18872
|
+
const ctx = {
|
|
18873
|
+
syms,
|
|
18874
|
+
cat,
|
|
18875
|
+
errors,
|
|
18876
|
+
unres,
|
|
18877
|
+
visited: /* @__PURE__ */ new Set(),
|
|
18878
|
+
partial: wasIncomplete,
|
|
18879
|
+
currentStatementId: entryId,
|
|
18880
|
+
unreached
|
|
18881
|
+
};
|
|
18882
|
+
const materialized = materializeValue(syms.get(entryId), ctx);
|
|
18883
|
+
const root = isElementNode(materialized) ? materialized : null;
|
|
18884
|
+
if (root) root.statementId = entryId;
|
|
18885
|
+
const { stateDeclarations, queryStatements, mutationStatements } = extractStatements(
|
|
18886
|
+
typedStmts,
|
|
18887
|
+
ctx
|
|
18888
|
+
);
|
|
18889
|
+
const orphaned = [...unreached];
|
|
18890
|
+
return {
|
|
18891
|
+
root,
|
|
18892
|
+
meta: {
|
|
18893
|
+
incomplete: wasIncomplete,
|
|
18894
|
+
unresolved: unres,
|
|
18895
|
+
orphaned,
|
|
18896
|
+
statementCount: stmtCount,
|
|
18897
|
+
errors
|
|
18898
|
+
},
|
|
18899
|
+
stateDeclarations,
|
|
18900
|
+
queryStatements,
|
|
18901
|
+
mutationStatements
|
|
18902
|
+
};
|
|
18903
|
+
}
|
|
18904
|
+
function skipString(input, start) {
|
|
18905
|
+
if (input[start] !== '"') return start;
|
|
18906
|
+
let i = start + 1;
|
|
18907
|
+
while (i < input.length) {
|
|
18908
|
+
const c = input[i];
|
|
18909
|
+
if (c === "\\") {
|
|
18910
|
+
i += 2;
|
|
18911
|
+
} else if (c === '"') {
|
|
18912
|
+
return i + 1;
|
|
18913
|
+
} else {
|
|
18914
|
+
i++;
|
|
18915
|
+
}
|
|
18916
|
+
}
|
|
18917
|
+
return i;
|
|
18918
|
+
}
|
|
18919
|
+
function stripFences(input) {
|
|
18920
|
+
const blocks = [];
|
|
18921
|
+
let i = 0;
|
|
18922
|
+
while (i < input.length) {
|
|
18923
|
+
let fenceStart = -1;
|
|
18924
|
+
while (i < input.length) {
|
|
18925
|
+
const nextI = skipString(input, i);
|
|
18926
|
+
if (nextI > i) {
|
|
18927
|
+
i = nextI;
|
|
18928
|
+
continue;
|
|
18929
|
+
}
|
|
18930
|
+
const c = input[i];
|
|
18931
|
+
if (c === "`" && i + 1 < input.length && input[i + 1] === "`" && i + 2 < input.length && input[i + 2] === "`") {
|
|
18932
|
+
fenceStart = i;
|
|
18933
|
+
break;
|
|
18934
|
+
}
|
|
18935
|
+
i++;
|
|
18936
|
+
}
|
|
18937
|
+
if (fenceStart === -1) break;
|
|
18938
|
+
let j = fenceStart + 3;
|
|
18939
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
18940
|
+
if (j >= input.length) {
|
|
18941
|
+
blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
|
|
18942
|
+
i = input.length;
|
|
18943
|
+
break;
|
|
18944
|
+
}
|
|
18945
|
+
j++;
|
|
18946
|
+
let closePos = -1;
|
|
18947
|
+
let k = j;
|
|
18948
|
+
while (k < input.length) {
|
|
18949
|
+
const nextK = skipString(input, k);
|
|
18950
|
+
if (nextK > k) {
|
|
18951
|
+
k = nextK;
|
|
18952
|
+
continue;
|
|
18953
|
+
}
|
|
18954
|
+
const c = input[k];
|
|
18955
|
+
if (c === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
|
|
18956
|
+
closePos = k;
|
|
18957
|
+
break;
|
|
18958
|
+
}
|
|
18959
|
+
k++;
|
|
18960
|
+
}
|
|
18961
|
+
if (closePos !== -1) {
|
|
18962
|
+
blocks.push(input.slice(j, closePos));
|
|
18963
|
+
i = closePos + 3;
|
|
18964
|
+
} else {
|
|
18965
|
+
blocks.push(input.slice(j));
|
|
18966
|
+
i = input.length;
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18969
|
+
if (blocks.length > 0) return blocks.join("\n");
|
|
18970
|
+
if (input.startsWith("```")) {
|
|
18971
|
+
let j = 3;
|
|
18972
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
18973
|
+
const start = j < input.length ? j + 1 : 3;
|
|
18974
|
+
const body = input.slice(start);
|
|
18975
|
+
const trailingFence = body.lastIndexOf("```");
|
|
18976
|
+
if (trailingFence !== -1) {
|
|
18977
|
+
return body.slice(0, trailingFence);
|
|
18978
|
+
}
|
|
18979
|
+
return body;
|
|
18980
|
+
}
|
|
18981
|
+
return input;
|
|
18982
|
+
}
|
|
18983
|
+
function stripComments(input) {
|
|
18984
|
+
let inStr = false;
|
|
18985
|
+
return input.split("\n").map((line) => {
|
|
18986
|
+
for (let i = 0; i < line.length; i++) {
|
|
18987
|
+
const c = line[i];
|
|
18988
|
+
if (inStr) {
|
|
18989
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
18990
|
+
i++;
|
|
18991
|
+
continue;
|
|
18992
|
+
}
|
|
18993
|
+
if (c === inStr) inStr = false;
|
|
18994
|
+
continue;
|
|
18995
|
+
}
|
|
18996
|
+
if (c === '"' || c === "'") {
|
|
18997
|
+
inStr = c;
|
|
18998
|
+
continue;
|
|
18999
|
+
}
|
|
19000
|
+
if (c === "/" && line[i + 1] === "/") {
|
|
19001
|
+
return line.substring(0, i).trimEnd();
|
|
19002
|
+
}
|
|
19003
|
+
if (c === "#") {
|
|
19004
|
+
return line.substring(0, i).trimEnd();
|
|
19005
|
+
}
|
|
19006
|
+
}
|
|
19007
|
+
return line;
|
|
19008
|
+
}).join("\n");
|
|
19009
|
+
}
|
|
19010
|
+
function preprocess(input) {
|
|
19011
|
+
return stripComments(stripFences(input.trim())).trim();
|
|
19012
|
+
}
|
|
19013
|
+
function parse(input, cat, rootName) {
|
|
19014
|
+
const trimmed = preprocess(input);
|
|
19015
|
+
if (!trimmed) return emptyResult();
|
|
19016
|
+
const { text, wasIncomplete } = autoClose(trimmed);
|
|
19017
|
+
const stmts = split(tokenize(text));
|
|
19018
|
+
if (!stmts.length) return emptyResult(wasIncomplete);
|
|
19019
|
+
const stmtMap = /* @__PURE__ */ new Map();
|
|
19020
|
+
let firstId = "";
|
|
19021
|
+
for (const s of stmts) {
|
|
19022
|
+
const expr = parseExpression(s.tokens);
|
|
19023
|
+
const stmt = classifyStatement(s, expr);
|
|
19024
|
+
stmtMap.set(s.id, stmt);
|
|
19025
|
+
if (!firstId) firstId = s.id;
|
|
19026
|
+
}
|
|
19027
|
+
const typedStmts = [...stmtMap.values()];
|
|
19028
|
+
return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
|
|
19029
|
+
}
|
|
19030
|
+
function createStreamParser(cat, rootName) {
|
|
19031
|
+
let buf = "";
|
|
19032
|
+
let cleaned = "";
|
|
19033
|
+
let completedEnd = 0;
|
|
19034
|
+
const completedStmtMap = /* @__PURE__ */ new Map();
|
|
19035
|
+
let firstId = "";
|
|
19036
|
+
function addStmt(text) {
|
|
19037
|
+
const t = text.trim();
|
|
19038
|
+
if (!t) return;
|
|
19039
|
+
for (const s of split(tokenize(t))) {
|
|
19040
|
+
const expr = parseExpression(s.tokens);
|
|
19041
|
+
const stmt = classifyStatement(s, expr);
|
|
19042
|
+
completedStmtMap.set(s.id, stmt);
|
|
19043
|
+
if (!firstId) firstId = s.id;
|
|
19044
|
+
}
|
|
19045
|
+
}
|
|
19046
|
+
function refreshCleaned() {
|
|
19047
|
+
const next = preprocess(buf);
|
|
19048
|
+
if (!next.startsWith(cleaned.slice(0, completedEnd))) {
|
|
19049
|
+
completedEnd = 0;
|
|
19050
|
+
completedStmtMap.clear();
|
|
19051
|
+
firstId = "";
|
|
19052
|
+
}
|
|
19053
|
+
cleaned = next;
|
|
19054
|
+
}
|
|
19055
|
+
function scanNewCompleted() {
|
|
19056
|
+
let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
|
|
19057
|
+
let stmtStart = completedEnd;
|
|
19058
|
+
for (let i = completedEnd; i < cleaned.length; i++) {
|
|
19059
|
+
const c = cleaned[i];
|
|
19060
|
+
if (esc) {
|
|
19061
|
+
esc = false;
|
|
19062
|
+
continue;
|
|
19063
|
+
}
|
|
19064
|
+
if (c === "\\" && inStr) {
|
|
19065
|
+
esc = true;
|
|
19066
|
+
continue;
|
|
19067
|
+
}
|
|
19068
|
+
if (inStr) {
|
|
19069
|
+
if (c === inStr) inStr = false;
|
|
19070
|
+
continue;
|
|
19071
|
+
}
|
|
19072
|
+
if (c === '"' || c === "'") {
|
|
19073
|
+
inStr = c;
|
|
19074
|
+
continue;
|
|
19075
|
+
}
|
|
19076
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
19077
|
+
else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
19078
|
+
else if (c === "?" && depth === 0) ternaryDepth++;
|
|
19079
|
+
else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
19080
|
+
else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
|
|
19081
|
+
let peek = i + 1;
|
|
19082
|
+
while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n"))
|
|
19083
|
+
peek++;
|
|
19084
|
+
if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) {
|
|
19085
|
+
continue;
|
|
19086
|
+
}
|
|
19087
|
+
const t = cleaned.slice(stmtStart, i).trim();
|
|
19088
|
+
if (t) addStmt(t);
|
|
19089
|
+
stmtStart = i + 1;
|
|
19090
|
+
completedEnd = i + 1;
|
|
19091
|
+
}
|
|
19092
|
+
}
|
|
19093
|
+
return stmtStart;
|
|
19094
|
+
}
|
|
19095
|
+
function currentResult() {
|
|
19096
|
+
refreshCleaned();
|
|
19097
|
+
const pendingStart = scanNewCompleted();
|
|
19098
|
+
const pendingText = cleaned.slice(pendingStart).trim();
|
|
19099
|
+
if (!pendingText) {
|
|
19100
|
+
if (completedStmtMap.size === 0) return emptyResult();
|
|
19101
|
+
return buildResult(
|
|
19102
|
+
completedStmtMap,
|
|
19103
|
+
[...completedStmtMap.values()],
|
|
19104
|
+
firstId,
|
|
19105
|
+
false,
|
|
19106
|
+
completedStmtMap.size,
|
|
19107
|
+
cat,
|
|
19108
|
+
rootName
|
|
19109
|
+
);
|
|
19110
|
+
}
|
|
19111
|
+
const { text: closed, wasIncomplete } = autoClose(pendingText);
|
|
19112
|
+
const stmts = split(tokenize(closed));
|
|
19113
|
+
if (!stmts.length) {
|
|
19114
|
+
if (completedStmtMap.size === 0) return emptyResult(wasIncomplete);
|
|
19115
|
+
return buildResult(
|
|
19116
|
+
completedStmtMap,
|
|
19117
|
+
[...completedStmtMap.values()],
|
|
19118
|
+
firstId,
|
|
19119
|
+
wasIncomplete,
|
|
19120
|
+
completedStmtMap.size,
|
|
19121
|
+
cat,
|
|
19122
|
+
rootName
|
|
19123
|
+
);
|
|
19124
|
+
}
|
|
19125
|
+
const allStmtMap = new Map(completedStmtMap);
|
|
19126
|
+
for (const s of stmts) {
|
|
19127
|
+
const expr = parseExpression(s.tokens);
|
|
19128
|
+
const stmt = classifyStatement(s, expr);
|
|
19129
|
+
allStmtMap.set(s.id, stmt);
|
|
19130
|
+
}
|
|
19131
|
+
const allTypedStmts = [...allStmtMap.values()];
|
|
19132
|
+
const fid = firstId || stmts[0].id;
|
|
19133
|
+
return buildResult(
|
|
19134
|
+
allStmtMap,
|
|
19135
|
+
allTypedStmts,
|
|
19136
|
+
fid,
|
|
19137
|
+
wasIncomplete,
|
|
19138
|
+
allStmtMap.size,
|
|
19139
|
+
cat,
|
|
19140
|
+
rootName
|
|
19141
|
+
);
|
|
19142
|
+
}
|
|
19143
|
+
function reset() {
|
|
19144
|
+
buf = "";
|
|
19145
|
+
cleaned = "";
|
|
19146
|
+
completedEnd = 0;
|
|
19147
|
+
completedStmtMap.clear();
|
|
19148
|
+
firstId = "";
|
|
19149
|
+
}
|
|
19150
|
+
return {
|
|
19151
|
+
push(chunk) {
|
|
19152
|
+
buf += chunk;
|
|
19153
|
+
return currentResult();
|
|
19154
|
+
},
|
|
19155
|
+
set(fullText) {
|
|
19156
|
+
if (fullText.length < buf.length || !fullText.startsWith(buf)) {
|
|
19157
|
+
reset();
|
|
19158
|
+
}
|
|
19159
|
+
const delta = fullText.slice(buf.length);
|
|
19160
|
+
if (delta) buf += delta;
|
|
19161
|
+
return currentResult();
|
|
19162
|
+
},
|
|
19163
|
+
getResult: currentResult
|
|
19164
|
+
};
|
|
19165
|
+
}
|
|
19166
|
+
function useGenUIProgram(text, catalog) {
|
|
19167
|
+
const parserRef = React14.useRef(
|
|
19168
|
+
null
|
|
19169
|
+
);
|
|
19170
|
+
if (!parserRef.current || parserRef.current.catalog !== catalog) {
|
|
19171
|
+
parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
|
|
19172
|
+
}
|
|
19173
|
+
const lastGoodRef = React14.useRef(null);
|
|
19174
|
+
return React14.useMemo(() => {
|
|
19175
|
+
const result = parserRef.current.parser.set(text);
|
|
19176
|
+
if (result.root) {
|
|
19177
|
+
lastGoodRef.current = result.root;
|
|
19178
|
+
}
|
|
19179
|
+
return { result, root: result.root ?? lastGoodRef.current };
|
|
19180
|
+
}, [text, catalog]);
|
|
19181
|
+
}
|
|
19182
|
+
var noop = () => {
|
|
19183
|
+
};
|
|
19184
|
+
function childKey(value, index) {
|
|
19185
|
+
if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
|
|
19186
|
+
return `i:${index}`;
|
|
19187
|
+
}
|
|
19188
|
+
var RenderDepthContext = React14.createContext(0);
|
|
19189
|
+
var MAX_DEPTH = 64;
|
|
19190
|
+
var GenUINode = React14.memo(function GenUINode2({
|
|
19191
|
+
node,
|
|
19192
|
+
components,
|
|
19193
|
+
onAction,
|
|
19194
|
+
isStreaming,
|
|
19195
|
+
onTelemetry
|
|
19196
|
+
}) {
|
|
19197
|
+
const depth = React14.useContext(RenderDepthContext);
|
|
19198
|
+
if (depth > MAX_DEPTH) {
|
|
19199
|
+
onTelemetry?.({ kind: "dropped-node", typeName: node.typeName });
|
|
19200
|
+
return null;
|
|
19201
|
+
}
|
|
19202
|
+
const renderer = components[node.typeName];
|
|
19203
|
+
if (!renderer) {
|
|
19204
|
+
onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
|
|
19205
|
+
return null;
|
|
19206
|
+
}
|
|
19207
|
+
const render = (value) => {
|
|
19208
|
+
if (value == null) return null;
|
|
19209
|
+
if (isElementNode(value)) {
|
|
19210
|
+
return /* @__PURE__ */ jsxRuntime.jsx(RenderDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
19211
|
+
GenUINode2,
|
|
19212
|
+
{
|
|
19213
|
+
node: value,
|
|
19214
|
+
components,
|
|
19215
|
+
onAction,
|
|
19216
|
+
isStreaming,
|
|
19217
|
+
onTelemetry
|
|
19218
|
+
}
|
|
19219
|
+
) });
|
|
19220
|
+
}
|
|
19221
|
+
if (Array.isArray(value)) {
|
|
19222
|
+
return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React14__default.default.Fragment, { children: render(item) }, childKey(item, index)));
|
|
19223
|
+
}
|
|
19224
|
+
if (typeof value === "object" && isRuntimeExpr(value)) return null;
|
|
19225
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
19226
|
+
return null;
|
|
19227
|
+
};
|
|
19228
|
+
return renderer({ node, ctx: { render, onAction, isStreaming } });
|
|
19229
|
+
});
|
|
19230
|
+
function GenUIView({
|
|
19231
|
+
root,
|
|
19232
|
+
components,
|
|
19233
|
+
onAction = noop,
|
|
19234
|
+
isStreaming = false,
|
|
19235
|
+
onTelemetry,
|
|
19236
|
+
placeholder = null
|
|
19237
|
+
}) {
|
|
19238
|
+
if (!root) return placeholder;
|
|
19239
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
19240
|
+
GenUINode,
|
|
19241
|
+
{
|
|
19242
|
+
node: root,
|
|
19243
|
+
components,
|
|
19244
|
+
onAction,
|
|
19245
|
+
isStreaming,
|
|
19246
|
+
onTelemetry
|
|
19247
|
+
}
|
|
19248
|
+
);
|
|
19249
|
+
}
|
|
19250
|
+
|
|
19251
|
+
// src/genui/registry.ts
|
|
19252
|
+
var CIMPLIFY_GENUI_REGISTRY = {
|
|
19253
|
+
version: "3",
|
|
19254
|
+
components: [
|
|
19255
|
+
{
|
|
19256
|
+
name: "Stack",
|
|
19257
|
+
description: 'Layout container. direction: "column" (default) or "row"; gap in points (default 12)',
|
|
19258
|
+
params: [{ name: "children", required: true }, { name: "direction" }, { name: "gap" }]
|
|
19259
|
+
},
|
|
19260
|
+
{
|
|
19261
|
+
name: "Card",
|
|
19262
|
+
description: "Elevated surface grouping related content, with an optional title",
|
|
19263
|
+
params: [{ name: "children", required: true }, { name: "title" }]
|
|
19264
|
+
},
|
|
19265
|
+
{
|
|
19266
|
+
name: "TextContent",
|
|
19267
|
+
description: 'Text block. size: "display", "heading", "title", "body" (default), "label", "caption"',
|
|
19268
|
+
params: [{ name: "text", required: true }, { name: "size" }]
|
|
19269
|
+
},
|
|
19270
|
+
{
|
|
19271
|
+
name: "Stat",
|
|
19272
|
+
description: "Compact stat: small label over a bold value, optional sub line and tone",
|
|
19273
|
+
params: [
|
|
19274
|
+
{ name: "label", required: true },
|
|
19275
|
+
{ name: "value", required: true },
|
|
19276
|
+
{ name: "sub" },
|
|
19277
|
+
{ name: "tone" }
|
|
19278
|
+
]
|
|
19279
|
+
},
|
|
19280
|
+
{
|
|
19281
|
+
name: "Metric",
|
|
19282
|
+
description: "KPI card with label, value, optional percent change and SF Symbol icon",
|
|
19283
|
+
params: [
|
|
19284
|
+
{ name: "label", required: true },
|
|
19285
|
+
{ name: "value", required: true },
|
|
19286
|
+
{ name: "change" },
|
|
19287
|
+
{ name: "icon" }
|
|
19288
|
+
]
|
|
19289
|
+
},
|
|
19290
|
+
{
|
|
19291
|
+
name: "KeyValue",
|
|
19292
|
+
description: "Label/value row; set mono true for numeric values",
|
|
19293
|
+
params: [
|
|
19294
|
+
{ name: "label", required: true },
|
|
19295
|
+
{ name: "value", required: true },
|
|
19296
|
+
{ name: "mono" }
|
|
19297
|
+
]
|
|
19298
|
+
},
|
|
19299
|
+
{
|
|
19300
|
+
name: "Badge",
|
|
19301
|
+
description: "Small status chip. tone: neutral, positive, info, indigo, violet, warning, danger",
|
|
19302
|
+
params: [{ name: "label", required: true }, { name: "tone" }]
|
|
19303
|
+
},
|
|
19304
|
+
{
|
|
19305
|
+
name: "Callout",
|
|
19306
|
+
description: "Inline alert with title, optional message. tone: info (default), success, warning, danger, accent, neutral",
|
|
19307
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "tone" }]
|
|
19308
|
+
},
|
|
19309
|
+
{
|
|
19310
|
+
name: "Button",
|
|
19311
|
+
description: 'Action button. action: "action:name" string or Action([...]) steps. variant: primary, cta, secondary (default), ghost, destructive',
|
|
19312
|
+
params: [
|
|
19313
|
+
{ name: "label", required: true },
|
|
19314
|
+
{ name: "action", required: true },
|
|
19315
|
+
{ name: "variant" }
|
|
19316
|
+
]
|
|
19317
|
+
},
|
|
19318
|
+
{
|
|
19319
|
+
name: "Buttons",
|
|
19320
|
+
description: 'Row (default) or column of Buttons; direction: "row" or "column"',
|
|
19321
|
+
params: [{ name: "children", required: true }, { name: "direction" }]
|
|
19322
|
+
},
|
|
19323
|
+
{
|
|
19324
|
+
name: "Divider",
|
|
19325
|
+
description: "Hairline separator",
|
|
19326
|
+
params: []
|
|
19327
|
+
},
|
|
19328
|
+
{
|
|
19329
|
+
name: "Table",
|
|
19330
|
+
description: "Data table: headers is an array of strings, rows an array of cell arrays",
|
|
19331
|
+
params: [
|
|
19332
|
+
{ name: "headers", required: true },
|
|
19333
|
+
{ name: "rows", required: true }
|
|
19334
|
+
]
|
|
19335
|
+
},
|
|
19336
|
+
{
|
|
19337
|
+
name: "BarChart",
|
|
19338
|
+
description: "Bar chart: labels (x axis strings) and values (numbers), optional title",
|
|
19339
|
+
params: [
|
|
19340
|
+
{ name: "labels", required: true },
|
|
19341
|
+
{ name: "values", required: true },
|
|
19342
|
+
{ name: "title" }
|
|
19343
|
+
]
|
|
19344
|
+
},
|
|
19345
|
+
{
|
|
19346
|
+
name: "LineChart",
|
|
19347
|
+
description: "Line chart with area fill: labels and values, optional title",
|
|
19348
|
+
params: [
|
|
19349
|
+
{ name: "labels", required: true },
|
|
19350
|
+
{ name: "values", required: true },
|
|
19351
|
+
{ name: "title" }
|
|
19352
|
+
]
|
|
19353
|
+
},
|
|
19354
|
+
{
|
|
19355
|
+
name: "Money",
|
|
19356
|
+
description: "Formatted money amount, never abbreviated. currency: ISO code (default USD)",
|
|
19357
|
+
params: [{ name: "amount", required: true }, { name: "currency" }]
|
|
19358
|
+
},
|
|
19359
|
+
{
|
|
19360
|
+
name: "Image",
|
|
19361
|
+
description: "Remote image by URL with optional caption",
|
|
19362
|
+
params: [{ name: "url", required: true }, { name: "caption" }]
|
|
19363
|
+
},
|
|
19364
|
+
{
|
|
19365
|
+
name: "ListRow",
|
|
19366
|
+
description: "Entity row for orders/customers/products/invoices: initials avatar (or SF Symbol tile via symbol), title + subtitle, trailing value and badge with tone",
|
|
19367
|
+
params: [
|
|
19368
|
+
{ name: "title", required: true },
|
|
19369
|
+
{ name: "subtitle" },
|
|
19370
|
+
{ name: "value" },
|
|
19371
|
+
{ name: "badge" },
|
|
19372
|
+
{ name: "tone" },
|
|
19373
|
+
{ name: "symbol" }
|
|
19374
|
+
]
|
|
19375
|
+
},
|
|
19376
|
+
{
|
|
19377
|
+
name: "Timeline",
|
|
19378
|
+
description: "Event history (order journey, payment trail): events is an array of {title, detail?, time?, tone?} objects, newest first",
|
|
19379
|
+
params: [{ name: "events", required: true }]
|
|
19380
|
+
},
|
|
19381
|
+
{
|
|
19382
|
+
name: "Progress",
|
|
19383
|
+
description: "Amount toward a bound (goal, stock cover, capacity): numeric value and target (default 100), optional detail caption and tone",
|
|
19384
|
+
params: [
|
|
19385
|
+
{ name: "label", required: true },
|
|
19386
|
+
{ name: "value", required: true },
|
|
19387
|
+
{ name: "target" },
|
|
19388
|
+
{ name: "detail" },
|
|
19389
|
+
{ name: "tone" }
|
|
19390
|
+
]
|
|
19391
|
+
},
|
|
19392
|
+
{
|
|
19393
|
+
name: "DonutChart",
|
|
19394
|
+
description: "Share of a whole (sales by channel, revenue by category): labels + values, optional title and center display string (e.g. the formatted total)",
|
|
19395
|
+
params: [
|
|
19396
|
+
{ name: "labels", required: true },
|
|
19397
|
+
{ name: "values", required: true },
|
|
19398
|
+
{ name: "title" },
|
|
19399
|
+
{ name: "center" }
|
|
19400
|
+
]
|
|
19401
|
+
},
|
|
19402
|
+
{
|
|
19403
|
+
name: "StatusTrack",
|
|
19404
|
+
description: "Stage tracker (order fulfilment, invoice lifecycle): steps are stage names, current is the active step name or index",
|
|
19405
|
+
params: [
|
|
19406
|
+
{ name: "steps", required: true },
|
|
19407
|
+
{ name: "current", required: true }
|
|
19408
|
+
]
|
|
19409
|
+
},
|
|
19410
|
+
{
|
|
19411
|
+
name: "LineItems",
|
|
19412
|
+
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",
|
|
19413
|
+
params: [
|
|
19414
|
+
{ name: "items", required: true },
|
|
19415
|
+
{ name: "total", required: true },
|
|
19416
|
+
{ name: "adjustments" }
|
|
19417
|
+
]
|
|
19418
|
+
},
|
|
19419
|
+
{
|
|
19420
|
+
name: "Grid",
|
|
19421
|
+
description: "Tile layout for Stat/Metric children, columns default 2",
|
|
19422
|
+
params: [{ name: "children", required: true }, { name: "columns" }]
|
|
19423
|
+
},
|
|
19424
|
+
{
|
|
19425
|
+
name: "Banner",
|
|
19426
|
+
description: "Hero-stat moment on a tonal wash: big value, label, optional percent delta. tone: positive (default), warning, danger, info, neutral",
|
|
19427
|
+
params: [
|
|
19428
|
+
{ name: "value", required: true },
|
|
19429
|
+
{ name: "label", required: true },
|
|
19430
|
+
{ name: "delta" },
|
|
19431
|
+
{ name: "tone" }
|
|
19432
|
+
]
|
|
19433
|
+
},
|
|
19434
|
+
{
|
|
19435
|
+
name: "EmptyState",
|
|
19436
|
+
description: "Nothing-to-show state with an SF Symbol and optional message",
|
|
19437
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "symbol" }]
|
|
19438
|
+
}
|
|
19439
|
+
]
|
|
19440
|
+
};
|
|
19441
|
+
|
|
19442
|
+
// src/genui/manifest.ts
|
|
19443
|
+
function genUIManifest(registry) {
|
|
19444
|
+
return {
|
|
19445
|
+
version: registry.version,
|
|
19446
|
+
components: registry.components.map((component) => ({
|
|
19447
|
+
name: component.name,
|
|
19448
|
+
description: component.description,
|
|
19449
|
+
params: component.params.map((param) => ({
|
|
19450
|
+
name: param.name,
|
|
19451
|
+
required: param.required ?? false
|
|
19452
|
+
}))
|
|
19453
|
+
}))
|
|
19454
|
+
};
|
|
19455
|
+
}
|
|
19456
|
+
function encodeString(value) {
|
|
19457
|
+
return JSON.stringify(value).replace(/\//g, "\\/");
|
|
19458
|
+
}
|
|
19459
|
+
function canonicalize(value) {
|
|
19460
|
+
if (value === null) return "null";
|
|
19461
|
+
if (typeof value === "string") return encodeString(value);
|
|
19462
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
19463
|
+
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
19464
|
+
const keys = Object.keys(value).sort();
|
|
19465
|
+
const entries = keys.map((key) => `${encodeString(key)}:${canonicalize(value[key])}`);
|
|
19466
|
+
return `{${entries.join(",")}}`;
|
|
19467
|
+
}
|
|
19468
|
+
function genUIManifestJson(manifest) {
|
|
19469
|
+
return canonicalize(manifest);
|
|
19470
|
+
}
|
|
19471
|
+
async function genUIFingerprint(manifest) {
|
|
19472
|
+
const bytes = new TextEncoder().encode(genUIManifestJson(manifest));
|
|
19473
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
19474
|
+
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
19475
|
+
}
|
|
19476
|
+
|
|
19477
|
+
Object.defineProperty(exports, "getVariantDisplayName", {
|
|
19478
|
+
enumerable: true,
|
|
19479
|
+
get: function () { return chunkT7Z25EY4_js.getVariantDisplayName; }
|
|
19480
|
+
});
|
|
19481
|
+
Object.defineProperty(exports, "messagePreview", {
|
|
19482
|
+
enumerable: true,
|
|
19483
|
+
get: function () { return chunkXREIZ56K_js.messagePreview; }
|
|
19484
|
+
});
|
|
19485
|
+
Object.defineProperty(exports, "splitGenUIContent", {
|
|
19486
|
+
enumerable: true,
|
|
19487
|
+
get: function () { return chunkXREIZ56K_js.splitGenUIContent; }
|
|
17244
19488
|
});
|
|
17245
19489
|
exports.AccommodationCard = AccommodationCard;
|
|
17246
19490
|
exports.AccountContent = AccountContent;
|
|
@@ -17276,6 +19520,7 @@ exports.BundleProductCard = BundleProductCard;
|
|
|
17276
19520
|
exports.BundleProductLayout = BundleProductLayout;
|
|
17277
19521
|
exports.BundleSelector = BundleSelector;
|
|
17278
19522
|
exports.Button = Button;
|
|
19523
|
+
exports.CIMPLIFY_GENUI_REGISTRY = CIMPLIFY_GENUI_REGISTRY;
|
|
17279
19524
|
exports.CardImage = CardImage;
|
|
17280
19525
|
exports.CardShell = CardShell;
|
|
17281
19526
|
exports.CardVariant = CardVariant;
|
|
@@ -17289,6 +19534,7 @@ exports.CataloguePage = CataloguePage;
|
|
|
17289
19534
|
exports.CatalogueTemplate = CatalogueTemplate;
|
|
17290
19535
|
exports.CategoryFilter = CategoryFilter;
|
|
17291
19536
|
exports.CategoryGrid = CategoryGrid;
|
|
19537
|
+
exports.ChatMarkdown = ChatMarkdown;
|
|
17292
19538
|
exports.ChatWidget = ChatWidget;
|
|
17293
19539
|
exports.CheckIcon = CheckIcon;
|
|
17294
19540
|
exports.CheckoutPage = CheckoutPage;
|
|
@@ -17334,6 +19580,7 @@ exports.FeaturedCollectionLayout = FeaturedCollectionLayout;
|
|
|
17334
19580
|
exports.FilterTabs = FilterTabs;
|
|
17335
19581
|
exports.FoodProductCard = FoodProductCard;
|
|
17336
19582
|
exports.FoodProductLayout = FoodProductLayout;
|
|
19583
|
+
exports.GenUIView = GenUIView;
|
|
17337
19584
|
exports.HappeningCard = HappeningCard;
|
|
17338
19585
|
exports.IdentityIcon = IdentityIcon;
|
|
17339
19586
|
exports.IdentityOfframp = IdentityOfframp;
|
|
@@ -17419,9 +19666,17 @@ exports.WelcomeCard = WelcomeCard;
|
|
|
17419
19666
|
exports.WholesaleProductCard = WholesaleProductCard;
|
|
17420
19667
|
exports.WholesaleProductLayout = WholesaleProductLayout;
|
|
17421
19668
|
exports.WishlistButton = WishlistButton;
|
|
19669
|
+
exports.clearOrdersCache = clearOrdersCache;
|
|
17422
19670
|
exports.cn = cn;
|
|
17423
19671
|
exports.defaultBoundaryKind = defaultBoundaryKind;
|
|
19672
|
+
exports.genUICatalog = genUICatalog;
|
|
19673
|
+
exports.genUIFingerprint = genUIFingerprint;
|
|
19674
|
+
exports.genUIManifest = genUIManifest;
|
|
19675
|
+
exports.genUIManifestJson = genUIManifestJson;
|
|
17424
19676
|
exports.getBoundaryLabels = getBoundaryLabels;
|
|
19677
|
+
exports.isTerminalOrderStatus = isTerminalOrderStatus;
|
|
19678
|
+
exports.parseGenUIProgram = parse;
|
|
19679
|
+
exports.partitionMessages = partitionMessages;
|
|
17425
19680
|
exports.prefetchProduct = prefetchProduct;
|
|
17426
19681
|
exports.prefetchProducts = prefetchProducts;
|
|
17427
19682
|
exports.resolveBoundaryKind = resolveBoundaryKind;
|
|
@@ -17455,6 +19710,7 @@ exports.useDeliveryOptions = useDeliveryOptions;
|
|
|
17455
19710
|
exports.useElements = useElements;
|
|
17456
19711
|
exports.useElementsReady = useElementsReady;
|
|
17457
19712
|
exports.useFxRate = useFxRate;
|
|
19713
|
+
exports.useGenUIProgram = useGenUIProgram;
|
|
17458
19714
|
exports.useLocations = useLocations;
|
|
17459
19715
|
exports.useOptionalCimplify = useOptionalCimplify;
|
|
17460
19716
|
exports.useOrder = useOrder;
|