@virtualdisplay.io/client 4.0.0-alpha.2 → 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.
@@ -1,798 +1,3574 @@
1
- //#region src/client/error.ts
2
- var e = {
3
- NO_MAPPING: "NO_MAPPING",
4
- ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
5
- VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
6
- PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
7
- INVALID_PARAMETER: "INVALID_PARAMETER",
8
- SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
9
- }, t = class t extends Error {
10
- code;
11
- constructor(e, n) {
12
- super(e), this.name = "VirtualdisplayError", this.code = n, Error.captureStackTrace !== void 0 && Error.captureStackTrace(this, t);
13
- }
14
- static noMapping() {
15
- return new t("No attributes available. Waiting for server metadata.", e.NO_MAPPING);
16
- }
17
- static attributeNotFound(n) {
18
- return new t(`Attribute "${n}" not found in mapping`, e.ATTRIBUTE_NOT_FOUND);
19
- }
20
- static valueNotFound(n, r) {
21
- return new t(`Value "${r}" not found for attribute "${n}"`, e.VALUE_NOT_FOUND);
22
- }
23
- static parentNotFound(n) {
24
- return new t(`Parent element not found: ${n}`, e.PARENT_NOT_FOUND);
25
- }
26
- static invalidParameter(n) {
27
- return new t(n, e.INVALID_PARAMETER);
28
- }
29
- static snapshotFailed(n, r) {
30
- return new t(`Snapshot failed for "${n}": ${r}`, e.SNAPSHOT_FAILED);
31
- }
32
- }, n = class {
33
- defaultServer = "https://server.virtualdisplay.io";
34
- options;
35
- constructor(e) {
36
- this.options = e;
37
- }
38
- create() {
39
- let e = this.createIframe();
40
- return this.attachToParent(e), e;
41
- }
42
- createIframe() {
43
- let e = document.createElement("iframe");
44
- return e.src = this.buildUrl(), Object.assign(e.style, {
45
- width: "100%",
46
- height: "100%",
47
- border: "none"
48
- }), e.setAttribute("allow", "xr-spatial-tracking; fullscreen"), e;
49
- }
50
- attachToParent(e) {
51
- this.getParentElement().appendChild(e);
52
- }
53
- buildUrl() {
54
- let e = this.getServerUrl(), t = new URL(e);
55
- return t.searchParams.set("license", this.options.license), t.searchParams.set("model", this.options.model), this.options.debug === !0 && t.searchParams.set("debug", "true"), this.options.language !== void 0 && t.searchParams.set("language", this.options.language), t.toString();
56
- }
57
- getServerUrl() {
58
- return this.defaultServer;
59
- }
60
- getParentElement() {
61
- if (typeof this.options.parent != "string") return this.options.parent;
62
- let e = document.querySelector(this.options.parent);
63
- if (e === null) throw t.parentNotFound(this.options.parent);
64
- return e;
65
- }
1
+ const c = {
2
+ HANDSHAKE: "handshake",
3
+ SESSION: "session",
4
+ MUTATION: "mutation",
5
+ STATE: "state",
6
+ LOADING_INDICATOR: "loading_indicator",
7
+ CAMERA: "camera",
8
+ SNAPSHOT: "snapshot",
9
+ MAPPING: "mapping",
10
+ AR_ACTIVATE: "ar_activate",
11
+ FULLSCREEN: "fullscreen",
12
+ CONFIG: "config",
13
+ ANNOTATION_TOGGLE: "annotation_toggle",
14
+ FOCUS: "focus",
15
+ PRICING: "pricing",
16
+ QR_SHOWN: "qr_shown",
17
+ SYNC: "sync"
18
+ }, Q = {
19
+ FOCUS: "focus",
20
+ UNFOCUS: "unfocus"
21
+ }, kt = {
22
+ FOCUSED: "focused"
23
+ }, At = {
24
+ /** Behaves exactly like an unpriced combination: no price, no button. */
25
+ HIDDEN: "hidden"
26
+ }, Et = new Set(Object.values(c));
27
+ function xt(n) {
28
+ if (typeof n != "object" || n === null)
29
+ return !1;
30
+ const t = n;
31
+ return typeof t.target == "string" ? !1 : typeof t.type == "string" && Et.has(t.type);
32
+ }
33
+ function Tt(n) {
34
+ const t = C(n);
35
+ return t !== null && t.type === c.HANDSHAKE;
36
+ }
37
+ function Rt(n) {
38
+ const t = C(n);
39
+ return t !== null && t.type === c.SESSION && typeof t.sessionId == "string";
40
+ }
41
+ function It(n) {
42
+ const t = C(n);
43
+ return t !== null && t.type === c.MUTATION && Array.isArray(t.results);
44
+ }
45
+ function Ot(n) {
46
+ const t = C(n);
47
+ return t !== null && t.type === c.SNAPSHOT && ("data" in t || "error" in t);
48
+ }
49
+ function Nt(n) {
50
+ const t = C(n);
51
+ return t !== null && t.type === c.CAMERA && Array.isArray(t.transforms);
52
+ }
53
+ function Lt(n) {
54
+ const t = C(n);
55
+ return t !== null && t.type === c.MAPPING && Array.isArray(t.attributes);
56
+ }
57
+ function Mt(n) {
58
+ const t = C(n);
59
+ return t !== null && t.type === c.LOADING_INDICATOR && typeof t.progress == "number";
60
+ }
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);
67
+ return t !== null && t.type === c.CONFIG && typeof t.viewerSettings == "object";
68
+ }
69
+ function Ft(n) {
70
+ const t = C(n);
71
+ return t !== null && t.type === c.FOCUS && typeof t.state == "string";
72
+ }
73
+ function $t(n) {
74
+ const t = C(n);
75
+ return t !== null && t.type === c.PRICING && typeof t.pricing == "object" && t.pricing !== null;
76
+ }
77
+ function C(n) {
78
+ return typeof n != "object" || n === null ? null : n;
79
+ }
80
+ class Y {
81
+ constructor(t) {
82
+ this.processor = t;
83
+ }
84
+ processor;
85
+ pending = [];
86
+ isReady = !1;
87
+ /**
88
+ * Add a message — processed immediately if ready, queued otherwise
89
+ */
90
+ enqueue(t) {
91
+ if (this.isReady) {
92
+ this.processor(t);
93
+ return;
94
+ }
95
+ this.pending.push(t);
96
+ }
97
+ /**
98
+ * Mark as ready and flush all pending messages
99
+ */
100
+ markReady() {
101
+ if (this.isReady || (this.isReady = !0, this.pending.length === 0))
102
+ return;
103
+ const t = [...this.pending];
104
+ this.pending.length = 0;
105
+ for (const e of t)
106
+ this.processor(e);
107
+ }
108
+ get ready() {
109
+ return this.isReady;
110
+ }
111
+ get pendingCount() {
112
+ return this.pending.length;
113
+ }
114
+ /**
115
+ * Check if a message of a specific type is pending
116
+ */
117
+ hasPending(t) {
118
+ return this.pending.some((e) => e.type === t);
119
+ }
120
+ /**
121
+ * Clear all pending messages
122
+ */
123
+ clear() {
124
+ this.pending.length = 0;
125
+ }
126
+ }
127
+ class Dt {
128
+ constructor(t, e) {
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();
130
+ }
131
+ getTarget;
132
+ handlers = /* @__PURE__ */ new Map();
133
+ publisher;
134
+ consumer;
135
+ shouldBuffer;
136
+ listener = null;
137
+ sourceId;
138
+ targetOrigin;
139
+ isValid;
140
+ /**
141
+ * Register a handler for a specific message type
142
+ */
143
+ on(t, e) {
144
+ this.handlers.set(t, e);
145
+ }
146
+ /**
147
+ * Remove a handler for a specific message type
148
+ */
149
+ off(t) {
150
+ this.handlers.delete(t);
151
+ }
152
+ /**
153
+ * Update the target origin for outgoing postMessage calls.
154
+ * Use when the target origin is discovered at runtime (e.g. from the first incoming message).
155
+ */
156
+ setTargetOrigin(t) {
157
+ this.targetOrigin = t;
158
+ }
159
+ /**
160
+ * Send a message to the target window
161
+ * Queued until markReady() is called
162
+ */
163
+ send(t) {
164
+ this.publisher.enqueue(t);
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
+ }
184
+ /**
185
+ * Enable sending and flush queued outgoing messages
186
+ */
187
+ markReady() {
188
+ this.publisher.markReady();
189
+ }
190
+ /**
191
+ * Enable processing of buffered incoming messages
192
+ * Only relevant when constructed with bufferIncoming: true
193
+ */
194
+ markConsumerReady() {
195
+ this.consumer.markReady();
196
+ }
197
+ /**
198
+ * Check if an incoming message of a specific type is pending in the consumer buffer
199
+ */
200
+ consumerHasPending(t) {
201
+ return this.consumer.hasPending(t);
202
+ }
203
+ /**
204
+ * Start listening for incoming messages
205
+ */
206
+ start() {
207
+ this.listener === null && (this.listener = (t) => {
208
+ const { data: e } = t;
209
+ if (!this.isValid(e) || e._source === this.sourceId)
210
+ return;
211
+ const i = this.getTarget();
212
+ if (i !== null && t.source !== null && t.source !== i)
213
+ return;
214
+ const o = e;
215
+ if (this.shouldBuffer !== null && !this.shouldBuffer(o)) {
216
+ this.routeToHandler(o);
217
+ return;
218
+ }
219
+ this.consumer.enqueue(o);
220
+ }, window.addEventListener("message", this.listener));
221
+ }
222
+ /**
223
+ * Stop listening and clean up all state
224
+ */
225
+ destroy() {
226
+ this.listener !== null && (window.removeEventListener("message", this.listener), this.listener = null), this.publisher.clear(), this.consumer.clear(), this.handlers.clear();
227
+ }
228
+ /**
229
+ * Whether the messenger is ready to send
230
+ */
231
+ get ready() {
232
+ return this.publisher.ready;
233
+ }
234
+ /**
235
+ * Number of messages waiting in the outgoing queue
236
+ */
237
+ get pendingCount() {
238
+ return this.publisher.pendingCount;
239
+ }
240
+ post(t) {
241
+ const e = this.getTarget();
242
+ e !== null && e.postMessage({ ...t, _source: this.sourceId }, this.targetOrigin);
243
+ }
244
+ routeToHandler(t) {
245
+ const e = this.handlers.get(t.type);
246
+ if (e === void 0)
247
+ return;
248
+ const i = e(t);
249
+ i instanceof Promise && i.catch((o) => {
250
+ const s = o instanceof Error ? o.message : String(o);
251
+ console.error(`Virtualdisplay: error handling ${t.type}: ${s}`);
252
+ });
253
+ }
254
+ }
255
+ class Bt {
256
+ constructor(t) {
257
+ this.send = t;
258
+ }
259
+ send;
260
+ setEnabled(t) {
261
+ this.send({
262
+ type: c.ANNOTATION_TOGGLE,
263
+ enabled: t
264
+ });
265
+ }
266
+ }
267
+ class Ut {
268
+ constructor(t, e) {
269
+ this.value = t, this.attribute = e;
270
+ }
271
+ value;
272
+ attribute;
273
+ isSelectedValue = !1;
274
+ get isSelected() {
275
+ return this.isSelectedValue;
276
+ }
277
+ setSelected(t) {
278
+ this.isSelectedValue = t;
279
+ }
280
+ }
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;
293
+ }
294
+ static valueNotFound(t, e) {
295
+ return new f(
296
+ `Value "${e}" not found for attribute "${t}"`,
297
+ A.VALUE_NOT_FOUND
298
+ );
299
+ }
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);
308
+ }
309
+ }
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);
356
+ }
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
+ }
367
+ }
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;
395
+ }
396
+ this.get(e.name)?.handleResult(e.value);
397
+ }
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;
408
+ }
409
+ }
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) {
422
+ const e = {
423
+ name: n.name,
424
+ message: n.message
425
+ };
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);
432
+ }
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))
520
+ };
521
+ }
522
+ let ht = !1, _ = dt(ut("client", { isDev: !1 }));
523
+ function ee() {
524
+ ht = !0, _ = dt(ut("client", { isDev: !0 }));
525
+ }
526
+ function Ki() {
527
+ return ht;
528
+ }
529
+ class ne {
530
+ constructor(t, e) {
531
+ this.send = t, this.onMutationFailed = e;
532
+ }
533
+ send;
534
+ onMutationFailed;
535
+ registry;
536
+ pendingCallbacks = [];
537
+ /**
538
+ * Handle incoming MAPPING message from server
539
+ */
540
+ handleMappingReceived(t) {
541
+ _.debug("Initializing from server mapping", {
542
+ attributeCount: t.attributes.length
543
+ }), this.registry = new jt(
544
+ t.attributes,
545
+ (e, i) => this.sendMutation(e, i),
546
+ this.onMutationFailed
547
+ ), this.replayPendingCallbacks();
548
+ }
549
+ handleMutationResult(t) {
550
+ this.registry?.handleResults(t.results);
551
+ }
552
+ /**
553
+ * Select a value for an attribute - sends mutation to server.
554
+ * Can be called before the mapping arrives — the mutation is sent immediately
555
+ * and the messenger's publisher queue holds it until the iframe is ready.
556
+ */
557
+ select(t, e) {
558
+ this.sendMutation([{ name: t, value: e }], !1);
559
+ }
560
+ getValues(t) {
561
+ return this.registry?.get(t)?.getValues() ?? [];
562
+ }
563
+ getValue(t, e) {
564
+ return this.registry?.get(t)?.getValue(e);
565
+ }
566
+ /**
567
+ * Register onChange callback for an attribute.
568
+ * Buffers the callback if the mapping hasn't arrived yet.
569
+ */
570
+ onChange(t, e) {
571
+ if (this.registry === void 0) {
572
+ this.pendingCallbacks.push({ name: t, callback: e });
573
+ return;
574
+ }
575
+ const i = this.registry.get(t);
576
+ if (i === void 0) {
577
+ _.warn(`onChange: attribute "${t}" not found — callback will not fire`);
578
+ return;
579
+ }
580
+ i.onChange(e);
581
+ }
582
+ getAttribute(t) {
583
+ return this.registry?.get(t);
584
+ }
585
+ /** The current selection as attribute name→value pairs, empty before the mapping arrives. */
586
+ getSelection() {
587
+ return this.registry?.getSelection() ?? {};
588
+ }
589
+ get hasMapping() {
590
+ return this.registry !== void 0;
591
+ }
592
+ sendMutation(t, e) {
593
+ const i = {
594
+ type: c.MUTATION,
595
+ mutations: t,
596
+ isInitial: e
597
+ };
598
+ this.send(i);
599
+ }
600
+ replayPendingCallbacks() {
601
+ for (const { name: t, callback: e } of this.pendingCallbacks) {
602
+ const i = this.registry?.get(t);
603
+ i === void 0 && _.warn(`onChange: attribute "${t}" not found — buffered callback will not fire`), i?.onChange(e);
604
+ }
605
+ this.pendingCallbacks.length = 0;
606
+ }
607
+ }
608
+ let ie = class {
609
+ constructor(t) {
610
+ this.send = t;
611
+ }
612
+ send;
613
+ commands = [];
614
+ /**
615
+ * Rotate camera horizontally
616
+ * @param degrees - Absolute rotation in degrees (0 = front view)
617
+ * @throws VirtualdisplayError if value is not a finite number
618
+ */
619
+ rotate(t) {
620
+ return this.validateFiniteNumber(t, "rotate"), this.commands.push({ action: "rotate", value: t }), this;
621
+ }
622
+ /**
623
+ * Tilt camera vertically
624
+ * @param degrees - Absolute tilt in degrees (0 = top view, 90 = horizontal)
625
+ * @throws VirtualdisplayError if value is not a finite number
626
+ */
627
+ tilt(t) {
628
+ return this.validateFiniteNumber(t, "tilt"), this.commands.push({ action: "tilt", value: t }), this;
629
+ }
630
+ /**
631
+ * Zoom camera
632
+ * @param percentage - Zoom percentage (100 = normal, >100 = zoom in, <100 = zoom out)
633
+ * @throws VirtualdisplayError if value is not a finite number
634
+ */
635
+ zoom(t) {
636
+ return this.validateFiniteNumber(t, "zoom"), this.commands.push({ action: "zoom", value: t }), this;
637
+ }
638
+ /**
639
+ * Execute accumulated camera commands
640
+ * @throws VirtualdisplayError if no commands have been accumulated
641
+ */
642
+ set() {
643
+ if (this.commands.length === 0)
644
+ throw f.invalidParameter(
645
+ "No camera commands to execute. Call rotate(), tilt(), or zoom() before set()."
646
+ );
647
+ const t = {
648
+ type: c.CAMERA,
649
+ commands: this.commands
650
+ };
651
+ this.send(t), this.commands = [];
652
+ }
653
+ validateFiniteNumber(t, e) {
654
+ if (!Number.isFinite(t))
655
+ throw f.invalidParameter(
656
+ `${e}() requires a finite number, got ${String(t)}`
657
+ );
658
+ }
659
+ /**
660
+ * Reset camera to initial position
661
+ * This is not chainable and executes immediately
662
+ */
663
+ reset() {
664
+ const t = {
665
+ type: c.CAMERA,
666
+ commands: [{ action: "reset" }]
667
+ };
668
+ this.send(t);
669
+ }
670
+ };
671
+ class oe {
672
+ state;
673
+ limitsState;
674
+ changeCallbacks = [];
675
+ constructor() {
676
+ this.state = {
677
+ rotation: 0,
678
+ tilt: 0,
679
+ zoom: 100
680
+ };
681
+ }
682
+ /**
683
+ * Get current rotation in degrees
684
+ */
685
+ get rotation() {
686
+ return this.state.rotation;
687
+ }
688
+ /**
689
+ * Get current tilt in degrees
690
+ */
691
+ get tilt() {
692
+ return this.state.tilt;
693
+ }
694
+ /**
695
+ * Get current zoom percentage
696
+ */
697
+ get zoom() {
698
+ return this.state.zoom;
699
+ }
700
+ /**
701
+ * Subscribe to camera position changes.
702
+ * Also fires when limits are applied via applyLimits() so components
703
+ * can re-evaluate disabled states without a separate callback.
704
+ */
705
+ onChange(t) {
706
+ this.changeCallbacks.push(t);
707
+ }
708
+ /**
709
+ * Current camera movement limits (set from CONFIG message).
710
+ * Undefined until the server sends limits in its CONFIG payload.
711
+ */
712
+ get limits() {
713
+ return this.limitsState;
714
+ }
715
+ /**
716
+ * Apply camera limits received from the server CONFIG message.
717
+ * Fires onChange callbacks so button disabled states update immediately.
718
+ */
719
+ applyLimits(t) {
720
+ this.limitsState = t;
721
+ const e = { rotation: this.rotation, tilt: this.tilt, zoom: this.zoom };
722
+ for (const i of this.changeCallbacks)
723
+ i(e);
724
+ }
725
+ /**
726
+ * Handle incoming camera position message from server
727
+ */
728
+ handlePositionUpdate(t) {
729
+ this.update(t.transforms);
730
+ }
731
+ /**
732
+ * Update camera position from server transforms
733
+ */
734
+ update(t) {
735
+ let e = !1;
736
+ for (const i of t)
737
+ switch (i.type) {
738
+ case "rotation":
739
+ this.state.rotation = i.value, e = !0;
740
+ break;
741
+ case "tilt":
742
+ this.state.tilt = i.value, e = !0;
743
+ break;
744
+ case "zoom":
745
+ this.state.zoom = i.value, e = !0;
746
+ break;
747
+ }
748
+ if (e) {
749
+ const i = {
750
+ rotation: this.rotation,
751
+ tilt: this.tilt,
752
+ zoom: this.zoom
753
+ };
754
+ for (const o of this.changeCallbacks)
755
+ o(i);
756
+ }
757
+ }
758
+ }
759
+ function se(n, t) {
760
+ const e = n.filter((s) => re(s, t));
761
+ if (e.length === 0)
762
+ return;
763
+ const i = Math.max(...e.map(K)), o = e.filter((s) => K(s) === i);
764
+ return o.length === 1 ? o[0] : void 0;
765
+ }
766
+ function re(n, t) {
767
+ return Object.entries(n.attributes).every(([e, i]) => t[e] === i);
768
+ }
769
+ function K(n) {
770
+ return Object.keys(n.attributes).length;
771
+ }
772
+ class ae {
773
+ constructor(t, e) {
774
+ this.dispatchChange = t, this.dispatchSubmit = e;
775
+ }
776
+ dispatchChange;
777
+ dispatchSubmit;
778
+ pricing;
779
+ selection = {};
780
+ resolved;
781
+ changeCallbacks = [];
782
+ renderCallbacks = [];
783
+ submitCallback;
784
+ submitDefaultLabel;
785
+ /** The resolved configuration, or undefined when nothing matches. */
786
+ get current() {
787
+ return this.resolved;
788
+ }
789
+ /** The VAT label for bare embeds (e.g. "incl. btw"), or undefined without pricing. */
790
+ get vatDisplay() {
791
+ return this.pricing?.vatDisplay;
792
+ }
793
+ /**
794
+ * The CTA label: the customer's `button_label`, or the adapter's `defaultLabel`
795
+ * when that is empty. Undefined when neither is set — a button must know what
796
+ * it says, so the CTA does not render.
797
+ */
798
+ get buttonLabel() {
799
+ const t = this.pricing?.buttonLabel;
800
+ if (t != null && t !== "")
801
+ return t;
802
+ if (this.submitDefaultLabel !== void 0 && this.submitDefaultLabel !== "")
803
+ return this.submitDefaultLabel;
804
+ }
805
+ /**
806
+ * Whether the current configuration may be submitted: a handler is registered
807
+ * and the configuration is either price-less or an `available` priced one. The
808
+ * CTA's visibility ({@link submitEnabled}) layers a label requirement on top of
809
+ * this; a programmatic `submit()` does not need a label.
810
+ */
811
+ get submittable() {
812
+ return this.submitCallback === void 0 ? !1 : this.pricing === void 0 ? !0 : this.resolved?.availability === "available";
813
+ }
814
+ /**
815
+ * Whether the CTA should render: the configuration is submittable and a label
816
+ * exists. A button must say something, so an empty label hides the CTA rather
817
+ * than rendering blank.
818
+ */
819
+ get submitEnabled() {
820
+ return this.submittable && this.buttonLabel !== void 0;
821
+ }
822
+ /**
823
+ * Subscribe to configuration changes. Fires after every selection change with
824
+ * the resolved configuration, including undefined when nothing matches.
825
+ */
826
+ onChange(t) {
827
+ this.changeCallbacks.push(t);
828
+ }
829
+ /**
830
+ * Register the submit handler the CTA hands the configuration to. Registering
831
+ * one is what switches the button on (capability driven). `defaultLabel` fills
832
+ * in when the customer authored no button label.
833
+ */
834
+ onSubmit(t, e) {
835
+ this.submitCallback = t, this.submitDefaultLabel = e?.defaultLabel, this.notifyRender();
836
+ }
837
+ /**
838
+ * Subscribe the built-in UI to re-render the price + CTA. Fires on every
839
+ * resolve and when a submit handler is registered (which can switch the button
840
+ * on without a configuration change). Separate from `onChange`, which stays the
841
+ * host-facing, mutation-driven contract. Returns a disposer.
842
+ */
843
+ onRender(t) {
844
+ return this.renderCallbacks.push(t), () => {
845
+ const e = this.renderCallbacks.indexOf(t);
846
+ e !== -1 && this.renderCallbacks.splice(e, 1);
847
+ };
848
+ }
849
+ /**
850
+ * Hand the current configuration to the registered handler and fire the
851
+ * bubbling `vd:configuration-submit` event. No-op without a handler or a
852
+ * resolved configuration. Returns the handler's promise so the caller can
853
+ * reflect the result (bezig / gelukt / mislukt).
854
+ */
855
+ async submit() {
856
+ if (!this.submittable)
857
+ return;
858
+ const t = this.resolved !== void 0 ? { attributes: this.resolved.attributes, reference: this.resolved.reference } : { attributes: { ...this.selection }, reference: "" };
859
+ this.dispatchSubmit(t), await this.submitCallback?.(t);
860
+ }
861
+ /** Called by the client when a PRICING message arrives from the viewer. */
862
+ handlePricingReceived(t) {
863
+ this.pricing = t, this.recompute();
864
+ }
865
+ /** Called by the client after every mutation result, with the full selection. */
866
+ handleSelectionChanged(t) {
867
+ this.selection = t, this.recompute();
868
+ }
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 {
1170
+ }
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)
66
1826
  };
