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

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