arcy.js 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- /* arcy.js — https://arcyai.com */
1
+ import { DESIGN_MODE_PROTOCOL, DESIGN_MODE_EXIT, DESIGN_MODE_SCREENSHOT, DESIGN_MODE_PICK, DESIGN_MODE_READY } from './chunk-7VVKR3AO.js';
2
+ import { FONT_KEY_PROPERTY, FALLBACK_CHROME, readViewport, POLICY_LINK_CLASS, BAR_TRAY_CLASS, ATTACHABLE_TYPES, TOOLTIP_CSS, surfaceWidth, clampOffsetX, dragTransform, carriesFiles, droppedFiles, MOBILE_MARGIN, CHAT_CONTRACT, CHAT_GLOBAL } from './chunk-6HBSGVRN.js';
3
+ import { createShellState, safeStore, browserSessionStorage, writeJson, browserLocalStorage, readJson, hasPendingPreview, createFlowRunState, FLOW_CONTRACT, FLOW_GLOBAL } from './chunk-FR6SJSDU.js';
4
+ import { PICKER_CONTRACT, PICKER_GLOBAL } from './chunk-DKYMIAYF.js';
5
+ import { warn, reportCspViolations, releaseHostFocus, Z_INDEX, createShell, injectStyles } from './chunk-LGWYJSYX.js';
6
+ import { captureEventFingerprint, containsPii } from './chunk-NY3NXM2V.js';
2
7
 
3
- // src/warn.ts
4
- function warn(message) {
5
- if (typeof console !== "undefined" && console.warn) {
6
- console.warn(`[arcy] ${message}`);
7
- }
8
- }
8
+ /* arcy.js — https://arcyai.com */
9
9
 
10
10
  // src/emitter.ts
11
11
  function createEmitter() {
@@ -40,149 +40,6 @@ function createEmitter() {
40
40
  return { on, emit };
41
41
  }
42
42
 
43
- // src/fingerprint/model.ts
44
- var FINGERPRINT_TEXT_MAX = 80;
45
-
46
- // src/fingerprint/shadow.ts
47
- function parentOrHost(el) {
48
- if (el.parentElement !== null) return el.parentElement;
49
- const parentNode = el.parentNode;
50
- if (parentNode !== null && "host" in parentNode) {
51
- return parentNode.host;
52
- }
53
- return null;
54
- }
55
-
56
- // src/fingerprint/capture.ts
57
- var PII_PATTERNS = [
58
- // Email
59
- /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
60
- // US SSN: 123-45-6789
61
- /\b\d{3}-\d{2}-\d{4}\b/g,
62
- // Card shapes: 13-19 digits, optionally grouped. A denylist, not a Luhn check.
63
- /\b\d(?:[ -]?\d){12,18}\b/g
64
- ];
65
- var MASK = "[masked]";
66
- function containsPii(raw) {
67
- return PII_PATTERNS.some((pattern) => {
68
- pattern.lastIndex = 0;
69
- return pattern.test(raw);
70
- });
71
- }
72
- var NORMALIZE_RAW_MAX = 4096;
73
- var NORMALIZE_RAW_MARGIN = 100;
74
- var ROLE_MAX = 64;
75
- function normalizeText(raw) {
76
- if (!raw) return void 0;
77
- let bounded = raw.slice(0, NORMALIZE_RAW_MAX);
78
- if (raw.length > NORMALIZE_RAW_MAX) {
79
- const lastSpace = bounded.search(/\s\S*$/);
80
- bounded = lastSpace === -1 ? bounded.slice(0, NORMALIZE_RAW_MAX - NORMALIZE_RAW_MARGIN) : bounded.slice(0, lastSpace);
81
- }
82
- let text = bounded.replace(/\s+/g, " ").trim();
83
- if (!text) return void 0;
84
- for (const pattern of PII_PATTERNS) {
85
- text = text.replace(pattern, MASK);
86
- }
87
- return text.slice(0, FINGERPRINT_TEXT_MAX);
88
- }
89
- function isStableId(id) {
90
- if (id.length === 0 || id.length > 64) return false;
91
- if (id.includes(":")) return false;
92
- if (id.startsWith("radix-")) return false;
93
- if (/^\d+$/.test(id)) return false;
94
- if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) return false;
95
- return true;
96
- }
97
- var ANCESTOR_ATTRS = ["data-testid", "data-test", "data-cy", "id", "name", "aria-label"];
98
- var TEST_ATTRS = ["data-testid", "data-test", "data-cy"];
99
- var TEST_ID_MAX = 64;
100
- function firstTestId(el) {
101
- for (const attr of TEST_ATTRS) {
102
- const raw = el.getAttribute(attr);
103
- if (raw === null) continue;
104
- const trimmed = raw.trim();
105
- if (trimmed === "" || trimmed.length > TEST_ID_MAX) continue;
106
- if (containsPii(trimmed)) continue;
107
- return trimmed;
108
- }
109
- return void 0;
110
- }
111
- var LANDMARK_ROLES = /* @__PURE__ */ new Set([
112
- "navigation",
113
- "main",
114
- "banner",
115
- "contentinfo",
116
- "search",
117
- "form",
118
- "region",
119
- "dialog"
120
- ]);
121
- function findSemanticAncestor(el) {
122
- let current = parentOrHost(el);
123
- while (current !== null) {
124
- const tag = current.tagName.toLowerCase();
125
- if (tag === "body" || tag === "html") return void 0;
126
- for (const attr of ANCESTOR_ATTRS) {
127
- const raw = current.getAttribute(attr);
128
- if (raw === null) continue;
129
- if (attr === "id" && !isStableId(raw)) continue;
130
- const value = normalizeText(raw);
131
- if (value !== void 0) return { tag, attr, value };
132
- }
133
- const role = current.getAttribute("role");
134
- if (role !== null && LANDMARK_ROLES.has(role)) {
135
- return { tag, attr: "role", value: role };
136
- }
137
- current = parentOrHost(current);
138
- }
139
- return void 0;
140
- }
141
- function siblingIndexOf(el) {
142
- let index = 0;
143
- let sibling = el.previousElementSibling;
144
- while (sibling !== null) {
145
- if (sibling.tagName === el.tagName) index += 1;
146
- sibling = sibling.previousElementSibling;
147
- }
148
- return index;
149
- }
150
- function captureEventFingerprint(el) {
151
- const fingerprint = {
152
- tag: el.tagName.toLowerCase(),
153
- siblingIndex: siblingIndexOf(el)
154
- };
155
- const text = normalizeText(el.textContent);
156
- if (text !== void 0) fingerprint.text = text;
157
- const ariaLabel = normalizeText(el.getAttribute("aria-label"));
158
- if (ariaLabel !== void 0) fingerprint.ariaLabel = ariaLabel;
159
- const placeholder = normalizeText(el.getAttribute("placeholder"));
160
- if (placeholder !== void 0) fingerprint.placeholder = placeholder;
161
- const id = el.getAttribute("id");
162
- if (id !== null && isStableId(id) && !containsPii(id)) fingerprint.id = id;
163
- const role = el.getAttribute("role");
164
- if (role !== null) {
165
- const trimmed = role.trim();
166
- if (trimmed !== "" && trimmed.length <= ROLE_MAX && !containsPii(trimmed)) {
167
- fingerprint.role = trimmed;
168
- }
169
- }
170
- const testId = firstTestId(el);
171
- if (testId !== void 0) fingerprint.testId = testId;
172
- const ancestor = findSemanticAncestor(el);
173
- if (ancestor !== void 0) {
174
- fingerprint.ancestorTag = ancestor.tag;
175
- fingerprint.ancestorAttr = ancestor.attr;
176
- fingerprint.ancestorValue = ancestor.value;
177
- }
178
- return fingerprint;
179
- }
180
-
181
- // src/generated/build-hashes.ts
182
- var PICKER_CHUNK_FILE = "arcy.picker.c598a581d964.js";
183
- var CHAT_CHUNK_FILE = "arcy.chat.ab181cf5a84b.js";
184
- var FLOW_CHUNK_FILE = "arcy.flow.fef131718e42.js";
185
-
186
43
  // src/telemetry/route.ts
187
44
  var MAX_ROUTE_LENGTH = 2048;
188
45
  var OPAQUE = /^(?:\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{12,})$/i;
@@ -246,192 +103,6 @@ function normalizeRoute(href, base) {
246
103
  return path.length + query.length > MAX_ROUTE_LENGTH ? path : path + query;
247
104
  }
248
105
 
249
- // src/picker/contract.ts
250
- var PICKER_GLOBAL = "__arcyPicker";
251
- var PICKER_CONTRACT = 2;
252
-
253
- // src/picker/message.ts
254
- var DESIGN_MODE_PROTOCOL = 2;
255
- var DESIGN_MODE_READY = "arcy:design-mode:ready";
256
- var DESIGN_MODE_PICK = "arcy:design-mode:pick";
257
- var DESIGN_MODE_EXIT = "arcy:design-mode:exit";
258
- var DESIGN_MODE_SCREENSHOT = "arcy:design-mode:screenshot";
259
-
260
- // src/shell/host.ts
261
- var HOST_ID = "arcy-widget";
262
- var HOST_ATTRIBUTE = "data-arcy";
263
- var Z_INDEX = "2147483000";
264
- var CRITICAL_STYLE = [
265
- ["position", "fixed"],
266
- ["inset", "0"],
267
- ["z-index", Z_INDEX],
268
- // The layer spans the viewport so the launcher (1.4) and the chat panel (1.5)
269
- // share one coordinate space. It must therefore be transparent to the
270
- // pointer; each surface inside re-enables its own.
271
- ["pointer-events", "none"],
272
- // The layer must also be invisible, and that is not the shadow root's job
273
- // (D512). The host is in the customer's light DOM whatever happens inside
274
- // it, so their `div { background: ... !important }` paints it, and a painted
275
- // full-viewport layer is their whole site replaced by a coloured sheet with
276
- // no error anywhere. These four are every property that fills or edges a
277
- // box, and none of them is something a customer wants on an invisible layer.
278
- ["background", "transparent"],
279
- ["border", "0"],
280
- ["box-shadow", "none"],
281
- ["outline", "0"]
282
- ];
283
- var RESET_STYLE = [
284
- ["margin", "0"],
285
- ["padding", "0"]
286
- ];
287
- var STYLE_MARKER = "data-arcy-style";
288
- var shells = /* @__PURE__ */ new Map();
289
- function isMounted(id = HOST_ID) {
290
- const shell = shells.get(id);
291
- return shell !== void 0 && shell.host.isConnected;
292
- }
293
- function createShell(options = {}) {
294
- if (typeof document === "undefined") return null;
295
- const id = options.id ?? HOST_ID;
296
- const container = options.container;
297
- if (isMounted(id)) {
298
- warn("This ARCY surface is already on this page. Ignoring the second mount.");
299
- return null;
300
- }
301
- shells.delete(id);
302
- const parent = container ?? document.body ?? document.documentElement;
303
- if (!parent) {
304
- warn("The page has no <body> to mount into yet. The widget will not render.");
305
- return null;
306
- }
307
- if (!container) {
308
- const hosts = document.querySelectorAll(`[${HOST_ATTRIBUTE}]`);
309
- for (let i = 0; i < hosts.length; i += 1) {
310
- if (hosts[i]?.id === id) {
311
- warn("Another copy of ARCY is already on this page. Ignoring this one.");
312
- return null;
313
- }
314
- }
315
- }
316
- try {
317
- const host = document.createElement("div");
318
- host.id = id;
319
- host.setAttribute(HOST_ATTRIBUTE, "");
320
- for (const [property, value] of CRITICAL_STYLE) {
321
- host.style.setProperty(property, value, "important");
322
- }
323
- for (const [property, value] of RESET_STYLE) {
324
- host.style.setProperty(property, value);
325
- }
326
- let root = host;
327
- let isolated = false;
328
- try {
329
- if (host.attachShadow) {
330
- root = host.attachShadow({ mode: "closed" });
331
- isolated = true;
332
- }
333
- } catch {
334
- }
335
- parent.appendChild(host);
336
- isolateInteractions(host);
337
- const shell = {
338
- host,
339
- root,
340
- isolated,
341
- destroy() {
342
- try {
343
- if (host.parentNode) host.parentNode.removeChild(host);
344
- } catch {
345
- }
346
- if (shells.get(id) === shell) shells.delete(id);
347
- }
348
- };
349
- shells.set(id, shell);
350
- return shell;
351
- } catch (error) {
352
- warn(`The widget could not mount. ${String(error)}`);
353
- return null;
354
- }
355
- }
356
- var ISOLATED_EVENTS = [
357
- "pointerdown",
358
- "pointerup",
359
- "pointercancel",
360
- "mousedown",
361
- "mouseup",
362
- "click",
363
- "dblclick",
364
- "auxclick",
365
- "contextmenu",
366
- "touchstart",
367
- "touchend",
368
- "touchcancel",
369
- "focusin",
370
- "focusout",
371
- "keydown",
372
- "keyup",
373
- "keypress",
374
- "beforeinput",
375
- "input",
376
- "change",
377
- "compositionstart",
378
- "compositionupdate",
379
- "compositionend",
380
- "cut",
381
- "copy",
382
- "paste"
383
- ];
384
- function isolateInteractions(host) {
385
- const stop = (event) => {
386
- try {
387
- event.stopPropagation();
388
- } catch {
389
- }
390
- };
391
- for (const type of ISOLATED_EVENTS) {
392
- try {
393
- host.addEventListener(type, stop);
394
- } catch {
395
- }
396
- }
397
- try {
398
- host.addEventListener("mousedown", (event) => {
399
- if (event.defaultPrevented) return;
400
- releaseHostFocus(host.ownerDocument, host);
401
- });
402
- } catch {
403
- }
404
- }
405
- function releaseHostFocus(doc, host) {
406
- try {
407
- const active = doc.activeElement;
408
- if (!active || active === host || host.contains(active)) return;
409
- if (active === doc.body || active === doc.documentElement) return;
410
- const blur = active.blur;
411
- if (typeof blur === "function") blur.call(active);
412
- } catch {
413
- }
414
- }
415
- function injectStyles(shell, css) {
416
- if (!shell.isolated) return false;
417
- try {
418
- const existing = shell.root.querySelector(`[${STYLE_MARKER}]`);
419
- if (existing) {
420
- existing.textContent = `${existing.textContent ?? ""}
421
- ${css}`;
422
- return true;
423
- }
424
- const style2 = shell.host.ownerDocument.createElement("style");
425
- style2.setAttribute(STYLE_MARKER, "");
426
- style2.textContent = css;
427
- shell.root.appendChild(style2);
428
- return true;
429
- } catch (error) {
430
- warn(`The widget could not apply its styles. ${String(error)}`);
431
- return false;
432
- }
433
- }
434
-
435
106
  // src/picker/notice.ts