67
- //#endregion
68
- //#region ../logger/src/format.ts
69
- function r(e) {
70
- let t = new Date(e.timestamp).toLocaleTimeString("en-GB", { hour12: !1 }), n = e.level.toUpperCase().padEnd(5), r = e.trace_id?.slice(0, 8) ?? "--------", i = e.msg, a = [
71
- "timestamp",
72
- "level",
73
- "msg",
74
- "service",
75
- "trace_id",
76
- "span_id"
77
- ], o = Object.entries(e).filter(([e]) => !a.includes(e)).map(([e, t]) => `${e}=${JSON.stringify(t)}`).join(" ");
78
- return `${t} ${n} [${r}] ${i}${o ? " " + o : ""}`;
79
- }
80
- function i(e) {
81
- return JSON.stringify(e);
82
- }
83
- //#endregion
84
- //#region ../logger/src/write.ts
85
- function a(e, t) {
86
- let n = t ? r(e) : i(e);
87
- e.level === "error" ? console.error(n) : e.level === "warn" ? console.warn(n) : console.log(n);
88
- }
89
- //#endregion
90
- //#region ../utils/src/date.ts
91
- function o() {
92
- return /* @__PURE__ */ new Date();
93
- }
94
- function s(e) {
95
- return e.toISOString();
96
- }
97
- //#endregion
98
- //#region ../logger/src/logger.ts
99
- var c = class e {
100
- constructor(e) {
101
- this.config = e;
102
- }
103
- log(e, t, n = {}) {
104
- let r = this.config.traceProvider?.() ?? {};
105
- a({
106
- timestamp: s(o()),
107
- level: e,
108
- msg: t,
109
- service: this.config.service,
110
- ...r,
111
- ...n
112
- }, this.config.isDev);
113
- }
114
- debug(e, t) {
115
- this.config.isDev && this.log("debug", e, t);
116
- }
117
- info(e, t) {
118
- this.log("info", e, t);
119
- }
120
- warn(e, t) {
121
- this.log("warn", e, t);
122
- }
123
- error(e, t) {
124
- this.log("error", e, t);
125
- }
126
- with(t) {
127
- let n = new e(this.config);
128
- return n.log = ((e, n, r = {}) => {
129
- this.log(e, n, {
130
- ...t,
131
- ...r
132
- });
133
- }).bind(this), n;
134
- }
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;
1831
+ }
1832
+ setIcon(t) {
1833
+ const e = this.element.querySelector("svg");
1834
+ e !== null ? e.replaceWith(t) : this.element.appendChild(t);
1835
+ }
1836
+ }
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"
135
1870
  };
