@virtualdisplay.io/client 4.0.0-alpha.20 → 4.0.0-alpha.21

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.
@@ -12,66 +12,72 @@ const c = {
12
12
  CONFIG: "config",
13
13
  ANNOTATION_TOGGLE: "annotation_toggle",
14
14
  FOCUS: "focus",
15
- PRICING: "pricing"
16
- }, Y = {
15
+ PRICING: "pricing",
16
+ QR_SHOWN: "qr_shown",
17
+ SYNC: "sync"
18
+ }, Q = {
17
19
  FOCUS: "focus",
18
20
  UNFOCUS: "unfocus"
19
21
  }, kt = {
20
22
  FOCUSED: "focused"
21
- }, St = {
23
+ }, At = {
22
24
  /** Behaves exactly like an unpriced combination: no price, no button. */
23
25
  HIDDEN: "hidden"
24
26
  }, Et = new Set(Object.values(c));
25
- function At(n) {
27
+ function xt(n) {
26
28
  if (typeof n != "object" || n === null)
27
29
  return !1;
28
30
  const t = n;
29
31
  return typeof t.target == "string" ? !1 : typeof t.type == "string" && Et.has(t.type);
30
32
  }
31
- function xt(n) {
32
- const t = k(n);
33
+ function Tt(n) {
34
+ const t = C(n);
33
35
  return t !== null && t.type === c.HANDSHAKE;
34
36
  }
35
- function Tt(n) {
36
- const t = k(n);
37
+ function Rt(n) {
38
+ const t = C(n);
37
39
  return t !== null && t.type === c.SESSION && typeof t.sessionId == "string";
38
40
  }
39
- function Rt(n) {
40
- const t = k(n);
41
+ function It(n) {
42
+ const t = C(n);
41
43
  return t !== null && t.type === c.MUTATION && Array.isArray(t.results);
42
44
  }
43
- function It(n) {
44
- const t = k(n);
45
+ function Ot(n) {
46
+ const t = C(n);
45
47
  return t !== null && t.type === c.SNAPSHOT && ("data" in t || "error" in t);
46
48
  }
47
- function Ot(n) {
48
- const t = k(n);
49
+ function Nt(n) {
50
+ const t = C(n);
49
51
  return t !== null && t.type === c.CAMERA && Array.isArray(t.transforms);
50
52
  }
51
- function Nt(n) {
52
- const t = k(n);
53
+ function Lt(n) {
54
+ const t = C(n);
53
55
  return t !== null && t.type === c.MAPPING && Array.isArray(t.attributes);
54
56
  }
55
- function Lt(n) {
56
- const t = k(n);
57
+ function Mt(n) {
58
+ const t = C(n);
57
59
  return t !== null && t.type === c.LOADING_INDICATOR && typeof t.progress == "number";
58
60
  }
59
- function Mt(n) {
60
- const t = k(n);
61
+ function zt(n) {
62
+ const t = C(n);
63
+ return t !== null && t.type === c.SYNC && typeof t.connected == "boolean";
64
+ }
65
+ function Pt(n) {
66
+ const t = C(n);
61
67
  return t !== null && t.type === c.CONFIG && typeof t.viewerSettings == "object";
62
68
  }
63
- function zt(n) {
64
- const t = k(n);
69
+ function Ft(n) {
70
+ const t = C(n);
65
71
  return t !== null && t.type === c.FOCUS && typeof t.state == "string";
66
72
  }
67
- function Ft(n) {
68
- const t = k(n);
73
+ function $t(n) {
74
+ const t = C(n);
69
75
  return t !== null && t.type === c.PRICING && typeof t.pricing == "object" && t.pricing !== null;
70
76
  }
71
- function k(n) {
77
+ function C(n) {
72
78
  return typeof n != "object" || n === null ? null : n;
73
79
  }
74
- class X {
80
+ class Y {
75
81
  constructor(t) {
76
82
  this.processor = t;
77
83
  }
@@ -118,9 +124,9 @@ class X {
118
124
  this.pending.length = 0;
119
125
  }
120
126
  }
121
- class Pt {
127
+ class Dt {
122
128
  constructor(t, e) {
123
- this.getTarget = t, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.targetOrigin = e?.targetOrigin ?? "*", this.isValid = e?.isValid ?? At, this.publisher = new X((i) => this.post(i)), this.consumer = new X((i) => this.routeToHandler(i)), this.shouldBuffer = e?.shouldBuffer ?? null, e?.bufferIncoming !== !0 && this.consumer.markReady();
129
+ this.getTarget = t, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.targetOrigin = e?.targetOrigin ?? "*", this.isValid = e?.isValid ?? xt, this.publisher = new Y((i) => this.post(i)), this.consumer = new Y((i) => this.routeToHandler(i)), this.shouldBuffer = e?.shouldBuffer ?? null, e?.bufferIncoming !== !0 && this.consumer.markReady();
124
130
  }
125
131
  getTarget;
126
132
  handlers = /* @__PURE__ */ new Map();
@@ -157,6 +163,24 @@ class Pt {
157
163
  send(t) {
158
164
  this.publisher.enqueue(t);
159
165
  }
166
+ /**
167
+ * Feed a message through the same routing path as one that just arrived via
168
+ * postMessage, without an actual postMessage round-trip: still validated
169
+ * against `isValid`, and still respects the consumer queue's buffering and
170
+ * the `shouldBuffer` bypass. For a transport that delivers messages some
171
+ * other way than the iframe channel this messenger already listens on (the
172
+ * viewer engine's SSE-sourced live sync channel, ar-session-handoff) and
173
+ * needs them to land exactly where a postMessage would.
174
+ */
175
+ receive(t) {
176
+ if (this.isValid(t)) {
177
+ if (this.shouldBuffer !== null && !this.shouldBuffer(t)) {
178
+ this.routeToHandler(t);
179
+ return;
180
+ }
181
+ this.consumer.enqueue(t);
182
+ }
183
+ }
160
184
  /**
161
185
  * Enable sending and flush queued outgoing messages
162
186
  */
@@ -228,1467 +252,281 @@ class Pt {
228
252
  });
229
253
  }
230
254
  }
231
- class g {
232
- element;
233
- constructor({ variant: t = "default", size: e = "default", label: i, icon: o, onClick: s, disabled: r = !1 } = {}) {
234
- this.element = document.createElement("button"), this.element.className = `ui-btn ui-btn--${t} ui-btn--size-${e}`, i !== void 0 && (this.element.setAttribute("aria-label", i), this.element.title = i), o !== void 0 ? this.element.appendChild(o) : i !== void 0 && (this.element.textContent = i), s && this.element.addEventListener("click", s), this.element.disabled = r;
235
- }
236
- setIcon(t) {
237
- const e = this.element.querySelector("svg");
238
- e !== null ? e.replaceWith(t) : this.element.appendChild(t);
255
+ class Bt {
256
+ constructor(t) {
257
+ this.send = t;
239
258
  }
240
- }
241
- const $t = {
242
- xmlns: "http://www.w3.org/2000/svg",
243
- width: 24,
244
- height: 24,
245
- viewBox: "0 0 24 24",
246
- fill: "none",
247
- stroke: "currentColor",
248
- "stroke-width": 2,
249
- "stroke-linecap": "round",
250
- "stroke-linejoin": "round"
251
- };
252
- const lt = ([n, t, e]) => {
253
- const i = document.createElementNS("http://www.w3.org/2000/svg", n);
254
- return Object.keys(t).forEach((o) => {
255
- i.setAttribute(o, String(t[o]));
256
- }), e?.length && e.forEach((o) => {
257
- const s = lt(o);
258
- i.appendChild(s);
259
- }), i;
260
- }, ct = (n, t = {}) => {
261
- const i = {
262
- ...$t,
263
- ...t
264
- };
265
- return lt(["svg", i, n]);
266
- };
267
- const Dt = [
268
- [
269
- "path",
270
- {
271
- d: "M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"
272
- }
273
- ],
274
- ["path", { d: "m3.3 7 8.7 5 8.7-5" }],
275
- ["path", { d: "M12 22V12" }]
276
- ];
277
- const Bt = [
278
- [
279
- "path",
280
- {
281
- d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"
282
- }
283
- ],
284
- ["circle", { cx: "12", cy: "13", r: "3" }]
285
- ];
286
- const ut = [["path", { d: "m6 9 6 6 6-6" }]];
287
- const Ut = [["path", { d: "m15 18-6-6 6-6" }]];
288
- const Vt = [["path", { d: "m9 18 6-6-6-6" }]];
289
- const jt = [["path", { d: "m18 15-6-6-6 6" }]];
290
- const Ht = [
291
- ["path", { d: "M15 3h6v6" }],
292
- ["path", { d: "m21 3-7 7" }],
293
- ["path", { d: "m3 21 7-7" }],
294
- ["path", { d: "M9 21H3v-6" }]
295
- ];
296
- const Gt = [
297
- ["path", { d: "m14 10 7-7" }],
298
- ["path", { d: "M20 10h-6V4" }],
299
- ["path", { d: "m3 21 7-7" }],
300
- ["path", { d: "M4 14h6v6" }]
301
- ];
302
- const qt = [
303
- ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }],
304
- ["path", { d: "M3 3v5h5" }]
305
- ];
306
- const Zt = [
307
- ["path", { d: "M10 5H3" }],
308
- ["path", { d: "M12 19H3" }],
309
- ["path", { d: "M14 3v4" }],
310
- ["path", { d: "M16 17v4" }],
311
- ["path", { d: "M21 12h-9" }],
312
- ["path", { d: "M21 19h-5" }],
313
- ["path", { d: "M21 5h-7" }],
314
- ["path", { d: "M8 10v4" }],
315
- ["path", { d: "M8 12H3" }]
316
- ];
317
- const Wt = [
318
- ["path", { d: "M18 6 6 18" }],
319
- ["path", { d: "m6 6 12 12" }]
320
- ];
321
- const Yt = [
322
- ["circle", { cx: "11", cy: "11", r: "8" }],
323
- ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }],
324
- ["line", { x1: "11", x2: "11", y1: "8", y2: "14" }],
325
- ["line", { x1: "8", x2: "14", y1: "11", y2: "11" }]
326
- ];
327
- const Xt = [
328
- ["circle", { cx: "11", cy: "11", r: "8" }],
329
- ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }],
330
- ["line", { x1: "8", x2: "14", y1: "11", y2: "11" }]
331
- ], m = {
332
- semantic: {
333
- // eslint-disable-next-line vd-custom/enforce-error-codes -- This is a hex color, not an error code
334
- error: "#dc2626"
335
- },
336
- neutral: {
337
- 500: "#737373",
338
- 600: "#525252",
339
- 900: "#171717"
340
- },
341
- white: "#ffffff",
342
- black: "#000000"
343
- }, a = {
344
- 0: "0",
345
- 0.5: "0.125rem",
346
- // 2px
347
- 1: "0.25rem",
348
- // 4px
349
- 1.5: "0.375rem",
350
- // 6px
351
- 2: "0.5rem",
352
- // 10px
353
- 3: "0.75rem",
354
- // 14px
355
- 4: "1rem",
356
- // 16px
357
- 5: "1.25rem",
358
- // 20px
359
- 6: "1.5rem",
360
- // 28px
361
- 8: "2rem",
362
- // 32px
363
- 9: "2.25rem",
364
- // 80px
365
- 24: "6rem"
366
- // 96px
367
- };
368
- class Kt {
369
- element;
370
- constructor() {
371
- this.element = document.createElement("div"), Object.assign(this.element.style, {
372
- background: m.white,
373
- borderRadius: a[3],
374
- padding: a[6],
375
- boxShadow: `0 ${a[2]} ${a[8]} oklch(0% 0 0 / 20%)`,
376
- maxWidth: "20rem",
377
- width: "100%"
259
+ send;
260
+ setEnabled(t) {
261
+ this.send({
262
+ type: c.ANNOTATION_TOGGLE,
263
+ enabled: t
378
264
  });
379
265
  }
380
266
  }
381
- class Qt {
382
- constructor(t = {}) {
383
- this.options = t, this.card = new Kt(), this.backdrop = this.createBackdrop(), this.backdrop.appendChild(this.card.element);
384
- }
385
- options;
386
- card;
387
- backdrop;
388
- keyHandler = null;
389
- open() {
390
- document.body.appendChild(this.backdrop), this.keyHandler = (t) => {
391
- t.key === "Escape" && this.close();
392
- }, document.addEventListener("keydown", this.keyHandler);
393
- }
394
- close() {
395
- this.backdrop.remove(), this.keyHandler !== null && (document.removeEventListener("keydown", this.keyHandler), this.keyHandler = null), this.options.onClose?.();
396
- }
397
- createBackdrop() {
398
- const t = document.createElement("div");
399
- return Object.assign(t.style, {
400
- position: "fixed",
401
- inset: a[0],
402
- background: "oklch(0% 0 0 / 50%)",
403
- zIndex: "9999",
404
- display: "flex",
405
- alignItems: "center",
406
- justifyContent: "center"
407
- }), t.addEventListener("click", (e) => {
408
- e.target === t && this.close();
409
- }), t;
267
+ class Ut {
268
+ constructor(t, e) {
269
+ this.value = t, this.attribute = e;
410
270
  }
411
- }
412
- class Jt {
413
- element;
414
- selectCallback = null;
415
- constructor(t, e, i) {
416
- this.element = document.createElement("div"), this.element.className = "ui-toggle-group";
417
- for (let o = 0; o < t.length; o++) {
418
- const s = t[o], r = document.createElement("button");
419
- r.className = "ui-toggle-group-item", r.dataset.value = s, r.textContent = i?.[o] ?? s, r.dataset.state = s === e ? "on" : "off", r.setAttribute("aria-pressed", s === e ? "true" : "false"), r.addEventListener("click", () => {
420
- this.selectCallback?.(s);
421
- }), this.element.appendChild(r);
422
- }
271
+ value;
272
+ attribute;
273
+ isSelectedValue = !1;
274
+ get isSelected() {
275
+ return this.isSelectedValue;
423
276
  }
424
277
  setSelected(t) {
425
- for (const e of this.element.querySelectorAll(".ui-toggle-group-item")) {
426
- const i = e.dataset.value === t;
427
- e.dataset.state = i ? "on" : "off", e.setAttribute("aria-pressed", i ? "true" : "false");
428
- }
429
- }
430
- onSelect(t) {
431
- this.selectCallback = t;
278
+ this.isSelectedValue = t;
432
279
  }
433
280
  }
434
- class te {
435
- element;
436
- select;
437
- constructor(t, e, i, o, s) {
438
- this.select = document.createElement("select"), this.select.className = "ui-native-select", this.select.dataset.name = t, this.select.setAttribute("aria-label", i);
439
- const r = document.createElement("option");
440
- r.value = "", r.disabled = !0, r.textContent = i, this.select.appendChild(r);
441
- let l = !1;
442
- for (let u = 0; u < e.length; u++) {
443
- const p = e[u], v = document.createElement("option");
444
- v.value = p, v.textContent = s?.[u] ?? p, p === o && (v.selected = !0, l = !0), this.select.appendChild(v);
445
- }
446
- l || (r.selected = !0);
447
- const d = ct(ut, { width: 16, height: 16, "stroke-width": 2 });
448
- d.classList.add("ui-native-select-icon"), this.element = document.createElement("div"), this.element.className = "ui-native-select-wrap", this.element.appendChild(this.select), this.element.appendChild(d);
281
+ const A = {
282
+ NO_MAPPING: "NO_MAPPING",
283
+ ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
284
+ VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
285
+ PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
286
+ INVALID_PARAMETER: "INVALID_PARAMETER",
287
+ SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
288
+ };
289
+ class f extends Error {
290
+ code;
291
+ constructor(t, e) {
292
+ super(t), this.name = "VirtualdisplayError", this.code = e;
449
293
  }
450
- setSelected(t) {
451
- this.select.value = t;
294
+ static valueNotFound(t, e) {
295
+ return new f(
296
+ `Value "${e}" not found for attribute "${t}"`,
297
+ A.VALUE_NOT_FOUND
298
+ );
452
299
  }
453
- onSelect(t) {
454
- this.select.addEventListener("change", () => {
455
- t(this.select.value);
456
- });
300
+ static parentNotFound(t) {
301
+ return new f(
302
+ `Parent element not found: ${t}`,
303
+ A.PARENT_NOT_FOUND
304
+ );
305
+ }
306
+ static invalidParameter(t) {
307
+ return new f(t, A.INVALID_PARAMETER);
457
308
  }
458
309
  }
459
- const ee = {}, ne = "en", dt = (
460
- /** @type {const} */
461
- ["en", "nl", "de"]
462
- ), Z = [
463
- "baseLocale"
464
- ], K = [];
465
- let Q, J;
466
- function ie(n) {
467
- if (K.length === 0)
468
- return;
469
- const t = typeof n == "string" ? n : n.href;
470
- if (Q === t)
471
- return J;
472
- const e = new URL(t, "http://dummy.com");
473
- let i;
474
- for (const o of K)
475
- if (new ee(o.match, e.href).exec(e.href)) {
476
- i = o;
477
- break;
310
+ class Vt {
311
+ name;
312
+ defaultValue;
313
+ values;
314
+ onSelect;
315
+ onChangeCallback;
316
+ constructor(t) {
317
+ this.name = t.name, this.defaultValue = t.defaultValue, this.onSelect = t.onSelect, this.values = this.createValues(t.values);
318
+ }
319
+ /**
320
+ * Select a value for this attribute
321
+ * @throws {VirtualdisplayError} When value not found
322
+ */
323
+ select(t) {
324
+ if (!this.values.has(t))
325
+ throw f.valueNotFound(this.name, t);
326
+ this.onSelect(this.name, t);
327
+ }
328
+ getValues() {
329
+ return Array.from(this.values.values());
330
+ }
331
+ getValue(t) {
332
+ return this.values.get(t);
333
+ }
334
+ /** The currently selected value, or undefined before any selection lands. */
335
+ get selected() {
336
+ for (const t of this.values.values())
337
+ if (t.isSelected)
338
+ return t.value;
339
+ }
340
+ /**
341
+ * Register callback that fires when selection changes
342
+ * Receives the newly selected AttributeValue
343
+ */
344
+ onChange(t) {
345
+ this.onChangeCallback = t;
346
+ }
347
+ /**
348
+ * Update selection state from server mutation result
349
+ * Direct value comparison - no source/target translation
350
+ */
351
+ handleResult(t) {
352
+ let e;
353
+ for (const i of this.values.values()) {
354
+ const o = i.value === t;
355
+ i.setSelected(o), o && (e = i);
478
356
  }
479
- return Q = t, J = i, i;
480
- }
481
- function ht(n) {
482
- const t = ie(n);
483
- return t && t.exclude !== !0 && Array.isArray(t.strategy) ? t.strategy : Z;
357
+ e !== void 0 && this.onChangeCallback !== void 0 && this.onChangeCallback(e);
358
+ }
359
+ createValues(t) {
360
+ const e = /* @__PURE__ */ new Map();
361
+ for (const i of t) {
362
+ const o = new Ut(i, this.name);
363
+ i === this.defaultValue && o.setSelected(!0), e.set(i, o);
364
+ }
365
+ return e;
366
+ }
484
367
  }
485
- const q = typeof window > "u";
486
- globalThis.__paraglide = /** @type {any} */
487
- globalThis.__paraglide ?? {};
488
- globalThis.__paraglide.ssr = /** @type {any} */
489
- globalThis.__paraglide.ssr ?? {};
490
- let tt = !1, h = () => {
491
- let n = Z;
492
- !q && typeof window < "u" && window.location?.href && (n = ht(window.location.href));
493
- const t = oe(n);
494
- if (t)
495
- return tt || (tt = !0, mt(t, { reload: !1 })), t;
496
- throw new Error("No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found");
497
- };
498
- function oe(n, t) {
499
- let e;
500
- for (const i of n) {
501
- if (i === "baseLocale")
502
- e = ne;
503
- else if (ft(i) && N.has(i)) {
504
- const s = N.get(i);
505
- if (s) {
506
- const r = s.getLocale();
507
- if (r instanceof Promise)
508
- continue;
509
- if (r !== void 0)
510
- return re(r);
368
+ class jt {
369
+ attributes = /* @__PURE__ */ new Map();
370
+ constructor(t, e, i) {
371
+ for (const o of t) {
372
+ const s = this.buildOptions(o, e);
373
+ this.attributes.set(o.name, new Vt(s));
374
+ }
375
+ this.onMutationFailed = i;
376
+ }
377
+ onMutationFailed;
378
+ get(t) {
379
+ return this.attributes.get(t);
380
+ }
381
+ /** The current selection as attribute name→value pairs, skipping unset attributes. */
382
+ getSelection() {
383
+ const t = {};
384
+ for (const [e, i] of this.attributes) {
385
+ const o = i.selected;
386
+ o !== void 0 && (t[e] = o);
387
+ }
388
+ return t;
389
+ }
390
+ handleResults(t) {
391
+ for (const e of t) {
392
+ if (!e.success) {
393
+ this.onMutationFailed?.(e.name, e.value);
394
+ continue;
511
395
  }
396
+ this.get(e.name)?.handleResult(e.value);
512
397
  }
513
- const o = pt(e);
514
- if (o)
515
- return o;
398
+ }
399
+ buildOptions(t, e) {
400
+ const i = {
401
+ name: t.name,
402
+ values: t.values,
403
+ onSelect: (o, s) => {
404
+ e([{ name: o, value: s }], !1);
405
+ }
406
+ };
407
+ return t.defaultValue !== void 0 ? { ...i, defaultValue: t.defaultValue } : i;
516
408
  }
517
409
  }
518
- const se = (n) => {
519
- window.location.reload();
520
- };
521
- let mt = (n, t) => {
410
+ function Ht(n) {
411
+ const t = new Date(n.timestamp).toLocaleTimeString("en-GB", { hour12: !1 }), e = n.level.toUpperCase().padEnd(5), i = n.trace_id?.slice(0, 8) ?? "--------", o = n.msg, s = ["timestamp", "level", "msg", "service", "trace_id", "span_id"], r = Object.entries(n).filter(([l]) => !s.includes(l)).map(([l, d]) => `${l}=${JSON.stringify(d)}`).join(" ");
412
+ return `${t} ${e} [${i}] ${o}${r ? " " + r : ""}`;
413
+ }
414
+ function qt(n) {
415
+ return JSON.stringify(n);
416
+ }
417
+ function Gt(n, t) {
418
+ const e = t ? Ht(n) : qt(n);
419
+ n.level === "error" ? console.error(e) : n.level === "warn" ? console.warn(e) : console.log(e);
420
+ }
421
+ function lt(n, t = 0) {
522
422
  const e = {
523
- reload: !0,
524
- ...t
423
+ name: n.name,
424
+ message: n.message
525
425
  };
526
- let i;
527
- try {
528
- i = h();
529
- } catch {
426
+ n.stack !== void 0 && (e.stack = n.stack);
427
+ for (const i of Object.getOwnPropertyNames(n)) {
428
+ if (i === "name" || i === "message" || i === "stack" || i === "cause")
429
+ continue;
430
+ const o = n[i];
431
+ o !== void 0 && (e[i] = o);
530
432
  }
531
- const o = [];
532
- let s = Z;
533
- !q && typeof window < "u" && window.location?.href && (s = ht(window.location.href));
534
- for (const l of s)
535
- if (l !== "baseLocale" && ft(l) && N.has(l)) {
536
- const d = N.get(l);
537
- if (d) {
538
- let u = d.setLocale(n);
539
- u instanceof Promise && (u = u.catch((p) => {
540
- throw new Error(`Custom strategy "${l}" setLocale failed.`, {
541
- cause: p
542
- });
543
- }), o.push(u));
544
- }
545
- }
546
- const r = () => {
547
- !q && e.reload && window.location && n !== i && se();
433
+ return n.cause !== void 0 && n.cause !== null && (e.cause = n.cause instanceof Error && t < 5 ? lt(n.cause, t + 1) : String(n.cause)), e;
434
+ }
435
+ function Zt(n) {
436
+ const t = {};
437
+ for (const [e, i] of Object.entries(n))
438
+ t[e] = i instanceof Error ? lt(i) : i;
439
+ return t;
440
+ }
441
+ function Wt() {
442
+ return /* @__PURE__ */ new Date();
443
+ }
444
+ function Qt(n) {
445
+ return n.toISOString();
446
+ }
447
+ function ct() {
448
+ return typeof navigator > "u" ? "" : navigator.userAgent;
449
+ }
450
+ function Yt() {
451
+ return typeof navigator > "u" ? !1 : navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
452
+ }
453
+ function Kt() {
454
+ return /iPhone|iPad|iPod/i.test(ct()) || Yt();
455
+ }
456
+ function Xt() {
457
+ return /Android/i.test(ct());
458
+ }
459
+ function Jt() {
460
+ return Kt() || Xt();
461
+ }
462
+ class te {
463
+ constructor(t) {
464
+ this.config = t;
465
+ }
466
+ config;
467
+ /**
468
+ * Log at specified level with optional attributes.
469
+ */
470
+ log(t, e, i = {}) {
471
+ const o = this.config.traceProvider?.() ?? {}, s = {
472
+ timestamp: Qt(Wt()),
473
+ level: t,
474
+ msg: e,
475
+ service: this.config.service,
476
+ ...o,
477
+ ...Zt(i)
478
+ };
479
+ Gt(s, this.config.isDev);
480
+ }
481
+ /**
482
+ * Log at DEBUG level (development only).
483
+ */
484
+ debug(t, e) {
485
+ this.config.isDev && this.log("debug", t, e);
486
+ }
487
+ /**
488
+ * Log at INFO level.
489
+ */
490
+ info(t, e) {
491
+ this.log("info", t, e);
492
+ }
493
+ /**
494
+ * Log at WARN level.
495
+ */
496
+ warn(t, e) {
497
+ this.log("warn", t, e);
498
+ }
499
+ /**
500
+ * Log at ERROR level.
501
+ */
502
+ error(t, e) {
503
+ this.log("error", t, e);
504
+ }
505
+ }
506
+ function ut(n, t) {
507
+ const e = t?.isDev !== void 0 ? t.isDev : typeof process < "u" ? process.env.NODE_ENV !== "production" : !0;
508
+ return new te({ service: n, isDev: e, traceProvider: t?.traceProvider });
509
+ }
510
+ function O(n) {
511
+ if (n !== void 0)
512
+ return typeof n == "object" && n !== null && !Array.isArray(n) && !(n instanceof Error) ? n : n instanceof Error ? { error: n.message } : { value: n };
513
+ }
514
+ function dt(n) {
515
+ return {
516
+ debug: (t, e) => n.debug(t, O(e)),
517
+ info: (t, e) => n.info(t, O(e)),
518
+ warn: (t, e) => n.warn(t, O(e)),
519
+ error: (t, e) => n.error(t, O(e))
548
520
  };
549
- if (o.length)
550
- return Promise.all(o).then(() => {
551
- r();
552
- });
553
- r();
554
- };
555
- function pt(n) {
556
- if (typeof n != "string")
557
- return;
558
- const t = n.toLowerCase();
559
- for (const e of dt)
560
- if (e.toLowerCase() === t)
561
- return e;
562
521
  }
563
- function re(n) {
564
- const t = pt(n);
565
- if (t)
566
- return t;
567
- throw new Error(`Invalid locale: ${n}. Expected one of: ${dt.join(", ")}`);
522
+ let ht = !1, _ = dt(ut("client", { isDev: !1 }));
523
+ function ee() {
524
+ ht = !0, _ = dt(ut("client", { isDev: !0 }));
568
525
  }
569
- const N = /* @__PURE__ */ new Map();
570
- function ft(n) {
571
- return typeof n == "string" && /^custom-[A-Za-z0-9_-]+$/.test(n);
526
+ function Ki() {
527
+ return ht;
572
528
  }
573
- const ae = (
574
- /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
575
- () => (
576
- /** @type {LocalizedString} */
577
- "Activate AR"
578
- )
579
- ), le = (
580
- /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
581
- () => (
582
- /** @type {LocalizedString} */
583
- "AR activeren"
584
- )
585
- ), ce = (
586
- /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
587
- () => (
588
- /** @type {LocalizedString} */
589
- "AR aktivieren"
590
- )
591
- ), ue = (
592
- /** @type {((inputs?: Controls_Ar_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Ar_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
593
- ((n = {}, t = {}) => {
594
- const e = t.locale ?? h();
595
- return e === "en" ? ae() : e === "nl" ? le() : ce();
596
- })
597
- ), de = (
598
- /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
599
- () => (
600
- /** @type {LocalizedString} */
601
- "Scan to view in AR"
602
- )
603
- ), he = (
604
- /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
605
- () => (
606
- /** @type {LocalizedString} */
607
- "Scan om in AR te bekijken"
608
- )
609
- ), me = (
610
- /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
611
- () => (
612
- /** @type {LocalizedString} */
613
- "Scannen für AR-Ansicht"
614
- )
615
- ), pe = (
616
- /** @type {((inputs?: Controls_Qr_Modaltitle1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modaltitle1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
617
- ((n = {}, t = {}) => {
618
- const e = t.locale ?? h();
619
- return e === "en" ? de() : e === "nl" ? he() : me();
620
- })
621
- ), fe = (
622
- /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
623
- () => (
624
- /** @type {LocalizedString} */
625
- "Use your smartphone camera to scan this QR code"
626
- )
627
- ), ge = (
628
- /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
629
- () => (
630
- /** @type {LocalizedString} */
631
- "Gebruik de camera van je smartphone om deze QR-code te scannen"
632
- )
633
- ), be = (
634
- /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
635
- () => (
636
- /** @type {LocalizedString} */
637
- "Verwende die Kamera deines Smartphones, um diesen QR-Code zu scannen"
638
- )
639
- ), ve = (
640
- /** @type {((inputs?: Controls_Qr_Modaldescription1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modaldescription1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
641
- ((n = {}, t = {}) => {
642
- const e = t.locale ?? h();
643
- return e === "en" ? fe() : e === "nl" ? ge() : be();
644
- })
645
- ), ye = (
646
- /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
647
- () => (
648
- /** @type {LocalizedString} */
649
- "Your phone will open the 3D model in AR mode."
650
- )
651
- ), _e = (
652
- /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
653
- () => (
654
- /** @type {LocalizedString} */
655
- "Je telefoon opent het 3D-model in AR-modus."
656
- )
657
- ), we = (
658
- /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
659
- () => (
660
- /** @type {LocalizedString} */
661
- "Dein Telefon öffnet das 3D-Modell im AR-Modus."
662
- )
663
- ), Ce = (
664
- /** @type {((inputs?: Controls_Qr_Modalinstructions1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modalinstructions1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
665
- ((n = {}, t = {}) => {
666
- const e = t.locale ?? h();
667
- return e === "en" ? ye() : e === "nl" ? _e() : we();
668
- })
669
- ), ke = (
670
- /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
671
- () => (
672
- /** @type {LocalizedString} */
673
- "Close"
674
- )
675
- ), Se = (
676
- /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
677
- () => (
678
- /** @type {LocalizedString} */
679
- "Sluiten"
680
- )
681
- ), Ee = (
682
- /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
683
- () => (
684
- /** @type {LocalizedString} */
685
- "Schließen"
686
- )
687
- ), Ae = (
688
- /** @type {((inputs?: Controls_Qr_Closebutton1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Closebutton1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
689
- ((n = {}, t = {}) => {
690
- const e = t.locale ?? h();
691
- return e === "en" ? ke() : e === "nl" ? Se() : Ee();
692
- })
693
- ), xe = (
694
- /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
695
- () => (
696
- /** @type {LocalizedString} */
697
- "Fullscreen"
698
- )
699
- ), Te = (
700
- /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
701
- () => (
702
- /** @type {LocalizedString} */
703
- "Volledig scherm"
704
- )
705
- ), Re = (
706
- /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
707
- () => (
708
- /** @type {LocalizedString} */
709
- "Vollbild"
710
- )
711
- ), Ie = (
712
- /** @type {((inputs?: Controls_Fullscreen_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Fullscreen_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
713
- ((n = {}, t = {}) => {
714
- const e = t.locale ?? h();
715
- return e === "en" ? xe() : e === "nl" ? Te() : Re();
716
- })
717
- ), Oe = (
718
- /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
719
- () => (
720
- /** @type {LocalizedString} */
721
- "Downloading"
722
- )
723
- ), Ne = (
724
- /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
725
- () => (
726
- /** @type {LocalizedString} */
727
- "Downloaden"
728
- )
729
- ), Le = (
730
- /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
731
- () => (
732
- /** @type {LocalizedString} */
733
- "Herunterladen"
734
- )
735
- ), gt = (
736
- /** @type {((inputs?: Loading_Downloadingshort1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Loading_Downloadingshort1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
737
- ((n = {}, t = {}) => {
738
- const e = t.locale ?? h();
739
- return e === "en" ? Oe() : e === "nl" ? Ne() : Le();
740
- })
741
- ), Me = (
742
- /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
743
- () => (
744
- /** @type {LocalizedString} */
745
- "Rendering"
746
- )
747
- ), ze = (
748
- /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
749
- () => (
750
- /** @type {LocalizedString} */
751
- "Renderen"
752
- )
753
- ), Fe = (
754
- /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
755
- () => (
756
- /** @type {LocalizedString} */
757
- "Rendern"
758
- )
759
- ), Pe = (
760
- /** @type {((inputs?: Loading_RenderingInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Loading_RenderingInputs, { locale?: "en" | "nl" | "de" }, {}>} */
761
- ((n = {}, t = {}) => {
762
- const e = t.locale ?? h();
763
- return e === "en" ? Me() : e === "nl" ? ze() : Fe();
764
- })
765
- ), $e = (
766
- /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
767
- () => (
768
- /** @type {LocalizedString} */
769
- "Screenshot"
770
- )
771
- ), De = (
772
- /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
773
- () => (
774
- /** @type {LocalizedString} */
775
- "Screenshot"
776
- )
777
- ), Be = (
778
- /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
779
- () => (
780
- /** @type {LocalizedString} */
781
- "Screenshot"
782
- )
783
- ), Ue = (
784
- /** @type {((inputs?: Controls_Screenshot_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Screenshot_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
785
- ((n = {}, t = {}) => {
786
- const e = t.locale ?? h();
787
- return e === "en" ? $e() : e === "nl" ? De() : Be();
788
- })
789
- ), Ve = (
790
- /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
791
- () => (
792
- /** @type {LocalizedString} */
793
- "Configure"
794
- )
795
- ), je = (
796
- /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
797
- () => (
798
- /** @type {LocalizedString} */
799
- "Configureren"
800
- )
801
- ), He = (
802
- /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
803
- () => (
804
- /** @type {LocalizedString} */
805
- "Konfigurieren"
806
- )
807
- ), Ge = (
808
- /** @type {((inputs?: Controls_Configurator_OpenInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Configurator_OpenInputs, { locale?: "en" | "nl" | "de" }, {}>} */
809
- ((n = {}, t = {}) => {
810
- const e = t.locale ?? h();
811
- return e === "en" ? Ve() : e === "nl" ? je() : He();
812
- })
813
- ), qe = (
814
- /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
815
- () => (
816
- /** @type {LocalizedString} */
817
- "Close"
818
- )
819
- ), Ze = (
820
- /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
821
- () => (
822
- /** @type {LocalizedString} */
823
- "Sluiten"
824
- )
825
- ), We = (
826
- /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
827
- () => (
828
- /** @type {LocalizedString} */
829
- "Schließen"
830
- )
831
- ), Ye = (
832
- /** @type {((inputs?: Controls_Configurator_CloseInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Configurator_CloseInputs, { locale?: "en" | "nl" | "de" }, {}>} */
833
- ((n = {}, t = {}) => {
834
- const e = t.locale ?? h();
835
- return e === "en" ? qe() : e === "nl" ? Ze() : We();
836
- })
837
- ), Xe = (
838
- /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
839
- () => (
840
- /** @type {LocalizedString} */
841
- "Reset camera"
842
- )
843
- ), Ke = (
844
- /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
845
- () => (
846
- /** @type {LocalizedString} */
847
- "Camera resetten"
848
- )
849
- ), Qe = (
850
- /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
851
- () => (
852
- /** @type {LocalizedString} */
853
- "Kamera zurücksetzen"
854
- )
855
- ), Je = (
856
- /** @type {((inputs?: Controls_Camera_ResetInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_ResetInputs, { locale?: "en" | "nl" | "de" }, {}>} */
857
- ((n = {}, t = {}) => {
858
- const e = t.locale ?? h();
859
- return e === "en" ? Xe() : e === "nl" ? Ke() : Qe();
860
- })
861
- ), tn = (
862
- /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
863
- () => (
864
- /** @type {LocalizedString} */
865
- "Zoom in"
866
- )
867
- ), en = (
868
- /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
869
- () => (
870
- /** @type {LocalizedString} */
871
- "Inzoomen"
872
- )
873
- ), nn = (
874
- /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
875
- () => (
876
- /** @type {LocalizedString} */
877
- "Hineinzoomen"
878
- )
879
- ), on = (
880
- /** @type {((inputs?: Controls_Camera_Zoomin1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Zoomin1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
881
- ((n = {}, t = {}) => {
882
- const e = t.locale ?? h();
883
- return e === "en" ? tn() : e === "nl" ? en() : nn();
884
- })
885
- ), sn = (
886
- /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
887
- () => (
888
- /** @type {LocalizedString} */
889
- "Zoom out"
890
- )
891
- ), rn = (
892
- /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
893
- () => (
894
- /** @type {LocalizedString} */
895
- "Uitzoomen"
896
- )
897
- ), an = (
898
- /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
899
- () => (
900
- /** @type {LocalizedString} */
901
- "Herauszoomen"
902
- )
903
- ), ln = (
904
- /** @type {((inputs?: Controls_Camera_Zoomout1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Zoomout1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
905
- ((n = {}, t = {}) => {
906
- const e = t.locale ?? h();
907
- return e === "en" ? sn() : e === "nl" ? rn() : an();
908
- })
909
- ), cn = (
910
- /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
911
- () => (
912
- /** @type {LocalizedString} */
913
- "Rotate left"
914
- )
915
- ), un = (
916
- /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
917
- () => (
918
- /** @type {LocalizedString} */
919
- "Naar links draaien"
920
- )
921
- ), dn = (
922
- /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
923
- () => (
924
- /** @type {LocalizedString} */
925
- "Nach links drehen"
926
- )
927
- ), hn = (
928
- /** @type {((inputs?: Controls_Camera_Rotateleft1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Rotateleft1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
929
- ((n = {}, t = {}) => {
930
- const e = t.locale ?? h();
931
- return e === "en" ? cn() : e === "nl" ? un() : dn();
932
- })
933
- ), mn = (
934
- /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
935
- () => (
936
- /** @type {LocalizedString} */
937
- "Rotate right"
938
- )
939
- ), pn = (
940
- /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
941
- () => (
942
- /** @type {LocalizedString} */
943
- "Naar rechts draaien"
944
- )
945
- ), fn = (
946
- /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
947
- () => (
948
- /** @type {LocalizedString} */
949
- "Nach rechts drehen"
950
- )
951
- ), gn = (
952
- /** @type {((inputs?: Controls_Camera_Rotateright1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Rotateright1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
953
- ((n = {}, t = {}) => {
954
- const e = t.locale ?? h();
955
- return e === "en" ? mn() : e === "nl" ? pn() : fn();
956
- })
957
- ), bn = (
958
- /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
959
- () => (
960
- /** @type {LocalizedString} */
961
- "Tilt up"
962
- )
963
- ), vn = (
964
- /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
965
- () => (
966
- /** @type {LocalizedString} */
967
- "Omhoog kantelen"
968
- )
969
- ), yn = (
970
- /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
971
- () => (
972
- /** @type {LocalizedString} */
973
- "Nach oben kippen"
974
- )
975
- ), _n = (
976
- /** @type {((inputs?: Controls_Camera_Tiltup1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Tiltup1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
977
- ((n = {}, t = {}) => {
978
- const e = t.locale ?? h();
979
- return e === "en" ? bn() : e === "nl" ? vn() : yn();
980
- })
981
- ), wn = (
982
- /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
983
- () => (
984
- /** @type {LocalizedString} */
985
- "Tilt down"
986
- )
987
- ), Cn = (
988
- /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
989
- () => (
990
- /** @type {LocalizedString} */
991
- "Omlaag kantelen"
992
- )
993
- ), kn = (
994
- /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
995
- () => (
996
- /** @type {LocalizedString} */
997
- "Nach unten kippen"
998
- )
999
- ), Sn = (
1000
- /** @type {((inputs?: Controls_Camera_Tiltdown1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Tiltdown1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1001
- ((n = {}, t = {}) => {
1002
- const e = t.locale ?? h();
1003
- return e === "en" ? wn() : e === "nl" ? Cn() : kn();
1004
- })
1005
- ), En = (
1006
- /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1007
- () => (
1008
- /** @type {LocalizedString} */
1009
- "Working…"
1010
- )
1011
- ), An = (
1012
- /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1013
- () => (
1014
- /** @type {LocalizedString} */
1015
- "Bezig…"
1016
- )
1017
- ), xn = (
1018
- /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1019
- () => (
1020
- /** @type {LocalizedString} */
1021
- "Läuft…"
1022
- )
1023
- ), Tn = (
1024
- /** @type {((inputs?: Commerce_Submit_BusyInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_BusyInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1025
- ((n = {}, t = {}) => {
1026
- const e = t.locale ?? h();
1027
- return e === "en" ? En() : e === "nl" ? An() : xn();
1028
- })
1029
- ), Rn = (
1030
- /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1031
- () => (
1032
- /** @type {LocalizedString} */
1033
- "Done"
1034
- )
1035
- ), In = (
1036
- /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1037
- () => (
1038
- /** @type {LocalizedString} */
1039
- "Gelukt"
1040
- )
1041
- ), On = (
1042
- /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1043
- () => (
1044
- /** @type {LocalizedString} */
1045
- "Fertig"
1046
- )
1047
- ), Nn = (
1048
- /** @type {((inputs?: Commerce_Submit_SuccessInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_SuccessInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1049
- ((n = {}, t = {}) => {
1050
- const e = t.locale ?? h();
1051
- return e === "en" ? Rn() : e === "nl" ? In() : On();
1052
- })
1053
- ), Ln = (
1054
- /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1055
- () => (
1056
- /** @type {LocalizedString} */
1057
- "Failed"
1058
- )
1059
- ), Mn = (
1060
- /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1061
- () => (
1062
- /** @type {LocalizedString} */
1063
- "Mislukt"
1064
- )
1065
- ), zn = (
1066
- /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1067
- () => (
1068
- /** @type {LocalizedString} */
1069
- "Fehlgeschlagen"
1070
- )
1071
- ), Fn = (
1072
- /** @type {((inputs?: Commerce_Submit_FailedInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_FailedInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1073
- ((n = {}, t = {}) => {
1074
- const e = t.locale ?? h();
1075
- return e === "en" ? Ln() : e === "nl" ? Mn() : zn();
1076
- })
1077
- ), _ = {
1078
- fontFamily: {
1079
- sans: ["Mona Sans", "ui-sans-serif", "system-ui", "sans-serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"]
1080
- },
1081
- fontSize: {
1082
- xs: "0.75rem",
1083
- // 12px
1084
- sm: "0.875rem",
1085
- // 14px
1086
- base: "1rem",
1087
- // 16px
1088
- lg: "1.125rem"
1089
- },
1090
- fontWeight: {
1091
- medium: "500",
1092
- semibold: "600"
1093
- }
1094
- }, Pn = _.fontFamily.sans.join(", ");
1095
- class $n {
1096
- qrLib = null;
1097
- dialog = null;
1098
- async show(t) {
1099
- this.hide(), this.qrLib ??= await import("./browser-ey6Iih3j.js").then((o) => o.b);
1100
- const e = await this.qrLib.toDataURL(t, {
1101
- width: 256,
1102
- margin: 4,
1103
- errorCorrectionLevel: "M",
1104
- color: { dark: m.black, light: m.white }
1105
- });
1106
- this.dialog = new Qt({ onClose: () => {
1107
- this.dialog = null;
1108
- } }), Object.assign(this.dialog.card.element.style, {
1109
- display: "flex",
1110
- flexDirection: "column",
1111
- alignItems: "center",
1112
- gap: a[3],
1113
- fontFamily: Pn
1114
- }), this.dialog.card.element.appendChild(this.createText(pe(), {
1115
- fontSize: _.fontSize.base,
1116
- fontWeight: _.fontWeight.semibold,
1117
- color: m.neutral[900]
1118
- })), this.dialog.card.element.appendChild(this.createText(ve(), {
1119
- fontSize: _.fontSize.sm,
1120
- color: m.neutral[600]
1121
- })), this.dialog.card.element.appendChild(this.createQrImage(e)), this.dialog.card.element.appendChild(this.createText(Ce(), {
1122
- fontSize: _.fontSize.xs,
1123
- color: m.neutral[500]
1124
- }));
1125
- const i = new g({
1126
- variant: "outline",
1127
- label: Ae(),
1128
- onClick: () => {
1129
- this.hide();
1130
- }
1131
- });
1132
- this.dialog.card.element.appendChild(i.element), this.dialog.open();
1133
- }
1134
- hide() {
1135
- this.dialog?.close(), this.dialog = null;
1136
- }
1137
- createText(t, e) {
1138
- const i = document.createElement("p");
1139
- return i.textContent = t, Object.assign(i.style, { margin: "0", textAlign: "center", ...e }), i;
1140
- }
1141
- createQrImage(t) {
1142
- const e = document.createElement("img");
1143
- return e.src = t, e.alt = "QR code", Object.assign(e.style, {
1144
- width: a[24],
1145
- height: a[24],
1146
- borderRadius: a[1]
1147
- }), e;
1148
- }
1149
- }
1150
- const x = _.fontFamily.sans.join(", "), Dn = `
1151
- --primary: oklch(5% 0 0);
1152
- --primary-foreground: oklch(100% 0 0);
1153
- --secondary: oklch(92% 0 0);
1154
- --secondary-foreground: oklch(5% 0 0);
1155
- --background: oklch(100% 0 0);
1156
- --foreground: oklch(5% 0 0);
1157
- --border: oklch(92% 0 0);
1158
- --ring: oklch(44% 0 0);
1159
- --radius: 0.625rem;
1160
- --accent: oklch(92% 0 0);
1161
- --accent-foreground: oklch(5% 0 0);
1162
- --muted-foreground: oklch(44% 0 0);
1163
- `, Bn = `
1164
- :root { ${Dn} }
1165
-
1166
- /* ── Button ──────────────────────────────────────────────── */
1167
- .ui-btn {
1168
- display: inline-flex;
1169
- align-items: center;
1170
- justify-content: center;
1171
- gap: 0.5rem;
1172
- border: 1px solid transparent;
1173
- border-radius: var(--radius);
1174
- font-family: ${x};
1175
- font-size: 0.875rem;
1176
- font-weight: 600;
1177
- white-space: nowrap;
1178
- cursor: pointer;
1179
- transition: all 200ms;
1180
- flex-shrink: 0;
1181
- }
1182
- .ui-btn:active { transform: scale(0.98); }
1183
- .ui-btn:disabled { opacity: 0.5; pointer-events: none; }
1184
- .ui-btn svg { inline-size: 1rem; block-size: 1rem; pointer-events: none; flex-shrink: 0; }
1185
-
1186
- .ui-btn--default {
1187
- background: var(--primary);
1188
- color: var(--primary-foreground);
1189
- box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
1190
- }
1191
- .ui-btn--default:hover { background: oklch(10% 0 0); }
1192
-
1193
- .ui-btn--outline {
1194
- background: var(--background);
1195
- color: var(--foreground);
1196
- border-color: var(--border);
1197
- box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1198
- }
1199
- .ui-btn--outline:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1200
-
1201
- .ui-btn--secondary {
1202
- background: var(--secondary);
1203
- color: var(--secondary-foreground);
1204
- box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1205
- }
1206
- .ui-btn--secondary:hover { background: oklch(88% 0 0); }
1207
-
1208
- .ui-btn--ghost { background: transparent; color: var(--foreground); }
1209
- .ui-btn--ghost:hover { background: var(--accent); }
1210
-
1211
- .ui-btn--size-default { block-size: 2.25rem; padding: 0 1rem; }
1212
- .ui-btn--size-sm { block-size: 2rem; padding: 0 0.75rem; border-radius: calc(var(--radius) - 0.125rem); }
1213
- .ui-btn--size-lg { block-size: 2.5rem; padding: 0 1.5rem; }
1214
- .ui-btn--size-icon { inline-size: 2.25rem; block-size: 2.25rem; padding: 0; }
1215
- .ui-btn--size-icon-sm { inline-size: 2rem; block-size: 2rem; padding: 0; }
1216
- .ui-btn--size-icon-lg { inline-size: 2.5rem; block-size: 2.5rem; padding: 0; }
1217
-
1218
- /* ── Toggle group ────────────────────────────────────────── */
1219
- .ui-toggle-group { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
1220
-
1221
- .ui-toggle-group-item {
1222
- display: inline-flex;
1223
- align-items: center;
1224
- justify-content: center;
1225
- block-size: 2rem;
1226
- padding: 0 0.75rem;
1227
- border: 1px solid var(--border);
1228
- border-radius: var(--radius);
1229
- background: var(--background);
1230
- color: var(--foreground);
1231
- font-family: ${x};
1232
- font-size: 0.875rem;
1233
- font-weight: 600;
1234
- cursor: pointer;
1235
- white-space: nowrap;
1236
- transition: all 200ms;
1237
- box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1238
- }
1239
- .ui-toggle-group-item:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1240
- .ui-toggle-group-item:active { transform: scale(0.98); }
1241
- .ui-toggle-group-item:focus-visible {
1242
- outline: none;
1243
- border-color: var(--ring);
1244
- box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1245
- }
1246
- .ui-toggle-group-item[data-state="on"] {
1247
- background: var(--primary);
1248
- color: var(--primary-foreground);
1249
- border-color: var(--primary);
1250
- }
1251
- .ui-toggle-group-item[data-state="on"]:hover { background: oklch(10% 0 0); }
1252
-
1253
- /* ── Native select ───────────────────────────────────────── */
1254
- .ui-native-select-wrap {
1255
- position: relative;
1256
- display: inline-flex;
1257
- min-inline-size: 8rem;
1258
- }
1259
-
1260
- .ui-native-select {
1261
- display: block;
1262
- inline-size: 100%;
1263
- block-size: 2.25rem;
1264
- padding: 0 2.25rem 0 0.75rem;
1265
- border: 1px solid var(--border);
1266
- border-radius: var(--radius);
1267
- background: var(--background);
1268
- appearance: none;
1269
- color: var(--foreground);
1270
- font-family: ${x};
1271
- font-size: 0.875rem;
1272
- font-weight: 400;
1273
- cursor: pointer;
1274
- transition: color 200ms, box-shadow 200ms, border-color 200ms;
1275
- box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1276
- }
1277
- .ui-native-select:hover { border-color: oklch(85% 0 0); }
1278
- .ui-native-select:focus-visible {
1279
- outline: none;
1280
- border-color: var(--ring);
1281
- box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1282
- }
1283
-
1284
- .ui-native-select-icon {
1285
- position: absolute;
1286
- inset-block-start: 50%;
1287
- inset-inline-end: 0.625rem;
1288
- transform: translateY(-50%);
1289
- color: var(--muted-foreground);
1290
- pointer-events: none;
1291
- }
1292
-
1293
- /* ── Annotation layer ───────────────────────────────────── */
1294
- .vd-annotations-layer {
1295
- position: absolute;
1296
- inset-block-start: 0;
1297
- inset-inline-start: 0;
1298
- inline-size: 100%;
1299
- block-size: 100%;
1300
- pointer-events: none;
1301
- overflow: visible !important; /* override CSS2DRenderer inline style */
1302
- }
1303
-
1304
- .vd-annotation-anchor {
1305
- position: relative;
1306
- }
1307
-
1308
- .vd-annotation-marker {
1309
- display: flex;
1310
- align-items: center;
1311
- justify-content: center;
1312
- inline-size: 1.75rem;
1313
- block-size: 1.75rem;
1314
- border-radius: 50%;
1315
- border: 2px solid oklch(100% 0 0);
1316
- background: var(--primary);
1317
- color: var(--primary-foreground);
1318
- font-family: ${x};
1319
- font-size: 0.8125rem;
1320
- font-weight: 600;
1321
- cursor: pointer;
1322
- pointer-events: auto;
1323
- box-shadow: 0 2px 8px oklch(5% 0 0 / 20%);
1324
- transition: transform 100ms ease;
1325
- position: relative;
1326
- }
1327
- .vd-annotation-marker:hover { transform: scale(1.1); }
1328
- .vd-annotation-marker:focus-visible {
1329
- outline: 2px solid var(--ring);
1330
- outline-offset: 2px;
1331
- }
1332
-
1333
- .vd-annotation-popup {
1334
- position: absolute;
1335
- inset-block-end: calc(100% + 0.5rem);
1336
- inset-inline-start: 50%;
1337
- transform: translateX(-50%);
1338
- background: var(--background);
1339
- color: var(--foreground);
1340
- border: 1px solid var(--border);
1341
- border-radius: var(--radius);
1342
- padding: 0.75rem 1rem;
1343
- max-inline-size: 15rem;
1344
- box-shadow: 0 4px 6px oklch(5% 0 0 / 8%), 0 2px 4px oklch(5% 0 0 / 6%);
1345
- pointer-events: auto;
1346
- white-space: normal;
1347
- }
1348
- .vd-annotation-popup:focus { outline: none; }
1349
-
1350
- .vd-annotation-popup-close {
1351
- position: absolute;
1352
- inset-block-start: 0.375rem;
1353
- inset-inline-end: 0.375rem;
1354
- display: flex;
1355
- align-items: center;
1356
- justify-content: center;
1357
- inline-size: 1.25rem;
1358
- block-size: 1.25rem;
1359
- padding: 0;
1360
- border: none;
1361
- border-radius: calc(var(--radius) - 4px);
1362
- background: transparent;
1363
- color: var(--muted-foreground);
1364
- cursor: pointer;
1365
- pointer-events: auto;
1366
- }
1367
- .vd-annotation-popup-close:hover {
1368
- color: var(--foreground);
1369
- background: var(--secondary);
1370
- }
1371
- .vd-annotation-popup-close:focus-visible {
1372
- outline: 2px solid var(--ring);
1373
- outline-offset: 1px;
1374
- }
1375
- .vd-annotation-popup-close svg {
1376
- inline-size: 0.875rem;
1377
- block-size: 0.875rem;
1378
- }
1379
-
1380
- @media (prefers-reduced-motion: no-preference) {
1381
- .vd-annotation-popup {
1382
- animation: vd-popup-in 150ms ease;
1383
- }
1384
- }
1385
-
1386
- @keyframes vd-popup-in {
1387
- from { opacity: 0; transform: translateX(-50%) translateY(4px); }
1388
- to { opacity: 1; transform: translateX(-50%) translateY(0); }
1389
- }
1390
-
1391
- .vd-annotation-popup-title {
1392
- font-family: ${x};
1393
- font-size: 0.875rem;
1394
- font-weight: 600;
1395
- line-height: 1.25rem;
1396
- /* Keep the title clear of the absolutely positioned close button. */
1397
- padding-inline-end: 1.5rem;
1398
- }
1399
-
1400
- .vd-annotation-popup-description {
1401
- font-family: ${x};
1402
- font-size: 0.8125rem;
1403
- font-weight: 400;
1404
- line-height: 1.125rem;
1405
- color: var(--muted-foreground);
1406
- margin-block-start: 0.25rem;
1407
- }
1408
- `;
1409
- let et = !1;
1410
- function Un() {
1411
- if (et || typeof document > "u")
1412
- return;
1413
- et = !0;
1414
- const n = document.createElement("style");
1415
- n.id = "vd-ui-vanilla", n.textContent = Bn, document.head.appendChild(n);
1416
- }
1417
- function Vn() {
1418
- return /* @__PURE__ */ new Date();
1419
- }
1420
- function jn(n) {
1421
- return n.toISOString();
1422
- }
1423
- function bt() {
1424
- return typeof navigator > "u" ? "" : navigator.userAgent;
1425
- }
1426
- function Hn() {
1427
- return typeof navigator > "u" ? !1 : navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
1428
- }
1429
- function Gn() {
1430
- return /iPhone|iPad|iPod/i.test(bt()) || Hn();
1431
- }
1432
- function qn() {
1433
- return /Android/i.test(bt());
1434
- }
1435
- function Zn() {
1436
- return Gn() || qn();
1437
- }
1438
- class Wn {
1439
- constructor(t) {
1440
- this.send = t;
1441
- }
1442
- send;
1443
- setEnabled(t) {
1444
- this.send({
1445
- type: c.ANNOTATION_TOGGLE,
1446
- enabled: t
1447
- });
1448
- }
1449
- }
1450
- class Yn {
1451
- constructor(t, e) {
1452
- this.value = t, this.attribute = e;
1453
- }
1454
- value;
1455
- attribute;
1456
- isSelectedValue = !1;
1457
- get isSelected() {
1458
- return this.isSelectedValue;
1459
- }
1460
- setSelected(t) {
1461
- this.isSelectedValue = t;
1462
- }
1463
- }
1464
- const E = {
1465
- NO_MAPPING: "NO_MAPPING",
1466
- ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
1467
- VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
1468
- PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
1469
- INVALID_PARAMETER: "INVALID_PARAMETER",
1470
- SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
1471
- };
1472
- class f extends Error {
1473
- code;
1474
- constructor(t, e) {
1475
- super(t), this.name = "VirtualdisplayError", this.code = e;
1476
- }
1477
- static valueNotFound(t, e) {
1478
- return new f(
1479
- `Value "${e}" not found for attribute "${t}"`,
1480
- E.VALUE_NOT_FOUND
1481
- );
1482
- }
1483
- static parentNotFound(t) {
1484
- return new f(
1485
- `Parent element not found: ${t}`,
1486
- E.PARENT_NOT_FOUND
1487
- );
1488
- }
1489
- static invalidParameter(t) {
1490
- return new f(t, E.INVALID_PARAMETER);
1491
- }
1492
- }
1493
- class Xn {
1494
- name;
1495
- defaultValue;
1496
- values;
1497
- onSelect;
1498
- onChangeCallback;
1499
- constructor(t) {
1500
- this.name = t.name, this.defaultValue = t.defaultValue, this.onSelect = t.onSelect, this.values = this.createValues(t.values);
1501
- }
1502
- /**
1503
- * Select a value for this attribute
1504
- * @throws {VirtualdisplayError} When value not found
1505
- */
1506
- select(t) {
1507
- if (!this.values.has(t))
1508
- throw f.valueNotFound(this.name, t);
1509
- this.onSelect(this.name, t);
1510
- }
1511
- getValues() {
1512
- return Array.from(this.values.values());
1513
- }
1514
- getValue(t) {
1515
- return this.values.get(t);
1516
- }
1517
- /** The currently selected value, or undefined before any selection lands. */
1518
- get selected() {
1519
- for (const t of this.values.values())
1520
- if (t.isSelected)
1521
- return t.value;
1522
- }
1523
- /**
1524
- * Register callback that fires when selection changes
1525
- * Receives the newly selected AttributeValue
1526
- */
1527
- onChange(t) {
1528
- this.onChangeCallback = t;
1529
- }
1530
- /**
1531
- * Update selection state from server mutation result
1532
- * Direct value comparison - no source/target translation
1533
- */
1534
- handleResult(t) {
1535
- let e;
1536
- for (const i of this.values.values()) {
1537
- const o = i.value === t;
1538
- i.setSelected(o), o && (e = i);
1539
- }
1540
- e !== void 0 && this.onChangeCallback !== void 0 && this.onChangeCallback(e);
1541
- }
1542
- createValues(t) {
1543
- const e = /* @__PURE__ */ new Map();
1544
- for (const i of t) {
1545
- const o = new Yn(i, this.name);
1546
- i === this.defaultValue && o.setSelected(!0), e.set(i, o);
1547
- }
1548
- return e;
1549
- }
1550
- }
1551
- class Kn {
1552
- attributes = /* @__PURE__ */ new Map();
1553
- constructor(t, e, i) {
1554
- for (const o of t) {
1555
- const s = this.buildOptions(o, e);
1556
- this.attributes.set(o.name, new Xn(s));
1557
- }
1558
- this.onMutationFailed = i;
1559
- }
1560
- onMutationFailed;
1561
- get(t) {
1562
- return this.attributes.get(t);
1563
- }
1564
- /** The current selection as attribute name→value pairs, skipping unset attributes. */
1565
- getSelection() {
1566
- const t = {};
1567
- for (const [e, i] of this.attributes) {
1568
- const o = i.selected;
1569
- o !== void 0 && (t[e] = o);
1570
- }
1571
- return t;
1572
- }
1573
- handleResults(t) {
1574
- for (const e of t) {
1575
- if (!e.success) {
1576
- this.onMutationFailed?.(e.name, e.value);
1577
- continue;
1578
- }
1579
- this.get(e.name)?.handleResult(e.value);
1580
- }
1581
- }
1582
- buildOptions(t, e) {
1583
- const i = {
1584
- name: t.name,
1585
- values: t.values,
1586
- onSelect: (o, s) => {
1587
- e([{ name: o, value: s }], !1);
1588
- }
1589
- };
1590
- return t.defaultValue !== void 0 ? { ...i, defaultValue: t.defaultValue } : i;
1591
- }
1592
- }
1593
- function Qn(n) {
1594
- const t = new Date(n.timestamp).toLocaleTimeString("en-GB", { hour12: !1 }), e = n.level.toUpperCase().padEnd(5), i = n.trace_id?.slice(0, 8) ?? "--------", o = n.msg, s = ["timestamp", "level", "msg", "service", "trace_id", "span_id"], r = Object.entries(n).filter(([l]) => !s.includes(l)).map(([l, d]) => `${l}=${JSON.stringify(d)}`).join(" ");
1595
- return `${t} ${e} [${i}] ${o}${r ? " " + r : ""}`;
1596
- }
1597
- function Jn(n) {
1598
- return JSON.stringify(n);
1599
- }
1600
- function ti(n, t) {
1601
- const e = t ? Qn(n) : Jn(n);
1602
- n.level === "error" ? console.error(e) : n.level === "warn" ? console.warn(e) : console.log(e);
1603
- }
1604
- function vt(n, t = 0) {
1605
- const e = {
1606
- name: n.name,
1607
- message: n.message
1608
- };
1609
- n.stack !== void 0 && (e.stack = n.stack);
1610
- for (const i of Object.getOwnPropertyNames(n)) {
1611
- if (i === "name" || i === "message" || i === "stack" || i === "cause")
1612
- continue;
1613
- const o = n[i];
1614
- o !== void 0 && (e[i] = o);
1615
- }
1616
- return n.cause !== void 0 && n.cause !== null && (e.cause = n.cause instanceof Error && t < 5 ? vt(n.cause, t + 1) : String(n.cause)), e;
1617
- }
1618
- function ei(n) {
1619
- const t = {};
1620
- for (const [e, i] of Object.entries(n))
1621
- t[e] = i instanceof Error ? vt(i) : i;
1622
- return t;
1623
- }
1624
- class ni {
1625
- constructor(t) {
1626
- this.config = t;
1627
- }
1628
- config;
1629
- /**
1630
- * Log at specified level with optional attributes.
1631
- */
1632
- log(t, e, i = {}) {
1633
- const o = this.config.traceProvider?.() ?? {}, s = {
1634
- timestamp: jn(Vn()),
1635
- level: t,
1636
- msg: e,
1637
- service: this.config.service,
1638
- ...o,
1639
- ...ei(i)
1640
- };
1641
- ti(s, this.config.isDev);
1642
- }
1643
- /**
1644
- * Log at DEBUG level (development only).
1645
- */
1646
- debug(t, e) {
1647
- this.config.isDev && this.log("debug", t, e);
1648
- }
1649
- /**
1650
- * Log at INFO level.
1651
- */
1652
- info(t, e) {
1653
- this.log("info", t, e);
1654
- }
1655
- /**
1656
- * Log at WARN level.
1657
- */
1658
- warn(t, e) {
1659
- this.log("warn", t, e);
1660
- }
1661
- /**
1662
- * Log at ERROR level.
1663
- */
1664
- error(t, e) {
1665
- this.log("error", t, e);
1666
- }
1667
- }
1668
- function yt(n, t) {
1669
- const e = t?.isDev !== void 0 ? t.isDev : typeof process < "u" ? process.env.NODE_ENV !== "production" : !0;
1670
- return new ni({ service: n, isDev: e, traceProvider: t?.traceProvider });
1671
- }
1672
- function O(n) {
1673
- if (n !== void 0)
1674
- return typeof n == "object" && n !== null && !Array.isArray(n) && !(n instanceof Error) ? n : n instanceof Error ? { error: n.message } : { value: n };
1675
- }
1676
- function _t(n) {
1677
- return {
1678
- debug: (t, e) => n.debug(t, O(e)),
1679
- info: (t, e) => n.info(t, O(e)),
1680
- warn: (t, e) => n.warn(t, O(e)),
1681
- error: (t, e) => n.error(t, O(e))
1682
- };
1683
- }
1684
- let wt = !1, w = _t(yt("client", { isDev: !1 }));
1685
- function ii() {
1686
- wt = !0, w = _t(yt("client", { isDev: !0 }));
1687
- }
1688
- function Gi() {
1689
- return wt;
1690
- }
1691
- class oi {
529
+ class ne {
1692
530
  constructor(t, e) {
1693
531
  this.send = t, this.onMutationFailed = e;
1694
532
  }
@@ -1700,9 +538,9 @@ class oi {
1700
538
  * Handle incoming MAPPING message from server
1701
539
  */
1702
540
  handleMappingReceived(t) {
1703
- w.debug("Initializing from server mapping", {
541
+ _.debug("Initializing from server mapping", {
1704
542
  attributeCount: t.attributes.length
1705
- }), this.registry = new Kn(
543
+ }), this.registry = new jt(
1706
544
  t.attributes,
1707
545
  (e, i) => this.sendMutation(e, i),
1708
546
  this.onMutationFailed
@@ -1736,7 +574,7 @@ class oi {
1736
574
  }
1737
575
  const i = this.registry.get(t);
1738
576
  if (i === void 0) {
1739
- w.warn(`onChange: attribute "${t}" not found — callback will not fire`);
577
+ _.warn(`onChange: attribute "${t}" not found — callback will not fire`);
1740
578
  return;
1741
579
  }
1742
580
  i.onChange(e);
@@ -1762,12 +600,12 @@ class oi {
1762
600
  replayPendingCallbacks() {
1763
601
  for (const { name: t, callback: e } of this.pendingCallbacks) {
1764
602
  const i = this.registry?.get(t);
1765
- i === void 0 && w.warn(`onChange: attribute "${t}" not found — buffered callback will not fire`), i?.onChange(e);
603
+ i === void 0 && _.warn(`onChange: attribute "${t}" not found — buffered callback will not fire`), i?.onChange(e);
1766
604
  }
1767
605
  this.pendingCallbacks.length = 0;
1768
606
  }
1769
607
  }
1770
- class si {
608
+ let ie = class {
1771
609
  constructor(t) {
1772
610
  this.send = t;
1773
611
  }
@@ -1829,8 +667,8 @@ class si {
1829
667
  };
1830
668
  this.send(t);
1831
669
  }
1832
- }
1833
- class ri {
670
+ };
671
+ class oe {
1834
672
  state;
1835
673
  limitsState;
1836
674
  changeCallbacks = [];
@@ -1918,20 +756,20 @@ class ri {
1918
756
  }
1919
757
  }
1920
758
  }
1921
- function ai(n, t) {
1922
- const e = n.filter((s) => li(s, t));
759
+ function se(n, t) {
760
+ const e = n.filter((s) => re(s, t));
1923
761
  if (e.length === 0)
1924
762
  return;
1925
- const i = Math.max(...e.map(nt)), o = e.filter((s) => nt(s) === i);
763
+ const i = Math.max(...e.map(K)), o = e.filter((s) => K(s) === i);
1926
764
  return o.length === 1 ? o[0] : void 0;
1927
765
  }
1928
- function li(n, t) {
766
+ function re(n, t) {
1929
767
  return Object.entries(n.attributes).every(([e, i]) => t[e] === i);
1930
768
  }
1931
- function nt(n) {
769
+ function K(n) {
1932
770
  return Object.keys(n.attributes).length;
1933
771
  }
1934
- class ci {
772
+ class ae {
1935
773
  constructor(t, e) {
1936
774
  this.dispatchChange = t, this.dispatchSubmit = e;
1937
775
  }
@@ -2028,288 +866,1515 @@ class ci {
2028
866
  handleSelectionChanged(t) {
2029
867
  this.selection = t, this.recompute();
2030
868
  }
2031
- recompute() {
2032
- this.resolved = this.resolve();
2033
- for (const t of this.changeCallbacks)
2034
- t(this.resolved);
2035
- this.dispatchChange(this.resolved), this.notifyRender();
869
+ recompute() {
870
+ this.resolved = this.resolve();
871
+ for (const t of this.changeCallbacks)
872
+ t(this.resolved);
873
+ this.dispatchChange(this.resolved), this.notifyRender();
874
+ }
875
+ notifyRender() {
876
+ for (const t of this.renderCallbacks)
877
+ t();
878
+ }
879
+ resolve() {
880
+ if (this.pricing === void 0)
881
+ return;
882
+ const t = se(this.pricing.rules, this.selection);
883
+ if (!(t === void 0 || t.availability === At.HIDDEN))
884
+ return {
885
+ attributes: { ...this.selection },
886
+ reference: t.reference,
887
+ price: t.priceCents,
888
+ currency: this.pricing.currency,
889
+ availability: t.availability
890
+ };
891
+ }
892
+ }
893
+ class le {
894
+ constructor(t, e) {
895
+ this.send = t, this.dispatch = e;
896
+ }
897
+ send;
898
+ dispatch;
899
+ focused = null;
900
+ attributes = [];
901
+ changeCallbacks = [];
902
+ /** The focused component name, or null in overview. */
903
+ get current() {
904
+ return this.focused;
905
+ }
906
+ /**
907
+ * Focus a component by name. Sends a focus message to the viewer.
908
+ * @throws VirtualdisplayError if component is not a non-empty string
909
+ */
910
+ set(t) {
911
+ if (typeof t != "string" || t.length === 0)
912
+ throw f.invalidParameter(
913
+ `set() requires a non-empty component name, got ${String(t)}`
914
+ );
915
+ this.send({ type: c.FOCUS, action: Q.FOCUS, component: t });
916
+ }
917
+ /** Return to the overview — unfocus the current component. */
918
+ clear() {
919
+ this.send({ type: c.FOCUS, action: Q.UNFOCUS });
920
+ }
921
+ /**
922
+ * Subscribe to focus changes. The callback fires with the focus state when a
923
+ * component is focused, and with null when returning to overview.
924
+ */
925
+ onChange(t) {
926
+ this.changeCallbacks.push(t);
927
+ }
928
+ /** Called by the client when a FocusStateMessage is received from the viewer. */
929
+ handleStateReceived(t) {
930
+ t.state === kt.FOCUSED && t.component !== void 0 ? (this.focused = t.component, this.attributes = t.attributes ?? []) : (this.focused = null, this.attributes = []);
931
+ const e = this.focused !== null ? { component: this.focused, attributes: this.attributes } : null;
932
+ for (const i of this.changeCallbacks)
933
+ i(e);
934
+ this.dispatch(e);
935
+ }
936
+ }
937
+ class ce {
938
+ constructor(t) {
939
+ this.filename = t;
940
+ }
941
+ filename;
942
+ developedCallbacks = /* @__PURE__ */ new Set();
943
+ errorCallbacks = /* @__PURE__ */ new Set();
944
+ developed = !1;
945
+ failed = !1;
946
+ photoData = null;
947
+ /**
948
+ * Register a callback for when the photo is developed
949
+ * If the photo is already developed, the callback is called immediately
950
+ *
951
+ * @param callback Function to call with photo data
952
+ * @returns This Photo instance for chaining
953
+ */
954
+ onDeveloped(t) {
955
+ return this.developed && this.photoData !== null ? (t(this.photoData), this) : (this.developedCallbacks.add(t), this);
956
+ }
957
+ /**
958
+ * Register a callback for when the photo capture fails
959
+ *
960
+ * @param callback Function to call with error
961
+ * @returns This Photo instance for chaining
962
+ */
963
+ onError(t) {
964
+ return this.errorCallbacks.add(t), this;
965
+ }
966
+ /**
967
+ * Get the filename for this photo
968
+ */
969
+ getFilename() {
970
+ return this.filename;
971
+ }
972
+ /**
973
+ * Mark the photo as developed and notify all callbacks
974
+ * This method is called by Snapshot when the photo data arrives from the server
975
+ */
976
+ develop(t) {
977
+ this.developed || this.failed || (this.photoData = t, this.developed = !0, this.developedCallbacks.forEach((e) => {
978
+ e(this.photoData);
979
+ }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
980
+ }
981
+ /**
982
+ * Mark the photo as failed and notify error callbacks
983
+ */
984
+ fail(t) {
985
+ this.developed || this.failed || (this.failed = !0, this.errorCallbacks.forEach((e) => {
986
+ e(t);
987
+ }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
988
+ }
989
+ }
990
+ const X = 3e4;
991
+ class ue {
992
+ constructor(t) {
993
+ this.send = t;
994
+ }
995
+ send;
996
+ validExtensions = [".png"];
997
+ pendingPhotos = /* @__PURE__ */ new Map();
998
+ /**
999
+ * Take a snapshot of the current 3D model view
1000
+ *
1001
+ * @param filename The filename for the snapshot (must include valid extension)
1002
+ * @returns Photo object for registering callbacks
1003
+ * @throws VirtualdisplayError if filename is invalid
1004
+ */
1005
+ take(t) {
1006
+ this.validateFilename(t), _.debug("Taking snapshot", { filename: t });
1007
+ const e = new ce(t), i = setTimeout(() => {
1008
+ this.pendingPhotos.delete(t), _.warn("Snapshot timed out", { filename: t }), e.fail(new f(
1009
+ `Snapshot timed out: the viewer did not respond within ${X}ms`,
1010
+ A.SNAPSHOT_FAILED
1011
+ ));
1012
+ }, X);
1013
+ return this.pendingPhotos.set(t, { photo: e, timeout: i }), this.send({
1014
+ type: c.SNAPSHOT,
1015
+ filename: t
1016
+ }), e;
1017
+ }
1018
+ /**
1019
+ * Handle incoming snapshot result from server
1020
+ * Matches the message to a pending photo by filename, then develops or fails it
1021
+ */
1022
+ handleSnapshotResult(t) {
1023
+ const e = this.pendingPhotos.get(t.filename);
1024
+ if (e === void 0) {
1025
+ _.warn("No pending photo found for filename", {
1026
+ filename: t.filename,
1027
+ pendingFilenames: Array.from(this.pendingPhotos.keys())
1028
+ });
1029
+ return;
1030
+ }
1031
+ clearTimeout(e.timeout), this.pendingPhotos.delete(t.filename);
1032
+ const { photo: i } = e;
1033
+ if (t.error !== void 0) {
1034
+ i.fail(new f(t.error, A.SNAPSHOT_FAILED));
1035
+ return;
1036
+ }
1037
+ t.data !== void 0 && i.develop({ filename: t.filename, data: t.data });
1038
+ }
1039
+ /**
1040
+ * Fail any pending photos and forget them. Called from client destroy
1041
+ * so callers awaiting onError() see a deterministic outcome instead of
1042
+ * leaking captures whose results will never arrive.
1043
+ */
1044
+ clear() {
1045
+ if (this.pendingPhotos.size === 0)
1046
+ return;
1047
+ const t = new f(
1048
+ "Client destroyed before snapshot completed",
1049
+ A.SNAPSHOT_FAILED
1050
+ );
1051
+ for (const { photo: e, timeout: i } of this.pendingPhotos.values())
1052
+ clearTimeout(i), e.fail(t);
1053
+ this.pendingPhotos.clear();
1054
+ }
1055
+ /**
1056
+ * Validate filename has a valid image extension
1057
+ */
1058
+ validateFilename(t) {
1059
+ if (t === "" || typeof t != "string")
1060
+ throw new f(
1061
+ "Filename must be a non-empty string",
1062
+ A.INVALID_PARAMETER
1063
+ );
1064
+ if (!this.validExtensions.some(
1065
+ (i) => t.toLowerCase().endsWith(i)
1066
+ ))
1067
+ throw new f(
1068
+ `Invalid filename: "${t}". Filename must end with one of: ${this.validExtensions.join(", ")}`,
1069
+ A.INVALID_PARAMETER
1070
+ );
1071
+ if (t.includes("..") || t.includes("/") || t.includes("\\"))
1072
+ throw new f(
1073
+ 'Filename cannot contain path separators or ".."',
1074
+ A.INVALID_PARAMETER
1075
+ );
1076
+ }
1077
+ }
1078
+ class de {
1079
+ container = null;
1080
+ attach(t) {
1081
+ this.container = t;
1082
+ }
1083
+ dispatch(t, e, i = !1) {
1084
+ const o = new CustomEvent(t, { detail: e, cancelable: i });
1085
+ return this.container?.dispatchEvent(o), o;
1086
+ }
1087
+ on(t, e) {
1088
+ const i = (o) => {
1089
+ const s = o, r = { ...s.detail, preventDefault: () => {
1090
+ s.preventDefault();
1091
+ } };
1092
+ e(r);
1093
+ };
1094
+ return this.container?.addEventListener(t, i), () => {
1095
+ this.container?.removeEventListener(t, i);
1096
+ };
1097
+ }
1098
+ }
1099
+ const he = {}, me = "en", mt = (
1100
+ /** @type {const} */
1101
+ ["en", "nl", "de"]
1102
+ ), Z = [
1103
+ "baseLocale"
1104
+ ], J = [];
1105
+ let tt, et;
1106
+ function pe(n) {
1107
+ if (J.length === 0)
1108
+ return;
1109
+ const t = typeof n == "string" ? n : n.href;
1110
+ if (tt === t)
1111
+ return et;
1112
+ const e = new URL(t, "http://dummy.com");
1113
+ let i;
1114
+ for (const o of J)
1115
+ if (new he(o.match, e.href).exec(e.href)) {
1116
+ i = o;
1117
+ break;
1118
+ }
1119
+ return tt = t, et = i, i;
1120
+ }
1121
+ function pt(n) {
1122
+ const t = pe(n);
1123
+ return t && t.exclude !== !0 && Array.isArray(t.strategy) ? t.strategy : Z;
1124
+ }
1125
+ const G = typeof window > "u";
1126
+ globalThis.__paraglide = /** @type {any} */
1127
+ globalThis.__paraglide ?? {};
1128
+ globalThis.__paraglide.ssr = /** @type {any} */
1129
+ globalThis.__paraglide.ssr ?? {};
1130
+ let nt = !1, h = () => {
1131
+ let n = Z;
1132
+ !G && typeof window < "u" && window.location?.href && (n = pt(window.location.href));
1133
+ const t = fe(n);
1134
+ if (t)
1135
+ return nt || (nt = !0, ft(t, { reload: !1 })), t;
1136
+ throw new Error("No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found");
1137
+ };
1138
+ function fe(n, t) {
1139
+ let e;
1140
+ for (const i of n) {
1141
+ if (i === "baseLocale")
1142
+ e = me;
1143
+ else if (bt(i) && N.has(i)) {
1144
+ const s = N.get(i);
1145
+ if (s) {
1146
+ const r = s.getLocale();
1147
+ if (r instanceof Promise)
1148
+ continue;
1149
+ if (r !== void 0)
1150
+ return be(r);
1151
+ }
1152
+ }
1153
+ const o = gt(e);
1154
+ if (o)
1155
+ return o;
1156
+ }
1157
+ }
1158
+ const ge = (n) => {
1159
+ window.location.reload();
1160
+ };
1161
+ let ft = (n, t) => {
1162
+ const e = {
1163
+ reload: !0,
1164
+ ...t
1165
+ };
1166
+ let i;
1167
+ try {
1168
+ i = h();
1169
+ } catch {
2036
1170
  }
2037
- notifyRender() {
2038
- for (const t of this.renderCallbacks)
2039
- t();
1171
+ const o = [];
1172
+ let s = Z;
1173
+ !G && typeof window < "u" && window.location?.href && (s = pt(window.location.href));
1174
+ for (const l of s)
1175
+ if (l !== "baseLocale" && bt(l) && N.has(l)) {
1176
+ const d = N.get(l);
1177
+ if (d) {
1178
+ let u = d.setLocale(n);
1179
+ u instanceof Promise && (u = u.catch((p) => {
1180
+ throw new Error(`Custom strategy "${l}" setLocale failed.`, {
1181
+ cause: p
1182
+ });
1183
+ }), o.push(u));
1184
+ }
1185
+ }
1186
+ const r = () => {
1187
+ !G && e.reload && window.location && n !== i && ge();
1188
+ };
1189
+ if (o.length)
1190
+ return Promise.all(o).then(() => {
1191
+ r();
1192
+ });
1193
+ r();
1194
+ };
1195
+ function gt(n) {
1196
+ if (typeof n != "string")
1197
+ return;
1198
+ const t = n.toLowerCase();
1199
+ for (const e of mt)
1200
+ if (e.toLowerCase() === t)
1201
+ return e;
1202
+ }
1203
+ function be(n) {
1204
+ const t = gt(n);
1205
+ if (t)
1206
+ return t;
1207
+ throw new Error(`Invalid locale: ${n}. Expected one of: ${mt.join(", ")}`);
1208
+ }
1209
+ const N = /* @__PURE__ */ new Map();
1210
+ function bt(n) {
1211
+ return typeof n == "string" && /^custom-[A-Za-z0-9_-]+$/.test(n);
1212
+ }
1213
+ const ve = (
1214
+ /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
1215
+ () => (
1216
+ /** @type {LocalizedString} */
1217
+ "Activate AR"
1218
+ )
1219
+ ), ye = (
1220
+ /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
1221
+ () => (
1222
+ /** @type {LocalizedString} */
1223
+ "AR activeren"
1224
+ )
1225
+ ), we = (
1226
+ /** @type {(inputs: Controls_Ar_Buttonlabel1Inputs) => LocalizedString} */
1227
+ () => (
1228
+ /** @type {LocalizedString} */
1229
+ "AR aktivieren"
1230
+ )
1231
+ ), _e = (
1232
+ /** @type {((inputs?: Controls_Ar_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Ar_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1233
+ ((n = {}, t = {}) => {
1234
+ const e = t.locale ?? h();
1235
+ return e === "en" ? ve() : e === "nl" ? ye() : we();
1236
+ })
1237
+ ), Ce = (
1238
+ /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
1239
+ () => (
1240
+ /** @type {LocalizedString} */
1241
+ "Scan to view in AR"
1242
+ )
1243
+ ), Se = (
1244
+ /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
1245
+ () => (
1246
+ /** @type {LocalizedString} */
1247
+ "Scan om in AR te bekijken"
1248
+ )
1249
+ ), ke = (
1250
+ /** @type {(inputs: Controls_Qr_Modaltitle1Inputs) => LocalizedString} */
1251
+ () => (
1252
+ /** @type {LocalizedString} */
1253
+ "Scannen für AR-Ansicht"
1254
+ )
1255
+ ), Ae = (
1256
+ /** @type {((inputs?: Controls_Qr_Modaltitle1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modaltitle1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1257
+ ((n = {}, t = {}) => {
1258
+ const e = t.locale ?? h();
1259
+ return e === "en" ? Ce() : e === "nl" ? Se() : ke();
1260
+ })
1261
+ ), Ee = (
1262
+ /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
1263
+ () => (
1264
+ /** @type {LocalizedString} */
1265
+ "Use your smartphone camera to scan this QR code"
1266
+ )
1267
+ ), xe = (
1268
+ /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
1269
+ () => (
1270
+ /** @type {LocalizedString} */
1271
+ "Gebruik de camera van je smartphone om deze QR-code te scannen"
1272
+ )
1273
+ ), Te = (
1274
+ /** @type {(inputs: Controls_Qr_Modaldescription1Inputs) => LocalizedString} */
1275
+ () => (
1276
+ /** @type {LocalizedString} */
1277
+ "Verwende die Kamera deines Smartphones, um diesen QR-Code zu scannen"
1278
+ )
1279
+ ), Re = (
1280
+ /** @type {((inputs?: Controls_Qr_Modaldescription1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modaldescription1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1281
+ ((n = {}, t = {}) => {
1282
+ const e = t.locale ?? h();
1283
+ return e === "en" ? Ee() : e === "nl" ? xe() : Te();
1284
+ })
1285
+ ), Ie = (
1286
+ /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
1287
+ () => (
1288
+ /** @type {LocalizedString} */
1289
+ "Your phone will open the 3D model in AR mode."
1290
+ )
1291
+ ), Oe = (
1292
+ /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
1293
+ () => (
1294
+ /** @type {LocalizedString} */
1295
+ "Je telefoon opent het 3D-model in AR-modus."
1296
+ )
1297
+ ), Ne = (
1298
+ /** @type {(inputs: Controls_Qr_Modalinstructions1Inputs) => LocalizedString} */
1299
+ () => (
1300
+ /** @type {LocalizedString} */
1301
+ "Dein Telefon öffnet das 3D-Modell im AR-Modus."
1302
+ )
1303
+ ), Le = (
1304
+ /** @type {((inputs?: Controls_Qr_Modalinstructions1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Modalinstructions1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1305
+ ((n = {}, t = {}) => {
1306
+ const e = t.locale ?? h();
1307
+ return e === "en" ? Ie() : e === "nl" ? Oe() : Ne();
1308
+ })
1309
+ ), Me = (
1310
+ /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
1311
+ () => (
1312
+ /** @type {LocalizedString} */
1313
+ "Close"
1314
+ )
1315
+ ), ze = (
1316
+ /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
1317
+ () => (
1318
+ /** @type {LocalizedString} */
1319
+ "Sluiten"
1320
+ )
1321
+ ), Pe = (
1322
+ /** @type {(inputs: Controls_Qr_Closebutton1Inputs) => LocalizedString} */
1323
+ () => (
1324
+ /** @type {LocalizedString} */
1325
+ "Schließen"
1326
+ )
1327
+ ), Fe = (
1328
+ /** @type {((inputs?: Controls_Qr_Closebutton1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Qr_Closebutton1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1329
+ ((n = {}, t = {}) => {
1330
+ const e = t.locale ?? h();
1331
+ return e === "en" ? Me() : e === "nl" ? ze() : Pe();
1332
+ })
1333
+ ), $e = (
1334
+ /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
1335
+ () => (
1336
+ /** @type {LocalizedString} */
1337
+ "Fullscreen"
1338
+ )
1339
+ ), De = (
1340
+ /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
1341
+ () => (
1342
+ /** @type {LocalizedString} */
1343
+ "Volledig scherm"
1344
+ )
1345
+ ), Be = (
1346
+ /** @type {(inputs: Controls_Fullscreen_Buttonlabel1Inputs) => LocalizedString} */
1347
+ () => (
1348
+ /** @type {LocalizedString} */
1349
+ "Vollbild"
1350
+ )
1351
+ ), Ue = (
1352
+ /** @type {((inputs?: Controls_Fullscreen_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Fullscreen_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1353
+ ((n = {}, t = {}) => {
1354
+ const e = t.locale ?? h();
1355
+ return e === "en" ? $e() : e === "nl" ? De() : Be();
1356
+ })
1357
+ ), Ve = (
1358
+ /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
1359
+ () => (
1360
+ /** @type {LocalizedString} */
1361
+ "Downloading"
1362
+ )
1363
+ ), je = (
1364
+ /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
1365
+ () => (
1366
+ /** @type {LocalizedString} */
1367
+ "Downloaden"
1368
+ )
1369
+ ), He = (
1370
+ /** @type {(inputs: Loading_Downloadingshort1Inputs) => LocalizedString} */
1371
+ () => (
1372
+ /** @type {LocalizedString} */
1373
+ "Herunterladen"
1374
+ )
1375
+ ), vt = (
1376
+ /** @type {((inputs?: Loading_Downloadingshort1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Loading_Downloadingshort1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1377
+ ((n = {}, t = {}) => {
1378
+ const e = t.locale ?? h();
1379
+ return e === "en" ? Ve() : e === "nl" ? je() : He();
1380
+ })
1381
+ ), qe = (
1382
+ /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
1383
+ () => (
1384
+ /** @type {LocalizedString} */
1385
+ "Rendering"
1386
+ )
1387
+ ), Ge = (
1388
+ /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
1389
+ () => (
1390
+ /** @type {LocalizedString} */
1391
+ "Renderen"
1392
+ )
1393
+ ), Ze = (
1394
+ /** @type {(inputs: Loading_RenderingInputs) => LocalizedString} */
1395
+ () => (
1396
+ /** @type {LocalizedString} */
1397
+ "Rendern"
1398
+ )
1399
+ ), We = (
1400
+ /** @type {((inputs?: Loading_RenderingInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Loading_RenderingInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1401
+ ((n = {}, t = {}) => {
1402
+ const e = t.locale ?? h();
1403
+ return e === "en" ? qe() : e === "nl" ? Ge() : Ze();
1404
+ })
1405
+ ), Qe = (
1406
+ /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
1407
+ () => (
1408
+ /** @type {LocalizedString} */
1409
+ "Screenshot"
1410
+ )
1411
+ ), Ye = (
1412
+ /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
1413
+ () => (
1414
+ /** @type {LocalizedString} */
1415
+ "Screenshot"
1416
+ )
1417
+ ), Ke = (
1418
+ /** @type {(inputs: Controls_Screenshot_Buttonlabel1Inputs) => LocalizedString} */
1419
+ () => (
1420
+ /** @type {LocalizedString} */
1421
+ "Screenshot"
1422
+ )
1423
+ ), Xe = (
1424
+ /** @type {((inputs?: Controls_Screenshot_Buttonlabel1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Screenshot_Buttonlabel1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1425
+ ((n = {}, t = {}) => {
1426
+ const e = t.locale ?? h();
1427
+ return e === "en" ? Qe() : e === "nl" ? Ye() : Ke();
1428
+ })
1429
+ ), Je = (
1430
+ /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
1431
+ () => (
1432
+ /** @type {LocalizedString} */
1433
+ "Configure"
1434
+ )
1435
+ ), tn = (
1436
+ /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
1437
+ () => (
1438
+ /** @type {LocalizedString} */
1439
+ "Configureren"
1440
+ )
1441
+ ), en = (
1442
+ /** @type {(inputs: Controls_Configurator_OpenInputs) => LocalizedString} */
1443
+ () => (
1444
+ /** @type {LocalizedString} */
1445
+ "Konfigurieren"
1446
+ )
1447
+ ), nn = (
1448
+ /** @type {((inputs?: Controls_Configurator_OpenInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Configurator_OpenInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1449
+ ((n = {}, t = {}) => {
1450
+ const e = t.locale ?? h();
1451
+ return e === "en" ? Je() : e === "nl" ? tn() : en();
1452
+ })
1453
+ ), on = (
1454
+ /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
1455
+ () => (
1456
+ /** @type {LocalizedString} */
1457
+ "Close"
1458
+ )
1459
+ ), sn = (
1460
+ /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
1461
+ () => (
1462
+ /** @type {LocalizedString} */
1463
+ "Sluiten"
1464
+ )
1465
+ ), rn = (
1466
+ /** @type {(inputs: Controls_Configurator_CloseInputs) => LocalizedString} */
1467
+ () => (
1468
+ /** @type {LocalizedString} */
1469
+ "Schließen"
1470
+ )
1471
+ ), an = (
1472
+ /** @type {((inputs?: Controls_Configurator_CloseInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Configurator_CloseInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1473
+ ((n = {}, t = {}) => {
1474
+ const e = t.locale ?? h();
1475
+ return e === "en" ? on() : e === "nl" ? sn() : rn();
1476
+ })
1477
+ ), ln = (
1478
+ /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
1479
+ () => (
1480
+ /** @type {LocalizedString} */
1481
+ "Reset camera"
1482
+ )
1483
+ ), cn = (
1484
+ /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
1485
+ () => (
1486
+ /** @type {LocalizedString} */
1487
+ "Camera resetten"
1488
+ )
1489
+ ), un = (
1490
+ /** @type {(inputs: Controls_Camera_ResetInputs) => LocalizedString} */
1491
+ () => (
1492
+ /** @type {LocalizedString} */
1493
+ "Kamera zurücksetzen"
1494
+ )
1495
+ ), dn = (
1496
+ /** @type {((inputs?: Controls_Camera_ResetInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_ResetInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1497
+ ((n = {}, t = {}) => {
1498
+ const e = t.locale ?? h();
1499
+ return e === "en" ? ln() : e === "nl" ? cn() : un();
1500
+ })
1501
+ ), hn = (
1502
+ /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
1503
+ () => (
1504
+ /** @type {LocalizedString} */
1505
+ "Zoom in"
1506
+ )
1507
+ ), mn = (
1508
+ /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
1509
+ () => (
1510
+ /** @type {LocalizedString} */
1511
+ "Inzoomen"
1512
+ )
1513
+ ), pn = (
1514
+ /** @type {(inputs: Controls_Camera_Zoomin1Inputs) => LocalizedString} */
1515
+ () => (
1516
+ /** @type {LocalizedString} */
1517
+ "Hineinzoomen"
1518
+ )
1519
+ ), fn = (
1520
+ /** @type {((inputs?: Controls_Camera_Zoomin1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Zoomin1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1521
+ ((n = {}, t = {}) => {
1522
+ const e = t.locale ?? h();
1523
+ return e === "en" ? hn() : e === "nl" ? mn() : pn();
1524
+ })
1525
+ ), gn = (
1526
+ /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
1527
+ () => (
1528
+ /** @type {LocalizedString} */
1529
+ "Zoom out"
1530
+ )
1531
+ ), bn = (
1532
+ /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
1533
+ () => (
1534
+ /** @type {LocalizedString} */
1535
+ "Uitzoomen"
1536
+ )
1537
+ ), vn = (
1538
+ /** @type {(inputs: Controls_Camera_Zoomout1Inputs) => LocalizedString} */
1539
+ () => (
1540
+ /** @type {LocalizedString} */
1541
+ "Herauszoomen"
1542
+ )
1543
+ ), yn = (
1544
+ /** @type {((inputs?: Controls_Camera_Zoomout1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Zoomout1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1545
+ ((n = {}, t = {}) => {
1546
+ const e = t.locale ?? h();
1547
+ return e === "en" ? gn() : e === "nl" ? bn() : vn();
1548
+ })
1549
+ ), wn = (
1550
+ /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
1551
+ () => (
1552
+ /** @type {LocalizedString} */
1553
+ "Rotate left"
1554
+ )
1555
+ ), _n = (
1556
+ /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
1557
+ () => (
1558
+ /** @type {LocalizedString} */
1559
+ "Naar links draaien"
1560
+ )
1561
+ ), Cn = (
1562
+ /** @type {(inputs: Controls_Camera_Rotateleft1Inputs) => LocalizedString} */
1563
+ () => (
1564
+ /** @type {LocalizedString} */
1565
+ "Nach links drehen"
1566
+ )
1567
+ ), Sn = (
1568
+ /** @type {((inputs?: Controls_Camera_Rotateleft1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Rotateleft1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1569
+ ((n = {}, t = {}) => {
1570
+ const e = t.locale ?? h();
1571
+ return e === "en" ? wn() : e === "nl" ? _n() : Cn();
1572
+ })
1573
+ ), kn = (
1574
+ /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
1575
+ () => (
1576
+ /** @type {LocalizedString} */
1577
+ "Rotate right"
1578
+ )
1579
+ ), An = (
1580
+ /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
1581
+ () => (
1582
+ /** @type {LocalizedString} */
1583
+ "Naar rechts draaien"
1584
+ )
1585
+ ), En = (
1586
+ /** @type {(inputs: Controls_Camera_Rotateright1Inputs) => LocalizedString} */
1587
+ () => (
1588
+ /** @type {LocalizedString} */
1589
+ "Nach rechts drehen"
1590
+ )
1591
+ ), xn = (
1592
+ /** @type {((inputs?: Controls_Camera_Rotateright1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Rotateright1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1593
+ ((n = {}, t = {}) => {
1594
+ const e = t.locale ?? h();
1595
+ return e === "en" ? kn() : e === "nl" ? An() : En();
1596
+ })
1597
+ ), Tn = (
1598
+ /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
1599
+ () => (
1600
+ /** @type {LocalizedString} */
1601
+ "Tilt up"
1602
+ )
1603
+ ), Rn = (
1604
+ /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
1605
+ () => (
1606
+ /** @type {LocalizedString} */
1607
+ "Omhoog kantelen"
1608
+ )
1609
+ ), In = (
1610
+ /** @type {(inputs: Controls_Camera_Tiltup1Inputs) => LocalizedString} */
1611
+ () => (
1612
+ /** @type {LocalizedString} */
1613
+ "Nach oben kippen"
1614
+ )
1615
+ ), On = (
1616
+ /** @type {((inputs?: Controls_Camera_Tiltup1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Tiltup1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1617
+ ((n = {}, t = {}) => {
1618
+ const e = t.locale ?? h();
1619
+ return e === "en" ? Tn() : e === "nl" ? Rn() : In();
1620
+ })
1621
+ ), Nn = (
1622
+ /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
1623
+ () => (
1624
+ /** @type {LocalizedString} */
1625
+ "Tilt down"
1626
+ )
1627
+ ), Ln = (
1628
+ /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
1629
+ () => (
1630
+ /** @type {LocalizedString} */
1631
+ "Omlaag kantelen"
1632
+ )
1633
+ ), Mn = (
1634
+ /** @type {(inputs: Controls_Camera_Tiltdown1Inputs) => LocalizedString} */
1635
+ () => (
1636
+ /** @type {LocalizedString} */
1637
+ "Nach unten kippen"
1638
+ )
1639
+ ), zn = (
1640
+ /** @type {((inputs?: Controls_Camera_Tiltdown1Inputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Controls_Camera_Tiltdown1Inputs, { locale?: "en" | "nl" | "de" }, {}>} */
1641
+ ((n = {}, t = {}) => {
1642
+ const e = t.locale ?? h();
1643
+ return e === "en" ? Nn() : e === "nl" ? Ln() : Mn();
1644
+ })
1645
+ ), Pn = (
1646
+ /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1647
+ () => (
1648
+ /** @type {LocalizedString} */
1649
+ "Working…"
1650
+ )
1651
+ ), Fn = (
1652
+ /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1653
+ () => (
1654
+ /** @type {LocalizedString} */
1655
+ "Bezig…"
1656
+ )
1657
+ ), $n = (
1658
+ /** @type {(inputs: Commerce_Submit_BusyInputs) => LocalizedString} */
1659
+ () => (
1660
+ /** @type {LocalizedString} */
1661
+ "Läuft…"
1662
+ )
1663
+ ), Dn = (
1664
+ /** @type {((inputs?: Commerce_Submit_BusyInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_BusyInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1665
+ ((n = {}, t = {}) => {
1666
+ const e = t.locale ?? h();
1667
+ return e === "en" ? Pn() : e === "nl" ? Fn() : $n();
1668
+ })
1669
+ ), Bn = (
1670
+ /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1671
+ () => (
1672
+ /** @type {LocalizedString} */
1673
+ "Done"
1674
+ )
1675
+ ), Un = (
1676
+ /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1677
+ () => (
1678
+ /** @type {LocalizedString} */
1679
+ "Gelukt"
1680
+ )
1681
+ ), Vn = (
1682
+ /** @type {(inputs: Commerce_Submit_SuccessInputs) => LocalizedString} */
1683
+ () => (
1684
+ /** @type {LocalizedString} */
1685
+ "Fertig"
1686
+ )
1687
+ ), jn = (
1688
+ /** @type {((inputs?: Commerce_Submit_SuccessInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_SuccessInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1689
+ ((n = {}, t = {}) => {
1690
+ const e = t.locale ?? h();
1691
+ return e === "en" ? Bn() : e === "nl" ? Un() : Vn();
1692
+ })
1693
+ ), Hn = (
1694
+ /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1695
+ () => (
1696
+ /** @type {LocalizedString} */
1697
+ "Failed"
1698
+ )
1699
+ ), qn = (
1700
+ /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1701
+ () => (
1702
+ /** @type {LocalizedString} */
1703
+ "Mislukt"
1704
+ )
1705
+ ), Gn = (
1706
+ /** @type {(inputs: Commerce_Submit_FailedInputs) => LocalizedString} */
1707
+ () => (
1708
+ /** @type {LocalizedString} */
1709
+ "Fehlgeschlagen"
1710
+ )
1711
+ ), Zn = (
1712
+ /** @type {((inputs?: Commerce_Submit_FailedInputs, options?: { locale?: "en" | "nl" | "de" }) => LocalizedString) & import('../runtime.js').MessageMetadata<Commerce_Submit_FailedInputs, { locale?: "en" | "nl" | "de" }, {}>} */
1713
+ ((n = {}, t = {}) => {
1714
+ const e = t.locale ?? h();
1715
+ return e === "en" ? Hn() : e === "nl" ? qn() : Gn();
1716
+ })
1717
+ );
1718
+ const Wn = {
1719
+ xmlns: "http://www.w3.org/2000/svg",
1720
+ width: 24,
1721
+ height: 24,
1722
+ viewBox: "0 0 24 24",
1723
+ fill: "none",
1724
+ stroke: "currentColor",
1725
+ "stroke-width": 2,
1726
+ "stroke-linecap": "round",
1727
+ "stroke-linejoin": "round"
1728
+ };
1729
+ const yt = ([n, t, e]) => {
1730
+ const i = document.createElementNS("http://www.w3.org/2000/svg", n);
1731
+ return Object.keys(t).forEach((o) => {
1732
+ i.setAttribute(o, String(t[o]));
1733
+ }), e?.length && e.forEach((o) => {
1734
+ const s = yt(o);
1735
+ i.appendChild(s);
1736
+ }), i;
1737
+ }, wt = (n, t = {}) => {
1738
+ const i = {
1739
+ ...Wn,
1740
+ ...t
1741
+ };
1742
+ return yt(["svg", i, n]);
1743
+ };
1744
+ const Qn = [
1745
+ [
1746
+ "path",
1747
+ {
1748
+ d: "M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"
1749
+ }
1750
+ ],
1751
+ ["path", { d: "m3.3 7 8.7 5 8.7-5" }],
1752
+ ["path", { d: "M12 22V12" }]
1753
+ ];
1754
+ const Yn = [
1755
+ [
1756
+ "path",
1757
+ {
1758
+ d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"
1759
+ }
1760
+ ],
1761
+ ["circle", { cx: "12", cy: "13", r: "3" }]
1762
+ ];
1763
+ const _t = [["path", { d: "m6 9 6 6 6-6" }]];
1764
+ const Kn = [["path", { d: "m15 18-6-6 6-6" }]];
1765
+ const Xn = [["path", { d: "m9 18 6-6-6-6" }]];
1766
+ const Jn = [["path", { d: "m18 15-6-6-6 6" }]];
1767
+ const ti = [
1768
+ ["path", { d: "M15 3h6v6" }],
1769
+ ["path", { d: "m21 3-7 7" }],
1770
+ ["path", { d: "m3 21 7-7" }],
1771
+ ["path", { d: "M9 21H3v-6" }]
1772
+ ];
1773
+ const ei = [
1774
+ ["path", { d: "m14 10 7-7" }],
1775
+ ["path", { d: "M20 10h-6V4" }],
1776
+ ["path", { d: "m3 21 7-7" }],
1777
+ ["path", { d: "M4 14h6v6" }]
1778
+ ];
1779
+ const ni = [
1780
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }],
1781
+ ["path", { d: "M3 3v5h5" }]
1782
+ ];
1783
+ const ii = [
1784
+ ["path", { d: "M10 5H3" }],
1785
+ ["path", { d: "M12 19H3" }],
1786
+ ["path", { d: "M14 3v4" }],
1787
+ ["path", { d: "M16 17v4" }],
1788
+ ["path", { d: "M21 12h-9" }],
1789
+ ["path", { d: "M21 19h-5" }],
1790
+ ["path", { d: "M21 5h-7" }],
1791
+ ["path", { d: "M8 10v4" }],
1792
+ ["path", { d: "M8 12H3" }]
1793
+ ];
1794
+ const oi = [
1795
+ ["path", { d: "M18 6 6 18" }],
1796
+ ["path", { d: "m6 6 12 12" }]
1797
+ ];
1798
+ const si = [
1799
+ ["circle", { cx: "11", cy: "11", r: "8" }],
1800
+ ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }],
1801
+ ["line", { x1: "11", x2: "11", y1: "8", y2: "14" }],
1802
+ ["line", { x1: "8", x2: "14", y1: "11", y2: "11" }]
1803
+ ];
1804
+ const ri = [
1805
+ ["circle", { cx: "11", cy: "11", r: "8" }],
1806
+ ["line", { x1: "21", x2: "16.65", y1: "21", y2: "16.65" }],
1807
+ ["line", { x1: "8", x2: "14", y1: "11", y2: "11" }]
1808
+ ];
1809
+ function y(n, { size: t = 20, strokeWidth: e = 1.75 } = {}) {
1810
+ return wt(n, { width: t, height: t, "stroke-width": e });
1811
+ }
1812
+ const g = {
1813
+ ar: (n) => y(Qn, n),
1814
+ fullscreen: (n) => y(ti, n),
1815
+ exitFullscreen: (n) => y(ei, n),
1816
+ screenshot: (n) => y(Yn, n),
1817
+ tiltUp: (n) => y(Jn, n),
1818
+ tiltDown: (n) => y(_t, n),
1819
+ rotateLeft: (n) => y(Kn, n),
1820
+ rotateRight: (n) => y(Xn, n),
1821
+ zoomIn: (n) => y(si, n),
1822
+ zoomOut: (n) => y(ri, n),
1823
+ reset: (n) => y(ni, n),
1824
+ configurator: (n) => y(ii, n),
1825
+ close: (n) => y(oi, n)
1826
+ };
1827
+ class b {
1828
+ element;
1829
+ constructor({ variant: t = "default", size: e = "default", label: i, icon: o, onClick: s, disabled: r = !1 } = {}) {
1830
+ this.element = document.createElement("button"), this.element.className = `ui-btn ui-btn--${t} ui-btn--size-${e}`, i !== void 0 && (this.element.setAttribute("aria-label", i), this.element.title = i), o !== void 0 ? this.element.appendChild(o) : i !== void 0 && (this.element.textContent = i), s && this.element.addEventListener("click", s), this.element.disabled = r;
2040
1831
  }
2041
- resolve() {
2042
- if (this.pricing === void 0)
2043
- return;
2044
- const t = ai(this.pricing.rules, this.selection);
2045
- if (!(t === void 0 || t.availability === St.HIDDEN))
2046
- return {
2047
- attributes: { ...this.selection },
2048
- reference: t.reference,
2049
- price: t.priceCents,
2050
- currency: this.pricing.currency,
2051
- availability: t.availability
2052
- };
1832
+ setIcon(t) {
1833
+ const e = this.element.querySelector("svg");
1834
+ e !== null ? e.replaceWith(t) : this.element.appendChild(t);
2053
1835
  }
2054
1836
  }
2055
- class ui {
2056
- constructor(t, e) {
2057
- this.send = t, this.dispatch = e;
2058
- }
2059
- send;
2060
- dispatch;
2061
- focused = null;
2062
- attributes = [];
2063
- changeCallbacks = [];
2064
- /** The focused component name, or null in overview. */
2065
- get current() {
2066
- return this.focused;
1837
+ const m = {
1838
+ semantic: {
1839
+ // eslint-disable-next-line vd-custom/enforce-error-codes -- This is a hex color, not an error code
1840
+ error: "#dc2626"
1841
+ },
1842
+ neutral: {
1843
+ 500: "#737373",
1844
+ 600: "#525252",
1845
+ 900: "#171717"
1846
+ },
1847
+ white: "#ffffff",
1848
+ black: "#000000"
1849
+ }, a = {
1850
+ 0: "0",
1851
+ 0.5: "0.125rem",
1852
+ // 2px
1853
+ 1: "0.25rem",
1854
+ // 4px
1855
+ 1.5: "0.375rem",
1856
+ // 6px
1857
+ 2: "0.5rem",
1858
+ // 10px
1859
+ 3: "0.75rem",
1860
+ // 14px
1861
+ 4: "1rem",
1862
+ // 16px
1863
+ 5: "1.25rem",
1864
+ // 20px
1865
+ 6: "1.5rem",
1866
+ // 28px
1867
+ 8: "2rem",
1868
+ // 32px
1869
+ 9: "2.25rem"
1870
+ };
1871
+ class ai {
1872
+ element;
1873
+ constructor() {
1874
+ this.element = document.createElement("div"), Object.assign(this.element.style, {
1875
+ background: m.white,
1876
+ borderRadius: a[3],
1877
+ padding: a[6],
1878
+ boxShadow: `0 ${a[2]} ${a[8]} oklch(0% 0 0 / 20%)`,
1879
+ maxWidth: "20rem",
1880
+ width: "100%"
1881
+ });
2067
1882
  }
2068
- /**
2069
- * Focus a component by name. Sends a focus message to the viewer.
2070
- * @throws VirtualdisplayError if component is not a non-empty string
2071
- */
2072
- set(t) {
2073
- if (typeof t != "string" || t.length === 0)
2074
- throw f.invalidParameter(
2075
- `set() requires a non-empty component name, got ${String(t)}`
2076
- );
2077
- this.send({ type: c.FOCUS, action: Y.FOCUS, component: t });
1883
+ }
1884
+ class li {
1885
+ constructor(t = {}) {
1886
+ this.options = t, this.card = new ai(), this.backdrop = this.createBackdrop(), this.backdrop.appendChild(this.card.element);
2078
1887
  }
2079
- /** Return to the overview — unfocus the current component. */
2080
- clear() {
2081
- this.send({ type: c.FOCUS, action: Y.UNFOCUS });
1888
+ options;
1889
+ card;
1890
+ backdrop;
1891
+ keyHandler = null;
1892
+ open() {
1893
+ document.body.appendChild(this.backdrop), this.keyHandler = (t) => {
1894
+ t.key === "Escape" && this.close();
1895
+ }, document.addEventListener("keydown", this.keyHandler);
2082
1896
  }
2083
- /**
2084
- * Subscribe to focus changes. The callback fires with the focus state when a
2085
- * component is focused, and with null when returning to overview.
2086
- */
2087
- onChange(t) {
2088
- this.changeCallbacks.push(t);
1897
+ close() {
1898
+ this.backdrop.remove(), this.keyHandler !== null && (document.removeEventListener("keydown", this.keyHandler), this.keyHandler = null), this.options.onClose?.();
2089
1899
  }
2090
- /** Called by the client when a FocusStateMessage is received from the viewer. */
2091
- handleStateReceived(t) {
2092
- t.state === kt.FOCUSED && t.component !== void 0 ? (this.focused = t.component, this.attributes = t.attributes ?? []) : (this.focused = null, this.attributes = []);
2093
- const e = this.focused !== null ? { component: this.focused, attributes: this.attributes } : null;
2094
- for (const i of this.changeCallbacks)
2095
- i(e);
2096
- this.dispatch(e);
1900
+ createBackdrop() {
1901
+ const t = document.createElement("div");
1902
+ return Object.assign(t.style, {
1903
+ position: "fixed",
1904
+ inset: a[0],
1905
+ background: "oklch(0% 0 0 / 50%)",
1906
+ zIndex: "9999",
1907
+ display: "flex",
1908
+ alignItems: "center",
1909
+ justifyContent: "center"
1910
+ }), t.addEventListener("click", (e) => {
1911
+ e.target === t && this.close();
1912
+ }), t;
2097
1913
  }
2098
1914
  }
2099
- class di {
2100
- constructor(t) {
2101
- this.filename = t;
1915
+ class ci {
1916
+ element;
1917
+ selectCallback = null;
1918
+ constructor(t, e, i) {
1919
+ this.element = document.createElement("div"), this.element.className = "ui-toggle-group";
1920
+ for (let o = 0; o < t.length; o++) {
1921
+ const s = t[o], r = document.createElement("button");
1922
+ r.className = "ui-toggle-group-item", r.dataset.value = s, r.textContent = i?.[o] ?? s, r.dataset.state = s === e ? "on" : "off", r.setAttribute("aria-pressed", s === e ? "true" : "false"), r.addEventListener("click", () => {
1923
+ this.selectCallback?.(s);
1924
+ }), this.element.appendChild(r);
1925
+ }
2102
1926
  }
2103
- filename;
2104
- developedCallbacks = /* @__PURE__ */ new Set();
2105
- errorCallbacks = /* @__PURE__ */ new Set();
2106
- developed = !1;
2107
- failed = !1;
2108
- photoData = null;
2109
- /**
2110
- * Register a callback for when the photo is developed
2111
- * If the photo is already developed, the callback is called immediately
2112
- *
2113
- * @param callback Function to call with photo data
2114
- * @returns This Photo instance for chaining
2115
- */
2116
- onDeveloped(t) {
2117
- return this.developed && this.photoData !== null ? (t(this.photoData), this) : (this.developedCallbacks.add(t), this);
1927
+ setSelected(t) {
1928
+ for (const e of this.element.querySelectorAll(".ui-toggle-group-item")) {
1929
+ const i = e.dataset.value === t;
1930
+ e.dataset.state = i ? "on" : "off", e.setAttribute("aria-pressed", i ? "true" : "false");
1931
+ }
2118
1932
  }
2119
- /**
2120
- * Register a callback for when the photo capture fails
2121
- *
2122
- * @param callback Function to call with error
2123
- * @returns This Photo instance for chaining
2124
- */
2125
- onError(t) {
2126
- return this.errorCallbacks.add(t), this;
1933
+ onSelect(t) {
1934
+ this.selectCallback = t;
2127
1935
  }
2128
- /**
2129
- * Get the filename for this photo
2130
- */
2131
- getFilename() {
2132
- return this.filename;
1936
+ }
1937
+ class ui {
1938
+ element;
1939
+ select;
1940
+ constructor(t, e, i, o, s) {
1941
+ this.select = document.createElement("select"), this.select.className = "ui-native-select", this.select.dataset.name = t, this.select.setAttribute("aria-label", i);
1942
+ const r = document.createElement("option");
1943
+ r.value = "", r.disabled = !0, r.textContent = i, this.select.appendChild(r);
1944
+ let l = !1;
1945
+ for (let u = 0; u < e.length; u++) {
1946
+ const p = e[u], v = document.createElement("option");
1947
+ v.value = p, v.textContent = s?.[u] ?? p, p === o && (v.selected = !0, l = !0), this.select.appendChild(v);
1948
+ }
1949
+ l || (r.selected = !0);
1950
+ const d = wt(_t, { width: 16, height: 16, "stroke-width": 2 });
1951
+ d.classList.add("ui-native-select-icon"), this.element = document.createElement("div"), this.element.className = "ui-native-select-wrap", this.element.appendChild(this.select), this.element.appendChild(d);
2133
1952
  }
2134
- /**
2135
- * Mark the photo as developed and notify all callbacks
2136
- * This method is called by Snapshot when the photo data arrives from the server
2137
- */
2138
- develop(t) {
2139
- this.developed || this.failed || (this.photoData = t, this.developed = !0, this.developedCallbacks.forEach((e) => {
2140
- e(this.photoData);
2141
- }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
1953
+ setSelected(t) {
1954
+ this.select.value = t;
2142
1955
  }
2143
- /**
2144
- * Mark the photo as failed and notify error callbacks
2145
- */
2146
- fail(t) {
2147
- this.developed || this.failed || (this.failed = !0, this.errorCallbacks.forEach((e) => {
2148
- e(t);
2149
- }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
1956
+ onSelect(t) {
1957
+ this.select.addEventListener("change", () => {
1958
+ t(this.select.value);
1959
+ });
1960
+ }
1961
+ }
1962
+ const w = {
1963
+ fontFamily: {
1964
+ sans: ["Mona Sans", "ui-sans-serif", "system-ui", "sans-serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"]
1965
+ },
1966
+ fontSize: {
1967
+ xs: "0.75rem",
1968
+ // 12px
1969
+ sm: "0.875rem",
1970
+ // 14px
1971
+ base: "1rem",
1972
+ // 16px
1973
+ lg: "1.125rem"
1974
+ },
1975
+ fontWeight: {
1976
+ medium: "500",
1977
+ semibold: "600"
2150
1978
  }
1979
+ }, x = w.fontFamily.sans.join(", "), di = `
1980
+ --primary: oklch(5% 0 0);
1981
+ --primary-foreground: oklch(100% 0 0);
1982
+ --secondary: oklch(92% 0 0);
1983
+ --secondary-foreground: oklch(5% 0 0);
1984
+ --background: oklch(100% 0 0);
1985
+ --foreground: oklch(5% 0 0);
1986
+ --border: oklch(92% 0 0);
1987
+ --ring: oklch(44% 0 0);
1988
+ --radius: 0.625rem;
1989
+ --accent: oklch(92% 0 0);
1990
+ --accent-foreground: oklch(5% 0 0);
1991
+ --muted-foreground: oklch(44% 0 0);
1992
+ `, hi = `
1993
+ :root { ${di} }
1994
+
1995
+ /* ── Button ──────────────────────────────────────────────── */
1996
+ .ui-btn {
1997
+ display: inline-flex;
1998
+ align-items: center;
1999
+ justify-content: center;
2000
+ gap: 0.5rem;
2001
+ border: 1px solid transparent;
2002
+ border-radius: var(--radius);
2003
+ font-family: ${x};
2004
+ font-size: 0.875rem;
2005
+ font-weight: 600;
2006
+ white-space: nowrap;
2007
+ cursor: pointer;
2008
+ transition: all 200ms;
2009
+ flex-shrink: 0;
2010
+ }
2011
+ .ui-btn:active { transform: scale(0.98); }
2012
+ .ui-btn:disabled { opacity: 0.5; pointer-events: none; }
2013
+ .ui-btn svg { inline-size: 1rem; block-size: 1rem; pointer-events: none; flex-shrink: 0; }
2014
+
2015
+ .ui-btn--default {
2016
+ background: var(--primary);
2017
+ color: var(--primary-foreground);
2018
+ box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
2019
+ }
2020
+ .ui-btn--default:hover { background: oklch(10% 0 0); }
2021
+
2022
+ .ui-btn--outline {
2023
+ background: var(--background);
2024
+ color: var(--foreground);
2025
+ border-color: var(--border);
2026
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
2027
+ }
2028
+ .ui-btn--outline:hover { background: var(--accent); border-color: oklch(85% 0 0); }
2029
+
2030
+ .ui-btn--secondary {
2031
+ background: var(--secondary);
2032
+ color: var(--secondary-foreground);
2033
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
2034
+ }
2035
+ .ui-btn--secondary:hover { background: oklch(88% 0 0); }
2036
+
2037
+ .ui-btn--ghost { background: transparent; color: var(--foreground); }
2038
+ .ui-btn--ghost:hover { background: var(--accent); }
2039
+
2040
+ .ui-btn--size-default { block-size: 2.25rem; padding: 0 1rem; }
2041
+ .ui-btn--size-sm { block-size: 2rem; padding: 0 0.75rem; border-radius: calc(var(--radius) - 0.125rem); }
2042
+ .ui-btn--size-lg { block-size: 2.5rem; padding: 0 1.5rem; }
2043
+ .ui-btn--size-icon { inline-size: 2.25rem; block-size: 2.25rem; padding: 0; }
2044
+ .ui-btn--size-icon-sm { inline-size: 2rem; block-size: 2rem; padding: 0; }
2045
+ .ui-btn--size-icon-lg { inline-size: 2.5rem; block-size: 2.5rem; padding: 0; }
2046
+
2047
+ /* ── Toggle group ────────────────────────────────────────── */
2048
+ .ui-toggle-group { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
2049
+
2050
+ .ui-toggle-group-item {
2051
+ display: inline-flex;
2052
+ align-items: center;
2053
+ justify-content: center;
2054
+ block-size: 2rem;
2055
+ padding: 0 0.75rem;
2056
+ border: 1px solid var(--border);
2057
+ border-radius: var(--radius);
2058
+ background: var(--background);
2059
+ color: var(--foreground);
2060
+ font-family: ${x};
2061
+ font-size: 0.875rem;
2062
+ font-weight: 600;
2063
+ cursor: pointer;
2064
+ white-space: nowrap;
2065
+ transition: all 200ms;
2066
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
2067
+ }
2068
+ .ui-toggle-group-item:hover { background: var(--accent); border-color: oklch(85% 0 0); }
2069
+ .ui-toggle-group-item:active { transform: scale(0.98); }
2070
+ .ui-toggle-group-item:focus-visible {
2071
+ outline: none;
2072
+ border-color: var(--ring);
2073
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
2074
+ }
2075
+ .ui-toggle-group-item[data-state="on"] {
2076
+ background: var(--primary);
2077
+ color: var(--primary-foreground);
2078
+ border-color: var(--primary);
2079
+ }
2080
+ .ui-toggle-group-item[data-state="on"]:hover { background: oklch(10% 0 0); }
2081
+
2082
+ /* ── Native select ───────────────────────────────────────── */
2083
+ .ui-native-select-wrap {
2084
+ position: relative;
2085
+ display: inline-flex;
2086
+ min-inline-size: 8rem;
2087
+ }
2088
+
2089
+ .ui-native-select {
2090
+ display: block;
2091
+ inline-size: 100%;
2092
+ block-size: 2.25rem;
2093
+ padding: 0 2.25rem 0 0.75rem;
2094
+ border: 1px solid var(--border);
2095
+ border-radius: var(--radius);
2096
+ background: var(--background);
2097
+ appearance: none;
2098
+ color: var(--foreground);
2099
+ font-family: ${x};
2100
+ font-size: 0.875rem;
2101
+ font-weight: 400;
2102
+ cursor: pointer;
2103
+ transition: color 200ms, box-shadow 200ms, border-color 200ms;
2104
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
2105
+ }
2106
+ .ui-native-select:hover { border-color: oklch(85% 0 0); }
2107
+ .ui-native-select:focus-visible {
2108
+ outline: none;
2109
+ border-color: var(--ring);
2110
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
2111
+ }
2112
+
2113
+ .ui-native-select-icon {
2114
+ position: absolute;
2115
+ inset-block-start: 50%;
2116
+ inset-inline-end: 0.625rem;
2117
+ transform: translateY(-50%);
2118
+ color: var(--muted-foreground);
2119
+ pointer-events: none;
2120
+ }
2121
+
2122
+ /* ── Annotation layer ───────────────────────────────────── */
2123
+ .vd-annotations-layer {
2124
+ position: absolute;
2125
+ inset-block-start: 0;
2126
+ inset-inline-start: 0;
2127
+ inline-size: 100%;
2128
+ block-size: 100%;
2129
+ pointer-events: none;
2130
+ overflow: visible !important; /* override CSS2DRenderer inline style */
2131
+ }
2132
+
2133
+ .vd-annotation-anchor {
2134
+ position: relative;
2135
+ }
2136
+
2137
+ .vd-annotation-marker {
2138
+ display: flex;
2139
+ align-items: center;
2140
+ justify-content: center;
2141
+ inline-size: 1.75rem;
2142
+ block-size: 1.75rem;
2143
+ border-radius: 50%;
2144
+ border: 2px solid oklch(100% 0 0);
2145
+ background: var(--primary);
2146
+ color: var(--primary-foreground);
2147
+ font-family: ${x};
2148
+ font-size: 0.8125rem;
2149
+ font-weight: 600;
2150
+ cursor: pointer;
2151
+ pointer-events: auto;
2152
+ box-shadow: 0 2px 8px oklch(5% 0 0 / 20%);
2153
+ transition: transform 100ms ease;
2154
+ position: relative;
2155
+ }
2156
+ .vd-annotation-marker:hover { transform: scale(1.1); }
2157
+ .vd-annotation-marker:focus-visible {
2158
+ outline: 2px solid var(--ring);
2159
+ outline-offset: 2px;
2160
+ }
2161
+
2162
+ .vd-annotation-popup {
2163
+ position: absolute;
2164
+ inset-block-end: calc(100% + 0.5rem);
2165
+ inset-inline-start: 50%;
2166
+ transform: translateX(-50%);
2167
+ background: var(--background);
2168
+ color: var(--foreground);
2169
+ border: 1px solid var(--border);
2170
+ border-radius: var(--radius);
2171
+ padding: 0.75rem 1rem;
2172
+ max-inline-size: 15rem;
2173
+ box-shadow: 0 4px 6px oklch(5% 0 0 / 8%), 0 2px 4px oklch(5% 0 0 / 6%);
2174
+ pointer-events: auto;
2175
+ white-space: normal;
2151
2176
  }
2152
- const it = 3e4;
2153
- class hi {
2154
- constructor(t) {
2155
- this.send = t;
2177
+ .vd-annotation-popup:focus { outline: none; }
2178
+
2179
+ .vd-annotation-popup-close {
2180
+ position: absolute;
2181
+ inset-block-start: 0.375rem;
2182
+ inset-inline-end: 0.375rem;
2183
+ display: flex;
2184
+ align-items: center;
2185
+ justify-content: center;
2186
+ inline-size: 1.25rem;
2187
+ block-size: 1.25rem;
2188
+ padding: 0;
2189
+ border: none;
2190
+ border-radius: calc(var(--radius) - 4px);
2191
+ background: transparent;
2192
+ color: var(--muted-foreground);
2193
+ cursor: pointer;
2194
+ pointer-events: auto;
2195
+ }
2196
+ .vd-annotation-popup-close:hover {
2197
+ color: var(--foreground);
2198
+ background: var(--secondary);
2199
+ }
2200
+ .vd-annotation-popup-close:focus-visible {
2201
+ outline: 2px solid var(--ring);
2202
+ outline-offset: 1px;
2203
+ }
2204
+ .vd-annotation-popup-close svg {
2205
+ inline-size: 0.875rem;
2206
+ block-size: 0.875rem;
2207
+ }
2208
+
2209
+ @media (prefers-reduced-motion: no-preference) {
2210
+ .vd-annotation-popup {
2211
+ animation: vd-popup-in 150ms ease;
2156
2212
  }
2157
- send;
2158
- validExtensions = [".png"];
2159
- pendingPhotos = /* @__PURE__ */ new Map();
2160
- /**
2161
- * Take a snapshot of the current 3D model view
2162
- *
2163
- * @param filename The filename for the snapshot (must include valid extension)
2164
- * @returns Photo object for registering callbacks
2165
- * @throws VirtualdisplayError if filename is invalid
2166
- */
2167
- take(t) {
2168
- this.validateFilename(t), w.debug("Taking snapshot", { filename: t });
2169
- const e = new di(t), i = setTimeout(() => {
2170
- this.pendingPhotos.delete(t), w.warn("Snapshot timed out", { filename: t }), e.fail(new f(
2171
- `Snapshot timed out: the viewer did not respond within ${it}ms`,
2172
- E.SNAPSHOT_FAILED
2173
- ));
2174
- }, it);
2175
- return this.pendingPhotos.set(t, { photo: e, timeout: i }), this.send({
2176
- type: c.SNAPSHOT,
2177
- filename: t
2178
- }), e;
2213
+ }
2214
+
2215
+ @keyframes vd-popup-in {
2216
+ from { opacity: 0; transform: translateX(-50%) translateY(4px); }
2217
+ to { opacity: 1; transform: translateX(-50%) translateY(0); }
2218
+ }
2219
+
2220
+ .vd-annotation-popup-title {
2221
+ font-family: ${x};
2222
+ font-size: 0.875rem;
2223
+ font-weight: 600;
2224
+ line-height: 1.25rem;
2225
+ /* Keep the title clear of the absolutely positioned close button. */
2226
+ padding-inline-end: 1.5rem;
2227
+ }
2228
+
2229
+ .vd-annotation-popup-description {
2230
+ font-family: ${x};
2231
+ font-size: 0.8125rem;
2232
+ font-weight: 400;
2233
+ line-height: 1.125rem;
2234
+ color: var(--muted-foreground);
2235
+ margin-block-start: 0.25rem;
2236
+ }
2237
+
2238
+ /* ── QR modal ────────────────────────────────────────────── */
2239
+ /* The slot the QR renders into. While loading it is a calm pulsing
2240
+ placeholder of the same size, so the modal opens instantly and the QR
2241
+ swaps in without any layout shift. */
2242
+ .ui-qr-slot {
2243
+ inline-size: 6rem;
2244
+ block-size: 6rem;
2245
+ border-radius: 0.25rem;
2246
+ overflow: hidden;
2247
+ }
2248
+
2249
+ .ui-qr-slot img {
2250
+ inline-size: 100%;
2251
+ block-size: 100%;
2252
+ display: block;
2253
+ }
2254
+
2255
+ .ui-qr-slot--loading {
2256
+ background: var(--secondary);
2257
+ }
2258
+
2259
+ @media (prefers-reduced-motion: no-preference) {
2260
+ .ui-qr-slot--loading {
2261
+ animation: vd-qr-pulse 1.6s ease-in-out infinite;
2179
2262
  }
2180
- /**
2181
- * Handle incoming snapshot result from server
2182
- * Matches the message to a pending photo by filename, then develops or fails it
2183
- */
2184
- handleSnapshotResult(t) {
2185
- const e = this.pendingPhotos.get(t.filename);
2186
- if (e === void 0) {
2187
- w.warn("No pending photo found for filename", {
2188
- filename: t.filename,
2189
- pendingFilenames: Array.from(this.pendingPhotos.keys())
2190
- });
2191
- return;
2192
- }
2193
- clearTimeout(e.timeout), this.pendingPhotos.delete(t.filename);
2194
- const { photo: i } = e;
2195
- if (t.error !== void 0) {
2196
- i.fail(new f(t.error, E.SNAPSHOT_FAILED));
2197
- return;
2198
- }
2199
- t.data !== void 0 && i.develop({ filename: t.filename, data: t.data });
2263
+ }
2264
+
2265
+ @keyframes vd-qr-pulse {
2266
+ 0%, 100% { opacity: 1; }
2267
+ 50% { opacity: 0.55; }
2268
+ }
2269
+ `;
2270
+ let it = !1;
2271
+ function Ct() {
2272
+ if (it || typeof document > "u")
2273
+ return;
2274
+ it = !0;
2275
+ const n = document.createElement("style");
2276
+ n.id = "vd-ui-vanilla", n.textContent = hi, document.head.appendChild(n);
2277
+ }
2278
+ const mi = w.fontFamily.sans.join(", ");
2279
+ class pi {
2280
+ qrLib = null;
2281
+ dialog = null;
2282
+ qrSlot = null;
2283
+ /** Open the modal immediately, with a loading placeholder in the QR slot. */
2284
+ show() {
2285
+ this.hide(), Ct(), this.dialog = new li({ onClose: () => {
2286
+ this.dialog = null, this.qrSlot = null;
2287
+ } }), Object.assign(this.dialog.card.element.style, {
2288
+ display: "flex",
2289
+ flexDirection: "column",
2290
+ alignItems: "center",
2291
+ gap: a[3],
2292
+ fontFamily: mi
2293
+ }), this.dialog.card.element.appendChild(this.createText(Ae(), {
2294
+ fontSize: w.fontSize.base,
2295
+ fontWeight: w.fontWeight.semibold,
2296
+ color: m.neutral[900]
2297
+ })), this.dialog.card.element.appendChild(this.createText(Re(), {
2298
+ fontSize: w.fontSize.sm,
2299
+ color: m.neutral[600]
2300
+ })), this.qrSlot = this.createQrSlot(), this.dialog.card.element.appendChild(this.qrSlot), this.dialog.card.element.appendChild(this.createText(Le(), {
2301
+ fontSize: w.fontSize.xs,
2302
+ color: m.neutral[500]
2303
+ }));
2304
+ const t = new b({
2305
+ variant: "outline",
2306
+ label: Fe(),
2307
+ onClick: () => {
2308
+ this.hide();
2309
+ }
2310
+ });
2311
+ this.dialog.card.element.appendChild(t.element), this.dialog.open();
2200
2312
  }
2201
2313
  /**
2202
- * Fail any pending photos and forget them. Called from client destroy
2203
- * so callers awaiting onError() see a deterministic outcome instead of
2204
- * leaking captures whose results will never arrive.
2314
+ * Render the QR for `url` into the open modal's slot, replacing the loading
2315
+ * placeholder. Resolves `true` once the QR is actually on screen; `false`
2316
+ * when the modal was closed before it could render (nothing reopens).
2205
2317
  */
2206
- clear() {
2207
- if (this.pendingPhotos.size === 0)
2208
- return;
2209
- const t = new f(
2210
- "Client destroyed before snapshot completed",
2211
- E.SNAPSHOT_FAILED
2212
- );
2213
- for (const { photo: e, timeout: i } of this.pendingPhotos.values())
2214
- clearTimeout(i), e.fail(t);
2215
- this.pendingPhotos.clear();
2318
+ async update(t) {
2319
+ this.qrLib ??= await import("./browser-ey6Iih3j.js").then((i) => i.b);
2320
+ const e = await this.qrLib.toDataURL(t, {
2321
+ width: 256,
2322
+ margin: 4,
2323
+ errorCorrectionLevel: "M",
2324
+ color: { dark: m.black, light: m.white }
2325
+ });
2326
+ return this.qrSlot === null ? !1 : (this.qrSlot.classList.remove("ui-qr-slot--loading"), this.qrSlot.replaceChildren(this.createQrImage(e)), !0);
2216
2327
  }
2217
- /**
2218
- * Validate filename has a valid image extension
2219
- */
2220
- validateFilename(t) {
2221
- if (t === "" || typeof t != "string")
2222
- throw new f(
2223
- "Filename must be a non-empty string",
2224
- E.INVALID_PARAMETER
2225
- );
2226
- if (!this.validExtensions.some(
2227
- (i) => t.toLowerCase().endsWith(i)
2228
- ))
2229
- throw new f(
2230
- `Invalid filename: "${t}". Filename must end with one of: ${this.validExtensions.join(", ")}`,
2231
- E.INVALID_PARAMETER
2232
- );
2233
- if (t.includes("..") || t.includes("/") || t.includes("\\"))
2234
- throw new f(
2235
- 'Filename cannot contain path separators or ".."',
2236
- E.INVALID_PARAMETER
2237
- );
2328
+ hide() {
2329
+ this.dialog?.close(), this.dialog = null, this.qrSlot = null;
2238
2330
  }
2239
- }
2240
- class mi {
2241
- container = null;
2242
- attach(t) {
2243
- this.container = t;
2331
+ createText(t, e) {
2332
+ const i = document.createElement("p");
2333
+ return i.textContent = t, Object.assign(i.style, { margin: "0", textAlign: "center", ...e }), i;
2244
2334
  }
2245
- dispatch(t, e, i = !1) {
2246
- const o = new CustomEvent(t, { detail: e, cancelable: i });
2247
- return this.container?.dispatchEvent(o), o;
2335
+ createQrSlot() {
2336
+ const t = document.createElement("div");
2337
+ return t.className = "ui-qr-slot ui-qr-slot--loading", t;
2248
2338
  }
2249
- on(t, e) {
2250
- const i = (o) => {
2251
- const s = o, r = { ...s.detail, preventDefault: () => {
2252
- s.preventDefault();
2253
- } };
2254
- e(r);
2255
- };
2256
- return this.container?.addEventListener(t, i), () => {
2257
- this.container?.removeEventListener(t, i);
2258
- };
2339
+ createQrImage(t) {
2340
+ const e = document.createElement("img");
2341
+ return e.src = t, e.alt = "QR code", e;
2259
2342
  }
2260
2343
  }
2261
- function y(n, { size: t = 20, strokeWidth: e = 1.75 } = {}) {
2262
- return ct(n, { width: t, height: t, "stroke-width": e });
2263
- }
2264
- const b = {
2265
- ar: (n) => y(Dt, n),
2266
- fullscreen: (n) => y(Ht, n),
2267
- exitFullscreen: (n) => y(Gt, n),
2268
- screenshot: (n) => y(Bt, n),
2269
- tiltUp: (n) => y(jt, n),
2270
- tiltDown: (n) => y(ut, n),
2271
- rotateLeft: (n) => y(Ut, n),
2272
- rotateRight: (n) => y(Vt, n),
2273
- zoomIn: (n) => y(Yt, n),
2274
- zoomOut: (n) => y(Xt, n),
2275
- reset: (n) => y(qt, n),
2276
- configurator: (n) => y(Zt, n),
2277
- close: (n) => y(Wt, n)
2278
- };
2279
2344
  function ot(n) {
2280
2345
  const t = Math.max(0, Math.min(1, n));
2281
2346
  return t * t * (3 - 2 * t);
2282
2347
  }
2283
- function pi(n, t, e, i, o, s) {
2284
- const [r, l, d] = n, u = Math.cos(t), p = Math.sin(t), v = u * r + p * d, A = -p * r + u * d, S = Math.cos(e), C = Math.sin(e), R = S * l - C * A, M = C * l + S * A, W = 0.55;
2348
+ function fi(n, t, e, i, o, s) {
2349
+ const [r, l, d] = n, u = Math.cos(t), p = Math.sin(t), v = u * r + p * d, E = -p * r + u * d, k = Math.cos(e), S = Math.sin(e), R = k * l - S * E, M = S * l + k * E, W = 0.55;
2285
2350
  return [i + v * s * W, o - R * s * W, M];
2286
2351
  }
2287
- function fi(n, t, e) {
2352
+ function gi(n, t, e) {
2288
2353
  n.beginPath(), n.moveTo(t[0], t[1]), n.lineTo(e[0], e[1]), n.strokeStyle = "#000", n.lineWidth = 2, n.stroke();
2289
2354
  }
2290
- function gi(n, t, e, i, o) {
2355
+ function bi(n, t, e, i, o) {
2291
2356
  const s = t(e), r = t(i), l = [
2292
2357
  s[0] + (r[0] - s[0]) * o,
2293
2358
  s[1] + (r[1] - s[1]) * o,
2294
2359
  s[2]
2295
2360
  ];
2296
- fi(n, s, l);
2361
+ gi(n, s, l);
2297
2362
  }
2298
- const z = [-1, -1, 1], F = [1, -1, 1], P = [1, -1, -1], $ = [-1, -1, -1], D = [-1, 1, 1], B = [1, 1, 1], U = [1, 1, -1], V = [-1, 1, -1], st = [
2299
- [z, F],
2300
- [F, P],
2301
- [P, $],
2363
+ const z = [-1, -1, 1], P = [1, -1, 1], F = [1, -1, -1], $ = [-1, -1, -1], D = [-1, 1, 1], B = [1, 1, 1], U = [1, 1, -1], V = [-1, 1, -1], st = [
2364
+ [z, P],
2365
+ [P, F],
2366
+ [F, $],
2302
2367
  [$, z],
2303
2368
  [z, D],
2304
- [F, B],
2305
- [P, U],
2369
+ [P, B],
2370
+ [F, U],
2306
2371
  [$, V],
2307
2372
  [D, B],
2308
2373
  [B, U],
2309
2374
  [U, V],
2310
2375
  [V, D]
2311
- ], bi = 400, rt = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';
2312
- function vi() {
2376
+ ], vi = 400, rt = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';
2377
+ function yi() {
2313
2378
  const n = document.createElement("div");
2314
2379
  Object.assign(n.style, {
2315
2380
  position: "absolute",
@@ -2352,9 +2417,9 @@ function vi() {
2352
2417
  fontSize: "13px",
2353
2418
  color: "#000",
2354
2419
  fontFamily: rt
2355
- }), o.textContent = gt(), e.appendChild(i), e.appendChild(o), n.appendChild(t), n.appendChild(e), { overlay: n, canvas: t, pctEl: i, phaseEl: o };
2420
+ }), o.textContent = vt(), e.appendChild(i), e.appendChild(o), n.appendChild(t), n.appendChild(e), { overlay: n, canvas: t, pctEl: i, phaseEl: o };
2356
2421
  }
2357
- class yi {
2422
+ class wi {
2358
2423
  overlay;
2359
2424
  canvas;
2360
2425
  ctx;
@@ -2367,17 +2432,17 @@ class yi {
2367
2432
  visible = !1;
2368
2433
  constructor(t, e = {}) {
2369
2434
  this.startTime = performance.now();
2370
- const i = vi();
2435
+ const i = yi();
2371
2436
  this.overlay = i.overlay, this.canvas = i.canvas, this.pctEl = i.pctEl, this.phaseEl = i.phaseEl, t.appendChild(this.overlay), this.ctx = this.canvas.getContext("2d"), this.ctx !== null && (this.sizeCanvas(), this.tick());
2372
- const o = e.showDelay ?? bi;
2437
+ const o = e.showDelay ?? vi;
2373
2438
  this.showTimerId = setTimeout(() => {
2374
2439
  this.reveal();
2375
2440
  }, o);
2376
2441
  }
2377
2442
  get loadingTexts() {
2378
2443
  return {
2379
- downloading: gt(),
2380
- rendering: Pe()
2444
+ downloading: vt(),
2445
+ rendering: We()
2381
2446
  };
2382
2447
  }
2383
2448
  updateProgress(t, e) {
@@ -2430,15 +2495,15 @@ class yi {
2430
2495
  return;
2431
2496
  const t = this.canvas.width / Math.min(devicePixelRatio, 2), e = this.canvas.height / Math.min(devicePixelRatio, 2), i = (performance.now() - this.startTime) / 1e3;
2432
2497
  this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, t, e);
2433
- const o = t / 2, s = e * 0.44, r = Math.min(t, e) * 0.08, l = 0.785, d = 0.615, u = (C) => pi(C, l, d, o, s, r), p = 6, v = i % p / p, A = st.length;
2434
- let S;
2435
- v < 0.35 ? S = ot(v / 0.35) * A : v < 0.55 ? S = A : v < 0.9 ? S = (1 - ot((v - 0.55) / 0.35)) * A : S = 0, this.ctx.shadowBlur = 5, this.ctx.shadowColor = "rgba(30,30,30,0.08)";
2436
- for (let C = 0; C < A && !(C >= S); C++) {
2437
- const R = st[C];
2498
+ const o = t / 2, s = e * 0.44, r = Math.min(t, e) * 0.08, l = 0.785, d = 0.615, u = (S) => fi(S, l, d, o, s, r), p = 6, v = i % p / p, E = st.length;
2499
+ let k;
2500
+ v < 0.35 ? k = ot(v / 0.35) * E : v < 0.55 ? k = E : v < 0.9 ? k = (1 - ot((v - 0.55) / 0.35)) * E : k = 0, this.ctx.shadowBlur = 5, this.ctx.shadowColor = "rgba(30,30,30,0.08)";
2501
+ for (let S = 0; S < E && !(S >= k); S++) {
2502
+ const R = st[S];
2438
2503
  if (R === void 0)
2439
2504
  continue;
2440
- const M = Math.min(1, S - C);
2441
- gi(this.ctx, u, R[0], R[1], M);
2505
+ const M = Math.min(1, k - S);
2506
+ bi(this.ctx, u, R[0], R[1], M);
2442
2507
  }
2443
2508
  this.ctx.shadowBlur = 0;
2444
2509
  }
@@ -2455,7 +2520,7 @@ class _i {
2455
2520
  fullscreenBtn = null;
2456
2521
  onFullscreenChange;
2457
2522
  setFullscreenIcon(t) {
2458
- this.fullscreenBtn !== null && this.fullscreenBtn.setIcon(t ? b.exitFullscreen() : b.fullscreen());
2523
+ this.fullscreenBtn !== null && this.fullscreenBtn.setIcon(t ? g.exitFullscreen() : g.fullscreen());
2459
2524
  }
2460
2525
  syncButton(t, e, i) {
2461
2526
  if (e && t === null) {
@@ -2465,19 +2530,19 @@ class _i {
2465
2530
  return !e && t !== null ? (t.element.remove(), null) : t;
2466
2531
  }
2467
2532
  applySettings(t) {
2468
- this.arBtn = this.syncButton(this.arBtn, t.ar !== !1 && this.options.onAr !== void 0, () => new g({
2533
+ this.arBtn = this.syncButton(this.arBtn, t.ar !== !1 && this.options.onAr !== void 0, () => new b({
2469
2534
  variant: "outline",
2470
2535
  size: "icon",
2471
- label: ue(),
2472
- icon: b.ar(),
2536
+ label: _e(),
2537
+ icon: g.ar(),
2473
2538
  onClick: () => {
2474
2539
  this.options.onAr?.();
2475
2540
  }
2476
- })), this.fullscreenBtn = this.syncButton(this.fullscreenBtn, t.fullscreen !== !1 && this.options.onFullscreen !== void 0, () => new g({
2541
+ })), this.fullscreenBtn = this.syncButton(this.fullscreenBtn, t.fullscreen !== !1 && this.options.onFullscreen !== void 0, () => new b({
2477
2542
  variant: "outline",
2478
2543
  size: "icon",
2479
- label: Ie(),
2480
- icon: b.fullscreen(),
2544
+ label: Ue(),
2545
+ icon: g.fullscreen(),
2481
2546
  onClick: () => {
2482
2547
  this.options.onFullscreen?.();
2483
2548
  }
@@ -2497,9 +2562,9 @@ class L {
2497
2562
  targetTilt: e.tilt,
2498
2563
  targetZoom: e.zoom,
2499
2564
  thetaOffset: 0
2500
- }, o = wi(t, e, i);
2565
+ }, o = Ci(t, e, i);
2501
2566
  e.onChange((s) => {
2502
- i.targetRotation = s.rotation - i.thetaOffset, i.targetTilt = s.tilt, i.targetZoom = s.zoom, Ci(o, e, s);
2567
+ i.targetRotation = s.rotation - i.thetaOffset, i.targetTilt = s.tilt, i.targetZoom = s.zoom, Si(o, e, s);
2503
2568
  }), this.element.appendChild(L.buildZoom(o.zoomOut, o.zoomIn)), this.element.appendChild(L.buildDpad(o.tiltUp, o.rotateLeft, o.reset, o.rotateRight, o.tiltDown));
2504
2569
  }
2505
2570
  static buildZoom(t, e) {
@@ -2520,7 +2585,7 @@ class L {
2520
2585
  return r;
2521
2586
  }
2522
2587
  }
2523
- function wi(n, t, e) {
2588
+ function Ci(n, t, e) {
2524
2589
  const i = (r) => Math.max(t.limits?.minZoom ?? 1, Math.min(t.limits?.maxZoom ?? 1 / 0, r)), o = (r) => {
2525
2590
  const l = t.limits?.minRotate, d = t.limits?.maxRotate;
2526
2591
  return l !== void 0 || d !== void 0 ? Math.max(l ?? -1 / 0, Math.min(d ?? 1 / 0, r)) : r;
@@ -2534,30 +2599,30 @@ function wi(n, t, e) {
2534
2599
  u > 180 || u < 0 ? (e.targetTilt = u > 180 ? 360 - u : -u, e.thetaOffset = (e.thetaOffset + 180) % 360, n.tilt(e.targetTilt).rotate(e.targetRotation + e.thetaOffset).set()) : (e.targetTilt = u, n.tilt(e.targetTilt).set());
2535
2600
  };
2536
2601
  return {
2537
- zoomOut: new g({ variant: "outline", size: "icon", label: ln(), icon: b.zoomOut(), onClick: () => {
2602
+ zoomOut: new b({ variant: "outline", size: "icon", label: yn(), icon: g.zoomOut(), onClick: () => {
2538
2603
  e.targetZoom = i(e.targetZoom - 10), n.zoom(e.targetZoom).set();
2539
2604
  } }),
2540
- zoomIn: new g({ variant: "outline", size: "icon", label: on(), icon: b.zoomIn(), onClick: () => {
2605
+ zoomIn: new b({ variant: "outline", size: "icon", label: fn(), icon: g.zoomIn(), onClick: () => {
2541
2606
  e.targetZoom = i(e.targetZoom + 10), n.zoom(e.targetZoom).set();
2542
2607
  } }),
2543
- tiltUp: new g({ variant: "outline", size: "icon", label: _n(), icon: b.tiltUp(), onClick: () => {
2608
+ tiltUp: new b({ variant: "outline", size: "icon", label: On(), icon: g.tiltUp(), onClick: () => {
2544
2609
  s(-15);
2545
2610
  } }),
2546
- rotateLeft: new g({ variant: "outline", size: "icon", label: hn(), icon: b.rotateLeft(), onClick: () => {
2611
+ rotateLeft: new b({ variant: "outline", size: "icon", label: Sn(), icon: g.rotateLeft(), onClick: () => {
2547
2612
  e.targetRotation = o(e.targetRotation - 15), n.rotate(e.targetRotation + e.thetaOffset).set();
2548
2613
  } }),
2549
- reset: new g({ variant: "outline", size: "icon", label: Je(), icon: b.reset(), onClick: () => {
2614
+ reset: new b({ variant: "outline", size: "icon", label: dn(), icon: g.reset(), onClick: () => {
2550
2615
  e.thetaOffset = 0, n.reset();
2551
2616
  } }),
2552
- rotateRight: new g({ variant: "outline", size: "icon", label: gn(), icon: b.rotateRight(), onClick: () => {
2617
+ rotateRight: new b({ variant: "outline", size: "icon", label: xn(), icon: g.rotateRight(), onClick: () => {
2553
2618
  e.targetRotation = o(e.targetRotation + 15), n.rotate(e.targetRotation + e.thetaOffset).set();
2554
2619
  } }),
2555
- tiltDown: new g({ variant: "outline", size: "icon", label: Sn(), icon: b.tiltDown(), onClick: () => {
2620
+ tiltDown: new b({ variant: "outline", size: "icon", label: zn(), icon: g.tiltDown(), onClick: () => {
2556
2621
  s(15);
2557
2622
  } })
2558
2623
  };
2559
2624
  }
2560
- function Ci(n, t, e) {
2625
+ function Si(n, t, e) {
2561
2626
  const i = t.limits;
2562
2627
  i !== void 0 && (n.zoomOut.element.disabled = i.minZoom !== void 0 && e.zoom <= i.minZoom + T, n.zoomIn.element.disabled = i.maxZoom !== void 0 && e.zoom >= i.maxZoom - T, n.tiltUp.element.disabled = i.minTilt !== void 0 && e.tilt <= i.minTilt + T, n.tiltDown.element.disabled = i.maxTilt !== void 0 && e.tilt >= i.maxTilt - T, n.rotateLeft.element.disabled = i.minRotate !== void 0 && e.rotation <= i.minRotate + T, n.rotateRight.element.disabled = i.maxRotate !== void 0 && e.rotation >= i.maxRotate - T);
2563
2628
  }
@@ -2573,7 +2638,7 @@ const I = {
2573
2638
  /** Configuration submitted (bubbles). `detail`: the resolved configuration handed to the host. */
2574
2639
  CONFIGURATION_SUBMIT: "vd:configuration-submit"
2575
2640
  }, ki = 5;
2576
- let Si = 0;
2641
+ let Ai = 0;
2577
2642
  function j(n) {
2578
2643
  const t = n.replace(/([A-Z])/g, " $1").trim();
2579
2644
  return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase();
@@ -2582,20 +2647,20 @@ function Ei(n, t, e, i, o) {
2582
2647
  const s = document.createElement("div");
2583
2648
  s.className = "vd-attribute", s.dataset.name = n.name;
2584
2649
  const r = document.createElement("span");
2585
- r.className = "vd-attribute-label", r.textContent = j(n.name), r.id = `vd-attribute-label-${Si++}`, s.appendChild(r), s.setAttribute("role", "group"), s.setAttribute("aria-labelledby", r.id);
2586
- const l = n.values.find((p) => t.getValue(n.name, p)?.isSelected === !0), d = n.values.map(j), u = n.values.length <= ki ? new Jt(n.values, l, d) : new te(n.name, n.values, j(n.name), l, d);
2650
+ r.className = "vd-attribute-label", r.textContent = j(n.name), r.id = `vd-attribute-label-${Ai++}`, s.appendChild(r), s.setAttribute("role", "group"), s.setAttribute("aria-labelledby", r.id);
2651
+ const l = n.values.find((p) => t.getValue(n.name, p)?.isSelected === !0), d = n.values.map(j), u = n.values.length <= ki ? new ci(n.values, l, d) : new ui(n.name, n.values, j(n.name), l, d);
2587
2652
  return u.onSelect((p) => {
2588
2653
  e.dispatch(I.SELECT, { attribute: n.name, value: p }, !0).defaultPrevented || i(n.name, p);
2589
2654
  }), o(n.name, (p) => {
2590
2655
  u.setSelected(p.value), e.dispatch(I.SELECTED, { attribute: n.name, value: p.value });
2591
2656
  }), s.appendChild(u.element), s;
2592
2657
  }
2593
- const Ai = 80, xi = 2500;
2594
- class Ti {
2595
- constructor(t, e = xi) {
2658
+ const xi = 80, Ti = 2500;
2659
+ class Ri {
2660
+ constructor(t, e = Ti) {
2596
2661
  this.configuration = t, this.resetMs = e, this.element = document.createElement("div"), this.element.className = "vd-commerce vd-hidden";
2597
2662
  const i = document.createElement("div");
2598
- i.className = "vd-commerce-price", this.priceEl = document.createElement("span"), this.priceEl.className = "vd-price", this.vatEl = document.createElement("span"), this.vatEl.className = "vd-vat", i.append(this.priceEl, this.vatEl), this.button = new g({ variant: "default", onClick: () => {
2663
+ i.className = "vd-commerce-price", this.priceEl = document.createElement("span"), this.priceEl.className = "vd-price", this.vatEl = document.createElement("span"), this.vatEl.className = "vd-vat", i.append(this.priceEl, this.vatEl), this.button = new b({ variant: "default", onClick: () => {
2599
2664
  this.handleSubmit();
2600
2665
  } }), this.button.element.classList.add("vd-cta", "vd-hidden"), this.element.append(i, this.button.element), this.unsubscribe = this.configuration.onRender(() => {
2601
2666
  this.update();
@@ -2624,7 +2689,7 @@ class Ti {
2624
2689
  this.priceEl.textContent = "", this.vatEl.textContent = "";
2625
2690
  return;
2626
2691
  }
2627
- this.priceEl.textContent = Ri(t.price, t.currency), this.vatEl.textContent = at(this.configuration.vatDisplay);
2692
+ this.priceEl.textContent = Ii(t.price, t.currency), this.vatEl.textContent = at(this.configuration.vatDisplay);
2628
2693
  }
2629
2694
  renderButton() {
2630
2695
  const t = this.configuration.submitEnabled;
@@ -2632,11 +2697,11 @@ class Ti {
2632
2697
  }
2633
2698
  async handleSubmit() {
2634
2699
  if (this.state !== "busy") {
2635
- this.setState("busy", Tn());
2700
+ this.setState("busy", Dn());
2636
2701
  try {
2637
- await this.configuration.submit(), this.setState("success", Nn());
2702
+ await this.configuration.submit(), this.setState("success", jn());
2638
2703
  } catch {
2639
- this.setState("failed", Fn());
2704
+ this.setState("failed", Zn());
2640
2705
  }
2641
2706
  this.scheduleReset();
2642
2707
  }
@@ -2654,9 +2719,9 @@ class Ti {
2654
2719
  }
2655
2720
  }
2656
2721
  function at(n) {
2657
- return (n ?? "").slice(0, Ai);
2722
+ return (n ?? "").slice(0, xi);
2658
2723
  }
2659
- function Ri(n, t) {
2724
+ function Ii(n, t) {
2660
2725
  try {
2661
2726
  const e = new Intl.NumberFormat(h(), { style: "currency", currency: t }), i = e.resolvedOptions().maximumFractionDigits ?? 2;
2662
2727
  return e.format(n / 10 ** i);
@@ -2664,7 +2729,7 @@ function Ri(n, t) {
2664
2729
  return `${t} ${(n / 100).toFixed(2)}`;
2665
2730
  }
2666
2731
  }
2667
- class Ct {
2732
+ class St {
2668
2733
  slot = null;
2669
2734
  panel = null;
2670
2735
  attach(t, e) {
@@ -2677,8 +2742,8 @@ class Ct {
2677
2742
  this.slot?.remove(), this.slot = null, this.panel = null;
2678
2743
  }
2679
2744
  }
2680
- const Ii = 572;
2681
- class Oi {
2745
+ const Oi = 572;
2746
+ class Ni {
2682
2747
  element = null;
2683
2748
  body = null;
2684
2749
  openButton = null;
@@ -2689,21 +2754,21 @@ class Oi {
2689
2754
  expanded = !0;
2690
2755
  visible = !1;
2691
2756
  attach(t, e) {
2692
- this.container = t, e.classList.add("vd-attributes-sidebar"), this.closeButton = new g({
2757
+ this.container = t, e.classList.add("vd-attributes-sidebar"), this.closeButton = new b({
2693
2758
  variant: "ghost",
2694
2759
  size: "icon-sm",
2695
- label: Ye(),
2696
- icon: b.close({ size: 18 }),
2760
+ label: an(),
2761
+ icon: g.close({ size: 18 }),
2697
2762
  onClick: () => {
2698
2763
  this.expanded = !1, this.render();
2699
2764
  }
2700
2765
  });
2701
2766
  const i = document.createElement("div");
2702
- i.className = "vd-sidebar-header", i.appendChild(this.closeButton.element), this.body = document.createElement("div"), this.body.className = "vd-sidebar-body", this.body.appendChild(e), this.element = document.createElement("div"), this.element.className = "vd-sidebar vd-hidden", this.element.append(i, this.body), t.appendChild(this.element), this.openButton = new g({
2767
+ i.className = "vd-sidebar-header", i.appendChild(this.closeButton.element), this.body = document.createElement("div"), this.body.className = "vd-sidebar-body", this.body.appendChild(e), this.element = document.createElement("div"), this.element.className = "vd-sidebar vd-hidden", this.element.append(i, this.body), t.appendChild(this.element), this.openButton = new b({
2703
2768
  variant: "outline",
2704
2769
  size: "default",
2705
- label: Ge(),
2706
- icon: b.configurator({ size: 18 }),
2770
+ label: nn(),
2771
+ icon: g.configurator({ size: 18 }),
2707
2772
  onClick: () => {
2708
2773
  this.expanded = !0, this.render();
2709
2774
  }
@@ -2718,7 +2783,7 @@ class Oi {
2718
2783
  observe() {
2719
2784
  typeof ResizeObserver > "u" || this.container === null || (this.observer = new ResizeObserver((t) => {
2720
2785
  const e = t[0]?.contentRect.width ?? 0;
2721
- this.setCollapsible(e > 0 && e < Ii);
2786
+ this.setCollapsible(e > 0 && e < Oi);
2722
2787
  }), this.observer.observe(this.container));
2723
2788
  }
2724
2789
  setCollapsible(t) {
@@ -2731,7 +2796,7 @@ class Oi {
2731
2796
  this.element.classList.toggle("vd-hidden", !this.visible), this.element.classList.toggle("vd-sidebar-collapsed", e), this.openButton.element.classList.toggle("vd-hidden", !e), this.closeButton.element.style.display = this.collapsible ? "" : "none", this.container.classList.toggle("vd-layout-sidebar-right", t), this.container.classList.toggle("vd-layout-sidebar-collapsed", e);
2732
2797
  }
2733
2798
  }
2734
- class Ni {
2799
+ class Li {
2735
2800
  attach(t, e) {
2736
2801
  }
2737
2802
  setVisible(t) {
@@ -2739,20 +2804,20 @@ class Ni {
2739
2804
  destroy() {
2740
2805
  }
2741
2806
  }
2742
- const Li = {
2743
- "inframe-bottom-left": () => new Ct(),
2744
- "sidebar-right": () => new Oi(),
2745
- hidden: () => new Ni()
2807
+ const Mi = {
2808
+ "inframe-bottom-left": () => new St(),
2809
+ "sidebar-right": () => new Ni(),
2810
+ hidden: () => new Li()
2746
2811
  };
2747
- function Mi(n) {
2748
- return (Li[n ?? ""] ?? (() => new Ct()))();
2812
+ function zi(n) {
2813
+ return (Mi[n ?? ""] ?? (() => new St()))();
2749
2814
  }
2750
- const H = _.fontFamily.sans.join(", "), G = `
2815
+ const H = w.fontFamily.sans.join(", "), q = `
2751
2816
  position: absolute;
2752
2817
  display: flex;
2753
2818
  z-index: 10;
2754
2819
  transition: inset-inline-end 0.18s ease;
2755
- `, zi = `
2820
+ `, Pi = `
2756
2821
  /* Override browser's default black fullscreen backdrop */
2757
2822
  .vd-container:fullscreen {
2758
2823
  background: oklch(100% 0 0);
@@ -2760,7 +2825,7 @@ const H = _.fontFamily.sans.join(", "), G = `
2760
2825
 
2761
2826
  /* Layout slots — four corners */
2762
2827
  .vd-slot-top-right {
2763
- ${G}
2828
+ ${q}
2764
2829
  inset-block-start: ${a[3]};
2765
2830
  inset-inline-end: ${a[3]};
2766
2831
  flex-direction: column;
@@ -2769,7 +2834,7 @@ const H = _.fontFamily.sans.join(", "), G = `
2769
2834
  }
2770
2835
 
2771
2836
  .vd-slot-bottom-left {
2772
- ${G}
2837
+ ${q}
2773
2838
  inset-block-end: ${a[3]};
2774
2839
  inset-inline-start: ${a[3]};
2775
2840
  flex-direction: column;
@@ -2778,7 +2843,7 @@ const H = _.fontFamily.sans.join(", "), G = `
2778
2843
  }
2779
2844
 
2780
2845
  .vd-slot-bottom-right {
2781
- ${G}
2846
+ ${q}
2782
2847
  inset-block-end: ${a[3]};
2783
2848
  inset-inline-end: ${a[3]};
2784
2849
  flex-direction: row;
@@ -2835,8 +2900,8 @@ const H = _.fontFamily.sans.join(", "), G = `
2835
2900
 
2836
2901
  /* Micro-label: recedes so the controls lead. */
2837
2902
  .vd-attribute-label {
2838
- font-size: ${_.fontSize.xs};
2839
- font-weight: ${_.fontWeight.medium};
2903
+ font-size: ${w.fontSize.xs};
2904
+ font-weight: ${w.fontWeight.medium};
2840
2905
  color: ${m.neutral[500]};
2841
2906
  text-transform: uppercase;
2842
2907
  letter-spacing: 0.05em;
@@ -2927,13 +2992,13 @@ const H = _.fontFamily.sans.join(", "), G = `
2927
2992
  }
2928
2993
 
2929
2994
  .vd-price {
2930
- font-size: ${_.fontSize.lg};
2931
- font-weight: ${_.fontWeight.semibold};
2995
+ font-size: ${w.fontSize.lg};
2996
+ font-weight: ${w.fontWeight.semibold};
2932
2997
  color: ${m.neutral[900]};
2933
2998
  }
2934
2999
 
2935
3000
  .vd-vat {
2936
- font-size: ${_.fontSize.xs};
3001
+ font-size: ${w.fontSize.xs};
2937
3002
  color: ${m.neutral[500]};
2938
3003
  }
2939
3004
 
@@ -2951,16 +3016,16 @@ const H = _.fontFamily.sans.join(", "), G = `
2951
3016
 
2952
3017
  `;
2953
3018
  function Fi() {
2954
- if (Un(), document.getElementById("vd-styles") !== null)
3019
+ if (Ct(), document.getElementById("vd-styles") !== null)
2955
3020
  return;
2956
3021
  const n = document.createElement("style");
2957
- n.id = "vd-styles", n.textContent = zi, document.head.appendChild(n);
3022
+ n.id = "vd-styles", n.textContent = Pi, document.head.appendChild(n);
2958
3023
  }
2959
- function Pi(n, t) {
3024
+ function $i(n, t) {
2960
3025
  const e = document.createElement("a");
2961
3026
  e.href = `data:image/png;base64,${n}`, e.download = t, e.click();
2962
3027
  }
2963
- class $i {
3028
+ class Di {
2964
3029
  options;
2965
3030
  settings = {};
2966
3031
  container = null;
@@ -2976,7 +3041,7 @@ class $i {
2976
3041
  this.options = t;
2977
3042
  }
2978
3043
  attach(t) {
2979
- Fi(), this.container = t, this.loading = new yi(t), this.slots.topRight = this.createSlot(t, "vd-slot-top-right"), this.slots.bottomRight = this.createSlot(t, "vd-slot-bottom-right");
3044
+ Fi(), this.container = t, this.loading = new wi(t), this.slots.topRight = this.createSlot(t, "vd-slot-top-right"), this.slots.bottomRight = this.createSlot(t, "vd-slot-bottom-right");
2980
3045
  }
2981
3046
  onReady() {
2982
3047
  this.loading?.hide(), this.loading = null, this.mountToolbar(), this.cameraControls !== null && (this.cameraControls.element.style.display = "flex"), this.layout?.setVisible(!0);
@@ -3002,7 +3067,7 @@ class $i {
3002
3067
  this.loading?.showError(t);
3003
3068
  }
3004
3069
  applyViewerSettings(t) {
3005
- this.settings = t, t.language !== void 0 && mt(t.language, { reload: !1 }), this.syncScreenshotButton(), this.actionBar?.applySettings(t), this.cameraControls !== null && (this.cameraControls.element.style.display = t.cameraControls === !0 ? "flex" : "none"), this.placeScreenshotButton();
3070
+ this.settings = t, t.language !== void 0 && ft(t.language, { reload: !1 }), this.syncScreenshotButton(), this.actionBar?.applySettings(t), this.cameraControls !== null && (this.cameraControls.element.style.display = t.cameraControls === !0 ? "flex" : "none"), this.placeScreenshotButton();
3006
3071
  }
3007
3072
  renderAttributeControls(t, e) {
3008
3073
  if (!(this.container === null || this.slots.topRight === null) && (this.cameraControls?.element.remove(), this.cameraControls = null, this.teardownConfigurator(), this.settings.cameraControls === !0 && (this.cameraControls = new L(this.options.cameraApi, this.options.cameraPositionApi), this.cameraControls.element.style.display = "none", this.slots.topRight.appendChild(this.cameraControls.element)), this.placeScreenshotButton(), e.length !== 0)) {
@@ -3015,7 +3080,7 @@ class $i {
3015
3080
  this.options.onSelect,
3016
3081
  this.options.onSubscribe
3017
3082
  ));
3018
- this.commerce = new Ti(this.options.configuration), this.attributes.appendChild(this.commerce.element), this.layout = Mi(this.settings.configuratorLayout), this.layout.attach(this.container, this.attributes);
3083
+ this.commerce = new Ri(this.options.configuration), this.attributes.appendChild(this.commerce.element), this.layout = zi(this.settings.configuratorLayout), this.layout.attach(this.container, this.attributes);
3019
3084
  }
3020
3085
  }
3021
3086
  get viewerSettings() {
@@ -3037,14 +3102,14 @@ class $i {
3037
3102
  }
3038
3103
  syncScreenshotButton() {
3039
3104
  const t = this.settings.screenshot === !0;
3040
- t && this.screenshotBtn === null && (this.screenshotBtn = new g({
3105
+ t && this.screenshotBtn === null && (this.screenshotBtn = new b({
3041
3106
  variant: "outline",
3042
3107
  size: "icon",
3043
- label: Ue(),
3044
- icon: b.screenshot(),
3108
+ label: Xe(),
3109
+ icon: g.screenshot(),
3045
3110
  onClick: () => {
3046
3111
  this.options.snapshotApi.take("snapshot.png").onDeveloped(({ data: e, filename: i }) => {
3047
- Pi(e, i);
3112
+ $i(e, i);
3048
3113
  });
3049
3114
  }
3050
3115
  })), !t && this.screenshotBtn !== null && (this.screenshotBtn.element.remove(), this.screenshotBtn = null);
@@ -3053,8 +3118,8 @@ class $i {
3053
3118
  this.screenshotBtn === null || this.slots.topRight === null || (this.cameraControls !== null ? this.cameraControls.element.appendChild(this.screenshotBtn.element) : this.slots.topRight.appendChild(this.screenshotBtn.element));
3054
3119
  }
3055
3120
  }
3056
- const Di = {};
3057
- class Bi {
3121
+ const Bi = {};
3122
+ class Ui {
3058
3123
  defaultServer = "https://server.virtualdisplay.io";
3059
3124
  options;
3060
3125
  serverHost;
@@ -3091,12 +3156,12 @@ class Bi {
3091
3156
  }
3092
3157
  buildUrl() {
3093
3158
  const t = this.getServerUrl(), e = new URL(t);
3094
- return e.searchParams.set("license", this.options.license), e.searchParams.set("model", this.options.model), this.options.language !== void 0 && e.searchParams.set("language", this.options.language), this.options.debug === !0 && e.searchParams.set("debug", "true"), this.options.traceparent !== void 0 && e.searchParams.set("traceparent", this.options.traceparent), e.toString();
3159
+ return e.searchParams.set("license", this.options.license), e.searchParams.set("model", this.options.model), this.options.language !== void 0 && e.searchParams.set("language", this.options.language), this.options.session !== void 0 && e.searchParams.set("session", this.options.session), this.options.debug === !0 && e.searchParams.set("debug", "true"), this.options.traceparent !== void 0 && e.searchParams.set("traceparent", this.options.traceparent), e.toString();
3095
3160
  }
3096
3161
  getServerUrl() {
3097
3162
  if (this.serverHost !== void 0)
3098
3163
  return this.serverHost;
3099
- if (Di?.VITE_VIRTUALDISPLAY_SERVER_HOST === void 0)
3164
+ if (Bi?.VITE_VIRTUALDISPLAY_SERVER_HOST === void 0)
3100
3165
  return this.defaultServer;
3101
3166
  }
3102
3167
  getParentElement() {
@@ -3108,42 +3173,44 @@ class Bi {
3108
3173
  return t;
3109
3174
  }
3110
3175
  }
3111
- function Ui(n) {
3176
+ function Vi(n) {
3112
3177
  const { messenger: t } = n;
3113
3178
  t.on(c.HANDSHAKE, (e) => {
3114
- xt(e) && (w.info("Server handshake received, flushing publisher queue"), t.markReady());
3179
+ Tt(e) && (_.info("Server handshake received, flushing publisher queue"), t.markReady());
3115
3180
  }), t.on(c.SESSION, (e) => {
3116
- Tt(e) && n.onSession(e.sessionId);
3181
+ Rt(e) && n.onSession(e.sessionId);
3117
3182
  }), t.on(c.MUTATION, (e) => {
3118
- Rt(e) && (n.attributes.handleMutationResult(e), n.configuration.handleSelectionChanged(n.attributes.getSelection()));
3183
+ It(e) && (n.attributes.handleMutationResult(e), n.configuration.handleSelectionChanged(n.attributes.getSelection()));
3119
3184
  }), t.on(c.SNAPSHOT, (e) => {
3120
- It(e) && n.snapshot.handleSnapshotResult(e);
3185
+ Ot(e) && n.snapshot.handleSnapshotResult(e);
3121
3186
  }), t.on(c.CAMERA, (e) => {
3122
- Ot(e) && n.cameraPosition.handlePositionUpdate(e);
3187
+ Nt(e) && n.cameraPosition.handlePositionUpdate(e);
3123
3188
  }), t.on(c.LOADING_INDICATOR, (e) => {
3124
- Lt(e) && (e.error !== void 0 ? n.ui.showError(e.error) : n.ui.updateProgress(e.progress, e.phase));
3189
+ Mt(e) && (e.error !== void 0 ? n.ui.showError(e.error) : n.ui.updateProgress(e.progress, e.phase));
3125
3190
  }), t.on(c.CONFIG, (e) => {
3126
- Mt(e) && n.applyConfig(e);
3191
+ Pt(e) && n.applyConfig(e);
3127
3192
  }), t.on(c.MAPPING, (e) => {
3128
- Nt(e) && (n.attributes.handleMappingReceived(e), n.configuration.handleSelectionChanged(n.attributes.getSelection()), n.ui.renderAttributeControls(n.attributes, e.attributes), n.readyStateTracker.handleMappingReceived());
3129
- }), Vi(n);
3193
+ Lt(e) && (n.attributes.handleMappingReceived(e), n.configuration.handleSelectionChanged(n.attributes.getSelection()), n.ui.renderAttributeControls(n.attributes, e.attributes), n.readyStateTracker.handleMappingReceived());
3194
+ }), ji(n);
3130
3195
  }
3131
- function Vi(n) {
3196
+ function ji(n) {
3132
3197
  const { messenger: t } = n;
3133
3198
  t.on(c.FOCUS, (e) => {
3134
- zt(e) && n.focus.handleStateReceived(e);
3199
+ Ft(e) && n.focus.handleStateReceived(e);
3135
3200
  }), t.on(c.PRICING, (e) => {
3136
- Ft(e) && n.configuration.handlePricingReceived(e.pricing);
3201
+ $t(e) && n.configuration.handlePricingReceived(e.pricing);
3202
+ }), t.on(c.SYNC, (e) => {
3203
+ zt(e) && n.onSync(e.connected);
3137
3204
  });
3138
3205
  }
3139
- class ji {
3206
+ class Hi {
3140
3207
  ready = !1;
3141
3208
  onReady;
3142
3209
  constructor(t) {
3143
3210
  this.onReady = t;
3144
3211
  }
3145
3212
  handleMappingReceived() {
3146
- this.ready || (this.ready = !0, w.info("Mapping received from server, client is now ready"), this.onReady?.());
3213
+ this.ready || (this.ready = !0, _.info("Mapping received from server, client is now ready"), this.onReady?.());
3147
3214
  }
3148
3215
  get isReady() {
3149
3216
  return this.ready;
@@ -3152,7 +3219,7 @@ class ji {
3152
3219
  this.ready = !1;
3153
3220
  }
3154
3221
  }
3155
- class Hi {
3222
+ class qi {
3156
3223
  attributes;
3157
3224
  readyStateTracker;
3158
3225
  messenger;
@@ -3164,9 +3231,12 @@ class Hi {
3164
3231
  annotationsApi;
3165
3232
  eventBus;
3166
3233
  ui;
3167
- qrModal;
3168
3234
  options;
3169
3235
  serverHost;
3236
+ /** The composed AR flow, or undefined in builds without one (the studio). */
3237
+ arActivation;
3238
+ /** The composed sync tracker, or undefined in builds without one (the studio). */
3239
+ syncTracker;
3170
3240
  iframe;
3171
3241
  container;
3172
3242
  readyCallbacks = [];
@@ -3174,42 +3244,39 @@ class Hi {
3174
3244
  /** The viewer's tracking session id, once the server announces it (SESSION). */
3175
3245
  viewerSessionId;
3176
3246
  constructor(t, e) {
3177
- this.options = t, this.serverHost = e?.serverHost, this.initializeLogging(), this.messenger = new Pt(
3247
+ this.options = t, this.serverHost = e?.serverHost, this.initializeLogging(), this.messenger = new Dt(
3178
3248
  () => this.iframe?.contentWindow ?? null,
3179
3249
  e?.messengerOptions
3180
3250
  );
3181
- const i = (o) => {
3182
- this.messenger.send(o);
3251
+ const i = (s) => {
3252
+ this.messenger.send(s);
3183
3253
  };
3184
- this.attributes = new oi(i, (o, s) => {
3185
- for (const r of this.mutationFailedCallbacks)
3186
- r({ attribute: o, value: s });
3187
- }), this.snapshotApi = new hi(i), this.annotationsApi = new Wn(i), this.cameraApi = new si(i), this.cameraPositionApi = new ri(), this.focusApi = new ui(i, (o) => {
3188
- this.dispatchFocusChange(o);
3189
- }), this.configurationApi = new ci(
3190
- (o) => {
3191
- this.dispatchConfigurationChange(o);
3254
+ this.attributes = new ne(i, (s, r) => {
3255
+ this.notifyMutationFailed(s, r);
3256
+ }), this.snapshotApi = new ue(i), this.annotationsApi = new Bt(i), this.cameraApi = new ie(i), this.cameraPositionApi = new oe(), this.focusApi = new le(i, (s) => {
3257
+ this.dispatchFocusChange(s);
3258
+ }), this.configurationApi = new ae(
3259
+ (s) => {
3260
+ this.dispatchConfigurationChange(s);
3192
3261
  },
3193
- (o) => {
3194
- this.dispatchConfigurationSubmit(o);
3262
+ (s) => {
3263
+ this.dispatchConfigurationSubmit(s);
3195
3264
  }
3196
- ), this.eventBus = new mi(), this.qrModal = new $n(), this.readyStateTracker = new ji(() => {
3197
- const o = this.readyCallbacks;
3198
- this.readyCallbacks = [];
3199
- for (const s of o)
3200
- s(this);
3201
- this.ui.onReady();
3202
- }), this.ui = new $i({
3265
+ ), this.eventBus = new de(), this.arActivation = e?.createArActivation?.(i), this.syncTracker = e?.createSyncTracker?.(), this.readyStateTracker = new Hi(() => {
3266
+ this.notifyReady();
3267
+ });
3268
+ const o = this.arActivation;
3269
+ this.ui = new Di({
3203
3270
  snapshotApi: this.snapshotApi,
3204
3271
  cameraApi: this.cameraApi,
3205
3272
  cameraPositionApi: this.cameraPositionApi,
3206
3273
  eventBus: this.eventBus,
3207
3274
  configuration: this.configurationApi,
3208
- onSelect: (o, s) => this.attributes.select(o, s),
3209
- onSubscribe: (o, s) => this.attributes.onChange(o, s),
3210
- onAr: () => {
3211
- this.activateAr();
3212
- },
3275
+ onSelect: (s, r) => this.attributes.select(s, r),
3276
+ onSubscribe: (s, r) => this.attributes.onChange(s, r),
3277
+ ...o !== void 0 && { onAr: () => {
3278
+ o.activate();
3279
+ } },
3213
3280
  onFullscreenEnter: () => {
3214
3281
  this.trackFullscreen();
3215
3282
  }
@@ -3217,7 +3284,7 @@ class Hi {
3217
3284
  }
3218
3285
  /** Wire the inbound viewer protocol to the facades. */
3219
3286
  wireIncomingMessages() {
3220
- Ui({
3287
+ Vi({
3221
3288
  messenger: this.messenger,
3222
3289
  attributes: this.attributes,
3223
3290
  configuration: this.configurationApi,
@@ -3230,10 +3297,30 @@ class Hi {
3230
3297
  this.handleConfig(t);
3231
3298
  },
3232
3299
  onSession: (t) => {
3233
- this.viewerSessionId = t;
3300
+ this.handleSession(t);
3301
+ },
3302
+ onSync: (t) => {
3303
+ this.syncTracker?.handleSync(t);
3234
3304
  }
3235
3305
  });
3236
3306
  }
3307
+ /** Store the tracking session id and hand it to the AR flow, which waits on it for the QR. */
3308
+ handleSession(t) {
3309
+ this.viewerSessionId = t, this.arActivation?.onSession(t);
3310
+ }
3311
+ /** Forward a failed mutation to every registered `onMutationFailed` callback. */
3312
+ notifyMutationFailed(t, e) {
3313
+ for (const i of this.mutationFailedCallbacks)
3314
+ i({ attribute: t, value: e });
3315
+ }
3316
+ /** Flush the queued `onReady` callbacks once, then let the standard interface know. */
3317
+ notifyReady() {
3318
+ const t = this.readyCallbacks;
3319
+ this.readyCallbacks = [];
3320
+ for (const e of t)
3321
+ e(this);
3322
+ this.ui.onReady();
3323
+ }
3237
3324
  /**
3238
3325
  * Dispatch a bubbling `vd:focus-change` event on the container so a page can
3239
3326
  * react to focus without registering an `onChange` callback.
@@ -3264,28 +3351,21 @@ class Hi {
3264
3351
  const o = i !== void 0 ? { ...e ?? {}, ...i } : e;
3265
3352
  if (i !== void 0 && e !== void 0) {
3266
3353
  const s = Object.entries(i).filter(([r, l]) => e[r] !== l).map(([r, l]) => `${r} (metadata: ${e[r] ?? "undefined"}, override: ${l})`);
3267
- s.length > 0 && w.warn(`Viewer settings override active: ${s.join(", ")}`);
3354
+ s.length > 0 && _.warn(`Viewer settings override active: ${s.join(", ")}`);
3268
3355
  }
3269
3356
  this.ui.applyViewerSettings(o), this.annotationsApi.setEnabled(o.annotations !== !1);
3270
3357
  }
3271
3358
  this.cameraPositionApi.applyLimits(t.cameraLimits);
3272
3359
  }
3273
- activateAr() {
3274
- if (Zn()) {
3275
- this.messenger.send({ type: c.AR_ACTIVATE });
3276
- return;
3277
- }
3278
- this.qrModal.show(this.iframe?.src ?? "");
3279
- }
3280
3360
  /** Tell the viewer the user entered fullscreen, so it records the engagement. */
3281
3361
  trackFullscreen() {
3282
3362
  this.messenger.send({ type: c.FULLSCREEN });
3283
3363
  }
3284
3364
  initializeLogging() {
3285
- this.options.debug === !0 && ii(), w.info("Initializing Virtualdisplay client", { model: this.options.model });
3365
+ this.options.debug === !0 && ee(), _.info("Initializing Virtualdisplay client", { model: this.options.model });
3286
3366
  }
3287
3367
  createAndConnectIframe() {
3288
- const e = new Bi(this.options, this.serverHost).create();
3368
+ const e = new Ui(this.options, this.serverHost).create();
3289
3369
  this.container = e.container, this.iframe = e.iframe, this.eventBus.attach(this.container), this.ui.attach(this.container);
3290
3370
  try {
3291
3371
  const i = new URL(this.iframe.src).origin;
@@ -3321,6 +3401,16 @@ class Hi {
3321
3401
  onMutationFailed(t) {
3322
3402
  this.mutationFailedCallbacks.push(t);
3323
3403
  }
3404
+ /**
3405
+ * Trigger the composed AR flow directly (ar-session-handoff task 035): a host
3406
+ * page that hides the built-in corner button (`viewerSettings.ar = false`,
3407
+ * e.g. the AR landing, which owns a single custom AR affordance) still needs
3408
+ * an entry point to the same flow that button would have called. A no-op
3409
+ * where no AR flow is composed (the studio client has none).
3410
+ */
3411
+ activateAr() {
3412
+ this.arActivation?.activate();
3413
+ }
3324
3414
  on(t, e) {
3325
3415
  this.eventBus.on(t, e);
3326
3416
  }
@@ -3350,25 +3440,135 @@ class Hi {
3350
3440
  return this.ui.viewerSettings;
3351
3441
  }
3352
3442
  destroy() {
3353
- this.readyStateTracker.clear(), this.readyCallbacks = [], this.mutationFailedCallbacks.length = 0, this.snapshotApi.clear(), this.messenger.destroy(), this.qrModal.hide(), this.ui.destroy(), this.container?.parentElement !== null && this.container?.parentElement !== void 0 && this.container.parentElement.removeChild(this.container), this.container = void 0, this.iframe = void 0;
3443
+ this.readyStateTracker.clear(), this.readyCallbacks = [], this.mutationFailedCallbacks.length = 0, this.snapshotApi.clear(), this.messenger.destroy(), this.arActivation?.destroy(), this.ui.destroy(), this.container?.parentElement !== null && this.container?.parentElement !== void 0 && this.container.parentElement.removeChild(this.container), this.container = void 0, this.iframe = void 0;
3444
+ }
3445
+ }
3446
+ const Gi = "ar.virtualdisplay.io";
3447
+ function Zi(n) {
3448
+ const t = new URL(`https://${Gi}/go`);
3449
+ return n.sessionId !== void 0 ? t.searchParams.set("session", n.sessionId) : (t.searchParams.set("license", n.license), t.searchParams.set("model", n.model)), t.toString();
3450
+ }
3451
+ const Wi = 1500;
3452
+ class Qi {
3453
+ options;
3454
+ qrModal = new pi();
3455
+ /** The viewer's tracking session id, once the engine announces it (SESSION). */
3456
+ sessionId;
3457
+ /** Callbacks waiting on the SESSION message, resolved (or timed out) exactly once each. */
3458
+ waiters = [];
3459
+ /** Session-wait timers still pending, cleared on destroy so no wait outlives the client. */
3460
+ pendingTimers = /* @__PURE__ */ new Set();
3461
+ /** Set on destroy: an in-flight hand-off must not re-mount the modal or send QR_SHOWN. */
3462
+ destroyed = !1;
3463
+ constructor(t) {
3464
+ this.options = t;
3465
+ }
3466
+ /** Store the engine-announced tracking session id and resolve anyone waiting on it. */
3467
+ onSession(t) {
3468
+ this.sessionId = t;
3469
+ const e = this.waiters;
3470
+ this.waiters = [];
3471
+ for (const i of e)
3472
+ i(t);
3473
+ }
3474
+ /** The AR button handler: mobile activates AR in the engine, desktop shows the hand-off QR. */
3475
+ activate() {
3476
+ if (Jt()) {
3477
+ this.options.send({ type: c.AR_ACTIVATE });
3478
+ return;
3479
+ }
3480
+ this.showQrCode();
3481
+ }
3482
+ destroy() {
3483
+ this.destroyed = !0;
3484
+ for (const t of this.pendingTimers)
3485
+ clearTimeout(t);
3486
+ this.pendingTimers.clear(), this.waiters = [], this.qrModal.hide();
3487
+ }
3488
+ /**
3489
+ * Desktop hand-off: open the modal at once in its loading state (the click
3490
+ * must respond immediately, even when the session id is still resolving),
3491
+ * then swap the AR landing QR in as soon as the URL is known. Guarded after
3492
+ * each await: a client torn down during the session wait must not update
3493
+ * the modal or send a stray QR_SHOWN through a dead messenger.
3494
+ */
3495
+ async showQrCode() {
3496
+ this.qrModal.show();
3497
+ const t = await this.waitForSessionId();
3498
+ if (this.destroyed)
3499
+ return;
3500
+ const e = Zi({
3501
+ sessionId: t,
3502
+ license: this.options.license,
3503
+ model: this.options.model
3504
+ }), i = await this.qrModal.update(e);
3505
+ if (this.destroyed) {
3506
+ this.qrModal.hide();
3507
+ return;
3508
+ }
3509
+ i && this.options.send({ type: c.QR_SHOWN });
3510
+ }
3511
+ /**
3512
+ * Resolve once the SESSION message arrives, or `undefined` after
3513
+ * {@link SESSION_WAIT_TIMEOUT_MS} if it never does (e.g. tracking blocked by
3514
+ * an ad blocker). The QR then falls back to a working sessionless landing URL
3515
+ * rather than encoding a wrong one.
3516
+ */
3517
+ waitForSessionId() {
3518
+ return this.sessionId !== void 0 ? Promise.resolve(this.sessionId) : new Promise((t) => {
3519
+ const e = setTimeout(() => {
3520
+ this.pendingTimers.delete(e), t(void 0);
3521
+ }, Wi);
3522
+ this.pendingTimers.add(e), this.waiters.push((i) => {
3523
+ this.pendingTimers.delete(e), clearTimeout(e), t(i);
3524
+ });
3525
+ });
3354
3526
  }
3355
3527
  }
3356
- class qi extends Hi {
3528
+ class Yi {
3529
+ callbacks = [];
3530
+ lastKnown;
3531
+ handleSync(t) {
3532
+ this.lastKnown = t;
3533
+ for (const e of this.callbacks)
3534
+ e(t);
3535
+ }
3536
+ onChange(t) {
3537
+ this.callbacks.push(t), this.lastKnown !== void 0 && t(this.lastKnown);
3538
+ }
3539
+ }
3540
+ class Ji extends qi {
3541
+ syncStatusTracker;
3542
+ constructor(t) {
3543
+ const e = new Yi();
3544
+ super(t, {
3545
+ createArActivation: (i) => new Qi({
3546
+ license: t.license,
3547
+ model: t.model,
3548
+ send: i
3549
+ }),
3550
+ createSyncTracker: () => e
3551
+ }), this.syncStatusTracker = e;
3552
+ }
3553
+ /** Subscribe to the live sync channel's connection state (ar-session-handoff). Latest-wins, no buffering. */
3554
+ onSyncChange(t) {
3555
+ this.syncStatusTracker.onChange(t);
3556
+ }
3357
3557
  }
3358
3558
  export {
3359
- Wn as Annotations,
3360
- Xn as Attribute,
3361
- Yn as AttributeValue,
3362
- si as Camera,
3363
- ci as Configuration,
3364
- E as ERROR_CODES,
3365
- ui as Focus,
3366
- di as Photo,
3367
- hi as Snapshot,
3559
+ Bt as Annotations,
3560
+ Vt as Attribute,
3561
+ Ut as AttributeValue,
3562
+ ie as Camera,
3563
+ ae as Configuration,
3564
+ A as ERROR_CODES,
3565
+ le as Focus,
3566
+ ce as Photo,
3567
+ ue as Snapshot,
3368
3568
  I as VD_EVENTS,
3369
- qi as VirtualdisplayClient,
3569
+ Ji as VirtualdisplayClient,
3370
3570
  f as VirtualdisplayError,
3371
- ii as enableDebugMode,
3372
- Gi as isDebugMode,
3373
- w as logger
3571
+ ee as enableDebugMode,
3572
+ Ki as isDebugMode,
3573
+ _ as logger
3374
3574
  };