@cimplify/sdk 0.88.0 → 0.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/{chunk-A7NODD4Q.mjs → chunk-2UDEWILL.mjs} +83 -17
- package/dist/{chunk-W5BVGAO6.js → chunk-BN4V4ONE.js} +83 -17
- package/dist/{chunk-6JA4HBBD.js → chunk-JVWFIFQ7.js} +4 -4
- package/dist/{chunk-ROBTTS3Q.js → chunk-KJP4U6L3.js} +28 -1
- package/dist/{chunk-VU5LFNEX.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-D0PrO1-Q.d.ts → client-CR1f94wY.d.ts} +97 -7
- package/dist/{client-B3FBDfTg.d.ts → client-CqpOcbin.d.ts} +2 -2
- package/dist/{client-B-8ceVyt.d.mts → client-DMttx1xk.d.mts} +2 -2
- package/dist/{client-r5VSGFRU.d.mts → client-YiSelM7x.d.mts} +97 -7
- package/dist/{index-C-Pxrzna.d.ts → index-CxTVu0HZ.d.ts} +3 -3
- package/dist/{index-DLulxmF1.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-TnJUoHqL.d.mts → payment-5d8FTA1q.d.mts} +1 -1
- package/dist/{payment-Cfoo9MT7.d.ts → payment-BX7ccIKY.d.ts} +1 -1
- package/dist/{price-CCDSrIsc.d.ts → price-DtfP_Xfq.d.ts} +1 -1
- package/dist/{price-CM0zVSyM.d.mts → price-arl_4Rzn.d.mts} +1 -1
- package/dist/{product-DoA36Eqh.d.mts → product-DZX4QuKS.d.mts} +68 -4
- package/dist/{product-DoA36Eqh.d.ts → product-DZX4QuKS.d.ts} +68 -4
- package/dist/react.d.mts +405 -7
- package/dist/react.d.ts +405 -7
- package/dist/react.js +2345 -74
- package/dist/react.mjs +2323 -72
- package/dist/{server-CI1CUFVu.d.mts → server-BceZMxcK.d.mts} +8 -1
- package/dist/{server-CHfYeTAa.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,21 +5909,45 @@ 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);
|
|
5907
5926
|
}, []);
|
|
5908
|
-
const
|
|
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);
|
|
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
|
+
);
|
|
5948
|
+
const fetchNewMessages = React14.useCallback(async () => {
|
|
5909
5949
|
const c = clientRef.current;
|
|
5910
5950
|
if (!c) return;
|
|
5911
|
-
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
|
|
5912
5951
|
const last = messagesRef.current[messagesRef.current.length - 1];
|
|
5913
5952
|
const result = await c.support.getMessages({
|
|
5914
5953
|
after: last?.created_at,
|
|
@@ -5918,6 +5957,10 @@ function useChat(options = {}) {
|
|
|
5918
5957
|
appendMessages(result.value);
|
|
5919
5958
|
}
|
|
5920
5959
|
}, [appendMessages]);
|
|
5960
|
+
const pollMessages = React14.useCallback(async () => {
|
|
5961
|
+
if (Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS) return;
|
|
5962
|
+
await fetchNewMessages();
|
|
5963
|
+
}, [fetchNewMessages]);
|
|
5921
5964
|
const startPolling = React14.useCallback(() => {
|
|
5922
5965
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5923
5966
|
const interval = Date.now() - lastActivityRef.current > IDLE_TIMEOUT_MS ? pollIdleInterval : pollInterval;
|
|
@@ -5950,6 +5993,19 @@ function useChat(options = {}) {
|
|
|
5950
5993
|
const envelope = JSON.parse(event.data);
|
|
5951
5994
|
if (envelope.type === "message.created" && envelope.data) {
|
|
5952
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();
|
|
6003
|
+
} else if (envelope.type === "resync") {
|
|
6004
|
+
const latest = envelope.latest_message_at;
|
|
6005
|
+
const last = messagesRef.current[messagesRef.current.length - 1];
|
|
6006
|
+
if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
|
|
6007
|
+
fetchNewMessages();
|
|
6008
|
+
}
|
|
5953
6009
|
}
|
|
5954
6010
|
} catch {
|
|
5955
6011
|
}
|
|
@@ -5957,7 +6013,7 @@ function useChat(options = {}) {
|
|
|
5957
6013
|
ws.onclose = () => {
|
|
5958
6014
|
wsRef.current = null;
|
|
5959
6015
|
if (!openedRef.current) return;
|
|
5960
|
-
|
|
6016
|
+
fetchNewMessages();
|
|
5961
6017
|
if (wsReconnectAttemptRef.current < WS_MAX_RECONNECT_ATTEMPTS) {
|
|
5962
6018
|
const attempt = wsReconnectAttemptRef.current++;
|
|
5963
6019
|
const delay = Math.min(1e3 * Math.pow(2, attempt) + Math.random() * 500, 3e4);
|
|
@@ -5969,11 +6025,12 @@ function useChat(options = {}) {
|
|
|
5969
6025
|
ws.onerror = () => {
|
|
5970
6026
|
};
|
|
5971
6027
|
wsRef.current = ws;
|
|
5972
|
-
}, [appendMessages, startPolling, stopPolling,
|
|
6028
|
+
}, [appendMessages, startPolling, stopPolling, fetchNewMessages]);
|
|
5973
6029
|
React14.useEffect(() => {
|
|
5974
6030
|
return () => {
|
|
5975
6031
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5976
6032
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6033
|
+
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
5977
6034
|
if (wsRef.current) {
|
|
5978
6035
|
wsRef.current.onclose = null;
|
|
5979
6036
|
wsRef.current.close();
|
|
@@ -5989,6 +6046,10 @@ function useChat(options = {}) {
|
|
|
5989
6046
|
if (result.ok) {
|
|
5990
6047
|
setMessages(result.value.messages);
|
|
5991
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);
|
|
5992
6053
|
connectWebSocket();
|
|
5993
6054
|
} else {
|
|
5994
6055
|
openedRef.current = false;
|
|
@@ -5997,38 +6058,60 @@ function useChat(options = {}) {
|
|
|
5997
6058
|
setIsLoading(false);
|
|
5998
6059
|
}, [client, connectWebSocket]);
|
|
5999
6060
|
const send = React14.useCallback(
|
|
6000
|
-
async (content) => {
|
|
6001
|
-
|
|
6061
|
+
async (content, opts) => {
|
|
6062
|
+
const attachments = opts?.attachments ?? [];
|
|
6063
|
+
if (!client || !content.trim() && attachments.length === 0) return false;
|
|
6002
6064
|
setError(null);
|
|
6003
6065
|
lastActivityRef.current = Date.now();
|
|
6004
6066
|
if (!openedRef.current) {
|
|
6005
6067
|
await openConversation();
|
|
6006
|
-
if (!openedRef.current) return;
|
|
6068
|
+
if (!openedRef.current) return false;
|
|
6007
6069
|
}
|
|
6070
|
+
const replyTo = opts?.replyTo;
|
|
6008
6071
|
const optimisticId = `opt_${Date.now()}`;
|
|
6009
6072
|
const optimistic = {
|
|
6010
6073
|
id: optimisticId,
|
|
6011
6074
|
sender_type: "customer",
|
|
6012
6075
|
content: content.trim(),
|
|
6013
|
-
content_type: "text",
|
|
6014
|
-
attachments
|
|
6076
|
+
content_type: attachments.length > 0 ? "image" : "text",
|
|
6077
|
+
attachments,
|
|
6015
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,
|
|
6016
6086
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6017
6087
|
};
|
|
6018
6088
|
setMessages((prev) => [...prev, optimistic]);
|
|
6019
6089
|
setIsSending(true);
|
|
6020
|
-
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
|
+
});
|
|
6021
6094
|
if (result.ok) {
|
|
6095
|
+
const confirmed = result.value;
|
|
6022
6096
|
setMessages(
|
|
6023
|
-
(prev) =>
|
|
6097
|
+
(prev) => (
|
|
6098
|
+
// The websocket may have already streamed the confirmed message
|
|
6099
|
+
// in while the POST was in flight; drop the optimistic bubble
|
|
6100
|
+
// instead of inserting a duplicate.
|
|
6101
|
+
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== optimisticId) : prev.map((m) => m.id === optimisticId ? confirmed : m)
|
|
6102
|
+
)
|
|
6024
6103
|
);
|
|
6104
|
+
if (aiEnabledRef.current && !humanRequestedRef.current) {
|
|
6105
|
+
startAwaitingReply();
|
|
6106
|
+
}
|
|
6025
6107
|
} else {
|
|
6026
6108
|
setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
|
|
6027
6109
|
setError(result.error);
|
|
6028
6110
|
}
|
|
6029
6111
|
setIsSending(false);
|
|
6112
|
+
return result.ok;
|
|
6030
6113
|
},
|
|
6031
|
-
[client, openConversation]
|
|
6114
|
+
[client, openConversation, startAwaitingReply]
|
|
6032
6115
|
);
|
|
6033
6116
|
const startConversation = React14.useCallback(
|
|
6034
6117
|
async (starterText) => {
|
|
@@ -6039,14 +6122,65 @@ function useChat(options = {}) {
|
|
|
6039
6122
|
},
|
|
6040
6123
|
[openConversation, send]
|
|
6041
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");
|
|
6042
6169
|
return {
|
|
6043
6170
|
messages,
|
|
6044
6171
|
isLoading,
|
|
6045
6172
|
isSending,
|
|
6046
6173
|
error,
|
|
6047
6174
|
isActive: conversationOpened && messages.length > 0,
|
|
6175
|
+
humanRequested: humanRequested && !agentArrived,
|
|
6176
|
+
awaitingReply,
|
|
6177
|
+
seenAt,
|
|
6178
|
+
mayHaveOlder,
|
|
6048
6179
|
send,
|
|
6049
|
-
startConversation
|
|
6180
|
+
startConversation,
|
|
6181
|
+
react,
|
|
6182
|
+
requestHuman,
|
|
6183
|
+
loadOlder
|
|
6050
6184
|
};
|
|
6051
6185
|
}
|
|
6052
6186
|
var ElementsContext = React14.createContext({
|
|
@@ -9344,6 +9478,7 @@ function ProductCustomizer({
|
|
|
9344
9478
|
onAddToCart,
|
|
9345
9479
|
onVariantChange,
|
|
9346
9480
|
showSpecialInstructions = true,
|
|
9481
|
+
submitLabel,
|
|
9347
9482
|
className,
|
|
9348
9483
|
classNames
|
|
9349
9484
|
}) {
|
|
@@ -9732,8 +9867,10 @@ function ProductCustomizer({
|
|
|
9732
9867
|
isAdded && classNames?.submitButtonAdded,
|
|
9733
9868
|
classNames?.submitButton
|
|
9734
9869
|
),
|
|
9735
|
-
children: isAdded ? "Added
|
|
9736
|
-
"Add to Cart
|
|
9870
|
+
children: isAdded ? "Added" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9871
|
+
submitLabel ?? "Add to Cart",
|
|
9872
|
+
" \xB7",
|
|
9873
|
+
" ",
|
|
9737
9874
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: displayTotalPrice, currency })
|
|
9738
9875
|
] })
|
|
9739
9876
|
}
|
|
@@ -9893,13 +10030,13 @@ function StoreVideo({
|
|
|
9893
10030
|
autoplay = true,
|
|
9894
10031
|
loop = true,
|
|
9895
10032
|
muted = true,
|
|
9896
|
-
lazy = true,
|
|
10033
|
+
lazy: lazy2 = true,
|
|
9897
10034
|
className
|
|
9898
10035
|
}) {
|
|
9899
10036
|
const ref = React14.useRef(null);
|
|
9900
|
-
const [inView, setInView] = React14.useState(!
|
|
10037
|
+
const [inView, setInView] = React14.useState(!lazy2);
|
|
9901
10038
|
React14.useEffect(() => {
|
|
9902
|
-
if (!
|
|
10039
|
+
if (!lazy2 || inView) return;
|
|
9903
10040
|
const node = ref.current;
|
|
9904
10041
|
if (!node || typeof IntersectionObserver === "undefined") {
|
|
9905
10042
|
setInView(true);
|
|
@@ -9916,7 +10053,7 @@ function StoreVideo({
|
|
|
9916
10053
|
);
|
|
9917
10054
|
observer.observe(node);
|
|
9918
10055
|
return () => observer.disconnect();
|
|
9919
|
-
}, [
|
|
10056
|
+
}, [lazy2, inView]);
|
|
9920
10057
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9921
10058
|
"div",
|
|
9922
10059
|
{
|
|
@@ -9934,7 +10071,7 @@ function StoreVideo({
|
|
|
9934
10071
|
muted,
|
|
9935
10072
|
playsInline: true,
|
|
9936
10073
|
controls,
|
|
9937
|
-
preload:
|
|
10074
|
+
preload: lazy2 ? "metadata" : "auto",
|
|
9938
10075
|
"aria-label": alt,
|
|
9939
10076
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" },
|
|
9940
10077
|
children: poster ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -16736,6 +16873,112 @@ function CurrencySelector({
|
|
|
16736
16873
|
);
|
|
16737
16874
|
}) });
|
|
16738
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
|
+
}
|
|
16739
16982
|
function ChatWidget({
|
|
16740
16983
|
client: clientProp,
|
|
16741
16984
|
businessName = "Support",
|
|
@@ -16753,7 +16996,35 @@ function ChatWidget({
|
|
|
16753
16996
|
const client = clientProp ?? context?.client;
|
|
16754
16997
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
16755
16998
|
const [unread, setUnread] = React14.useState(0);
|
|
16756
|
-
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
|
+
);
|
|
16757
17028
|
const prevCountRef = React14.useRef(messages.length);
|
|
16758
17029
|
React14.useEffect(() => {
|
|
16759
17030
|
if (!open && messages.length > prevCountRef.current) {
|
|
@@ -16800,11 +17071,15 @@ function ChatWidget({
|
|
|
16800
17071
|
messages,
|
|
16801
17072
|
isLoading,
|
|
16802
17073
|
isSending,
|
|
17074
|
+
isTyping: showTyping,
|
|
17075
|
+
seenAt,
|
|
16803
17076
|
error,
|
|
16804
17077
|
isActive,
|
|
16805
17078
|
isLeft,
|
|
16806
17079
|
onClose: handleClose,
|
|
16807
17080
|
onSend: send,
|
|
17081
|
+
onReact: react,
|
|
17082
|
+
onAttach: attachPhoto,
|
|
16808
17083
|
onStartConversation: startConversation,
|
|
16809
17084
|
classNames
|
|
16810
17085
|
}
|
|
@@ -16857,11 +17132,15 @@ function Panel({
|
|
|
16857
17132
|
messages,
|
|
16858
17133
|
isLoading,
|
|
16859
17134
|
isSending,
|
|
17135
|
+
isTyping,
|
|
17136
|
+
seenAt,
|
|
16860
17137
|
error,
|
|
16861
17138
|
isActive,
|
|
16862
17139
|
isLeft,
|
|
16863
17140
|
onClose,
|
|
16864
17141
|
onSend,
|
|
17142
|
+
onReact,
|
|
17143
|
+
onAttach,
|
|
16865
17144
|
onStartConversation,
|
|
16866
17145
|
classNames
|
|
16867
17146
|
}) {
|
|
@@ -16899,7 +17178,9 @@ function Panel({
|
|
|
16899
17178
|
{
|
|
16900
17179
|
messages,
|
|
16901
17180
|
isLoading,
|
|
16902
|
-
isSending,
|
|
17181
|
+
isSending: isTyping,
|
|
17182
|
+
seenAt,
|
|
17183
|
+
onReact,
|
|
16903
17184
|
className: classNames?.messages
|
|
16904
17185
|
}
|
|
16905
17186
|
),
|
|
@@ -16910,6 +17191,7 @@ function Panel({
|
|
|
16910
17191
|
placeholder,
|
|
16911
17192
|
isSending,
|
|
16912
17193
|
onSend,
|
|
17194
|
+
onAttach,
|
|
16913
17195
|
className: classNames?.input
|
|
16914
17196
|
}
|
|
16915
17197
|
),
|
|
@@ -17005,16 +17287,29 @@ function Welcome({
|
|
|
17005
17287
|
}
|
|
17006
17288
|
);
|
|
17007
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
|
+
}
|
|
17008
17299
|
function MessageList({
|
|
17009
17300
|
messages,
|
|
17010
17301
|
isLoading,
|
|
17011
17302
|
isSending,
|
|
17303
|
+
seenAt,
|
|
17304
|
+
onReact,
|
|
17012
17305
|
className
|
|
17013
17306
|
}) {
|
|
17014
17307
|
const bottomRef = React14.useRef(null);
|
|
17015
17308
|
React14.useEffect(() => {
|
|
17016
17309
|
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
17017
17310
|
}, [messages.length, isSending]);
|
|
17311
|
+
const { bubbles, reactionsByTarget } = partitionMessages(messages);
|
|
17312
|
+
const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
|
|
17018
17313
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17019
17314
|
"div",
|
|
17020
17315
|
{
|
|
@@ -17024,40 +17319,111 @@ function MessageList({
|
|
|
17024
17319
|
),
|
|
17025
17320
|
children: [
|
|
17026
17321
|
isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17027
|
-
|
|
17322
|
+
bubbles.map((msg, i) => {
|
|
17028
17323
|
const isCustomer = msg.sender_type === "customer";
|
|
17029
|
-
const prev =
|
|
17324
|
+
const prev = bubbles[i - 1];
|
|
17030
17325
|
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
17031
17326
|
const isOptimistic = msg.id.startsWith("opt_");
|
|
17327
|
+
const reactions = reactionsByTarget.get(msg.id);
|
|
17032
17328
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
17033
17329
|
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
|
|
17034
|
-
/* @__PURE__ */ jsxRuntime.
|
|
17330
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17035
17331
|
"div",
|
|
17036
17332
|
{
|
|
17037
17333
|
className: cn(
|
|
17038
|
-
"flex max-w-[85%]",
|
|
17334
|
+
"group relative flex max-w-[85%]",
|
|
17039
17335
|
isCustomer ? "ml-auto" : "mr-auto"
|
|
17040
17336
|
),
|
|
17041
|
-
children:
|
|
17042
|
-
"div",
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
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
|
+
]
|
|
17059
17416
|
}
|
|
17060
|
-
)
|
|
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" })
|
|
17061
17427
|
] }, msg.id);
|
|
17062
17428
|
}),
|
|
17063
17429
|
isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
@@ -17070,10 +17436,24 @@ function ChatInput({
|
|
|
17070
17436
|
placeholder,
|
|
17071
17437
|
isSending,
|
|
17072
17438
|
onSend,
|
|
17439
|
+
onAttach,
|
|
17073
17440
|
className
|
|
17074
17441
|
}) {
|
|
17075
17442
|
const [value, setValue] = React14.useState("");
|
|
17443
|
+
const [uploading, setUploading] = React14.useState(false);
|
|
17076
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
|
+
);
|
|
17077
17457
|
const handleSend = React14.useCallback(() => {
|
|
17078
17458
|
const trimmed = value.trim();
|
|
17079
17459
|
if (!trimmed || isSending) return;
|
|
@@ -17107,6 +17487,37 @@ function ChatInput({
|
|
|
17107
17487
|
className
|
|
17108
17488
|
),
|
|
17109
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
|
+
),
|
|
17110
17521
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17111
17522
|
"div",
|
|
17112
17523
|
{
|
|
@@ -17209,6 +17620,25 @@ function SendIcon({ className }) {
|
|
|
17209
17620
|
}
|
|
17210
17621
|
);
|
|
17211
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
|
+
}
|
|
17212
17642
|
function SparkleIcon({ className }) {
|
|
17213
17643
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17214
17644
|
"svg",
|
|
@@ -17223,9 +17653,1838 @@ function SparkleIcon({ className }) {
|
|
|
17223
17653
|
);
|
|
17224
17654
|
}
|
|
17225
17655
|
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
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; }
|
|
17229
19488
|
});
|
|
17230
19489
|
exports.AccommodationCard = AccommodationCard;
|
|
17231
19490
|
exports.AccountContent = AccountContent;
|
|
@@ -17261,6 +19520,7 @@ exports.BundleProductCard = BundleProductCard;
|
|
|
17261
19520
|
exports.BundleProductLayout = BundleProductLayout;
|
|
17262
19521
|
exports.BundleSelector = BundleSelector;
|
|
17263
19522
|
exports.Button = Button;
|
|
19523
|
+
exports.CIMPLIFY_GENUI_REGISTRY = CIMPLIFY_GENUI_REGISTRY;
|
|
17264
19524
|
exports.CardImage = CardImage;
|
|
17265
19525
|
exports.CardShell = CardShell;
|
|
17266
19526
|
exports.CardVariant = CardVariant;
|
|
@@ -17274,6 +19534,7 @@ exports.CataloguePage = CataloguePage;
|
|
|
17274
19534
|
exports.CatalogueTemplate = CatalogueTemplate;
|
|
17275
19535
|
exports.CategoryFilter = CategoryFilter;
|
|
17276
19536
|
exports.CategoryGrid = CategoryGrid;
|
|
19537
|
+
exports.ChatMarkdown = ChatMarkdown;
|
|
17277
19538
|
exports.ChatWidget = ChatWidget;
|
|
17278
19539
|
exports.CheckIcon = CheckIcon;
|
|
17279
19540
|
exports.CheckoutPage = CheckoutPage;
|
|
@@ -17319,6 +19580,7 @@ exports.FeaturedCollectionLayout = FeaturedCollectionLayout;
|
|
|
17319
19580
|
exports.FilterTabs = FilterTabs;
|
|
17320
19581
|
exports.FoodProductCard = FoodProductCard;
|
|
17321
19582
|
exports.FoodProductLayout = FoodProductLayout;
|
|
19583
|
+
exports.GenUIView = GenUIView;
|
|
17322
19584
|
exports.HappeningCard = HappeningCard;
|
|
17323
19585
|
exports.IdentityIcon = IdentityIcon;
|
|
17324
19586
|
exports.IdentityOfframp = IdentityOfframp;
|
|
@@ -17404,9 +19666,17 @@ exports.WelcomeCard = WelcomeCard;
|
|
|
17404
19666
|
exports.WholesaleProductCard = WholesaleProductCard;
|
|
17405
19667
|
exports.WholesaleProductLayout = WholesaleProductLayout;
|
|
17406
19668
|
exports.WishlistButton = WishlistButton;
|
|
19669
|
+
exports.clearOrdersCache = clearOrdersCache;
|
|
17407
19670
|
exports.cn = cn;
|
|
17408
19671
|
exports.defaultBoundaryKind = defaultBoundaryKind;
|
|
19672
|
+
exports.genUICatalog = genUICatalog;
|
|
19673
|
+
exports.genUIFingerprint = genUIFingerprint;
|
|
19674
|
+
exports.genUIManifest = genUIManifest;
|
|
19675
|
+
exports.genUIManifestJson = genUIManifestJson;
|
|
17409
19676
|
exports.getBoundaryLabels = getBoundaryLabels;
|
|
19677
|
+
exports.isTerminalOrderStatus = isTerminalOrderStatus;
|
|
19678
|
+
exports.parseGenUIProgram = parse;
|
|
19679
|
+
exports.partitionMessages = partitionMessages;
|
|
17410
19680
|
exports.prefetchProduct = prefetchProduct;
|
|
17411
19681
|
exports.prefetchProducts = prefetchProducts;
|
|
17412
19682
|
exports.resolveBoundaryKind = resolveBoundaryKind;
|
|
@@ -17440,6 +19710,7 @@ exports.useDeliveryOptions = useDeliveryOptions;
|
|
|
17440
19710
|
exports.useElements = useElements;
|
|
17441
19711
|
exports.useElementsReady = useElementsReady;
|
|
17442
19712
|
exports.useFxRate = useFxRate;
|
|
19713
|
+
exports.useGenUIProgram = useGenUIProgram;
|
|
17443
19714
|
exports.useLocations = useLocations;
|
|
17444
19715
|
exports.useOptionalCimplify = useOptionalCimplify;
|
|
17445
19716
|
exports.useOrder = useOrder;
|