136
- function l(e, t) {
137
- return new c({
138
- service: e,
139
- isDev: t?.isDev === void 0 ? typeof process < "u" ? process.env.NODE_ENV !== "production" : !0 : t.isDev,
140
- traceProvider: t?.traceProvider
141
- });
142
- }
143
- //#endregion
144
- //#region src/utils/logger.ts
145
- function u(e) {
146
- if (e !== void 0) return typeof e == "object" && e && !Array.isArray(e) && !(e instanceof Error) ? e : e instanceof Error ? { error: e.message } : { value: e };
147
- }
148
- function d(e) {
149
- return {
150
- debug: (t, n) => e.debug(t, u(n)),
151
- info: (t, n) => e.info(t, u(n)),
152
- warn: (t, n) => e.warn(t, u(n)),
153
- error: (t, n) => e.error(t, u(n))
154
- };
155
- }
156
- var f = !1, p = d(l("client", { isDev: !1 }));
157
- function m() {
158
- f = !0, p = d(l("client", { isDev: !0 }));
159
- }
160
- function h() {
161
- return f;
162
- }
163
- //#endregion
164
- //#region src/client/ready-state-tracker.ts
165
- var g = class {
166
- ready = !1;
167
- onReady;
168
- constructor(e) {
169
- this.onReady = e;
170
- }
171
- handleMappingReceived() {
172
- this.markReady("Mapping received from server");
173
- }
174
- handleMutationResult(e) {
175
- e.isInitial && this.markReady("Initial mutations processed");
176
- }
177
- get isReady() {
178
- return this.ready;
179
- }
180
- clear() {
181
- this.ready = !1;
182
- }
183
- markReady(e) {
184
- this.ready || (this.ready = !0, p.info(`${e} - client is now ready`), this.onReady?.());
185
- }
186
- }, _ = class {
187
- isSelectedValue = !1;
188
- constructor(e, t) {
189
- this.value = e, this.attribute = t;
190
- }
191
- get isSelected() {
192
- return this.isSelectedValue;
193
- }
194
- setSelected(e) {
195
- this.isSelectedValue = e;
196
- }
197
- }, v = class {
198
- name;
199
- defaultValue;
200
- values;
201
- onSelect;
202
- onChangeCallback;
203
- constructor(e) {
204
- this.name = e.name, this.defaultValue = e.defaultValue, this.onSelect = e.onSelect, this.values = this.createValues(e.values);
205
- }
206
- select(e) {
207
- if (!this.values.has(e)) throw t.valueNotFound(this.name, e);
208
- this.onSelect(this.name, e);
209
- }
210
- getValues() {
211
- return Array.from(this.values.values());
212
- }
213
- getValue(e) {
214
- return this.values.get(e);
215
- }
216
- onChange(e) {
217
- this.onChangeCallback = e;
218
- }
219
- handleResult(e) {
220
- let t;
221
- for (let n of this.values.values()) {
222
- let r = n.value === e;
223
- n.setSelected(r), r && (t = n);
224
- }
225
- t !== void 0 && this.onChangeCallback !== void 0 && this.onChangeCallback(t);
226
- }
227
- createValues(e) {
228
- let t = /* @__PURE__ */ new Map();
229
- for (let n of e) {
230
- let e = new _(n, this.name);
231
- n === this.defaultValue && e.setSelected(!0), t.set(n, e);
232
- }
233
- return t;
234
- }
235
- }, y = class {
236
- attributes = /* @__PURE__ */ new Map();
237
- constructor(e, t, n) {
238
- for (let n of e) {
239
- let e = this.buildOptions(n, t);
240
- this.attributes.set(n.name, new v(e));
241
- }
242
- this.onMutationFailed = n;
243
- }
244
- onMutationFailed;
245
- get(e) {
246
- return this.attributes.get(e);
247
- }
248
- defaults() {
249
- let e = [];
250
- for (let t of this.attributes.values()) t.defaultValue !== void 0 && e.push({
251
- name: t.name,
252
- value: t.defaultValue
253
- });
254
- return e;
255
- }
256
- handleResults(e) {
257
- for (let t of e) {
258
- if (!t.success) {
259
- this.onMutationFailed?.(t.name, t.value);
260
- continue;
261
- }
262
- this.get(t.name)?.handleResult(t.value);
263
- }
264
- }
265
- buildOptions(e, t) {
266
- let n = {
267
- name: e.name,
268
- values: e.values,
269
- onSelect: (e, n) => {
270
- t([{
271
- name: e,
272
- value: n
273
- }], !1);
274
- }
275
- };
276
- return e.defaultValue === void 0 ? n : {
277
- ...n,
278
- defaultValue: e.defaultValue
279
- };
280
- }
281
- }, b = {
282
- READY: "ready",
283
- MUTATION: "mutation",
284
- STATE: "state",
285
- CONFIG: "config",
286
- CAMERA: "camera",
287
- SNAPSHOT: "snapshot",
288
- MAPPING: "mapping"
289
- }, x = new Set(Object.values(b));
290
- function S(e) {
291
- if (typeof e != "object" || !e) return !1;
292
- let t = e;
293
- return typeof t.target == "string" ? !1 : typeof t.type == "string" && x.has(t.type);
294
- }
295
- function C(e) {
296
- let t = O(e);
297
- return t !== null && t.type === b.READY;
298
- }
299
- function w(e) {
300
- let t = O(e);
301
- return t !== null && t.type === b.MUTATION && Array.isArray(t.results);
302
- }
303
- function T(e) {
304
- let t = O(e);
305
- return t !== null && t.type === b.SNAPSHOT && ("data" in t || "error" in t);
306
- }
307
- function E(e) {
308
- let t = O(e);
309
- return t !== null && t.type === b.CAMERA && Array.isArray(t.transforms);
310
- }
311
- function D(e) {
312
- let t = O(e);
313
- return t !== null && t.type === b.MAPPING && Array.isArray(t.attributes);
314
- }
315
- function O(e) {
316
- return typeof e != "object" || !e ? null : e;
317
- }
318
- //#endregion
319
- //#region ../messaging/src/queue.ts
320
- var k = class {
321
- pending = [];
322
- isReady = !1;
323
- constructor(e) {
324
- this.processor = e;
325
- }
326
- enqueue(e) {
327
- if (this.isReady) {
328
- this.processor(e);
329
- return;
330
- }
331
- this.pending.push(e);
332
- }
333
- markReady() {
334
- if (this.isReady || (this.isReady = !0, this.pending.length === 0)) return;
335
- let e = [...this.pending];
336
- this.pending.length = 0;
337
- for (let t of e) this.processor(t);
338
- }
339
- get ready() {
340
- return this.isReady;
341
- }
342
- get pendingCount() {
343
- return this.pending.length;
344
- }
345
- hasPending(e) {
346
- return this.pending.some((t) => t.type === e);
347
- }
348
- clear() {
349
- this.pending.length = 0;
350
- }
351
- }, A = class {
352
- handlers = /* @__PURE__ */ new Map();
353
- publisher;
354
- consumer;
355
- shouldBuffer;
356
- listener = null;
357
- sourceId;
358
- constructor(e, t) {
359
- this.getTarget = e, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.publisher = new k((e) => this.post(e)), this.consumer = new k((e) => this.routeToHandler(e)), this.shouldBuffer = t?.shouldBuffer ?? null, t?.bufferIncoming !== !0 && this.consumer.markReady();
360
- }
361
- on(e, t) {
362
- this.handlers.set(e, t);
363
- }
364
- off(e) {
365
- this.handlers.delete(e);
366
- }
367
- send(e) {
368
- this.publisher.enqueue(e);
369
- }
370
- markReady() {
371
- this.publisher.markReady();
372
- }
373
- markConsumerReady() {
374
- this.consumer.markReady();
375
- }
376
- consumerHasPending(e) {
377
- return this.consumer.hasPending(e);
378
- }
379
- start() {
380
- this.listener === null && (this.listener = (e) => {
381
- let { data: t } = e;
382
- if (!S(t) || t._source === this.sourceId) return;
383
- let n = t;
384
- if (this.shouldBuffer !== null && !this.shouldBuffer(n)) {
385
- this.routeToHandler(n);
386
- return;
387
- }
388
- this.consumer.enqueue(n);
389
- }, window.addEventListener("message", this.listener));
390
- }
391
- destroy() {
392
- this.listener !== null && (window.removeEventListener("message", this.listener), this.listener = null), this.publisher.clear(), this.consumer.clear(), this.handlers.clear();
393
- }
394
- get ready() {
395
- return this.publisher.ready;
396
- }
397
- get pendingCount() {
398
- return this.publisher.pendingCount;
399
- }
400
- post(e) {
401
- let t = this.getTarget();
402
- t !== null && t.postMessage({
403
- ...e,
404
- _source: this.sourceId
405
- }, "*");
406
- }
407
- routeToHandler(e) {
408
- let t = this.handlers.get(e.type);
409
- if (t === void 0) return;
410
- let n = t(e);
411
- n instanceof Promise && n.catch((t) => {
412
- let n = t instanceof Error ? t.message : String(t);
413
- console.error(`Virtualdisplay: error handling ${e.type}: ${n}`);
414
- });
415
- }
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
+ });
1882
+ }
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);
1887
+ }
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);
1896
+ }
1897
+ close() {
1898
+ this.backdrop.remove(), this.keyHandler !== null && (document.removeEventListener("keydown", this.keyHandler), this.keyHandler = null), this.options.onClose?.();
1899
+ }
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;
1913
+ }
1914
+ }
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
+ }
1926
+ }
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
+ }
1932
+ }
1933
+ onSelect(t) {
1934
+ this.selectCallback = t;
1935
+ }
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);
1952
+ }
1953
+ setSelected(t) {
1954
+ this.select.value = t;
1955
+ }
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"
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;
2176
+ }
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;
2212
+ }
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;
2262
+ }
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();
2312
+ }
2313
+ /**
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).
2317
+ */
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);
2327
+ }
2328
+ hide() {
2329
+ this.dialog?.close(), this.dialog = null, this.qrSlot = null;
2330
+ }
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;
2334
+ }
2335
+ createQrSlot() {
2336
+ const t = document.createElement("div");
2337
+ return t.className = "ui-qr-slot ui-qr-slot--loading", t;
2338
+ }
2339
+ createQrImage(t) {
2340
+ const e = document.createElement("img");
2341
+ return e.src = t, e.alt = "QR code", e;
2342
+ }
2343
+ }
2344
+ function ot(n) {
2345
+ const t = Math.max(0, Math.min(1, n));
2346
+ return t * t * (3 - 2 * t);
2347
+ }
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;
2350
+ return [i + v * s * W, o - R * s * W, M];
2351
+ }
2352
+ function gi(n, t, e) {
2353
+ n.beginPath(), n.moveTo(t[0], t[1]), n.lineTo(e[0], e[1]), n.strokeStyle = "#000", n.lineWidth = 2, n.stroke();
2354
+ }
2355
+ function bi(n, t, e, i, o) {
2356
+ const s = t(e), r = t(i), l = [
2357
+ s[0] + (r[0] - s[0]) * o,
2358
+ s[1] + (r[1] - s[1]) * o,
2359
+ s[2]
2360
+ ];
2361
+ gi(n, s, l);
2362
+ }
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, $],
2367
+ [$, z],
2368
+ [z, D],
2369
+ [P, B],
2370
+ [F, U],
2371
+ [$, V],
2372
+ [D, B],
2373
+ [B, U],
2374
+ [U, V],
2375
+ [V, D]
2376
+ ], vi = 400, rt = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif';
2377
+ function yi() {
2378
+ const n = document.createElement("div");
2379
+ Object.assign(n.style, {
2380
+ position: "absolute",
2381
+ inset: "0",
2382
+ display: "flex",
2383
+ flexDirection: "column",
2384
+ alignItems: "center",
2385
+ justifyContent: "center",
2386
+ background: "#fff",
2387
+ zIndex: "20",
2388
+ opacity: "0",
2389
+ transition: "opacity 0.3s ease-in-out"
2390
+ });
2391
+ const t = document.createElement("canvas");
2392
+ Object.assign(t.style, {
2393
+ width: "100%",
2394
+ height: "100%",
2395
+ position: "absolute",
2396
+ inset: "0"
2397
+ });
2398
+ const e = document.createElement("div");
2399
+ Object.assign(e.style, {
2400
+ position: "absolute",
2401
+ top: "56%",
2402
+ left: "0",
2403
+ right: "0",
2404
+ textAlign: "center",
2405
+ pointerEvents: "none"
2406
+ });
2407
+ const i = document.createElement("span");
2408
+ Object.assign(i.style, {
2409
+ fontSize: "13px",
2410
+ fontWeight: "400",
2411
+ color: "#000",
2412
+ fontVariantNumeric: "tabular-nums",
2413
+ fontFamily: rt
2414
+ });
2415
+ const o = document.createElement("span");
2416
+ return Object.assign(o.style, {
2417
+ fontSize: "13px",
2418
+ color: "#000",
2419
+ fontFamily: rt
2420
+ }), o.textContent = vt(), e.appendChild(i), e.appendChild(o), n.appendChild(t), n.appendChild(e), { overlay: n, canvas: t, pctEl: i, phaseEl: o };
2421
+ }
2422
+ class wi {
2423
+ overlay;
2424
+ canvas;
2425
+ ctx;
2426
+ pctEl;
2427
+ phaseEl;
2428
+ animationId = null;
2429
+ showTimerId = null;
2430
+ startTime;
2431
+ hidden = !1;
2432
+ visible = !1;
2433
+ constructor(t, e = {}) {
2434
+ this.startTime = performance.now();
2435
+ const i = yi();
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());
2437
+ const o = e.showDelay ?? vi;
2438
+ this.showTimerId = setTimeout(() => {
2439
+ this.reveal();
2440
+ }, o);
2441
+ }
2442
+ get loadingTexts() {
2443
+ return {
2444
+ downloading: vt(),
2445
+ rendering: We()
2446
+ };
2447
+ }
2448
+ updateProgress(t, e) {
2449
+ this.hidden || (this.pctEl.textContent = `${Math.round(t)}%`, this.phaseEl.textContent = e !== "" ? ` · ${e}` : "");
2450
+ }
2451
+ /**
2452
+ * Show an error state — stops animation and displays the error message.
2453
+ * The overlay stays visible so the user knows something went wrong.
2454
+ */
2455
+ showError(t) {
2456
+ if (this.hidden = !1, this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.ctx !== null) {
2457
+ const e = Math.min(devicePixelRatio, 2), i = this.canvas.width / e, o = this.canvas.height / e;
2458
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, i * e, o * e);
2459
+ }
2460
+ this.pctEl.textContent = "", this.phaseEl.textContent = t, this.phaseEl.style.color = "#b91c1c", this.reveal();
2461
+ }
2462
+ hide() {
2463
+ if (!this.hidden) {
2464
+ if (this.hidden = !0, this.cancelShowTimer(), !this.visible) {
2465
+ this.destroy();
2466
+ return;
2467
+ }
2468
+ this.overlay.style.opacity = "0", this.overlay.addEventListener("transitionend", () => {
2469
+ this.destroy();
2470
+ }, { once: !0 }), setTimeout(() => {
2471
+ this.destroy();
2472
+ }, 500);
2473
+ }
2474
+ }
2475
+ destroy() {
2476
+ this.cancelShowTimer(), this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.overlay.remove();
2477
+ }
2478
+ reveal() {
2479
+ this.hidden || this.visible || (this.visible = !0, this.overlay.style.opacity = "1");
2480
+ }
2481
+ cancelShowTimer() {
2482
+ this.showTimerId !== null && (clearTimeout(this.showTimerId), this.showTimerId = null);
2483
+ }
2484
+ sizeCanvas() {
2485
+ if (this.ctx === null)
2486
+ return;
2487
+ const t = Math.min(devicePixelRatio, 2), e = this.overlay.clientWidth || 440, i = this.overlay.clientHeight || 360;
2488
+ this.canvas.width = e * t, this.canvas.height = i * t, this.ctx.scale(t, t), this.ctx.lineCap = "round", this.ctx.lineJoin = "round";
2489
+ }
2490
+ tick = () => {
2491
+ this.hidden || (this.drawFrame(), this.animationId = requestAnimationFrame(this.tick));
2492
+ };
2493
+ drawFrame() {
2494
+ if (this.ctx === null)
2495
+ return;
2496
+ const t = this.canvas.width / Math.min(devicePixelRatio, 2), e = this.canvas.height / Math.min(devicePixelRatio, 2), i = (performance.now() - this.startTime) / 1e3;
2497
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, t, e);
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];
2503
+ if (R === void 0)
2504
+ continue;
2505
+ const M = Math.min(1, k - S);
2506
+ bi(this.ctx, u, R[0], R[1], M);
2507
+ }
2508
+ this.ctx.shadowBlur = 0;
2509
+ }
2510
+ }
2511
+ class _i {
2512
+ constructor(t) {
2513
+ this.options = t, this.element = document.createElement("div"), Object.assign(this.element.style, { display: "flex", gap: "0.375rem" }), this.onFullscreenChange = () => {
2514
+ this.setFullscreenIcon(document.fullscreenElement !== null);
2515
+ }, document.addEventListener("fullscreenchange", this.onFullscreenChange);
2516
+ }
2517
+ options;
2518
+ element;
2519
+ arBtn = null;
2520
+ fullscreenBtn = null;
2521
+ onFullscreenChange;
2522
+ setFullscreenIcon(t) {
2523
+ this.fullscreenBtn !== null && this.fullscreenBtn.setIcon(t ? g.exitFullscreen() : g.fullscreen());
2524
+ }
2525
+ syncButton(t, e, i) {
2526
+ if (e && t === null) {
2527
+ const o = i();
2528
+ return this.element.appendChild(o.element), o;
2529
+ }
2530
+ return !e && t !== null ? (t.element.remove(), null) : t;
2531
+ }
2532
+ applySettings(t) {
2533
+ this.arBtn = this.syncButton(this.arBtn, t.ar !== !1 && this.options.onAr !== void 0, () => new b({
2534
+ variant: "outline",
2535
+ size: "icon",
2536
+ label: _e(),
2537
+ icon: g.ar(),
2538
+ onClick: () => {
2539
+ this.options.onAr?.();
2540
+ }
2541
+ })), this.fullscreenBtn = this.syncButton(this.fullscreenBtn, t.fullscreen !== !1 && this.options.onFullscreen !== void 0, () => new b({
2542
+ variant: "outline",
2543
+ size: "icon",
2544
+ label: Ue(),
2545
+ icon: g.fullscreen(),
2546
+ onClick: () => {
2547
+ this.options.onFullscreen?.();
2548
+ }
2549
+ }));
2550
+ }
2551
+ destroy() {
2552
+ document.removeEventListener("fullscreenchange", this.onFullscreenChange);
2553
+ }
2554
+ }
2555
+ const T = 0.5;
2556
+ class L {
2557
+ element;
2558
+ constructor(t, e) {
2559
+ this.element = Object.assign(document.createElement("div"), { className: "vd-camera-controls" });
2560
+ const i = {
2561
+ targetRotation: e.rotation,
2562
+ targetTilt: e.tilt,
2563
+ targetZoom: e.zoom,
2564
+ thetaOffset: 0
2565
+ }, o = Ci(t, e, i);
2566
+ e.onChange((s) => {
2567
+ i.targetRotation = s.rotation - i.thetaOffset, i.targetTilt = s.tilt, i.targetZoom = s.zoom, Si(o, e, s);
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));
2569
+ }
2570
+ static buildZoom(t, e) {
2571
+ const i = document.createElement("div");
2572
+ return i.className = "vd-zoom", i.appendChild(t.element), i.appendChild(e.element), i;
2573
+ }
2574
+ static buildDpad(t, e, i, o, s) {
2575
+ const r = document.createElement("div");
2576
+ r.className = "vd-dpad";
2577
+ for (const [l, d] of [
2578
+ [t, "up"],
2579
+ [e, "left"],
2580
+ [i, "center"],
2581
+ [o, "right"],
2582
+ [s, "down"]
2583
+ ])
2584
+ l.element.classList.add(`vd-dpad-${d}`), r.appendChild(l.element);
2585
+ return r;
2586
+ }
2587
+ }
2588
+ function Ci(n, t, e) {
2589
+ const i = (r) => Math.max(t.limits?.minZoom ?? 1, Math.min(t.limits?.maxZoom ?? 1 / 0, r)), o = (r) => {
2590
+ const l = t.limits?.minRotate, d = t.limits?.maxRotate;
2591
+ return l !== void 0 || d !== void 0 ? Math.max(l ?? -1 / 0, Math.min(d ?? 1 / 0, r)) : r;
2592
+ }, s = (r) => {
2593
+ const l = t.limits?.minTilt, d = t.limits?.maxTilt;
2594
+ if (l !== void 0 || d !== void 0) {
2595
+ e.targetTilt = Math.max(l ?? 0, Math.min(d ?? 180, e.targetTilt + r)), n.tilt(e.targetTilt).set();
2596
+ return;
2597
+ }
2598
+ const u = e.targetTilt + r;
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());
2600
+ };
2601
+ return {
2602
+ zoomOut: new b({ variant: "outline", size: "icon", label: yn(), icon: g.zoomOut(), onClick: () => {
2603
+ e.targetZoom = i(e.targetZoom - 10), n.zoom(e.targetZoom).set();
2604
+ } }),
2605
+ zoomIn: new b({ variant: "outline", size: "icon", label: fn(), icon: g.zoomIn(), onClick: () => {
2606
+ e.targetZoom = i(e.targetZoom + 10), n.zoom(e.targetZoom).set();
2607
+ } }),
2608
+ tiltUp: new b({ variant: "outline", size: "icon", label: On(), icon: g.tiltUp(), onClick: () => {
2609
+ s(-15);
2610
+ } }),
2611
+ rotateLeft: new b({ variant: "outline", size: "icon", label: Sn(), icon: g.rotateLeft(), onClick: () => {
2612
+ e.targetRotation = o(e.targetRotation - 15), n.rotate(e.targetRotation + e.thetaOffset).set();
2613
+ } }),
2614
+ reset: new b({ variant: "outline", size: "icon", label: dn(), icon: g.reset(), onClick: () => {
2615
+ e.thetaOffset = 0, n.reset();
2616
+ } }),
2617
+ rotateRight: new b({ variant: "outline", size: "icon", label: xn(), icon: g.rotateRight(), onClick: () => {
2618
+ e.targetRotation = o(e.targetRotation + 15), n.rotate(e.targetRotation + e.thetaOffset).set();
2619
+ } }),
2620
+ tiltDown: new b({ variant: "outline", size: "icon", label: zn(), icon: g.tiltDown(), onClick: () => {
2621
+ s(15);
2622
+ } })
2623
+ };
2624
+ }
2625
+ function Si(n, t, e) {
2626
+ const i = t.limits;
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);
2628
+ }
2629
+ const I = {
2630
+ /** Attribute selection requested (cancelable). `detail`: `{ attribute, value }`. */
2631
+ SELECT: "ui:select",
2632
+ /** Attribute selection applied. `detail`: `{ attribute, value }`. */
2633
+ SELECTED: "ui:selected",
2634
+ /** Focus changed (bubbles). `detail`: `{ component, attributes }`, or `null` for the overview. */
2635
+ FOCUS_CHANGE: "vd:focus-change",
2636
+ /** Resolved configuration changed (bubbles). `detail`: the resolved configuration, or `null` when nothing matches. */
2637
+ CONFIGURATION_CHANGE: "vd:configuration-change",
2638
+ /** Configuration submitted (bubbles). `detail`: the resolved configuration handed to the host. */
2639
+ CONFIGURATION_SUBMIT: "vd:configuration-submit"
2640
+ }, ki = 5;
2641
+ let Ai = 0;
2642
+ function j(n) {
2643
+ const t = n.replace(/([A-Z])/g, " $1").trim();
2644
+ return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase();
2645
+ }
2646
+ function Ei(n, t, e, i, o) {
2647
+ const s = document.createElement("div");
2648
+ s.className = "vd-attribute", s.dataset.name = n.name;
2649
+ const r = document.createElement("span");
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);
2652
+ return u.onSelect((p) => {
2653
+ e.dispatch(I.SELECT, { attribute: n.name, value: p }, !0).defaultPrevented || i(n.name, p);
2654
+ }), o(n.name, (p) => {
2655
+ u.setSelected(p.value), e.dispatch(I.SELECTED, { attribute: n.name, value: p.value });
2656
+ }), s.appendChild(u.element), s;
2657
+ }
2658
+ const xi = 80, Ti = 2500;
2659
+ class Ri {
2660
+ constructor(t, e = Ti) {
2661
+ this.configuration = t, this.resetMs = e, this.element = document.createElement("div"), this.element.className = "vd-commerce vd-hidden";
2662
+ const i = document.createElement("div");
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: () => {
2664
+ this.handleSubmit();
2665
+ } }), this.button.element.classList.add("vd-cta", "vd-hidden"), this.element.append(i, this.button.element), this.unsubscribe = this.configuration.onRender(() => {
2666
+ this.update();
2667
+ }), this.update();
2668
+ }
2669
+ configuration;
2670
+ resetMs;
2671
+ element;
2672
+ priceEl;
2673
+ vatEl;
2674
+ button;
2675
+ unsubscribe;
2676
+ state = "idle";
2677
+ resetTimer;
2678
+ /** Re-render from the facade. Called on every resolve and on submit-handler registration. */
2679
+ update() {
2680
+ this.renderPrice(this.configuration.current), this.renderButton();
2681
+ const t = this.configuration.current !== void 0 || this.configuration.submitEnabled;
2682
+ this.element.classList.toggle("vd-hidden", !t);
2683
+ }
2684
+ destroy() {
2685
+ this.unsubscribe(), this.resetTimer !== void 0 && clearTimeout(this.resetTimer), this.element.remove();
2686
+ }
2687
+ renderPrice(t) {
2688
+ if (t === void 0) {
2689
+ this.priceEl.textContent = "", this.vatEl.textContent = "";
2690
+ return;
2691
+ }
2692
+ this.priceEl.textContent = Ii(t.price, t.currency), this.vatEl.textContent = at(this.configuration.vatDisplay);
2693
+ }
2694
+ renderButton() {
2695
+ const t = this.configuration.submitEnabled;
2696
+ this.button.element.classList.toggle("vd-hidden", !t), t && this.state === "idle" && this.setButtonText(at(this.configuration.buttonLabel));
2697
+ }
2698
+ async handleSubmit() {
2699
+ if (this.state !== "busy") {
2700
+ this.setState("busy", Dn());
2701
+ try {
2702
+ await this.configuration.submit(), this.setState("success", jn());
2703
+ } catch {
2704
+ this.setState("failed", Zn());
2705
+ }
2706
+ this.scheduleReset();
2707
+ }
2708
+ }
2709
+ scheduleReset() {
2710
+ this.resetTimer !== void 0 && clearTimeout(this.resetTimer), this.resetTimer = setTimeout(() => {
2711
+ this.state = "idle", this.update();
2712
+ }, this.resetMs);
2713
+ }
2714
+ setState(t, e) {
2715
+ this.state = t, this.element.dataset.submit = t, this.button.element.disabled = t === "busy", this.setButtonText(e);
2716
+ }
2717
+ setButtonText(t) {
2718
+ this.button.element.textContent = t, this.button.element.setAttribute("aria-label", t);
2719
+ }
2720
+ }
2721
+ function at(n) {
2722
+ return (n ?? "").slice(0, xi);
2723
+ }
2724
+ function Ii(n, t) {
2725
+ try {
2726
+ const e = new Intl.NumberFormat(h(), { style: "currency", currency: t }), i = e.resolvedOptions().maximumFractionDigits ?? 2;
2727
+ return e.format(n / 10 ** i);
2728
+ } catch {
2729
+ return `${t} ${(n / 100).toFixed(2)}`;
2730
+ }
2731
+ }
2732
+ class St {
2733
+ slot = null;
2734
+ panel = null;
2735
+ attach(t, e) {
2736
+ this.panel = e, e.style.display = "none", this.slot = document.createElement("div"), this.slot.className = "vd-slot-bottom-left", this.slot.appendChild(e), t.appendChild(this.slot);
2737
+ }
2738
+ setVisible(t) {
2739
+ this.panel !== null && (this.panel.style.display = t ? "flex" : "none");
2740
+ }
2741
+ destroy() {
2742
+ this.slot?.remove(), this.slot = null, this.panel = null;
2743
+ }
2744
+ }
2745
+ const Oi = 572;
2746
+ class Ni {
2747
+ element = null;
2748
+ body = null;
2749
+ openButton = null;
2750
+ closeButton = null;
2751
+ container = null;
2752
+ observer = null;
2753
+ collapsible = !1;
2754
+ expanded = !0;
2755
+ visible = !1;
2756
+ attach(t, e) {
2757
+ this.container = t, e.classList.add("vd-attributes-sidebar"), this.closeButton = new b({
2758
+ variant: "ghost",
2759
+ size: "icon-sm",
2760
+ label: an(),
2761
+ icon: g.close({ size: 18 }),
2762
+ onClick: () => {
2763
+ this.expanded = !1, this.render();
2764
+ }
2765
+ });
2766
+ const i = document.createElement("div");
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({
2768
+ variant: "outline",
2769
+ size: "default",
2770
+ label: nn(),
2771
+ icon: g.configurator({ size: 18 }),
2772
+ onClick: () => {
2773
+ this.expanded = !0, this.render();
2774
+ }
2775
+ }), this.openButton.element.classList.add("vd-configurator-toggle", "vd-hidden"), t.appendChild(this.openButton.element), this.observe();
2776
+ }
2777
+ setVisible(t) {
2778
+ this.visible = t, this.render();
2779
+ }
2780
+ destroy() {
2781
+ this.observer?.disconnect(), this.observer = null, this.openButton?.element.remove(), this.element?.remove(), this.container?.classList.remove("vd-layout-sidebar-right", "vd-layout-sidebar-collapsed"), this.container = null;
2782
+ }
2783
+ observe() {
2784
+ typeof ResizeObserver > "u" || this.container === null || (this.observer = new ResizeObserver((t) => {
2785
+ const e = t[0]?.contentRect.width ?? 0;
2786
+ this.setCollapsible(e > 0 && e < Oi);
2787
+ }), this.observer.observe(this.container));
2788
+ }
2789
+ setCollapsible(t) {
2790
+ t !== this.collapsible && (this.collapsible = t, this.expanded = !t, this.render());
2791
+ }
2792
+ render() {
2793
+ if (this.element === null || this.openButton === null || this.closeButton === null || this.container === null)
2794
+ return;
2795
+ const t = this.visible && this.expanded, e = this.visible && !this.expanded;
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);
2797
+ }
2798
+ }
2799
+ class Li {
2800
+ attach(t, e) {
2801
+ }
2802
+ setVisible(t) {
2803
+ }
2804
+ destroy() {
2805
+ }
2806
+ }
2807
+ const Mi = {
2808
+ "inframe-bottom-left": () => new St(),
2809
+ "sidebar-right": () => new Ni(),
2810
+ hidden: () => new Li()
416
2811
  };