436
107
  var NOTICE_HOST_ID = "arcy-picker-notice";
437
108
  var CARD_OFFSET = [
@@ -523,7 +194,6 @@ function refuse(message) {
523
194
  }
524
195
  var PICKER_PARAM = "arcy_picker";
525
196
  var PICKER_VERIFY_PATH = "/api/v1/sdk/tagging/verify";
526
- var PICKER_CDN_BASE = "https://cdn.arcyai.com";
527
197
  var PICKER_LOAD_TIMEOUT_MS = 15e3;
528
198
  var VERIFY_TIMEOUT_MS = 1e4;
529
199
  function detectPickerNonce(search, hash) {
@@ -608,115 +278,6 @@ function normalizeOrigin(value) {
608
278
  return null;
609
279
  }
610
280
  }
611
- function loadPickerChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
612
- return new Promise((resolve) => {
613
- let settled = false;
614
- let script = null;
615
- const settle = (value) => {
616
- if (settled) return;
617
- settled = true;
618
- if (script) {
619
- script.onload = null;
620
- script.onerror = null;
621
- }
622
- resolve(value);
623
- };
624
- let warned = false;
625
- const complain = (message) => {
626
- if (warned) return;
627
- warned = true;
628
- refuse(message);
629
- };
630
- const read = () => {
631
- let registration;
632
- try {
633
- registration = win[PICKER_GLOBAL];
634
- } catch {
635
- complain(
636
- "The element picker registration could not be read. Design mode is off."
637
- );
638
- return null;
639
- }
640
- if (!registration) return null;
641
- if (typeof registration.mount !== "function") {
642
- complain(
643
- "The element picker registered something unusable. Design mode is off."
644
- );
645
- return null;
646
- }
647
- let contract;
648
- try {
649
- contract = registration.contract;
650
- } catch {
651
- contract = void 0;
652
- }
653
- if (contract !== PICKER_CONTRACT) {
654
- complain(
655
- `The element picker on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${PICKER_CONTRACT}. Update arcy.js to use design mode.`
656
- );
657
- return null;
658
- }
659
- return registration;
660
- };
661
- const readAndSettle = (whenAbsent) => {
662
- try {
663
- const registration = read();
664
- if (!registration && !warned) whenAbsent();
665
- settle(registration);
666
- } catch {
667
- settle(null);
668
- }
669
- };
670
- let present = false;
671
- try {
672
- present = win[PICKER_GLOBAL] !== void 0;
673
- } catch {
674
- present = true;
675
- }
676
- if (present) {
677
- readAndSettle(() => {
678
- });
679
- return;
680
- }
681
- try {
682
- const doc = win.document;
683
- script = doc.createElement("script");
684
- script.async = true;
685
- script.src = `${cdnBase}/${PICKER_CHUNK_FILE}`;
686
- const src = script.src;
687
- script.onload = () => readAndSettle(
688
- () => refuse(
689
- "The element picker loaded but did not register. Design mode is off."
690
- )
691
- );
692
- script.onerror = () => {
693
- try {
694
- refuse(
695
- `Failed to load ${src}. Design mode is off. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
696
- );
697
- } catch {
698
- }
699
- settle(null);
700
- };
701
- (doc.head || doc.documentElement).appendChild(script);
702
- } catch (error) {
703
- refuse(`The element picker could not load. ${String(error)}`);
704
- settle(null);
705
- return;
706
- }
707
- try {
708
- setTimeoutImpl(() => {
709
- if (!settled) {
710
- refuse(
711
- "The element picker did not load in time. Design mode is off."
712
- );
713
- }
714
- settle(null);
715
- }, timeoutMs);
716
- } catch {
717
- }
718
- });
719
- }
720
281
  function currentPath(win) {
721
282
  try {
722
283
  const path = win.location.pathname;
@@ -754,7 +315,7 @@ async function maybeActivateDesignMode(options) {
754
315
  apiBase,
755
316
  fetchImpl,
756
317
  onVerified,
757
- cdnBase = PICKER_CDN_BASE,
318
+ loadChunk,
758
319
  loadTimeoutMs = PICKER_LOAD_TIMEOUT_MS,
759
320
  setTimeoutImpl = (fn, ms) => setTimeout(fn, ms)
760
321
  } = options;
@@ -784,12 +345,7 @@ async function maybeActivateDesignMode(options) {
784
345
  onVerified();
785
346
  } catch {
786
347
  }
787
- const registration = await loadPickerChunk(
788
- win,
789
- cdnBase,
790
- loadTimeoutMs,
791
- setTimeoutImpl
792
- );
348
+ const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
793
349
  if (!registration) return "load_failed";
794
350
  const { returnOrigin, screenshotsEnabled } = verified;
795
351
  const handle = registration.mount({
@@ -843,84 +399,6 @@ async function maybeActivateDesignMode(options) {
843
399
  }
844
400
  }
845
401
 
846
- // src/telemetry/storage.ts
847
- var NULL_STORE = {
848
- getItem: () => null,
849
- setItem: () => {
850
- },
851
- removeItem: () => {
852
- }
853
- };
854
- function safeStore(raw) {
855
- if (!raw) return NULL_STORE;
856
- return {
857
- getItem(key) {
858
- try {
859
- return raw.getItem(key);
860
- } catch {
861
- return null;
862
- }
863
- },
864
- setItem(key, value) {
865
- try {
866
- raw.setItem(key, value);
867
- } catch {
868
- }
869
- },
870
- removeItem(key) {
871
- try {
872
- raw.removeItem(key);
873
- } catch {
874
- }
875
- }
876
- };
877
- }
878
- function browserLocalStorage() {
879
- try {
880
- const candidate = globalThis.localStorage;
881
- return safeStore(candidate);
882
- } catch {
883
- return NULL_STORE;
884
- }
885
- }
886
- function browserSessionStorage() {
887
- try {
888
- const candidate = globalThis.sessionStorage;
889
- return safeStore(candidate);
890
- } catch {
891
- return NULL_STORE;
892
- }
893
- }
894
- function readJson(store, key) {
895
- const raw = store.getItem(key);
896
- if (!raw) return null;
897
- try {
898
- const parsed = JSON.parse(raw);
899
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
900
- return null;
901
- }
902
- return parsed;
903
- } catch {
904
- return null;
905
- }
906
- }
907
- function writeJson(store, key, value) {
908
- try {
909
- store.setItem(key, JSON.stringify(value));
910
- } catch {
911
- }
912
- }
913
-
914
- // src/flow/preview-state.ts
915
- var PREVIEW_KEY_PREFIX = "arcy.preview.";
916
- function hasPendingPreview(token, store = browserSessionStorage()) {
917
- try {
918
- return safeStore(store).getItem(PREVIEW_KEY_PREFIX + token) !== null;
919
- } catch {
920
- return false;
921
- }
922
- }
923
-
924
402
  // src/flow/preview.ts
925
403
  var PREVIEW_PARAM = "arcy_preview";
926
404
  var PREVIEW_PROTOCOL = 1;
@@ -1108,6 +586,8 @@ function createSessionBootstrap(options) {
1108
586
  if (payload.viewerLocale !== void 0)
1109
587
  out.viewerLocale = payload.viewerLocale;
1110
588
  if (payload.route !== void 0) out.route = payload.route;
589
+ if (payload.sessionContext !== void 0)
590
+ out.sessionContext = payload.sessionContext;
1111
591
  return JSON.stringify(out);
1112
592
  }
1113
593
  async function attempt(json) {
@@ -1191,190 +671,14 @@ function createSessionBootstrap(options) {
1191
671
  };
1192
672
  }
1193
673
 
1194
- // src/shell/chat-contract.ts
1195
- var CHAT_GLOBAL = "__arcyChat";
1196
- var CHAT_CONTRACT = 4;
1197
- var ATTACHABLE_TYPES = [
1198
- "image/png",
1199
- "image/jpeg",
1200
- "image/webp"
1201
- ];
1202
- var FONT_KEY_PROPERTY = "--_arcy-font-key";
1203
- var POLICY_LINK_CLASS = "arcy-chat-policy";
1204
- var FALLBACK_CHROME = {
1205
- historyTitle: "Recent chats",
1206
- historyEmpty: "No conversations yet.",
1207
- newChat: "New chat",
1208
- flowsTitle: "What I can do",
1209
- flowsEmpty: "Nothing to run yet.",
1210
- collapse: "Collapse",
1211
- back: "Back",
1212
- closeSheet: "Close",
1213
- settingsTitle: "Settings",
1214
- dockChat: "Dock to the corner",
1215
- dockChatHint: "Shrink ARCY to a button in the bottom-right corner.",
1216
- language: "Language",
1217
- openChat: "Open chat",
1218
- moveBar: "Move the chat bar",
1219
- send: "Send",
1220
- flowRunning: "Running",
1221
- flowStep: "Step {step} of {total}",
1222
- flowStop: "Stop",
1223
- flowFailed: "Sorry, I could not finish that for you this time. You can ask me here and I will help you do it.",
1224
- attachMenu: "Add",
1225
- attachImage: "Attach an image",
1226
- removeImage: "Remove image",
1227
- imageTooLarge: "That image is too large. Each image must be under 5 MB.",
1228
- imageWrongType: "That file type is not supported. Use PNG, JPEG or WEBP.",
1229
- imageTooMany: "You can attach up to 3 images.",
1230
- imageFailed: "That image could not be uploaded. Please try again.",
1231
- likeAnswer: "Good answer",
1232
- dislikeAnswer: "Bad answer",
1233
- answering: "Answering",
1234
- aiDisclaimer: "{app} is AI and can make mistakes",
1235
- consentNotice: "Your messages are processed to answer your question.",
1236
- consentWithPolicy: "{policy}",
1237
- disclaimerJoin: " \xB7 ",
1238
- privacyPolicy: "Privacy Policy",
1239
- poweredBy: "Powered by ARCY",
1240
- genericError: "Something went wrong. Please try again.",
1241
- userCapReached: "You've reached today's limit for questions. Please try again tomorrow.",
1242
- imageConversation: "Image",
1243
- searchedSourcesLabel: "Searched {count} sources",
1244
- sourcesLabel: "Sources:",
1245
- flowOfferHint: "You can do this in one click with the Quick Flow below.",
1246
- fillConfirm: "Confirm",
1247
- fillYes: "Yes",
1248
- fillNo: "No",
1249
- fillRequired: "This field needs an answer.",
1250
- fillInvalidNumber: "Please enter a number.",
1251
- fillNumberTooSmall: "Please enter {min} or more.",
1252
- fillNumberTooLarge: "Please enter {max} or less.",
1253
- fillInvalidEmail: "Please enter a valid email address.",
1254
- fillInvalidUrl: "Please enter a valid link.",
1255
- fillInvalidDate: "Please enter a valid date.",
1256
- fillInvalidTime: "Please enter a valid time.",
1257
- fillDateTooEarly: "Please pick {min} or later.",
1258
- fillDateTooLate: "Please pick {max} or earlier.",
1259
- fillTooLong: "Please keep it under {max} characters.",
1260
- fillInvalidOption: "Please pick one of the options.",
1261
- fillInvalid: "That does not look right. Please try again."
1262
- };
1263
-
1264
674
  // src/shell/chat-loader.ts
1265
- var CHAT_CDN_BASE = "https://cdn.arcyai.com";
1266
675
  var CHAT_LOAD_TIMEOUT_MS = 15e3;
1267
- function loadChatChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
1268
- return new Promise((resolve) => {
1269
- let settled = false;
1270
- let script = null;
1271
- const settle = (value) => {
1272
- if (settled) return;
1273
- settled = true;
1274
- if (script) {
1275
- script.onload = null;
1276
- script.onerror = null;
1277
- }
1278
- resolve(value);
1279
- };
1280
- let warned = false;
1281
- const complain = (message) => {
1282
- if (warned) return;
1283
- warned = true;
1284
- warn(message);
1285
- };
1286
- const read = () => {
1287
- let registration;
1288
- try {
1289
- registration = win[CHAT_GLOBAL];
1290
- } catch {
1291
- complain(
1292
- "The chat panel registration could not be read. The panel will not open."
1293
- );
1294
- return null;
1295
- }
1296
- if (!registration) return null;
1297
- if (typeof registration.mount !== "function") {
1298
- complain(
1299
- "The chat panel registered something unusable. The panel will not open."
1300
- );
1301
- return null;
1302
- }
1303
- let contract;
1304
- try {
1305
- contract = registration.contract;
1306
- } catch {
1307
- contract = void 0;
1308
- }
1309
- if (contract !== CHAT_CONTRACT) {
1310
- complain(
1311
- `The chat panel on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${CHAT_CONTRACT}. Update arcy.js to open the chat panel.`
1312
- );
1313
- return null;
1314
- }
1315
- return registration;
1316
- };
1317
- const readAndSettle = (whenAbsent) => {
1318
- try {
1319
- const registration = read();
1320
- if (!registration && !warned) whenAbsent();
1321
- settle(registration);
1322
- } catch {
1323
- settle(null);
1324
- }
1325
- };
1326
- let present = false;
1327
- try {
1328
- present = win[CHAT_GLOBAL] !== void 0;
1329
- } catch {
1330
- present = true;
1331
- }
1332
- if (present) {
1333
- readAndSettle(() => {
1334
- });
1335
- return;
1336
- }
1337
- try {
1338
- const doc = win.document;
1339
- script = doc.createElement("script");
1340
- script.async = true;
1341
- script.src = `${cdnBase}/${CHAT_CHUNK_FILE}`;
1342
- const src = script.src;
1343
- script.onload = () => readAndSettle(
1344
- () => warn("The chat panel loaded but did not register. The panel will not open.")
1345
- );
1346
- script.onerror = () => {
1347
- try {
1348
- warn(
1349
- `Failed to load ${src}. The chat panel will not open. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
1350
- );
1351
- } catch {
1352
- }
1353
- settle(null);
1354
- };
1355
- (doc.head || doc.documentElement).appendChild(script);
1356
- } catch (error) {
1357
- warn(`The chat panel could not load. ${String(error)}`);
1358
- settle(null);
1359
- return;
1360
- }
1361
- try {
1362
- setTimeoutImpl(() => {
1363
- if (!settled) {
1364
- warn("The chat panel did not load in time.");
1365
- }
1366
- settle(null);
1367
- }, timeoutMs);
1368
- } catch {
1369
- }
1370
- });
1371
- }
1372
676
  function createChatController(options) {
1373
677
  const {
1374
678
  root,
1375
679
  host,
1376
680
  win = typeof window !== "undefined" ? window : void 0,
1377
- cdnBase = CHAT_CDN_BASE,
681
+ loadChunk,
1378
682
  loadTimeoutMs = CHAT_LOAD_TIMEOUT_MS,
1379
683
  setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
1380
684
  onRequestClose,
@@ -1407,12 +711,7 @@ function createChatController(options) {
1407
711
  loading = (async () => {
1408
712
  if (!win) return;
1409
713
  try {
1410
- const registration = await loadChatChunk(
1411
- win,
1412
- cdnBase,
1413
- loadTimeoutMs,
1414
- setTimeoutImpl
1415
- );
714
+ const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
1416
715
  if (!registration || destroyed) return;
1417
716
  const context = {
1418
717
  root,
@@ -1524,144 +823,12 @@ function createChatController(options) {
1524
823
  };
1525
824
  }
1526
825
 
1527
- // src/flow/flow-contract.ts
1528
- var FLOW_GLOBAL = "__arcyFlow";
1529
- var FLOW_CONTRACT = 2;
1530
-
1531
- // src/flow/state.ts
1532
- var FLOW_KEY_PREFIX = "arcy.flow.";
1533
- function isValid(stored) {
1534
- return typeof stored.flowId === "string" && stored.flowId.length > 0 && typeof stored.currentStepCvid === "string" && stored.currentStepCvid.length > 0 && typeof stored.flowSessionId === "string" && stored.flowSessionId.length > 0 && typeof stored.startedAt === "number";
1535
- }
1536
- function createFlowRunState(token, { store: raw = browserLocalStorage() } = {}) {
1537
- const store = safeStore(raw);
1538
- const key = FLOW_KEY_PREFIX + token;
1539
- return {
1540
- read() {
1541
- const stored = readJson(store, key);
1542
- if (!stored || !isValid(stored)) return null;
1543
- return {
1544
- flowId: stored.flowId,
1545
- currentStepCvid: stored.currentStepCvid,
1546
- flowSessionId: stored.flowSessionId,
1547
- startedAt: stored.startedAt
1548
- };
1549
- },
1550
- save(run) {
1551
- writeJson(store, key, run);
1552
- },
1553
- clear() {
1554
- store.removeItem(key);
1555
- }
1556
- };
1557
- }
1558
-
1559
826
  // src/flow/flow-loader.ts
1560
- var FLOW_CDN_BASE = "https://cdn.arcyai.com";
1561
827
  var FLOW_LOAD_TIMEOUT_MS = 15e3;
1562
- function loadFlowChunk(win, cdnBase, timeoutMs, setTimeoutImpl) {
1563
- return new Promise((resolve) => {
1564
- let settled = false;
1565
- let script = null;
1566
- const settle = (value) => {
1567
- if (settled) return;
1568
- settled = true;
1569
- if (script) {
1570
- script.onload = null;
1571
- script.onerror = null;
1572
- }
1573
- resolve(value);
1574
- };
1575
- let warned = false;
1576
- const complain = (message) => {
1577
- if (warned) return;
1578
- warned = true;
1579
- warn(message);
1580
- };
1581
- const read = () => {
1582
- let registration;
1583
- try {
1584
- registration = win[FLOW_GLOBAL];
1585
- } catch {
1586
- complain("The flow engine registration could not be read. The flow will not start.");
1587
- return null;
1588
- }
1589
- if (!registration) return null;
1590
- if (typeof registration.mount !== "function") {
1591
- complain("The flow engine registered something unusable. The flow will not start.");
1592
- return null;
1593
- }
1594
- let contract;
1595
- try {
1596
- contract = registration.contract;
1597
- } catch {
1598
- contract = void 0;
1599
- }
1600
- if (contract !== FLOW_CONTRACT) {
1601
- complain(
1602
- `The flow engine on the CDN speaks contract ${typeof contract === "number" ? contract : "unknown"}, this arcy.js speaks ${FLOW_CONTRACT}. Update arcy.js to run flows.`
1603
- );
1604
- return null;
1605
- }
1606
- return registration;
1607
- };
1608
- const readAndSettle = (whenAbsent) => {
1609
- try {
1610
- const registration = read();
1611
- if (!registration && !warned) whenAbsent();
1612
- settle(registration);
1613
- } catch {
1614
- settle(null);
1615
- }
1616
- };
1617
- let present = false;
1618
- try {
1619
- present = win[FLOW_GLOBAL] !== void 0;
1620
- } catch {
1621
- present = true;
1622
- }
1623
- if (present) {
1624
- readAndSettle(() => {
1625
- });
1626
- return;
1627
- }
1628
- try {
1629
- const doc = win.document;
1630
- script = doc.createElement("script");
1631
- script.async = true;
1632
- script.src = `${cdnBase}/${FLOW_CHUNK_FILE}`;
1633
- const src = script.src;
1634
- script.onload = () => readAndSettle(
1635
- () => warn("The flow engine loaded but did not register. The flow will not start.")
1636
- );
1637
- script.onerror = () => {
1638
- try {
1639
- warn(
1640
- `Failed to load ${src}. The flow will not start. If this page sets a Content-Security-Policy, it must allow scripts from ${cdnBase}.`
1641
- );
1642
- } catch {
1643
- }
1644
- settle(null);
1645
- };
1646
- (doc.head || doc.documentElement).appendChild(script);
1647
- } catch (error) {
1648
- warn(`The flow engine could not load. ${String(error)}`);
1649
- settle(null);
1650
- return;
1651
- }
1652
- try {
1653
- setTimeoutImpl(() => {
1654
- if (!settled) warn("The flow engine did not load in time.");
1655
- settle(null);
1656
- }, timeoutMs);
1657
- } catch {
1658
- }
1659
- });
1660
- }
1661
828
  function createFlowController(options) {
1662
829
  const {
1663
830
  win = typeof window !== "undefined" ? window : void 0,
1664
- cdnBase = FLOW_CDN_BASE,
831
+ loadChunk,
1665
832
  loadTimeoutMs = FLOW_LOAD_TIMEOUT_MS,
1666
833
  setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
1667
834
  context
@@ -1675,12 +842,7 @@ function createFlowController(options) {
1675
842
  loading = (async () => {
1676
843
  if (!win) return;
1677
844
  try {
1678
- const registration = await loadFlowChunk(
1679
- win,
1680
- cdnBase,
1681
- loadTimeoutMs,
1682
- setTimeoutImpl
1683
- );
845
+ const registration = await loadChunk(win, loadTimeoutMs, setTimeoutImpl);
1684
846
  if (!registration || destroyed) return;
1685
847
  handle = registration.mount({ ...context, doc: win.document, win });
1686
848
  if (!handle) warn("The flow engine could not start.");
@@ -1759,90 +921,6 @@ function createFlowController(options) {
1759
921
  };
1760
922
  }
1761
923
 
1762
- // src/shell/drag.ts
1763
- function carriesFiles(event) {
1764
- const transfer = event.dataTransfer;
1765
- if (!transfer) return false;
1766
- const types = transfer.types;
1767
- if (!types) return false;
1768
- for (let index = 0; index < types.length; index += 1) {
1769
- if (types[index] === "Files") return true;
1770
- }
1771
- return false;
1772
- }
1773
- function droppedFiles(event) {
1774
- const transfer = event.dataTransfer;
1775
- return Array.from(transfer?.files ?? []);
1776
- }
1777
-
1778
- // src/shell/tooltip.ts
1779
- var TOOLTIP_CSS = `
1780
- [data-tip] {
1781
- position: relative;
1782
- }
1783
-
1784
- [data-tip]::after {
1785
- content: attr(data-tip);
1786
- position: absolute;
1787
- left: 50%;
1788
- bottom: calc(100% + 6px);
1789
- transform: translateX(-50%);
1790
- z-index: 2;
1791
- padding: 4px 8px;
1792
- border-radius: 6px;
1793
- background: var(--_arcy-main-text, #101828);
1794
- color: var(--_arcy-main-bg, #ffffff);
1795
- font-size: 11px;
1796
- line-height: 1.3;
1797
- white-space: nowrap;
1798
- pointer-events: none;
1799
- opacity: 0;
1800
- visibility: hidden;
1801
- transition: opacity 120ms ease;
1802
- }
1803
-
1804
- [data-tip][data-tip-placement="below"]::after {
1805
- bottom: auto;
1806
- top: calc(100% + 6px);
1807
- }
1808
-
1809
- /* Alignment, for a control near an edge the panel clips (D895). Centring a
1810
- bubble on the leftmost header icon puts half of it outside the panel, and
1811
- the panel clips its overflow, so the half outside is simply gone. No
1812
- z-index can fix that: the bubble is not behind anything, it is cut off.
1813
- These pin the bubble to the control's own edge instead. */
1814
- [data-tip][data-tip-align="start"]::after {
1815
- left: 0;
1816
- transform: none;
1817
- }
1818
-
1819
- [data-tip][data-tip-align="end"]::after {
1820
- left: auto;
1821
- right: 0;
1822
- transform: none;
1823
- }
1824
-
1825
- [data-tip]:hover::after,
1826
- [data-tip]:focus-visible::after {
1827
- opacity: 1;
1828
- visibility: visible;
1829
- }
1830
-
1831
- /* A coarse pointer has no hover: the tooltip would latch open on tap and
1832
- sit over the thing the visitor just pressed. */
1833
- @media (hover: none) {
1834
- [data-tip]::after {
1835
- display: none;
1836
- }
1837
- }
1838
-
1839
- @media (prefers-reduced-motion: reduce) {
1840
- [data-tip]::after {
1841
- transition: none;
1842
- }
1843
- }
1844
- `;
1845
-
1846
924
  // src/shell/styles.ts
1847
925
  var BAR_CSS = TOOLTIP_CSS + `
1848
926
  .arcy-bar-wrap {
@@ -2209,20 +1287,20 @@ var BAR_CSS = TOOLTIP_CSS + `
2209
1287
  outline-offset: 1px;
2210
1288
  }
2211
1289
 
2212
- /* The attachment count, so a visitor can see what is about to be sent
2213
- without the bar owning the upload. */
2214
- .arcy-bar-badge {
2215
- display: flex;
1290
+ /* The attachment tray. See bar.ts's renderTray(). */
1291
+ .arcy-bar-tray {
1292
+ display: none;
1293
+ flex-wrap: wrap;
2216
1294
  align-items: center;
2217
- justify-content: center;
2218
- flex: 0 0 auto;
2219
- min-width: 20px;
2220
- height: 20px;
2221
- padding: 0 6px;
2222
- border-radius: 999px;
2223
- font-size: 12px;
2224
- background: var(--_arcy-brand-bg, #101828);
2225
- color: var(--_arcy-brand-text, #ffffff);
1295
+ gap: 6px;
1296
+ padding: 10px 4px 2px 4px;
1297
+ min-width: 0;
1298
+ }
1299
+
1300
+ /* A composer holding pictures is two rows, not one. */
1301
+ .arcy-bar[data-attachments="true"] {
1302
+ flex-direction: column;
1303
+ align-items: stretch;
2226
1304
  }
2227
1305
 
2228
1306
  /* \u2500\u2500 Flow-running mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
@@ -2344,58 +1422,6 @@ var BAR_CSS = TOOLTIP_CSS + `
2344
1422
  }
2345
1423
  `;
2346
1424
 
2347
- // src/shell/viewport.ts
2348
- var MOBILE_BREAKPOINT = 640;
2349
- var MOBILE_MARGIN = 12;
2350
- var PANEL_MARGIN = 16;
2351
- var positive = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
2352
- function readSize(win) {
2353
- const el = win?.document?.documentElement;
2354
- return {
2355
- width: positive(el?.clientWidth) || positive(win?.innerWidth),
2356
- height: positive(el?.clientHeight) || positive(win?.innerHeight)
2357
- };
2358
- }
2359
- function keyboardOffset(win) {
2360
- const vv = win?.visualViewport;
2361
- if (!vv) return 0;
2362
- const layoutHeight = readSize(win).height;
2363
- const visualHeight = positive(vv.height);
2364
- if (!layoutHeight || !visualHeight) return 0;
2365
- const top = typeof vv.offsetTop === "number" && Number.isFinite(vv.offsetTop) ? vv.offsetTop : 0;
2366
- const covered = layoutHeight - (visualHeight + top);
2367
- if (!Number.isFinite(covered) || covered < 24) return 0;
2368
- return Math.min(covered, layoutHeight);
2369
- }
2370
- function readViewport(win) {
2371
- const size2 = readSize(win);
2372
- return {
2373
- ...size2,
2374
- isMobile: size2.width > 0 && size2.width < MOBILE_BREAKPOINT,
2375
- keyboardOffset: keyboardOffset(win)
2376
- };
2377
- }
2378
- var FALLBACK_WIDTH_PX = 560;
2379
- function surfaceWidth(widthPercent, viewport) {
2380
- const available = positive(viewport.width);
2381
- if (!available) return FALLBACK_WIDTH_PX;
2382
- if (available < MOBILE_BREAKPOINT) {
2383
- return Math.max(0, available - MOBILE_MARGIN * 2);
2384
- }
2385
- const target = Math.round(widthPercent / 100 * available);
2386
- return Math.min(target, available - PANEL_MARGIN * 2);
2387
- }
2388
- function dragTransform(offset) {
2389
- if (offset.x === 0 && offset.y === 0) return "none";
2390
- return `translate(${offset.x}px, ${offset.y}px)`;
2391
- }
2392
- function clampOffsetX(x, width, viewportWidth) {
2393
- const available = positive(viewportWidth);
2394
- if (!available) return x;
2395
- const maxX = Math.max(0, (available - positive(width)) / 2);
2396
- return Math.min(maxX, Math.max(-maxX, x));
2397
- }
2398
-
2399
1425
  // src/shell/bar.ts
2400
1426
  var MAX_INPUT_LENGTH = 1e4;
2401
1427
  var BAR_HEIGHT = 52;
@@ -2508,7 +1534,7 @@ function createBar(options = {}) {
2508
1534
  let statusStep = null;
2509
1535
  let stop = null;
2510
1536
  let disclaimer = null;
2511
- let badge = null;
1537
+ let tray = null;
2512
1538
  let mode = "idle";
2513
1539
  let flow = null;
2514
1540
  let awaitingInput = false;
@@ -2631,6 +1657,15 @@ function createBar(options = {}) {
2631
1657
  leading.tabIndex = hasMenu ? 0 : -1;
2632
1658
  if (!hasMenu) closeMenu();
2633
1659
  }
1660
+ function renderTray() {
1661
+ if (!tray || !bar) return;
1662
+ const visible = attachEnabled && attachmentCount > 0 && !isCollapsed();
1663
+ const was = bar.getAttribute("data-attachments") === "true";
1664
+ tray.style.setProperty("display", visible ? "flex" : "none");
1665
+ bar.setAttribute("data-attachments", visible ? "true" : "false");
1666
+ if (was === visible) return;
1667
+ safe("onGeometryChange", () => options.onGeometryChange?.())();
1668
+ }
2634
1669
  function setMenuOpen(open) {
2635
1670
  menuOpen = open;
2636
1671
  if (menu) menu.style.setProperty("display", open ? "flex" : "none");
@@ -2689,6 +1724,7 @@ function createBar(options = {}) {
2689
1724
  );
2690
1725
  bar.setAttribute("data-collapsed", isCollapsed() ? "true" : "false");
2691
1726
  renderLeading();
1727
+ renderTray();
2692
1728
  render();
2693
1729
  form.style.setProperty("display", effective === "flow" ? "none" : "flex");
2694
1730
  status.style.setProperty(
@@ -2894,9 +1930,9 @@ function createBar(options = {}) {
2894
1930
  input.maxLength = MAX_INPUT_LENGTH;
2895
1931
  input.setAttribute("aria-label", labels.openChat);
2896
1932
  input.autocomplete = "off";
2897
- badge = doc.createElement("span");
2898
- badge.className = "arcy-bar-badge";
2899
- badge.style.setProperty("display", "none");
1933
+ tray = doc.createElement("div");
1934
+ tray.className = BAR_TRAY_CLASS;
1935
+ tray.style.setProperty("display", "none");
2900
1936
  grip = doc.createElement("button");
2901
1937
  grip.type = "button";
2902
1938
  grip.className = "arcy-bar-grip";
@@ -2937,7 +1973,6 @@ function createBar(options = {}) {
2937
1973
  form.appendChild(grip);
2938
1974
  form.appendChild(leading);
2939
1975
  form.appendChild(input);
2940
- form.appendChild(badge);
2941
1976
  form.appendChild(submit);
2942
1977
  status = doc.createElement("div");
2943
1978
  status.className = "arcy-bar-status";
@@ -2958,6 +1993,7 @@ function createBar(options = {}) {
2958
1993
  status.appendChild(statusName);
2959
1994
  status.appendChild(statusStep);
2960
1995
  status.appendChild(stop);
1996
+ bar.appendChild(tray);
2961
1997
  bar.appendChild(form);
2962
1998
  bar.appendChild(status);
2963
1999
  bar.appendChild(menu);
@@ -3045,7 +2081,7 @@ function createBar(options = {}) {
3045
2081
  dock = null;
3046
2082
  dockIcon = null;
3047
2083
  menu = null;
3048
- badge = null;
2084
+ tray = null;
3049
2085
  menuOpen = false;
3050
2086
  flow = null;
3051
2087
  mode = "idle";
@@ -3201,13 +2237,11 @@ function createBar(options = {}) {
3201
2237
  setAttachmentCount(count) {
3202
2238
  attachmentCount = count;
3203
2239
  try {
3204
- if (!badge) return;
3205
- const visible = attachEnabled && count > 0;
3206
- badge.textContent = visible ? String(count) : "";
3207
- badge.style.setProperty("display", visible ? "flex" : "none");
2240
+ renderTray();
3208
2241
  } catch {
3209
2242
  }
3210
2243
  },
2244
+ attachmentSlot: () => tray,
3211
2245
  focusInput() {
3212
2246
  try {
3213
2247
  if (shell) releaseHostFocus(shell.host.ownerDocument, shell.host);
@@ -3432,93 +2466,6 @@ function createTokenCache(token, raw = browserLocalStorage()) {
3432
2466
  };
3433
2467
  }
3434
2468
 
3435
- // src/shell/state.ts
3436
- var SHELL_KEY_PREFIX = "arcy.shell.";
3437
- var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
3438
- var LOCALE_PATTERN = /^[a-z]{2}-[A-Z]{2}$/;
3439
- function readPosition(value) {
3440
- if (!value || typeof value !== "object") return null;
3441
- const { x, y } = value;
3442
- if (typeof x !== "number" || typeof y !== "number") return null;
3443
- if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
3444
- return { x, y };
3445
- }
3446
- function uuidV4(fill) {
3447
- const source = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
3448
- if (!fill && source && typeof source.randomUUID === "function") {
3449
- try {
3450
- const native = source.randomUUID();
3451
- if (UUID_PATTERN.test(native)) return native;
3452
- } catch {
3453
- }
3454
- }
3455
- const bytes = new Uint8Array(16);
3456
- if (fill) {
3457
- fill(bytes);
3458
- } else if (source && typeof source.getRandomValues === "function") {
3459
- source.getRandomValues(bytes);
3460
- } else {
3461
- for (let i = 0; i < bytes.length; i++) {
3462
- bytes[i] = Math.floor(Math.random() * 256);
3463
- }
3464
- }
3465
- bytes[6] = (bytes[6] ?? 0) & 15 | 64;
3466
- bytes[8] = (bytes[8] ?? 0) & 63 | 128;
3467
- let hex = "";
3468
- for (let i = 0; i < bytes.length; i++) {
3469
- hex += (bytes[i] ?? 0).toString(16).padStart(2, "0");
3470
- }
3471
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
3472
- }
3473
- function createShellState(token, { store: raw = browserLocalStorage(), fill } = {}) {
3474
- const store = safeStore(raw);
3475
- const key = SHELL_KEY_PREFIX + token;
3476
- function readRaw() {
3477
- return readJson(store, key) ?? {};
3478
- }
3479
- function patch(next) {
3480
- writeJson(store, key, { ...readRaw(), ...next });
3481
- }
3482
- function read() {
3483
- const stored = readRaw();
3484
- const id = stored.anonymousId;
3485
- return {
3486
- anonymousId: typeof id === "string" && UUID_PATTERN.test(id) ? id : null,
3487
- position: readPosition(stored.position),
3488
- // Anything but a literal `true` means not docked. The value is in the
3489
- // visitor's own storage, so a truthy string written by an older build
3490
- // must not be able to hide the widget.
3491
- docked: stored.docked === true,
3492
- locale: typeof stored.locale === "string" && LOCALE_PATTERN.test(stored.locale) ? stored.locale : null
3493
- };
3494
- }
3495
- return {
3496
- read,
3497
- anonymousId() {
3498
- const existing = read().anonymousId;
3499
- if (existing) return existing;
3500
- patch({ anonymousId: uuidV4(fill) });
3501
- return read().anonymousId;
3502
- },
3503
- rememberPosition(position) {
3504
- patch({ position });
3505
- },
3506
- rememberDocked(docked) {
3507
- patch({ docked });
3508
- },
3509
- rememberLocale(locale) {
3510
- patch({ locale });
3511
- },
3512
- forgetAnonymousId() {
3513
- const current = readRaw();
3514
- if (current.anonymousId === void 0) return;
3515
- delete current.anonymousId;
3516
- if (Object.keys(current).length === 0) store.removeItem(key);
3517
- else writeJson(store, key, current);
3518
- }
3519
- };
3520
- }
3521
-
3522
2469
  // src/session/visit.ts
3523
2470
  var VISIT_KEY_PREFIX = "arcy.visit.";
3524
2471
  var FIRST_VISIT_VALUE = "first";
@@ -3610,6 +2557,97 @@ function createConversationStore(token, { store: raw = browserLocalStorage(), fi
3610
2557
  };
3611
2558
  }
3612
2559
 
2560
+ // src/session/context.ts
2561
+ var CAMPAIGN_KEYS = [
2562
+ ["utm_source", "utmSource"],
2563
+ ["utm_medium", "utmMedium"],
2564
+ ["utm_campaign", "utmCampaign"],
2565
+ ["utm_content", "utmContent"],
2566
+ ["utm_term", "utmTerm"]
2567
+ ];
2568
+ var MAX_CAMPAIGN_LENGTH = 255;
2569
+ var MAX_REFERRER_LENGTH = 512;
2570
+ var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
2571
+ function clean(value, max) {
2572
+ const trimmed = value.replace(CONTROL_CHARACTERS, "").trim();
2573
+ if (trimmed.length === 0) return void 0;
2574
+ return trimmed.slice(0, max);
2575
+ }
2576
+ function scrubReferrer(referrer) {
2577
+ if (typeof referrer !== "string" || referrer.length === 0) return void 0;
2578
+ try {
2579
+ const url = new URL(referrer);
2580
+ const host = url.host;
2581
+ if (!host) return void 0;
2582
+ const path = scrubPath(url.pathname);
2583
+ return clean(path === "/" ? host : host + path, MAX_REFERRER_LENGTH);
2584
+ } catch {
2585
+ return void 0;
2586
+ }
2587
+ }
2588
+ function readCampaign(search) {
2589
+ const out = {};
2590
+ if (typeof search !== "string" || search.length === 0) return out;
2591
+ const query = search.charAt(0) === "?" ? search.slice(1) : search;
2592
+ for (const pair of query.split("&")) {
2593
+ const eq = pair.indexOf("=");
2594
+ if (eq <= 0) continue;
2595
+ const rawKey = pair.slice(0, eq).toLowerCase();
2596
+ const match = CAMPAIGN_KEYS.find((entry) => entry[0] === rawKey);
2597
+ if (!match) continue;
2598
+ let value = pair.slice(eq + 1).replace(/\+/g, " ");
2599
+ try {
2600
+ value = decodeURIComponent(value);
2601
+ } catch {
2602
+ }
2603
+ const cleaned = clean(value, MAX_CAMPAIGN_LENGTH);
2604
+ if (cleaned) out[match[1]] = cleaned;
2605
+ }
2606
+ return out;
2607
+ }
2608
+ function readBrowser(userAgent) {
2609
+ const ua = typeof userAgent === "string" ? userAgent : "";
2610
+ if (ua.length === 0) return void 0;
2611
+ if (/Edg[A-Z]?\//.test(ua)) return "Edge";
2612
+ if (/OPR\/|Opera/.test(ua)) return "Opera";
2613
+ if (/SamsungBrowser\//.test(ua)) return "Samsung Internet";
2614
+ if (/Firefox\/|FxiOS\//.test(ua)) return "Firefox";
2615
+ if (/Chrome\/|CriOS\//.test(ua)) return "Chrome";
2616
+ if (/Safari\//.test(ua)) return "Safari";
2617
+ return void 0;
2618
+ }
2619
+ function readOs(userAgent) {
2620
+ const ua = typeof userAgent === "string" ? userAgent : "";
2621
+ if (ua.length === 0) return void 0;
2622
+ if (/iPhone|iPad|iPod/.test(ua)) return "iOS";
2623
+ if (/Android/.test(ua)) return "Android";
2624
+ if (/CrOS/.test(ua)) return "ChromeOS";
2625
+ if (/Windows/.test(ua)) return "Windows";
2626
+ if (/Mac OS X|Macintosh/.test(ua)) return "macOS";
2627
+ if (/Linux/.test(ua)) return "Linux";
2628
+ return void 0;
2629
+ }
2630
+ function viewportBucket(width) {
2631
+ if (typeof width !== "number" || !(width > 0)) return void 0;
2632
+ if (width < 480) return "xs";
2633
+ if (width < 768) return "sm";
2634
+ if (width < 1024) return "md";
2635
+ if (width < 1280) return "lg";
2636
+ return "xl";
2637
+ }
2638
+ function captureSessionContext(input) {
2639
+ const context = { ...readCampaign(input.search) };
2640
+ const referrer = scrubReferrer(input.referrer);
2641
+ if (referrer) context.referrer = referrer;
2642
+ const browser = readBrowser(input.userAgent);
2643
+ if (browser) context.browser = browser;
2644
+ const os = readOs(input.userAgent);
2645
+ if (os) context.os = os;
2646
+ const viewport = viewportBucket(input.viewportWidth);
2647
+ if (viewport) context.viewport = viewport;
2648
+ return Object.keys(context).length > 0 ? context : void 0;
2649
+ }
2650
+
3613
2651
  // src/telemetry/consent.ts
3614
2652
  var STATES = ["ON", "REQUIRE_CONSENT", "OFF"];
3615
2653
  function createConsentCache(store, key) {
@@ -3666,8 +2704,14 @@ function deviceClass(nav) {
3666
2704
 
3667
2705
  // src/telemetry/describe.ts
3668
2706
  var MAX_TEXT_LENGTH = 100;
2707
+ var PII_SCAN_MAX = 4096;
3669
2708
  var VALUE_BEARING = ["INPUT", "TEXTAREA", "SELECT"];
3670
- function describeClick(element, describeElement) {
2709
+ function readableCopy(raw) {
2710
+ if (raw.length === 0) return void 0;
2711
+ if (containsPii(raw.slice(0, PII_SCAN_MAX))) return void 0;
2712
+ return raw.slice(0, MAX_TEXT_LENGTH);
2713
+ }
2714
+ function describeElementPayload(element, describeElement) {
3671
2715
  const tag = (element.tagName ?? "").toUpperCase();
3672
2716
  const payload = { tag: tag.toLowerCase() };
3673
2717
  const role = element.getAttribute("role");
@@ -3676,10 +2720,12 @@ function describeClick(element, describeElement) {
3676
2720
  if (type) payload.type = type.slice(0, 32);
3677
2721
  if (VALUE_BEARING.indexOf(tag) === -1) {
3678
2722
  const text = (element.textContent ?? "").replace(/\s+/g, " ").trim();
3679
- if (text) payload.text = text.slice(0, MAX_TEXT_LENGTH);
2723
+ const readable = readableCopy(text);
2724
+ if (readable) payload.text = readable;
3680
2725
  } else {
3681
2726
  const label = element.getAttribute("aria-label") ?? element.getAttribute("name");
3682
- if (label) payload.label = label.slice(0, MAX_TEXT_LENGTH);
2727
+ const readable = readableCopy(label ?? "");
2728
+ if (readable) payload.label = readable;
3683
2729
  }
3684
2730
  if (describeElement) {
3685
2731
  const fingerprint = describeElement(element);
@@ -3689,8 +2735,17 @@ function describeClick(element, describeElement) {
3689
2735
  }
3690
2736
  return payload;
3691
2737
  }
3692
- function describeSubmit(form) {
3693
- const payload = {};
2738
+ function describeClick(element, describeElement) {
2739
+ return describeElementPayload(element, describeElement);
2740
+ }
2741
+ function describeField(element, describeElement) {
2742
+ const payload = describeElementPayload(element, describeElement);
2743
+ const valid = element.validity?.valid;
2744
+ if (typeof valid === "boolean") payload.valid = valid;
2745
+ return payload;
2746
+ }
2747
+ function describeSubmit(form, describeElement) {
2748
+ const payload = describeElementPayload(form, describeElement);
3694
2749
  const name = form.getAttribute("name") ?? form.getAttribute("id");
3695
2750
  if (name) payload.form = name.slice(0, MAX_TEXT_LENGTH);
3696
2751
  const count = form.elements?.length;
@@ -3698,6 +2753,169 @@ function describeSubmit(form) {
3698
2753
  return payload;
3699
2754
  }
3700
2755
 
2756
+ // src/telemetry/errors.ts
2757
+ var REQUEST_METHODS = [
2758
+ "GET",
2759
+ "POST",
2760
+ "PUT",
2761
+ "PATCH",
2762
+ "DELETE",
2763
+ "HEAD",
2764
+ "OPTIONS"
2765
+ ];
2766
+ var OTHER_METHOD = "OTHER";
2767
+ var FAILED_STATUS_MIN = 400;
2768
+ var ERROR_MESSAGE_MAX = 200;
2769
+ var CONTROL_CHARACTERS2 = /[\u0000-\u001f\u007f]+/g;
2770
+ function readErrorMessage(raw) {
2771
+ let text = null;
2772
+ if (typeof raw === "string") text = raw;
2773
+ else if (raw && typeof raw === "object") {
2774
+ const message = raw.message;
2775
+ if (typeof message === "string") text = message;
2776
+ }
2777
+ if (text === null) return null;
2778
+ const flattened = text.replace(CONTROL_CHARACTERS2, " ").trim();
2779
+ if (flattened.length === 0) return null;
2780
+ return flattened.length > ERROR_MESSAGE_MAX ? flattened.slice(0, ERROR_MESSAGE_MAX) : flattened;
2781
+ }
2782
+ function normalizeMethod(raw) {
2783
+ if (typeof raw !== "string") return "GET";
2784
+ const upper = raw.toUpperCase();
2785
+ return REQUEST_METHODS.indexOf(upper) === -1 ? OTHER_METHOD : upper;
2786
+ }
2787
+ function isFailedStatus(status) {
2788
+ return typeof status === "number" && status >= FAILED_STATUS_MIN;
2789
+ }
2790
+ function installNetworkWatch(options) {
2791
+ const { target, report, ignore } = options;
2792
+ const restores = [];
2793
+ function emit(method, status, raw) {
2794
+ try {
2795
+ if (!isFailedStatus(status)) return;
2796
+ if (raw !== null && ignore?.(raw)) return;
2797
+ report({
2798
+ method,
2799
+ status,
2800
+ url: raw === null ? null : normalizeRoute(raw, target.location?.href)
2801
+ });
2802
+ } catch {
2803
+ }
2804
+ }
2805
+ function installFetch() {
2806
+ const original = target.fetch;
2807
+ if (typeof original !== "function") return;
2808
+ const wrapper = function(...args) {
2809
+ const result = original.apply(this, args);
2810
+ try {
2811
+ const promise = result;
2812
+ if (typeof promise?.then !== "function") return result;
2813
+ promise.then(
2814
+ (response) => {
2815
+ const status = response?.status;
2816
+ emit(fetchMethod(args), status, fetchUrl(args));
2817
+ },
2818
+ () => {
2819
+ }
2820
+ );
2821
+ } catch {
2822
+ }
2823
+ return result;
2824
+ };
2825
+ target.fetch = wrapper;
2826
+ restores.push(() => {
2827
+ if (target.fetch === wrapper) target.fetch = original;
2828
+ });
2829
+ }
2830
+ function installXhr() {
2831
+ const proto = target.XMLHttpRequest?.prototype;
2832
+ const originalOpen = proto?.open;
2833
+ const originalSend = proto?.send;
2834
+ if (!proto || typeof originalOpen !== "function" || typeof originalSend !== "function") {
2835
+ return;
2836
+ }
2837
+ if (typeof WeakMap !== "function") return;
2838
+ const pending = /* @__PURE__ */ new WeakMap();
2839
+ const open = function(...args) {
2840
+ const result = originalOpen.apply(this, args);
2841
+ try {
2842
+ pending.set(this, {
2843
+ method: normalizeMethod(args[0]),
2844
+ url: typeof args[1] === "string" ? args[1] : null,
2845
+ listening: pending.get(this)?.listening ?? false
2846
+ });
2847
+ } catch {
2848
+ }
2849
+ return result;
2850
+ };
2851
+ const send = function(...args) {
2852
+ try {
2853
+ const xhr = this;
2854
+ const record = pending.get(this);
2855
+ if (typeof xhr.addEventListener === "function" && record?.listening !== true) {
2856
+ if (record) record.listening = true;
2857
+ xhr.addEventListener("loadend", () => {
2858
+ const current = pending.get(this);
2859
+ emit(current?.method ?? "GET", xhr.status, current?.url ?? null);
2860
+ });
2861
+ }
2862
+ } catch {
2863
+ }
2864
+ return originalSend.apply(this, args);
2865
+ };
2866
+ proto.open = open;
2867
+ restores.push(() => {
2868
+ if (proto.open === open) proto.open = originalOpen;
2869
+ });
2870
+ proto.send = send;
2871
+ restores.push(() => {
2872
+ if (proto.send === send) proto.send = originalSend;
2873
+ });
2874
+ }
2875
+ try {
2876
+ installFetch();
2877
+ installXhr();
2878
+ } catch {
2879
+ for (const restore of restores) {
2880
+ try {
2881
+ restore();
2882
+ } catch {
2883
+ }
2884
+ }
2885
+ return null;
2886
+ }
2887
+ if (restores.length === 0) return null;
2888
+ return () => {
2889
+ for (const restore of restores) {
2890
+ try {
2891
+ restore();
2892
+ } catch {
2893
+ }
2894
+ }
2895
+ restores.length = 0;
2896
+ };
2897
+ }
2898
+ function fetchMethod(args) {
2899
+ const init = args[1];
2900
+ if (init && typeof init === "object" && typeof init.method === "string") {
2901
+ return normalizeMethod(init.method);
2902
+ }
2903
+ const input = args[0];
2904
+ if (input && typeof input === "object" && typeof input.method === "string") {
2905
+ return normalizeMethod(input.method);
2906
+ }
2907
+ return "GET";
2908
+ }
2909
+ function fetchUrl(args) {
2910
+ const input = args[0];
2911
+ if (typeof input === "string") return input;
2912
+ if (!input || typeof input !== "object") return null;
2913
+ const url = input.url;
2914
+ if (typeof url === "string") return url;
2915
+ const href = input.href;
2916
+ return typeof href === "string" ? href : null;
2917
+ }
2918
+
3701
2919
  // src/telemetry/events.ts
3702
2920
  function buildEvent(id, name, occurredAt, context, payload) {
3703
2921
  const event = {
@@ -3716,6 +2934,63 @@ function buildEvent(id, name, occurredAt, context, payload) {
3716
2934
  return event;
3717
2935
  }
3718
2936
 
2937
+ // src/telemetry/fields.ts
2938
+ var NON_FIELD_TYPES = [
2939
+ "button",
2940
+ "submit",
2941
+ "reset",
2942
+ "image",
2943
+ "hidden",
2944
+ "checkbox",
2945
+ "radio"
2946
+ ];
2947
+ var FIELD_TAGS = ["INPUT", "TEXTAREA", "SELECT"];
2948
+ function isFieldElement(element) {
2949
+ if (!element || typeof element.getAttribute !== "function") return false;
2950
+ const tag = (element.tagName ?? "").toUpperCase();
2951
+ if (FIELD_TAGS.indexOf(tag) === -1) return false;
2952
+ if (tag !== "INPUT") return true;
2953
+ const type = (element.getAttribute("type") ?? "text").toLowerCase();
2954
+ return NON_FIELD_TYPES.indexOf(type) === -1;
2955
+ }
2956
+ function isEmpty(element) {
2957
+ const value = element.value;
2958
+ return typeof value === "string" ? value.length === 0 : value == null;
2959
+ }
2960
+ function createFieldObserver(options) {
2961
+ let active = null;
2962
+ return {
2963
+ focus(element) {
2964
+ if (!isFieldElement(element)) return;
2965
+ active = {
2966
+ element,
2967
+ empty: isEmpty(element),
2968
+ filled: false,
2969
+ cleared: false
2970
+ };
2971
+ options.emit("field_focused", element);
2972
+ },
2973
+ change(element) {
2974
+ if (!active || active.element !== element) return;
2975
+ const empty = isEmpty(element);
2976
+ if (empty === active.empty) return;
2977
+ active.empty = empty;
2978
+ if (!empty) {
2979
+ if (active.filled) return;
2980
+ active.filled = true;
2981
+ options.emit("input_filled", element);
2982
+ return;
2983
+ }
2984
+ if (active.cleared) return;
2985
+ active.cleared = true;
2986
+ options.emit("input_cleared", element);
2987
+ },
2988
+ blur(element) {
2989
+ if (active && active.element === element) active = null;
2990
+ }
2991
+ };
2992
+ }
2993
+
3719
2994
  // src/telemetry/interactive.ts
3720
2995
  var INTERACTIVE_TAGS = [
3721
2996
  "A",
@@ -3766,6 +3041,78 @@ function findInteractive(target, cursorOf) {
3766
3041
  return null;
3767
3042
  }
3768
3043
 
3044
+ // src/telemetry/scroll.ts
3045
+ var SCROLL_MILESTONES = [25, 50, 75, 100];
3046
+ var SCROLL_THROTTLE_MS = 250;
3047
+ var FINAL_MILESTONE = 100;
3048
+ function createScrollObserver(options) {
3049
+ const {
3050
+ readDepth,
3051
+ emit,
3052
+ setTimeoutImpl = (fn, ms) => setTimeout(fn, ms),
3053
+ clearTimeoutImpl = (handle) => clearTimeout(handle),
3054
+ throttleMs = SCROLL_THROTTLE_MS
3055
+ } = options;
3056
+ let reported = 0;
3057
+ let pending = null;
3058
+ function evaluate() {
3059
+ pending = null;
3060
+ let depth = null;
3061
+ try {
3062
+ depth = readDepth();
3063
+ } catch {
3064
+ return;
3065
+ }
3066
+ if (depth === null) return;
3067
+ let reached = 0;
3068
+ for (const milestone of SCROLL_MILESTONES) {
3069
+ if (depth >= milestone) reached = milestone;
3070
+ }
3071
+ if (reached <= reported) return;
3072
+ reported = reached;
3073
+ emit(reached);
3074
+ }
3075
+ return {
3076
+ scrolled() {
3077
+ if (pending !== null) return;
3078
+ if (reported >= FINAL_MILESTONE) return;
3079
+ try {
3080
+ pending = setTimeoutImpl(evaluate, throttleMs);
3081
+ } catch {
3082
+ pending = null;
3083
+ }
3084
+ },
3085
+ reset() {
3086
+ reported = 0;
3087
+ if (pending !== null) {
3088
+ try {
3089
+ clearTimeoutImpl(pending);
3090
+ } catch {
3091
+ }
3092
+ pending = null;
3093
+ }
3094
+ },
3095
+ dispose() {
3096
+ if (pending === null) return;
3097
+ try {
3098
+ clearTimeoutImpl(pending);
3099
+ } catch {
3100
+ }
3101
+ pending = null;
3102
+ }
3103
+ };
3104
+ }
3105
+ function scrollDepthPercent(metrics) {
3106
+ const viewport = metrics.innerHeight ?? 0;
3107
+ const total = metrics.documentHeight ?? 0;
3108
+ const scrollable = total - viewport;
3109
+ if (!(scrollable > 0) || !(viewport > 0)) return null;
3110
+ const scrolled = (metrics.scrollY ?? 0) + viewport;
3111
+ const percent = scrolled / total * 100;
3112
+ if (!(percent > 0)) return 0;
3113
+ return percent > 100 ? 100 : percent;
3114
+ }
3115
+
3769
3116
  // src/telemetry/session.ts
3770
3117
  var SESSION_WINDOW_MS = 30 * 60 * 1e3;
3771
3118
  var TOUCH_WRITE_INTERVAL_MS = 60 * 1e3;
@@ -4069,7 +3416,7 @@ function createTelemetry(options) {
4069
3416
  function currentRoute() {
4070
3417
  return normalizeRoute(win?.location?.href) ?? void 0;
4071
3418
  }
4072
- function capture(name, payload, fields) {
3419
+ function capture(name, payload, fields2) {
4073
3420
  if (halted) return false;
4074
3421
  if (!consent.shouldCapture()) return false;
4075
3422
  if (transport.stopped() !== null) return false;
@@ -4086,13 +3433,13 @@ function createTelemetry(options) {
4086
3433
  route: currentRoute(),
4087
3434
  localeCode,
4088
3435
  deviceClass: resolvedDeviceClass,
4089
- flowSessionId: fields?.flowSessionId ?? getFlowSessionId?.() ?? void 0
3436
+ flowSessionId: fields2?.flowSessionId ?? getFlowSessionId?.() ?? void 0
4090
3437
  },
4091
3438
  payload
4092
3439
  );
4093
- if (fields?.flowId) event.flowId = fields.flowId;
4094
- if (fields?.flowVersion) event.flowVersion = fields.flowVersion;
4095
- if (fields?.stepCvid) event.stepCvid = fields.stepCvid;
3440
+ if (fields2?.flowId) event.flowId = fields2.flowId;
3441
+ if (fields2?.flowVersion) event.flowVersion = fields2.flowVersion;
3442
+ if (fields2?.stepCvid) event.stepCvid = fields2.stepCvid;
4096
3443
  transport.enqueue(event);
4097
3444
  return true;
4098
3445
  }
@@ -4118,7 +3465,7 @@ function createTelemetry(options) {
4118
3465
  const raw = event;
4119
3466
  if (isSynthetic(raw)) return;
4120
3467
  if (!raw.target || typeof raw.target.getAttribute !== "function") return;
4121
- capture("form_submit", describeSubmit(raw.target));
3468
+ capture("form_submit", describeSubmit(raw.target, describeElement));
4122
3469
  } catch {
4123
3470
  }
4124
3471
  };
@@ -4128,16 +3475,118 @@ function createTelemetry(options) {
4128
3475
  if (route === lastRoute) return;
4129
3476
  const from = lastRoute;
4130
3477
  lastRoute = route;
3478
+ scroll.reset();
4131
3479
  capture("navigation", from ? { from } : void 0);
4132
3480
  } catch {
4133
3481
  }
4134
3482
  };
3483
+ const fields = createFieldObserver({
3484
+ emit: (name, element) => {
3485
+ capture(name, describeField(element, describeElement));
3486
+ }
3487
+ });
3488
+ const scroll = createScrollObserver({
3489
+ readDepth: () => scrollDepthPercent({
3490
+ scrollY: win?.scrollY,
3491
+ innerHeight: win?.innerHeight,
3492
+ documentHeight: doc?.documentElement?.scrollHeight
3493
+ }),
3494
+ emit: (percent) => {
3495
+ capture("scroll_depth", { percent });
3496
+ },
3497
+ setTimeoutImpl: options.setTimeoutImpl,
3498
+ clearTimeoutImpl: options.clearTimeoutImpl
3499
+ });
3500
+ let sessionEnded = false;
3501
+ const onFocusIn = (event) => {
3502
+ try {
3503
+ const raw = event;
3504
+ if (isSynthetic(raw) || !raw.target) return;
3505
+ fields.focus(raw.target);
3506
+ } catch {
3507
+ }
3508
+ };
3509
+ const onFieldChange = (event) => {
3510
+ try {
3511
+ const raw = event;
3512
+ if (isSynthetic(raw) || !raw.target) return;
3513
+ fields.change(raw.target);
3514
+ } catch {
3515
+ }
3516
+ };
3517
+ const onFocusOut = (event) => {
3518
+ try {
3519
+ const raw = event;
3520
+ if (!raw.target) return;
3521
+ fields.blur(raw.target);
3522
+ } catch {
3523
+ }
3524
+ };
3525
+ const onScroll = () => {
3526
+ try {
3527
+ scroll.scrolled();
3528
+ } catch {
3529
+ }
3530
+ };
3531
+ const onVisibilityChange = () => {
3532
+ try {
3533
+ if (doc?.visibilityState === "visible") {
3534
+ capture("page_visible");
3535
+ return;
3536
+ }
3537
+ capture("page_hidden");
3538
+ void transport.flush(true);
3539
+ } catch {
3540
+ }
3541
+ };
4135
3542
  const onPageHide = () => {
4136
3543
  try {
3544
+ if (!sessionEnded) {
3545
+ sessionEnded = true;
3546
+ capture("session_end");
3547
+ }
4137
3548
  void transport.flush(true);
4138
3549
  } catch {
4139
3550
  }
4140
3551
  };
3552
+ const onError = (event) => {
3553
+ try {
3554
+ const raw = event;
3555
+ captureError({
3556
+ message: readErrorMessage(raw.message) ?? "Uncaught error",
3557
+ kind: "error",
3558
+ source: scrubSource(raw.filename)
3559
+ });
3560
+ } catch {
3561
+ }
3562
+ };
3563
+ const onRejection = (event) => {
3564
+ try {
3565
+ const raw = event;
3566
+ captureError({
3567
+ // The kind alone when the reason is neither a string nor an `Error`.
3568
+ // Serializing an unknown object from the customer's application is the
3569
+ // one thing this event must never do.
3570
+ message: readErrorMessage(raw.reason) ?? "Unhandled promise rejection",
3571
+ kind: "unhandledrejection"
3572
+ });
3573
+ } catch {
3574
+ }
3575
+ };
3576
+ function captureError(encounter) {
3577
+ const payload = {
3578
+ message: encounter.message,
3579
+ kind: encounter.kind
3580
+ };
3581
+ if (encounter.source) payload.source = encounter.source;
3582
+ capture("js_error", payload);
3583
+ }
3584
+ function scrubSource(raw) {
3585
+ if (typeof raw !== "string") return void 0;
3586
+ return normalizeRoute(raw, win?.location?.href) ?? void 0;
3587
+ }
3588
+ const ownOrigin = (apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
3589
+ let uninstallNetworkWatch = null;
4141
3590
  return {
4142
3591
  start() {
4143
3592
  if (started || halted) return;
@@ -4145,10 +3594,31 @@ function createTelemetry(options) {
4145
3594
  lastRoute = currentRoute() ?? null;
4146
3595
  doc?.addEventListener("click", onClick, true);
4147
3596
  doc?.addEventListener("submit", onSubmit, true);
3597
+ doc?.addEventListener("focusin", onFocusIn, true);
3598
+ doc?.addEventListener("focusout", onFocusOut, true);
3599
+ doc?.addEventListener("input", onFieldChange, true);
3600
+ doc?.addEventListener("change", onFieldChange, true);
4148
3601
  win?.addEventListener(ROUTE_CHANGE_EVENT, onNavigate);
4149
3602
  win?.addEventListener("popstate", onNavigate);
3603
+ win?.addEventListener("scroll", onScroll, { passive: true });
4150
3604
  win?.addEventListener("pagehide", onPageHide);
4151
- doc?.addEventListener("visibilitychange", onPageHide);
3605
+ doc?.addEventListener("visibilitychange", onVisibilityChange);
3606
+ win?.addEventListener("error", onError);
3607
+ win?.addEventListener("unhandledrejection", onRejection);
3608
+ if (win && uninstallNetworkWatch === null) {
3609
+ uninstallNetworkWatch = installNetworkWatch({
3610
+ target: win,
3611
+ report: (failure) => {
3612
+ const payload = {
3613
+ method: failure.method,
3614
+ status: failure.status
3615
+ };
3616
+ if (failure.url) payload.url = failure.url;
3617
+ capture("request_failed", payload);
3618
+ },
3619
+ ignore: (url) => url.indexOf(ownOrigin) === 0
3620
+ });
3621
+ }
4152
3622
  loadNavigationPending = !capture("navigation") && consent.state() === null;
4153
3623
  },
4154
3624
  stop(options2) {
@@ -4157,10 +3627,22 @@ function createTelemetry(options) {
4157
3627
  started = false;
4158
3628
  doc?.removeEventListener("click", onClick, true);
4159
3629
  doc?.removeEventListener("submit", onSubmit, true);
3630
+ doc?.removeEventListener("focusin", onFocusIn, true);
3631
+ doc?.removeEventListener("focusout", onFocusOut, true);
3632
+ doc?.removeEventListener("input", onFieldChange, true);
3633
+ doc?.removeEventListener("change", onFieldChange, true);
4160
3634
  win?.removeEventListener(ROUTE_CHANGE_EVENT, onNavigate);
4161
3635
  win?.removeEventListener("popstate", onNavigate);
3636
+ win?.removeEventListener("scroll", onScroll);
4162
3637
  win?.removeEventListener("pagehide", onPageHide);
4163
- doc?.removeEventListener("visibilitychange", onPageHide);
3638
+ doc?.removeEventListener("visibilitychange", onVisibilityChange);
3639
+ win?.removeEventListener("error", onError);
3640
+ win?.removeEventListener("unhandledrejection", onRejection);
3641
+ if (uninstallNetworkWatch) {
3642
+ uninstallNetworkWatch();
3643
+ uninstallNetworkWatch = null;
3644
+ }
3645
+ scroll.dispose();
4164
3646
  if (options2?.discard) transport.discard();
4165
3647
  else void transport.flush(false);
4166
3648
  transport.dispose();
@@ -4176,8 +3658,8 @@ function createTelemetry(options) {
4176
3658
  consent.setConsent(granted);
4177
3659
  if (!granted) transport.discard();
4178
3660
  },
4179
- track(name, payload, fields) {
4180
- capture(name, payload, fields);
3661
+ track(name, payload, fields2) {
3662
+ capture(name, payload, fields2);
4181
3663
  },
4182
3664
  flush: () => transport.flush(false),
4183
3665
  sessionId: () => session.touch(),
@@ -4188,14 +3670,15 @@ function createTelemetry(options) {
4188
3670
  }
4189
3671
 
4190
3672
  // src/version.ts
4191
- var VERSION = "0.1.0";
3673
+ var VERSION = "0.1.1";
4192
3674
 
4193
3675
  // src/api.ts
4194
3676
  var OPTION_KEYS = [
4195
3677
  "telemetry",
4196
3678
  "locale",
4197
3679
  "defaultOpen",
4198
- "contentLocale"
3680
+ "contentLocale",
3681
+ "apiBase"
4199
3682
  ];
4200
3683
  function emptyIdentity() {
4201
3684
  return { userId: null, isAnonymous: false, attributes: {} };
@@ -4217,6 +3700,7 @@ function sanitizeAttributes(attributes, method) {
4217
3700
  }
4218
3701
  const out = {};
4219
3702
  for (const [key, value] of Object.entries(attributes)) {
3703
+ if (value === void 0) continue;
4220
3704
  if (value === null) {
4221
3705
  out[key] = null;
4222
3706
  continue;
@@ -4236,11 +3720,25 @@ function sanitizeAttributes(attributes, method) {
4236
3720
  }
4237
3721
  return out;
4238
3722
  }
4239
- function createArcy() {
4240
- return createArcyInternals().api;
3723
+ var unavailableChunks = {
3724
+ cdnBase: null,
3725
+ loadChat: () => Promise.resolve(null),
3726
+ loadFlow: () => Promise.resolve(null),
3727
+ loadPicker: () => Promise.resolve(null)
3728
+ };
3729
+ function createArcy(chunks) {
3730
+ return createArcyInternals({ chunks }).api;
4241
3731
  }
4242
3732
  function createArcyInternals(internalOptions = {}) {
3733
+ const chunks = internalOptions.chunks ?? unavailableChunks;
4243
3734
  const emitter = createEmitter();
3735
+ function resolveApiBase() {
3736
+ const fromOptions = options.apiBase;
3737
+ if (typeof fromOptions === "string" && fromOptions.length > 0) {
3738
+ return fromOptions.replace(/\/+$/, "");
3739
+ }
3740
+ return internalOptions.apiBase ?? DEFAULT_API_BASE;
3741
+ }
4244
3742
  let token = null;
4245
3743
  let options = {};
4246
3744
  let identity = emptyIdentity();
@@ -4254,6 +3752,8 @@ function createArcyInternals(internalOptions = {}) {
4254
3752
  let bootstrap = null;
4255
3753
  let sendSessionSequence = 0;
4256
3754
  let entryRouteValue;
3755
+ let sessionContextValue;
3756
+ let sessionContextResolved = false;
4257
3757
  let shellState = null;
4258
3758
  let visitState = null;
4259
3759
  let conversationStore = null;
@@ -4292,7 +3792,7 @@ function createArcyInternals(internalOptions = {}) {
4292
3792
  for (const [key, value] of Object.entries(nextOptions)) {
4293
3793
  if (!OPTION_KEYS.includes(key)) {
4294
3794
  warn(
4295
- `init() ignored unknown option "${key}". Only telemetry, locale, defaultOpen, and contentLocale can be set in code; everything else is configured in the dashboard.`
3795
+ `init() ignored unknown option "${key}". Only telemetry, locale, defaultOpen, contentLocale, and apiBase can be set in code; everything else is configured in the dashboard.`
4296
3796
  );
4297
3797
  continue;
4298
3798
  }
@@ -4300,6 +3800,12 @@ function createArcyInternals(internalOptions = {}) {
4300
3800
  }
4301
3801
  }
4302
3802
  }
3803
+ if (typeof window !== "undefined") {
3804
+ reportCspViolations(window, {
3805
+ apiBase: resolveApiBase(),
3806
+ cdnBase: chunks.cdnBase
3807
+ });
3808
+ }
4303
3809
  start(nextToken);
4304
3810
  startDesignMode(nextToken);
4305
3811
  startPreviewMode(nextToken);
@@ -4343,7 +3849,7 @@ function createArcyInternals(internalOptions = {}) {
4343
3849
  // The same seam the bootstrap gets. Without it the collector's batches
4344
3850
  // leave through the real `fetch` even under test, so nothing in this
4345
3851
  // package could ever assert what a captured event actually carries.
4346
- apiBase: internalOptions.apiBase,
3852
+ apiBase: resolveApiBase(),
4347
3853
  fetchImpl: internalOptions.fetchImpl,
4348
3854
  // 15.1 plugs in here. This is what makes a click on the customer's
4349
3855
  // page resolvable to the element the dashboard picked: both sides
@@ -4368,7 +3874,7 @@ function createArcyInternals(internalOptions = {}) {
4368
3874
  try {
4369
3875
  bootstrap = createSessionBootstrap({
4370
3876
  token: activeToken,
4371
- apiBase: internalOptions.apiBase,
3877
+ apiBase: resolveApiBase(),
4372
3878
  fetchImpl: internalOptions.fetchImpl,
4373
3879
  setTimeoutImpl: internalOptions.setTimeoutImpl
4374
3880
  });
@@ -4383,7 +3889,8 @@ function createArcyInternals(internalOptions = {}) {
4383
3889
  void maybeActivateDesignMode({
4384
3890
  win: window,
4385
3891
  token: activeToken,
4386
- apiBase: internalOptions.apiBase ?? DEFAULT_API_BASE,
3892
+ loadChunk: chunks.loadPicker,
3893
+ apiBase: resolveApiBase(),
4387
3894
  fetchImpl: internalOptions.fetchImpl !== void 0 ? internalOptions.fetchImpl : typeof fetch !== "undefined" ? fetch.bind(globalThis) : null,
4388
3895
  setTimeoutImpl: internalOptions.setTimeoutImpl,
4389
3896
  // Discard, not flush (D490, D492): the queue at this moment is the
@@ -4408,7 +3915,7 @@ function createArcyInternals(internalOptions = {}) {
4408
3915
  }
4409
3916
  function verifyPreview(nonce) {
4410
3917
  return verifyPreviewNonce({
4411
- apiBase: internalOptions.apiBase ?? DEFAULT_API_BASE,
3918
+ apiBase: resolveApiBase(),
4412
3919
  token: token ?? "",
4413
3920
  nonce,
4414
3921
  sessionToken: bootstrap?.config()?.sessionToken ?? null,
@@ -4710,6 +4217,7 @@ function createArcyInternals(internalOptions = {}) {
4710
4217
  chatController = createChatController({
4711
4218
  root,
4712
4219
  host,
4220
+ loadChunk: chunks.loadChat,
4713
4221
  // The panel's collapse control reaches the public close() through
4714
4222
  // here, so isOpen() has exactly one writer. There is no close, only
4715
4223
  // collapse: the bar never leaves.
@@ -4742,6 +4250,10 @@ function createArcyInternals(internalOptions = {}) {
4742
4250
  setDisclaimer: (text) => mountedBar.setDisclaimer(text),
4743
4251
  setAttachEnabled: (enabled) => mountedBar.setAttachEnabled(enabled),
4744
4252
  setAttachmentCount: (count) => mountedBar.setAttachmentCount(count),
4253
+ // Where the panel's image tiles render: inside the composer, above
4254
+ // its input. The tiles stay the panel's (it owns the upload they
4255
+ // stand for); only the place is the bar's.
4256
+ attachmentSlot: () => mountedBar.attachmentSlot(),
4745
4257
  focusInput: () => mountedBar.focusInput(),
4746
4258
  getMetrics: () => mountedBar.getMetrics(),
4747
4259
  // The panel's Settings sheet asked for the widget to become a
@@ -4752,7 +4264,7 @@ function createArcyInternals(internalOptions = {}) {
4752
4264
  // The flows catalog fetch inputs; read live rather than captured,
4753
4265
  // same reasoning as everything else in this options object, since
4754
4266
  // the panel can mount before the bootstrap resolves.
4755
- apiBase: internalOptions.apiBase,
4267
+ apiBase: resolveApiBase(),
4756
4268
  getSessionToken: () => bootstrap?.config()?.sessionToken ?? null,
4757
4269
  getSessionId: () => telemetry?.sessionId(),
4758
4270
  getRoute: () => typeof window !== "undefined" ? window.location.pathname : "",
@@ -4806,7 +4318,7 @@ function createArcyInternals(internalOptions = {}) {
4806
4318
  if (!sessionToken) return null;
4807
4319
  const visits = visitState;
4808
4320
  return {
4809
- apiBase: internalOptions.apiBase ?? DEFAULT_API_BASE,
4321
+ apiBase: resolveApiBase(),
4810
4322
  token,
4811
4323
  sessionToken,
4812
4324
  sessionId: telemetry?.sessionId(),
@@ -4882,7 +4394,7 @@ function createArcyInternals(internalOptions = {}) {
4882
4394
  try {
4883
4395
  const context = buildFlowContext();
4884
4396
  if (!context) return;
4885
- flowController = createFlowController({ context });
4397
+ flowController = createFlowController({ context, loadChunk: chunks.loadFlow });
4886
4398
  } catch (error) {
4887
4399
  warn(`The flow engine could not be prepared. ${String(error)}`);
4888
4400
  }
@@ -4939,6 +4451,22 @@ function createArcyInternals(internalOptions = {}) {
4939
4451
  return void 0;
4940
4452
  }
4941
4453
  }
4454
+ function sessionContext() {
4455
+ if (sessionContextResolved) return sessionContextValue;
4456
+ sessionContextResolved = true;
4457
+ try {
4458
+ if (typeof window === "undefined") return void 0;
4459
+ sessionContextValue = captureSessionContext({
4460
+ referrer: typeof document !== "undefined" ? document.referrer : null,
4461
+ search: window.location?.search ?? null,
4462
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : null,
4463
+ viewportWidth: window.innerWidth
4464
+ });
4465
+ return sessionContextValue;
4466
+ } catch {
4467
+ return void 0;
4468
+ }
4469
+ }
4942
4470
  async function sendSession() {
4943
4471
  if (!bootstrap || !telemetry) return;
4944
4472
  const mySequence = ++sendSessionSequence;
@@ -4951,6 +4479,10 @@ function createArcyInternals(internalOptions = {}) {
4951
4479
  // identify() per route change from making every navigation a new
4952
4480
  // payload, which is the re-send storm D338's skip exists to prevent.
4953
4481
  route: entryRoute(),
4482
+ // Frozen with the entry route and for the same reason. Segmentation
4483
+ // dimensions, not behavior: they land on the session row rather than on
4484
+ // four hundred events that would each repeat them (#790).
4485
+ sessionContext: sessionContext(),
4954
4486
  // Sent whether or not this browser has identified (D517). It is the
4955
4487
  // Lift Subject for anyone who never will, and the anonymous half of the
4956
4488
  // pairing ADR 0055 discovers for everyone who does.
@@ -5004,15 +4536,15 @@ function createArcyInternals(internalOptions = {}) {
5004
4536
  warn("identify() needs a user id. Ignoring the call.");
5005
4537
  return;
5006
4538
  }
5007
- const clean = sanitizeAttributes(attributes, "identify");
5008
- if (clean === null) return;
4539
+ const clean2 = sanitizeAttributes(attributes, "identify");
4540
+ if (clean2 === null) return;
5009
4541
  const hash = readUserHash((options2 ?? {}).userHash, "identify");
5010
4542
  if (!hash.ok) return;
5011
4543
  const sameUser = identity.userId === userId && !identity.isAnonymous;
5012
4544
  identity = {
5013
4545
  userId,
5014
4546
  isAnonymous: false,
5015
- attributes: sameUser ? { ...identity.attributes, ...clean } : clean,
4547
+ attributes: sameUser ? { ...identity.attributes, ...clean2 } : clean2,
5016
4548
  // **The proof carries over for the same id and never across ids.** An
5017
4549
  // SPA re-identifying the same user on every route change passes the hash
5018
4550
  // once at login and nothing afterwards, and dropping it there would
@@ -5026,9 +4558,9 @@ function createArcyInternals(internalOptions = {}) {
5026
4558
  }
5027
4559
  async function identifyAnonymous(attributes) {
5028
4560
  if (!requireInit("identifyAnonymous")) return;
5029
- const clean = sanitizeAttributes(attributes, "identifyAnonymous");
5030
- if (clean === null) return;
5031
- identity = { userId: null, isAnonymous: true, attributes: clean };
4561
+ const clean2 = sanitizeAttributes(attributes, "identifyAnonymous");
4562
+ if (clean2 === null) return;
4563
+ identity = { userId: null, isAnonymous: true, attributes: clean2 };
5032
4564
  await syncSession();
5033
4565
  }
5034
4566
  async function updateUser(attributes) {
@@ -5039,9 +4571,9 @@ function createArcyInternals(internalOptions = {}) {
5039
4571
  );
5040
4572
  return;
5041
4573
  }
5042
- const clean = sanitizeAttributes(attributes, "updateUser");
5043
- if (clean === null) return;
5044
- identity = { ...identity, attributes: { ...identity.attributes, ...clean } };
4574
+ const clean2 = sanitizeAttributes(attributes, "updateUser");
4575
+ if (clean2 === null) return;
4576
+ identity = { ...identity, attributes: { ...identity.attributes, ...clean2 } };
5045
4577
  await syncSession();
5046
4578
  }
5047
4579
  function reset() {
@@ -5102,8 +4634,110 @@ function createArcyInternals(internalOptions = {}) {
5102
4634
  };
5103
4635
  }
5104
4636
 
4637
+ // src/chunks/bundled.ts
4638
+ async function importChunk(win, importer, globalKey, contract, surface, timeoutMs, setTimeoutImpl) {
4639
+ const holder = win;
4640
+ let warned = false;
4641
+ const complain = (message) => {
4642
+ if (warned) return;
4643
+ warned = true;
4644
+ warn(message);
4645
+ };
4646
+ const read = () => {
4647
+ let registration2;
4648
+ try {
4649
+ registration2 = holder[globalKey];
4650
+ } catch {
4651
+ complain(`The ${surface} registration could not be read. It will not start.`);
4652
+ return null;
4653
+ }
4654
+ if (!registration2) return null;
4655
+ if (typeof registration2.mount !== "function") {
4656
+ complain(`The ${surface} registered something unusable. It will not start.`);
4657
+ return null;
4658
+ }
4659
+ let declared;
4660
+ try {
4661
+ declared = registration2.contract;
4662
+ } catch {
4663
+ declared = void 0;
4664
+ }
4665
+ if (declared !== contract) {
4666
+ complain(
4667
+ `The bundled ${surface} speaks contract ${typeof declared === "number" ? declared : "unknown"}, this arcy.js speaks ${contract}. Reinstall arcy.js so both halves come from one version.`
4668
+ );
4669
+ return null;
4670
+ }
4671
+ return registration2;
4672
+ };
4673
+ let present = false;
4674
+ try {
4675
+ present = holder[globalKey] !== void 0;
4676
+ } catch {
4677
+ present = true;
4678
+ }
4679
+ if (present) return read();
4680
+ const arrived = await Promise.race([
4681
+ importer().then(
4682
+ () => "loaded",
4683
+ (error) => {
4684
+ warn(
4685
+ `The ${surface} chunk could not be loaded from your own build. ${String(error)}`
4686
+ );
4687
+ return "failed";
4688
+ }
4689
+ ),
4690
+ new Promise((resolve) => {
4691
+ try {
4692
+ setTimeoutImpl(() => resolve("timeout"), timeoutMs);
4693
+ } catch {
4694
+ }
4695
+ })
4696
+ ]);
4697
+ if (arrived === "failed") return null;
4698
+ if (arrived === "timeout") {
4699
+ warn(`The ${surface} did not load in time.`);
4700
+ return null;
4701
+ }
4702
+ const registration = read();
4703
+ if (!registration && !warned) {
4704
+ complain(`The ${surface} loaded but did not register. It will not start.`);
4705
+ }
4706
+ return registration;
4707
+ }
4708
+ var bundledChunks = {
4709
+ cdnBase: null,
4710
+ loadChat: (win, timeoutMs, setTimeoutImpl) => importChunk(
4711
+ win,
4712
+ () => import('./chat-BK57WIAA.js'),
4713
+ CHAT_GLOBAL,
4714
+ CHAT_CONTRACT,
4715
+ "chat panel",
4716
+ timeoutMs,
4717
+ setTimeoutImpl
4718
+ ),
4719
+ loadFlow: (win, timeoutMs, setTimeoutImpl) => importChunk(
4720
+ win,
4721
+ () => import('./flow-M7W3H3C5.js'),
4722
+ FLOW_GLOBAL,
4723
+ FLOW_CONTRACT,
4724
+ "flow engine",
4725
+ timeoutMs,
4726
+ setTimeoutImpl
4727
+ ),
4728
+ loadPicker: (win, timeoutMs, setTimeoutImpl) => importChunk(
4729
+ win,
4730
+ () => import('./picker-JPESE6JJ.js'),
4731
+ PICKER_GLOBAL,
4732
+ PICKER_CONTRACT,
4733
+ "design-mode picker",
4734
+ timeoutMs,
4735
+ setTimeoutImpl
4736
+ )
4737
+ };
4738
+
5105
4739
  // src/index.ts
5106
- var arcy = createArcy();
4740
+ var arcy = createArcy(bundledChunks);
5107
4741
  var src_default = arcy;
5108
4742
 
5109
4743
  export { VERSION, src_default as default };