@cimplify/sdk 0.89.0 → 0.92.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/advanced.js +20 -20
- package/dist/advanced.mjs +1 -1
- package/dist/{chunk-JHJTXAPS.js → chunk-3MBMIEWT.js} +4 -4
- package/dist/{chunk-DENWOZEA.mjs → chunk-3XD2FVP4.mjs} +163 -43
- package/dist/{chunk-M4ALCELT.mjs → chunk-54H2YYIW.mjs} +2 -2
- package/dist/{chunk-T2CJ67QV.js → chunk-DCRN67NO.js} +201 -78
- package/dist/{chunk-T7Z25EY4.js → chunk-HKN3YG76.js} +84 -0
- package/dist/{chunk-GHFDYM5K.js → chunk-IVJEUGWF.js} +10 -1
- package/dist/{chunk-ROBTTS3Q.js → chunk-OAHSF542.js} +40 -1
- package/dist/{chunk-APBLFZ2I.mjs → chunk-PYBFTMHI.mjs} +40 -1
- package/dist/{chunk-OZDDHSIB.mjs → chunk-RRBY35TJ.mjs} +80 -1
- package/dist/{chunk-ZU4ZRYUR.mjs → chunk-T7LDBZZZ.mjs} +10 -1
- package/dist/chunk-U4MW6PYU.mjs +23 -0
- package/dist/chunk-XREIZ56K.js +26 -0
- package/dist/{client-CqZgdUtw.d.ts → client-BQbSKlHS.d.mts} +165 -11
- package/dist/{client-CAY2NbRG.d.mts → client-Bw4HtLP1.d.mts} +2 -2
- package/dist/{client-D8ZFs-sq.d.mts → client-CeW3acve.d.ts} +165 -11
- package/dist/{client-CUbwb6Om.d.ts → client-Otzv_rgP.d.ts} +2 -2
- package/dist/{index-CmWOz9bi.d.mts → index-CmIboQjl.d.mts} +3 -3
- package/dist/{index-Bc5qUxAt.d.ts → index-DgoRwz8l.d.ts} +3 -3
- package/dist/index.d.mts +49 -7
- package/dist/index.d.ts +49 -7
- package/dist/index.js +109 -82
- package/dist/index.mjs +11 -5
- package/dist/mock/cli.mjs +61 -1
- package/dist/mock/library.js +61 -1
- package/dist/mock/library.mjs +61 -1
- package/dist/mock/msw.js +61 -1
- package/dist/mock/msw.mjs +61 -1
- package/dist/{payment-Dcgb9n1n.d.mts → payment-BFtLqiG2.d.mts} +1 -1
- package/dist/{payment-Xq0WDZXh.d.ts → payment-Cq8FT1B6.d.ts} +1 -1
- package/dist/{price-DAAjKgAp.d.ts → price-DaFXT-Lr.d.ts} +1 -1
- package/dist/{price--Ma_WO5_.d.mts → price-DleDWk_s.d.mts} +1 -1
- package/dist/{product-BakHcsP2.d.ts → product-bZ8G5Z8D.d.mts} +95 -2
- package/dist/{product-BakHcsP2.d.mts → product-bZ8G5Z8D.d.ts} +95 -2
- package/dist/react.d.mts +471 -8
- package/dist/react.d.ts +471 -8
- package/dist/react.js +2787 -402
- package/dist/react.mjs +2699 -337
- package/dist/{server-Bk_hlefX.d.mts → server-D-iK-bNt.d.mts} +8 -1
- package/dist/{server-Bei5lBrc.d.ts → server-DwsY9hlX.d.ts} +8 -1
- package/dist/server.d.mts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +7 -7
- package/dist/server.mjs +2 -2
- 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 +24 -23
- package/dist/testing/suite.mjs +5 -4
- package/dist/testing.d.mts +5 -5
- package/dist/testing.d.ts +5 -5
- package/dist/testing.js +80 -79
- package/dist/testing.mjs +6 -5
- package/dist/utils.d.mts +4 -4
- package/dist/utils.d.ts +4 -4
- package/package.json +4 -1
- package/registry/booking-page.json +1 -1
- package/registry/chat-widget.json +1 -1
- package/registry/customer-input-fields.json +1 -1
- package/registry/date-slot-picker.json +1 -1
- package/registry/product-customizer.json +1 -1
- package/registry/rental-service-card.json +1 -1
package/dist/react.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
4
|
+
var chunkHKN3YG76_js = require('./chunk-HKN3YG76.js');
|
|
5
|
+
var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
|
|
6
|
+
var chunkDCRN67NO_js = require('./chunk-DCRN67NO.js');
|
|
7
|
+
var chunkIVJEUGWF_js = require('./chunk-IVJEUGWF.js');
|
|
7
8
|
var chunkPTPOQVXS_js = require('./chunk-PTPOQVXS.js');
|
|
8
9
|
var chunkAETVRBKI_js = require('./chunk-AETVRBKI.js');
|
|
9
10
|
require('./chunk-OWW5GUSB.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";
|
|
@@ -743,6 +747,7 @@ function mapServerCart(serverCart, fallbackCurrency) {
|
|
|
743
747
|
scheduledStart: item.scheduled_start || void 0,
|
|
744
748
|
scheduledEnd: item.scheduled_end || void 0,
|
|
745
749
|
staffId: item.staff_id || void 0,
|
|
750
|
+
units: item.units || void 0,
|
|
746
751
|
lineType: item.line_type,
|
|
747
752
|
confirmationCode: item.confirmation_code || void 0,
|
|
748
753
|
serviceStatus: item.service_status || void 0,
|
|
@@ -790,18 +795,18 @@ function deriveView(base, ops) {
|
|
|
790
795
|
};
|
|
791
796
|
}
|
|
792
797
|
async function syncFromServer(entry) {
|
|
793
|
-
|
|
794
|
-
entry.syncing = true;
|
|
795
|
-
try {
|
|
798
|
+
const run = async () => {
|
|
796
799
|
const result = await entry.client.cart.get();
|
|
797
800
|
if (!result.ok) throw result.error;
|
|
798
801
|
entry.collection.setBase(mapServerCart(result.value, entry.currency));
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
+
};
|
|
803
|
+
const next = entry.syncTail.then(run, run);
|
|
804
|
+
entry.syncTail = next.catch(() => {
|
|
805
|
+
});
|
|
806
|
+
return next;
|
|
802
807
|
}
|
|
803
808
|
function emitMutationError(entry, operation, opId, idempotencyKey, error) {
|
|
804
|
-
entry.client.events.emit(
|
|
809
|
+
entry.client.events.emit(chunkDCRN67NO_js.MUTATION_ERROR, {
|
|
805
810
|
service: "cart",
|
|
806
811
|
operation,
|
|
807
812
|
opId,
|
|
@@ -838,9 +843,15 @@ function getOrCreateStore(params) {
|
|
|
838
843
|
optimisticEnabled,
|
|
839
844
|
initialized: false,
|
|
840
845
|
initializePromise: null,
|
|
841
|
-
|
|
846
|
+
syncTail: Promise.resolve(),
|
|
842
847
|
failedOpRemovers: /* @__PURE__ */ new Set()
|
|
843
848
|
};
|
|
849
|
+
client.events.on(chunkDCRN67NO_js.SESSION_CLAIMED, () => {
|
|
850
|
+
if (entry.initialized) void syncFromServer(entry);
|
|
851
|
+
});
|
|
852
|
+
client.events.on(chunkDCRN67NO_js.SESSION_CLEARED, () => {
|
|
853
|
+
if (entry.initialized) void syncFromServer(entry);
|
|
854
|
+
});
|
|
844
855
|
cartStores.set(storeKey, entry);
|
|
845
856
|
return entry;
|
|
846
857
|
}
|
|
@@ -899,7 +910,8 @@ async function performAddItem(entry, product, quantity, options) {
|
|
|
899
910
|
specialInstructions: normalizedOptions.specialInstructions,
|
|
900
911
|
scheduledStart: normalizedOptions.scheduledStart,
|
|
901
912
|
scheduledEnd: normalizedOptions.scheduledEnd,
|
|
902
|
-
staffId: normalizedOptions.staffId
|
|
913
|
+
staffId: normalizedOptions.staffId,
|
|
914
|
+
units: normalizedOptions.units
|
|
903
915
|
});
|
|
904
916
|
const idempotencyKey = `idem_${typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`}`;
|
|
905
917
|
const patch = { kind: "add_item", tempItem, lineKey };
|
|
@@ -921,6 +933,7 @@ async function performAddItem(entry, product, quantity, options) {
|
|
|
921
933
|
scheduled_end: normalizedOptions.scheduledEnd,
|
|
922
934
|
staff_id: normalizedOptions.staffId,
|
|
923
935
|
resource_id: normalizedOptions.resourceId,
|
|
936
|
+
units: normalizedOptions.units,
|
|
924
937
|
customer_inputs: normalizedOptions.customerInputs
|
|
925
938
|
},
|
|
926
939
|
{ idempotencyKey }
|
|
@@ -1132,7 +1145,7 @@ var DEFAULT_COUNTRY = "US";
|
|
|
1132
1145
|
function createDefaultClient() {
|
|
1133
1146
|
const processRef = globalThis.process;
|
|
1134
1147
|
const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
|
|
1135
|
-
return
|
|
1148
|
+
return chunkDCRN67NO_js.createCimplifyClient({ publicKey: envPublicKey });
|
|
1136
1149
|
}
|
|
1137
1150
|
function getStoredLocationId() {
|
|
1138
1151
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -1285,7 +1298,7 @@ function CimplifyProvider({
|
|
|
1285
1298
|
const apiCredentials = resolvedClient.getCredentialsMode() === "include" ? "use-credentials" : "anonymous";
|
|
1286
1299
|
preconnectOrigin(resolvedClient.getBaseUrl(), apiCredentials);
|
|
1287
1300
|
preconnectOrigin(resolvedClient.getLinkApiUrl(), apiCredentials);
|
|
1288
|
-
preconnectOrigin(
|
|
1301
|
+
preconnectOrigin(chunkHKN3YG76_js.DEFAULT_CDN_BASE_URL);
|
|
1289
1302
|
}
|
|
1290
1303
|
const setCurrentLocation = React14.useCallback(
|
|
1291
1304
|
(location) => {
|
|
@@ -3444,7 +3457,7 @@ function CimplifySignInButton({
|
|
|
3444
3457
|
const onClick = async () => {
|
|
3445
3458
|
if (busy || disabled || !signInOpts.clientId) return;
|
|
3446
3459
|
setBusy(true);
|
|
3447
|
-
await
|
|
3460
|
+
await chunkIVJEUGWF_js.startSignIn({
|
|
3448
3461
|
...signInOpts,
|
|
3449
3462
|
onSuccess: () => {
|
|
3450
3463
|
setBusy(false);
|
|
@@ -4303,7 +4316,7 @@ function Price({
|
|
|
4303
4316
|
] });
|
|
4304
4317
|
}
|
|
4305
4318
|
function createCachedQuery(config) {
|
|
4306
|
-
const
|
|
4319
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
4307
4320
|
const inflight = /* @__PURE__ */ new Map();
|
|
4308
4321
|
const gcMaxAge = config.gcMaxAge ?? 3e5;
|
|
4309
4322
|
let lastSweep = 0;
|
|
@@ -4312,8 +4325,8 @@ function createCachedQuery(config) {
|
|
|
4312
4325
|
if (now - lastSweep < 6e4) return;
|
|
4313
4326
|
lastSweep = now;
|
|
4314
4327
|
const cutoff = now - gcMaxAge;
|
|
4315
|
-
for (const [key, entry] of
|
|
4316
|
-
if (entry.ts < cutoff)
|
|
4328
|
+
for (const [key, entry] of cache2) {
|
|
4329
|
+
if (entry.ts < cutoff) cache2.delete(key);
|
|
4317
4330
|
}
|
|
4318
4331
|
}
|
|
4319
4332
|
function useCachedQuery(options = {}) {
|
|
@@ -4331,13 +4344,13 @@ function createCachedQuery(config) {
|
|
|
4331
4344
|
const optionsRef = React14.useRef(options);
|
|
4332
4345
|
optionsRef.current = options;
|
|
4333
4346
|
if (config.locationSensitive && previousLocationRef.current !== locationId) {
|
|
4334
|
-
|
|
4347
|
+
cache2.clear();
|
|
4335
4348
|
inflight.clear();
|
|
4336
4349
|
previousLocationRef.current = locationId;
|
|
4337
4350
|
}
|
|
4338
4351
|
sweep();
|
|
4339
4352
|
const cacheKey2 = config.key(client, options);
|
|
4340
|
-
const cached =
|
|
4353
|
+
const cached = cache2.get(cacheKey2);
|
|
4341
4354
|
const [data, setData] = React14.useState(cached?.data ?? null);
|
|
4342
4355
|
const [isLoading, setIsLoading] = React14.useState(enabled && !cached);
|
|
4343
4356
|
const [error, setError] = React14.useState(null);
|
|
@@ -4350,7 +4363,7 @@ function createCachedQuery(config) {
|
|
|
4350
4363
|
const nextRequestId = ++requestIdRef.current;
|
|
4351
4364
|
setError(null);
|
|
4352
4365
|
if (!force) {
|
|
4353
|
-
const hit =
|
|
4366
|
+
const hit = cache2.get(cacheKey2);
|
|
4354
4367
|
if (hit) {
|
|
4355
4368
|
setData(hit.data);
|
|
4356
4369
|
setIsLoading(false);
|
|
@@ -4367,7 +4380,7 @@ function createCachedQuery(config) {
|
|
|
4367
4380
|
});
|
|
4368
4381
|
}
|
|
4369
4382
|
const value = await promise;
|
|
4370
|
-
|
|
4383
|
+
cache2.set(cacheKey2, { data: value, ts: Date.now() });
|
|
4371
4384
|
if (nextRequestId === requestIdRef.current) {
|
|
4372
4385
|
setData(value);
|
|
4373
4386
|
setError(null);
|
|
@@ -4388,23 +4401,23 @@ function createCachedQuery(config) {
|
|
|
4388
4401
|
void load(false);
|
|
4389
4402
|
}, [load]);
|
|
4390
4403
|
const refetch = React14.useCallback(async () => {
|
|
4391
|
-
|
|
4404
|
+
cache2.delete(cacheKey2);
|
|
4392
4405
|
await load(true);
|
|
4393
4406
|
}, [cacheKey2, load]);
|
|
4394
4407
|
return { data, isLoading, error, refetch };
|
|
4395
4408
|
}
|
|
4396
4409
|
useCachedQuery.prefetch = (client, options) => {
|
|
4397
4410
|
const key = config.key(client, options);
|
|
4398
|
-
if (
|
|
4411
|
+
if (cache2.has(key) || inflight.has(key)) return void 0;
|
|
4399
4412
|
const promise = config.fetch(client, options);
|
|
4400
4413
|
inflight.set(key, promise);
|
|
4401
4414
|
return promise.then((value) => {
|
|
4402
|
-
|
|
4415
|
+
cache2.set(key, { data: value, ts: Date.now() });
|
|
4403
4416
|
}).catch(() => {
|
|
4404
4417
|
}).finally(() => inflight.delete(key));
|
|
4405
4418
|
};
|
|
4406
4419
|
useCachedQuery.clear = () => {
|
|
4407
|
-
|
|
4420
|
+
cache2.clear();
|
|
4408
4421
|
inflight.clear();
|
|
4409
4422
|
};
|
|
4410
4423
|
return useCachedQuery;
|
|
@@ -4532,6 +4545,9 @@ function useCategories(options = {}) {
|
|
|
4532
4545
|
return { categories: data ?? [], isLoading, error, refetch };
|
|
4533
4546
|
}
|
|
4534
4547
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled", "refunded"]);
|
|
4548
|
+
function isTerminalOrderStatus(status) {
|
|
4549
|
+
return TERMINAL_STATUSES.has(status);
|
|
4550
|
+
}
|
|
4535
4551
|
var orderCache = /* @__PURE__ */ new Map();
|
|
4536
4552
|
var orderInflight = /* @__PURE__ */ new Map();
|
|
4537
4553
|
function buildOrderCacheKey(client, orderId) {
|
|
@@ -5373,6 +5389,9 @@ function useOrders(options = {}) {
|
|
|
5373
5389
|
const { data, isLoading, error, refetch } = ordersQuery(options);
|
|
5374
5390
|
return { orders: data ?? [], isLoading, error, refetch };
|
|
5375
5391
|
}
|
|
5392
|
+
function clearOrdersCache() {
|
|
5393
|
+
ordersQuery.clear();
|
|
5394
|
+
}
|
|
5376
5395
|
function useSubscriptions(options = {}) {
|
|
5377
5396
|
const { client } = useCimplifyClient();
|
|
5378
5397
|
const { status, enabled = true } = options;
|
|
@@ -5468,14 +5487,24 @@ function useServices(options = {}) {
|
|
|
5468
5487
|
}
|
|
5469
5488
|
|
|
5470
5489
|
// src/react/hooks/use-available-slots.ts
|
|
5490
|
+
function narrowingUnits(units) {
|
|
5491
|
+
return units != null && units > 1 ? units : void 0;
|
|
5492
|
+
}
|
|
5471
5493
|
var slotsQuery = createCachedQuery({
|
|
5472
5494
|
name: "useAvailableSlots",
|
|
5473
|
-
key: (client, options) => cacheKey(
|
|
5495
|
+
key: (client, options) => cacheKey(
|
|
5496
|
+
client.getPublicKey(),
|
|
5497
|
+
options.serviceId,
|
|
5498
|
+
options.date,
|
|
5499
|
+
options.participantCount,
|
|
5500
|
+
narrowingUnits(options.units)
|
|
5501
|
+
),
|
|
5474
5502
|
fetch: async (client, options) => {
|
|
5475
5503
|
const result = await client.scheduling.getAvailableSlots({
|
|
5476
5504
|
service_id: options.serviceId,
|
|
5477
5505
|
date: options.date,
|
|
5478
|
-
participant_count: options.participantCount
|
|
5506
|
+
participant_count: options.participantCount,
|
|
5507
|
+
units: narrowingUnits(options.units)
|
|
5479
5508
|
});
|
|
5480
5509
|
if (!result.ok) throw result.error;
|
|
5481
5510
|
return result.value;
|
|
@@ -5492,6 +5521,9 @@ function useAvailableSlots(serviceId, date, options = {}) {
|
|
|
5492
5521
|
}
|
|
5493
5522
|
|
|
5494
5523
|
// src/react/hooks/use-service-availability.ts
|
|
5524
|
+
function narrowingUnits2(units) {
|
|
5525
|
+
return units != null && units > 1 ? units : void 0;
|
|
5526
|
+
}
|
|
5495
5527
|
var serviceAvailabilityQuery = createCachedQuery({
|
|
5496
5528
|
name: "useServiceAvailability",
|
|
5497
5529
|
key: (client, options) => cacheKey(
|
|
@@ -5499,7 +5531,8 @@ var serviceAvailabilityQuery = createCachedQuery({
|
|
|
5499
5531
|
options.serviceId,
|
|
5500
5532
|
options.startDate,
|
|
5501
5533
|
options.endDate,
|
|
5502
|
-
options.participantCount
|
|
5534
|
+
options.participantCount,
|
|
5535
|
+
narrowingUnits2(options.units)
|
|
5503
5536
|
),
|
|
5504
5537
|
fetch: async (client, options) => {
|
|
5505
5538
|
const result = await client.scheduling.getServiceAvailability({
|
|
@@ -5507,7 +5540,8 @@ var serviceAvailabilityQuery = createCachedQuery({
|
|
|
5507
5540
|
start_date: options.startDate,
|
|
5508
5541
|
end_date: options.endDate,
|
|
5509
5542
|
location_id: options.locationId,
|
|
5510
|
-
participant_count: options.participantCount
|
|
5543
|
+
participant_count: options.participantCount,
|
|
5544
|
+
units: narrowingUnits2(options.units)
|
|
5511
5545
|
});
|
|
5512
5546
|
if (!result.ok) throw result.error;
|
|
5513
5547
|
return result.value.availability;
|
|
@@ -5878,6 +5912,7 @@ var POLL_IDLE_INTERVAL_MS = 15e3;
|
|
|
5878
5912
|
var IDLE_TIMEOUT_MS = 6e4;
|
|
5879
5913
|
var WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
5880
5914
|
var WS_PATH = "/api/v1/support/conversation/ws";
|
|
5915
|
+
var AWAIT_REPLY_TIMEOUT_MS = 3e4;
|
|
5881
5916
|
function useChat(options = {}) {
|
|
5882
5917
|
const context = useOptionalCimplifyClient();
|
|
5883
5918
|
const client = options.client ?? context?.client;
|
|
@@ -5886,6 +5921,13 @@ function useChat(options = {}) {
|
|
|
5886
5921
|
const [isSending, setIsSending] = React14.useState(false);
|
|
5887
5922
|
const [error, setError] = React14.useState(null);
|
|
5888
5923
|
const [conversationOpened, setConversationOpened] = React14.useState(false);
|
|
5924
|
+
const [humanRequested, setHumanRequested] = React14.useState(false);
|
|
5925
|
+
const [awaitingReply, setAwaitingReply] = React14.useState(false);
|
|
5926
|
+
const [seenAt, setSeenAt] = React14.useState(null);
|
|
5927
|
+
const [mayHaveOlder, setMayHaveOlder] = React14.useState(false);
|
|
5928
|
+
const [claimedAs, setClaimedAs] = React14.useState(
|
|
5929
|
+
() => client?.support.claimedCustomerName ?? null
|
|
5930
|
+
);
|
|
5889
5931
|
const pollRef = React14.useRef(null);
|
|
5890
5932
|
const lastActivityRef = React14.useRef(Date.now());
|
|
5891
5933
|
const openedRef = React14.useRef(false);
|
|
@@ -5894,17 +5936,43 @@ function useChat(options = {}) {
|
|
|
5894
5936
|
const wsRef = React14.useRef(null);
|
|
5895
5937
|
const wsReconnectAttemptRef = React14.useRef(0);
|
|
5896
5938
|
const wsReconnectTimerRef = React14.useRef(null);
|
|
5939
|
+
const aiEnabledRef = React14.useRef(false);
|
|
5940
|
+
const humanRequestedRef = React14.useRef(false);
|
|
5941
|
+
const awaitReplyTimerRef = React14.useRef(null);
|
|
5942
|
+
const claimUnsubRef = React14.useRef(null);
|
|
5897
5943
|
messagesRef.current = messages;
|
|
5898
5944
|
clientRef.current = client;
|
|
5945
|
+
humanRequestedRef.current = humanRequested;
|
|
5899
5946
|
const pollInterval = options.pollInterval ?? POLL_INTERVAL_MS;
|
|
5900
5947
|
const pollIdleInterval = options.pollIdleInterval ?? POLL_IDLE_INTERVAL_MS;
|
|
5901
|
-
const
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5948
|
+
const stopAwaitingReply = React14.useCallback(() => {
|
|
5949
|
+
if (awaitReplyTimerRef.current) {
|
|
5950
|
+
clearTimeout(awaitReplyTimerRef.current);
|
|
5951
|
+
awaitReplyTimerRef.current = null;
|
|
5952
|
+
}
|
|
5953
|
+
setAwaitingReply(false);
|
|
5907
5954
|
}, []);
|
|
5955
|
+
const startAwaitingReply = React14.useCallback(() => {
|
|
5956
|
+
setAwaitingReply(true);
|
|
5957
|
+
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
5958
|
+
awaitReplyTimerRef.current = setTimeout(() => {
|
|
5959
|
+
awaitReplyTimerRef.current = null;
|
|
5960
|
+
setAwaitingReply(false);
|
|
5961
|
+
}, AWAIT_REPLY_TIMEOUT_MS);
|
|
5962
|
+
}, []);
|
|
5963
|
+
const appendMessages = React14.useCallback(
|
|
5964
|
+
(incoming) => {
|
|
5965
|
+
if (incoming.some((m) => m.sender_type === "bot" || m.sender_type === "agent")) {
|
|
5966
|
+
stopAwaitingReply();
|
|
5967
|
+
}
|
|
5968
|
+
setMessages((prev) => {
|
|
5969
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
5970
|
+
const fresh = incoming.filter((m) => !ids.has(m.id));
|
|
5971
|
+
return fresh.length > 0 ? [...prev, ...fresh] : prev;
|
|
5972
|
+
});
|
|
5973
|
+
},
|
|
5974
|
+
[stopAwaitingReply]
|
|
5975
|
+
);
|
|
5908
5976
|
const fetchNewMessages = React14.useCallback(async () => {
|
|
5909
5977
|
const c = clientRef.current;
|
|
5910
5978
|
if (!c) return;
|
|
@@ -5932,6 +6000,17 @@ function useChat(options = {}) {
|
|
|
5932
6000
|
pollRef.current = null;
|
|
5933
6001
|
}
|
|
5934
6002
|
}, []);
|
|
6003
|
+
const resyncConversation = React14.useCallback(async () => {
|
|
6004
|
+
const c = clientRef.current;
|
|
6005
|
+
if (!c || !openedRef.current) return;
|
|
6006
|
+
const result = await c.support.openConversation();
|
|
6007
|
+
if (!result.ok) return;
|
|
6008
|
+
setMessages(result.value.messages);
|
|
6009
|
+
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6010
|
+
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6011
|
+
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6012
|
+
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6013
|
+
}, []);
|
|
5935
6014
|
const connectWebSocket = React14.useCallback(() => {
|
|
5936
6015
|
const c = clientRef.current;
|
|
5937
6016
|
if (!c || typeof WebSocket === "undefined") {
|
|
@@ -5953,12 +6032,21 @@ function useChat(options = {}) {
|
|
|
5953
6032
|
const envelope = JSON.parse(event.data);
|
|
5954
6033
|
if (envelope.type === "message.created" && envelope.data) {
|
|
5955
6034
|
appendMessages([envelope.data]);
|
|
6035
|
+
} else if (envelope.type === "thread.read") {
|
|
6036
|
+
const at = envelope.data?.last_read_at;
|
|
6037
|
+
if (at) setSeenAt((prev) => !prev || prev < at ? at : prev);
|
|
6038
|
+
} else if (envelope.type === "thread.responding") {
|
|
6039
|
+
const active = envelope.data?.active;
|
|
6040
|
+
if (active) startAwaitingReply();
|
|
6041
|
+
else stopAwaitingReply();
|
|
5956
6042
|
} else if (envelope.type === "resync") {
|
|
5957
6043
|
const latest = envelope.latest_message_at;
|
|
5958
6044
|
const last = messagesRef.current[messagesRef.current.length - 1];
|
|
5959
6045
|
if (latest && (!last || new Date(last.created_at).getTime() < new Date(latest).getTime())) {
|
|
5960
6046
|
fetchNewMessages();
|
|
5961
6047
|
}
|
|
6048
|
+
} else if (envelope.type === "rekey") {
|
|
6049
|
+
void resyncConversation();
|
|
5962
6050
|
}
|
|
5963
6051
|
} catch {
|
|
5964
6052
|
}
|
|
@@ -5978,11 +6066,57 @@ function useChat(options = {}) {
|
|
|
5978
6066
|
ws.onerror = () => {
|
|
5979
6067
|
};
|
|
5980
6068
|
wsRef.current = ws;
|
|
5981
|
-
}, [appendMessages, startPolling, stopPolling, fetchNewMessages]);
|
|
6069
|
+
}, [appendMessages, startPolling, stopPolling, fetchNewMessages, resyncConversation]);
|
|
6070
|
+
const resetConversation = React14.useCallback(() => {
|
|
6071
|
+
stopPolling();
|
|
6072
|
+
if (wsReconnectTimerRef.current) {
|
|
6073
|
+
clearTimeout(wsReconnectTimerRef.current);
|
|
6074
|
+
wsReconnectTimerRef.current = null;
|
|
6075
|
+
}
|
|
6076
|
+
if (wsRef.current) {
|
|
6077
|
+
wsRef.current.onclose = null;
|
|
6078
|
+
wsRef.current.close();
|
|
6079
|
+
wsRef.current = null;
|
|
6080
|
+
}
|
|
6081
|
+
wsReconnectAttemptRef.current = 0;
|
|
6082
|
+
openedRef.current = false;
|
|
6083
|
+
stopAwaitingReply();
|
|
6084
|
+
setMessages([]);
|
|
6085
|
+
setConversationOpened(false);
|
|
6086
|
+
setHumanRequested(false);
|
|
6087
|
+
setSeenAt(null);
|
|
6088
|
+
setMayHaveOlder(false);
|
|
6089
|
+
setError(null);
|
|
6090
|
+
setClaimedAs(null);
|
|
6091
|
+
}, [stopPolling, stopAwaitingReply]);
|
|
6092
|
+
const watchSessionClaims = React14.useCallback(() => {
|
|
6093
|
+
const c = clientRef.current;
|
|
6094
|
+
if (!c || claimUnsubRef.current) return;
|
|
6095
|
+
const unsubClaimed = c.events.on(chunkDCRN67NO_js.SESSION_CLAIMED, (event) => {
|
|
6096
|
+
setClaimedAs(event.customerName || null);
|
|
6097
|
+
if (!openedRef.current) return;
|
|
6098
|
+
connectWebSocket();
|
|
6099
|
+
void resyncConversation();
|
|
6100
|
+
});
|
|
6101
|
+
const unsubCleared = c.events.on(chunkDCRN67NO_js.SESSION_CLEARED, resetConversation);
|
|
6102
|
+
claimUnsubRef.current = () => {
|
|
6103
|
+
unsubClaimed();
|
|
6104
|
+
unsubCleared();
|
|
6105
|
+
};
|
|
6106
|
+
}, [connectWebSocket, resyncConversation, resetConversation]);
|
|
6107
|
+
const signOut = React14.useCallback(async () => {
|
|
6108
|
+
const c = clientRef.current;
|
|
6109
|
+
if (!c) return false;
|
|
6110
|
+
const result = await c.link.logout();
|
|
6111
|
+
return result.ok;
|
|
6112
|
+
}, []);
|
|
5982
6113
|
React14.useEffect(() => {
|
|
5983
6114
|
return () => {
|
|
5984
6115
|
if (pollRef.current) clearInterval(pollRef.current);
|
|
5985
6116
|
if (wsReconnectTimerRef.current) clearTimeout(wsReconnectTimerRef.current);
|
|
6117
|
+
if (awaitReplyTimerRef.current) clearTimeout(awaitReplyTimerRef.current);
|
|
6118
|
+
claimUnsubRef.current?.();
|
|
6119
|
+
claimUnsubRef.current = null;
|
|
5986
6120
|
if (wsRef.current) {
|
|
5987
6121
|
wsRef.current.onclose = null;
|
|
5988
6122
|
wsRef.current.close();
|
|
@@ -5998,35 +6132,53 @@ function useChat(options = {}) {
|
|
|
5998
6132
|
if (result.ok) {
|
|
5999
6133
|
setMessages(result.value.messages);
|
|
6000
6134
|
setConversationOpened(true);
|
|
6135
|
+
aiEnabledRef.current = result.value.conversation.ai_enabled;
|
|
6136
|
+
setHumanRequested(result.value.conversation.needs_human_review ?? false);
|
|
6137
|
+
setSeenAt(result.value.conversation.team_read_at ?? null);
|
|
6138
|
+
setMayHaveOlder(result.value.messages.length >= 50);
|
|
6139
|
+
setClaimedAs(client.support.claimedCustomerName);
|
|
6001
6140
|
connectWebSocket();
|
|
6141
|
+
watchSessionClaims();
|
|
6002
6142
|
} else {
|
|
6003
6143
|
openedRef.current = false;
|
|
6004
6144
|
setError(result.error);
|
|
6005
6145
|
}
|
|
6006
6146
|
setIsLoading(false);
|
|
6007
|
-
}, [client, connectWebSocket]);
|
|
6147
|
+
}, [client, connectWebSocket, watchSessionClaims]);
|
|
6008
6148
|
const send = React14.useCallback(
|
|
6009
|
-
async (content) => {
|
|
6010
|
-
|
|
6149
|
+
async (content, opts) => {
|
|
6150
|
+
const attachments = opts?.attachments ?? [];
|
|
6151
|
+
if (!client || !content.trim() && attachments.length === 0) return false;
|
|
6011
6152
|
setError(null);
|
|
6012
6153
|
lastActivityRef.current = Date.now();
|
|
6013
6154
|
if (!openedRef.current) {
|
|
6014
6155
|
await openConversation();
|
|
6015
|
-
if (!openedRef.current) return;
|
|
6156
|
+
if (!openedRef.current) return false;
|
|
6016
6157
|
}
|
|
6158
|
+
const replyTo = opts?.replyTo;
|
|
6017
6159
|
const optimisticId = `opt_${Date.now()}`;
|
|
6018
6160
|
const optimistic = {
|
|
6019
6161
|
id: optimisticId,
|
|
6020
6162
|
sender_type: "customer",
|
|
6021
6163
|
content: content.trim(),
|
|
6022
|
-
content_type: "text",
|
|
6023
|
-
attachments
|
|
6164
|
+
content_type: attachments.length > 0 ? "image" : "text",
|
|
6165
|
+
attachments,
|
|
6024
6166
|
metadata: {},
|
|
6167
|
+
// Same shape the server echoes back, so the bubble doesn't reflow
|
|
6168
|
+
// when the confirmed message replaces it.
|
|
6169
|
+
reply_to: replyTo ? {
|
|
6170
|
+
id: replyTo.id,
|
|
6171
|
+
sender_type: replyTo.sender_type,
|
|
6172
|
+
preview: chunkXREIZ56K_js.messagePreview(replyTo.content)
|
|
6173
|
+
} : void 0,
|
|
6025
6174
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6026
6175
|
};
|
|
6027
6176
|
setMessages((prev) => [...prev, optimistic]);
|
|
6028
6177
|
setIsSending(true);
|
|
6029
|
-
const result = await client.support.sendMessage(content.trim()
|
|
6178
|
+
const result = await client.support.sendMessage(content.trim(), {
|
|
6179
|
+
replyToMessageId: replyTo?.id,
|
|
6180
|
+
attachments: attachments.length > 0 ? attachments : void 0
|
|
6181
|
+
});
|
|
6030
6182
|
if (result.ok) {
|
|
6031
6183
|
const confirmed = result.value;
|
|
6032
6184
|
setMessages(
|
|
@@ -6037,13 +6189,17 @@ function useChat(options = {}) {
|
|
|
6037
6189
|
prev.some((m) => m.id === confirmed.id) ? prev.filter((m) => m.id !== optimisticId) : prev.map((m) => m.id === optimisticId ? confirmed : m)
|
|
6038
6190
|
)
|
|
6039
6191
|
);
|
|
6192
|
+
if (aiEnabledRef.current && !humanRequestedRef.current) {
|
|
6193
|
+
startAwaitingReply();
|
|
6194
|
+
}
|
|
6040
6195
|
} else {
|
|
6041
6196
|
setMessages((prev) => prev.filter((m) => m.id !== optimisticId));
|
|
6042
6197
|
setError(result.error);
|
|
6043
6198
|
}
|
|
6044
6199
|
setIsSending(false);
|
|
6200
|
+
return result.ok;
|
|
6045
6201
|
},
|
|
6046
|
-
[client, openConversation]
|
|
6202
|
+
[client, openConversation, startAwaitingReply]
|
|
6047
6203
|
);
|
|
6048
6204
|
const startConversation = React14.useCallback(
|
|
6049
6205
|
async (starterText) => {
|
|
@@ -6054,14 +6210,67 @@ function useChat(options = {}) {
|
|
|
6054
6210
|
},
|
|
6055
6211
|
[openConversation, send]
|
|
6056
6212
|
);
|
|
6213
|
+
const react = React14.useCallback(
|
|
6214
|
+
async (message, emoji) => {
|
|
6215
|
+
if (!client || message.id.startsWith("opt_")) return false;
|
|
6216
|
+
const result = await client.support.reactToMessage(message.id, emoji);
|
|
6217
|
+
if (result.ok) appendMessages([result.value]);
|
|
6218
|
+
return result.ok;
|
|
6219
|
+
},
|
|
6220
|
+
[client, appendMessages]
|
|
6221
|
+
);
|
|
6222
|
+
const requestHuman = React14.useCallback(async () => {
|
|
6223
|
+
if (!client) return false;
|
|
6224
|
+
setError(null);
|
|
6225
|
+
if (!openedRef.current) {
|
|
6226
|
+
await openConversation();
|
|
6227
|
+
if (!openedRef.current) return false;
|
|
6228
|
+
}
|
|
6229
|
+
const result = await client.support.requestHuman();
|
|
6230
|
+
if (result.ok) {
|
|
6231
|
+
setHumanRequested(true);
|
|
6232
|
+
return true;
|
|
6233
|
+
}
|
|
6234
|
+
setError(result.error);
|
|
6235
|
+
return false;
|
|
6236
|
+
}, [client, openConversation]);
|
|
6237
|
+
const loadOlder = React14.useCallback(async () => {
|
|
6238
|
+
const c = clientRef.current;
|
|
6239
|
+
const earliest = messagesRef.current[0];
|
|
6240
|
+
if (!c || !earliest) return false;
|
|
6241
|
+
const result = await c.support.getMessages({ before: earliest.created_at, limit: 50 });
|
|
6242
|
+
if (!result.ok) {
|
|
6243
|
+
setError(result.error);
|
|
6244
|
+
return false;
|
|
6245
|
+
}
|
|
6246
|
+
const older = result.value;
|
|
6247
|
+
setMayHaveOlder(older.length >= 50);
|
|
6248
|
+
if (older.length === 0) return false;
|
|
6249
|
+
setMessages((prev) => {
|
|
6250
|
+
const ids = new Set(prev.map((m) => m.id));
|
|
6251
|
+
const fresh = older.filter((m) => !ids.has(m.id));
|
|
6252
|
+
return fresh.length > 0 ? [...fresh, ...prev] : prev;
|
|
6253
|
+
});
|
|
6254
|
+
return true;
|
|
6255
|
+
}, []);
|
|
6256
|
+
const agentArrived = messages.some((m) => m.sender_type === "agent");
|
|
6057
6257
|
return {
|
|
6058
6258
|
messages,
|
|
6059
6259
|
isLoading,
|
|
6060
6260
|
isSending,
|
|
6061
6261
|
error,
|
|
6062
6262
|
isActive: conversationOpened && messages.length > 0,
|
|
6263
|
+
humanRequested: humanRequested && !agentArrived,
|
|
6264
|
+
awaitingReply,
|
|
6265
|
+
seenAt,
|
|
6266
|
+
mayHaveOlder,
|
|
6267
|
+
claimedAs,
|
|
6268
|
+
signOut,
|
|
6063
6269
|
send,
|
|
6064
|
-
startConversation
|
|
6270
|
+
startConversation,
|
|
6271
|
+
react,
|
|
6272
|
+
requestHuman,
|
|
6273
|
+
loadOlder
|
|
6065
6274
|
};
|
|
6066
6275
|
}
|
|
6067
6276
|
var ElementsContext = React14.createContext({
|
|
@@ -6136,15 +6345,15 @@ function AuthElement({
|
|
|
6136
6345
|
}, [onReady, onAuthenticated, onError]);
|
|
6137
6346
|
React14.useEffect(() => {
|
|
6138
6347
|
if (!elements || !containerRef.current) return;
|
|
6139
|
-
const element = elements.create(
|
|
6348
|
+
const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.AUTH, { prefillEmail });
|
|
6140
6349
|
elementRef.current = element;
|
|
6141
|
-
element.on(
|
|
6350
|
+
element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6142
6351
|
element.on(
|
|
6143
|
-
|
|
6352
|
+
chunkIVJEUGWF_js.EVENT_TYPES.AUTHENTICATED,
|
|
6144
6353
|
(data) => onAuthenticatedRef.current?.(data)
|
|
6145
6354
|
);
|
|
6146
6355
|
element.on(
|
|
6147
|
-
|
|
6356
|
+
chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
|
|
6148
6357
|
(data) => onErrorRef.current?.(data)
|
|
6149
6358
|
);
|
|
6150
6359
|
element.mount(containerRef.current);
|
|
@@ -6173,15 +6382,15 @@ function AddressElement({
|
|
|
6173
6382
|
}, [onReady, onChange, onError]);
|
|
6174
6383
|
React14.useEffect(() => {
|
|
6175
6384
|
if (!elements || !containerRef.current) return;
|
|
6176
|
-
const element = elements.create(
|
|
6385
|
+
const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.ADDRESS, { mode });
|
|
6177
6386
|
elementRef.current = element;
|
|
6178
|
-
element.on(
|
|
6387
|
+
element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6179
6388
|
element.on(
|
|
6180
|
-
|
|
6389
|
+
chunkIVJEUGWF_js.EVENT_TYPES.CHANGE,
|
|
6181
6390
|
(data) => onChangeRef.current?.(data)
|
|
6182
6391
|
);
|
|
6183
6392
|
element.on(
|
|
6184
|
-
|
|
6393
|
+
chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
|
|
6185
6394
|
(data) => onErrorRef.current?.(data)
|
|
6186
6395
|
);
|
|
6187
6396
|
element.mount(containerRef.current);
|
|
@@ -6211,15 +6420,15 @@ function PaymentElement({
|
|
|
6211
6420
|
}, [onReady, onChange, onError]);
|
|
6212
6421
|
React14.useEffect(() => {
|
|
6213
6422
|
if (!elements || !containerRef.current) return;
|
|
6214
|
-
const element = elements.create(
|
|
6423
|
+
const element = elements.create(chunkIVJEUGWF_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
|
|
6215
6424
|
elementRef.current = element;
|
|
6216
|
-
element.on(
|
|
6425
|
+
element.on(chunkIVJEUGWF_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
6217
6426
|
element.on(
|
|
6218
|
-
|
|
6427
|
+
chunkIVJEUGWF_js.EVENT_TYPES.CHANGE,
|
|
6219
6428
|
(data) => onChangeRef.current?.(data)
|
|
6220
6429
|
);
|
|
6221
6430
|
element.on(
|
|
6222
|
-
|
|
6431
|
+
chunkIVJEUGWF_js.EVENT_TYPES.ERROR,
|
|
6223
6432
|
(data) => onErrorRef.current?.(data)
|
|
6224
6433
|
);
|
|
6225
6434
|
element.mount(containerRef.current);
|
|
@@ -6510,7 +6719,7 @@ function VariantSelector({
|
|
|
6510
6719
|
{
|
|
6511
6720
|
"data-cimplify-variant-name": true,
|
|
6512
6721
|
className: cn("flex-1 min-w-0 text-sm", classNames?.name),
|
|
6513
|
-
children:
|
|
6722
|
+
children: chunkHKN3YG76_js.getVariantDisplayName(variant, productName)
|
|
6514
6723
|
}
|
|
6515
6724
|
),
|
|
6516
6725
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-variant-pricing": true, className: cn("text-sm text-muted-foreground", classNames?.pricing), children: [
|
|
@@ -7791,6 +8000,177 @@ function VolumePricing({
|
|
|
7791
8000
|
}
|
|
7792
8001
|
);
|
|
7793
8002
|
}
|
|
8003
|
+
function createPlacesSessionToken() {
|
|
8004
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
8005
|
+
return crypto.randomUUID();
|
|
8006
|
+
}
|
|
8007
|
+
return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
8008
|
+
}
|
|
8009
|
+
var MIN_QUERY_LENGTH = 3;
|
|
8010
|
+
var DEBOUNCE_MS = 300;
|
|
8011
|
+
var BLUR_CLOSE_MS = 120;
|
|
8012
|
+
function AddressAutocomplete({
|
|
8013
|
+
value,
|
|
8014
|
+
onSelect,
|
|
8015
|
+
onDirty,
|
|
8016
|
+
placeholder,
|
|
8017
|
+
required,
|
|
8018
|
+
autoFocus,
|
|
8019
|
+
id,
|
|
8020
|
+
className,
|
|
8021
|
+
classNames
|
|
8022
|
+
}) {
|
|
8023
|
+
const clientContext = useOptionalCimplifyClient();
|
|
8024
|
+
const [inputText, setInputText] = React14__default.default.useState(value?.formatted_address ?? "");
|
|
8025
|
+
const [suggestions, setSuggestions] = React14__default.default.useState([]);
|
|
8026
|
+
const [isOpen, setIsOpen] = React14__default.default.useState(false);
|
|
8027
|
+
const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
|
|
8028
|
+
const debounceRef = React14__default.default.useRef(null);
|
|
8029
|
+
const seqRef = React14__default.default.useRef(0);
|
|
8030
|
+
const sessionTokenRef = React14__default.default.useRef(createPlacesSessionToken());
|
|
8031
|
+
const blurTimeoutRef = React14__default.default.useRef(null);
|
|
8032
|
+
React14__default.default.useEffect(() => {
|
|
8033
|
+
return () => {
|
|
8034
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8035
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8036
|
+
seqRef.current += 1;
|
|
8037
|
+
};
|
|
8038
|
+
}, []);
|
|
8039
|
+
const fetchSuggestions = React14__default.default.useCallback(
|
|
8040
|
+
async (query) => {
|
|
8041
|
+
const client = clientContext?.client;
|
|
8042
|
+
if (!client || query.length < MIN_QUERY_LENGTH) {
|
|
8043
|
+
setSuggestions([]);
|
|
8044
|
+
setIsOpen(false);
|
|
8045
|
+
return;
|
|
8046
|
+
}
|
|
8047
|
+
const seq = ++seqRef.current;
|
|
8048
|
+
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8049
|
+
if (seqRef.current !== seq) return;
|
|
8050
|
+
if (result.ok && result.value.predictions.length > 0) {
|
|
8051
|
+
setSuggestions(result.value.predictions);
|
|
8052
|
+
setHighlightedIndex(0);
|
|
8053
|
+
setIsOpen(true);
|
|
8054
|
+
} else {
|
|
8055
|
+
setSuggestions([]);
|
|
8056
|
+
setIsOpen(false);
|
|
8057
|
+
}
|
|
8058
|
+
},
|
|
8059
|
+
[clientContext]
|
|
8060
|
+
);
|
|
8061
|
+
const handleInputChange = (nextValue) => {
|
|
8062
|
+
setInputText(nextValue);
|
|
8063
|
+
onDirty?.();
|
|
8064
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8065
|
+
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), DEBOUNCE_MS);
|
|
8066
|
+
};
|
|
8067
|
+
const selectSuggestion = React14__default.default.useCallback(
|
|
8068
|
+
async (suggestion) => {
|
|
8069
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8070
|
+
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8071
|
+
const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8072
|
+
setInputText(fallback);
|
|
8073
|
+
setSuggestions([]);
|
|
8074
|
+
setIsOpen(false);
|
|
8075
|
+
const client = clientContext?.client;
|
|
8076
|
+
if (!client) return;
|
|
8077
|
+
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8078
|
+
sessionTokenRef.current = createPlacesSessionToken();
|
|
8079
|
+
if (result.ok) {
|
|
8080
|
+
const d = result.value;
|
|
8081
|
+
const address = {
|
|
8082
|
+
formatted_address: d.formatted_address || fallback,
|
|
8083
|
+
street_address: d.street_address,
|
|
8084
|
+
apartment: d.apartment,
|
|
8085
|
+
city: d.city,
|
|
8086
|
+
region: d.region,
|
|
8087
|
+
postal_code: d.postal_code,
|
|
8088
|
+
country: d.country,
|
|
8089
|
+
latitude: d.latitude,
|
|
8090
|
+
longitude: d.longitude,
|
|
8091
|
+
place_id: d.place_id
|
|
8092
|
+
};
|
|
8093
|
+
setInputText(address.formatted_address);
|
|
8094
|
+
onSelect(address);
|
|
8095
|
+
}
|
|
8096
|
+
},
|
|
8097
|
+
[clientContext, onSelect]
|
|
8098
|
+
);
|
|
8099
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-address-autocomplete": true, className: cn("relative", className, classNames?.root), children: [
|
|
8100
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8101
|
+
"input",
|
|
8102
|
+
{
|
|
8103
|
+
id,
|
|
8104
|
+
type: "text",
|
|
8105
|
+
value: inputText,
|
|
8106
|
+
onChange: (e) => handleInputChange(e.target.value),
|
|
8107
|
+
onFocus: () => {
|
|
8108
|
+
if (suggestions.length > 0) setIsOpen(true);
|
|
8109
|
+
},
|
|
8110
|
+
onBlur: () => {
|
|
8111
|
+
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), BLUR_CLOSE_MS);
|
|
8112
|
+
},
|
|
8113
|
+
onKeyDown: (e) => {
|
|
8114
|
+
if (e.key === "Escape") {
|
|
8115
|
+
setIsOpen(false);
|
|
8116
|
+
return;
|
|
8117
|
+
}
|
|
8118
|
+
if (!isOpen || suggestions.length === 0) return;
|
|
8119
|
+
if (e.key === "ArrowDown") {
|
|
8120
|
+
e.preventDefault();
|
|
8121
|
+
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8122
|
+
} else if (e.key === "ArrowUp") {
|
|
8123
|
+
e.preventDefault();
|
|
8124
|
+
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8125
|
+
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8126
|
+
e.preventDefault();
|
|
8127
|
+
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8128
|
+
}
|
|
8129
|
+
},
|
|
8130
|
+
placeholder: placeholder ?? "Search for an address...",
|
|
8131
|
+
required,
|
|
8132
|
+
autoFocus,
|
|
8133
|
+
autoComplete: "off",
|
|
8134
|
+
"aria-autocomplete": "list",
|
|
8135
|
+
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8136
|
+
"data-cimplify-address-input": true,
|
|
8137
|
+
className: classNames?.input
|
|
8138
|
+
}
|
|
8139
|
+
),
|
|
8140
|
+
isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8141
|
+
"div",
|
|
8142
|
+
{
|
|
8143
|
+
role: "listbox",
|
|
8144
|
+
"data-cimplify-address-list": true,
|
|
8145
|
+
className: cn(
|
|
8146
|
+
"absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg",
|
|
8147
|
+
classNames?.list
|
|
8148
|
+
),
|
|
8149
|
+
children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8150
|
+
"button",
|
|
8151
|
+
{
|
|
8152
|
+
type: "button",
|
|
8153
|
+
role: "option",
|
|
8154
|
+
"aria-selected": i === highlightedIndex,
|
|
8155
|
+
onMouseDown: (e) => {
|
|
8156
|
+
e.preventDefault();
|
|
8157
|
+
void selectSuggestion(s);
|
|
8158
|
+
},
|
|
8159
|
+
"data-cimplify-address-option": true,
|
|
8160
|
+
className: cn(
|
|
8161
|
+
"block w-full px-3 py-2.5 text-left text-sm transition-colors",
|
|
8162
|
+
i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70",
|
|
8163
|
+
classNames?.option,
|
|
8164
|
+
i === highlightedIndex && classNames?.activeOption
|
|
8165
|
+
),
|
|
8166
|
+
children: s.description
|
|
8167
|
+
},
|
|
8168
|
+
s.place_id
|
|
8169
|
+
))
|
|
8170
|
+
}
|
|
8171
|
+
)
|
|
8172
|
+
] });
|
|
8173
|
+
}
|
|
7794
8174
|
function parseValue(value) {
|
|
7795
8175
|
if (!value) return void 0;
|
|
7796
8176
|
const date = dateFns.parseISO(value);
|
|
@@ -8132,12 +8512,12 @@ function FieldInput({
|
|
|
8132
8512
|
classNames?.input
|
|
8133
8513
|
);
|
|
8134
8514
|
switch (field.field_type) {
|
|
8135
|
-
case
|
|
8136
|
-
case
|
|
8515
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Text:
|
|
8516
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Url:
|
|
8137
8517
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8138
8518
|
"input",
|
|
8139
8519
|
{
|
|
8140
|
-
type: field.field_type ===
|
|
8520
|
+
type: field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Url ? "url" : "text",
|
|
8141
8521
|
value: typeof value === "string" ? value : "",
|
|
8142
8522
|
onChange: (e) => onValueChange(e.target.value),
|
|
8143
8523
|
placeholder: field.placeholder,
|
|
@@ -8146,7 +8526,7 @@ function FieldInput({
|
|
|
8146
8526
|
className: inputClass
|
|
8147
8527
|
}
|
|
8148
8528
|
);
|
|
8149
|
-
case
|
|
8529
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Textarea:
|
|
8150
8530
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8151
8531
|
"textarea",
|
|
8152
8532
|
{
|
|
@@ -8159,7 +8539,7 @@ function FieldInput({
|
|
|
8159
8539
|
className: cn(inputClass, "resize-none", classNames?.textarea)
|
|
8160
8540
|
}
|
|
8161
8541
|
);
|
|
8162
|
-
case
|
|
8542
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Number:
|
|
8163
8543
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8164
8544
|
"input",
|
|
8165
8545
|
{
|
|
@@ -8173,7 +8553,7 @@ function FieldInput({
|
|
|
8173
8553
|
className: inputClass
|
|
8174
8554
|
}
|
|
8175
8555
|
);
|
|
8176
|
-
case
|
|
8556
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Select:
|
|
8177
8557
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8178
8558
|
"select",
|
|
8179
8559
|
{
|
|
@@ -8187,7 +8567,7 @@ function FieldInput({
|
|
|
8187
8567
|
]
|
|
8188
8568
|
}
|
|
8189
8569
|
);
|
|
8190
|
-
case
|
|
8570
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Radio:
|
|
8191
8571
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.radioGroup), children: (field.options || []).map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8192
8572
|
"label",
|
|
8193
8573
|
{
|
|
@@ -8212,7 +8592,7 @@ function FieldInput({
|
|
|
8212
8592
|
},
|
|
8213
8593
|
opt
|
|
8214
8594
|
)) });
|
|
8215
|
-
case
|
|
8595
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Checkbox:
|
|
8216
8596
|
return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: cn("flex items-center gap-2 text-sm cursor-pointer", classNames?.checkboxLabel), children: [
|
|
8217
8597
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8218
8598
|
"input",
|
|
@@ -8225,7 +8605,7 @@ function FieldInput({
|
|
|
8225
8605
|
),
|
|
8226
8606
|
field.placeholder || field.name
|
|
8227
8607
|
] });
|
|
8228
|
-
case
|
|
8608
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Color:
|
|
8229
8609
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8230
8610
|
"input",
|
|
8231
8611
|
{
|
|
@@ -8235,7 +8615,7 @@ function FieldInput({
|
|
|
8235
8615
|
className: cn("w-12 h-10 rounded-md border border-input cursor-pointer", classNames?.colorInput)
|
|
8236
8616
|
}
|
|
8237
8617
|
);
|
|
8238
|
-
case
|
|
8618
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Date:
|
|
8239
8619
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8240
8620
|
DatePicker,
|
|
8241
8621
|
{
|
|
@@ -8246,8 +8626,8 @@ function FieldInput({
|
|
|
8246
8626
|
"aria-label": field.name
|
|
8247
8627
|
}
|
|
8248
8628
|
);
|
|
8249
|
-
case
|
|
8250
|
-
case
|
|
8629
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.File:
|
|
8630
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image:
|
|
8251
8631
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8252
8632
|
FileUploadInput,
|
|
8253
8633
|
{
|
|
@@ -8258,7 +8638,7 @@ function FieldInput({
|
|
|
8258
8638
|
classNames
|
|
8259
8639
|
}
|
|
8260
8640
|
);
|
|
8261
|
-
case
|
|
8641
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Email:
|
|
8262
8642
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8263
8643
|
"input",
|
|
8264
8644
|
{
|
|
@@ -8270,7 +8650,7 @@ function FieldInput({
|
|
|
8270
8650
|
className: inputClass
|
|
8271
8651
|
}
|
|
8272
8652
|
);
|
|
8273
|
-
case
|
|
8653
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.DateTime: {
|
|
8274
8654
|
const stringValue = typeof value === "string" ? value : "";
|
|
8275
8655
|
const [datePart, timePartRaw] = stringValue.includes("T") ? stringValue.split("T", 2) : [stringValue, ""];
|
|
8276
8656
|
const timePart = (timePartRaw ?? "").slice(0, 5);
|
|
@@ -8309,7 +8689,7 @@ function FieldInput({
|
|
|
8309
8689
|
)
|
|
8310
8690
|
] });
|
|
8311
8691
|
}
|
|
8312
|
-
case
|
|
8692
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Time:
|
|
8313
8693
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8314
8694
|
TimePicker,
|
|
8315
8695
|
{
|
|
@@ -8320,7 +8700,7 @@ function FieldInput({
|
|
|
8320
8700
|
"aria-label": field.name
|
|
8321
8701
|
}
|
|
8322
8702
|
);
|
|
8323
|
-
case
|
|
8703
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.MultiSelect:
|
|
8324
8704
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8325
8705
|
MultiSelectInput,
|
|
8326
8706
|
{
|
|
@@ -8330,7 +8710,7 @@ function FieldInput({
|
|
|
8330
8710
|
classNames
|
|
8331
8711
|
}
|
|
8332
8712
|
);
|
|
8333
|
-
case
|
|
8713
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.DateRange:
|
|
8334
8714
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8335
8715
|
DateRangeInput,
|
|
8336
8716
|
{
|
|
@@ -8340,7 +8720,7 @@ function FieldInput({
|
|
|
8340
8720
|
classNames
|
|
8341
8721
|
}
|
|
8342
8722
|
);
|
|
8343
|
-
case
|
|
8723
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Address:
|
|
8344
8724
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8345
8725
|
AddressInput,
|
|
8346
8726
|
{
|
|
@@ -8351,7 +8731,7 @@ function FieldInput({
|
|
|
8351
8731
|
inputClass
|
|
8352
8732
|
}
|
|
8353
8733
|
);
|
|
8354
|
-
case
|
|
8734
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Location:
|
|
8355
8735
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8356
8736
|
LocationInput,
|
|
8357
8737
|
{
|
|
@@ -8362,7 +8742,7 @@ function FieldInput({
|
|
|
8362
8742
|
inputClass
|
|
8363
8743
|
}
|
|
8364
8744
|
);
|
|
8365
|
-
case
|
|
8745
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Phone:
|
|
8366
8746
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8367
8747
|
PhoneInput,
|
|
8368
8748
|
{
|
|
@@ -8373,7 +8753,7 @@ function FieldInput({
|
|
|
8373
8753
|
inputClass
|
|
8374
8754
|
}
|
|
8375
8755
|
);
|
|
8376
|
-
case
|
|
8756
|
+
case chunkHKN3YG76_js.INPUT_FIELD_TYPE.Signature:
|
|
8377
8757
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8378
8758
|
SignatureInput,
|
|
8379
8759
|
{
|
|
@@ -8406,7 +8786,7 @@ function FileUploadInput({
|
|
|
8406
8786
|
const [isUploading, setIsUploading] = React14__default.default.useState(false);
|
|
8407
8787
|
const fileUrl = typeof value === "string" ? value : void 0;
|
|
8408
8788
|
const acceptedFormats = field.validation?.accepted_formats;
|
|
8409
|
-
const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type ===
|
|
8789
|
+
const accept = acceptedFormats ? acceptedFormats.map((f) => `.${f}`).join(",") : field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? "image/*" : void 0;
|
|
8410
8790
|
const handleFile = async (file) => {
|
|
8411
8791
|
if (!onFileUpload) return;
|
|
8412
8792
|
if (field.validation?.max_size_mb) {
|
|
@@ -8424,7 +8804,7 @@ function FileUploadInput({
|
|
|
8424
8804
|
}
|
|
8425
8805
|
};
|
|
8426
8806
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("space-y-2", classNames?.fileInput), children: fileUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
8427
|
-
field.field_type ===
|
|
8807
|
+
field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image && /* @__PURE__ */ jsxRuntime.jsx("img", { src: fileUrl, alt: "Uploaded", className: "w-16 h-16 object-cover rounded-md border border-border" }),
|
|
8428
8808
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground truncate", children: fileUrl.split("/").pop() }) }),
|
|
8429
8809
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8430
8810
|
"button",
|
|
@@ -8450,7 +8830,7 @@ function FileUploadInput({
|
|
|
8450
8830
|
}
|
|
8451
8831
|
),
|
|
8452
8832
|
isUploading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: "Uploading..." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8453
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type ===
|
|
8833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: field.placeholder || `Upload ${field.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? "image" : "file"}` }),
|
|
8454
8834
|
acceptedFormats && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground/60 mt-1", children: [
|
|
8455
8835
|
acceptedFormats.map((f) => f.toUpperCase()).join(", "),
|
|
8456
8836
|
field.validation?.max_size_mb && ` \xB7 Max ${field.validation.max_size_mb}MB`
|
|
@@ -8551,129 +8931,18 @@ function AddressInput({
|
|
|
8551
8931
|
classNames,
|
|
8552
8932
|
inputClass
|
|
8553
8933
|
}) {
|
|
8554
|
-
const clientContext = useOptionalCimplifyClient();
|
|
8555
8934
|
const addr = value && typeof value === "object" ? value : null;
|
|
8556
|
-
|
|
8557
|
-
const [suggestions, setSuggestions] = React14__default.default.useState([]);
|
|
8558
|
-
const [isOpen, setIsOpen] = React14__default.default.useState(false);
|
|
8559
|
-
const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
|
|
8560
|
-
const debounceRef = React14__default.default.useRef(null);
|
|
8561
|
-
const seqRef = React14__default.default.useRef(0);
|
|
8562
|
-
const sessionTokenRef = React14__default.default.useRef(createSessionToken());
|
|
8563
|
-
const blurTimeoutRef = React14__default.default.useRef(null);
|
|
8564
|
-
React14__default.default.useEffect(() => {
|
|
8565
|
-
return () => {
|
|
8566
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8567
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8568
|
-
seqRef.current += 1;
|
|
8569
|
-
};
|
|
8570
|
-
}, []);
|
|
8571
|
-
const fetchSuggestions = React14__default.default.useCallback(async (query) => {
|
|
8572
|
-
const client = clientContext?.client;
|
|
8573
|
-
if (!client || query.length < 3) {
|
|
8574
|
-
setSuggestions([]);
|
|
8575
|
-
setIsOpen(false);
|
|
8576
|
-
return;
|
|
8577
|
-
}
|
|
8578
|
-
const seq = ++seqRef.current;
|
|
8579
|
-
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8580
|
-
if (seqRef.current !== seq) return;
|
|
8581
|
-
if (result.ok && result.value.predictions.length > 0) {
|
|
8582
|
-
setSuggestions(result.value.predictions);
|
|
8583
|
-
setHighlightedIndex(0);
|
|
8584
|
-
setIsOpen(true);
|
|
8585
|
-
} else {
|
|
8586
|
-
setSuggestions([]);
|
|
8587
|
-
setIsOpen(false);
|
|
8588
|
-
}
|
|
8589
|
-
}, [clientContext]);
|
|
8590
|
-
const handleInputChange = (nextValue) => {
|
|
8591
|
-
setInputText(nextValue);
|
|
8592
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8593
|
-
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
8594
|
-
};
|
|
8595
|
-
const selectSuggestion = React14__default.default.useCallback(async (suggestion) => {
|
|
8596
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8597
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8598
|
-
const fallback = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8599
|
-
setInputText(fallback);
|
|
8600
|
-
setSuggestions([]);
|
|
8601
|
-
setIsOpen(false);
|
|
8602
|
-
const client = clientContext?.client;
|
|
8603
|
-
if (!client) return;
|
|
8604
|
-
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8605
|
-
sessionTokenRef.current = createSessionToken();
|
|
8606
|
-
if (result.ok) {
|
|
8607
|
-
const d = result.value;
|
|
8608
|
-
const addressValue = {
|
|
8609
|
-
formatted_address: d.formatted_address || fallback,
|
|
8610
|
-
street_address: d.street_address,
|
|
8611
|
-
apartment: d.apartment,
|
|
8612
|
-
city: d.city,
|
|
8613
|
-
region: d.region,
|
|
8614
|
-
postal_code: d.postal_code,
|
|
8615
|
-
country: d.country,
|
|
8616
|
-
latitude: d.latitude,
|
|
8617
|
-
longitude: d.longitude,
|
|
8618
|
-
place_id: d.place_id
|
|
8619
|
-
};
|
|
8620
|
-
setInputText(addressValue.formatted_address);
|
|
8621
|
-
onValueChange(addressValue);
|
|
8622
|
-
}
|
|
8623
|
-
}, [clientContext, onValueChange]);
|
|
8624
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2 relative", classNames?.addressInput), children: [
|
|
8935
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2", classNames?.addressInput), children: [
|
|
8625
8936
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8626
|
-
|
|
8937
|
+
AddressAutocomplete,
|
|
8627
8938
|
{
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
onChange: (e) => handleInputChange(e.target.value),
|
|
8631
|
-
onFocus: () => {
|
|
8632
|
-
if (suggestions.length > 0) setIsOpen(true);
|
|
8633
|
-
},
|
|
8634
|
-
onBlur: () => {
|
|
8635
|
-
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
8636
|
-
},
|
|
8637
|
-
onKeyDown: (e) => {
|
|
8638
|
-
if (e.key === "Escape") {
|
|
8639
|
-
setIsOpen(false);
|
|
8640
|
-
return;
|
|
8641
|
-
}
|
|
8642
|
-
if (!isOpen || suggestions.length === 0) return;
|
|
8643
|
-
if (e.key === "ArrowDown") {
|
|
8644
|
-
e.preventDefault();
|
|
8645
|
-
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8646
|
-
} else if (e.key === "ArrowUp") {
|
|
8647
|
-
e.preventDefault();
|
|
8648
|
-
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8649
|
-
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8650
|
-
e.preventDefault();
|
|
8651
|
-
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8652
|
-
}
|
|
8653
|
-
},
|
|
8939
|
+
value: addr,
|
|
8940
|
+
onSelect: onValueChange,
|
|
8654
8941
|
placeholder: field.placeholder || "Search for an address...",
|
|
8655
8942
|
required: field.is_required,
|
|
8656
|
-
|
|
8657
|
-
"aria-autocomplete": "list",
|
|
8658
|
-
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8659
|
-
className: inputClass
|
|
8943
|
+
classNames: { input: inputClass }
|
|
8660
8944
|
}
|
|
8661
8945
|
),
|
|
8662
|
-
isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8663
|
-
"button",
|
|
8664
|
-
{
|
|
8665
|
-
type: "button",
|
|
8666
|
-
role: "option",
|
|
8667
|
-
"aria-selected": i === highlightedIndex,
|
|
8668
|
-
onMouseDown: (e) => {
|
|
8669
|
-
e.preventDefault();
|
|
8670
|
-
void selectSuggestion(s);
|
|
8671
|
-
},
|
|
8672
|
-
className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
|
|
8673
|
-
children: s.description
|
|
8674
|
-
},
|
|
8675
|
-
s.place_id
|
|
8676
|
-
)) }),
|
|
8677
8946
|
addr && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8678
8947
|
"input",
|
|
8679
8948
|
{
|
|
@@ -8693,121 +8962,22 @@ function LocationInput({
|
|
|
8693
8962
|
classNames,
|
|
8694
8963
|
inputClass
|
|
8695
8964
|
}) {
|
|
8696
|
-
const clientContext = useOptionalCimplifyClient();
|
|
8697
8965
|
const loc = value && typeof value === "object" ? value : null;
|
|
8698
|
-
|
|
8699
|
-
const [suggestions, setSuggestions] = React14__default.default.useState([]);
|
|
8700
|
-
const [isOpen, setIsOpen] = React14__default.default.useState(false);
|
|
8701
|
-
const [highlightedIndex, setHighlightedIndex] = React14__default.default.useState(-1);
|
|
8702
|
-
const debounceRef = React14__default.default.useRef(null);
|
|
8703
|
-
const seqRef = React14__default.default.useRef(0);
|
|
8704
|
-
const sessionTokenRef = React14__default.default.useRef(createSessionToken());
|
|
8705
|
-
const blurTimeoutRef = React14__default.default.useRef(null);
|
|
8706
|
-
React14__default.default.useEffect(() => {
|
|
8707
|
-
return () => {
|
|
8708
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8709
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8710
|
-
seqRef.current += 1;
|
|
8711
|
-
};
|
|
8712
|
-
}, []);
|
|
8713
|
-
const fetchSuggestions = React14__default.default.useCallback(async (query) => {
|
|
8714
|
-
const client = clientContext?.client;
|
|
8715
|
-
if (!client || query.length < 3) {
|
|
8716
|
-
setSuggestions([]);
|
|
8717
|
-
setIsOpen(false);
|
|
8718
|
-
return;
|
|
8719
|
-
}
|
|
8720
|
-
const seq = ++seqRef.current;
|
|
8721
|
-
const result = await client.places.autocomplete(query, sessionTokenRef.current);
|
|
8722
|
-
if (seqRef.current !== seq) return;
|
|
8723
|
-
if (result.ok && result.value.predictions.length > 0) {
|
|
8724
|
-
setSuggestions(result.value.predictions);
|
|
8725
|
-
setHighlightedIndex(0);
|
|
8726
|
-
setIsOpen(true);
|
|
8727
|
-
} else {
|
|
8728
|
-
setSuggestions([]);
|
|
8729
|
-
setIsOpen(false);
|
|
8730
|
-
}
|
|
8731
|
-
}, [clientContext]);
|
|
8732
|
-
const handleInputChange = (nextValue) => {
|
|
8733
|
-
setInputText(nextValue);
|
|
8734
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8735
|
-
debounceRef.current = setTimeout(() => void fetchSuggestions(nextValue.trim()), 300);
|
|
8736
|
-
};
|
|
8737
|
-
const selectSuggestion = React14__default.default.useCallback(async (suggestion) => {
|
|
8738
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
8739
|
-
if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
|
|
8740
|
-
const label = suggestion.description.split(",")[0]?.trim() || suggestion.description;
|
|
8741
|
-
setInputText(label);
|
|
8742
|
-
setSuggestions([]);
|
|
8743
|
-
setIsOpen(false);
|
|
8744
|
-
const client = clientContext?.client;
|
|
8745
|
-
if (!client) return;
|
|
8746
|
-
const result = await client.places.details(suggestion.place_id, sessionTokenRef.current);
|
|
8747
|
-
sessionTokenRef.current = createSessionToken();
|
|
8748
|
-
if (result.ok) {
|
|
8749
|
-
const d = result.value;
|
|
8750
|
-
onValueChange({
|
|
8751
|
-
latitude: d.latitude,
|
|
8752
|
-
longitude: d.longitude,
|
|
8753
|
-
label: d.formatted_address || label
|
|
8754
|
-
});
|
|
8755
|
-
setInputText(d.formatted_address || label);
|
|
8756
|
-
}
|
|
8757
|
-
}, [clientContext, onValueChange]);
|
|
8758
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", classNames?.locationInput), children: [
|
|
8966
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(classNames?.locationInput), children: [
|
|
8759
8967
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8760
|
-
|
|
8968
|
+
AddressAutocomplete,
|
|
8761
8969
|
{
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
},
|
|
8768
|
-
onBlur: () => {
|
|
8769
|
-
blurTimeoutRef.current = setTimeout(() => setIsOpen(false), 120);
|
|
8770
|
-
},
|
|
8771
|
-
onKeyDown: (e) => {
|
|
8772
|
-
if (e.key === "Escape") {
|
|
8773
|
-
setIsOpen(false);
|
|
8774
|
-
return;
|
|
8775
|
-
}
|
|
8776
|
-
if (!isOpen || suggestions.length === 0) return;
|
|
8777
|
-
if (e.key === "ArrowDown") {
|
|
8778
|
-
e.preventDefault();
|
|
8779
|
-
setHighlightedIndex((i) => i < suggestions.length - 1 ? i + 1 : 0);
|
|
8780
|
-
} else if (e.key === "ArrowUp") {
|
|
8781
|
-
e.preventDefault();
|
|
8782
|
-
setHighlightedIndex((i) => i > 0 ? i - 1 : suggestions.length - 1);
|
|
8783
|
-
} else if (e.key === "Enter" && highlightedIndex >= 0) {
|
|
8784
|
-
e.preventDefault();
|
|
8785
|
-
void selectSuggestion(suggestions[highlightedIndex]);
|
|
8786
|
-
}
|
|
8787
|
-
},
|
|
8970
|
+
value: loc ? { formatted_address: loc.label ?? "", latitude: loc.latitude, longitude: loc.longitude } : null,
|
|
8971
|
+
onSelect: (address) => onValueChange({
|
|
8972
|
+
latitude: address.latitude,
|
|
8973
|
+
longitude: address.longitude,
|
|
8974
|
+
label: address.formatted_address
|
|
8975
|
+
}),
|
|
8788
8976
|
placeholder: field.placeholder || "Search for a location...",
|
|
8789
8977
|
required: field.is_required,
|
|
8790
|
-
|
|
8791
|
-
"aria-autocomplete": "list",
|
|
8792
|
-
"aria-expanded": isOpen && suggestions.length > 0,
|
|
8793
|
-
className: inputClass
|
|
8978
|
+
classNames: { input: inputClass }
|
|
8794
8979
|
}
|
|
8795
8980
|
),
|
|
8796
|
-
isOpen && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "listbox", className: "absolute left-0 right-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-border bg-background shadow-lg", children: suggestions.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8797
|
-
"button",
|
|
8798
|
-
{
|
|
8799
|
-
type: "button",
|
|
8800
|
-
role: "option",
|
|
8801
|
-
"aria-selected": i === highlightedIndex,
|
|
8802
|
-
onMouseDown: (e) => {
|
|
8803
|
-
e.preventDefault();
|
|
8804
|
-
void selectSuggestion(s);
|
|
8805
|
-
},
|
|
8806
|
-
className: cn("block w-full px-3 py-2.5 text-left text-sm transition-colors", i === highlightedIndex ? "bg-muted text-foreground" : "text-foreground hover:bg-muted/70"),
|
|
8807
|
-
children: s.description
|
|
8808
|
-
},
|
|
8809
|
-
s.place_id
|
|
8810
|
-
)) }),
|
|
8811
8981
|
loc && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: [
|
|
8812
8982
|
loc.latitude.toFixed(4),
|
|
8813
8983
|
", ",
|
|
@@ -8938,12 +9108,6 @@ function SignatureInput({
|
|
|
8938
9108
|
!sig?.data_url && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: field.placeholder || "Draw your signature above" })
|
|
8939
9109
|
] });
|
|
8940
9110
|
}
|
|
8941
|
-
function createSessionToken() {
|
|
8942
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
8943
|
-
return crypto.randomUUID();
|
|
8944
|
-
}
|
|
8945
|
-
return `places_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
8946
|
-
}
|
|
8947
9111
|
function getTimeOfDay(timeStr) {
|
|
8948
9112
|
const hour = parseInt(timeStr.split("T").pop()?.split(":")[0] ?? timeStr.split(":")[0], 10);
|
|
8949
9113
|
if (hour < 12) return "morning";
|
|
@@ -9189,6 +9353,7 @@ function DateSlotPicker({
|
|
|
9189
9353
|
serviceId,
|
|
9190
9354
|
daysToShow = 7,
|
|
9191
9355
|
participantCount,
|
|
9356
|
+
units,
|
|
9192
9357
|
selectedSlot,
|
|
9193
9358
|
onSlotSelect,
|
|
9194
9359
|
availability: availabilityProp,
|
|
@@ -9223,6 +9388,7 @@ function DateSlotPicker({
|
|
|
9223
9388
|
dateRange.endDate,
|
|
9224
9389
|
{
|
|
9225
9390
|
participantCount,
|
|
9391
|
+
units,
|
|
9226
9392
|
enabled: availabilityProp === void 0
|
|
9227
9393
|
}
|
|
9228
9394
|
);
|
|
@@ -9359,6 +9525,7 @@ function ProductCustomizer({
|
|
|
9359
9525
|
onAddToCart,
|
|
9360
9526
|
onVariantChange,
|
|
9361
9527
|
showSpecialInstructions = true,
|
|
9528
|
+
submitLabel,
|
|
9362
9529
|
className,
|
|
9363
9530
|
classNames
|
|
9364
9531
|
}) {
|
|
@@ -9379,6 +9546,7 @@ function ProductCustomizer({
|
|
|
9379
9546
|
const [specialInstructions, setSpecialInstructions] = React14.useState("");
|
|
9380
9547
|
const [selectedSlot, setSelectedSlot] = React14.useState(null);
|
|
9381
9548
|
const [selectedStaffId, setSelectedStaffId] = React14.useState(null);
|
|
9549
|
+
const [unitsCount, setUnitsCount] = React14.useState(1);
|
|
9382
9550
|
const cart = useCart();
|
|
9383
9551
|
const currency = chunkPTPOQVXS_js.getProductCurrency(product);
|
|
9384
9552
|
const productType = product.type || "product";
|
|
@@ -9386,6 +9554,7 @@ function ProductCustomizer({
|
|
|
9386
9554
|
const isBundle = productType === "bundle";
|
|
9387
9555
|
const isDigital = productType === "digital";
|
|
9388
9556
|
const isService = productType === "service";
|
|
9557
|
+
const offersUnits = isService && chunkHKN3YG76_js.isMultiDayService(product);
|
|
9389
9558
|
const isStandard = !isComposite && !isBundle;
|
|
9390
9559
|
const hasVariants = isStandard && product.variants && product.variants.length > 0;
|
|
9391
9560
|
const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
|
|
@@ -9407,6 +9576,7 @@ function ProductCustomizer({
|
|
|
9407
9576
|
setSpecialInstructions("");
|
|
9408
9577
|
setSelectedSlot(null);
|
|
9409
9578
|
setSelectedStaffId(null);
|
|
9579
|
+
setUnitsCount(1);
|
|
9410
9580
|
}, [product.id, product.min_order_quantity]);
|
|
9411
9581
|
const slotStaff = React14.useMemo(
|
|
9412
9582
|
() => (selectedSlot?.available_staff ?? []).map((member) => ({
|
|
@@ -9496,7 +9666,17 @@ function ProductCustomizer({
|
|
|
9496
9666
|
if (perUnit === void 0 || perUnit === null) return void 0;
|
|
9497
9667
|
return chunkPTPOQVXS_js.parsePrice(perUnit) * quantity;
|
|
9498
9668
|
}, [quote, quantity]);
|
|
9499
|
-
const displayTotalPrice =
|
|
9669
|
+
const displayTotalPrice = React14.useMemo(() => {
|
|
9670
|
+
const total = quotedTotalPrice ?? localTotalPrice;
|
|
9671
|
+
const multiplier = chunkHKN3YG76_js.serviceBillingMultiplier(product, {
|
|
9672
|
+
quantity,
|
|
9673
|
+
units: unitsCount,
|
|
9674
|
+
scheduledStart: selectedSlot?.start_time,
|
|
9675
|
+
scheduledEnd: selectedSlot?.end_time
|
|
9676
|
+
});
|
|
9677
|
+
if (multiplier === null) return total;
|
|
9678
|
+
return total / Math.max(1, quantity) * multiplier;
|
|
9679
|
+
}, [quotedTotalPrice, localTotalPrice, product, quantity, unitsCount, selectedSlot]);
|
|
9500
9680
|
const handleVariantChange = React14.useCallback(
|
|
9501
9681
|
(variantId, variant) => {
|
|
9502
9682
|
setSelectedVariantId(variantId);
|
|
@@ -9528,7 +9708,8 @@ function ProductCustomizer({
|
|
|
9528
9708
|
scheduledStart: selectedSlot.start_time,
|
|
9529
9709
|
scheduledEnd: selectedSlot.end_time,
|
|
9530
9710
|
staffId: selectedStaffId ?? selectedSlot.available_staff?.[0]?.staff_id,
|
|
9531
|
-
resourceId: selectedSlot.available_resources?.[0]?.resource_id
|
|
9711
|
+
resourceId: selectedSlot.available_resources?.[0]?.resource_id,
|
|
9712
|
+
units: unitsCount > 1 ? unitsCount : void 0
|
|
9532
9713
|
} : {},
|
|
9533
9714
|
customerInputs: Object.keys(customerInputValues).length > 0 ? Object.entries(customerInputValues).filter(([, v]) => v !== void 0 && v !== "").map(([fieldId, value]) => ({ field_id: fieldId, value })) : void 0,
|
|
9534
9715
|
specialInstructions: specialInstructions.trim() || void 0
|
|
@@ -9549,7 +9730,7 @@ function ProductCustomizer({
|
|
|
9549
9730
|
} finally {
|
|
9550
9731
|
setIsSubmitting(false);
|
|
9551
9732
|
}
|
|
9552
|
-
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, isSubmitting, onAddToCart, cart]);
|
|
9733
|
+
}, [product, quantity, selectedVariantId, selectedVariant, quoteId, normalizedAddOnOptionIds, selectedAddOnOptions, isComposite, compositeSelections, isBundle, bundleSelections, selectedBillingPlan, customerInputValues, specialInstructions, isService, selectedSlot, selectedStaffId, unitsCount, isSubmitting, onAddToCart, cart]);
|
|
9553
9734
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer": true, className: cn("space-y-6", className, classNames?.root), children: [
|
|
9554
9735
|
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9555
9736
|
CompositeSelector,
|
|
@@ -9586,9 +9767,17 @@ function ProductCustomizer({
|
|
|
9586
9767
|
setSelectedStaffId(null);
|
|
9587
9768
|
},
|
|
9588
9769
|
participantCount: quantity,
|
|
9589
|
-
|
|
9770
|
+
units: offersUnits ? unitsCount : void 0,
|
|
9771
|
+
currency,
|
|
9772
|
+
schedulingMode: product.scheduling_mode ?? (offersUnits ? "multi_day" : "intraday"),
|
|
9773
|
+
durationUnit: product.duration_unit,
|
|
9774
|
+
durationValue: product.duration_value
|
|
9590
9775
|
}
|
|
9591
9776
|
),
|
|
9777
|
+
offersUnits && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-units": true, className: classNames?.units, children: [
|
|
9778
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Units" }),
|
|
9779
|
+
/* @__PURE__ */ jsxRuntime.jsx(QuantitySelector, { value: unitsCount, onChange: setUnitsCount, min: 1 })
|
|
9780
|
+
] }),
|
|
9592
9781
|
isService && slotStaff.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-staff": true, children: [
|
|
9593
9782
|
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-xs font-medium uppercase tracking-wider text-muted-foreground mb-3", children: "Choose your specialist" }),
|
|
9594
9783
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -9747,8 +9936,10 @@ function ProductCustomizer({
|
|
|
9747
9936
|
isAdded && classNames?.submitButtonAdded,
|
|
9748
9937
|
classNames?.submitButton
|
|
9749
9938
|
),
|
|
9750
|
-
children: isAdded ? "Added
|
|
9751
|
-
"Add to Cart
|
|
9939
|
+
children: isAdded ? "Added" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9940
|
+
submitLabel ?? "Add to Cart",
|
|
9941
|
+
" \xB7",
|
|
9942
|
+
" ",
|
|
9752
9943
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: displayTotalPrice, currency })
|
|
9753
9944
|
] })
|
|
9754
9945
|
}
|
|
@@ -9908,13 +10099,13 @@ function StoreVideo({
|
|
|
9908
10099
|
autoplay = true,
|
|
9909
10100
|
loop = true,
|
|
9910
10101
|
muted = true,
|
|
9911
|
-
lazy = true,
|
|
10102
|
+
lazy: lazy2 = true,
|
|
9912
10103
|
className
|
|
9913
10104
|
}) {
|
|
9914
10105
|
const ref = React14.useRef(null);
|
|
9915
|
-
const [inView, setInView] = React14.useState(!
|
|
10106
|
+
const [inView, setInView] = React14.useState(!lazy2);
|
|
9916
10107
|
React14.useEffect(() => {
|
|
9917
|
-
if (!
|
|
10108
|
+
if (!lazy2 || inView) return;
|
|
9918
10109
|
const node = ref.current;
|
|
9919
10110
|
if (!node || typeof IntersectionObserver === "undefined") {
|
|
9920
10111
|
setInView(true);
|
|
@@ -9931,7 +10122,7 @@ function StoreVideo({
|
|
|
9931
10122
|
);
|
|
9932
10123
|
observer.observe(node);
|
|
9933
10124
|
return () => observer.disconnect();
|
|
9934
|
-
}, [
|
|
10125
|
+
}, [lazy2, inView]);
|
|
9935
10126
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9936
10127
|
"div",
|
|
9937
10128
|
{
|
|
@@ -9949,7 +10140,7 @@ function StoreVideo({
|
|
|
9949
10140
|
muted,
|
|
9950
10141
|
playsInline: true,
|
|
9951
10142
|
controls,
|
|
9952
|
-
preload:
|
|
10143
|
+
preload: lazy2 ? "metadata" : "auto",
|
|
9953
10144
|
"aria-label": alt,
|
|
9954
10145
|
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" },
|
|
9955
10146
|
children: poster ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -10191,7 +10382,7 @@ function CartLineItemRow({
|
|
|
10191
10382
|
const isOptimistic = item.isOptimistic === true;
|
|
10192
10383
|
const lineTotal = chunkPTPOQVXS_js.parsePrice(unitPrice) * item.quantity;
|
|
10193
10384
|
const imageUrl = item.product.image_url;
|
|
10194
|
-
const variantLabel2 = item.variant ?
|
|
10385
|
+
const variantLabel2 = item.variant ? chunkHKN3YG76_js.getVariantDisplayName(item.variant, item.product.name) : null;
|
|
10195
10386
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10196
10387
|
"div",
|
|
10197
10388
|
{
|
|
@@ -10291,7 +10482,7 @@ function CartLineItemRow({
|
|
|
10291
10482
|
input.field_name,
|
|
10292
10483
|
":"
|
|
10293
10484
|
] }),
|
|
10294
|
-
/* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cimplify-cart-input-value": true, className: "text-foreground m-0 truncate", children: input.field_type ===
|
|
10485
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { "data-cimplify-cart-input-value": true, className: "text-foreground m-0 truncate", children: input.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.File || input.field_type === chunkHKN3YG76_js.INPUT_FIELD_TYPE.Image ? String(input.value).split("/").pop() || "Uploaded file" : String(input.value) })
|
|
10295
10486
|
] }, input.field_id)) }),
|
|
10296
10487
|
(item.lineType === "service" || item.product.type === "service") && item.scheduledStart && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-service-info": true, className: cn("mt-1.5 flex items-center gap-1.5 text-xs text-muted-foreground", classNames?.serviceInfo), children: [
|
|
10297
10488
|
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3.5 h-3.5 shrink-0", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
|
|
@@ -11176,7 +11367,7 @@ function FoodProductCard({
|
|
|
11176
11367
|
const tags = product.tags || [];
|
|
11177
11368
|
const isSignature = product.metadata?.is_signature === true;
|
|
11178
11369
|
const isNew = product.metadata?.is_new === true;
|
|
11179
|
-
const isComposite = product.type ===
|
|
11370
|
+
const isComposite = product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite;
|
|
11180
11371
|
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
|
|
11181
11372
|
/* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "square", renderImage, children: [
|
|
11182
11373
|
isSignature && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-3 left-3 inline-flex items-center gap-1 text-[11px] font-semibold tracking-wide bg-amber-50 text-amber-700 border border-amber-200/60 px-2 py-0.5 rounded-md", children: [
|
|
@@ -11640,18 +11831,18 @@ function RentalServiceCard({
|
|
|
11640
11831
|
const currency = chunkPTPOQVXS_js.getProductCurrency(product);
|
|
11641
11832
|
const hasDeposit = product.deposit_type && product.deposit_type !== "none" && product.deposit_amount;
|
|
11642
11833
|
const unit = product.duration_unit ? UNIT_LABELS2[product.duration_unit] || product.duration_unit : "day";
|
|
11834
|
+
const priceUnit = product.price_basis === "per_person" ? "guest" : product.price_basis === "flat" ? "" : unit;
|
|
11643
11835
|
const status = product.inventory_status;
|
|
11644
11836
|
return /* @__PURE__ */ jsxRuntime.jsxs(CardShell, { product, renderLink, className, children: [
|
|
11645
11837
|
/* @__PURE__ */ jsxRuntime.jsxs(CardImage, { src: image, alt: product.name, aspectRatio: "16/9", renderImage, children: [
|
|
11646
11838
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent pointer-events-none" }),
|
|
11647
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 left-3 inline-flex items-center text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children: [
|
|
11839
|
+
priceUnit && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 left-3 inline-flex items-center text-[12px] font-semibold px-2.5 py-1 rounded-lg bg-background/92 backdrop-blur-xl text-foreground shadow-sm", children: [
|
|
11648
11840
|
"Per ",
|
|
11649
|
-
|
|
11841
|
+
priceUnit
|
|
11650
11842
|
] }),
|
|
11651
11843
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute bottom-3 right-3 text-white font-bold text-lg drop-shadow-sm", children: [
|
|
11652
11844
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: product.default_price, currency }),
|
|
11653
|
-
|
|
11654
|
-
unit
|
|
11845
|
+
priceUnit && `/${priceUnit}`
|
|
11655
11846
|
] }),
|
|
11656
11847
|
product.general_service_capacity != null && product.general_service_capacity > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute top-3 left-3 text-[11px] font-semibold tracking-wide bg-blue-50/90 text-blue-700 border border-blue-200/50 backdrop-blur px-2 py-0.5 rounded-md", children: [
|
|
11657
11848
|
"Up to ",
|
|
@@ -11943,36 +12134,36 @@ var BUILT_IN_CARDS = {
|
|
|
11943
12134
|
["composite" /* Composite */]: CompositeProductCard,
|
|
11944
12135
|
["subscription" /* Subscription */]: SubscriptionCard
|
|
11945
12136
|
};
|
|
11946
|
-
var RENTAL_UNITS = /* @__PURE__ */ new Set([
|
|
11947
|
-
var LEASE_UNITS = /* @__PURE__ */ new Set([
|
|
12137
|
+
var RENTAL_UNITS = /* @__PURE__ */ new Set([chunkHKN3YG76_js.DURATION_UNIT.Days, chunkHKN3YG76_js.DURATION_UNIT.Hours]);
|
|
12138
|
+
var LEASE_UNITS = /* @__PURE__ */ new Set([chunkHKN3YG76_js.DURATION_UNIT.Weeks, chunkHKN3YG76_js.DURATION_UNIT.Months, chunkHKN3YG76_js.DURATION_UNIT.Years]);
|
|
11948
12139
|
function resolveCardVariant(product) {
|
|
11949
|
-
if (product.type ===
|
|
12140
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Bundle) {
|
|
11950
12141
|
return "bundle" /* Bundle */;
|
|
11951
12142
|
}
|
|
11952
|
-
if (product.type ===
|
|
12143
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite) {
|
|
11953
12144
|
return "composite" /* Composite */;
|
|
11954
12145
|
}
|
|
11955
12146
|
if (product.quantity_pricing && product.quantity_pricing.length > 1) {
|
|
11956
12147
|
return "wholesale" /* Wholesale */;
|
|
11957
12148
|
}
|
|
11958
|
-
if (product.type ===
|
|
12149
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Digital) {
|
|
11959
12150
|
return "digital" /* Digital */;
|
|
11960
12151
|
}
|
|
11961
|
-
if (product.type ===
|
|
12152
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Service) {
|
|
11962
12153
|
switch (product.boundary_kind) {
|
|
11963
|
-
case
|
|
12154
|
+
case chunkHKN3YG76_js.BOUNDARY_KIND.CheckInOut:
|
|
11964
12155
|
return "accommodation" /* Accommodation */;
|
|
11965
|
-
case
|
|
12156
|
+
case chunkHKN3YG76_js.BOUNDARY_KIND.MoveInOut:
|
|
11966
12157
|
return "lease" /* Lease */;
|
|
11967
|
-
case
|
|
11968
|
-
case
|
|
11969
|
-
case
|
|
12158
|
+
case chunkHKN3YG76_js.BOUNDARY_KIND.PickupReturn:
|
|
12159
|
+
case chunkHKN3YG76_js.BOUNDARY_KIND.DeliveryCollection:
|
|
12160
|
+
case chunkHKN3YG76_js.BOUNDARY_KIND.AccessVacate:
|
|
11970
12161
|
return "rental" /* Rental */;
|
|
11971
12162
|
}
|
|
11972
12163
|
if (product.duration_unit && RENTAL_UNITS.has(product.duration_unit)) {
|
|
11973
12164
|
return "rental" /* Rental */;
|
|
11974
12165
|
}
|
|
11975
|
-
if (product.duration_unit ===
|
|
12166
|
+
if (product.duration_unit === chunkHKN3YG76_js.DURATION_UNIT.Nights) {
|
|
11976
12167
|
return "accommodation" /* Accommodation */;
|
|
11977
12168
|
}
|
|
11978
12169
|
if (product.duration_unit && LEASE_UNITS.has(product.duration_unit)) {
|
|
@@ -11983,10 +12174,10 @@ function resolveCardVariant(product) {
|
|
|
11983
12174
|
}
|
|
11984
12175
|
return "standard" /* Standard */;
|
|
11985
12176
|
}
|
|
11986
|
-
if (product.render_hint ===
|
|
12177
|
+
if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Food) {
|
|
11987
12178
|
return "food" /* Food */;
|
|
11988
12179
|
}
|
|
11989
|
-
if (product.render_hint ===
|
|
12180
|
+
if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Physical) {
|
|
11990
12181
|
return "retail" /* Retail */;
|
|
11991
12182
|
}
|
|
11992
12183
|
return "retail" /* Retail */;
|
|
@@ -12245,6 +12436,15 @@ function ProductGrid({
|
|
|
12245
12436
|
] });
|
|
12246
12437
|
}
|
|
12247
12438
|
|
|
12439
|
+
// src/react/utils/attachment.ts
|
|
12440
|
+
var IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".svg"];
|
|
12441
|
+
function isImageAttachment(attachment) {
|
|
12442
|
+
if (attachment.mime_type?.startsWith("image/")) return true;
|
|
12443
|
+
if (attachment.mime_type) return false;
|
|
12444
|
+
const path = attachment.url.split("?")[0].split("#")[0].toLowerCase();
|
|
12445
|
+
return IMAGE_EXTENSIONS.some((extension) => path.endsWith(extension));
|
|
12446
|
+
}
|
|
12447
|
+
|
|
12248
12448
|
// src/react/utils/boundary-labels.ts
|
|
12249
12449
|
var PRESET_LABELS = {
|
|
12250
12450
|
check_in_out: { start: "Check-in", end: "Check-out" },
|
|
@@ -12254,14 +12454,14 @@ var PRESET_LABELS = {
|
|
|
12254
12454
|
move_in_out: { start: "Move-in", end: "Move-out" }
|
|
12255
12455
|
};
|
|
12256
12456
|
function defaultBoundaryKind(unit) {
|
|
12257
|
-
return unit ===
|
|
12457
|
+
return unit === chunkHKN3YG76_js.DURATION_UNIT.Nights ? chunkHKN3YG76_js.BOUNDARY_KIND.CheckInOut : chunkHKN3YG76_js.BOUNDARY_KIND.PickupReturn;
|
|
12258
12458
|
}
|
|
12259
12459
|
function resolveBoundaryKind(product) {
|
|
12260
12460
|
return product.boundary_kind ?? defaultBoundaryKind(product.duration_unit);
|
|
12261
12461
|
}
|
|
12262
12462
|
function getBoundaryLabels(product) {
|
|
12263
12463
|
const kind = resolveBoundaryKind(product);
|
|
12264
|
-
if (kind ===
|
|
12464
|
+
if (kind === chunkHKN3YG76_js.BOUNDARY_KIND.Custom) {
|
|
12265
12465
|
return {
|
|
12266
12466
|
start: product.boundary_start_label?.trim() || "Start",
|
|
12267
12467
|
end: product.boundary_end_label?.trim() || "End"
|
|
@@ -13426,6 +13626,7 @@ function BookingPage({
|
|
|
13426
13626
|
const [selectedDate, setSelectedDate] = React14.useState(null);
|
|
13427
13627
|
const [selectedStaffId, setSelectedStaffId] = React14.useState(null);
|
|
13428
13628
|
const [selectedResourceId, setSelectedResourceId] = React14.useState(null);
|
|
13629
|
+
const [bookingUnits, setBookingUnits] = React14.useState(1);
|
|
13429
13630
|
const [isSubmitting, setIsSubmitting] = React14.useState(false);
|
|
13430
13631
|
const [error, setError] = React14.useState(null);
|
|
13431
13632
|
const hasResourceStep = resources && resources.length > 0;
|
|
@@ -13519,7 +13720,8 @@ function BookingPage({
|
|
|
13519
13720
|
scheduledStart: selectedSlot.start_time,
|
|
13520
13721
|
scheduledEnd: selectedSlot.end_time,
|
|
13521
13722
|
staffId: selectedStaffId || void 0,
|
|
13522
|
-
resourceId: selectedResourceId || void 0
|
|
13723
|
+
resourceId: selectedResourceId || void 0,
|
|
13724
|
+
units: bookingUnits > 1 ? bookingUnits : void 0
|
|
13523
13725
|
});
|
|
13524
13726
|
onBooked?.(selectedSlot, selectedStaffId);
|
|
13525
13727
|
}
|
|
@@ -13615,6 +13817,31 @@ function BookingPage({
|
|
|
13615
13817
|
] }),
|
|
13616
13818
|
step === STEP.CONFIRM && selectedSlot && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-step": "confirm", className: classNames?.step, children: [
|
|
13617
13819
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { "data-cimplify-booking-step-title": true, className: classNames?.stepTitle, children: "Confirm your booking" }),
|
|
13820
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-units": true, className: classNames?.summaryRow, children: [
|
|
13821
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Units" }),
|
|
13822
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-units-stepper": true, children: [
|
|
13823
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13824
|
+
"button",
|
|
13825
|
+
{
|
|
13826
|
+
type: "button",
|
|
13827
|
+
"data-cimplify-booking-units-decrement": true,
|
|
13828
|
+
onClick: () => setBookingUnits((current) => Math.max(1, current - 1)),
|
|
13829
|
+
disabled: bookingUnits <= 1,
|
|
13830
|
+
children: "\u2212"
|
|
13831
|
+
}
|
|
13832
|
+
),
|
|
13833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-booking-units-value": true, children: bookingUnits }),
|
|
13834
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13835
|
+
"button",
|
|
13836
|
+
{
|
|
13837
|
+
type: "button",
|
|
13838
|
+
"data-cimplify-booking-units-increment": true,
|
|
13839
|
+
onClick: () => setBookingUnits((current) => current + 1),
|
|
13840
|
+
children: "+"
|
|
13841
|
+
}
|
|
13842
|
+
)
|
|
13843
|
+
] })
|
|
13844
|
+
] }),
|
|
13618
13845
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-summary": true, className: classNames?.summary, children: [
|
|
13619
13846
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-booking-summary-row": true, className: classNames?.summaryRow, children: [
|
|
13620
13847
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Service" }),
|
|
@@ -14322,7 +14549,7 @@ function DefaultProductLayout({
|
|
|
14322
14549
|
className
|
|
14323
14550
|
}) {
|
|
14324
14551
|
const [selectedVariantId, setSelectedVariantId] = React14__default.default.useState();
|
|
14325
|
-
const images =
|
|
14552
|
+
const images = chunkHKN3YG76_js.resolveGallery(product, selectedVariantId);
|
|
14326
14553
|
const currency = chunkPTPOQVXS_js.getProductCurrency(product);
|
|
14327
14554
|
const hasRange = product.quantity_pricing && product.quantity_pricing.length > 1 || product.variants && product.variants.length > 1;
|
|
14328
14555
|
const onSale = chunkPTPOQVXS_js.isOnSale(product);
|
|
@@ -14969,25 +15196,25 @@ function resolveTemplateKey(product) {
|
|
|
14969
15196
|
if (typeof metaTemplate === "string" && metaTemplate.trim()) {
|
|
14970
15197
|
return metaTemplate.trim();
|
|
14971
15198
|
}
|
|
14972
|
-
if (product.type ===
|
|
15199
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Bundle) {
|
|
14973
15200
|
return "bundle" /* Bundle */;
|
|
14974
15201
|
}
|
|
14975
|
-
if (product.type ===
|
|
15202
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Composite) {
|
|
14976
15203
|
return "composite" /* Composite */;
|
|
14977
15204
|
}
|
|
14978
15205
|
if (product.quantity_pricing && product.quantity_pricing.length > 1) {
|
|
14979
15206
|
return "wholesale" /* Wholesale */;
|
|
14980
15207
|
}
|
|
14981
|
-
if (product.type ===
|
|
15208
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Service) {
|
|
14982
15209
|
return "service" /* Service */;
|
|
14983
15210
|
}
|
|
14984
|
-
if (product.type ===
|
|
15211
|
+
if (product.type === chunkHKN3YG76_js.PRODUCT_TYPE.Digital) {
|
|
14985
15212
|
return "digital" /* Digital */;
|
|
14986
15213
|
}
|
|
14987
|
-
if (product.render_hint ===
|
|
15214
|
+
if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Food) {
|
|
14988
15215
|
return "food" /* Food */;
|
|
14989
15216
|
}
|
|
14990
|
-
if (product.render_hint ===
|
|
15217
|
+
if (product.render_hint === chunkHKN3YG76_js.RENDER_HINT.Physical) {
|
|
14991
15218
|
return "physical" /* Physical */;
|
|
14992
15219
|
}
|
|
14993
15220
|
return "default" /* Default */;
|
|
@@ -16751,6 +16978,112 @@ function CurrencySelector({
|
|
|
16751
16978
|
);
|
|
16752
16979
|
}) });
|
|
16753
16980
|
}
|
|
16981
|
+
var Streamdown = React14.lazy(() => import('streamdown').then((m) => ({ default: m.Streamdown })));
|
|
16982
|
+
function detectLookbehindSupport() {
|
|
16983
|
+
try {
|
|
16984
|
+
new RegExp("(?<=a)b");
|
|
16985
|
+
return true;
|
|
16986
|
+
} catch {
|
|
16987
|
+
return false;
|
|
16988
|
+
}
|
|
16989
|
+
}
|
|
16990
|
+
var supportsRegExpLookbehind = detectLookbehindSupport();
|
|
16991
|
+
marked.marked.setOptions({
|
|
16992
|
+
gfm: true,
|
|
16993
|
+
breaks: true
|
|
16994
|
+
});
|
|
16995
|
+
var ALLOWED_TAGS = [
|
|
16996
|
+
"a",
|
|
16997
|
+
"b",
|
|
16998
|
+
"blockquote",
|
|
16999
|
+
"br",
|
|
17000
|
+
"code",
|
|
17001
|
+
"del",
|
|
17002
|
+
"em",
|
|
17003
|
+
"h1",
|
|
17004
|
+
"h2",
|
|
17005
|
+
"h3",
|
|
17006
|
+
"h4",
|
|
17007
|
+
"hr",
|
|
17008
|
+
"i",
|
|
17009
|
+
"li",
|
|
17010
|
+
"ol",
|
|
17011
|
+
"p",
|
|
17012
|
+
"pre",
|
|
17013
|
+
"strong",
|
|
17014
|
+
"table",
|
|
17015
|
+
"tbody",
|
|
17016
|
+
"td",
|
|
17017
|
+
"th",
|
|
17018
|
+
"thead",
|
|
17019
|
+
"tr",
|
|
17020
|
+
"ul"
|
|
17021
|
+
];
|
|
17022
|
+
var ALLOWED_ATTR = ["href", "rel", "target", "title", "start"];
|
|
17023
|
+
var hooksInstalled = false;
|
|
17024
|
+
function installHooks() {
|
|
17025
|
+
if (hooksInstalled) return;
|
|
17026
|
+
hooksInstalled = true;
|
|
17027
|
+
DOMPurify__default.default.addHook("afterSanitizeAttributes", (node) => {
|
|
17028
|
+
if (!(node instanceof HTMLAnchorElement)) return;
|
|
17029
|
+
if (!node.getAttribute("href")) return;
|
|
17030
|
+
node.setAttribute("rel", "noreferrer noopener");
|
|
17031
|
+
node.setAttribute("target", "_blank");
|
|
17032
|
+
});
|
|
17033
|
+
}
|
|
17034
|
+
var CACHE_LIMIT = 300;
|
|
17035
|
+
var cache = /* @__PURE__ */ new Map();
|
|
17036
|
+
function renderMarkdown(text) {
|
|
17037
|
+
const cached = cache.get(text);
|
|
17038
|
+
if (cached !== void 0) return cached;
|
|
17039
|
+
installHooks();
|
|
17040
|
+
const html = DOMPurify__default.default.sanitize(marked.marked.parse(text), {
|
|
17041
|
+
ALLOWED_TAGS,
|
|
17042
|
+
ALLOWED_ATTR
|
|
17043
|
+
});
|
|
17044
|
+
if (cache.size >= CACHE_LIMIT) {
|
|
17045
|
+
const oldest = cache.keys().next().value;
|
|
17046
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
17047
|
+
}
|
|
17048
|
+
cache.set(text, html);
|
|
17049
|
+
return html;
|
|
17050
|
+
}
|
|
17051
|
+
function SettledMarkdown({ text, className }) {
|
|
17052
|
+
const html = React14.useMemo(() => renderMarkdown(text), [text]);
|
|
17053
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, dangerouslySetInnerHTML: { __html: html } });
|
|
17054
|
+
}
|
|
17055
|
+
var ChatMarkdown = React14.memo(function ChatMarkdown2({
|
|
17056
|
+
text,
|
|
17057
|
+
className,
|
|
17058
|
+
streaming = false
|
|
17059
|
+
}) {
|
|
17060
|
+
if (streaming && supportsRegExpLookbehind) {
|
|
17061
|
+
return (
|
|
17062
|
+
// While the streamdown chunk loads, the settled renderer keeps the
|
|
17063
|
+
// text on screen — no blank frame at stream start.
|
|
17064
|
+
/* @__PURE__ */ jsxRuntime.jsx(React14.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className }), children: /* @__PURE__ */ jsxRuntime.jsx(Streamdown, { className, children: text }) })
|
|
17065
|
+
);
|
|
17066
|
+
}
|
|
17067
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SettledMarkdown, { text, className });
|
|
17068
|
+
});
|
|
17069
|
+
|
|
17070
|
+
// src/react/chat-model.ts
|
|
17071
|
+
function partitionMessages(messages) {
|
|
17072
|
+
const bubbles = [];
|
|
17073
|
+
const reactionsByTarget = /* @__PURE__ */ new Map();
|
|
17074
|
+
for (const message of messages) {
|
|
17075
|
+
if (message.content_type === "reaction") {
|
|
17076
|
+
const targetId = message.reply_to?.id;
|
|
17077
|
+
if (!targetId) continue;
|
|
17078
|
+
const list = reactionsByTarget.get(targetId);
|
|
17079
|
+
if (list) list.push(message);
|
|
17080
|
+
else reactionsByTarget.set(targetId, [message]);
|
|
17081
|
+
continue;
|
|
17082
|
+
}
|
|
17083
|
+
bubbles.push(message);
|
|
17084
|
+
}
|
|
17085
|
+
return { bubbles, reactionsByTarget };
|
|
17086
|
+
}
|
|
16754
17087
|
function ChatWidget({
|
|
16755
17088
|
client: clientProp,
|
|
16756
17089
|
businessName = "Support",
|
|
@@ -16767,24 +17100,46 @@ function ChatWidget({
|
|
|
16767
17100
|
const context = useOptionalCimplifyClient();
|
|
16768
17101
|
const client = clientProp ?? context?.client;
|
|
16769
17102
|
const [open, setOpen] = React14.useState(defaultOpen);
|
|
16770
|
-
const [
|
|
16771
|
-
const {
|
|
16772
|
-
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
17103
|
+
const [readCount, setReadCount] = React14.useState(0);
|
|
17104
|
+
const {
|
|
17105
|
+
messages,
|
|
17106
|
+
isLoading,
|
|
17107
|
+
isSending,
|
|
17108
|
+
awaitingReply,
|
|
17109
|
+
seenAt,
|
|
17110
|
+
error,
|
|
17111
|
+
isActive,
|
|
17112
|
+
claimedAs,
|
|
17113
|
+
signOut,
|
|
17114
|
+
send,
|
|
17115
|
+
react,
|
|
17116
|
+
startConversation
|
|
17117
|
+
} = useChat({ client, pollInterval });
|
|
17118
|
+
const showTyping = isSending || awaitingReply;
|
|
17119
|
+
const attachPhoto = React14.useCallback(
|
|
17120
|
+
async (file) => {
|
|
17121
|
+
if (!client || !file.type.startsWith("image/") || file.size > 5 * 1024 * 1024) return;
|
|
17122
|
+
const uploaded = await client.uploads.upload(file);
|
|
17123
|
+
if (!uploaded.ok) return;
|
|
17124
|
+
const attachment = {
|
|
17125
|
+
upload_id: uploaded.value.id,
|
|
17126
|
+
url: uploaded.value.url,
|
|
17127
|
+
mime_type: uploaded.value.content_type,
|
|
17128
|
+
filename: uploaded.value.filename,
|
|
17129
|
+
size: uploaded.value.size_bytes
|
|
17130
|
+
};
|
|
17131
|
+
await send("", { attachments: [attachment] });
|
|
17132
|
+
},
|
|
17133
|
+
[client, send]
|
|
17134
|
+
);
|
|
17135
|
+
const unread = open ? 0 : messages.slice(readCount).filter((m) => m.sender_type !== "customer").length;
|
|
16783
17136
|
const handleOpen = React14.useCallback(() => {
|
|
16784
17137
|
setOpen(true);
|
|
16785
|
-
setUnread(0);
|
|
16786
17138
|
}, []);
|
|
16787
|
-
const handleClose = React14.useCallback(() =>
|
|
17139
|
+
const handleClose = React14.useCallback(() => {
|
|
17140
|
+
setReadCount(messages.length);
|
|
17141
|
+
setOpen(false);
|
|
17142
|
+
}, [messages.length]);
|
|
16788
17143
|
const isLeft = position === "bottom-left";
|
|
16789
17144
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16790
17145
|
"div",
|
|
@@ -16815,11 +17170,17 @@ function ChatWidget({
|
|
|
16815
17170
|
messages,
|
|
16816
17171
|
isLoading,
|
|
16817
17172
|
isSending,
|
|
17173
|
+
isTyping: showTyping,
|
|
17174
|
+
seenAt,
|
|
16818
17175
|
error,
|
|
16819
17176
|
isActive,
|
|
16820
17177
|
isLeft,
|
|
17178
|
+
claimedAs,
|
|
17179
|
+
onSignOut: () => void signOut(),
|
|
16821
17180
|
onClose: handleClose,
|
|
16822
17181
|
onSend: send,
|
|
17182
|
+
onReact: react,
|
|
17183
|
+
onAttach: attachPhoto,
|
|
16823
17184
|
onStartConversation: startConversation,
|
|
16824
17185
|
classNames
|
|
16825
17186
|
}
|
|
@@ -16872,11 +17233,17 @@ function Panel({
|
|
|
16872
17233
|
messages,
|
|
16873
17234
|
isLoading,
|
|
16874
17235
|
isSending,
|
|
17236
|
+
isTyping,
|
|
17237
|
+
seenAt,
|
|
16875
17238
|
error,
|
|
16876
17239
|
isActive,
|
|
16877
17240
|
isLeft,
|
|
17241
|
+
claimedAs,
|
|
17242
|
+
onSignOut,
|
|
16878
17243
|
onClose,
|
|
16879
17244
|
onSend,
|
|
17245
|
+
onReact,
|
|
17246
|
+
onAttach,
|
|
16880
17247
|
onStartConversation,
|
|
16881
17248
|
classNames
|
|
16882
17249
|
}) {
|
|
@@ -16896,6 +17263,8 @@ function Panel({
|
|
|
16896
17263
|
Header,
|
|
16897
17264
|
{
|
|
16898
17265
|
businessName,
|
|
17266
|
+
claimedAs,
|
|
17267
|
+
onSignOut,
|
|
16899
17268
|
onClose,
|
|
16900
17269
|
className: classNames?.header
|
|
16901
17270
|
}
|
|
@@ -16914,7 +17283,9 @@ function Panel({
|
|
|
16914
17283
|
{
|
|
16915
17284
|
messages,
|
|
16916
17285
|
isLoading,
|
|
16917
|
-
isSending,
|
|
17286
|
+
isSending: isTyping,
|
|
17287
|
+
seenAt,
|
|
17288
|
+
onReact,
|
|
16918
17289
|
className: classNames?.messages
|
|
16919
17290
|
}
|
|
16920
17291
|
),
|
|
@@ -16925,6 +17296,7 @@ function Panel({
|
|
|
16925
17296
|
placeholder,
|
|
16926
17297
|
isSending,
|
|
16927
17298
|
onSend,
|
|
17299
|
+
onAttach,
|
|
16928
17300
|
className: classNames?.input
|
|
16929
17301
|
}
|
|
16930
17302
|
),
|
|
@@ -16939,6 +17311,8 @@ function Panel({
|
|
|
16939
17311
|
}
|
|
16940
17312
|
function Header({
|
|
16941
17313
|
businessName,
|
|
17314
|
+
claimedAs,
|
|
17315
|
+
onSignOut,
|
|
16942
17316
|
onClose,
|
|
16943
17317
|
className
|
|
16944
17318
|
}) {
|
|
@@ -16953,7 +17327,22 @@ function Header({
|
|
|
16953
17327
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-[10px] bg-white/15", children: /* @__PURE__ */ jsxRuntime.jsx(ChatIcon, { className: "h-5 w-5" }) }),
|
|
16954
17328
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
16955
17329
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[15px] font-semibold leading-tight", children: businessName }),
|
|
16956
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1
|
|
17330
|
+
claimedAs ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 truncate text-xs text-white/60", children: [
|
|
17331
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
|
|
17332
|
+
"Chatting as ",
|
|
17333
|
+
claimedAs
|
|
17334
|
+
] }),
|
|
17335
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
17336
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17337
|
+
"button",
|
|
17338
|
+
{
|
|
17339
|
+
type: "button",
|
|
17340
|
+
onClick: onSignOut,
|
|
17341
|
+
className: "flex-shrink-0 underline underline-offset-2 hover:text-white",
|
|
17342
|
+
children: "Not you?"
|
|
17343
|
+
}
|
|
17344
|
+
)
|
|
17345
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-xs text-white/60", children: [
|
|
16957
17346
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-[7px] w-[7px] flex-shrink-0 rounded-full bg-emerald-400" }),
|
|
16958
17347
|
"Online"
|
|
16959
17348
|
] })
|
|
@@ -17020,16 +17409,25 @@ function Welcome({
|
|
|
17020
17409
|
}
|
|
17021
17410
|
);
|
|
17022
17411
|
}
|
|
17412
|
+
var QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F"];
|
|
17413
|
+
function lastSeenCustomerMessageId(bubbles, seenAt) {
|
|
17414
|
+
if (!seenAt || !bubbles.some((m) => m.sender_type === "agent")) return null;
|
|
17415
|
+
for (let i = bubbles.length - 1; i >= 0; i--) {
|
|
17416
|
+
if (bubbles[i].sender_type !== "customer") continue;
|
|
17417
|
+
return bubbles[i].created_at <= seenAt ? bubbles[i].id : null;
|
|
17418
|
+
}
|
|
17419
|
+
return null;
|
|
17420
|
+
}
|
|
17023
17421
|
function MessageList({
|
|
17024
17422
|
messages,
|
|
17025
17423
|
isLoading,
|
|
17026
17424
|
isSending,
|
|
17425
|
+
seenAt,
|
|
17426
|
+
onReact,
|
|
17027
17427
|
className
|
|
17028
17428
|
}) {
|
|
17029
|
-
const
|
|
17030
|
-
|
|
17031
|
-
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
17032
|
-
}, [messages.length, isSending]);
|
|
17429
|
+
const { bubbles, reactionsByTarget } = partitionMessages(messages);
|
|
17430
|
+
const seenMessageId = lastSeenCustomerMessageId(bubbles, seenAt);
|
|
17033
17431
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17034
17432
|
"div",
|
|
17035
17433
|
{
|
|
@@ -17039,44 +17437,123 @@ function MessageList({
|
|
|
17039
17437
|
),
|
|
17040
17438
|
children: [
|
|
17041
17439
|
isLoading && messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17042
|
-
|
|
17440
|
+
bubbles.map((msg, i) => {
|
|
17043
17441
|
const isCustomer = msg.sender_type === "customer";
|
|
17044
|
-
const prev =
|
|
17442
|
+
const prev = bubbles[i - 1];
|
|
17045
17443
|
const sameSenderAsPrev = prev?.sender_type === msg.sender_type;
|
|
17046
17444
|
const isOptimistic = msg.id.startsWith("opt_");
|
|
17445
|
+
const reactions = reactionsByTarget.get(msg.id);
|
|
17047
17446
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
17048
17447
|
!sameSenderAsPrev && i > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3" }),
|
|
17049
|
-
/* @__PURE__ */ jsxRuntime.
|
|
17448
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17050
17449
|
"div",
|
|
17051
17450
|
{
|
|
17052
17451
|
className: cn(
|
|
17053
|
-
"flex max-w-[85%]",
|
|
17452
|
+
"group relative flex max-w-[85%]",
|
|
17054
17453
|
isCustomer ? "ml-auto" : "mr-auto"
|
|
17055
17454
|
),
|
|
17056
|
-
children:
|
|
17057
|
-
"div",
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17455
|
+
children: [
|
|
17456
|
+
!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(
|
|
17457
|
+
"button",
|
|
17458
|
+
{
|
|
17459
|
+
type: "button",
|
|
17460
|
+
"aria-label": `React ${emoji}`,
|
|
17461
|
+
onClick: () => void onReact(msg, emoji),
|
|
17462
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full text-[13px] hover:bg-muted",
|
|
17463
|
+
children: emoji
|
|
17464
|
+
},
|
|
17465
|
+
emoji
|
|
17466
|
+
)) }),
|
|
17467
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17468
|
+
"div",
|
|
17469
|
+
{
|
|
17470
|
+
className: cn(
|
|
17471
|
+
"rounded-2xl px-3.5 py-2.5 text-sm leading-relaxed",
|
|
17472
|
+
isCustomer ? "bg-foreground text-background" : "border border-border bg-background text-foreground",
|
|
17473
|
+
isCustomer ? sameSenderAsPrev ? "rounded-tr" : "rounded-br" : sameSenderAsPrev ? "rounded-tl" : "rounded-bl",
|
|
17474
|
+
isOptimistic && "opacity-70"
|
|
17475
|
+
),
|
|
17476
|
+
children: [
|
|
17477
|
+
!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: [
|
|
17478
|
+
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, { className: "h-[11px] w-[11px]" }),
|
|
17479
|
+
"AI"
|
|
17480
|
+
] }),
|
|
17481
|
+
msg.reply_to && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17482
|
+
"div",
|
|
17483
|
+
{
|
|
17484
|
+
className: cn(
|
|
17485
|
+
"mb-1.5 flex gap-1.5 rounded-lg px-2 py-1 text-xs",
|
|
17486
|
+
isCustomer ? "bg-background/15" : "bg-muted"
|
|
17487
|
+
),
|
|
17488
|
+
children: [
|
|
17489
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17490
|
+
"span",
|
|
17491
|
+
{
|
|
17492
|
+
className: cn(
|
|
17493
|
+
"w-[3px] shrink-0 self-stretch rounded-full",
|
|
17494
|
+
isCustomer ? "bg-background/40" : "bg-muted-foreground/40"
|
|
17495
|
+
)
|
|
17496
|
+
}
|
|
17497
|
+
),
|
|
17498
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate opacity-80", children: msg.reply_to.preview })
|
|
17499
|
+
]
|
|
17500
|
+
}
|
|
17501
|
+
),
|
|
17502
|
+
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(
|
|
17503
|
+
(attachment, index) => attachment.mime_type?.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: attachment.url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17504
|
+
"img",
|
|
17505
|
+
{
|
|
17506
|
+
src: attachment.url,
|
|
17507
|
+
alt: attachment.filename ?? "Photo",
|
|
17508
|
+
loading: "lazy",
|
|
17509
|
+
className: "max-h-48 max-w-full rounded-lg object-cover"
|
|
17510
|
+
}
|
|
17511
|
+
) }, index) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17512
|
+
"a",
|
|
17513
|
+
{
|
|
17514
|
+
href: attachment.url,
|
|
17515
|
+
target: "_blank",
|
|
17516
|
+
rel: "noreferrer",
|
|
17517
|
+
className: "truncate text-xs underline",
|
|
17518
|
+
children: attachment.filename ?? "Attachment"
|
|
17519
|
+
},
|
|
17520
|
+
index
|
|
17521
|
+
)
|
|
17522
|
+
) }),
|
|
17523
|
+
isCustomer ? msg.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap", children: msg.content }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
17524
|
+
ChatMarkdown,
|
|
17525
|
+
{
|
|
17526
|
+
text: msg.content,
|
|
17527
|
+
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"
|
|
17528
|
+
}
|
|
17529
|
+
)
|
|
17530
|
+
]
|
|
17531
|
+
}
|
|
17532
|
+
)
|
|
17533
|
+
]
|
|
17074
17534
|
}
|
|
17075
|
-
)
|
|
17535
|
+
),
|
|
17536
|
+
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(
|
|
17537
|
+
"span",
|
|
17538
|
+
{
|
|
17539
|
+
className: "rounded-full border border-border bg-background px-1.5 py-0.5 text-xs shadow-sm",
|
|
17540
|
+
children: reaction.content
|
|
17541
|
+
},
|
|
17542
|
+
reaction.id
|
|
17543
|
+
)) }),
|
|
17544
|
+
msg.id === seenMessageId && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 text-right text-[10px] font-medium text-muted-foreground", children: "Seen" })
|
|
17076
17545
|
] }, msg.id);
|
|
17077
17546
|
}),
|
|
17078
17547
|
isSending && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mr-auto mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(TypingDots, {}) }),
|
|
17079
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17548
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17549
|
+
"div",
|
|
17550
|
+
{
|
|
17551
|
+
ref: (node) => {
|
|
17552
|
+
node?.scrollIntoView({ behavior: "smooth" });
|
|
17553
|
+
}
|
|
17554
|
+
},
|
|
17555
|
+
`${messages.length}:${isSending}`
|
|
17556
|
+
)
|
|
17080
17557
|
]
|
|
17081
17558
|
}
|
|
17082
17559
|
);
|
|
@@ -17085,10 +17562,24 @@ function ChatInput({
|
|
|
17085
17562
|
placeholder,
|
|
17086
17563
|
isSending,
|
|
17087
17564
|
onSend,
|
|
17565
|
+
onAttach,
|
|
17088
17566
|
className
|
|
17089
17567
|
}) {
|
|
17090
17568
|
const [value, setValue] = React14.useState("");
|
|
17569
|
+
const [uploading, setUploading] = React14.useState(false);
|
|
17091
17570
|
const textareaRef = React14.useRef(null);
|
|
17571
|
+
const fileRef = React14.useRef(null);
|
|
17572
|
+
const handleAttach = React14.useCallback(
|
|
17573
|
+
async (file) => {
|
|
17574
|
+
setUploading(true);
|
|
17575
|
+
try {
|
|
17576
|
+
await onAttach(file);
|
|
17577
|
+
} finally {
|
|
17578
|
+
setUploading(false);
|
|
17579
|
+
}
|
|
17580
|
+
},
|
|
17581
|
+
[onAttach]
|
|
17582
|
+
);
|
|
17092
17583
|
const handleSend = React14.useCallback(() => {
|
|
17093
17584
|
const trimmed = value.trim();
|
|
17094
17585
|
if (!trimmed || isSending) return;
|
|
@@ -17122,6 +17613,37 @@ function ChatInput({
|
|
|
17122
17613
|
className
|
|
17123
17614
|
),
|
|
17124
17615
|
children: [
|
|
17616
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17617
|
+
"input",
|
|
17618
|
+
{
|
|
17619
|
+
ref: fileRef,
|
|
17620
|
+
type: "file",
|
|
17621
|
+
accept: "image/*",
|
|
17622
|
+
className: "hidden",
|
|
17623
|
+
"aria-hidden": "true",
|
|
17624
|
+
tabIndex: -1,
|
|
17625
|
+
onChange: (e) => {
|
|
17626
|
+
const file = e.target.files?.[0];
|
|
17627
|
+
e.target.value = "";
|
|
17628
|
+
if (file) void handleAttach(file);
|
|
17629
|
+
}
|
|
17630
|
+
}
|
|
17631
|
+
),
|
|
17632
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17633
|
+
"button",
|
|
17634
|
+
{
|
|
17635
|
+
type: "button",
|
|
17636
|
+
"aria-label": "Attach a photo",
|
|
17637
|
+
disabled: uploading,
|
|
17638
|
+
onClick: () => fileRef.current?.click(),
|
|
17639
|
+
className: cn(
|
|
17640
|
+
"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px]",
|
|
17641
|
+
"bg-muted text-muted-foreground transition-colors hover:text-foreground",
|
|
17642
|
+
uploading && "animate-pulse"
|
|
17643
|
+
),
|
|
17644
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ImageIcon, { className: "h-[18px] w-[18px]" })
|
|
17645
|
+
}
|
|
17646
|
+
),
|
|
17125
17647
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17126
17648
|
"div",
|
|
17127
17649
|
{
|
|
@@ -17224,6 +17746,25 @@ function SendIcon({ className }) {
|
|
|
17224
17746
|
}
|
|
17225
17747
|
);
|
|
17226
17748
|
}
|
|
17749
|
+
function ImageIcon({ className }) {
|
|
17750
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17751
|
+
"svg",
|
|
17752
|
+
{
|
|
17753
|
+
className,
|
|
17754
|
+
viewBox: "0 0 24 24",
|
|
17755
|
+
fill: "none",
|
|
17756
|
+
stroke: "currentColor",
|
|
17757
|
+
strokeWidth: 2,
|
|
17758
|
+
strokeLinecap: "round",
|
|
17759
|
+
strokeLinejoin: "round",
|
|
17760
|
+
children: [
|
|
17761
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
17762
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
17763
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
17764
|
+
]
|
|
17765
|
+
}
|
|
17766
|
+
);
|
|
17767
|
+
}
|
|
17227
17768
|
function SparkleIcon({ className }) {
|
|
17228
17769
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
17229
17770
|
"svg",
|
|
@@ -17238,9 +17779,1838 @@ function SparkleIcon({ className }) {
|
|
|
17238
17779
|
);
|
|
17239
17780
|
}
|
|
17240
17781
|
|
|
17241
|
-
|
|
17782
|
+
// src/react/genui/catalog.ts
|
|
17783
|
+
function genUICatalog(registry) {
|
|
17784
|
+
return new Map(
|
|
17785
|
+
registry.components.map((component) => [
|
|
17786
|
+
component.name,
|
|
17787
|
+
{
|
|
17788
|
+
params: component.params.map((param) => ({
|
|
17789
|
+
name: param.name,
|
|
17790
|
+
required: param.required ?? false
|
|
17791
|
+
}))
|
|
17792
|
+
}
|
|
17793
|
+
])
|
|
17794
|
+
);
|
|
17795
|
+
}
|
|
17796
|
+
|
|
17797
|
+
// src/genui/lang/parser/ast.ts
|
|
17798
|
+
function isRuntimeExpr(node) {
|
|
17799
|
+
switch (node.k) {
|
|
17800
|
+
case "StateRef":
|
|
17801
|
+
case "RuntimeRef":
|
|
17802
|
+
case "BinOp":
|
|
17803
|
+
case "UnaryOp":
|
|
17804
|
+
case "Ternary":
|
|
17805
|
+
case "Member":
|
|
17806
|
+
case "Index":
|
|
17807
|
+
case "Assign":
|
|
17808
|
+
return true;
|
|
17809
|
+
default:
|
|
17810
|
+
return false;
|
|
17811
|
+
}
|
|
17812
|
+
}
|
|
17813
|
+
var AST_KINDS = /* @__PURE__ */ new Set([
|
|
17814
|
+
"Comp",
|
|
17815
|
+
"Ref",
|
|
17816
|
+
"StateRef",
|
|
17817
|
+
"RuntimeRef",
|
|
17818
|
+
"BinOp",
|
|
17819
|
+
"UnaryOp",
|
|
17820
|
+
"Ternary",
|
|
17821
|
+
"Member",
|
|
17822
|
+
"Index",
|
|
17823
|
+
"Assign",
|
|
17824
|
+
"Str",
|
|
17825
|
+
"Num",
|
|
17826
|
+
"Bool",
|
|
17827
|
+
"Null",
|
|
17828
|
+
"Arr",
|
|
17829
|
+
"Obj",
|
|
17830
|
+
"Ph"
|
|
17831
|
+
]);
|
|
17832
|
+
function isASTNode(value) {
|
|
17833
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
17834
|
+
return AST_KINDS.has(value.k);
|
|
17835
|
+
}
|
|
17836
|
+
function walkAST(node, visit) {
|
|
17837
|
+
const walk = (current) => {
|
|
17838
|
+
visit(current);
|
|
17839
|
+
switch (current.k) {
|
|
17840
|
+
case "Comp":
|
|
17841
|
+
current.args.forEach(walk);
|
|
17842
|
+
Object.values(current.mappedProps ?? {}).forEach(walk);
|
|
17843
|
+
break;
|
|
17844
|
+
case "Arr":
|
|
17845
|
+
current.els.forEach(walk);
|
|
17846
|
+
break;
|
|
17847
|
+
case "Obj":
|
|
17848
|
+
current.entries.forEach(([, value]) => walk(value));
|
|
17849
|
+
break;
|
|
17850
|
+
case "BinOp":
|
|
17851
|
+
walk(current.left);
|
|
17852
|
+
walk(current.right);
|
|
17853
|
+
break;
|
|
17854
|
+
case "UnaryOp":
|
|
17855
|
+
walk(current.operand);
|
|
17856
|
+
break;
|
|
17857
|
+
case "Ternary":
|
|
17858
|
+
walk(current.cond);
|
|
17859
|
+
walk(current.then);
|
|
17860
|
+
walk(current.else);
|
|
17861
|
+
break;
|
|
17862
|
+
case "Member":
|
|
17863
|
+
walk(current.obj);
|
|
17864
|
+
break;
|
|
17865
|
+
case "Index":
|
|
17866
|
+
walk(current.obj);
|
|
17867
|
+
walk(current.index);
|
|
17868
|
+
break;
|
|
17869
|
+
case "Assign":
|
|
17870
|
+
walk(current.value);
|
|
17871
|
+
break;
|
|
17872
|
+
}
|
|
17873
|
+
};
|
|
17874
|
+
walk(node);
|
|
17875
|
+
}
|
|
17876
|
+
|
|
17877
|
+
// src/genui/lang/parser/builtins.ts
|
|
17878
|
+
function resolveField(obj, path) {
|
|
17879
|
+
if (!path || obj == null) return void 0;
|
|
17880
|
+
if (!path.includes(".")) return obj[path];
|
|
17881
|
+
let cur = obj;
|
|
17882
|
+
for (const p of path.split(".")) {
|
|
17883
|
+
if (cur == null) return void 0;
|
|
17884
|
+
cur = cur[p];
|
|
17885
|
+
}
|
|
17886
|
+
return cur;
|
|
17887
|
+
}
|
|
17888
|
+
function toNumber2(val) {
|
|
17889
|
+
if (typeof val === "number") return val;
|
|
17890
|
+
if (typeof val === "string") {
|
|
17891
|
+
const n = Number(val);
|
|
17892
|
+
return isNaN(n) ? 0 : n;
|
|
17893
|
+
}
|
|
17894
|
+
if (typeof val === "boolean") return val ? 1 : 0;
|
|
17895
|
+
return 0;
|
|
17896
|
+
}
|
|
17897
|
+
var BUILTINS = {
|
|
17898
|
+
Count: {
|
|
17899
|
+
name: "Count",
|
|
17900
|
+
signature: "Count(array) \u2192 number",
|
|
17901
|
+
description: "Returns array length",
|
|
17902
|
+
fn: (arr) => Array.isArray(arr) ? arr.length : 0
|
|
17903
|
+
},
|
|
17904
|
+
First: {
|
|
17905
|
+
name: "First",
|
|
17906
|
+
signature: "First(array) \u2192 element",
|
|
17907
|
+
description: "Returns first element of array",
|
|
17908
|
+
fn: (arr) => Array.isArray(arr) ? arr[0] ?? null : null
|
|
17909
|
+
},
|
|
17910
|
+
Last: {
|
|
17911
|
+
name: "Last",
|
|
17912
|
+
signature: "Last(array) \u2192 element",
|
|
17913
|
+
description: "Returns last element of array",
|
|
17914
|
+
fn: (arr) => Array.isArray(arr) ? arr[arr.length - 1] ?? null : null
|
|
17915
|
+
},
|
|
17916
|
+
Sum: {
|
|
17917
|
+
name: "Sum",
|
|
17918
|
+
signature: "Sum(numbers[]) \u2192 number",
|
|
17919
|
+
description: "Sum of numeric array",
|
|
17920
|
+
fn: (arr) => Array.isArray(arr) ? arr.reduce((a, b) => a + toNumber2(b), 0) : 0
|
|
17921
|
+
},
|
|
17922
|
+
Avg: {
|
|
17923
|
+
name: "Avg",
|
|
17924
|
+
signature: "Avg(numbers[]) \u2192 number",
|
|
17925
|
+
description: "Average of numeric array",
|
|
17926
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((a, b) => a + toNumber2(b), 0) / arr.length : 0
|
|
17927
|
+
},
|
|
17928
|
+
Min: {
|
|
17929
|
+
name: "Min",
|
|
17930
|
+
signature: "Min(numbers[]) \u2192 number",
|
|
17931
|
+
description: "Minimum value in array",
|
|
17932
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.min(acc, toNumber2(b)), toNumber2(arr[0])) : 0
|
|
17933
|
+
},
|
|
17934
|
+
Max: {
|
|
17935
|
+
name: "Max",
|
|
17936
|
+
signature: "Max(numbers[]) \u2192 number",
|
|
17937
|
+
description: "Maximum value in array",
|
|
17938
|
+
fn: (arr) => Array.isArray(arr) && arr.length ? arr.reduce((acc, b) => Math.max(acc, toNumber2(b)), toNumber2(arr[0])) : 0
|
|
17939
|
+
},
|
|
17940
|
+
Sort: {
|
|
17941
|
+
name: "Sort",
|
|
17942
|
+
signature: "Sort(array, field, direction?) \u2192 sorted array",
|
|
17943
|
+
description: 'Sort array by field. Direction: "asc" (default) or "desc"',
|
|
17944
|
+
fn: (arr, field, dir) => {
|
|
17945
|
+
if (!Array.isArray(arr)) return arr;
|
|
17946
|
+
const f = String(field ?? "");
|
|
17947
|
+
const desc = String(dir ?? "asc") === "desc";
|
|
17948
|
+
return [...arr].sort((a, b) => {
|
|
17949
|
+
const av = f ? resolveField(a, f) : a;
|
|
17950
|
+
const bv = f ? resolveField(b, f) : b;
|
|
17951
|
+
const aIsNumeric = typeof av === "number" || typeof av === "string" && !isNaN(Number(av)) && av !== "";
|
|
17952
|
+
const bIsNumeric = typeof bv === "number" || typeof bv === "string" && !isNaN(Number(bv)) && bv !== "";
|
|
17953
|
+
if (aIsNumeric && bIsNumeric) {
|
|
17954
|
+
const diff = toNumber2(av) - toNumber2(bv);
|
|
17955
|
+
return desc ? -diff : diff;
|
|
17956
|
+
}
|
|
17957
|
+
const cmp = String(av ?? "").localeCompare(String(bv ?? ""));
|
|
17958
|
+
return desc ? -cmp : cmp;
|
|
17959
|
+
});
|
|
17960
|
+
}
|
|
17961
|
+
},
|
|
17962
|
+
Filter: {
|
|
17963
|
+
name: "Filter",
|
|
17964
|
+
signature: 'Filter(array, field, operator: "==" | "!=" | ">" | "<" | ">=" | "<=" | "contains", value) \u2192 filtered array',
|
|
17965
|
+
description: "Filter array by field value",
|
|
17966
|
+
fn: (arr, field, op, value) => {
|
|
17967
|
+
if (!Array.isArray(arr)) return [];
|
|
17968
|
+
const f = String(field ?? "");
|
|
17969
|
+
const o = String(op ?? "==");
|
|
17970
|
+
return arr.filter((item) => {
|
|
17971
|
+
const v = f ? resolveField(item, f) : item;
|
|
17972
|
+
switch (o) {
|
|
17973
|
+
case "==":
|
|
17974
|
+
return v == value;
|
|
17975
|
+
case "!=":
|
|
17976
|
+
return v != value;
|
|
17977
|
+
case ">":
|
|
17978
|
+
return toNumber2(v) > toNumber2(value);
|
|
17979
|
+
case "<":
|
|
17980
|
+
return toNumber2(v) < toNumber2(value);
|
|
17981
|
+
case ">=":
|
|
17982
|
+
return toNumber2(v) >= toNumber2(value);
|
|
17983
|
+
case "<=":
|
|
17984
|
+
return toNumber2(v) <= toNumber2(value);
|
|
17985
|
+
case "contains":
|
|
17986
|
+
return String(v ?? "").includes(String(value ?? ""));
|
|
17987
|
+
default:
|
|
17988
|
+
return false;
|
|
17989
|
+
}
|
|
17990
|
+
});
|
|
17991
|
+
}
|
|
17992
|
+
},
|
|
17993
|
+
Round: {
|
|
17994
|
+
name: "Round",
|
|
17995
|
+
signature: "Round(number, decimals?) \u2192 number",
|
|
17996
|
+
description: "Round to N decimal places (default 0)",
|
|
17997
|
+
fn: (n, decimals) => {
|
|
17998
|
+
const num = toNumber2(n);
|
|
17999
|
+
const d = decimals != null ? toNumber2(decimals) : 0;
|
|
18000
|
+
const factor = Math.pow(10, d);
|
|
18001
|
+
return Math.round(num * factor) / factor;
|
|
18002
|
+
}
|
|
18003
|
+
},
|
|
18004
|
+
Abs: {
|
|
18005
|
+
name: "Abs",
|
|
18006
|
+
signature: "Abs(number) \u2192 number",
|
|
18007
|
+
description: "Absolute value",
|
|
18008
|
+
fn: (n) => Math.abs(toNumber2(n))
|
|
18009
|
+
},
|
|
18010
|
+
Floor: {
|
|
18011
|
+
name: "Floor",
|
|
18012
|
+
signature: "Floor(number) \u2192 number",
|
|
18013
|
+
description: "Round down to nearest integer",
|
|
18014
|
+
fn: (n) => Math.floor(toNumber2(n))
|
|
18015
|
+
},
|
|
18016
|
+
Ceil: {
|
|
18017
|
+
name: "Ceil",
|
|
18018
|
+
signature: "Ceil(number) \u2192 number",
|
|
18019
|
+
description: "Round up to nearest integer",
|
|
18020
|
+
fn: (n) => Math.ceil(toNumber2(n))
|
|
18021
|
+
}
|
|
18022
|
+
};
|
|
18023
|
+
var LAZY_BUILTINS = /* @__PURE__ */ new Set(["Each"]);
|
|
18024
|
+
var ACTION_STEPS = {
|
|
18025
|
+
Run: "run",
|
|
18026
|
+
ToAssistant: "continue_conversation",
|
|
18027
|
+
OpenUrl: "open_url",
|
|
18028
|
+
Set: "set",
|
|
18029
|
+
Reset: "reset"
|
|
18030
|
+
};
|
|
18031
|
+
var ACTION_NAMES = /* @__PURE__ */ new Set(["Action", ...Object.keys(ACTION_STEPS)]);
|
|
18032
|
+
var BUILTIN_NAMES = /* @__PURE__ */ new Set([
|
|
18033
|
+
...Object.keys(BUILTINS),
|
|
18034
|
+
...LAZY_BUILTINS,
|
|
18035
|
+
...ACTION_NAMES
|
|
18036
|
+
]);
|
|
18037
|
+
function isBuiltin(name) {
|
|
18038
|
+
return BUILTIN_NAMES.has(name);
|
|
18039
|
+
}
|
|
18040
|
+
var RESERVED_CALLS = { Query: "Query", Mutation: "Mutation" };
|
|
18041
|
+
function isReservedCall(name) {
|
|
18042
|
+
return name in RESERVED_CALLS;
|
|
18043
|
+
}
|
|
18044
|
+
|
|
18045
|
+
// src/genui/lang/parser/expressions.ts
|
|
18046
|
+
var PREC_TERNARY = 1;
|
|
18047
|
+
var PREC_OR = 2;
|
|
18048
|
+
var PREC_AND = 3;
|
|
18049
|
+
var PREC_EQ = 4;
|
|
18050
|
+
var PREC_CMP = 5;
|
|
18051
|
+
var PREC_ADD = 6;
|
|
18052
|
+
var PREC_MUL = 7;
|
|
18053
|
+
var PREC_UNARY = 8;
|
|
18054
|
+
var PREC_MEMBER = 9;
|
|
18055
|
+
function parseExpression(tokens) {
|
|
18056
|
+
let pos = 0;
|
|
18057
|
+
const cur = () => tokens[pos] ?? { t: 13 /* EOF */ };
|
|
18058
|
+
const adv = () => {
|
|
18059
|
+
const tok = cur();
|
|
18060
|
+
pos++;
|
|
18061
|
+
return tok;
|
|
18062
|
+
};
|
|
18063
|
+
const eat = (kind) => {
|
|
18064
|
+
if (cur().t === kind) pos++;
|
|
18065
|
+
};
|
|
18066
|
+
function getInfixPrec(tok) {
|
|
18067
|
+
switch (tok.t) {
|
|
18068
|
+
case 34 /* Question */:
|
|
18069
|
+
return PREC_TERNARY;
|
|
18070
|
+
case 32 /* Or */:
|
|
18071
|
+
return PREC_OR;
|
|
18072
|
+
case 31 /* And */:
|
|
18073
|
+
return PREC_AND;
|
|
18074
|
+
case 25 /* EqEq */:
|
|
18075
|
+
case 26 /* NotEq */:
|
|
18076
|
+
return PREC_EQ;
|
|
18077
|
+
case 27 /* Greater */:
|
|
18078
|
+
case 28 /* Less */:
|
|
18079
|
+
case 29 /* GreaterEq */:
|
|
18080
|
+
case 30 /* LessEq */:
|
|
18081
|
+
return PREC_CMP;
|
|
18082
|
+
case 20 /* Plus */:
|
|
18083
|
+
case 21 /* Minus */:
|
|
18084
|
+
return PREC_ADD;
|
|
18085
|
+
case 22 /* Star */:
|
|
18086
|
+
case 23 /* Slash */:
|
|
18087
|
+
case 24 /* Percent */:
|
|
18088
|
+
return PREC_MUL;
|
|
18089
|
+
case 19 /* Dot */:
|
|
18090
|
+
case 3 /* LBrack */:
|
|
18091
|
+
return PREC_MEMBER;
|
|
18092
|
+
default:
|
|
18093
|
+
return 0;
|
|
18094
|
+
}
|
|
18095
|
+
}
|
|
18096
|
+
function parseExpr(minPrec = 0) {
|
|
18097
|
+
let left = parsePrefix();
|
|
18098
|
+
while (getInfixPrec(cur()) > minPrec) {
|
|
18099
|
+
left = parseInfix(left);
|
|
18100
|
+
}
|
|
18101
|
+
return left;
|
|
18102
|
+
}
|
|
18103
|
+
function parsePrefix() {
|
|
18104
|
+
const tok = cur();
|
|
18105
|
+
if (tok.t === 14 /* Str */) {
|
|
18106
|
+
adv();
|
|
18107
|
+
return { k: "Str", v: tok.v };
|
|
18108
|
+
}
|
|
18109
|
+
if (tok.t === 15 /* Num */) {
|
|
18110
|
+
adv();
|
|
18111
|
+
return { k: "Num", v: tok.v };
|
|
18112
|
+
}
|
|
18113
|
+
if (tok.t === 10 /* True */) {
|
|
18114
|
+
adv();
|
|
18115
|
+
return { k: "Bool", v: true };
|
|
18116
|
+
}
|
|
18117
|
+
if (tok.t === 11 /* False */) {
|
|
18118
|
+
adv();
|
|
18119
|
+
return { k: "Bool", v: false };
|
|
18120
|
+
}
|
|
18121
|
+
if (tok.t === 12 /* Null */) {
|
|
18122
|
+
adv();
|
|
18123
|
+
return { k: "Null" };
|
|
18124
|
+
}
|
|
18125
|
+
if (tok.t === 3 /* LBrack */) return parseArr();
|
|
18126
|
+
if (tok.t === 5 /* LBrace */) return parseObj();
|
|
18127
|
+
if (tok.t === 18 /* StateVar */) {
|
|
18128
|
+
const name = tok.v;
|
|
18129
|
+
adv();
|
|
18130
|
+
if (cur().t === 9 /* Equals */) {
|
|
18131
|
+
adv();
|
|
18132
|
+
const value = parseExpr(0);
|
|
18133
|
+
return { k: "Assign", target: name, value };
|
|
18134
|
+
}
|
|
18135
|
+
return { k: "StateRef", n: name };
|
|
18136
|
+
}
|
|
18137
|
+
if (tok.t === 17 /* Type */) {
|
|
18138
|
+
const name = tok.v;
|
|
18139
|
+
if (tokens[pos + 1]?.t === 1 /* LParen */ && (!isBuiltin(name) || name === "Action"))
|
|
18140
|
+
return parseComp();
|
|
18141
|
+
adv();
|
|
18142
|
+
return { k: "Ref", n: name };
|
|
18143
|
+
}
|
|
18144
|
+
if (tok.t === 35 /* BuiltinCall */) {
|
|
18145
|
+
if (tokens[pos + 1]?.t === 1 /* LParen */) return parseComp();
|
|
18146
|
+
adv();
|
|
18147
|
+
return { k: "Ref", n: tok.v };
|
|
18148
|
+
}
|
|
18149
|
+
if (tok.t === 16 /* Ident */) {
|
|
18150
|
+
adv();
|
|
18151
|
+
return { k: "Ref", n: tok.v };
|
|
18152
|
+
}
|
|
18153
|
+
if (tok.t === 33 /* Not */) {
|
|
18154
|
+
adv();
|
|
18155
|
+
return { k: "UnaryOp", op: "!", operand: parseExpr(PREC_UNARY) };
|
|
18156
|
+
}
|
|
18157
|
+
if (tok.t === 21 /* Minus */) {
|
|
18158
|
+
adv();
|
|
18159
|
+
return { k: "UnaryOp", op: "-", operand: parseExpr(PREC_UNARY) };
|
|
18160
|
+
}
|
|
18161
|
+
if (tok.t === 1 /* LParen */) {
|
|
18162
|
+
adv();
|
|
18163
|
+
const inner = parseExpr(0);
|
|
18164
|
+
eat(2 /* RParen */);
|
|
18165
|
+
return inner;
|
|
18166
|
+
}
|
|
18167
|
+
adv();
|
|
18168
|
+
return { k: "Null" };
|
|
18169
|
+
}
|
|
18170
|
+
function parseInfix(left) {
|
|
18171
|
+
const tok = cur();
|
|
18172
|
+
if (tok.t === 20 /* Plus */) {
|
|
18173
|
+
adv();
|
|
18174
|
+
return { k: "BinOp", op: "+", left, right: parseExpr(PREC_ADD) };
|
|
18175
|
+
}
|
|
18176
|
+
if (tok.t === 21 /* Minus */) {
|
|
18177
|
+
adv();
|
|
18178
|
+
return { k: "BinOp", op: "-", left, right: parseExpr(PREC_ADD) };
|
|
18179
|
+
}
|
|
18180
|
+
if (tok.t === 22 /* Star */) {
|
|
18181
|
+
adv();
|
|
18182
|
+
return { k: "BinOp", op: "*", left, right: parseExpr(PREC_MUL) };
|
|
18183
|
+
}
|
|
18184
|
+
if (tok.t === 23 /* Slash */) {
|
|
18185
|
+
adv();
|
|
18186
|
+
return { k: "BinOp", op: "/", left, right: parseExpr(PREC_MUL) };
|
|
18187
|
+
}
|
|
18188
|
+
if (tok.t === 24 /* Percent */) {
|
|
18189
|
+
adv();
|
|
18190
|
+
return { k: "BinOp", op: "%", left, right: parseExpr(PREC_MUL) };
|
|
18191
|
+
}
|
|
18192
|
+
if (tok.t === 25 /* EqEq */) {
|
|
18193
|
+
adv();
|
|
18194
|
+
return { k: "BinOp", op: "==", left, right: parseExpr(PREC_EQ) };
|
|
18195
|
+
}
|
|
18196
|
+
if (tok.t === 26 /* NotEq */) {
|
|
18197
|
+
adv();
|
|
18198
|
+
return { k: "BinOp", op: "!=", left, right: parseExpr(PREC_EQ) };
|
|
18199
|
+
}
|
|
18200
|
+
if (tok.t === 27 /* Greater */) {
|
|
18201
|
+
adv();
|
|
18202
|
+
return { k: "BinOp", op: ">", left, right: parseExpr(PREC_CMP) };
|
|
18203
|
+
}
|
|
18204
|
+
if (tok.t === 28 /* Less */) {
|
|
18205
|
+
adv();
|
|
18206
|
+
return { k: "BinOp", op: "<", left, right: parseExpr(PREC_CMP) };
|
|
18207
|
+
}
|
|
18208
|
+
if (tok.t === 29 /* GreaterEq */) {
|
|
18209
|
+
adv();
|
|
18210
|
+
return { k: "BinOp", op: ">=", left, right: parseExpr(PREC_CMP) };
|
|
18211
|
+
}
|
|
18212
|
+
if (tok.t === 30 /* LessEq */) {
|
|
18213
|
+
adv();
|
|
18214
|
+
return { k: "BinOp", op: "<=", left, right: parseExpr(PREC_CMP) };
|
|
18215
|
+
}
|
|
18216
|
+
if (tok.t === 31 /* And */) {
|
|
18217
|
+
adv();
|
|
18218
|
+
return { k: "BinOp", op: "&&", left, right: parseExpr(PREC_AND) };
|
|
18219
|
+
}
|
|
18220
|
+
if (tok.t === 32 /* Or */) {
|
|
18221
|
+
adv();
|
|
18222
|
+
return { k: "BinOp", op: "||", left, right: parseExpr(PREC_OR) };
|
|
18223
|
+
}
|
|
18224
|
+
if (tok.t === 34 /* Question */) {
|
|
18225
|
+
adv();
|
|
18226
|
+
const then = parseExpr(0);
|
|
18227
|
+
eat(8 /* Colon */);
|
|
18228
|
+
const els = parseExpr(0);
|
|
18229
|
+
return { k: "Ternary", cond: left, then, else: els };
|
|
18230
|
+
}
|
|
18231
|
+
if (tok.t === 19 /* Dot */) {
|
|
18232
|
+
adv();
|
|
18233
|
+
const fieldTok = cur();
|
|
18234
|
+
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(), "?");
|
|
18235
|
+
return { k: "Member", obj: left, field };
|
|
18236
|
+
}
|
|
18237
|
+
if (tok.t === 3 /* LBrack */) {
|
|
18238
|
+
adv();
|
|
18239
|
+
const index = parseExpr(0);
|
|
18240
|
+
eat(4 /* RBrack */);
|
|
18241
|
+
return { k: "Index", obj: left, index };
|
|
18242
|
+
}
|
|
18243
|
+
return left;
|
|
18244
|
+
}
|
|
18245
|
+
function parseComp() {
|
|
18246
|
+
const name = cur().v;
|
|
18247
|
+
adv();
|
|
18248
|
+
eat(1 /* LParen */);
|
|
18249
|
+
const args = [];
|
|
18250
|
+
while (cur().t !== 2 /* RParen */ && cur().t !== 13 /* EOF */) {
|
|
18251
|
+
args.push(parseExpr(0));
|
|
18252
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18253
|
+
}
|
|
18254
|
+
eat(2 /* RParen */);
|
|
18255
|
+
return { k: "Comp", name, args };
|
|
18256
|
+
}
|
|
18257
|
+
function parseArr() {
|
|
18258
|
+
adv();
|
|
18259
|
+
const els = [];
|
|
18260
|
+
while (cur().t !== 4 /* RBrack */ && cur().t !== 13 /* EOF */) {
|
|
18261
|
+
els.push(parseExpr(0));
|
|
18262
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18263
|
+
}
|
|
18264
|
+
eat(4 /* RBrack */);
|
|
18265
|
+
return { k: "Arr", els };
|
|
18266
|
+
}
|
|
18267
|
+
function parseObj() {
|
|
18268
|
+
adv();
|
|
18269
|
+
const entries = [];
|
|
18270
|
+
while (cur().t !== 6 /* RBrace */ && cur().t !== 13 /* EOF */) {
|
|
18271
|
+
const kt = cur();
|
|
18272
|
+
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(), "?");
|
|
18273
|
+
eat(8 /* Colon */);
|
|
18274
|
+
entries.push([key, parseExpr(0)]);
|
|
18275
|
+
if (cur().t === 7 /* Comma */) adv();
|
|
18276
|
+
}
|
|
18277
|
+
eat(6 /* RBrace */);
|
|
18278
|
+
return { k: "Obj", entries };
|
|
18279
|
+
}
|
|
18280
|
+
return parseExpr(0);
|
|
18281
|
+
}
|
|
18282
|
+
|
|
18283
|
+
// src/genui/lang/parser/lexer.ts
|
|
18284
|
+
function tokenize(src) {
|
|
18285
|
+
const tokens = [];
|
|
18286
|
+
let i = 0;
|
|
18287
|
+
const n = src.length;
|
|
18288
|
+
while (i < n) {
|
|
18289
|
+
while (i < n && (src[i] === " " || src[i] === " " || src[i] === "\r")) i++;
|
|
18290
|
+
if (i >= n) break;
|
|
18291
|
+
const c = src[i];
|
|
18292
|
+
if (c === "\n") {
|
|
18293
|
+
tokens.push({ t: 0 /* Newline */ });
|
|
18294
|
+
i++;
|
|
18295
|
+
continue;
|
|
18296
|
+
}
|
|
18297
|
+
if (c === "(") {
|
|
18298
|
+
tokens.push({ t: 1 /* LParen */ });
|
|
18299
|
+
i++;
|
|
18300
|
+
continue;
|
|
18301
|
+
}
|
|
18302
|
+
if (c === ")") {
|
|
18303
|
+
tokens.push({ t: 2 /* RParen */ });
|
|
18304
|
+
i++;
|
|
18305
|
+
continue;
|
|
18306
|
+
}
|
|
18307
|
+
if (c === "[") {
|
|
18308
|
+
tokens.push({ t: 3 /* LBrack */ });
|
|
18309
|
+
i++;
|
|
18310
|
+
continue;
|
|
18311
|
+
}
|
|
18312
|
+
if (c === "]") {
|
|
18313
|
+
tokens.push({ t: 4 /* RBrack */ });
|
|
18314
|
+
i++;
|
|
18315
|
+
continue;
|
|
18316
|
+
}
|
|
18317
|
+
if (c === "{") {
|
|
18318
|
+
tokens.push({ t: 5 /* LBrace */ });
|
|
18319
|
+
i++;
|
|
18320
|
+
continue;
|
|
18321
|
+
}
|
|
18322
|
+
if (c === "}") {
|
|
18323
|
+
tokens.push({ t: 6 /* RBrace */ });
|
|
18324
|
+
i++;
|
|
18325
|
+
continue;
|
|
18326
|
+
}
|
|
18327
|
+
if (c === ",") {
|
|
18328
|
+
tokens.push({ t: 7 /* Comma */ });
|
|
18329
|
+
i++;
|
|
18330
|
+
continue;
|
|
18331
|
+
}
|
|
18332
|
+
if (c === ":") {
|
|
18333
|
+
tokens.push({ t: 8 /* Colon */ });
|
|
18334
|
+
i++;
|
|
18335
|
+
continue;
|
|
18336
|
+
}
|
|
18337
|
+
if (c === "=") {
|
|
18338
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18339
|
+
tokens.push({ t: 25 /* EqEq */ });
|
|
18340
|
+
i += 2;
|
|
18341
|
+
} else {
|
|
18342
|
+
tokens.push({ t: 9 /* Equals */ });
|
|
18343
|
+
i++;
|
|
18344
|
+
}
|
|
18345
|
+
continue;
|
|
18346
|
+
}
|
|
18347
|
+
if (c === "!") {
|
|
18348
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18349
|
+
tokens.push({ t: 26 /* NotEq */ });
|
|
18350
|
+
i += 2;
|
|
18351
|
+
} else {
|
|
18352
|
+
tokens.push({ t: 33 /* Not */ });
|
|
18353
|
+
i++;
|
|
18354
|
+
}
|
|
18355
|
+
continue;
|
|
18356
|
+
}
|
|
18357
|
+
if (c === ">") {
|
|
18358
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18359
|
+
tokens.push({ t: 29 /* GreaterEq */ });
|
|
18360
|
+
i += 2;
|
|
18361
|
+
} else {
|
|
18362
|
+
tokens.push({ t: 27 /* Greater */ });
|
|
18363
|
+
i++;
|
|
18364
|
+
}
|
|
18365
|
+
continue;
|
|
18366
|
+
}
|
|
18367
|
+
if (c === "<") {
|
|
18368
|
+
if (i + 1 < n && src[i + 1] === "=") {
|
|
18369
|
+
tokens.push({ t: 30 /* LessEq */ });
|
|
18370
|
+
i += 2;
|
|
18371
|
+
} else {
|
|
18372
|
+
tokens.push({ t: 28 /* Less */ });
|
|
18373
|
+
i++;
|
|
18374
|
+
}
|
|
18375
|
+
continue;
|
|
18376
|
+
}
|
|
18377
|
+
if (c === "&") {
|
|
18378
|
+
if (i + 1 < n && src[i + 1] === "&") {
|
|
18379
|
+
tokens.push({ t: 31 /* And */ });
|
|
18380
|
+
i += 2;
|
|
18381
|
+
} else {
|
|
18382
|
+
tokens.push({ t: 31 /* And */ });
|
|
18383
|
+
i++;
|
|
18384
|
+
}
|
|
18385
|
+
continue;
|
|
18386
|
+
}
|
|
18387
|
+
if (c === "|") {
|
|
18388
|
+
if (i + 1 < n && src[i + 1] === "|") {
|
|
18389
|
+
tokens.push({ t: 32 /* Or */ });
|
|
18390
|
+
i += 2;
|
|
18391
|
+
} else {
|
|
18392
|
+
tokens.push({ t: 32 /* Or */ });
|
|
18393
|
+
i++;
|
|
18394
|
+
}
|
|
18395
|
+
continue;
|
|
18396
|
+
}
|
|
18397
|
+
if (c === ".") {
|
|
18398
|
+
tokens.push({ t: 19 /* Dot */ });
|
|
18399
|
+
i++;
|
|
18400
|
+
continue;
|
|
18401
|
+
}
|
|
18402
|
+
if (c === "?") {
|
|
18403
|
+
tokens.push({ t: 34 /* Question */ });
|
|
18404
|
+
i++;
|
|
18405
|
+
continue;
|
|
18406
|
+
}
|
|
18407
|
+
if (c === "+") {
|
|
18408
|
+
tokens.push({ t: 20 /* Plus */ });
|
|
18409
|
+
i++;
|
|
18410
|
+
continue;
|
|
18411
|
+
}
|
|
18412
|
+
if (c === "*") {
|
|
18413
|
+
tokens.push({ t: 22 /* Star */ });
|
|
18414
|
+
i++;
|
|
18415
|
+
continue;
|
|
18416
|
+
}
|
|
18417
|
+
if (c === "/") {
|
|
18418
|
+
tokens.push({ t: 23 /* Slash */ });
|
|
18419
|
+
i++;
|
|
18420
|
+
continue;
|
|
18421
|
+
}
|
|
18422
|
+
if (c === "%") {
|
|
18423
|
+
tokens.push({ t: 24 /* Percent */ });
|
|
18424
|
+
i++;
|
|
18425
|
+
continue;
|
|
18426
|
+
}
|
|
18427
|
+
if (c === '"') {
|
|
18428
|
+
const start = i;
|
|
18429
|
+
i++;
|
|
18430
|
+
let isClosed = false;
|
|
18431
|
+
while (i < n) {
|
|
18432
|
+
if (src[i] === "\\") {
|
|
18433
|
+
i += 2;
|
|
18434
|
+
} else if (src[i] === '"') {
|
|
18435
|
+
i++;
|
|
18436
|
+
isClosed = true;
|
|
18437
|
+
break;
|
|
18438
|
+
} else {
|
|
18439
|
+
i++;
|
|
18440
|
+
}
|
|
18441
|
+
}
|
|
18442
|
+
const rawString = src.slice(start, i);
|
|
18443
|
+
try {
|
|
18444
|
+
const validJsonString = isClosed ? rawString : rawString + '"';
|
|
18445
|
+
tokens.push({ t: 14 /* Str */, v: JSON.parse(validJsonString) });
|
|
18446
|
+
} catch {
|
|
18447
|
+
const stripped = rawString.replace(/^"|"$/g, "");
|
|
18448
|
+
tokens.push({ t: 14 /* Str */, v: stripped });
|
|
18449
|
+
}
|
|
18450
|
+
continue;
|
|
18451
|
+
}
|
|
18452
|
+
if (c === "'") {
|
|
18453
|
+
i++;
|
|
18454
|
+
let result = "";
|
|
18455
|
+
while (i < n) {
|
|
18456
|
+
if (src[i] === "\\") {
|
|
18457
|
+
i++;
|
|
18458
|
+
if (i < n) {
|
|
18459
|
+
const esc = src[i];
|
|
18460
|
+
if (esc === "'") result += "'";
|
|
18461
|
+
else if (esc === "\\") result += "\\";
|
|
18462
|
+
else if (esc === "n") result += "\n";
|
|
18463
|
+
else if (esc === "t") result += " ";
|
|
18464
|
+
else result += esc;
|
|
18465
|
+
i++;
|
|
18466
|
+
}
|
|
18467
|
+
} else if (src[i] === "'") {
|
|
18468
|
+
i++;
|
|
18469
|
+
break;
|
|
18470
|
+
} else {
|
|
18471
|
+
result += src[i];
|
|
18472
|
+
i++;
|
|
18473
|
+
}
|
|
18474
|
+
}
|
|
18475
|
+
tokens.push({ t: 14 /* Str */, v: result });
|
|
18476
|
+
continue;
|
|
18477
|
+
}
|
|
18478
|
+
if (c === "-") {
|
|
18479
|
+
const prev = tokens.length > 0 ? tokens[tokens.length - 1] : null;
|
|
18480
|
+
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 */);
|
|
18481
|
+
if (!afterValue && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") ; else {
|
|
18482
|
+
tokens.push({ t: 21 /* Minus */ });
|
|
18483
|
+
i++;
|
|
18484
|
+
continue;
|
|
18485
|
+
}
|
|
18486
|
+
}
|
|
18487
|
+
const isDigit = c >= "0" && c <= "9";
|
|
18488
|
+
const isNegDigit = c === "-" && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9";
|
|
18489
|
+
if (isDigit || isNegDigit) {
|
|
18490
|
+
const start = i;
|
|
18491
|
+
if (src[i] === "-") i++;
|
|
18492
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18493
|
+
if (i < n && src[i] === "." && i + 1 < n && src[i + 1] >= "0" && src[i + 1] <= "9") {
|
|
18494
|
+
i++;
|
|
18495
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18496
|
+
}
|
|
18497
|
+
if (i < n && (src[i] === "e" || src[i] === "E")) {
|
|
18498
|
+
i++;
|
|
18499
|
+
if (i < n && (src[i] === "+" || src[i] === "-")) i++;
|
|
18500
|
+
while (i < n && src[i] >= "0" && src[i] <= "9") i++;
|
|
18501
|
+
}
|
|
18502
|
+
tokens.push({ t: 15 /* Num */, v: +src.slice(start, i) });
|
|
18503
|
+
continue;
|
|
18504
|
+
}
|
|
18505
|
+
if (c === "$" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
18506
|
+
const start = i;
|
|
18507
|
+
i++;
|
|
18508
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18509
|
+
i++;
|
|
18510
|
+
tokens.push({ t: 18 /* StateVar */, v: src.slice(start, i) });
|
|
18511
|
+
continue;
|
|
18512
|
+
}
|
|
18513
|
+
const isAlpha = c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
|
|
18514
|
+
if (isAlpha) {
|
|
18515
|
+
const start = i;
|
|
18516
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18517
|
+
i++;
|
|
18518
|
+
const word = src.slice(start, i);
|
|
18519
|
+
if (word === "true") {
|
|
18520
|
+
tokens.push({ t: 10 /* True */ });
|
|
18521
|
+
continue;
|
|
18522
|
+
}
|
|
18523
|
+
if (word === "false") {
|
|
18524
|
+
tokens.push({ t: 11 /* False */ });
|
|
18525
|
+
continue;
|
|
18526
|
+
}
|
|
18527
|
+
if (word === "null") {
|
|
18528
|
+
tokens.push({ t: 12 /* Null */ });
|
|
18529
|
+
continue;
|
|
18530
|
+
}
|
|
18531
|
+
const kind = c >= "A" && c <= "Z" ? 17 /* Type */ : 16 /* Ident */;
|
|
18532
|
+
tokens.push({ t: kind, v: word });
|
|
18533
|
+
continue;
|
|
18534
|
+
}
|
|
18535
|
+
if (c === "@" && i + 1 < n && (src[i + 1] >= "a" && src[i + 1] <= "z" || src[i + 1] >= "A" && src[i + 1] <= "Z" || src[i + 1] === "_")) {
|
|
18536
|
+
i++;
|
|
18537
|
+
const start = i;
|
|
18538
|
+
while (i < n && (src[i] >= "a" && src[i] <= "z" || src[i] >= "A" && src[i] <= "Z" || src[i] >= "0" && src[i] <= "9" || src[i] === "_"))
|
|
18539
|
+
i++;
|
|
18540
|
+
tokens.push({ t: 35 /* BuiltinCall */, v: src.slice(start, i) });
|
|
18541
|
+
continue;
|
|
18542
|
+
}
|
|
18543
|
+
i++;
|
|
18544
|
+
}
|
|
18545
|
+
tokens.push({ t: 13 /* EOF */ });
|
|
18546
|
+
return tokens;
|
|
18547
|
+
}
|
|
18548
|
+
|
|
18549
|
+
// src/genui/lang/parser/types.ts
|
|
18550
|
+
function isElementNode(value) {
|
|
18551
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
18552
|
+
const node = value;
|
|
18553
|
+
return node.type === "element" && typeof node.typeName === "string" && typeof node.props === "object" && node.props !== null && typeof node.partial === "boolean";
|
|
18554
|
+
}
|
|
18555
|
+
|
|
18556
|
+
// src/genui/lang/parser/materialize.ts
|
|
18557
|
+
function containsDynamicValue(v) {
|
|
18558
|
+
if (v == null || typeof v !== "object") return false;
|
|
18559
|
+
if (isASTNode(v)) return true;
|
|
18560
|
+
if (Array.isArray(v)) return v.some(containsDynamicValue);
|
|
18561
|
+
if (isElementNode(v)) {
|
|
18562
|
+
return Object.values(v.props).some(containsDynamicValue);
|
|
18563
|
+
}
|
|
18564
|
+
const obj = v;
|
|
18565
|
+
return Object.values(obj).some(containsDynamicValue);
|
|
18566
|
+
}
|
|
18567
|
+
function resolveRef(name, ctx, mode) {
|
|
18568
|
+
if (ctx.visited.has(name)) {
|
|
18569
|
+
ctx.unres.push(name);
|
|
18570
|
+
return mode === "expr" ? { k: "Ph", n: name } : null;
|
|
18571
|
+
}
|
|
18572
|
+
if (!ctx.syms.has(name)) {
|
|
18573
|
+
ctx.unres.push(name);
|
|
18574
|
+
return mode === "expr" ? { k: "Ph", n: name } : null;
|
|
18575
|
+
}
|
|
18576
|
+
const target = ctx.syms.get(name);
|
|
18577
|
+
ctx.unreached?.delete(name);
|
|
18578
|
+
if (target.k === "Comp" && isReservedCall(target.name)) {
|
|
18579
|
+
const refType = target.name === RESERVED_CALLS.Mutation ? "mutation" : "query";
|
|
18580
|
+
return { k: "RuntimeRef", n: name, refType };
|
|
18581
|
+
}
|
|
18582
|
+
ctx.visited.add(name);
|
|
18583
|
+
const prevStatementId = ctx.currentStatementId;
|
|
18584
|
+
ctx.currentStatementId = name;
|
|
18585
|
+
try {
|
|
18586
|
+
const result = mode === "value" ? materializeValue(target, ctx) : materializeExpr(target, ctx);
|
|
18587
|
+
if (mode === "value" && isElementNode(result)) {
|
|
18588
|
+
result.statementId = name;
|
|
18589
|
+
}
|
|
18590
|
+
return result;
|
|
18591
|
+
} finally {
|
|
18592
|
+
ctx.currentStatementId = prevStatementId;
|
|
18593
|
+
ctx.visited.delete(name);
|
|
18594
|
+
}
|
|
18595
|
+
}
|
|
18596
|
+
function materializeLazyBuiltin(node, ctx, scopedRefs) {
|
|
18597
|
+
if (!LAZY_BUILTINS.has(node.name) || node.args.length < 3) return null;
|
|
18598
|
+
const varArg = node.args[1];
|
|
18599
|
+
const varName = varArg.k === "Ref" ? varArg.n : varArg.k === "Str" ? varArg.v : null;
|
|
18600
|
+
if (!varName) return null;
|
|
18601
|
+
const nextScopedRefs = new Set(scopedRefs);
|
|
18602
|
+
nextScopedRefs.add(varName);
|
|
18603
|
+
const recursedArgs = node.args.map(
|
|
18604
|
+
(a, i) => i === 1 ? a : materializeExprInternal(a, ctx, nextScopedRefs)
|
|
18605
|
+
);
|
|
18606
|
+
return { ...node, args: recursedArgs };
|
|
18607
|
+
}
|
|
18608
|
+
function materializeExprInternal(node, ctx, scopedRefs) {
|
|
18609
|
+
switch (node.k) {
|
|
18610
|
+
case "Ref":
|
|
18611
|
+
return scopedRefs.has(node.n) ? node : resolveRef(node.n, ctx, "expr");
|
|
18612
|
+
case "Ph":
|
|
18613
|
+
return node;
|
|
18614
|
+
case "Comp": {
|
|
18615
|
+
const lazy2 = materializeLazyBuiltin(node, ctx, scopedRefs);
|
|
18616
|
+
if (lazy2) return lazy2;
|
|
18617
|
+
const recursedArgs = node.args.map((a) => materializeExprInternal(a, ctx, scopedRefs));
|
|
18618
|
+
if (isBuiltin(node.name) || isReservedCall(node.name)) {
|
|
18619
|
+
return { ...node, args: recursedArgs };
|
|
18620
|
+
}
|
|
18621
|
+
const def = ctx.cat?.get(node.name);
|
|
18622
|
+
if (def) {
|
|
18623
|
+
const mappedProps = {};
|
|
18624
|
+
for (let i = 0; i < def.params.length && i < recursedArgs.length; i++) {
|
|
18625
|
+
mappedProps[def.params[i].name] = recursedArgs[i];
|
|
18626
|
+
}
|
|
18627
|
+
return { ...node, args: recursedArgs, mappedProps };
|
|
18628
|
+
}
|
|
18629
|
+
ctx.errors.push({
|
|
18630
|
+
code: "unknown-component",
|
|
18631
|
+
component: node.name,
|
|
18632
|
+
path: "",
|
|
18633
|
+
message: `Unknown component "${node.name}" \u2014 not found in catalog or builtins`,
|
|
18634
|
+
statementId: ctx.currentStatementId
|
|
18635
|
+
});
|
|
18636
|
+
return { ...node, args: recursedArgs };
|
|
18637
|
+
}
|
|
18638
|
+
case "Arr":
|
|
18639
|
+
return { ...node, els: node.els.map((e) => materializeExprInternal(e, ctx, scopedRefs)) };
|
|
18640
|
+
case "Obj":
|
|
18641
|
+
return {
|
|
18642
|
+
...node,
|
|
18643
|
+
entries: node.entries.map(
|
|
18644
|
+
([k, v]) => [k, materializeExprInternal(v, ctx, scopedRefs)]
|
|
18645
|
+
)
|
|
18646
|
+
};
|
|
18647
|
+
case "BinOp":
|
|
18648
|
+
return {
|
|
18649
|
+
...node,
|
|
18650
|
+
left: materializeExprInternal(node.left, ctx, scopedRefs),
|
|
18651
|
+
right: materializeExprInternal(node.right, ctx, scopedRefs)
|
|
18652
|
+
};
|
|
18653
|
+
case "UnaryOp":
|
|
18654
|
+
return { ...node, operand: materializeExprInternal(node.operand, ctx, scopedRefs) };
|
|
18655
|
+
case "Ternary":
|
|
18656
|
+
return {
|
|
18657
|
+
...node,
|
|
18658
|
+
cond: materializeExprInternal(node.cond, ctx, scopedRefs),
|
|
18659
|
+
then: materializeExprInternal(node.then, ctx, scopedRefs),
|
|
18660
|
+
else: materializeExprInternal(node.else, ctx, scopedRefs)
|
|
18661
|
+
};
|
|
18662
|
+
case "Member":
|
|
18663
|
+
return { ...node, obj: materializeExprInternal(node.obj, ctx, scopedRefs) };
|
|
18664
|
+
case "Index":
|
|
18665
|
+
return {
|
|
18666
|
+
...node,
|
|
18667
|
+
obj: materializeExprInternal(node.obj, ctx, scopedRefs),
|
|
18668
|
+
index: materializeExprInternal(node.index, ctx, scopedRefs)
|
|
18669
|
+
};
|
|
18670
|
+
case "Assign":
|
|
18671
|
+
return { ...node, value: materializeExprInternal(node.value, ctx, scopedRefs) };
|
|
18672
|
+
// Literals, StateRef, RuntimeRef — pass through unchanged
|
|
18673
|
+
default:
|
|
18674
|
+
return node;
|
|
18675
|
+
}
|
|
18676
|
+
}
|
|
18677
|
+
function materializeExpr(node, ctx) {
|
|
18678
|
+
return materializeExprInternal(node, ctx, /* @__PURE__ */ new Set());
|
|
18679
|
+
}
|
|
18680
|
+
function materializeValue(node, ctx) {
|
|
18681
|
+
switch (node.k) {
|
|
18682
|
+
// ── Ref resolution ───────────────────────────────────────────────────
|
|
18683
|
+
case "Ref":
|
|
18684
|
+
return resolveRef(node.n, ctx, "value");
|
|
18685
|
+
// ── Literals → plain values ──────────────────────────────────────────
|
|
18686
|
+
case "Str":
|
|
18687
|
+
return node.v;
|
|
18688
|
+
case "Num":
|
|
18689
|
+
return node.v;
|
|
18690
|
+
case "Bool":
|
|
18691
|
+
return node.v;
|
|
18692
|
+
case "Null":
|
|
18693
|
+
return null;
|
|
18694
|
+
case "Ph":
|
|
18695
|
+
return null;
|
|
18696
|
+
// ── Collections ──────────────────────────────────────────────────────
|
|
18697
|
+
case "Arr": {
|
|
18698
|
+
const items = [];
|
|
18699
|
+
for (const e of node.els) {
|
|
18700
|
+
if (e.k === "Ph") continue;
|
|
18701
|
+
const value = materializeValue(e, ctx);
|
|
18702
|
+
if (value === null && (e.k === "Comp" || e.k === "Ref")) continue;
|
|
18703
|
+
items.push(value);
|
|
18704
|
+
}
|
|
18705
|
+
return items;
|
|
18706
|
+
}
|
|
18707
|
+
case "Obj": {
|
|
18708
|
+
const o = {};
|
|
18709
|
+
for (const [k, v] of node.entries) o[k] = materializeValue(v, ctx);
|
|
18710
|
+
return o;
|
|
18711
|
+
}
|
|
18712
|
+
// ── Component nodes ──────────────────────────────────────────────────
|
|
18713
|
+
case "Comp": {
|
|
18714
|
+
const { name, args } = node;
|
|
18715
|
+
if (isBuiltin(name)) {
|
|
18716
|
+
const lazy2 = materializeLazyBuiltin(node, ctx, /* @__PURE__ */ new Set());
|
|
18717
|
+
if (lazy2) return lazy2;
|
|
18718
|
+
return { ...node, args: args.map((a) => materializeExpr(a, ctx)) };
|
|
18719
|
+
}
|
|
18720
|
+
if (isReservedCall(name)) {
|
|
18721
|
+
ctx.errors.push({
|
|
18722
|
+
code: "inline-reserved",
|
|
18723
|
+
component: name,
|
|
18724
|
+
path: "",
|
|
18725
|
+
message: `${name}() must be declared as a top-level statement, not used inline as a value`,
|
|
18726
|
+
statementId: ctx.currentStatementId
|
|
18727
|
+
});
|
|
18728
|
+
return null;
|
|
18729
|
+
}
|
|
18730
|
+
const def = ctx.cat?.get(name);
|
|
18731
|
+
const props = {};
|
|
18732
|
+
if (def) {
|
|
18733
|
+
for (let i = 0; i < def.params.length && i < args.length; i++) {
|
|
18734
|
+
props[def.params[i].name] = materializeValue(args[i], ctx);
|
|
18735
|
+
}
|
|
18736
|
+
if (args.length > def.params.length) {
|
|
18737
|
+
const excessCount = args.length - def.params.length;
|
|
18738
|
+
ctx.errors.push({
|
|
18739
|
+
code: "excess-args",
|
|
18740
|
+
component: name,
|
|
18741
|
+
path: "",
|
|
18742
|
+
message: `${name} takes ${def.params.length} arg(s), got ${args.length} (${excessCount} excess dropped)`,
|
|
18743
|
+
statementId: ctx.currentStatementId
|
|
18744
|
+
});
|
|
18745
|
+
}
|
|
18746
|
+
const missingRequired = def.params.filter(
|
|
18747
|
+
(p) => p.required && (!(p.name in props) || props[p.name] === null)
|
|
18748
|
+
);
|
|
18749
|
+
if (missingRequired.length) {
|
|
18750
|
+
const stillInvalid = missingRequired.filter((p) => {
|
|
18751
|
+
if (p.defaultValue !== void 0) {
|
|
18752
|
+
props[p.name] = p.defaultValue;
|
|
18753
|
+
return false;
|
|
18754
|
+
}
|
|
18755
|
+
return true;
|
|
18756
|
+
});
|
|
18757
|
+
if (stillInvalid.length) {
|
|
18758
|
+
for (const p of stillInvalid) {
|
|
18759
|
+
const isNull = p.name in props;
|
|
18760
|
+
ctx.errors.push({
|
|
18761
|
+
code: isNull ? "null-required" : "missing-required",
|
|
18762
|
+
component: name,
|
|
18763
|
+
path: `/${p.name}`,
|
|
18764
|
+
message: isNull ? `required field "${p.name}" cannot be null` : `missing required field "${p.name}"`,
|
|
18765
|
+
statementId: ctx.currentStatementId
|
|
18766
|
+
});
|
|
18767
|
+
}
|
|
18768
|
+
return null;
|
|
18769
|
+
}
|
|
18770
|
+
}
|
|
18771
|
+
} else if (!isBuiltin(name) && !isReservedCall(name)) {
|
|
18772
|
+
ctx.errors.push({
|
|
18773
|
+
code: "unknown-component",
|
|
18774
|
+
component: name,
|
|
18775
|
+
path: "",
|
|
18776
|
+
message: `Unknown component "${name}" \u2014 not found in catalog or builtins`,
|
|
18777
|
+
statementId: ctx.currentStatementId
|
|
18778
|
+
});
|
|
18779
|
+
return null;
|
|
18780
|
+
}
|
|
18781
|
+
const hasDynamicProps = Object.values(props).some((v) => containsDynamicValue(v));
|
|
18782
|
+
return { type: "element", typeName: name, props, partial: ctx.partial, hasDynamicProps };
|
|
18783
|
+
}
|
|
18784
|
+
// ── Runtime expression nodes → preserve as ASTNode, normalize children ─
|
|
18785
|
+
default: {
|
|
18786
|
+
if (isRuntimeExpr(node)) {
|
|
18787
|
+
return materializeExpr(node, ctx);
|
|
18788
|
+
}
|
|
18789
|
+
return node;
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
}
|
|
18793
|
+
|
|
18794
|
+
// src/genui/lang/parser/statements.ts
|
|
18795
|
+
function autoClose(input) {
|
|
18796
|
+
const stack = [];
|
|
18797
|
+
let inStr = false;
|
|
18798
|
+
let esc = false;
|
|
18799
|
+
for (let i = 0; i < input.length; i++) {
|
|
18800
|
+
const c = input[i];
|
|
18801
|
+
if (esc) {
|
|
18802
|
+
esc = false;
|
|
18803
|
+
continue;
|
|
18804
|
+
}
|
|
18805
|
+
if (c === "\\" && inStr) {
|
|
18806
|
+
esc = true;
|
|
18807
|
+
continue;
|
|
18808
|
+
}
|
|
18809
|
+
if (inStr) {
|
|
18810
|
+
if (c === inStr) inStr = false;
|
|
18811
|
+
continue;
|
|
18812
|
+
}
|
|
18813
|
+
if (c === '"' || c === "'") {
|
|
18814
|
+
inStr = c;
|
|
18815
|
+
continue;
|
|
18816
|
+
}
|
|
18817
|
+
if (c === "(" || c === "[" || c === "{") stack.push(c);
|
|
18818
|
+
else if (c === ")" && stack[stack.length - 1] === "(") stack.pop();
|
|
18819
|
+
else if (c === "]" && stack[stack.length - 1] === "[") stack.pop();
|
|
18820
|
+
else if (c === "}" && stack[stack.length - 1] === "{") stack.pop();
|
|
18821
|
+
}
|
|
18822
|
+
const wasIncomplete = !!inStr || stack.length > 0;
|
|
18823
|
+
if (!wasIncomplete) return { text: input, wasIncomplete: false };
|
|
18824
|
+
let out = input;
|
|
18825
|
+
if (inStr) {
|
|
18826
|
+
if (esc) out += "\\";
|
|
18827
|
+
out += inStr;
|
|
18828
|
+
}
|
|
18829
|
+
for (let j = stack.length - 1; j >= 0; j--)
|
|
18830
|
+
out += stack[j] === "(" ? ")" : stack[j] === "[" ? "]" : "}";
|
|
18831
|
+
return { text: out, wasIncomplete: true };
|
|
18832
|
+
}
|
|
18833
|
+
function split(tokens) {
|
|
18834
|
+
const stmts = [];
|
|
18835
|
+
let pos = 0;
|
|
18836
|
+
while (pos < tokens.length) {
|
|
18837
|
+
while (pos < tokens.length && tokens[pos].t === 0 /* Newline */) pos++;
|
|
18838
|
+
if (pos >= tokens.length || tokens[pos].t === 13 /* EOF */) break;
|
|
18839
|
+
const tok = tokens[pos];
|
|
18840
|
+
if (tok.t !== 16 /* Ident */ && tok.t !== 17 /* Type */ && tok.t !== 18 /* StateVar */) {
|
|
18841
|
+
while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
|
|
18842
|
+
continue;
|
|
18843
|
+
}
|
|
18844
|
+
const id = tok.v;
|
|
18845
|
+
const idTokenType = tok.t;
|
|
18846
|
+
pos++;
|
|
18847
|
+
if (pos >= tokens.length || tokens[pos].t !== 9 /* Equals */) {
|
|
18848
|
+
while (pos < tokens.length && tokens[pos].t !== 0 /* Newline */ && tokens[pos].t !== 13 /* EOF */) pos++;
|
|
18849
|
+
continue;
|
|
18850
|
+
}
|
|
18851
|
+
pos++;
|
|
18852
|
+
const expr = [];
|
|
18853
|
+
let depth = 0;
|
|
18854
|
+
let ternaryDepth = 0;
|
|
18855
|
+
while (pos < tokens.length && tokens[pos].t !== 13 /* EOF */) {
|
|
18856
|
+
const tt = tokens[pos].t;
|
|
18857
|
+
if (tt === 0 /* Newline */ && depth <= 0 && ternaryDepth <= 0) {
|
|
18858
|
+
let peek = pos + 1;
|
|
18859
|
+
while (peek < tokens.length && tokens[peek].t === 0 /* Newline */) peek++;
|
|
18860
|
+
const nextT = peek < tokens.length ? tokens[peek].t : 13 /* EOF */;
|
|
18861
|
+
if (nextT === 34 /* Question */ || nextT === 8 /* Colon */ && ternaryDepth > 0) {
|
|
18862
|
+
pos++;
|
|
18863
|
+
continue;
|
|
18864
|
+
}
|
|
18865
|
+
break;
|
|
18866
|
+
}
|
|
18867
|
+
if (tt === 0 /* Newline */) {
|
|
18868
|
+
pos++;
|
|
18869
|
+
continue;
|
|
18870
|
+
}
|
|
18871
|
+
if (tt === 1 /* LParen */ || tt === 3 /* LBrack */ || tt === 5 /* LBrace */) depth++;
|
|
18872
|
+
else if ((tt === 2 /* RParen */ || tt === 4 /* RBrack */ || tt === 6 /* RBrace */) && depth > 0) depth--;
|
|
18873
|
+
else if (tt === 34 /* Question */ && depth === 0) ternaryDepth++;
|
|
18874
|
+
else if (tt === 8 /* Colon */ && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
18875
|
+
expr.push(tokens[pos++]);
|
|
18876
|
+
}
|
|
18877
|
+
if (expr.length) stmts.push({ id, idTokenType, tokens: expr });
|
|
18878
|
+
}
|
|
18879
|
+
return stmts;
|
|
18880
|
+
}
|
|
18881
|
+
|
|
18882
|
+
// src/genui/lang/parser/parser.ts
|
|
18883
|
+
function emptyResult(incomplete = true) {
|
|
18884
|
+
return {
|
|
18885
|
+
root: null,
|
|
18886
|
+
meta: {
|
|
18887
|
+
incomplete,
|
|
18888
|
+
unresolved: [],
|
|
18889
|
+
orphaned: [],
|
|
18890
|
+
statementCount: 0,
|
|
18891
|
+
errors: []
|
|
18892
|
+
},
|
|
18893
|
+
stateDeclarations: {},
|
|
18894
|
+
queryStatements: [],
|
|
18895
|
+
mutationStatements: []
|
|
18896
|
+
};
|
|
18897
|
+
}
|
|
18898
|
+
function collectQueryDeps(node) {
|
|
18899
|
+
if (!isASTNode(node)) return [];
|
|
18900
|
+
const refs = /* @__PURE__ */ new Set();
|
|
18901
|
+
walkAST(node, (current) => {
|
|
18902
|
+
if (current.k === "StateRef") refs.add(current.n);
|
|
18903
|
+
});
|
|
18904
|
+
return [...refs];
|
|
18905
|
+
}
|
|
18906
|
+
function classifyStatement(raw, expr) {
|
|
18907
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Query) {
|
|
18908
|
+
const deps = collectQueryDeps(expr.args[1]);
|
|
18909
|
+
return {
|
|
18910
|
+
kind: "query",
|
|
18911
|
+
id: raw.id,
|
|
18912
|
+
call: { callee: RESERVED_CALLS.Query, args: expr.args },
|
|
18913
|
+
expr,
|
|
18914
|
+
deps: deps.length > 0 ? deps : void 0
|
|
18915
|
+
};
|
|
18916
|
+
}
|
|
18917
|
+
if (expr.k === "Comp" && expr.name === RESERVED_CALLS.Mutation) {
|
|
18918
|
+
return {
|
|
18919
|
+
kind: "mutation",
|
|
18920
|
+
id: raw.id,
|
|
18921
|
+
call: { callee: RESERVED_CALLS.Mutation, args: expr.args },
|
|
18922
|
+
expr
|
|
18923
|
+
};
|
|
18924
|
+
}
|
|
18925
|
+
if (raw.idTokenType === 18 /* StateVar */) {
|
|
18926
|
+
return { kind: "state", id: raw.id, init: expr };
|
|
18927
|
+
}
|
|
18928
|
+
return { kind: "value", id: raw.id, expr };
|
|
18929
|
+
}
|
|
18930
|
+
function extractStatements(stmts, ctx) {
|
|
18931
|
+
const stateDeclarations = {};
|
|
18932
|
+
const queryStatements = [];
|
|
18933
|
+
const mutationStatements = [];
|
|
18934
|
+
for (const stmt of stmts) {
|
|
18935
|
+
switch (stmt.kind) {
|
|
18936
|
+
case "state":
|
|
18937
|
+
stateDeclarations[stmt.id] = materializeValue(stmt.init, ctx);
|
|
18938
|
+
break;
|
|
18939
|
+
case "query":
|
|
18940
|
+
queryStatements.push({
|
|
18941
|
+
statementId: stmt.id,
|
|
18942
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
18943
|
+
argsAST: stmt.call.args[1] ?? null,
|
|
18944
|
+
defaultsAST: stmt.call.args[2] ?? null,
|
|
18945
|
+
refreshAST: stmt.call.args[3] ?? null,
|
|
18946
|
+
deps: stmt.deps,
|
|
18947
|
+
complete: true
|
|
18948
|
+
});
|
|
18949
|
+
break;
|
|
18950
|
+
case "mutation":
|
|
18951
|
+
mutationStatements.push({
|
|
18952
|
+
statementId: stmt.id,
|
|
18953
|
+
toolAST: stmt.call.args[0] ?? null,
|
|
18954
|
+
argsAST: stmt.call.args[1] ?? null
|
|
18955
|
+
});
|
|
18956
|
+
break;
|
|
18957
|
+
}
|
|
18958
|
+
}
|
|
18959
|
+
for (const stmt of stmts) {
|
|
18960
|
+
const nodes = stmt.kind === "state" ? [stmt.init] : stmt.kind === "value" ? [stmt.expr] : stmt.kind === "query" || stmt.kind === "mutation" ? stmt.call.args : [];
|
|
18961
|
+
for (const node of nodes) {
|
|
18962
|
+
for (const dep of collectQueryDeps(node)) {
|
|
18963
|
+
if (!(dep in stateDeclarations)) {
|
|
18964
|
+
stateDeclarations[dep] = null;
|
|
18965
|
+
}
|
|
18966
|
+
}
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18969
|
+
return { stateDeclarations, queryStatements, mutationStatements };
|
|
18970
|
+
}
|
|
18971
|
+
var DEFAULT_ROOT_STATEMENT_ID = "root";
|
|
18972
|
+
function isComponentStatement(stmt) {
|
|
18973
|
+
return stmt.kind === "value" && stmt.expr.k === "Comp" && !isBuiltin(stmt.expr.name) && stmt.expr.name !== RESERVED_CALLS.Query && stmt.expr.name !== RESERVED_CALLS.Mutation;
|
|
18974
|
+
}
|
|
18975
|
+
function pickEntryId(stmtMap, typedStmts, firstId, rootName) {
|
|
18976
|
+
if (stmtMap.has(DEFAULT_ROOT_STATEMENT_ID)) return DEFAULT_ROOT_STATEMENT_ID;
|
|
18977
|
+
if (rootName && stmtMap.has(rootName)) return rootName;
|
|
18978
|
+
const preferredComponent = rootName ? typedStmts.find((stmt) => isComponentStatement(stmt) && stmt.expr.name === rootName) : void 0;
|
|
18979
|
+
if (preferredComponent) return preferredComponent.id;
|
|
18980
|
+
const firstComponent = typedStmts.find(isComponentStatement);
|
|
18981
|
+
return firstComponent?.id ?? firstId;
|
|
18982
|
+
}
|
|
18983
|
+
function buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtCount, cat, rootName) {
|
|
18984
|
+
const entryId = pickEntryId(stmtMap, typedStmts, firstId, rootName);
|
|
18985
|
+
if (!stmtMap.has(entryId)) return emptyResult(wasIncomplete);
|
|
18986
|
+
const syms = /* @__PURE__ */ new Map();
|
|
18987
|
+
for (const [id, stmt] of stmtMap) {
|
|
18988
|
+
syms.set(id, stmt.kind === "state" ? stmt.init : stmt.expr);
|
|
18989
|
+
}
|
|
18990
|
+
const unres = [];
|
|
18991
|
+
const errors = [];
|
|
18992
|
+
const unreached = /* @__PURE__ */ new Set();
|
|
18993
|
+
for (const [id, stmt] of stmtMap) {
|
|
18994
|
+
if (id === entryId) continue;
|
|
18995
|
+
if (stmt.kind === "state" || stmt.kind === "query" || stmt.kind === "mutation") continue;
|
|
18996
|
+
unreached.add(id);
|
|
18997
|
+
}
|
|
18998
|
+
const ctx = {
|
|
18999
|
+
syms,
|
|
19000
|
+
cat,
|
|
19001
|
+
errors,
|
|
19002
|
+
unres,
|
|
19003
|
+
visited: /* @__PURE__ */ new Set(),
|
|
19004
|
+
partial: wasIncomplete,
|
|
19005
|
+
currentStatementId: entryId,
|
|
19006
|
+
unreached
|
|
19007
|
+
};
|
|
19008
|
+
const materialized = materializeValue(syms.get(entryId), ctx);
|
|
19009
|
+
const root = isElementNode(materialized) ? materialized : null;
|
|
19010
|
+
if (root) root.statementId = entryId;
|
|
19011
|
+
const { stateDeclarations, queryStatements, mutationStatements } = extractStatements(
|
|
19012
|
+
typedStmts,
|
|
19013
|
+
ctx
|
|
19014
|
+
);
|
|
19015
|
+
const orphaned = [...unreached];
|
|
19016
|
+
return {
|
|
19017
|
+
root,
|
|
19018
|
+
meta: {
|
|
19019
|
+
incomplete: wasIncomplete,
|
|
19020
|
+
unresolved: unres,
|
|
19021
|
+
orphaned,
|
|
19022
|
+
statementCount: stmtCount,
|
|
19023
|
+
errors
|
|
19024
|
+
},
|
|
19025
|
+
stateDeclarations,
|
|
19026
|
+
queryStatements,
|
|
19027
|
+
mutationStatements
|
|
19028
|
+
};
|
|
19029
|
+
}
|
|
19030
|
+
function skipString(input, start) {
|
|
19031
|
+
if (input[start] !== '"') return start;
|
|
19032
|
+
let i = start + 1;
|
|
19033
|
+
while (i < input.length) {
|
|
19034
|
+
const c = input[i];
|
|
19035
|
+
if (c === "\\") {
|
|
19036
|
+
i += 2;
|
|
19037
|
+
} else if (c === '"') {
|
|
19038
|
+
return i + 1;
|
|
19039
|
+
} else {
|
|
19040
|
+
i++;
|
|
19041
|
+
}
|
|
19042
|
+
}
|
|
19043
|
+
return i;
|
|
19044
|
+
}
|
|
19045
|
+
function stripFences(input) {
|
|
19046
|
+
const blocks = [];
|
|
19047
|
+
let i = 0;
|
|
19048
|
+
while (i < input.length) {
|
|
19049
|
+
let fenceStart = -1;
|
|
19050
|
+
while (i < input.length) {
|
|
19051
|
+
const nextI = skipString(input, i);
|
|
19052
|
+
if (nextI > i) {
|
|
19053
|
+
i = nextI;
|
|
19054
|
+
continue;
|
|
19055
|
+
}
|
|
19056
|
+
const c = input[i];
|
|
19057
|
+
if (c === "`" && i + 1 < input.length && input[i + 1] === "`" && i + 2 < input.length && input[i + 2] === "`") {
|
|
19058
|
+
fenceStart = i;
|
|
19059
|
+
break;
|
|
19060
|
+
}
|
|
19061
|
+
i++;
|
|
19062
|
+
}
|
|
19063
|
+
if (fenceStart === -1) break;
|
|
19064
|
+
let j = fenceStart + 3;
|
|
19065
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
19066
|
+
if (j >= input.length) {
|
|
19067
|
+
blocks.push(input.slice(fenceStart + 3).replace(/^[^\n]*\n?/, ""));
|
|
19068
|
+
i = input.length;
|
|
19069
|
+
break;
|
|
19070
|
+
}
|
|
19071
|
+
j++;
|
|
19072
|
+
let closePos = -1;
|
|
19073
|
+
let k = j;
|
|
19074
|
+
while (k < input.length) {
|
|
19075
|
+
const nextK = skipString(input, k);
|
|
19076
|
+
if (nextK > k) {
|
|
19077
|
+
k = nextK;
|
|
19078
|
+
continue;
|
|
19079
|
+
}
|
|
19080
|
+
const c = input[k];
|
|
19081
|
+
if (c === "`" && k + 1 < input.length && input[k + 1] === "`" && k + 2 < input.length && input[k + 2] === "`") {
|
|
19082
|
+
closePos = k;
|
|
19083
|
+
break;
|
|
19084
|
+
}
|
|
19085
|
+
k++;
|
|
19086
|
+
}
|
|
19087
|
+
if (closePos !== -1) {
|
|
19088
|
+
blocks.push(input.slice(j, closePos));
|
|
19089
|
+
i = closePos + 3;
|
|
19090
|
+
} else {
|
|
19091
|
+
blocks.push(input.slice(j));
|
|
19092
|
+
i = input.length;
|
|
19093
|
+
}
|
|
19094
|
+
}
|
|
19095
|
+
if (blocks.length > 0) return blocks.join("\n");
|
|
19096
|
+
if (input.startsWith("```")) {
|
|
19097
|
+
let j = 3;
|
|
19098
|
+
while (j < input.length && input[j] !== "\n") j++;
|
|
19099
|
+
const start = j < input.length ? j + 1 : 3;
|
|
19100
|
+
const body = input.slice(start);
|
|
19101
|
+
const trailingFence = body.lastIndexOf("```");
|
|
19102
|
+
if (trailingFence !== -1) {
|
|
19103
|
+
return body.slice(0, trailingFence);
|
|
19104
|
+
}
|
|
19105
|
+
return body;
|
|
19106
|
+
}
|
|
19107
|
+
return input;
|
|
19108
|
+
}
|
|
19109
|
+
function stripComments(input) {
|
|
19110
|
+
let inStr = false;
|
|
19111
|
+
return input.split("\n").map((line) => {
|
|
19112
|
+
for (let i = 0; i < line.length; i++) {
|
|
19113
|
+
const c = line[i];
|
|
19114
|
+
if (inStr) {
|
|
19115
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
19116
|
+
i++;
|
|
19117
|
+
continue;
|
|
19118
|
+
}
|
|
19119
|
+
if (c === inStr) inStr = false;
|
|
19120
|
+
continue;
|
|
19121
|
+
}
|
|
19122
|
+
if (c === '"' || c === "'") {
|
|
19123
|
+
inStr = c;
|
|
19124
|
+
continue;
|
|
19125
|
+
}
|
|
19126
|
+
if (c === "/" && line[i + 1] === "/") {
|
|
19127
|
+
return line.substring(0, i).trimEnd();
|
|
19128
|
+
}
|
|
19129
|
+
if (c === "#") {
|
|
19130
|
+
return line.substring(0, i).trimEnd();
|
|
19131
|
+
}
|
|
19132
|
+
}
|
|
19133
|
+
return line;
|
|
19134
|
+
}).join("\n");
|
|
19135
|
+
}
|
|
19136
|
+
function preprocess(input) {
|
|
19137
|
+
return stripComments(stripFences(input.trim())).trim();
|
|
19138
|
+
}
|
|
19139
|
+
function parse(input, cat, rootName) {
|
|
19140
|
+
const trimmed = preprocess(input);
|
|
19141
|
+
if (!trimmed) return emptyResult();
|
|
19142
|
+
const { text, wasIncomplete } = autoClose(trimmed);
|
|
19143
|
+
const stmts = split(tokenize(text));
|
|
19144
|
+
if (!stmts.length) return emptyResult(wasIncomplete);
|
|
19145
|
+
const stmtMap = /* @__PURE__ */ new Map();
|
|
19146
|
+
let firstId = "";
|
|
19147
|
+
for (const s of stmts) {
|
|
19148
|
+
const expr = parseExpression(s.tokens);
|
|
19149
|
+
const stmt = classifyStatement(s, expr);
|
|
19150
|
+
stmtMap.set(s.id, stmt);
|
|
19151
|
+
if (!firstId) firstId = s.id;
|
|
19152
|
+
}
|
|
19153
|
+
const typedStmts = [...stmtMap.values()];
|
|
19154
|
+
return buildResult(stmtMap, typedStmts, firstId, wasIncomplete, stmtMap.size, cat, rootName);
|
|
19155
|
+
}
|
|
19156
|
+
function createStreamParser(cat, rootName) {
|
|
19157
|
+
let buf = "";
|
|
19158
|
+
let cleaned = "";
|
|
19159
|
+
let completedEnd = 0;
|
|
19160
|
+
const completedStmtMap = /* @__PURE__ */ new Map();
|
|
19161
|
+
let firstId = "";
|
|
19162
|
+
function addStmt(text) {
|
|
19163
|
+
const t = text.trim();
|
|
19164
|
+
if (!t) return;
|
|
19165
|
+
for (const s of split(tokenize(t))) {
|
|
19166
|
+
const expr = parseExpression(s.tokens);
|
|
19167
|
+
const stmt = classifyStatement(s, expr);
|
|
19168
|
+
completedStmtMap.set(s.id, stmt);
|
|
19169
|
+
if (!firstId) firstId = s.id;
|
|
19170
|
+
}
|
|
19171
|
+
}
|
|
19172
|
+
function refreshCleaned() {
|
|
19173
|
+
const next = preprocess(buf);
|
|
19174
|
+
if (!next.startsWith(cleaned.slice(0, completedEnd))) {
|
|
19175
|
+
completedEnd = 0;
|
|
19176
|
+
completedStmtMap.clear();
|
|
19177
|
+
firstId = "";
|
|
19178
|
+
}
|
|
19179
|
+
cleaned = next;
|
|
19180
|
+
}
|
|
19181
|
+
function scanNewCompleted() {
|
|
19182
|
+
let depth = 0, ternaryDepth = 0, inStr = false, esc = false;
|
|
19183
|
+
let stmtStart = completedEnd;
|
|
19184
|
+
for (let i = completedEnd; i < cleaned.length; i++) {
|
|
19185
|
+
const c = cleaned[i];
|
|
19186
|
+
if (esc) {
|
|
19187
|
+
esc = false;
|
|
19188
|
+
continue;
|
|
19189
|
+
}
|
|
19190
|
+
if (c === "\\" && inStr) {
|
|
19191
|
+
esc = true;
|
|
19192
|
+
continue;
|
|
19193
|
+
}
|
|
19194
|
+
if (inStr) {
|
|
19195
|
+
if (c === inStr) inStr = false;
|
|
19196
|
+
continue;
|
|
19197
|
+
}
|
|
19198
|
+
if (c === '"' || c === "'") {
|
|
19199
|
+
inStr = c;
|
|
19200
|
+
continue;
|
|
19201
|
+
}
|
|
19202
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
19203
|
+
else if (c === ")" || c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
19204
|
+
else if (c === "?" && depth === 0) ternaryDepth++;
|
|
19205
|
+
else if (c === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth--;
|
|
19206
|
+
else if (c === "\n" && depth <= 0 && ternaryDepth <= 0) {
|
|
19207
|
+
let peek = i + 1;
|
|
19208
|
+
while (peek < cleaned.length && (cleaned[peek] === " " || cleaned[peek] === " " || cleaned[peek] === "\r" || cleaned[peek] === "\n"))
|
|
19209
|
+
peek++;
|
|
19210
|
+
if (peek < cleaned.length && (cleaned[peek] === "?" || cleaned[peek] === ":" && ternaryDepth > 0)) {
|
|
19211
|
+
continue;
|
|
19212
|
+
}
|
|
19213
|
+
const t = cleaned.slice(stmtStart, i).trim();
|
|
19214
|
+
if (t) addStmt(t);
|
|
19215
|
+
stmtStart = i + 1;
|
|
19216
|
+
completedEnd = i + 1;
|
|
19217
|
+
}
|
|
19218
|
+
}
|
|
19219
|
+
return stmtStart;
|
|
19220
|
+
}
|
|
19221
|
+
function currentResult() {
|
|
19222
|
+
refreshCleaned();
|
|
19223
|
+
const pendingStart = scanNewCompleted();
|
|
19224
|
+
const pendingText = cleaned.slice(pendingStart).trim();
|
|
19225
|
+
if (!pendingText) {
|
|
19226
|
+
if (completedStmtMap.size === 0) return emptyResult();
|
|
19227
|
+
return buildResult(
|
|
19228
|
+
completedStmtMap,
|
|
19229
|
+
[...completedStmtMap.values()],
|
|
19230
|
+
firstId,
|
|
19231
|
+
false,
|
|
19232
|
+
completedStmtMap.size,
|
|
19233
|
+
cat,
|
|
19234
|
+
rootName
|
|
19235
|
+
);
|
|
19236
|
+
}
|
|
19237
|
+
const { text: closed, wasIncomplete } = autoClose(pendingText);
|
|
19238
|
+
const stmts = split(tokenize(closed));
|
|
19239
|
+
if (!stmts.length) {
|
|
19240
|
+
if (completedStmtMap.size === 0) return emptyResult(wasIncomplete);
|
|
19241
|
+
return buildResult(
|
|
19242
|
+
completedStmtMap,
|
|
19243
|
+
[...completedStmtMap.values()],
|
|
19244
|
+
firstId,
|
|
19245
|
+
wasIncomplete,
|
|
19246
|
+
completedStmtMap.size,
|
|
19247
|
+
cat,
|
|
19248
|
+
rootName
|
|
19249
|
+
);
|
|
19250
|
+
}
|
|
19251
|
+
const allStmtMap = new Map(completedStmtMap);
|
|
19252
|
+
for (const s of stmts) {
|
|
19253
|
+
const expr = parseExpression(s.tokens);
|
|
19254
|
+
const stmt = classifyStatement(s, expr);
|
|
19255
|
+
allStmtMap.set(s.id, stmt);
|
|
19256
|
+
}
|
|
19257
|
+
const allTypedStmts = [...allStmtMap.values()];
|
|
19258
|
+
const fid = firstId || stmts[0].id;
|
|
19259
|
+
return buildResult(
|
|
19260
|
+
allStmtMap,
|
|
19261
|
+
allTypedStmts,
|
|
19262
|
+
fid,
|
|
19263
|
+
wasIncomplete,
|
|
19264
|
+
allStmtMap.size,
|
|
19265
|
+
cat,
|
|
19266
|
+
rootName
|
|
19267
|
+
);
|
|
19268
|
+
}
|
|
19269
|
+
function reset() {
|
|
19270
|
+
buf = "";
|
|
19271
|
+
cleaned = "";
|
|
19272
|
+
completedEnd = 0;
|
|
19273
|
+
completedStmtMap.clear();
|
|
19274
|
+
firstId = "";
|
|
19275
|
+
}
|
|
19276
|
+
return {
|
|
19277
|
+
push(chunk) {
|
|
19278
|
+
buf += chunk;
|
|
19279
|
+
return currentResult();
|
|
19280
|
+
},
|
|
19281
|
+
set(fullText) {
|
|
19282
|
+
if (fullText.length < buf.length || !fullText.startsWith(buf)) {
|
|
19283
|
+
reset();
|
|
19284
|
+
}
|
|
19285
|
+
const delta = fullText.slice(buf.length);
|
|
19286
|
+
if (delta) buf += delta;
|
|
19287
|
+
return currentResult();
|
|
19288
|
+
},
|
|
19289
|
+
getResult: currentResult
|
|
19290
|
+
};
|
|
19291
|
+
}
|
|
19292
|
+
function useGenUIProgram(text, catalog) {
|
|
19293
|
+
const parserRef = React14.useRef(
|
|
19294
|
+
null
|
|
19295
|
+
);
|
|
19296
|
+
if (!parserRef.current || parserRef.current.catalog !== catalog) {
|
|
19297
|
+
parserRef.current = { catalog, parser: createStreamParser(catalog, "root") };
|
|
19298
|
+
}
|
|
19299
|
+
const lastGoodRef = React14.useRef(null);
|
|
19300
|
+
return React14.useMemo(() => {
|
|
19301
|
+
const result = parserRef.current.parser.set(text);
|
|
19302
|
+
if (result.root) {
|
|
19303
|
+
lastGoodRef.current = result.root;
|
|
19304
|
+
}
|
|
19305
|
+
return { result, root: result.root ?? lastGoodRef.current };
|
|
19306
|
+
}, [text, catalog]);
|
|
19307
|
+
}
|
|
19308
|
+
var noop = () => {
|
|
19309
|
+
};
|
|
19310
|
+
function childKey(value, index) {
|
|
19311
|
+
if (isElementNode(value) && value.statementId) return `s:${value.statementId}`;
|
|
19312
|
+
return `i:${index}`;
|
|
19313
|
+
}
|
|
19314
|
+
var RenderDepthContext = React14.createContext(0);
|
|
19315
|
+
var MAX_DEPTH = 64;
|
|
19316
|
+
var GenUINode = React14.memo(function GenUINode2({
|
|
19317
|
+
node,
|
|
19318
|
+
components,
|
|
19319
|
+
onAction,
|
|
19320
|
+
isStreaming,
|
|
19321
|
+
onTelemetry
|
|
19322
|
+
}) {
|
|
19323
|
+
const depth = React14.useContext(RenderDepthContext);
|
|
19324
|
+
if (depth > MAX_DEPTH) {
|
|
19325
|
+
onTelemetry?.({ kind: "dropped-node", typeName: node.typeName });
|
|
19326
|
+
return null;
|
|
19327
|
+
}
|
|
19328
|
+
const renderer = components[node.typeName];
|
|
19329
|
+
if (!renderer) {
|
|
19330
|
+
onTelemetry?.({ kind: "unknown-component", typeName: node.typeName });
|
|
19331
|
+
return null;
|
|
19332
|
+
}
|
|
19333
|
+
const render = (value) => {
|
|
19334
|
+
if (value == null) return null;
|
|
19335
|
+
if (isElementNode(value)) {
|
|
19336
|
+
return /* @__PURE__ */ jsxRuntime.jsx(RenderDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
19337
|
+
GenUINode2,
|
|
19338
|
+
{
|
|
19339
|
+
node: value,
|
|
19340
|
+
components,
|
|
19341
|
+
onAction,
|
|
19342
|
+
isStreaming,
|
|
19343
|
+
onTelemetry
|
|
19344
|
+
}
|
|
19345
|
+
) });
|
|
19346
|
+
}
|
|
19347
|
+
if (Array.isArray(value)) {
|
|
19348
|
+
return value.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(React14__default.default.Fragment, { children: render(item) }, childKey(item, index)));
|
|
19349
|
+
}
|
|
19350
|
+
if (typeof value === "object" && isRuntimeExpr(value)) return null;
|
|
19351
|
+
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
19352
|
+
return null;
|
|
19353
|
+
};
|
|
19354
|
+
return renderer({ node, ctx: { render, onAction, isStreaming } });
|
|
19355
|
+
});
|
|
19356
|
+
function GenUIView({
|
|
19357
|
+
root,
|
|
19358
|
+
components,
|
|
19359
|
+
onAction = noop,
|
|
19360
|
+
isStreaming = false,
|
|
19361
|
+
onTelemetry,
|
|
19362
|
+
placeholder = null
|
|
19363
|
+
}) {
|
|
19364
|
+
if (!root) return placeholder;
|
|
19365
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
19366
|
+
GenUINode,
|
|
19367
|
+
{
|
|
19368
|
+
node: root,
|
|
19369
|
+
components,
|
|
19370
|
+
onAction,
|
|
19371
|
+
isStreaming,
|
|
19372
|
+
onTelemetry
|
|
19373
|
+
}
|
|
19374
|
+
);
|
|
19375
|
+
}
|
|
19376
|
+
|
|
19377
|
+
// src/genui/registry.ts
|
|
19378
|
+
var CIMPLIFY_GENUI_REGISTRY = {
|
|
19379
|
+
version: "3",
|
|
19380
|
+
components: [
|
|
19381
|
+
{
|
|
19382
|
+
name: "Stack",
|
|
19383
|
+
description: 'Layout container. direction: "column" (default) or "row"; gap in points (default 12)',
|
|
19384
|
+
params: [{ name: "children", required: true }, { name: "direction" }, { name: "gap" }]
|
|
19385
|
+
},
|
|
19386
|
+
{
|
|
19387
|
+
name: "Card",
|
|
19388
|
+
description: "Elevated surface grouping related content, with an optional title",
|
|
19389
|
+
params: [{ name: "children", required: true }, { name: "title" }]
|
|
19390
|
+
},
|
|
19391
|
+
{
|
|
19392
|
+
name: "TextContent",
|
|
19393
|
+
description: 'Text block. size: "display", "heading", "title", "body" (default), "label", "caption"',
|
|
19394
|
+
params: [{ name: "text", required: true }, { name: "size" }]
|
|
19395
|
+
},
|
|
19396
|
+
{
|
|
19397
|
+
name: "Stat",
|
|
19398
|
+
description: "Compact stat: small label over a bold value, optional sub line and tone",
|
|
19399
|
+
params: [
|
|
19400
|
+
{ name: "label", required: true },
|
|
19401
|
+
{ name: "value", required: true },
|
|
19402
|
+
{ name: "sub" },
|
|
19403
|
+
{ name: "tone" }
|
|
19404
|
+
]
|
|
19405
|
+
},
|
|
19406
|
+
{
|
|
19407
|
+
name: "Metric",
|
|
19408
|
+
description: "KPI card with label, value, optional percent change and SF Symbol icon",
|
|
19409
|
+
params: [
|
|
19410
|
+
{ name: "label", required: true },
|
|
19411
|
+
{ name: "value", required: true },
|
|
19412
|
+
{ name: "change" },
|
|
19413
|
+
{ name: "icon" }
|
|
19414
|
+
]
|
|
19415
|
+
},
|
|
19416
|
+
{
|
|
19417
|
+
name: "KeyValue",
|
|
19418
|
+
description: "Label/value row; set mono true for numeric values",
|
|
19419
|
+
params: [
|
|
19420
|
+
{ name: "label", required: true },
|
|
19421
|
+
{ name: "value", required: true },
|
|
19422
|
+
{ name: "mono" }
|
|
19423
|
+
]
|
|
19424
|
+
},
|
|
19425
|
+
{
|
|
19426
|
+
name: "Badge",
|
|
19427
|
+
description: "Small status chip. tone: neutral, positive, info, indigo, violet, warning, danger",
|
|
19428
|
+
params: [{ name: "label", required: true }, { name: "tone" }]
|
|
19429
|
+
},
|
|
19430
|
+
{
|
|
19431
|
+
name: "Callout",
|
|
19432
|
+
description: "Inline alert with title, optional message. tone: info (default), success, warning, danger, accent, neutral",
|
|
19433
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "tone" }]
|
|
19434
|
+
},
|
|
19435
|
+
{
|
|
19436
|
+
name: "Button",
|
|
19437
|
+
description: 'Action button. action: "action:name" string or Action([...]) steps. variant: primary, cta, secondary (default), ghost, destructive',
|
|
19438
|
+
params: [
|
|
19439
|
+
{ name: "label", required: true },
|
|
19440
|
+
{ name: "action", required: true },
|
|
19441
|
+
{ name: "variant" }
|
|
19442
|
+
]
|
|
19443
|
+
},
|
|
19444
|
+
{
|
|
19445
|
+
name: "Buttons",
|
|
19446
|
+
description: 'Row (default) or column of Buttons; direction: "row" or "column"',
|
|
19447
|
+
params: [{ name: "children", required: true }, { name: "direction" }]
|
|
19448
|
+
},
|
|
19449
|
+
{
|
|
19450
|
+
name: "Divider",
|
|
19451
|
+
description: "Hairline separator",
|
|
19452
|
+
params: []
|
|
19453
|
+
},
|
|
19454
|
+
{
|
|
19455
|
+
name: "Table",
|
|
19456
|
+
description: "Data table: headers is an array of strings, rows an array of cell arrays",
|
|
19457
|
+
params: [
|
|
19458
|
+
{ name: "headers", required: true },
|
|
19459
|
+
{ name: "rows", required: true }
|
|
19460
|
+
]
|
|
19461
|
+
},
|
|
19462
|
+
{
|
|
19463
|
+
name: "BarChart",
|
|
19464
|
+
description: "Bar chart: labels (x axis strings) and values (numbers), optional title",
|
|
19465
|
+
params: [
|
|
19466
|
+
{ name: "labels", required: true },
|
|
19467
|
+
{ name: "values", required: true },
|
|
19468
|
+
{ name: "title" }
|
|
19469
|
+
]
|
|
19470
|
+
},
|
|
19471
|
+
{
|
|
19472
|
+
name: "LineChart",
|
|
19473
|
+
description: "Line chart with area fill: labels and values, optional title",
|
|
19474
|
+
params: [
|
|
19475
|
+
{ name: "labels", required: true },
|
|
19476
|
+
{ name: "values", required: true },
|
|
19477
|
+
{ name: "title" }
|
|
19478
|
+
]
|
|
19479
|
+
},
|
|
19480
|
+
{
|
|
19481
|
+
name: "Money",
|
|
19482
|
+
description: "Formatted money amount, never abbreviated. currency: ISO code (default USD)",
|
|
19483
|
+
params: [{ name: "amount", required: true }, { name: "currency" }]
|
|
19484
|
+
},
|
|
19485
|
+
{
|
|
19486
|
+
name: "Image",
|
|
19487
|
+
description: "Remote image by URL with optional caption",
|
|
19488
|
+
params: [{ name: "url", required: true }, { name: "caption" }]
|
|
19489
|
+
},
|
|
19490
|
+
{
|
|
19491
|
+
name: "ListRow",
|
|
19492
|
+
description: "Entity row for orders/customers/products/invoices: initials avatar (or SF Symbol tile via symbol), title + subtitle, trailing value and badge with tone",
|
|
19493
|
+
params: [
|
|
19494
|
+
{ name: "title", required: true },
|
|
19495
|
+
{ name: "subtitle" },
|
|
19496
|
+
{ name: "value" },
|
|
19497
|
+
{ name: "badge" },
|
|
19498
|
+
{ name: "tone" },
|
|
19499
|
+
{ name: "symbol" }
|
|
19500
|
+
]
|
|
19501
|
+
},
|
|
19502
|
+
{
|
|
19503
|
+
name: "Timeline",
|
|
19504
|
+
description: "Event history (order journey, payment trail): events is an array of {title, detail?, time?, tone?} objects, newest first",
|
|
19505
|
+
params: [{ name: "events", required: true }]
|
|
19506
|
+
},
|
|
19507
|
+
{
|
|
19508
|
+
name: "Progress",
|
|
19509
|
+
description: "Amount toward a bound (goal, stock cover, capacity): numeric value and target (default 100), optional detail caption and tone",
|
|
19510
|
+
params: [
|
|
19511
|
+
{ name: "label", required: true },
|
|
19512
|
+
{ name: "value", required: true },
|
|
19513
|
+
{ name: "target" },
|
|
19514
|
+
{ name: "detail" },
|
|
19515
|
+
{ name: "tone" }
|
|
19516
|
+
]
|
|
19517
|
+
},
|
|
19518
|
+
{
|
|
19519
|
+
name: "DonutChart",
|
|
19520
|
+
description: "Share of a whole (sales by channel, revenue by category): labels + values, optional title and center display string (e.g. the formatted total)",
|
|
19521
|
+
params: [
|
|
19522
|
+
{ name: "labels", required: true },
|
|
19523
|
+
{ name: "values", required: true },
|
|
19524
|
+
{ name: "title" },
|
|
19525
|
+
{ name: "center" }
|
|
19526
|
+
]
|
|
19527
|
+
},
|
|
19528
|
+
{
|
|
19529
|
+
name: "StatusTrack",
|
|
19530
|
+
description: "Stage tracker (order fulfilment, invoice lifecycle): steps are stage names, current is the active step name or index",
|
|
19531
|
+
params: [
|
|
19532
|
+
{ name: "steps", required: true },
|
|
19533
|
+
{ name: "current", required: true }
|
|
19534
|
+
]
|
|
19535
|
+
},
|
|
19536
|
+
{
|
|
19537
|
+
name: "LineItems",
|
|
19538
|
+
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",
|
|
19539
|
+
params: [
|
|
19540
|
+
{ name: "items", required: true },
|
|
19541
|
+
{ name: "total", required: true },
|
|
19542
|
+
{ name: "adjustments" }
|
|
19543
|
+
]
|
|
19544
|
+
},
|
|
19545
|
+
{
|
|
19546
|
+
name: "Grid",
|
|
19547
|
+
description: "Tile layout for Stat/Metric children, columns default 2",
|
|
19548
|
+
params: [{ name: "children", required: true }, { name: "columns" }]
|
|
19549
|
+
},
|
|
19550
|
+
{
|
|
19551
|
+
name: "Banner",
|
|
19552
|
+
description: "Hero-stat moment on a tonal wash: big value, label, optional percent delta. tone: positive (default), warning, danger, info, neutral",
|
|
19553
|
+
params: [
|
|
19554
|
+
{ name: "value", required: true },
|
|
19555
|
+
{ name: "label", required: true },
|
|
19556
|
+
{ name: "delta" },
|
|
19557
|
+
{ name: "tone" }
|
|
19558
|
+
]
|
|
19559
|
+
},
|
|
19560
|
+
{
|
|
19561
|
+
name: "EmptyState",
|
|
19562
|
+
description: "Nothing-to-show state with an SF Symbol and optional message",
|
|
19563
|
+
params: [{ name: "title", required: true }, { name: "message" }, { name: "symbol" }]
|
|
19564
|
+
}
|
|
19565
|
+
]
|
|
19566
|
+
};
|
|
19567
|
+
|
|
19568
|
+
// src/genui/manifest.ts
|
|
19569
|
+
function genUIManifest(registry) {
|
|
19570
|
+
return {
|
|
19571
|
+
version: registry.version,
|
|
19572
|
+
components: registry.components.map((component) => ({
|
|
19573
|
+
name: component.name,
|
|
19574
|
+
description: component.description,
|
|
19575
|
+
params: component.params.map((param) => ({
|
|
19576
|
+
name: param.name,
|
|
19577
|
+
required: param.required ?? false
|
|
19578
|
+
}))
|
|
19579
|
+
}))
|
|
19580
|
+
};
|
|
19581
|
+
}
|
|
19582
|
+
function encodeString(value) {
|
|
19583
|
+
return JSON.stringify(value).replace(/\//g, "\\/");
|
|
19584
|
+
}
|
|
19585
|
+
function canonicalize(value) {
|
|
19586
|
+
if (value === null) return "null";
|
|
19587
|
+
if (typeof value === "string") return encodeString(value);
|
|
19588
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
19589
|
+
if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
|
|
19590
|
+
const keys = Object.keys(value).sort();
|
|
19591
|
+
const entries = keys.map((key) => `${encodeString(key)}:${canonicalize(value[key])}`);
|
|
19592
|
+
return `{${entries.join(",")}}`;
|
|
19593
|
+
}
|
|
19594
|
+
function genUIManifestJson(manifest) {
|
|
19595
|
+
return canonicalize(manifest);
|
|
19596
|
+
}
|
|
19597
|
+
async function genUIFingerprint(manifest) {
|
|
19598
|
+
const bytes = new TextEncoder().encode(genUIManifestJson(manifest));
|
|
19599
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
19600
|
+
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
|
|
19601
|
+
}
|
|
19602
|
+
|
|
19603
|
+
Object.defineProperty(exports, "getVariantDisplayName", {
|
|
19604
|
+
enumerable: true,
|
|
19605
|
+
get: function () { return chunkHKN3YG76_js.getVariantDisplayName; }
|
|
19606
|
+
});
|
|
19607
|
+
Object.defineProperty(exports, "messagePreview", {
|
|
19608
|
+
enumerable: true,
|
|
19609
|
+
get: function () { return chunkXREIZ56K_js.messagePreview; }
|
|
19610
|
+
});
|
|
19611
|
+
Object.defineProperty(exports, "splitGenUIContent", {
|
|
17242
19612
|
enumerable: true,
|
|
17243
|
-
get: function () { return
|
|
19613
|
+
get: function () { return chunkXREIZ56K_js.splitGenUIContent; }
|
|
17244
19614
|
});
|
|
17245
19615
|
exports.AccommodationCard = AccommodationCard;
|
|
17246
19616
|
exports.AccountContent = AccountContent;
|
|
@@ -17261,6 +19631,7 @@ exports.AccountSubscriptionsPage = AccountSubscriptionsPage;
|
|
|
17261
19631
|
exports.Ad = Ad;
|
|
17262
19632
|
exports.AdProvider = AdProvider;
|
|
17263
19633
|
exports.AddOnSelector = AddOnSelector;
|
|
19634
|
+
exports.AddressAutocomplete = AddressAutocomplete;
|
|
17264
19635
|
exports.AddressElement = AddressElement;
|
|
17265
19636
|
exports.AuthElement = AuthElement;
|
|
17266
19637
|
exports.AvailabilityBadge = AvailabilityBadge;
|
|
@@ -17276,6 +19647,7 @@ exports.BundleProductCard = BundleProductCard;
|
|
|
17276
19647
|
exports.BundleProductLayout = BundleProductLayout;
|
|
17277
19648
|
exports.BundleSelector = BundleSelector;
|
|
17278
19649
|
exports.Button = Button;
|
|
19650
|
+
exports.CIMPLIFY_GENUI_REGISTRY = CIMPLIFY_GENUI_REGISTRY;
|
|
17279
19651
|
exports.CardImage = CardImage;
|
|
17280
19652
|
exports.CardShell = CardShell;
|
|
17281
19653
|
exports.CardVariant = CardVariant;
|
|
@@ -17289,6 +19661,7 @@ exports.CataloguePage = CataloguePage;
|
|
|
17289
19661
|
exports.CatalogueTemplate = CatalogueTemplate;
|
|
17290
19662
|
exports.CategoryFilter = CategoryFilter;
|
|
17291
19663
|
exports.CategoryGrid = CategoryGrid;
|
|
19664
|
+
exports.ChatMarkdown = ChatMarkdown;
|
|
17292
19665
|
exports.ChatWidget = ChatWidget;
|
|
17293
19666
|
exports.CheckIcon = CheckIcon;
|
|
17294
19667
|
exports.CheckoutPage = CheckoutPage;
|
|
@@ -17334,6 +19707,7 @@ exports.FeaturedCollectionLayout = FeaturedCollectionLayout;
|
|
|
17334
19707
|
exports.FilterTabs = FilterTabs;
|
|
17335
19708
|
exports.FoodProductCard = FoodProductCard;
|
|
17336
19709
|
exports.FoodProductLayout = FoodProductLayout;
|
|
19710
|
+
exports.GenUIView = GenUIView;
|
|
17337
19711
|
exports.HappeningCard = HappeningCard;
|
|
17338
19712
|
exports.IdentityIcon = IdentityIcon;
|
|
17339
19713
|
exports.IdentityOfframp = IdentityOfframp;
|
|
@@ -17419,9 +19793,19 @@ exports.WelcomeCard = WelcomeCard;
|
|
|
17419
19793
|
exports.WholesaleProductCard = WholesaleProductCard;
|
|
17420
19794
|
exports.WholesaleProductLayout = WholesaleProductLayout;
|
|
17421
19795
|
exports.WishlistButton = WishlistButton;
|
|
19796
|
+
exports.clearOrdersCache = clearOrdersCache;
|
|
17422
19797
|
exports.cn = cn;
|
|
19798
|
+
exports.createPlacesSessionToken = createPlacesSessionToken;
|
|
17423
19799
|
exports.defaultBoundaryKind = defaultBoundaryKind;
|
|
19800
|
+
exports.genUICatalog = genUICatalog;
|
|
19801
|
+
exports.genUIFingerprint = genUIFingerprint;
|
|
19802
|
+
exports.genUIManifest = genUIManifest;
|
|
19803
|
+
exports.genUIManifestJson = genUIManifestJson;
|
|
17424
19804
|
exports.getBoundaryLabels = getBoundaryLabels;
|
|
19805
|
+
exports.isImageAttachment = isImageAttachment;
|
|
19806
|
+
exports.isTerminalOrderStatus = isTerminalOrderStatus;
|
|
19807
|
+
exports.parseGenUIProgram = parse;
|
|
19808
|
+
exports.partitionMessages = partitionMessages;
|
|
17425
19809
|
exports.prefetchProduct = prefetchProduct;
|
|
17426
19810
|
exports.prefetchProducts = prefetchProducts;
|
|
17427
19811
|
exports.resolveBoundaryKind = resolveBoundaryKind;
|
|
@@ -17455,6 +19839,7 @@ exports.useDeliveryOptions = useDeliveryOptions;
|
|
|
17455
19839
|
exports.useElements = useElements;
|
|
17456
19840
|
exports.useElementsReady = useElementsReady;
|
|
17457
19841
|
exports.useFxRate = useFxRate;
|
|
19842
|
+
exports.useGenUIProgram = useGenUIProgram;
|
|
17458
19843
|
exports.useLocations = useLocations;
|
|
17459
19844
|
exports.useOptionalCimplify = useOptionalCimplify;
|
|
17460
19845
|
exports.useOrder = useOrder;
|