417
- new Set([b.CAMERA]);
418
- //#endregion
419
- //#region src/attributes/attributes.ts
420
- var j = class {
421
- registry;
422
- pendingCallbacks = [];
423
- constructor(e, t) {
424
- this.send = e, this.onMutationFailed = t;
425
- }
426
- handleMappingReceived(e) {
427
- p.debug("Initializing from server mapping", { attributeCount: e.attributes.length }), this.registry = new y(e.attributes, (e, t) => this.sendMutation(e, t), this.onMutationFailed);
428
- let t = this.registry.defaults();
429
- t.length > 0 && this.sendMutation(t, !0), this.replayPendingCallbacks();
430
- }
431
- handleMutationResult(e) {
432
- this.registry?.handleResults(e.results);
433
- }
434
- select(e, t) {
435
- this.sendMutation([{
436
- name: e,
437
- value: t
438
- }], !1);
439
- }
440
- getValues(e) {
441
- return this.registry?.get(e)?.getValues() ?? [];
442
- }
443
- getValue(e, t) {
444
- return this.registry?.get(e)?.getValue(t);
445
- }
446
- onChange(e, t) {
447
- if (this.registry === void 0) {
448
- this.pendingCallbacks.push({
449
- name: e,
450
- callback: t
451
- });
452
- return;
453
- }
454
- let n = this.registry.get(e);
455
- if (n === void 0) {
456
- p.warn(`onChange: attribute "${e}" not found — callback will not fire`);
457
- return;
458
- }
459
- n.onChange(t);
460
- }
461
- getAttribute(e) {
462
- return this.registry?.get(e);
463
- }
464
- get hasMapping() {
465
- return this.registry !== void 0;
466
- }
467
- sendMutation(e, t) {
468
- let n = {
469
- type: b.MUTATION,
470
- mutations: e,
471
- isInitial: t
472
- };
473
- this.send(n);
474
- }
475
- replayPendingCallbacks() {
476
- for (let { name: e, callback: t } of this.pendingCallbacks) {
477
- let n = this.registry?.get(e);
478
- n === void 0 && p.warn(`onChange: attribute "${e}" not found — buffered callback will not fire`), n?.onChange(t);
479
- }
480
- this.pendingCallbacks.length = 0;
481
- }
482
- }, M = class {
483
- commands = [];
484
- constructor(e) {
485
- this.send = e;
486
- }
487
- rotate(e) {
488
- return this.validateFiniteNumber(e, "rotate"), this.commands.push({
489
- action: "rotate",
490
- value: e
491
- }), this;
492
- }
493
- tilt(e) {
494
- return this.validateFiniteNumber(e, "tilt"), this.commands.push({
495
- action: "tilt",
496
- value: e
497
- }), this;
498
- }
499
- zoom(e) {
500
- return this.validateFiniteNumber(e, "zoom"), this.commands.push({
501
- action: "zoom",
502
- value: e
503
- }), this;
504
- }
505
- set() {
506
- if (this.commands.length === 0) throw t.invalidParameter("No camera commands to execute. Call rotate(), tilt(), or zoom() before set().");
507
- let e = {
508
- type: b.CAMERA,
509
- commands: this.commands
510
- };
511
- this.send(e), this.commands = [];
512
- }
513
- validateFiniteNumber(e, n) {
514
- if (!Number.isFinite(e)) throw t.invalidParameter(`${n}() requires a finite number, got ${String(e)}`);
515
- }
516
- reset() {
517
- let e = {
518
- type: b.CAMERA,
519
- commands: [{ action: "reset" }]
520
- };
521
- this.send(e);
522
- }
523
- }, N = class {
524
- state;
525
- changeCallbacks = [];
526
- constructor() {
527
- this.state = {
528
- rotation: 0,
529
- tilt: 0,
530
- zoom: 0
531
- };
532
- }
533
- get rotation() {
534
- return this.state.rotation;
535
- }
536
- get tilt() {
537
- return this.state.tilt;
538
- }
539
- get zoom() {
540
- return this.state.zoom;
541
- }
542
- onChange(e) {
543
- this.changeCallbacks.push(e);
544
- }
545
- handlePositionUpdate(e) {
546
- this.update(e.transforms);
547
- }
548
- update(e) {
549
- let t = !1;
550
- for (let n of e) switch (n.type) {
551
- case "rotation":
552
- this.state.rotation = n.value, t = !0;
553
- break;
554
- case "tilt":
555
- this.state.tilt = n.value, t = !0;
556
- break;
557
- case "zoom":
558
- this.state.zoom = n.value, t = !0;
559
- break;
560
- default: break;
561
- }
562
- if (t) {
563
- let e = {
564
- rotation: this.rotation,
565
- tilt: this.tilt,
566
- zoom: this.zoom
567
- };
568
- for (let t of this.changeCallbacks) t(e);
569
- }
570
- }
571
- }, P = class {
572
- developedCallbacks = /* @__PURE__ */ new Set();
573
- errorCallbacks = /* @__PURE__ */ new Set();
574
- developed = !1;
575
- failed = !1;
576
- photoData = null;
577
- constructor(e) {
578
- this.filename = e;
579
- }
580
- onDeveloped(e) {
581
- return this.developed && this.photoData !== null ? (e(this.photoData), this) : (this.developedCallbacks.add(e), this);
582
- }
583
- onError(e) {
584
- return this.errorCallbacks.add(e), this;
585
- }
586
- getFilename() {
587
- return this.filename;
588
- }
589
- develop(e) {
590
- this.developed || this.failed || (this.photoData = e, this.developed = !0, this.developedCallbacks.forEach((e) => {
591
- e(this.photoData);
592
- }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
593
- }
594
- fail(e) {
595
- this.developed || this.failed || (this.failed = !0, this.errorCallbacks.forEach((t) => {
596
- t(e);
597
- }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
598
- }
599
- }, F = class {
600
- validExtensions = [".png"];
601
- pendingPhotos = /* @__PURE__ */ new Map();
602
- constructor(e) {
603
- this.send = e;
604
- }
605
- take(e) {
606
- this.validateFilename(e), p.debug("Taking snapshot", { filename: e });
607
- let t = new P(e);
608
- return this.pendingPhotos.set(t.getFilename(), t), this.send({
609
- type: b.SNAPSHOT,
610
- filename: e
611
- }), t;
612
- }
613
- handleSnapshotResult(n) {
614
- let r = this.pendingPhotos.get(n.filename);
615
- if (r === void 0) {
616
- p.warn("No pending photo found for filename", {
617
- filename: n.filename,
618
- pendingFilenames: Array.from(this.pendingPhotos.keys())
619
- });
620
- return;
621
- }
622
- if (this.pendingPhotos.delete(n.filename), n.error !== void 0) {
623
- r.fail(new t(n.error, e.SNAPSHOT_FAILED));
624
- return;
625
- }
626
- n.data !== void 0 && r.develop({
627
- filename: n.filename,
628
- data: n.data
629
- });
630
- }
631
- validateFilename(n) {
632
- if (n === "" || typeof n != "string") throw new t("Filename must be a non-empty string", e.INVALID_PARAMETER);
633
- if (!this.validExtensions.some((e) => n.toLowerCase().endsWith(e))) throw new t(`Invalid filename: "${n}". Filename must end with one of: ${this.validExtensions.join(", ")}`, e.INVALID_PARAMETER);
634
- if (n.includes("..") || n.includes("/") || n.includes("\\")) throw new t("Filename cannot contain path separators or \"..\"", e.INVALID_PARAMETER);
635
- }
636
- }, I = class {
637
- send;
638
- constructor(e) {
639
- this.send = e;
640
- }
641
- sendInitialConfig(e) {
642
- let t = this.buildUIConfig(e), n = this.buildViewerConfig(t, e.camera);
643
- n !== void 0 && this.sendConfig(n);
644
- }
645
- buildUIConfig(e) {
646
- let t = this.extractUIValues(e);
647
- if (Object.values(t).some((e) => e !== void 0)) return {
648
- ...t.arEnabled !== void 0 && { arEnabled: t.arEnabled },
649
- ...t.fullscreenEnabled !== void 0 && { fullscreenEnabled: t.fullscreenEnabled },
650
- ...t.loadingIndicatorEnabled !== void 0 && { loadingIndicatorEnabled: t.loadingIndicatorEnabled }
651
- };
652
- }
653
- extractUIValues(e) {
654
- return {
655
- arEnabled: e.arEnabled ?? e.ui?.arEnabled,
656
- fullscreenEnabled: e.fullscreenEnabled ?? e.ui?.fullscreenEnabled,
657
- loadingIndicatorEnabled: e.loadingIndicatorEnabled ?? e.ui?.loadingIndicatorEnabled
658
- };
659
- }
660
- buildViewerConfig(e, t) {
661
- if (!(e === void 0 && t === void 0)) return {
662
- ...e !== void 0 && { ui: e },
663
- ...t !== void 0 && { camera: t }
664
- };
665
- }
666
- setArEnabled(e) {
667
- p.debug(`Setting AR button enabled: ${e}`), this.updateUIConfig({ arEnabled: e });
668
- }
669
- setFullscreenEnabled(e) {
670
- p.debug(`Setting fullscreen button enabled: ${e}`), this.updateUIConfig({ fullscreenEnabled: e });
671
- }
672
- setLoadingIndicatorEnabled(e) {
673
- p.debug(`Setting loading indicator enabled: ${e}`), this.updateUIConfig({ loadingIndicatorEnabled: e });
674
- }
675
- updateUIConfig(e) {
676
- p.debug("Updating UI configuration", e), Object.keys(e).length > 0 && this.sendConfig({ ui: e });
677
- }
678
- hideAllUI() {
679
- p.debug("Hiding all UI elements"), this.sendUIConfig({
680
- arEnabled: !1,
681
- fullscreenEnabled: !1,
682
- loadingIndicatorEnabled: !1
683
- });
684
- }
685
- showAllUI() {
686
- p.debug("Showing all UI elements"), this.sendUIConfig({
687
- arEnabled: !0,
688
- fullscreenEnabled: !0,
689
- loadingIndicatorEnabled: !0
690
- });
691
- }
692
- sendUIConfig(e) {
693
- this.sendConfig({ ui: e });
694
- }
695
- sendConfig(e) {
696
- let t = {
697
- type: b.CONFIG,
698
- config: e
699
- };
700
- p.debug("Sending config", t), this.send(t);
701
- }
702
- }, L = class {
703
- attributes;
704
- readyStateTracker;
705
- messenger;
706
- viewerService;
707
- cameraApi;
708
- cameraPositionApi;
709
- snapshotApi;
710
- options;
711
- iframe;
712
- readyCallbacks = [];
713
- mutationFailedCallbacks = [];
714
- constructor(e) {
715
- this.options = e, this.initializeLogging(), this.messenger = new A(() => this.iframe?.contentWindow ?? null);
716
- let t = (e) => {
717
- this.messenger.send(e);
718
- };
719
- this.attributes = new j(t, (e, t) => {
720
- for (let n of this.mutationFailedCallbacks) n({
721
- attribute: e,
722
- value: t
723
- });
724
- }), this.readyStateTracker = new g(() => {
725
- let e = this.readyCallbacks;
726
- this.readyCallbacks = [];
727
- for (let t of e) t(this);
728
- }), this.viewerService = new I(t), this.cameraApi = new M(t), this.cameraPositionApi = new N(), this.snapshotApi = new F(t), this.registerHandlers(), this.messenger.start(), this.createAndConnectIframe();
729
- }
730
- registerHandlers() {
731
- this.messenger.on(b.READY, (e) => {
732
- C(e) && (p.info("Server ready — flushing publisher queue"), this.messenger.markReady(), this.viewerService.sendInitialConfig(this.options));
733
- }), this.messenger.on(b.MUTATION, (e) => {
734
- w(e) && (this.attributes.handleMutationResult(e), this.readyStateTracker.handleMutationResult(e));
735
- }), this.messenger.on(b.SNAPSHOT, (e) => {
736
- T(e) && this.snapshotApi.handleSnapshotResult(e);
737
- }), this.messenger.on(b.CAMERA, (e) => {
738
- E(e) && this.cameraPositionApi.handlePositionUpdate(e);
739
- }), this.messenger.on(b.MAPPING, (e) => {
740
- D(e) && (this.attributes.handleMappingReceived(e), this.readyStateTracker.handleMappingReceived());
741
- });
742
- }
743
- initializeLogging() {
744
- this.options.debug === !0 && m(), p.info("Initializing Virtualdisplay client", {
745
- model: this.options.model,
746
- debug: this.options.debug,
747
- language: this.options.language
748
- });
749
- }
750
- createAndConnectIframe() {
751
- p.debug("Creating iframe for Virtualdisplay viewer"), this.iframe = new n(this.options).create(), p.debug("Iframe created, waiting for server READY");
752
- }
753
- select(e, t) {
754
- this.attributes.select(e, t);
755
- }
756
- getValues(e) {
757
- return this.attributes.getValues(e);
758
- }
759
- getValue(e, t) {
760
- return this.attributes.getValue(e, t);
761
- }
762
- onChange(e, t) {
763
- this.attributes.onChange(e, t);
764
- }
765
- getAttribute(e) {
766
- return this.attributes.getAttribute(e);
767
- }
768
- get isReady() {
769
- return this.readyStateTracker.isReady;
770
- }
771
- onReady(e) {
772
- if (this.readyStateTracker.isReady) {
773
- e(this);
774
- return;
775
- }
776
- this.readyCallbacks.push(e);
777
- }
778
- onMutationFailed(e) {
779
- this.mutationFailedCallbacks.push(e);
780
- }
781
- get viewer() {
782
- return this.viewerService;
783
- }
784
- get camera() {
785
- return this.cameraApi;
786
- }
787
- get cameraPosition() {
788
- return this.cameraPositionApi;
789
- }
790
- get snapshot() {
791
- return this.snapshotApi;
792
- }
793
- destroy() {
794
- p.info("Destroying Virtualdisplay client"), this.readyStateTracker.clear(), this.readyCallbacks = [], this.messenger.destroy(), this.iframe?.parentElement !== null && this.iframe?.parentElement !== void 0 && this.iframe.parentElement.removeChild(this.iframe), this.iframe = void 0, p.debug("Virtualdisplay client destroyed");
795
- }
2812
+ function zi(n) {
2813
+ return (Mi[n ?? ""] ?? (() => new St()))();
2814
+ }
2815
+ const H = w.fontFamily.sans.join(", "), q = `
2816
+ position: absolute;
2817
+ display: flex;
2818
+ z-index: 10;
2819
+ transition: inset-inline-end 0.18s ease;
2820
+ `, Pi = `
2821
+ /* Override browser's default black fullscreen backdrop */
2822
+ .vd-container:fullscreen {
2823
+ background: oklch(100% 0 0);
2824
+ }
2825
+
2826
+ /* Layout slots — four corners */
2827
+ .vd-slot-top-right {
2828
+ ${q}
2829
+ inset-block-start: ${a[3]};
2830
+ inset-inline-end: ${a[3]};
2831
+ flex-direction: column;
2832
+ align-items: flex-end;
2833
+ gap: ${a[1.5]};
2834
+ }
2835
+
2836
+ .vd-slot-bottom-left {
2837
+ ${q}
2838
+ inset-block-end: ${a[3]};
2839
+ inset-inline-start: ${a[3]};
2840
+ flex-direction: column;
2841
+ align-items: flex-start;
2842
+ gap: ${a[1.5]};
2843
+ }
2844
+
2845
+ .vd-slot-bottom-right {
2846
+ ${q}
2847
+ inset-block-end: ${a[3]};
2848
+ inset-inline-end: ${a[3]};
2849
+ flex-direction: row;
2850
+ gap: ${a[1.5]};
2851
+ }
2852
+
2853
+ /* Component styles — no positioning */
2854
+ .vd-camera-controls {
2855
+ display: flex;
2856
+ flex-direction: column;
2857
+ gap: ${a[1]};
2858
+ background: color-mix(in oklch, ${m.white} 85%, transparent);
2859
+ backdrop-filter: blur(8px);
2860
+ border-radius: ${a[2]};
2861
+ padding: ${a[2]};
2862
+ }
2863
+
2864
+ .vd-zoom {
2865
+ display: flex;
2866
+ gap: ${a[0.5]};
2867
+ }
2868
+
2869
+ .vd-dpad {
2870
+ display: grid;
2871
+ grid-template-columns: repeat(3, ${a[9]});
2872
+ grid-template-rows: repeat(3, ${a[9]});
2873
+ gap: ${a[0.5]};
2874
+ }
2875
+
2876
+ .vd-dpad-up { grid-column: 2; grid-row: 1; }
2877
+ .vd-dpad-left { grid-column: 1; grid-row: 2; }
2878
+ .vd-dpad-center { grid-column: 2; grid-row: 2; }
2879
+ .vd-dpad-right { grid-column: 3; grid-row: 2; }
2880
+ .vd-dpad-down { grid-column: 2; grid-row: 3; }
2881
+
2882
+ .vd-attributes {
2883
+ display: flex;
2884
+ flex-direction: column;
2885
+ gap: ${a[3]};
2886
+ background: color-mix(in oklch, ${m.white} 85%, transparent);
2887
+ backdrop-filter: blur(8px);
2888
+ border: 1px solid color-mix(in oklch, ${m.neutral[900]} 8%, transparent);
2889
+ border-radius: ${a[3]};
2890
+ padding: ${a[3]};
2891
+ box-shadow: 0 ${a[1]} ${a[4]} color-mix(in oklch, ${m.neutral[900]} 6%, transparent);
2892
+ font-family: ${H};
2893
+ }
2894
+
2895
+ .vd-attribute {
2896
+ display: flex;
2897
+ flex-direction: column;
2898
+ gap: ${a[1.5]};
2899
+ }
2900
+
2901
+ /* Micro-label: recedes so the controls lead. */
2902
+ .vd-attribute-label {
2903
+ font-size: ${w.fontSize.xs};
2904
+ font-weight: ${w.fontWeight.medium};
2905
+ color: ${m.neutral[500]};
2906
+ text-transform: uppercase;
2907
+ letter-spacing: 0.05em;
2908
+ font-family: ${H};
2909
+ }
2910
+
2911
+ /* sidebar-right layout — a docked, full-height right panel */
2912
+ .vd-container { --vd-sidebar-width: 300px; }
2913
+
2914
+ .vd-hidden { display: none !important; }
2915
+
2916
+ .vd-sidebar {
2917
+ position: absolute;
2918
+ inset-block: 0;
2919
+ inset-inline-end: 0;
2920
+ inline-size: var(--vd-sidebar-width);
2921
+ z-index: 11;
2922
+ display: flex;
2923
+ flex-direction: column;
2924
+ background: color-mix(in oklch, ${m.white} 90%, transparent);
2925
+ backdrop-filter: blur(10px);
2926
+ border-inline-start: 1px solid color-mix(in oklch, ${m.neutral[900]} 8%, transparent);
2927
+ box-shadow: -${a[1]} 0 ${a[4]} color-mix(in oklch, ${m.neutral[900]} 5%, transparent);
2928
+ }
2929
+
2930
+ @media (prefers-reduced-motion: no-preference) {
2931
+ .vd-sidebar { transition: transform 0.18s ease; }
2932
+ }
2933
+
2934
+ .vd-sidebar-collapsed { transform: translateX(100%); }
2935
+
2936
+ .vd-sidebar-header {
2937
+ display: flex;
2938
+ justify-content: flex-end;
2939
+ padding: ${a[1.5]} ${a[1.5]} 0;
2940
+ }
2941
+
2942
+ .vd-sidebar-body {
2943
+ flex: 1;
2944
+ overflow-y: auto;
2945
+ padding: ${a[4]};
2946
+ }
2947
+
2948
+ /* attributes inside the sidebar: flat and full-width — the panel provides the frosting */
2949
+ .vd-attributes.vd-attributes-sidebar {
2950
+ background: transparent;
2951
+ backdrop-filter: none;
2952
+ border: none;
2953
+ border-radius: 0;
2954
+ padding: 0;
2955
+ box-shadow: none;
2956
+ gap: ${a[5]};
2957
+ }
2958
+
2959
+ /* floating toggle, shown when the sidebar is collapsed on narrow embeds */
2960
+ .vd-configurator-toggle {
2961
+ position: absolute;
2962
+ inset-block-end: ${a[3]};
2963
+ inset-inline-end: ${a[3]};
2964
+ z-index: 12;
2965
+ }
2966
+
2967
+ /* while the panel is open, keep the corner controls clear of it */
2968
+ .vd-layout-sidebar-right .vd-slot-top-right { inset-inline-end: calc(var(--vd-sidebar-width) + ${a[3]}); }
2969
+ .vd-layout-sidebar-right .vd-slot-bottom-right { inset-inline-end: calc(var(--vd-sidebar-width) + ${a[3]}); }
2970
+
2971
+ /* while collapsed on a narrow embed, lift the bottom-right controls above the
2972
+ floating toggle so the AR/fullscreen buttons aren't hidden behind the
2973
+ "open configurator" button (spacing[9] = the default toggle button height) */
2974
+ .vd-layout-sidebar-collapsed .vd-slot-bottom-right {
2975
+ inset-block-end: calc(${a[3]} + ${a[9]} + ${a[1.5]});
2976
+ }
2977
+
2978
+ /* Price + CTA — sit under the attribute controls, separated by a hairline */
2979
+ .vd-commerce {
2980
+ display: flex;
2981
+ flex-direction: column;
2982
+ gap: ${a[2]};
2983
+ padding-block-start: ${a[3]};
2984
+ border-block-start: 1px solid color-mix(in oklch, ${m.neutral[900]} 8%, transparent);
2985
+ font-family: ${H};
2986
+ }
2987
+
2988
+ .vd-commerce-price {
2989
+ display: flex;
2990
+ align-items: baseline;
2991
+ gap: ${a[1.5]};
2992
+ }
2993
+
2994
+ .vd-price {
2995
+ font-size: ${w.fontSize.lg};
2996
+ font-weight: ${w.fontWeight.semibold};
2997
+ color: ${m.neutral[900]};
2998
+ }
2999
+
3000
+ .vd-vat {
3001
+ font-size: ${w.fontSize.xs};
3002
+ color: ${m.neutral[500]};
3003
+ }
3004
+
3005
+ .vd-cta {
3006
+ inline-size: 100%;
3007
+ justify-content: center;
3008
+ }
3009
+
3010
+ /* Failed submit is the one place red belongs: a clear, recoverable error state. */
3011
+ .vd-commerce[data-submit="failed"] .vd-cta {
3012
+ background: ${m.semantic.error};
3013
+ border-color: ${m.semantic.error};
3014
+ color: ${m.white};
3015
+ }
3016
+
3017
+ `;
3018
+ function Fi() {
3019
+ if (Ct(), document.getElementById("vd-styles") !== null)
3020
+ return;
3021
+ const n = document.createElement("style");
3022
+ n.id = "vd-styles", n.textContent = Pi, document.head.appendChild(n);
3023
+ }
3024
+ function $i(n, t) {
3025
+ const e = document.createElement("a");
3026
+ e.href = `data:image/png;base64,${n}`, e.download = t, e.click();
3027
+ }
3028
+ class Di {
3029
+ options;
3030
+ settings = {};
3031
+ container = null;
3032
+ loading = null;
3033
+ slots = { topRight: null, bottomRight: null };
3034
+ screenshotBtn = null;
3035
+ actionBar = null;
3036
+ cameraControls = null;
3037
+ attributes = null;
3038
+ layout = null;
3039
+ commerce = null;
3040
+ constructor(t) {
3041
+ this.options = t;
3042
+ }
3043
+ attach(t) {
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");
3045
+ }
3046
+ onReady() {
3047
+ this.loading?.hide(), this.loading = null, this.mountToolbar(), this.cameraControls !== null && (this.cameraControls.element.style.display = "flex"), this.layout?.setVisible(!0);
3048
+ }
3049
+ mountToolbar() {
3050
+ if (this.actionBar !== null || this.slots.bottomRight === null || this.container === null)
3051
+ return;
3052
+ const t = typeof this.container.requestFullscreen == "function";
3053
+ this.actionBar = new _i({
3054
+ onAr: this.options.onAr,
3055
+ onFullscreen: t ? () => {
3056
+ this.toggleFullscreen();
3057
+ } : void 0
3058
+ }), this.actionBar.applySettings(this.settings), this.slots.bottomRight.appendChild(this.actionBar.element), this.syncScreenshotButton(), this.placeScreenshotButton();
3059
+ }
3060
+ toggleFullscreen() {
3061
+ this.container !== null && (document.fullscreenElement !== null ? document.exitFullscreen() : (this.options.onFullscreenEnter?.(), this.container.requestFullscreen()));
3062
+ }
3063
+ updateProgress(t, e) {
3064
+ this.loading?.updateProgress(t, e);
3065
+ }
3066
+ showError(t) {
3067
+ this.loading?.showError(t);
3068
+ }
3069
+ applyViewerSettings(t) {
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();
3071
+ }
3072
+ renderAttributeControls(t, e) {
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)) {
3074
+ this.attributes = document.createElement("div"), this.attributes.className = "vd-attributes";
3075
+ for (const i of e)
3076
+ this.attributes.appendChild(Ei(
3077
+ i,
3078
+ t,
3079
+ this.options.eventBus,
3080
+ this.options.onSelect,
3081
+ this.options.onSubscribe
3082
+ ));
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);
3084
+ }
3085
+ }
3086
+ get viewerSettings() {
3087
+ return this.settings;
3088
+ }
3089
+ /** Tear down the configurator panel: price + CTA, attribute controls, layout. */
3090
+ teardownConfigurator() {
3091
+ this.commerce?.destroy(), this.commerce = null, this.attributes?.remove(), this.attributes = null, this.layout?.destroy(), this.layout = null;
3092
+ }
3093
+ destroy() {
3094
+ this.loading?.destroy(), this.screenshotBtn?.element.remove(), this.actionBar?.destroy(), this.actionBar?.element.remove(), this.cameraControls?.element.remove(), this.teardownConfigurator();
3095
+ for (const t of Object.values(this.slots))
3096
+ t?.remove();
3097
+ this.container = null;
3098
+ }
3099
+ createSlot(t, e) {
3100
+ const i = document.createElement("div");
3101
+ return i.className = e, t.appendChild(i), i;
3102
+ }
3103
+ syncScreenshotButton() {
3104
+ const t = this.settings.screenshot === !0;
3105
+ t && this.screenshotBtn === null && (this.screenshotBtn = new b({
3106
+ variant: "outline",
3107
+ size: "icon",
3108
+ label: Xe(),
3109
+ icon: g.screenshot(),
3110
+ onClick: () => {
3111
+ this.options.snapshotApi.take("snapshot.png").onDeveloped(({ data: e, filename: i }) => {
3112
+ $i(e, i);
3113
+ });
3114
+ }
3115
+ })), !t && this.screenshotBtn !== null && (this.screenshotBtn.element.remove(), this.screenshotBtn = null);
3116
+ }
3117
+ placeScreenshotButton() {
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));
3119
+ }
3120
+ }
3121
+ const Bi = {};
3122
+ class Ui {
3123
+ defaultServer = "https://server.virtualdisplay.io";
3124
+ options;
3125
+ serverHost;
3126
+ constructor(t, e) {
3127
+ this.options = t, this.serverHost = e;
3128
+ }
3129
+ /**
3130
+ * Create container and iframe, attach to parent element
3131
+ */
3132
+ create() {
3133
+ const t = this.createContainer(), e = this.createIframe();
3134
+ return t.appendChild(e), this.attachToParent(t), { container: t, iframe: e };
3135
+ }
3136
+ createContainer() {
3137
+ const t = document.createElement("div");
3138
+ return t.className = "vd-container", Object.assign(t.style, {
3139
+ position: "relative",
3140
+ width: "100%",
3141
+ height: "100%",
3142
+ overflow: "hidden"
3143
+ }), t;
3144
+ }
3145
+ createIframe() {
3146
+ const t = document.createElement("iframe");
3147
+ return t.src = this.buildUrl(), Object.assign(t.style, {
3148
+ width: "100%",
3149
+ height: "100%",
3150
+ border: "none",
3151
+ display: "block"
3152
+ }), t.setAttribute("allow", "xr-spatial-tracking; fullscreen"), t;
3153
+ }
3154
+ attachToParent(t) {
3155
+ this.getParentElement().appendChild(t);
3156
+ }
3157
+ buildUrl() {
3158
+ const t = this.getServerUrl(), e = new URL(t);
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();
3160
+ }
3161
+ getServerUrl() {
3162
+ if (this.serverHost !== void 0)
3163
+ return this.serverHost;
3164
+ if (Bi?.VITE_VIRTUALDISPLAY_SERVER_HOST === void 0)
3165
+ return this.defaultServer;
3166
+ }
3167
+ getParentElement() {
3168
+ if (typeof this.options.parent != "string")
3169
+ return this.options.parent;
3170
+ const t = document.querySelector(this.options.parent);
3171
+ if (t === null)
3172
+ throw f.parentNotFound(this.options.parent);
3173
+ return t;
3174
+ }
3175
+ }
3176
+ function Vi(n) {
3177
+ const { messenger: t } = n;
3178
+ t.on(c.HANDSHAKE, (e) => {
3179
+ Tt(e) && (_.info("Server handshake received, flushing publisher queue"), t.markReady());
3180
+ }), t.on(c.SESSION, (e) => {
3181
+ Rt(e) && n.onSession(e.sessionId);
3182
+ }), t.on(c.MUTATION, (e) => {
3183
+ It(e) && (n.attributes.handleMutationResult(e), n.configuration.handleSelectionChanged(n.attributes.getSelection()));
3184
+ }), t.on(c.SNAPSHOT, (e) => {
3185
+ Ot(e) && n.snapshot.handleSnapshotResult(e);
3186
+ }), t.on(c.CAMERA, (e) => {
3187
+ Nt(e) && n.cameraPosition.handlePositionUpdate(e);
3188
+ }), t.on(c.LOADING_INDICATOR, (e) => {
3189
+ Mt(e) && (e.error !== void 0 ? n.ui.showError(e.error) : n.ui.updateProgress(e.progress, e.phase));
3190
+ }), t.on(c.CONFIG, (e) => {
3191
+ Pt(e) && n.applyConfig(e);
3192
+ }), t.on(c.MAPPING, (e) => {
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);
3195
+ }
3196
+ function ji(n) {
3197
+ const { messenger: t } = n;
3198
+ t.on(c.FOCUS, (e) => {
3199
+ Ft(e) && n.focus.handleStateReceived(e);
3200
+ }), t.on(c.PRICING, (e) => {
3201
+ $t(e) && n.configuration.handlePricingReceived(e.pricing);
3202
+ }), t.on(c.SYNC, (e) => {
3203
+ zt(e) && n.onSync(e.connected);
3204
+ });
3205
+ }
3206
+ class Hi {
3207
+ ready = !1;
3208
+ onReady;
3209
+ constructor(t) {
3210
+ this.onReady = t;
3211
+ }
3212
+ handleMappingReceived() {
3213
+ this.ready || (this.ready = !0, _.info("Mapping received from server, client is now ready"), this.onReady?.());
3214
+ }
3215
+ get isReady() {
3216
+ return this.ready;
3217
+ }
3218
+ clear() {
3219
+ this.ready = !1;
3220
+ }
3221
+ }
3222
+ class qi {
3223
+ attributes;
3224
+ readyStateTracker;
3225
+ messenger;
3226
+ cameraApi;
3227
+ cameraPositionApi;
3228
+ focusApi;
3229
+ configurationApi;
3230
+ snapshotApi;
3231
+ annotationsApi;
3232
+ eventBus;
3233
+ ui;
3234
+ options;
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;
3240
+ iframe;
3241
+ container;
3242
+ readyCallbacks = [];
3243
+ mutationFailedCallbacks = [];
3244
+ /** The viewer's tracking session id, once the server announces it (SESSION). */
3245
+ viewerSessionId;
3246
+ constructor(t, e) {
3247
+ this.options = t, this.serverHost = e?.serverHost, this.initializeLogging(), this.messenger = new Dt(
3248
+ () => this.iframe?.contentWindow ?? null,
3249
+ e?.messengerOptions
3250
+ );
3251
+ const i = (s) => {
3252
+ this.messenger.send(s);
3253
+ };
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);
3261
+ },
3262
+ (s) => {
3263
+ this.dispatchConfigurationSubmit(s);
3264
+ }
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({
3270
+ snapshotApi: this.snapshotApi,
3271
+ cameraApi: this.cameraApi,
3272
+ cameraPositionApi: this.cameraPositionApi,
3273
+ eventBus: this.eventBus,
3274
+ configuration: this.configurationApi,
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
+ } },
3280
+ onFullscreenEnter: () => {
3281
+ this.trackFullscreen();
3282
+ }
3283
+ }), this.wireIncomingMessages(), e?.registerHandlers?.(this.messenger, i), this.messenger.start(), this.createAndConnectIframe();
3284
+ }
3285
+ /** Wire the inbound viewer protocol to the facades. */
3286
+ wireIncomingMessages() {
3287
+ Vi({
3288
+ messenger: this.messenger,
3289
+ attributes: this.attributes,
3290
+ configuration: this.configurationApi,
3291
+ snapshot: this.snapshotApi,
3292
+ cameraPosition: this.cameraPositionApi,
3293
+ focus: this.focusApi,
3294
+ ui: this.ui,
3295
+ readyStateTracker: this.readyStateTracker,
3296
+ applyConfig: (t) => {
3297
+ this.handleConfig(t);
3298
+ },
3299
+ onSession: (t) => {
3300
+ this.handleSession(t);
3301
+ },
3302
+ onSync: (t) => {
3303
+ this.syncTracker?.handleSync(t);
3304
+ }
3305
+ });
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
+ }
3324
+ /**
3325
+ * Dispatch a bubbling `vd:focus-change` event on the container so a page can
3326
+ * react to focus without registering an `onChange` callback.
3327
+ */
3328
+ dispatchFocusChange(t) {
3329
+ this.container?.dispatchEvent(
3330
+ new CustomEvent(I.FOCUS_CHANGE, { detail: t, bubbles: !0 })
3331
+ );
3332
+ }
3333
+ /**
3334
+ * Dispatch a bubbling `vd:configuration-change` event on the container so a
3335
+ * page or tag manager can react to price changes without a client callback.
3336
+ */
3337
+ dispatchConfigurationChange(t) {
3338
+ this.container?.dispatchEvent(
3339
+ new CustomEvent(I.CONFIGURATION_CHANGE, { detail: t ?? null, bubbles: !0 })
3340
+ );
3341
+ }
3342
+ /** Dispatch a bubbling `vd:configuration-submit` event when the CTA is used. */
3343
+ dispatchConfigurationSubmit(t) {
3344
+ this.container?.dispatchEvent(
3345
+ new CustomEvent(I.CONFIGURATION_SUBMIT, { detail: t, bubbles: !0 })
3346
+ );
3347
+ }
3348
+ handleConfig(t) {
3349
+ const e = t.viewerSettings, i = this.options.viewerSettings;
3350
+ if (e !== void 0 || i !== void 0) {
3351
+ const o = i !== void 0 ? { ...e ?? {}, ...i } : e;
3352
+ if (i !== void 0 && e !== void 0) {
3353
+ const s = Object.entries(i).filter(([r, l]) => e[r] !== l).map(([r, l]) => `${r} (metadata: ${e[r] ?? "undefined"}, override: ${l})`);
3354
+ s.length > 0 && _.warn(`Viewer settings override active: ${s.join(", ")}`);
3355
+ }
3356
+ this.ui.applyViewerSettings(o), this.annotationsApi.setEnabled(o.annotations !== !1);
3357
+ }
3358
+ this.cameraPositionApi.applyLimits(t.cameraLimits);
3359
+ }
3360
+ /** Tell the viewer the user entered fullscreen, so it records the engagement. */
3361
+ trackFullscreen() {
3362
+ this.messenger.send({ type: c.FULLSCREEN });
3363
+ }
3364
+ initializeLogging() {
3365
+ this.options.debug === !0 && ee(), _.info("Initializing Virtualdisplay client", { model: this.options.model });
3366
+ }
3367
+ createAndConnectIframe() {
3368
+ const e = new Ui(this.options, this.serverHost).create();
3369
+ this.container = e.container, this.iframe = e.iframe, this.eventBus.attach(this.container), this.ui.attach(this.container);
3370
+ try {
3371
+ const i = new URL(this.iframe.src).origin;
3372
+ this.messenger.setTargetOrigin(i);
3373
+ } catch {
3374
+ }
3375
+ }
3376
+ select(t, e) {
3377
+ this.attributes.select(t, e);
3378
+ }
3379
+ getValues(t) {
3380
+ return this.attributes.getValues(t);
3381
+ }
3382
+ getValue(t, e) {
3383
+ return this.attributes.getValue(t, e);
3384
+ }
3385
+ onChange(t, e) {
3386
+ this.attributes.onChange(t, e);
3387
+ }
3388
+ getAttribute(t) {
3389
+ return this.attributes.getAttribute(t);
3390
+ }
3391
+ get isReady() {
3392
+ return this.readyStateTracker.isReady;
3393
+ }
3394
+ onReady(t) {
3395
+ if (this.readyStateTracker.isReady) {
3396
+ t(this);
3397
+ return;
3398
+ }
3399
+ this.readyCallbacks.push(t);
3400
+ }
3401
+ onMutationFailed(t) {
3402
+ this.mutationFailedCallbacks.push(t);
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
+ }
3414
+ on(t, e) {
3415
+ this.eventBus.on(t, e);
3416
+ }
3417
+ get annotations() {
3418
+ return this.annotationsApi;
3419
+ }
3420
+ get camera() {
3421
+ return this.cameraApi;
3422
+ }
3423
+ get focus() {
3424
+ return this.focusApi;
3425
+ }
3426
+ get configuration() {
3427
+ return this.configurationApi;
3428
+ }
3429
+ get cameraPosition() {
3430
+ return this.cameraPositionApi;
3431
+ }
3432
+ get snapshot() {
3433
+ return this.snapshotApi;
3434
+ }
3435
+ /** The viewer's tracking session id, for a host page to correlate its own actions. */
3436
+ get sessionId() {
3437
+ return this.viewerSessionId;
3438
+ }
3439
+ get viewerSettings() {
3440
+ return this.ui.viewerSettings;
3441
+ }
3442
+ destroy() {
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
+ });
3526
+ }
3527
+ }
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
+ }
3557
+ }
3558
+ export {
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,
3568
+ I as VD_EVENTS,
3569
+ Ji as VirtualdisplayClient,
3570
+ f as VirtualdisplayError,
3571
+ ee as enableDebugMode,
3572
+ Ki as isDebugMode,
3573
+ _ as logger
796
3574
  };
797
- //#endregion
798
- export { v as Attribute, _ as AttributeValue, M as Camera, e as ERROR_CODES, P as Photo, F as Snapshot, L as VirtualdisplayClient, t as VirtualdisplayError, I as VirtualdisplayViewerService, m as enableDebugMode, h as isDebugMode, p as logger };