@virtualdisplay.io/client 4.0.0-alpha.3 → 4.0.0-alpha.32

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +254 -1
  2. package/README.md +127 -156
  3. package/dist/browser-7qZ1KQYo.cjs +8 -0
  4. package/dist/browser-B8yql1bQ.js +1226 -0
  5. package/dist/index.d.ts +672 -0
  6. package/dist/rolldown-runtime-5iZMYShA.cjs +1 -0
  7. package/dist/rolldown-runtime-CYdcPHCu.js +12 -0
  8. package/dist/virtualdisplay.client.cjs.js +691 -1
  9. package/dist/virtualdisplay.client.es.js +3142 -458
  10. package/dist/virtualdisplay.client.umd.js +698 -1
  11. package/package.json +3 -3
  12. package/dist/src/attributes/attribute.d.ts +0 -38
  13. package/dist/src/attributes/attribute.d.ts.map +0 -1
  14. package/dist/src/attributes/attributes.d.ts +0 -43
  15. package/dist/src/attributes/attributes.d.ts.map +0 -1
  16. package/dist/src/attributes/selections.d.ts +0 -16
  17. package/dist/src/attributes/selections.d.ts.map +0 -1
  18. package/dist/src/attributes/value.d.ts +0 -13
  19. package/dist/src/attributes/value.d.ts.map +0 -1
  20. package/dist/src/camera/camera.d.ts +0 -40
  21. package/dist/src/camera/camera.d.ts.map +0 -1
  22. package/dist/src/camera/index.d.ts +0 -4
  23. package/dist/src/camera/index.d.ts.map +0 -1
  24. package/dist/src/camera/position.d.ts +0 -43
  25. package/dist/src/camera/position.d.ts.map +0 -1
  26. package/dist/src/client/client.d.ts +0 -83
  27. package/dist/src/client/client.d.ts.map +0 -1
  28. package/dist/src/client/error.d.ts +0 -26
  29. package/dist/src/client/error.d.ts.map +0 -1
  30. package/dist/src/client/iframe-builder.d.ts +0 -25
  31. package/dist/src/client/iframe-builder.d.ts.map +0 -1
  32. package/dist/src/client/options.d.ts +0 -17
  33. package/dist/src/client/options.d.ts.map +0 -1
  34. package/dist/src/client/ready-state-tracker.d.ts +0 -26
  35. package/dist/src/client/ready-state-tracker.d.ts.map +0 -1
  36. package/dist/src/client/types.d.ts +0 -5
  37. package/dist/src/client/types.d.ts.map +0 -1
  38. package/dist/src/index.d.ts +0 -11
  39. package/dist/src/index.d.ts.map +0 -1
  40. package/dist/src/snapshot/index.d.ts +0 -3
  41. package/dist/src/snapshot/index.d.ts.map +0 -1
  42. package/dist/src/snapshot/photo.d.ts +0 -47
  43. package/dist/src/snapshot/photo.d.ts.map +0 -1
  44. package/dist/src/snapshot/snapshot.d.ts +0 -30
  45. package/dist/src/snapshot/snapshot.d.ts.map +0 -1
  46. package/dist/src/ui/viewer-config.d.ts +0 -63
  47. package/dist/src/ui/viewer-config.d.ts.map +0 -1
  48. package/dist/src/utils/logger.d.ts +0 -11
  49. package/dist/src/utils/logger.d.ts.map +0 -1
@@ -1,210 +1,392 @@
1
- //#region src/client/error.ts
2
- var e = {
3
- NO_MAPPING: "NO_MAPPING",
4
- ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
5
- VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
6
- PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
7
- INVALID_PARAMETER: "INVALID_PARAMETER",
8
- SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
9
- }, t = class t extends Error {
10
- code;
11
- constructor(e, n) {
12
- super(e), this.name = "VirtualdisplayError", this.code = n, Error.captureStackTrace?.(this, t);
1
+ import { n as e } from "./rolldown-runtime-CYdcPHCu.js";
2
+ // ../messaging/src/types.ts
3
+ var t = {
4
+ HANDSHAKE: "handshake",
5
+ SESSION: "session",
6
+ MUTATION: "mutation",
7
+ STATE: "state",
8
+ LOADING_INDICATOR: "loading_indicator",
9
+ CAMERA: "camera",
10
+ SNAPSHOT: "snapshot",
11
+ MAPPING: "mapping",
12
+ AR_ACTIVATE: "ar_activate",
13
+ FULLSCREEN: "fullscreen",
14
+ CONFIG: "config",
15
+ ANNOTATION_TOGGLE: "annotation_toggle",
16
+ FOCUS: "focus",
17
+ PRICING: "pricing",
18
+ QR_SHOWN: "qr_shown",
19
+ SYNC: "sync",
20
+ SELECTABLE: "selectable",
21
+ APP_RESOLVED: "app_resolved"
22
+ }, n = "main", r = {
23
+ FOCUS: "focus",
24
+ UNFOCUS: "unfocus"
25
+ }, i = {
26
+ FOCUSED: "focused",
27
+ UNFOCUSED: "unfocused"
28
+ }, a = {
29
+ AVAILABLE: "available",
30
+ VIEW_ONLY: "view_only",
31
+ HIDDEN: "hidden"
32
+ };
33
+ // ../messaging/src/as-object.ts
34
+ function o(e) {
35
+ return typeof e != "object" || !e ? null : e;
36
+ }
37
+ // ../messaging/src/guards.ts
38
+ var s = new Set(Object.values(t));
39
+ function c(e) {
40
+ if (typeof e != "object" || !e) return !1;
41
+ let t = e;
42
+ return typeof t.target != "string" && typeof t.type == "string" && s.has(t.type);
43
+ }
44
+ function l(e) {
45
+ let n = o(e);
46
+ return n !== null && n.type === t.HANDSHAKE;
47
+ }
48
+ function u(e) {
49
+ let n = o(e);
50
+ return n !== null && n.type === t.SESSION && typeof n.sessionId == "string";
51
+ }
52
+ function ee(e) {
53
+ let n = o(e);
54
+ return n !== null && n.type === t.APP_RESOLVED && typeof n.appType == "string" && n.appType !== "" && typeof n.name == "string";
55
+ }
56
+ function d(e) {
57
+ let n = o(e);
58
+ return n !== null && n.type === t.MUTATION && typeof n.instance == "string" && n.instance !== "" && Array.isArray(n.results);
59
+ }
60
+ function f(e) {
61
+ let n = o(e);
62
+ return n !== null && n.type === t.SNAPSHOT && ("data" in n || "error" in n);
63
+ }
64
+ function p(e) {
65
+ let n = o(e);
66
+ return n !== null && n.type === t.CAMERA && Array.isArray(n.transforms);
67
+ }
68
+ function te(e) {
69
+ let n = o(e);
70
+ return n !== null && n.type === t.MAPPING && typeof n.instance == "string" && n.instance !== "" && Array.isArray(n.attributes);
71
+ }
72
+ function ne(e) {
73
+ let n = o(e);
74
+ return n !== null && n.type === t.LOADING_INDICATOR && typeof n.progress == "number";
75
+ }
76
+ function m(e) {
77
+ let n = o(e);
78
+ return n !== null && n.type === t.SYNC && typeof n.connected == "boolean";
79
+ }
80
+ function re(e) {
81
+ let n = o(e);
82
+ return n !== null && n.type === t.SELECTABLE && typeof n.instance == "string" && n.instance !== "" && Array.isArray(n.attributes);
83
+ }
84
+ function ie(e) {
85
+ let n = o(e);
86
+ return n !== null && n.type === t.CONFIG && typeof n.viewerSettings == "object";
87
+ }
88
+ function ae(e) {
89
+ let n = o(e);
90
+ return n !== null && n.type === t.FOCUS && typeof n.instance == "string" && n.instance !== "" && typeof n.state == "string";
91
+ }
92
+ function oe(e) {
93
+ let n = o(e);
94
+ return n !== null && n.type === t.PRICING && typeof n.instance == "string" && n.instance !== "" && typeof n.pricing == "object" && n.pricing !== null;
95
+ }
96
+ // ../messaging/src/queue.ts
97
+ var se = class {
98
+ processor;
99
+ pending = [];
100
+ isReady = !1;
101
+ constructor(e) {
102
+ this.processor = e;
13
103
  }
14
- static noMapping() {
15
- return new t("No attributes available. Waiting for server metadata.", e.NO_MAPPING);
104
+ enqueue(e) {
105
+ this.isReady ? this.processor(e) : this.pending.push(e);
16
106
  }
17
- static attributeNotFound(n) {
18
- return new t(`Attribute "${n}" not found in mapping`, e.ATTRIBUTE_NOT_FOUND);
107
+ markReady() {
108
+ if (this.isReady || (this.isReady = !0, this.pending.length === 0)) return;
109
+ let e = [...this.pending];
110
+ this.pending.length = 0;
111
+ for (let t of e) this.processor(t);
19
112
  }
20
- static valueNotFound(n, r) {
21
- return new t(`Value "${r}" not found for attribute "${n}"`, e.VALUE_NOT_FOUND);
113
+ hasPending(e) {
114
+ return this.pending.some((t) => t.type === e);
22
115
  }
23
- static parentNotFound(n) {
24
- return new t(`Parent element not found: ${n}`, e.PARENT_NOT_FOUND);
116
+ clear() {
117
+ this.pending.length = 0;
25
118
  }
26
- static invalidParameter(n) {
27
- return new t(n, e.INVALID_PARAMETER);
119
+ }, ce = class {
120
+ getTarget;
121
+ handlers = /* @__PURE__ */ new Map();
122
+ publisher;
123
+ consumer;
124
+ listener = null;
125
+ sourceId;
126
+ targetOrigin;
127
+ isValid;
128
+ onHandlerError;
129
+ constructor(e, t) {
130
+ this.getTarget = e, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.targetOrigin = "*", this.isValid = t?.isValid ?? c, this.onHandlerError = t?.onHandlerError ?? le, this.publisher = new se((e) => this.post(e)), this.consumer = new se((e) => this.routeToHandler(e)), t?.bufferIncoming !== !0 && this.consumer.markReady();
28
131
  }
29
- static snapshotFailed(n, r) {
30
- return new t(`Snapshot failed for "${n}": ${r}`, e.SNAPSHOT_FAILED);
132
+ on(e, t) {
133
+ this.handlers.set(e, t);
31
134
  }
32
- }, n = class {
33
- defaultServer = "https://server.virtualdisplay.io";
34
- options;
35
- constructor(e) {
36
- this.options = e;
135
+ setTargetOrigin(e) {
136
+ this.targetOrigin = e;
37
137
  }
38
- create() {
39
- let e = this.createIframe();
40
- return this.attachToParent(e), e;
138
+ send(e) {
139
+ this.publisher.enqueue(e);
41
140
  }
42
- createIframe() {
43
- let e = document.createElement("iframe");
44
- return e.src = this.buildUrl(), Object.assign(e.style, {
45
- width: "100%",
46
- height: "100%",
47
- border: "none"
48
- }), e.setAttribute("allow", "xr-spatial-tracking; fullscreen"), e;
141
+ receive(e) {
142
+ this.isValid(e) && this.consumer.enqueue(e);
49
143
  }
50
- attachToParent(e) {
51
- this.getParentElement().appendChild(e);
144
+ markReady() {
145
+ this.publisher.markReady();
52
146
  }
53
- buildUrl() {
54
- let e = this.getServerUrl(), t = new URL(e);
55
- return t.searchParams.set("license", this.options.license), t.searchParams.set("model", this.options.model), this.options.debug === !0 && t.searchParams.set("debug", "true"), this.options.language !== void 0 && t.searchParams.set("language", this.options.language), t.toString();
147
+ markConsumerReady() {
148
+ this.consumer.markReady();
56
149
  }
57
- getServerUrl() {
58
- return this.defaultServer;
150
+ consumerHasPending(e) {
151
+ return this.consumer.hasPending(e);
59
152
  }
60
- getParentElement() {
61
- if (typeof this.options.parent != "string") return this.options.parent;
62
- let e = document.querySelector(this.options.parent);
63
- if (e === null) throw t.parentNotFound(this.options.parent);
64
- return e;
153
+ start() {
154
+ this.listener === null && (this.listener = (e) => {
155
+ let { data: t } = e;
156
+ if (!this.isValid(t) || t._source === this.sourceId) return;
157
+ let n = this.getTarget();
158
+ if (n !== null && e.source !== null && e.source !== n) return;
159
+ let r = t;
160
+ this.consumer.enqueue(r);
161
+ }, window.addEventListener("message", this.listener));
162
+ }
163
+ destroy() {
164
+ this.listener !== null && (window.removeEventListener("message", this.listener), this.listener = null), this.publisher.clear(), this.consumer.clear(), this.handlers.clear();
165
+ }
166
+ post(e) {
167
+ let t = this.getTarget();
168
+ t !== null && t.postMessage({
169
+ ...e,
170
+ _source: this.sourceId
171
+ }, this.targetOrigin);
172
+ }
173
+ routeToHandler(e) {
174
+ let t = this.handlers.get(e.type);
175
+ if (t === void 0) return;
176
+ let n;
177
+ try {
178
+ n = t(e);
179
+ } catch (t) {
180
+ this.onHandlerError(e, t);
181
+ return;
182
+ }
183
+ n instanceof Promise && n.catch((t) => {
184
+ this.onHandlerError(e, t);
185
+ });
65
186
  }
66
187
  };
67
- //#endregion
68
- //#region ../logger/src/format.ts
69
- function r(e) {
70
- let t = new Date(e.timestamp).toLocaleTimeString("en-GB", { hour12: !1 }), n = e.level.toUpperCase().padEnd(5), r = e.trace_id?.slice(0, 8) ?? "--------", i = e.msg, a = [
71
- "timestamp",
72
- "level",
73
- "msg",
74
- "service",
75
- "trace_id",
76
- "span_id"
77
- ], o = Object.entries(e).filter(([e]) => !a.includes(e)).map(([e, t]) => `${e}=${JSON.stringify(t)}`).join(" ");
78
- return `${t} ${n} [${r}] ${i}${o ? " " + o : ""}`;
79
- }
80
- function i(e) {
81
- return JSON.stringify(e);
82
- }
83
- //#endregion
84
- //#region ../logger/src/write.ts
85
- function a(e, t) {
86
- let n = t ? r(e) : i(e);
87
- e.level === "error" ? console.error(n) : e.level === "warn" ? console.warn(n) : console.log(n);
88
- }
89
- //#endregion
90
- //#region ../utils/src/date.ts
91
- function o() {
92
- return /* @__PURE__ */ new Date();
93
- }
94
- function s(e) {
95
- return e.toISOString();
188
+ function le(e, t) {
189
+ let n = t instanceof Error ? t.message : String(t);
190
+ console.error(`Virtualdisplay: error handling ${e.type}: ${n}`);
96
191
  }
97
- //#endregion
98
- //#region ../logger/src/logger.ts
99
- var c = class e {
100
- constructor(e) {
101
- this.config = e;
192
+ t.CAMERA, t.LOADING_INDICATOR;
193
+ // ../client-core/src/annotations/annotations.ts
194
+ var ue = class {
195
+ send;
196
+ instance;
197
+ constructor(e, t) {
198
+ this.send = e, this.instance = t;
102
199
  }
103
- log(e, t, n = {}) {
104
- let r = this.config.traceProvider?.() ?? {};
105
- a({
106
- timestamp: s(o()),
107
- level: e,
108
- msg: t,
109
- service: this.config.service,
110
- ...r,
111
- ...n
112
- }, this.config.isDev);
200
+ setEnabled(e) {
201
+ this.send({
202
+ type: t.ANNOTATION_TOGGLE,
203
+ instance: this.instance,
204
+ enabled: e
205
+ });
113
206
  }
114
- debug(e, t) {
115
- this.config.isDev && this.log("debug", e, t);
207
+ }, de = class {
208
+ value;
209
+ attribute;
210
+ label;
211
+ isSelectedValue = !1;
212
+ constructor(e, t, n) {
213
+ this.value = e, this.attribute = t, this.label = n;
116
214
  }
117
- info(e, t) {
118
- this.log("info", e, t);
215
+ get isSelected() {
216
+ return this.isSelectedValue;
119
217
  }
120
- warn(e, t) {
121
- this.log("warn", e, t);
218
+ setSelected(e) {
219
+ this.isSelectedValue = e;
122
220
  }
123
- error(e, t) {
124
- this.log("error", e, t);
221
+ }, fe = {}, pe = [
222
+ "en",
223
+ "nl",
224
+ "de"
225
+ ], me = "PARAGLIDE_LOCALE", h = ["globalVariable", "baseLocale"], he = [], g = typeof window > "u";
226
+ globalThis.__paraglide = globalThis.__paraglide ?? {}, globalThis.__paraglide.ssr = globalThis.__paraglide.ssr ?? {};
227
+ var _, ge = !1, v = () => {
228
+ let e = h;
229
+ !g && typeof window < "u" && window.location?.href && (e = Ne(window.location.href));
230
+ let t = _e(e, typeof window < "u" ? window.location?.href : void 0);
231
+ if (t) return ge || (_ = t, ge = !0, ye(t, { reload: !1 })), t;
232
+ throw Error("No locale found. Read the docs https://paraglidejs.com/errors#no-locale-found");
233
+ };
234
+ function _e(e, t) {
235
+ let n;
236
+ for (let t of e) {
237
+ if (t === "baseLocale") n = "en";
238
+ else if (t === "globalVariable" && _ !== void 0) n = _;
239
+ else if (Pe(t) && x.has(t)) {
240
+ let e = x.get(t);
241
+ if (e) {
242
+ let t = e.getLocale();
243
+ if (t instanceof Promise) continue;
244
+ if (t !== void 0) return xe(t);
245
+ }
246
+ }
247
+ let e = y(n);
248
+ if (e) return e;
125
249
  }
126
- with(t) {
127
- let n = new e(this.config);
128
- return n.log = ((e, n, r = {}) => {
129
- this.log(e, n, {
130
- ...t,
131
- ...r
132
- });
133
- }).bind(this), n;
250
+ }
251
+ var ve = (e) => {
252
+ e ? window.location.href = e : window.location.reload();
253
+ }, ye = (e, t) => {
254
+ let n = {
255
+ reload: !0,
256
+ ...t
257
+ }, r;
258
+ try {
259
+ r = v();
260
+ } catch {}
261
+ let i = [], a = h;
262
+ !g && typeof window < "u" && window.location?.href && (a = Ne(window.location.href));
263
+ for (let t of a) if (t === "globalVariable") _ = e;
264
+ else if (t === "baseLocale") continue;
265
+ else if (Pe(t) && x.has(t)) {
266
+ let n = x.get(t);
267
+ if (n) {
268
+ let r = n.setLocale(e);
269
+ r instanceof Promise && (r = r.catch((e) => {
270
+ throw Error(`Custom strategy "${t}" setLocale failed.`, { cause: e });
271
+ }), i.push(r));
272
+ }
134
273
  }
135
- };
136
- function l(e, t) {
137
- return new c({
138
- service: e,
139
- isDev: t?.isDev === void 0 ? typeof process < "u" ? process.env.NODE_ENV !== "production" : !0 : t.isDev,
140
- traceProvider: t?.traceProvider
274
+ let o = () => {
275
+ !g && n.reload && window.location && e !== r && ve(void 0);
276
+ };
277
+ if (i.length) return Promise.all(i).then(() => {
278
+ o();
141
279
  });
280
+ o();
281
+ }, be = () => typeof window < "u" ? window.location.origin : "http://fallback.com";
282
+ function y(e) {
283
+ if (typeof e != "string") return;
284
+ let t = e.toLowerCase();
285
+ for (let e of pe) if (e.toLowerCase() === t) return e;
142
286
  }
143
- //#endregion
144
- //#region src/utils/logger.ts
145
- function u(e) {
146
- if (e !== void 0) return typeof e == "object" && e && !Array.isArray(e) && !(e instanceof Error) ? e : e instanceof Error ? { error: e.message } : { value: e };
287
+ function xe(e) {
288
+ let t = y(e);
289
+ if (t) return t;
290
+ throw Error(`Invalid locale: ${e}. Expected one of: ${pe.join(", ")}`);
147
291
  }
148
- function d(e) {
149
- return {
150
- debug: (t, n) => e.debug(t, u(n)),
151
- info: (t, n) => e.info(t, u(n)),
152
- warn: (t, n) => e.warn(t, u(n)),
153
- error: (t, n) => e.error(t, u(n))
154
- };
292
+ function b(e) {
293
+ return e;
155
294
  }
156
- var f = !1, p = d(l("client", { isDev: !1 }));
157
- function m() {
158
- f = !0, p = d(l("client", { isDev: !0 }));
295
+ function Se(e, t) {
296
+ return new fe(e, t.href);
159
297
  }
160
- function h() {
161
- return f;
298
+ function Ce() {
299
+ return 0;
162
300
  }
163
- //#endregion
164
- //#region src/client/ready-state-tracker.ts
165
- var g = class {
166
- ready = !1;
167
- onReady;
168
- constructor(e) {
169
- this.onReady = e;
170
- }
171
- handleMappingReceived() {
172
- this.markReady("Mapping received from server");
173
- }
174
- handleMutationResult(e) {
175
- e.isInitial && this.markReady("Initial mutations processed");
176
- }
177
- get isReady() {
178
- return this.ready;
179
- }
180
- clear() {
181
- this.ready = !1;
182
- }
183
- markReady(e) {
184
- this.ready || (this.ready = !0, p.info(`${e} - client is now ready`), this.onReady?.());
301
+ function we(e) {
302
+ return he;
303
+ }
304
+ function Te(e, t) {
305
+ return e.exec(t.href);
306
+ }
307
+ var Ee = me.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
308
+ RegExp(`(?:^|;\\s*)${Ee}=([^;]*)`);
309
+ function De(e) {
310
+ return Oe(e);
311
+ }
312
+ function Oe(e) {
313
+ let t = b(typeof e == "string" ? new URL(e, be()) : new URL(e.href)), n = t.pathname.replace(/^\/+/, "").split("/");
314
+ return n.length > 0 && y(n[0]) && (t.pathname = "/" + n.slice(1).join("/")), b(t);
315
+ }
316
+ var ke = -1, Ae, je;
317
+ function Me(e) {
318
+ if (he.length === 0) return;
319
+ let t = typeof e == "string" ? e : e.href, n = Ce();
320
+ if (Ae === t && ke === n) return je;
321
+ let r = b(new URL(t, "http://example.com")), i = De(r), a = i.href === r.href ? [r] : [r, i], o;
322
+ for (let e of a) {
323
+ for (let t of we(e)) if (Te(Se(t.match, e), e)) {
324
+ o = t;
325
+ break;
326
+ }
327
+ if (o) break;
185
328
  }
186
- }, _ = class {
187
- isSelectedValue = !1;
329
+ return ke = n, Ae = t, je = o, o;
330
+ }
331
+ function Ne(e) {
332
+ let t = Me(e);
333
+ return t && t.exclude !== !0 && Array.isArray(t.strategy) ? t.strategy : h;
334
+ }
335
+ var x = /* @__PURE__ */ new Map();
336
+ function Pe(e) {
337
+ return typeof e == "string" && /^custom-[A-Za-z0-9_-]+$/.test(e);
338
+ }
339
+ // ../client-core/src/attributes/label.ts
340
+ function Fe(e, t) {
341
+ return Ie(t !== void 0 && Object.hasOwn(t, e) ? t[e] : void 0) ?? S(e);
342
+ }
343
+ function Ie(e) {
344
+ if (e === void 0) return;
345
+ for (let t of [
346
+ v(),
347
+ "nl",
348
+ "en"
349
+ ]) if (Object.hasOwn(e, t) && e[t] !== void 0) return e[t];
350
+ let [t] = Object.values(e);
351
+ return t;
352
+ }
353
+ function S(e) {
354
+ let t = e.replace(/([A-Z])/g, " $1").trim();
355
+ return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase();
356
+ }
357
+ // ../client-core/src/client/error.ts
358
+ var C = {
359
+ NO_MAPPING: "NO_MAPPING",
360
+ ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
361
+ VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
362
+ PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
363
+ INVALID_PARAMETER: "INVALID_PARAMETER",
364
+ SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
365
+ }, w = class e extends Error {
366
+ code;
188
367
  constructor(e, t) {
189
- this.value = e, this.attribute = t;
368
+ super(e), this.name = "VirtualdisplayError", this.code = t;
190
369
  }
191
- get isSelected() {
192
- return this.isSelectedValue;
370
+ static valueNotFound(t, n) {
371
+ return new e(`Value "${n}" not found for attribute "${t}"`, C.VALUE_NOT_FOUND);
193
372
  }
194
- setSelected(e) {
195
- this.isSelectedValue = e;
373
+ static parentNotFound(t) {
374
+ return new e(`Parent element not found: ${t}`, C.PARENT_NOT_FOUND);
375
+ }
376
+ static invalidParameter(t) {
377
+ return new e(t, C.INVALID_PARAMETER);
196
378
  }
197
- }, v = class {
379
+ }, Le = class {
198
380
  name;
199
381
  defaultValue;
200
382
  values;
201
383
  onSelect;
202
384
  onChangeCallback;
203
385
  constructor(e) {
204
- this.name = e.name, this.defaultValue = e.defaultValue, this.onSelect = e.onSelect, this.values = this.createValues(e.values);
386
+ this.name = e.name, this.defaultValue = e.defaultValue, this.onSelect = e.onSelect, this.values = this.createValues(e.values, e.labels);
205
387
  }
206
388
  select(e) {
207
- if (!this.values.has(e)) throw t.valueNotFound(this.name, e);
389
+ if (!this.values.has(e)) throw w.valueNotFound(this.name, e);
208
390
  this.onSelect(this.name, e);
209
391
  }
210
392
  getValues() {
@@ -213,6 +395,9 @@ var g = class {
213
395
  getValue(e) {
214
396
  return this.values.get(e);
215
397
  }
398
+ get selected() {
399
+ for (let e of this.values.values()) if (e.isSelected) return e.value;
400
+ }
216
401
  onChange(e) {
217
402
  this.onChangeCallback = e;
218
403
  }
@@ -224,20 +409,20 @@ var g = class {
224
409
  }
225
410
  t !== void 0 && this.onChangeCallback !== void 0 && this.onChangeCallback(t);
226
411
  }
227
- createValues(e) {
228
- let t = /* @__PURE__ */ new Map();
229
- for (let n of e) {
230
- let e = new _(n, this.name);
231
- n === this.defaultValue && e.setSelected(!0), t.set(n, e);
412
+ createValues(e, t) {
413
+ let n = /* @__PURE__ */ new Map();
414
+ for (let r of e) {
415
+ let e = new de(r, this.name, Fe(r, t));
416
+ r === this.defaultValue && e.setSelected(!0), n.set(r, e);
232
417
  }
233
- return t;
418
+ return n;
234
419
  }
235
- }, y = class {
420
+ }, Re = class {
236
421
  attributes = /* @__PURE__ */ new Map();
237
422
  constructor(e, t, n) {
238
423
  for (let n of e) {
239
424
  let e = this.buildOptions(n, t);
240
- this.attributes.set(n.name, new v(e));
425
+ this.attributes.set(n.name, new Le(e));
241
426
  }
242
427
  this.onMutationFailed = n;
243
428
  }
@@ -245,27 +430,22 @@ var g = class {
245
430
  get(e) {
246
431
  return this.attributes.get(e);
247
432
  }
248
- defaults() {
249
- let e = [];
250
- for (let t of this.attributes.values()) t.defaultValue !== void 0 && e.push({
251
- name: t.name,
252
- value: t.defaultValue
253
- });
433
+ getSelection() {
434
+ let e = {};
435
+ for (let [t, n] of this.attributes) {
436
+ let r = n.selected;
437
+ r !== void 0 && (e[t] = r);
438
+ }
254
439
  return e;
255
440
  }
256
441
  handleResults(e) {
257
- for (let t of e) {
258
- if (!t.success) {
259
- this.onMutationFailed?.(t.name, t.value);
260
- continue;
261
- }
262
- this.get(t.name)?.handleResult(t.value);
263
- }
442
+ for (let t of e) t.success ? this.get(t.name)?.handleResult(t.value) : this.onMutationFailed?.(t.name, t.value);
264
443
  }
265
444
  buildOptions(e, t) {
266
445
  let n = {
267
446
  name: e.name,
268
447
  values: e.values,
448
+ ...e.labels !== void 0 && { labels: e.labels },
269
449
  onSelect: (e, n) => {
270
450
  t([{
271
451
  name: e,
@@ -278,155 +458,185 @@ var g = class {
278
458
  defaultValue: e.defaultValue
279
459
  };
280
460
  }
281
- }, b = {
282
- READY: "ready",
283
- MUTATION: "mutation",
284
- STATE: "state",
285
- CONFIG: "config",
286
- CAMERA: "camera",
287
- SNAPSHOT: "snapshot",
288
- MAPPING: "mapping"
289
- }, x = new Set(Object.values(b));
290
- function S(e) {
291
- if (typeof e != "object" || !e) return !1;
292
- let t = e;
293
- return typeof t.target == "string" ? !1 : typeof t.type == "string" && x.has(t.type);
461
+ }, T = 4096, E = 16384, ze = [
462
+ 1024,
463
+ 256,
464
+ 64
465
+ ], Be = 20, D = [
466
+ "timestamp",
467
+ "level",
468
+ "msg",
469
+ "service",
470
+ "trace_id",
471
+ "span_id"
472
+ ];
473
+ function Ve(e) {
474
+ let t = new Date(e.timestamp).toLocaleTimeString("en-GB", { hour12: !1 }), n = e.level.toUpperCase().padEnd(5), r = e.trace_id?.slice(0, 8) ?? "--------", i = e.msg, a = Object.entries(e).filter(([e]) => !D.includes(e)).map(([e, t]) => `${e}=${O(t, T).line}`).join(" ");
475
+ return `${t} ${n} [${r}] ${i}${a ? " " + a : ""}`;
294
476
  }
295
- function C(e) {
296
- let t = O(e);
297
- return t !== null && t.type === b.READY;
477
+ function He(e) {
478
+ for (let t of [T, ...ze]) {
479
+ let { line: n, truncated: r } = O(e, t);
480
+ if (!r) {
481
+ if (n.length <= E) return n;
482
+ continue;
483
+ }
484
+ let { line: i } = O({
485
+ ...e,
486
+ log_truncated: !0
487
+ }, t);
488
+ if (i.length <= E) return i;
489
+ }
490
+ let t = Object.keys(e).filter((e) => !D.includes(e)), n = {
491
+ ...We(e, D),
492
+ log_truncated: !0,
493
+ log_dropped_count: t.length
494
+ }, r = O({
495
+ ...n,
496
+ log_dropped_attrs: t.slice(0, Be)
497
+ }, T);
498
+ return r.line.length <= E ? r.line : O(n, T).line;
298
499
  }
299
- function w(e) {
300
- let t = O(e);
301
- return t !== null && t.type === b.MUTATION && Array.isArray(t.results);
500
+ function O(e, t) {
501
+ let n = [], r = !1;
502
+ return {
503
+ line: JSON.stringify(e, function(e, i) {
504
+ if (typeof i == "string") return i.length > t && (r = !0), Ue(i, t);
505
+ if (typeof i == "bigint") return i.toString();
506
+ if (typeof i != "object" || !i) return i;
507
+ for (; n.length > 0 && n.at(-1) !== this;) n.pop();
508
+ return n.includes(i) ? "[Circular]" : (n.push(i), i);
509
+ }),
510
+ truncated: r
511
+ };
302
512
  }
303
- function T(e) {
304
- let t = O(e);
305
- return t !== null && t.type === b.SNAPSHOT && ("data" in t || "error" in t);
513
+ function Ue(e, t) {
514
+ return e.length <= t ? e : `${e.slice(0, t)}...[truncated ${e.length - t} chars]`;
306
515
  }
307
- function E(e) {
308
- let t = O(e);
309
- return t !== null && t.type === b.CAMERA && Array.isArray(t.transforms);
516
+ function We(e, t) {
517
+ let n = {};
518
+ for (let r of t) e[r] !== void 0 && (n[r] = e[r]);
519
+ return n;
310
520
  }
311
- function D(e) {
312
- let t = O(e);
313
- return t !== null && t.type === b.MAPPING && Array.isArray(t.attributes);
521
+ // ../logger/src/write.ts
522
+ function Ge(e, t) {
523
+ let n = t ? Ve(e) : He(e);
524
+ e.level === "error" ? console.error(n) : e.level === "warn" ? console.warn(n) : console.log(n);
314
525
  }
315
- function O(e) {
316
- return typeof e != "object" || !e ? null : e;
526
+ // ../logger/src/serialise.ts
527
+ function Ke(e, t = 0) {
528
+ let n = {
529
+ name: e.name,
530
+ message: e.message
531
+ };
532
+ return e.stack !== void 0 && (n.stack = e.stack), Je(e, n), e.cause !== void 0 && e.cause !== null && (n.cause = e.cause instanceof Error && t < 5 ? Ke(e.cause, t + 1) : String(e.cause)), n;
317
533
  }
318
- //#endregion
319
- //#region ../messaging/src/queue.ts
320
- var k = class {
321
- pending = [];
322
- isReady = !1;
323
- constructor(e) {
324
- this.processor = e;
534
+ var qe = /* @__PURE__ */ new Set([
535
+ "name",
536
+ "message",
537
+ "stack",
538
+ "cause"
539
+ ]);
540
+ function Je(e, t) {
541
+ for (let n of Object.getOwnPropertyNames(e)) {
542
+ if (qe.has(n)) continue;
543
+ let r = e[n];
544
+ r !== void 0 && (t[n] = r);
325
545
  }
326
- enqueue(e) {
327
- if (this.isReady) {
328
- this.processor(e);
329
- return;
330
- }
331
- this.pending.push(e);
546
+ }
547
+ function Ye(e) {
548
+ let t = {};
549
+ for (let [n, r] of Object.entries(e)) t[n] = r instanceof Error ? Ke(r) : r;
550
+ return t;
551
+ }
552
+ // ../utils/src/now.ts
553
+ function Xe() {
554
+ return /* @__PURE__ */ new Date();
555
+ }
556
+ // ../utils/src/device.ts
557
+ function Ze() {
558
+ return typeof navigator > "u" ? "" : navigator.userAgent;
559
+ }
560
+ function Qe() {
561
+ return typeof navigator > "u" ? !1 : navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
562
+ }
563
+ function $e() {
564
+ return /iPhone|iPad|iPod/i.test(Ze()) || Qe();
565
+ }
566
+ function et() {
567
+ return /Android/i.test(Ze());
568
+ }
569
+ function tt() {
570
+ return $e() || et();
571
+ }
572
+ // ../logger/src/logger.ts
573
+ var nt = class {
574
+ config;
575
+ constructor(e) {
576
+ this.config = e;
332
577
  }
333
- markReady() {
334
- if (this.isReady || (this.isReady = !0, this.pending.length === 0)) return;
335
- let e = [...this.pending];
336
- this.pending.length = 0;
337
- for (let t of e) this.processor(t);
578
+ log(e, t, n = {}) {
579
+ let r = this.config.traceProvider?.() ?? {};
580
+ Ge({
581
+ timestamp: Xe().toISOString(),
582
+ level: e,
583
+ msg: t,
584
+ service: this.config.service,
585
+ ...r,
586
+ ...Ye(n)
587
+ }, this.config.isDev);
338
588
  }
339
- get ready() {
340
- return this.isReady;
589
+ debug(e, t) {
590
+ this.config.isDev && this.log("debug", e, t);
341
591
  }
342
- get pendingCount() {
343
- return this.pending.length;
592
+ info(e, t) {
593
+ this.log("info", e, t);
344
594
  }
345
- hasPending(e) {
346
- return this.pending.some((t) => t.type === e);
595
+ warn(e, t) {
596
+ this.log("warn", e, t);
347
597
  }
348
- clear() {
349
- this.pending.length = 0;
350
- }
351
- }, A = class {
352
- handlers = /* @__PURE__ */ new Map();
353
- publisher;
354
- consumer;
355
- shouldBuffer;
356
- listener = null;
357
- sourceId;
358
- constructor(e, t) {
359
- this.getTarget = e, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.publisher = new k((e) => this.post(e)), this.consumer = new k((e) => this.routeToHandler(e)), this.shouldBuffer = t?.shouldBuffer ?? null, t?.bufferIncoming !== !0 && this.consumer.markReady();
360
- }
361
- on(e, t) {
362
- this.handlers.set(e, t);
363
- }
364
- off(e) {
365
- this.handlers.delete(e);
366
- }
367
- send(e) {
368
- this.publisher.enqueue(e);
369
- }
370
- markReady() {
371
- this.publisher.markReady();
372
- }
373
- markConsumerReady() {
374
- this.consumer.markReady();
375
- }
376
- consumerHasPending(e) {
377
- return this.consumer.hasPending(e);
378
- }
379
- start() {
380
- this.listener === null && (this.listener = (e) => {
381
- let { data: t } = e;
382
- if (!S(t) || t._source === this.sourceId) return;
383
- let n = t;
384
- if (this.shouldBuffer !== null && !this.shouldBuffer(n)) {
385
- this.routeToHandler(n);
386
- return;
387
- }
388
- this.consumer.enqueue(n);
389
- }, window.addEventListener("message", this.listener));
390
- }
391
- destroy() {
392
- this.listener !== null && (window.removeEventListener("message", this.listener), this.listener = null), this.publisher.clear(), this.consumer.clear(), this.handlers.clear();
393
- }
394
- get ready() {
395
- return this.publisher.ready;
396
- }
397
- get pendingCount() {
398
- return this.publisher.pendingCount;
399
- }
400
- post(e) {
401
- let t = this.getTarget();
402
- t !== null && t.postMessage({
403
- ...e,
404
- _source: this.sourceId
405
- }, "*");
406
- }
407
- routeToHandler(e) {
408
- let t = this.handlers.get(e.type);
409
- if (t === void 0) return;
410
- let n = t(e);
411
- n instanceof Promise && n.catch((t) => {
412
- let n = t instanceof Error ? t.message : String(t);
413
- console.error(`Virtualdisplay: error handling ${e.type}: ${n}`);
414
- });
598
+ error(e, t) {
599
+ this.log("error", e, t);
415
600
  }
416
601
  };
417
- new Set([b.CAMERA]);
418
- //#endregion
419
- //#region src/attributes/attributes.ts
420
- var j = class {
602
+ function rt(e, t) {
603
+ return new nt({
604
+ service: e,
605
+ isDev: t?.isDev === void 0 ? typeof process < "u" ? process.env.NODE_ENV !== "production" : !0 : t.isDev,
606
+ traceProvider: t?.traceProvider
607
+ });
608
+ }
609
+ // ../client-core/src/utils/logger.ts
610
+ function k(e) {
611
+ if (e !== void 0) return typeof e == "object" && e && !Array.isArray(e) && !(e instanceof Error) ? e : e instanceof Error ? { error: e.message } : { value: e };
612
+ }
613
+ function it(e) {
614
+ return {
615
+ debug: (t, n) => e.debug(t, k(n)),
616
+ info: (t, n) => e.info(t, k(n)),
617
+ warn: (t, n) => e.warn(t, k(n)),
618
+ error: (t, n) => e.error(t, k(n))
619
+ };
620
+ }
621
+ var at = !1, A = it(rt("client", { isDev: !1 }));
622
+ function ot() {
623
+ at = !0, A = it(rt("client", { isDev: !0 }));
624
+ }
625
+ function st() {
626
+ return at;
627
+ }
628
+ // ../client-core/src/attributes/attributes.ts
629
+ var ct = class {
630
+ send;
631
+ instance;
632
+ onMutationFailed;
421
633
  registry;
422
634
  pendingCallbacks = [];
423
- constructor(e, t) {
424
- this.send = e, this.onMutationFailed = t;
635
+ constructor(e, t, n) {
636
+ this.send = e, this.instance = t, this.onMutationFailed = n;
425
637
  }
426
638
  handleMappingReceived(e) {
427
- p.debug("Initializing from server mapping", { attributeCount: e.attributes.length }), this.registry = new y(e.attributes, (e, t) => this.sendMutation(e, t), this.onMutationFailed);
428
- let t = this.registry.defaults();
429
- t.length > 0 && this.sendMutation(t, !0), this.replayPendingCallbacks();
639
+ A.debug("Initializing from server mapping", { attributeCount: e.attributes.length }), this.registry = new Re(e.attributes, (e, t) => this.sendMutation(e, t), this.onMutationFailed), this.replayPendingCallbacks();
430
640
  }
431
641
  handleMutationResult(e) {
432
642
  this.registry?.handleResults(e.results);
@@ -437,6 +647,12 @@ var j = class {
437
647
  value: t
438
648
  }], !1);
439
649
  }
650
+ selectTransient(e, t) {
651
+ this.sendMutation([{
652
+ name: e,
653
+ value: t
654
+ }], !1, !0);
655
+ }
440
656
  getValues(e) {
441
657
  return this.registry?.get(e)?.getValues() ?? [];
442
658
  }
@@ -452,34 +668,49 @@ var j = class {
452
668
  return;
453
669
  }
454
670
  let n = this.registry.get(e);
455
- if (n === void 0) {
456
- p.warn(`onChange: attribute "${e}" not found — callback will not fire`);
457
- return;
458
- }
459
- n.onChange(t);
671
+ n === void 0 ? A.warn(`onChange: attribute "${e}" not found — callback will not fire`) : n.onChange(t);
460
672
  }
461
673
  getAttribute(e) {
462
674
  return this.registry?.get(e);
463
675
  }
464
- get hasMapping() {
465
- return this.registry !== void 0;
676
+ getSelection() {
677
+ return this.registry?.getSelection() ?? {};
466
678
  }
467
- sendMutation(e, t) {
468
- let n = {
469
- type: b.MUTATION,
679
+ sendMutation(e, n, r = !1) {
680
+ let i = {
681
+ type: t.MUTATION,
682
+ instance: this.instance,
470
683
  mutations: e,
471
- isInitial: t
684
+ isInitial: n,
685
+ ...r && { transient: !0 }
472
686
  };
473
- this.send(n);
687
+ this.send(i);
474
688
  }
475
689
  replayPendingCallbacks() {
476
690
  for (let { name: e, callback: t } of this.pendingCallbacks) {
477
691
  let n = this.registry?.get(e);
478
- n === void 0 && p.warn(`onChange: attribute "${e}" not found — buffered callback will not fire`), n?.onChange(t);
692
+ n === void 0 && A.warn(`onChange: attribute "${e}" not found — buffered callback will not fire`), n?.onChange(t);
479
693
  }
480
694
  this.pendingCallbacks.length = 0;
481
695
  }
482
- }, M = class {
696
+ }, lt = class {
697
+ snapshot;
698
+ callbacks = [];
699
+ handleSnapshot(e) {
700
+ this.snapshot = e.attributes;
701
+ for (let t of this.callbacks) t(e.attributes);
702
+ }
703
+ onChange(e) {
704
+ this.callbacks.push(e), this.snapshot !== void 0 && e(this.snapshot);
705
+ }
706
+ selectableFor(e) {
707
+ return this.snapshot?.find((t) => t.name === e)?.selectable ?? null;
708
+ }
709
+ get current() {
710
+ return this.snapshot ?? [];
711
+ }
712
+ }, ut = class {
713
+ send;
483
714
  commands = [];
484
715
  constructor(e) {
485
716
  this.send = e;
@@ -503,31 +734,32 @@ var j = class {
503
734
  }), this;
504
735
  }
505
736
  set() {
506
- if (this.commands.length === 0) throw t.invalidParameter("No camera commands to execute. Call rotate(), tilt(), or zoom() before set().");
737
+ if (this.commands.length === 0) throw w.invalidParameter("No camera commands to execute. Call rotate(), tilt(), or zoom() before set().");
507
738
  let e = {
508
- type: b.CAMERA,
739
+ type: t.CAMERA,
509
740
  commands: this.commands
510
741
  };
511
742
  this.send(e), this.commands = [];
512
743
  }
513
- validateFiniteNumber(e, n) {
514
- if (!Number.isFinite(e)) throw t.invalidParameter(`${n}() requires a finite number, got ${String(e)}`);
744
+ validateFiniteNumber(e, t) {
745
+ if (!Number.isFinite(e)) throw w.invalidParameter(`${t}() requires a finite number, got ${String(e)}`);
515
746
  }
516
747
  reset() {
517
748
  let e = {
518
- type: b.CAMERA,
749
+ type: t.CAMERA,
519
750
  commands: [{ action: "reset" }]
520
751
  };
521
752
  this.send(e);
522
753
  }
523
- }, N = class {
754
+ }, dt = class {
524
755
  state;
756
+ limitsState;
525
757
  changeCallbacks = [];
526
758
  constructor() {
527
759
  this.state = {
528
760
  rotation: 0,
529
761
  tilt: 0,
530
- zoom: 0
762
+ zoom: 100
531
763
  };
532
764
  }
533
765
  get rotation() {
@@ -542,6 +774,18 @@ var j = class {
542
774
  onChange(e) {
543
775
  this.changeCallbacks.push(e);
544
776
  }
777
+ get limits() {
778
+ return this.limitsState;
779
+ }
780
+ applyLimits(e) {
781
+ this.limitsState = e;
782
+ let t = {
783
+ rotation: this.rotation,
784
+ tilt: this.tilt,
785
+ zoom: this.zoom
786
+ };
787
+ for (let e of this.changeCallbacks) e(t);
788
+ }
545
789
  handlePositionUpdate(e) {
546
790
  this.update(e.transforms);
547
791
  }
@@ -554,10 +798,7 @@ var j = class {
554
798
  case "tilt":
555
799
  this.state.tilt = n.value, t = !0;
556
800
  break;
557
- case "zoom":
558
- this.state.zoom = n.value, t = !0;
559
- break;
560
- default: break;
801
+ case "zoom": this.state.zoom = n.value, t = !0;
561
802
  }
562
803
  if (t) {
563
804
  let e = {
@@ -568,7 +809,142 @@ var j = class {
568
809
  for (let t of this.changeCallbacks) t(e);
569
810
  }
570
811
  }
571
- }, P = class {
812
+ };
813
+ // ../client-core/src/configuration/pricing-rule.ts
814
+ function ft(e, t) {
815
+ let n = e.filter((e) => pt(e, t));
816
+ if (n.length === 0) return;
817
+ let r = Math.max(...n.map(mt)), i = n.filter((e) => mt(e) === r);
818
+ return i.length === 1 ? i[0] : void 0;
819
+ }
820
+ function pt(e, t) {
821
+ return Object.entries(e.attributes).every(([e, n]) => t[e] === n);
822
+ }
823
+ function mt(e) {
824
+ return Object.keys(e.attributes).length;
825
+ }
826
+ // ../client-core/src/configuration/configuration.ts
827
+ var ht = class {
828
+ dispatchChange;
829
+ dispatchSubmit;
830
+ pricing;
831
+ selection = {};
832
+ resolved;
833
+ changeCallbacks = [];
834
+ renderCallbacks = [];
835
+ submitCallback;
836
+ submitDefaultLabel;
837
+ constructor(e, t) {
838
+ this.dispatchChange = e, this.dispatchSubmit = t;
839
+ }
840
+ get current() {
841
+ return this.resolved;
842
+ }
843
+ get vatDisplay() {
844
+ return this.pricing?.vatDisplay;
845
+ }
846
+ get buttonLabel() {
847
+ let e = this.pricing?.buttonLabel;
848
+ if (e != null && e !== "") return e;
849
+ if (this.submitDefaultLabel !== void 0 && this.submitDefaultLabel !== "") return this.submitDefaultLabel;
850
+ }
851
+ get submittable() {
852
+ return this.submitCallback === void 0 ? !1 : this.pricing === void 0 || this.resolved?.availability === "available";
853
+ }
854
+ get submitEnabled() {
855
+ return this.submittable && this.buttonLabel !== void 0;
856
+ }
857
+ onChange(e) {
858
+ this.changeCallbacks.push(e);
859
+ }
860
+ onSubmit(e, t) {
861
+ this.submitCallback = e, this.submitDefaultLabel = t?.defaultLabel, this.notifyRender();
862
+ }
863
+ onRender(e) {
864
+ return this.renderCallbacks.push(e), () => {
865
+ let t = this.renderCallbacks.indexOf(e);
866
+ t !== -1 && this.renderCallbacks.splice(t, 1);
867
+ };
868
+ }
869
+ async submit() {
870
+ if (!this.submittable) return;
871
+ let e = this.resolved === void 0 ? {
872
+ attributes: { ...this.selection },
873
+ reference: ""
874
+ } : {
875
+ attributes: this.resolved.attributes,
876
+ reference: this.resolved.reference
877
+ };
878
+ this.dispatchSubmit(e), await this.submitCallback?.(e);
879
+ }
880
+ handlePricingReceived(e) {
881
+ this.pricing = e, this.recompute();
882
+ }
883
+ handleSelectionChanged(e) {
884
+ this.selection = e, this.recompute();
885
+ }
886
+ recompute() {
887
+ this.resolved = this.resolve();
888
+ for (let e of this.changeCallbacks) e(this.resolved);
889
+ this.dispatchChange(this.resolved), this.notifyRender();
890
+ }
891
+ notifyRender() {
892
+ for (let e of this.renderCallbacks) e();
893
+ }
894
+ resolve() {
895
+ if (this.pricing === void 0) return;
896
+ let e = ft(this.pricing.rules, this.selection);
897
+ if (e !== void 0 && e.availability !== a.HIDDEN) return {
898
+ attributes: { ...this.selection },
899
+ reference: e.reference,
900
+ price: e.priceCents,
901
+ currency: this.pricing.currency,
902
+ availability: e.availability
903
+ };
904
+ }
905
+ }, gt = class {
906
+ send;
907
+ dispatch;
908
+ instance;
909
+ focused = null;
910
+ attributes = [];
911
+ changeCallbacks = [];
912
+ constructor(e, t, n) {
913
+ this.send = e, this.dispatch = t, this.instance = n;
914
+ }
915
+ get current() {
916
+ return this.focused;
917
+ }
918
+ set(e) {
919
+ if (typeof e != "string" || e.length === 0) throw w.invalidParameter(`set() requires a non-empty component name, got ${String(e)}`);
920
+ this.send({
921
+ type: t.FOCUS,
922
+ instance: this.instance,
923
+ action: r.FOCUS,
924
+ component: e
925
+ });
926
+ }
927
+ clear() {
928
+ this.send({
929
+ type: t.FOCUS,
930
+ instance: this.instance,
931
+ action: r.UNFOCUS
932
+ });
933
+ }
934
+ onChange(e) {
935
+ this.changeCallbacks.push(e);
936
+ }
937
+ handleStateReceived(e) {
938
+ e.state === i.FOCUSED && e.component !== void 0 ? (this.focused = e.component, this.attributes = e.attributes ?? []) : (this.focused = null, this.attributes = []);
939
+ let t = this.focused === null ? null : {
940
+ component: this.focused,
941
+ attributes: this.attributes
942
+ };
943
+ for (let e of this.changeCallbacks) e(t);
944
+ this.dispatch(t);
945
+ }
946
+ }, _t = class {
947
+ filename;
572
948
  developedCallbacks = /* @__PURE__ */ new Set();
573
949
  errorCallbacks = /* @__PURE__ */ new Set();
574
950
  developed = !1;
@@ -596,159 +972,2360 @@ var j = class {
596
972
  t(e);
597
973
  }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
598
974
  }
599
- }, F = class {
975
+ }, vt = 3e4, yt = class {
976
+ send;
600
977
  validExtensions = [".png"];
601
978
  pendingPhotos = /* @__PURE__ */ new Map();
602
979
  constructor(e) {
603
980
  this.send = e;
604
981
  }
605
982
  take(e) {
606
- this.validateFilename(e), p.debug("Taking snapshot", { filename: e });
607
- let t = new P(e);
608
- return this.pendingPhotos.set(t.getFilename(), t), this.send({
609
- type: b.SNAPSHOT,
610
- filename: e
611
- }), t;
612
- }
613
- handleSnapshotResult(n) {
614
- let r = this.pendingPhotos.get(n.filename);
615
- if (r === void 0) {
616
- p.warn("No pending photo found for filename", {
617
- filename: n.filename,
983
+ return this.request(e, void 0);
984
+ }
985
+ takeOf(e, t) {
986
+ return this.request(e, t);
987
+ }
988
+ request(e, n) {
989
+ this.validateFilename(e), A.debug("Taking snapshot", { filename: e });
990
+ let r = new _t(e), i = setTimeout(() => {
991
+ this.pendingPhotos.delete(e), A.warn("Snapshot timed out", { filename: e }), r.fail(new w(`Snapshot timed out: the viewer did not respond within ${vt}ms`, C.SNAPSHOT_FAILED));
992
+ }, vt);
993
+ return this.pendingPhotos.set(e, {
994
+ photo: r,
995
+ timeout: i
996
+ }), this.send({
997
+ type: t.SNAPSHOT,
998
+ filename: e,
999
+ ...n !== void 0 && { subject: n }
1000
+ }), r;
1001
+ }
1002
+ handleSnapshotResult(e) {
1003
+ let t = this.pendingPhotos.get(e.filename);
1004
+ if (t === void 0) {
1005
+ A.warn("No pending photo found for filename", {
1006
+ filename: e.filename,
618
1007
  pendingFilenames: Array.from(this.pendingPhotos.keys())
619
1008
  });
620
1009
  return;
621
1010
  }
622
- if (this.pendingPhotos.delete(n.filename), n.error !== void 0) {
623
- r.fail(new t(n.error, e.SNAPSHOT_FAILED));
624
- return;
1011
+ clearTimeout(t.timeout), this.pendingPhotos.delete(e.filename);
1012
+ let { photo: n } = t;
1013
+ e.error === void 0 ? e.data !== void 0 && n.develop({
1014
+ filename: e.filename,
1015
+ data: e.data
1016
+ }) : n.fail(new w(e.error, C.SNAPSHOT_FAILED));
1017
+ }
1018
+ clear() {
1019
+ if (this.pendingPhotos.size === 0) return;
1020
+ let e = new w("Client destroyed before snapshot completed", C.SNAPSHOT_FAILED);
1021
+ for (let { photo: t, timeout: n } of this.pendingPhotos.values()) clearTimeout(n), t.fail(e);
1022
+ this.pendingPhotos.clear();
1023
+ }
1024
+ validateFilename(e) {
1025
+ if (e === "" || typeof e != "string") throw new w("Filename must be a non-empty string", C.INVALID_PARAMETER);
1026
+ if (!this.validExtensions.some((t) => e.toLowerCase().endsWith(t))) throw new w(`Invalid filename: "${e}". Filename must end with one of: ${this.validExtensions.join(", ")}`, C.INVALID_PARAMETER);
1027
+ if (e.includes("..") || e.includes("/") || e.includes("\\")) throw new w("Filename cannot contain path separators or \"..\"", C.INVALID_PARAMETER);
1028
+ }
1029
+ }, bt = class {
1030
+ container = null;
1031
+ attach(e) {
1032
+ this.container = e;
1033
+ }
1034
+ dispatch(e, t, n = !1) {
1035
+ let r = new CustomEvent(e, {
1036
+ detail: t,
1037
+ cancelable: n
1038
+ });
1039
+ return this.container?.dispatchEvent(r), r;
1040
+ }
1041
+ on(e, t) {
1042
+ let n = (e) => {
1043
+ let n = e;
1044
+ t({
1045
+ ...n.detail,
1046
+ preventDefault: () => {
1047
+ n.preventDefault();
1048
+ }
1049
+ });
1050
+ };
1051
+ return this.container?.addEventListener(e, n), () => {
1052
+ this.container?.removeEventListener(e, n);
1053
+ };
1054
+ }
1055
+ }, xt = ((e = {}, t = {}) => {
1056
+ let n = t.locale ?? v();
1057
+ return n === "nl" ? "Bezig…" : n === "de" ? "Läuft…" : "Working…";
1058
+ }), St = ((e = {}, t = {}) => {
1059
+ let n = t.locale ?? v();
1060
+ return n === "nl" ? "Mislukt" : n === "de" ? "Fehlgeschlagen" : "Failed";
1061
+ }), Ct = ((e = {}, t = {}) => {
1062
+ let n = t.locale ?? v();
1063
+ return n === "nl" ? "Gelukt" : n === "de" ? "Fertig" : "Done";
1064
+ }), wt = ((e = {}, t = {}) => {
1065
+ let n = t.locale ?? v();
1066
+ return n === "nl" ? "Annotaties" : n === "de" ? "Anmerkungen" : "Annotations";
1067
+ }), Tt = ((e = {}, t = {}) => {
1068
+ let n = t.locale ?? v();
1069
+ return n === "nl" ? "AR activeren" : n === "de" ? "AR aktivieren" : "Activate AR";
1070
+ }), Et = ((e = {}, t = {}) => {
1071
+ let n = t.locale ?? v();
1072
+ return n === "nl" ? "Camera resetten" : n === "de" ? "Kamera zurücksetzen" : "Reset camera";
1073
+ }), Dt = ((e = {}, t = {}) => {
1074
+ let n = t.locale ?? v();
1075
+ return n === "nl" ? "Naar links draaien" : n === "de" ? "Nach links drehen" : "Rotate left";
1076
+ }), Ot = ((e = {}, t = {}) => {
1077
+ let n = t.locale ?? v();
1078
+ return n === "nl" ? "Naar rechts draaien" : n === "de" ? "Nach rechts drehen" : "Rotate right";
1079
+ }), kt = ((e = {}, t = {}) => {
1080
+ let n = t.locale ?? v();
1081
+ return n === "nl" ? "Omlaag kantelen" : n === "de" ? "Nach unten kippen" : "Tilt down";
1082
+ }), At = ((e = {}, t = {}) => {
1083
+ let n = t.locale ?? v();
1084
+ return n === "nl" ? "Omhoog kantelen" : n === "de" ? "Nach oben kippen" : "Tilt up";
1085
+ }), jt = ((e = {}, t = {}) => {
1086
+ let n = t.locale ?? v();
1087
+ return n === "nl" ? "Inzoomen" : n === "de" ? "Hineinzoomen" : "Zoom in";
1088
+ }), Mt = ((e = {}, t = {}) => {
1089
+ let n = t.locale ?? v();
1090
+ return n === "nl" ? "Uitzoomen" : n === "de" ? "Herauszoomen" : "Zoom out";
1091
+ }), Nt = ((e = {}, t = {}) => {
1092
+ let n = t.locale ?? v();
1093
+ return n === "nl" ? "Sluiten" : n === "de" ? "Schließen" : "Close";
1094
+ }), Pt = ((e = {}, t = {}) => {
1095
+ let n = t.locale ?? v();
1096
+ return n === "nl" ? "Configureren" : n === "de" ? "Konfigurieren" : "Configure";
1097
+ }), Ft = ((e = {}, t = {}) => {
1098
+ let n = t.locale ?? v();
1099
+ return n === "nl" ? "Volledig scherm" : n === "de" ? "Vollbild" : "Fullscreen";
1100
+ }), It = ((e = {}, t = {}) => {
1101
+ let n = t.locale ?? v();
1102
+ return n === "nl" ? "Sluiten" : n === "de" ? "Schließen" : "Close";
1103
+ }), Lt = ((e = {}, t = {}) => {
1104
+ let n = t.locale ?? v();
1105
+ return n === "nl" ? "QR-code" : n === "de" ? "QR-Code" : "QR code";
1106
+ }), Rt = ((e = {}, t = {}) => {
1107
+ let n = t.locale ?? v();
1108
+ return n === "nl" ? "Gebruik de camera van je smartphone om deze QR-code te scannen" : n === "de" ? "Verwende die Kamera deines Smartphones, um diesen QR-Code zu scannen" : "Use your smartphone camera to scan this QR code";
1109
+ }), zt = ((e = {}, t = {}) => {
1110
+ let n = t.locale ?? v();
1111
+ return n === "nl" ? "Je telefoon opent het 3D-model in AR-modus." : n === "de" ? "Dein Telefon öffnet das 3D-Modell im AR-Modus." : "Your phone will open the 3D model in AR mode.";
1112
+ }), Bt = ((e = {}, t = {}) => {
1113
+ let n = t.locale ?? v();
1114
+ return n === "nl" ? "Scan om in AR te bekijken" : n === "de" ? "Scannen für AR-Ansicht" : "Scan to view in AR";
1115
+ }), Vt = ((e = {}, t = {}) => {
1116
+ let n = t.locale ?? v();
1117
+ return n === "nl" ? "We kunnen de QR-code nu niet tonen. Open deze link op je telefoon:" : n === "de" ? "Wir können den QR-Code gerade nicht anzeigen. Öffne diesen Link auf deinem Handy:" : "We can't show the QR code right now. Open this link on your phone:";
1118
+ }), Ht = ((e = {}, t = {}) => (t.locale ?? v(), "Screenshot")), Ut = ((e = {}, t = {}) => {
1119
+ let n = t.locale ?? v();
1120
+ return n === "nl" ? "Downloaden" : n === "de" ? "Herunterladen" : "Downloading";
1121
+ }), Wt = ((e = {}, t = {}) => {
1122
+ let n = t.locale ?? v();
1123
+ return n === "nl" ? "Laden mislukt. Ververs de pagina om het opnieuw te proberen." : n === "de" ? "Laden fehlgeschlagen. Lade die Seite neu, um es erneut zu versuchen." : "Load failed. Refresh the page to try again.";
1124
+ }), Gt = {
1125
+ xmlns: "http://www.w3.org/2000/svg",
1126
+ width: 24,
1127
+ height: 24,
1128
+ viewBox: "0 0 24 24",
1129
+ fill: "none",
1130
+ stroke: "currentColor",
1131
+ "stroke-width": 2,
1132
+ "stroke-linecap": "round",
1133
+ "stroke-linejoin": "round"
1134
+ }, Kt = ([e, t, n]) => {
1135
+ let r = document.createElementNS("http://www.w3.org/2000/svg", e);
1136
+ return Object.keys(t).forEach((e) => {
1137
+ r.setAttribute(e, String(t[e]));
1138
+ }), n?.length && n.forEach((e) => {
1139
+ let t = Kt(e);
1140
+ r.appendChild(t);
1141
+ }), r;
1142
+ }, qt = (e, t = {}) => Kt([
1143
+ "svg",
1144
+ {
1145
+ ...Gt,
1146
+ ...t
1147
+ },
1148
+ e
1149
+ ]), Jt = [
1150
+ ["path", { 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" }],
1151
+ ["path", { d: "m3.3 7 8.7 5 8.7-5" }],
1152
+ ["path", { d: "M12 22V12" }]
1153
+ ], Yt = [["path", { 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" }], ["circle", {
1154
+ cx: "12",
1155
+ cy: "13",
1156
+ r: "3"
1157
+ }]], Xt = [["path", { d: "m6 9 6 6 6-6" }]], Zt = [["path", { d: "m15 18-6-6 6-6" }]], Qt = [["path", { d: "m9 18 6-6-6-6" }]], $t = [["path", { d: "m18 15-6-6-6 6" }]], en = [
1158
+ ["path", { d: "M12.75 7.09a3 3 0 0 1 2.16 2.16" }],
1159
+ ["path", { d: "M17.072 17.072c-1.634 2.17-3.527 3.912-4.471 4.727a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 1.432-4.568" }],
1160
+ ["path", { d: "m2 2 20 20" }],
1161
+ ["path", { d: "M8.475 2.818A8 8 0 0 1 20 10c0 1.183-.31 2.377-.81 3.533" }],
1162
+ ["path", { d: "M9.13 9.13a3 3 0 0 0 3.74 3.74" }]
1163
+ ], tn = [["path", { d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }], ["circle", {
1164
+ cx: "12",
1165
+ cy: "10",
1166
+ r: "3"
1167
+ }]], nn = [
1168
+ ["path", { d: "M15 3h6v6" }],
1169
+ ["path", { d: "m21 3-7 7" }],
1170
+ ["path", { d: "m3 21 7-7" }],
1171
+ ["path", { d: "M9 21H3v-6" }]
1172
+ ], rn = [
1173
+ ["path", { d: "m14 10 7-7" }],
1174
+ ["path", { d: "M20 10h-6V4" }],
1175
+ ["path", { d: "m3 21 7-7" }],
1176
+ ["path", { d: "M4 14h6v6" }]
1177
+ ], an = [["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }], ["path", { d: "M3 3v5h5" }]], on = [
1178
+ ["path", { d: "M10 5H3" }],
1179
+ ["path", { d: "M12 19H3" }],
1180
+ ["path", { d: "M14 3v4" }],
1181
+ ["path", { d: "M16 17v4" }],
1182
+ ["path", { d: "M21 12h-9" }],
1183
+ ["path", { d: "M21 19h-5" }],
1184
+ ["path", { d: "M21 5h-7" }],
1185
+ ["path", { d: "M8 10v4" }],
1186
+ ["path", { d: "M8 12H3" }]
1187
+ ], sn = [["path", { d: "M18 6 6 18" }], ["path", { d: "m6 6 12 12" }]], cn = [
1188
+ ["circle", {
1189
+ cx: "11",
1190
+ cy: "11",
1191
+ r: "8"
1192
+ }],
1193
+ ["line", {
1194
+ x1: "21",
1195
+ x2: "16.65",
1196
+ y1: "21",
1197
+ y2: "16.65"
1198
+ }],
1199
+ ["line", {
1200
+ x1: "11",
1201
+ x2: "11",
1202
+ y1: "8",
1203
+ y2: "14"
1204
+ }],
1205
+ ["line", {
1206
+ x1: "8",
1207
+ x2: "14",
1208
+ y1: "11",
1209
+ y2: "11"
1210
+ }]
1211
+ ], ln = [
1212
+ ["circle", {
1213
+ cx: "11",
1214
+ cy: "11",
1215
+ r: "8"
1216
+ }],
1217
+ ["line", {
1218
+ x1: "21",
1219
+ x2: "16.65",
1220
+ y1: "21",
1221
+ y2: "16.65"
1222
+ }],
1223
+ ["line", {
1224
+ x1: "8",
1225
+ x2: "14",
1226
+ y1: "11",
1227
+ y2: "11"
1228
+ }]
1229
+ ];
1230
+ // ../ui/src/icons.ts
1231
+ function j(e, { size: t = 20, strokeWidth: n = 1.75 } = {}) {
1232
+ return qt(e, {
1233
+ width: t,
1234
+ height: t,
1235
+ "stroke-width": n
1236
+ });
1237
+ }
1238
+ var M = {
1239
+ ar: (e) => j(Jt, e),
1240
+ annotations: (e) => j(tn, e),
1241
+ annotationsOff: (e) => j(en, e),
1242
+ fullscreen: (e) => j(nn, e),
1243
+ exitFullscreen: (e) => j(rn, e),
1244
+ screenshot: (e) => j(Yt, e),
1245
+ tiltUp: (e) => j($t, e),
1246
+ tiltDown: (e) => j(Xt, e),
1247
+ rotateLeft: (e) => j(Zt, e),
1248
+ rotateRight: (e) => j(Qt, e),
1249
+ zoomIn: (e) => j(cn, e),
1250
+ zoomOut: (e) => j(ln, e),
1251
+ reset: (e) => j(an, e),
1252
+ configurator: (e) => j(on, e),
1253
+ close: (e) => j(sn, e)
1254
+ }, N = class {
1255
+ element;
1256
+ constructor({ variant: e = "default", size: t = "default", label: n, icon: r, onClick: i, disabled: a = !1 } = {}) {
1257
+ this.element = document.createElement("button"), this.element.className = `ui-btn ui-btn--${e} ui-btn--size-${t}`, n !== void 0 && (this.element.setAttribute("aria-label", n), this.element.title = n), r === void 0 ? n !== void 0 && (this.element.textContent = n) : this.element.appendChild(r), i && this.element.addEventListener("click", i), this.element.disabled = a;
1258
+ }
1259
+ setIcon(e) {
1260
+ let t = this.element.querySelector("svg");
1261
+ t === null ? this.element.appendChild(e) : t.replaceWith(e);
1262
+ }
1263
+ }, un = class {
1264
+ element;
1265
+ selectCallback = null;
1266
+ constructor(e, t, n) {
1267
+ this.element = document.createElement("div"), this.element.className = "ui-toggle-group";
1268
+ for (let r = 0; r < e.length; r++) {
1269
+ let i = e[r], a = document.createElement("button");
1270
+ a.className = "ui-toggle-group-item", a.dataset.value = i, a.textContent = n?.[r] ?? i, a.dataset.state = i === t ? "on" : "off", a.setAttribute("aria-pressed", i === t ? "true" : "false"), a.addEventListener("click", () => {
1271
+ this.selectCallback?.(i);
1272
+ }), this.element.appendChild(a);
1273
+ }
1274
+ }
1275
+ setSelected(e) {
1276
+ for (let t of this.element.querySelectorAll(".ui-toggle-group-item")) {
1277
+ let n = t.dataset.value === e;
1278
+ t.dataset.state = n ? "on" : "off", t.setAttribute("aria-pressed", n ? "true" : "false");
625
1279
  }
626
- n.data !== void 0 && r.develop({
627
- filename: n.filename,
628
- data: n.data
1280
+ }
1281
+ onSelect(e) {
1282
+ this.selectCallback = e;
1283
+ }
1284
+ setEnabled(e) {
1285
+ for (let t of this.element.querySelectorAll(".ui-toggle-group-item")) {
1286
+ let n = e === null || e.includes(t.dataset.value ?? "");
1287
+ t.disabled = !n, t.setAttribute("aria-disabled", n ? "false" : "true");
1288
+ }
1289
+ }
1290
+ }, dn = class {
1291
+ element;
1292
+ select;
1293
+ constructor(e, t, n, r, i) {
1294
+ this.select = document.createElement("select"), this.select.className = "ui-native-select", this.select.dataset.name = e, this.select.setAttribute("aria-label", n);
1295
+ let a = document.createElement("option");
1296
+ a.value = "", a.disabled = !0, a.textContent = n, this.select.appendChild(a);
1297
+ let o = !1;
1298
+ for (let e = 0; e < t.length; e++) {
1299
+ let n = t[e], a = document.createElement("option");
1300
+ a.value = n, a.textContent = i?.[e] ?? n, n === r && (a.selected = !0, o = !0), this.select.appendChild(a);
1301
+ }
1302
+ o || (a.selected = !0);
1303
+ let s = qt(Xt, {
1304
+ width: 16,
1305
+ height: 16,
1306
+ "stroke-width": 2
1307
+ });
1308
+ s.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(s);
1309
+ }
1310
+ setSelected(e) {
1311
+ this.select.value = e;
1312
+ }
1313
+ onSelect(e) {
1314
+ this.select.addEventListener("change", () => {
1315
+ e(this.select.value);
629
1316
  });
630
1317
  }
631
- validateFilename(n) {
632
- if (n === "" || typeof n != "string") throw new t("Filename must be a non-empty string", e.INVALID_PARAMETER);
633
- if (!this.validExtensions.some((e) => n.toLowerCase().endsWith(e))) throw new t(`Invalid filename: "${n}". Filename must end with one of: ${this.validExtensions.join(", ")}`, e.INVALID_PARAMETER);
634
- if (n.includes("..") || n.includes("/") || n.includes("\\")) throw new t("Filename cannot contain path separators or \"..\"", e.INVALID_PARAMETER);
1318
+ setEnabled(e) {
1319
+ for (let t of this.select.options) t.value !== "" && (t.disabled = e !== null && !e.includes(t.value));
635
1320
  }
636
- }, I = class {
637
- send;
1321
+ }, fn = class {
1322
+ element;
1323
+ input;
1324
+ valueText;
1325
+ values;
1326
+ options;
1327
+ selectCallback = null;
1328
+ settleCallback = null;
1329
+ constructor(e, t) {
1330
+ if (e.length === 0) throw Error("Virtualdisplay: Slider needs at least one value");
1331
+ this.values = e, this.options = t, this.input = document.createElement("input"), this.input.type = "range", this.input.className = "ui-slider-input", this.input.min = "0", this.input.max = String(e.length - 1), this.input.step = "1", this.input.setAttribute("aria-label", t.label), this.valueText = document.createElement("span"), this.valueText.className = "ui-slider-value", this.valueText.setAttribute("aria-hidden", "true");
1332
+ let n = document.createElement("div");
1333
+ n.className = "ui-slider-track", n.appendChild(this.input), n.appendChild(this.buildEnds()), this.element = document.createElement("div"), this.element.className = "ui-slider", this.element.appendChild(n), this.element.appendChild(this.valueText);
1334
+ let r = t.initialSelected === void 0 ? 0 : e.indexOf(t.initialSelected);
1335
+ this.applyIndex(r === -1 ? 0 : r), this.listen();
1336
+ }
1337
+ setSelected(e) {
1338
+ let t = this.values.indexOf(e);
1339
+ t !== -1 && this.applyIndex(t);
1340
+ }
1341
+ onSelect(e) {
1342
+ this.selectCallback = e;
1343
+ }
1344
+ onSettle(e) {
1345
+ this.settleCallback = e;
1346
+ }
1347
+ listen() {
1348
+ this.input.addEventListener("input", () => {
1349
+ let e = this.values[this.currentIndex];
1350
+ e !== void 0 && (this.applyIndex(this.currentIndex), this.selectCallback?.(e));
1351
+ }), this.input.addEventListener("change", () => {
1352
+ let e = this.values[this.currentIndex];
1353
+ e !== void 0 && this.settleCallback?.(e);
1354
+ });
1355
+ }
1356
+ get currentIndex() {
1357
+ return Number(this.input.value);
1358
+ }
1359
+ applyIndex(e) {
1360
+ this.input.value = String(e);
1361
+ let t = this.displayAt(e);
1362
+ this.valueText.textContent = t, this.input.setAttribute("aria-valuetext", t);
1363
+ let n = this.values.length === 1 ? 100 : e / (this.values.length - 1) * 100;
1364
+ this.element.style.setProperty("--ui-slider-pct", `${n}%`);
1365
+ }
1366
+ buildEnds() {
1367
+ let e = document.createElement("div");
1368
+ e.className = "ui-slider-ends", e.setAttribute("aria-hidden", "true");
1369
+ for (let t of [0, this.values.length - 1]) {
1370
+ let n = document.createElement("span");
1371
+ n.textContent = this.labelAt(t), e.appendChild(n);
1372
+ }
1373
+ return e;
1374
+ }
1375
+ labelAt(e) {
1376
+ return this.options.labels?.[e] ?? this.values[e] ?? "";
1377
+ }
1378
+ displayAt(e) {
1379
+ let t = this.labelAt(e);
1380
+ return this.options.unit === void 0 ? t : `${t} ${this.options.unit}`;
1381
+ }
1382
+ }, P = {
1383
+ neutral: {
1384
+ 50: "oklch(98.5% 0 0)",
1385
+ 100: "oklch(96.5% 0 0)",
1386
+ 200: "oklch(92% 0 0)",
1387
+ 300: "oklch(82% 0 0)",
1388
+ 400: "oklch(70% 0 0)",
1389
+ 500: "oklch(55.5% 0 0)",
1390
+ 600: "oklch(44.5% 0 0)",
1391
+ 700: "oklch(37% 0 0)",
1392
+ 800: "oklch(27% 0 0)",
1393
+ 900: "oklch(21% 0 0)",
1394
+ 950: "oklch(14.5% 0 0)"
1395
+ },
1396
+ white: "oklch(100% 0 0)",
1397
+ semantic: { error: "oklch(47% 0.17 25)" }
1398
+ }, F = {
1399
+ px: "1px",
1400
+ 0: "0",
1401
+ .5: "0.125rem",
1402
+ 1: "0.25rem",
1403
+ 1.5: "0.375rem",
1404
+ 2: "0.5rem",
1405
+ 2.5: "0.625rem",
1406
+ 3: "0.75rem",
1407
+ 3.5: "0.875rem",
1408
+ 4: "1rem",
1409
+ 5: "1.25rem",
1410
+ 6: "1.5rem",
1411
+ 7: "1.75rem",
1412
+ 8: "2rem",
1413
+ 9: "2.25rem",
1414
+ 10: "2.5rem",
1415
+ 12: "3rem",
1416
+ 14: "3.5rem",
1417
+ 16: "4rem",
1418
+ 20: "5rem",
1419
+ 24: "6rem"
1420
+ }, I = {
1421
+ fontFamily: {
1422
+ sans: [
1423
+ "Mona Sans",
1424
+ "ui-sans-serif",
1425
+ "system-ui",
1426
+ "sans-serif",
1427
+ "Apple Color Emoji",
1428
+ "Segoe UI Emoji",
1429
+ "Segoe UI Symbol",
1430
+ "Noto Color Emoji"
1431
+ ],
1432
+ mono: [
1433
+ "JetBrains Mono",
1434
+ "ui-monospace",
1435
+ "monospace"
1436
+ ]
1437
+ },
1438
+ fontSize: {
1439
+ xs: "0.75rem",
1440
+ sm: "0.875rem",
1441
+ base: "1rem",
1442
+ lg: "1.125rem",
1443
+ xl: "1.25rem",
1444
+ "2xl": "1.5rem",
1445
+ "3xl": "1.875rem",
1446
+ "4xl": "2.25rem"
1447
+ },
1448
+ fontWeight: {
1449
+ normal: "400",
1450
+ medium: "500",
1451
+ semibold: "600",
1452
+ bold: "700"
1453
+ },
1454
+ lineHeight: {
1455
+ tight: "1.25",
1456
+ normal: "1.5",
1457
+ relaxed: "1.75"
1458
+ }
1459
+ }, pn = class {
1460
+ element;
1461
+ constructor() {
1462
+ this.element = document.createElement("div"), Object.assign(this.element.style, {
1463
+ background: P.white,
1464
+ borderRadius: "var(--radius)",
1465
+ padding: F[6],
1466
+ boxShadow: `0 ${F[2]} ${F[8]} oklch(0% 0 0 / 20%)`,
1467
+ maxWidth: "20rem",
1468
+ width: "100%"
1469
+ });
1470
+ }
1471
+ }, mn = class {
1472
+ options;
1473
+ card;
1474
+ backdrop;
1475
+ keyHandler = null;
1476
+ constructor(e = {}) {
1477
+ this.options = e, this.card = new pn(), this.backdrop = this.createBackdrop(), this.backdrop.appendChild(this.card.element);
1478
+ }
1479
+ get content() {
1480
+ return this.card.element;
1481
+ }
1482
+ open() {
1483
+ document.body.appendChild(this.backdrop), this.keyHandler = (e) => {
1484
+ e.key === "Escape" && this.close();
1485
+ }, document.addEventListener("keydown", this.keyHandler);
1486
+ }
1487
+ close() {
1488
+ this.backdrop.remove(), this.keyHandler !== null && (document.removeEventListener("keydown", this.keyHandler), this.keyHandler = null), this.options.onClose?.();
1489
+ }
1490
+ createBackdrop() {
1491
+ let e = document.createElement("div");
1492
+ return Object.assign(e.style, {
1493
+ position: "fixed",
1494
+ inset: F[0],
1495
+ background: "oklch(0% 0 0 / 50%)",
1496
+ zIndex: "9999",
1497
+ display: "flex",
1498
+ alignItems: "center",
1499
+ justifyContent: "center"
1500
+ }), e.addEventListener("click", (t) => {
1501
+ t.target === e && this.close();
1502
+ }), e;
1503
+ }
1504
+ }, L = I.fontFamily.sans.join(", "), hn = `
1505
+ :root {
1506
+ --primary: oklch(5% 0 0);
1507
+ --primary-foreground: oklch(100% 0 0);
1508
+ --secondary: oklch(92% 0 0);
1509
+ --secondary-foreground: oklch(5% 0 0);
1510
+ --background: oklch(100% 0 0);
1511
+ --foreground: oklch(5% 0 0);
1512
+ --border: oklch(92% 0 0);
1513
+ --ring: oklch(44% 0 0);
1514
+ --radius: 0.625rem;
1515
+ --accent: oklch(92% 0 0);
1516
+ --accent-foreground: oklch(5% 0 0);
1517
+ --muted-foreground: oklch(44% 0 0);
1518
+ }
1519
+
1520
+ /* ── Button ──────────────────────────────────────────────── */
1521
+ .ui-btn {
1522
+ display: inline-flex;
1523
+ align-items: center;
1524
+ justify-content: center;
1525
+ gap: 0.5rem;
1526
+ border: 1px solid transparent;
1527
+ border-radius: var(--radius);
1528
+ font-family: ${L};
1529
+ font-size: 0.875rem;
1530
+ font-weight: 600;
1531
+ white-space: nowrap;
1532
+ cursor: pointer;
1533
+ transition: all 200ms;
1534
+ flex-shrink: 0;
1535
+ }
1536
+ .ui-btn:active { transform: scale(0.98); }
1537
+ .ui-btn:disabled { opacity: 0.5; pointer-events: none; }
1538
+ .ui-btn svg { inline-size: 1rem; block-size: 1rem; pointer-events: none; flex-shrink: 0; }
1539
+
1540
+ .ui-btn--default {
1541
+ background: var(--primary);
1542
+ color: var(--primary-foreground);
1543
+ box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
1544
+ }
1545
+ .ui-btn--default:hover { background: oklch(10% 0 0); }
1546
+
1547
+ .ui-btn--outline {
1548
+ background: var(--background);
1549
+ color: var(--foreground);
1550
+ border-color: var(--border);
1551
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1552
+ }
1553
+ .ui-btn--outline:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1554
+
1555
+ .ui-btn--secondary {
1556
+ background: var(--secondary);
1557
+ color: var(--secondary-foreground);
1558
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1559
+ }
1560
+ .ui-btn--secondary:hover { background: oklch(88% 0 0); }
1561
+
1562
+ .ui-btn--ghost { background: transparent; color: var(--foreground); }
1563
+ .ui-btn--ghost:hover { background: var(--accent); }
1564
+
1565
+ .ui-btn--size-default { block-size: 2.25rem; padding: 0 1rem; }
1566
+ .ui-btn--size-sm { block-size: 2rem; padding: 0 0.75rem; border-radius: calc(var(--radius) - 0.125rem); }
1567
+ .ui-btn--size-lg { block-size: 2.5rem; padding: 0 1.5rem; }
1568
+ .ui-btn--size-icon { inline-size: 2.25rem; block-size: 2.25rem; padding: 0; }
1569
+ .ui-btn--size-icon-sm { inline-size: 2rem; block-size: 2rem; padding: 0; }
1570
+ .ui-btn--size-icon-lg { inline-size: 2.5rem; block-size: 2.5rem; padding: 0; }
1571
+
1572
+ /* ── Toggle group ────────────────────────────────────────── */
1573
+ .ui-toggle-group { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
1574
+
1575
+ .ui-toggle-group-item {
1576
+ display: inline-flex;
1577
+ align-items: center;
1578
+ justify-content: center;
1579
+ block-size: 2rem;
1580
+ padding: 0 0.75rem;
1581
+ border: 1px solid var(--border);
1582
+ border-radius: var(--radius);
1583
+ background: var(--background);
1584
+ color: var(--foreground);
1585
+ font-family: ${L};
1586
+ font-size: 0.875rem;
1587
+ font-weight: 600;
1588
+ cursor: pointer;
1589
+ white-space: nowrap;
1590
+ transition: all 200ms;
1591
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1592
+ }
1593
+ .ui-toggle-group-item:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1594
+ .ui-toggle-group-item:disabled { opacity: 0.4; cursor: not-allowed; }
1595
+ .ui-toggle-group-item:disabled:hover { background: var(--background); border-color: var(--border); }
1596
+ .ui-toggle-group-item:active { transform: scale(0.98); }
1597
+ .ui-toggle-group-item:focus-visible {
1598
+ outline: none;
1599
+ border-color: var(--ring);
1600
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1601
+ }
1602
+ .ui-toggle-group-item[data-state="on"] {
1603
+ background: var(--primary);
1604
+ color: var(--primary-foreground);
1605
+ border-color: var(--primary);
1606
+ }
1607
+ .ui-toggle-group-item[data-state="on"]:hover { background: oklch(10% 0 0); }
1608
+
1609
+ /* ── Native select ───────────────────────────────────────── */
1610
+ .ui-native-select-wrap {
1611
+ position: relative;
1612
+ display: inline-flex;
1613
+ min-inline-size: 8rem;
1614
+ }
1615
+
1616
+ .ui-native-select {
1617
+ display: block;
1618
+ inline-size: 100%;
1619
+ block-size: 2.25rem;
1620
+ padding: 0 2.25rem 0 0.75rem;
1621
+ border: 1px solid var(--border);
1622
+ border-radius: var(--radius);
1623
+ background: var(--background);
1624
+ appearance: none;
1625
+ color: var(--foreground);
1626
+ font-family: ${L};
1627
+ font-size: 0.875rem;
1628
+ font-weight: 400;
1629
+ cursor: pointer;
1630
+ transition: color 200ms, box-shadow 200ms, border-color 200ms;
1631
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1632
+ }
1633
+ .ui-native-select:hover { border-color: oklch(85% 0 0); }
1634
+ .ui-native-select:focus-visible {
1635
+ outline: none;
1636
+ border-color: var(--ring);
1637
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1638
+ }
1639
+
1640
+ .ui-native-select-icon {
1641
+ position: absolute;
1642
+ inset-block-start: 50%;
1643
+ inset-inline-end: 0.625rem;
1644
+ transform: translateY(-50%);
1645
+ color: var(--muted-foreground);
1646
+ pointer-events: none;
1647
+ }
1648
+
1649
+ /* ── Slider ──────────────────────────────────────────────── */
1650
+ .ui-slider {
1651
+ display: flex;
1652
+ align-items: center;
1653
+ gap: 0.75rem;
1654
+ inline-size: 100%;
1655
+ }
1656
+
1657
+ .ui-slider-track {
1658
+ flex: 1;
1659
+ min-inline-size: 0;
1660
+ }
1661
+
1662
+ /* Tabular figures keep the readout and layout steady mid-drag. */
1663
+ .ui-slider-value {
1664
+ font-family: ${L};
1665
+ font-size: 0.875rem;
1666
+ font-weight: 600;
1667
+ color: var(--foreground);
1668
+ font-variant-numeric: tabular-nums;
1669
+ white-space: nowrap;
1670
+ }
1671
+
1672
+ .ui-slider-ends {
1673
+ display: flex;
1674
+ justify-content: space-between;
1675
+ font-family: ${L};
1676
+ font-size: 0.75rem;
1677
+ color: var(--muted-foreground);
1678
+ font-variant-numeric: tabular-nums;
1679
+ }
1680
+
1681
+ /* 2.75rem tall: the whole input is the touch target (44px). */
1682
+ .ui-slider-input {
1683
+ appearance: none;
1684
+ display: block;
1685
+ inline-size: 100%;
1686
+ block-size: 2.75rem;
1687
+ margin: 0;
1688
+ padding: 0;
1689
+ background: transparent;
1690
+ cursor: pointer;
1691
+ }
1692
+ .ui-slider-input:focus-visible { outline: none; }
1693
+
1694
+ /* --ui-slider-pct is set by the component; it fills the track up to the thumb. */
1695
+ .ui-slider-input::-webkit-slider-runnable-track {
1696
+ block-size: 0.375rem;
1697
+ border-radius: 9999px;
1698
+ background: linear-gradient(to right, var(--primary) var(--ui-slider-pct, 0%), var(--secondary) var(--ui-slider-pct, 0%));
1699
+ }
1700
+ .ui-slider-input::-webkit-slider-thumb {
1701
+ appearance: none;
1702
+ inline-size: 1.25rem;
1703
+ block-size: 1.25rem;
1704
+ margin-block-start: -0.4375rem; /* centre the 1.25rem thumb on the 0.375rem track */
1705
+ border-radius: 50%;
1706
+ border: 2px solid var(--primary);
1707
+ background: var(--background);
1708
+ box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
1709
+ transition: box-shadow 200ms, border-color 200ms;
1710
+ }
1711
+ .ui-slider-input:focus-visible::-webkit-slider-thumb {
1712
+ border-color: var(--ring);
1713
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1714
+ }
1715
+
1716
+ .ui-slider-input::-moz-range-track {
1717
+ block-size: 0.375rem;
1718
+ border-radius: 9999px;
1719
+ background: var(--secondary);
1720
+ }
1721
+ .ui-slider-input::-moz-range-progress {
1722
+ block-size: 0.375rem;
1723
+ border-radius: 9999px;
1724
+ background: var(--primary);
1725
+ }
1726
+ .ui-slider-input::-moz-range-thumb {
1727
+ inline-size: 1.25rem;
1728
+ block-size: 1.25rem;
1729
+ border-radius: 50%;
1730
+ border: 2px solid var(--primary);
1731
+ background: var(--background);
1732
+ box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
1733
+ transition: box-shadow 200ms, border-color 200ms;
1734
+ }
1735
+ .ui-slider-input:focus-visible::-moz-range-thumb {
1736
+ border-color: var(--ring);
1737
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1738
+ }
1739
+
1740
+ /* ── Annotation layer ───────────────────────────────────── */
1741
+ .vd-annotations-layer {
1742
+ position: absolute;
1743
+ inset-block-start: 0;
1744
+ inset-inline-start: 0;
1745
+ inline-size: 100%;
1746
+ block-size: 100%;
1747
+ pointer-events: none;
1748
+ overflow: visible !important; /* override CSS2DRenderer inline style */
1749
+ }
1750
+
1751
+ .vd-annotation-anchor {
1752
+ position: relative;
1753
+ }
1754
+
1755
+ /* A pin stands on its point: the number on top, a stick down, and a ring on
1756
+ the spot. Open, a card with the number and the title takes the number's
1757
+ place at the top of the stick. */
1758
+ .vd-annotation-anchor--pin {
1759
+ display: flex;
1760
+ flex-direction: column;
1761
+ align-items: center;
1762
+ }
1763
+ .vd-annotation-head {
1764
+ position: relative;
1765
+ display: flex;
1766
+ justify-content: center;
1767
+ }
1768
+ /* A light edge around the stick and the ring, as the dot has one, so a pin
1769
+ stays visible on a dark surface. */
1770
+ .vd-annotation-stick {
1771
+ inline-size: 2px;
1772
+ block-size: 2rem;
1773
+ background: var(--primary);
1774
+ box-shadow: 0 0 0 1px var(--background);
1775
+ }
1776
+ /* Half its height below the anchor's edge, so the ring's centre is the point. */
1777
+ .vd-annotation-point {
1778
+ box-sizing: border-box;
1779
+ inline-size: 0.625rem;
1780
+ block-size: 0.625rem;
1781
+ margin-block-end: -0.3125rem;
1782
+ border-radius: 50%;
1783
+ border: 2px solid var(--primary);
1784
+ background: var(--background);
1785
+ box-shadow: 0 0 0 1px var(--background), 0 1px 2px oklch(5% 0 0 / 20%);
1786
+ }
1787
+ .vd-annotation-anchor--pin .vd-annotation-marker--open {
1788
+ visibility: hidden;
1789
+ }
1790
+ .vd-annotation-anchor--pin .vd-annotation-popup {
1791
+ inset-block-start: auto;
1792
+ inset-block-end: 0;
1793
+ }
1794
+ /* Too close to the top edge, the card hangs down from the head over the
1795
+ stick, where above it would be clipped by the frame. */
1796
+ .vd-annotation-anchor--pin .vd-annotation-popup--below {
1797
+ inset-block-start: 0;
1798
+ inset-block-end: auto;
1799
+ }
1800
+
1801
+ .vd-annotation-marker {
1802
+ display: flex;
1803
+ align-items: center;
1804
+ justify-content: center;
1805
+ inline-size: 1.75rem;
1806
+ block-size: 1.75rem;
1807
+ border-radius: 50%;
1808
+ border: 2px solid oklch(100% 0 0);
1809
+ background: var(--primary);
1810
+ color: var(--primary-foreground);
1811
+ font-family: ${L};
1812
+ font-size: 0.8125rem;
1813
+ font-weight: 600;
1814
+ cursor: pointer;
1815
+ pointer-events: auto;
1816
+ box-shadow: 0 2px 8px oklch(5% 0 0 / 20%);
1817
+ transition: transform 100ms ease;
1818
+ position: relative;
1819
+ }
1820
+ .vd-annotation-marker:hover { transform: scale(1.1); }
1821
+ .vd-annotation-marker:focus-visible {
1822
+ outline: 2px solid var(--ring);
1823
+ outline-offset: 2px;
1824
+ }
1825
+
1826
+ .vd-annotation-popup {
1827
+ position: absolute;
1828
+ inset-block-end: calc(100% + 0.5rem);
1829
+ inset-inline-start: 50%;
1830
+ transform: translateX(-50%);
1831
+ background: var(--background);
1832
+ color: var(--foreground);
1833
+ border: 1px solid var(--border);
1834
+ border-radius: var(--radius);
1835
+ padding: 0.75rem 1rem;
1836
+ /* The popup is absolutely positioned inside the ~28px marker anchor, so
1837
+ without an explicit width it shrink-to-fits against that anchor and
1838
+ collapses to one word per line. max-content sizes it by its own text,
1839
+ capped below. */
1840
+ inline-size: max-content;
1841
+ max-inline-size: 15rem;
1842
+ box-shadow: 0 4px 6px oklch(5% 0 0 / 8%), 0 2px 4px oklch(5% 0 0 / 6%);
1843
+ pointer-events: auto;
1844
+ white-space: normal;
1845
+ }
1846
+ .vd-annotation-popup:focus { outline: none; }
1847
+
1848
+ /* Flipped below the marker when the popup would clip the container's top
1849
+ edge; the layer positions it at open time and on the render cadence. */
1850
+ .vd-annotation-popup--below {
1851
+ inset-block-end: auto;
1852
+ inset-block-start: calc(100% + 0.5rem);
1853
+ }
1854
+
1855
+ .vd-annotation-popup-close {
1856
+ position: absolute;
1857
+ inset-block-start: 0.375rem;
1858
+ inset-inline-end: 0.375rem;
1859
+ display: flex;
1860
+ align-items: center;
1861
+ justify-content: center;
1862
+ inline-size: 1.25rem;
1863
+ block-size: 1.25rem;
1864
+ padding: 0;
1865
+ border: none;
1866
+ border-radius: calc(var(--radius) - 4px);
1867
+ background: transparent;
1868
+ color: var(--muted-foreground);
1869
+ cursor: pointer;
1870
+ pointer-events: auto;
1871
+ }
1872
+ .vd-annotation-popup-close:hover {
1873
+ color: var(--foreground);
1874
+ background: var(--secondary);
1875
+ }
1876
+ .vd-annotation-popup-close:focus-visible {
1877
+ outline: 2px solid var(--ring);
1878
+ outline-offset: 1px;
1879
+ }
1880
+ .vd-annotation-popup-close svg {
1881
+ inline-size: 0.875rem;
1882
+ block-size: 0.875rem;
1883
+ }
1884
+
1885
+ @media (prefers-reduced-motion: no-preference) {
1886
+ .vd-annotation-popup {
1887
+ animation: vd-popup-in 150ms ease;
1888
+ }
1889
+ }
1890
+
1891
+ /* Opacity only: the layer sets the popup's transform inline to clamp it
1892
+ inside the container, and a keyframe animating transform would override
1893
+ that for its duration and make the popup jump at the end. */
1894
+ @keyframes vd-popup-in {
1895
+ from { opacity: 0; }
1896
+ to { opacity: 1; }
1897
+ }
1898
+
1899
+ .vd-annotation-popup-title {
1900
+ font-family: ${L};
1901
+ font-size: 0.875rem;
1902
+ font-weight: 600;
1903
+ line-height: 1.25rem;
1904
+ /* Keep the title clear of the absolutely positioned close button. */
1905
+ padding-inline-end: 1.5rem;
1906
+ }
1907
+
1908
+ .vd-annotation-popup-number {
1909
+ display: inline-flex;
1910
+ align-items: center;
1911
+ justify-content: center;
1912
+ inline-size: 1.25rem;
1913
+ block-size: 1.25rem;
1914
+ margin-inline-end: 0.5rem;
1915
+ border-radius: 50%;
1916
+ background: var(--primary);
1917
+ color: var(--primary-foreground);
1918
+ font-size: 0.75rem;
1919
+ font-weight: 600;
1920
+ vertical-align: middle;
1921
+ }
1922
+
1923
+ /* A brief card shows its title, a button that opens the description. */
1924
+ .vd-annotation-popup--brief .vd-annotation-popup-description {
1925
+ display: none;
1926
+ }
1927
+ button.vd-annotation-popup-title {
1928
+ display: block;
1929
+ inline-size: 100%;
1930
+ margin: 0;
1931
+ /* A button's own padding would set the title off the description below it;
1932
+ the end keeps the room the close button needs. */
1933
+ padding-block: 0;
1934
+ padding-inline-start: 0;
1935
+ border: none;
1936
+ background: none;
1937
+ color: inherit;
1938
+ text-align: start;
1939
+ cursor: pointer;
1940
+ }
1941
+ button.vd-annotation-popup-title:focus-visible {
1942
+ outline: 2px solid var(--ring);
1943
+ outline-offset: 2px;
1944
+ border-radius: calc(var(--radius) - 4px);
1945
+ }
1946
+
1947
+ .vd-annotation-popup-description {
1948
+ font-family: ${L};
1949
+ font-size: 0.8125rem;
1950
+ font-weight: 400;
1951
+ line-height: 1.125rem;
1952
+ color: var(--muted-foreground);
1953
+ margin-block-start: 0.25rem;
1954
+ }
1955
+
1956
+ /* ── QR modal ────────────────────────────────────────────── */
1957
+ /* The slot the QR renders into. While loading it is a calm pulsing
1958
+ placeholder of the same size, so the modal opens instantly and the QR
1959
+ swaps in without any layout shift. */
1960
+ .ui-qr-slot {
1961
+ inline-size: 6rem;
1962
+ block-size: 6rem;
1963
+ border-radius: 0.25rem;
1964
+ overflow: hidden;
1965
+ }
1966
+
1967
+ .ui-qr-slot img {
1968
+ inline-size: 100%;
1969
+ block-size: 100%;
1970
+ display: block;
1971
+ }
1972
+
1973
+ .ui-qr-slot--loading {
1974
+ background: var(--secondary);
1975
+ }
1976
+
1977
+ @media (prefers-reduced-motion: no-preference) {
1978
+ .ui-qr-slot--loading {
1979
+ animation: vd-qr-pulse 1.6s ease-in-out infinite;
1980
+ }
1981
+ }
1982
+
1983
+ @keyframes vd-qr-pulse {
1984
+ 0%, 100% { opacity: 1; }
1985
+ 50% { opacity: 0.55; }
1986
+ }
1987
+ `, gn = !1;
1988
+ function _n() {
1989
+ if (gn || typeof document > "u") return;
1990
+ gn = !0;
1991
+ let e = document.createElement("style");
1992
+ e.id = "vd-ui-vanilla", e.textContent = hn, document.head.appendChild(e);
1993
+ }
1994
+ // ../ui/src/vanilla/composites/qr-modal/index.ts
1995
+ var vn = I.fontFamily.sans.join(", "), yn = class {
1996
+ qrLib = null;
1997
+ dialog = null;
1998
+ qrSlot = null;
1999
+ show() {
2000
+ this.hide(), _n(), this.dialog = new mn({ onClose: () => {
2001
+ this.dialog = null, this.qrSlot = null;
2002
+ } }), Object.assign(this.dialog.content.style, {
2003
+ display: "flex",
2004
+ flexDirection: "column",
2005
+ alignItems: "center",
2006
+ gap: F[3],
2007
+ fontFamily: vn
2008
+ }), this.dialog.content.appendChild(this.createText(Bt(), {
2009
+ fontSize: I.fontSize.base,
2010
+ fontWeight: I.fontWeight.semibold,
2011
+ color: P.neutral[900]
2012
+ })), this.dialog.content.appendChild(this.createText(Rt(), {
2013
+ fontSize: I.fontSize.sm,
2014
+ color: P.neutral[600]
2015
+ })), this.qrSlot = this.createQrSlot(), this.dialog.content.appendChild(this.qrSlot), this.dialog.content.appendChild(this.createText(zt(), {
2016
+ fontSize: I.fontSize.xs,
2017
+ color: P.neutral[500]
2018
+ }));
2019
+ let e = new N({
2020
+ variant: "outline",
2021
+ label: It(),
2022
+ onClick: () => {
2023
+ this.hide();
2024
+ }
2025
+ });
2026
+ this.dialog.content.appendChild(e.element), this.dialog.open();
2027
+ }
2028
+ async update(t) {
2029
+ let n;
2030
+ try {
2031
+ this.qrLib ??= await import("./browser-B8yql1bQ.js").then((t) => /* @__PURE__ */ e(t.default, 1)), n = await this.qrLib.toDataURL(t, {
2032
+ width: 256,
2033
+ margin: 4,
2034
+ errorCorrectionLevel: "M",
2035
+ color: {
2036
+ dark: "#000000",
2037
+ light: "#ffffff"
2038
+ }
2039
+ });
2040
+ } catch (e) {
2041
+ throw this.showLink(t), e;
2042
+ }
2043
+ return this.qrSlot !== null && (this.qrSlot.classList.remove("ui-qr-slot--loading"), this.qrSlot.replaceChildren(this.createQrImage(n)), !0);
2044
+ }
2045
+ showLink(e) {
2046
+ this.qrSlot !== null && (this.qrSlot.classList.remove("ui-qr-slot--loading"), this.qrSlot.replaceChildren(this.createText(Vt(), {
2047
+ fontSize: I.fontSize.sm,
2048
+ color: P.neutral[600]
2049
+ }), this.createText(e, {
2050
+ fontSize: I.fontSize.xs,
2051
+ color: P.neutral[900],
2052
+ overflowWrap: "anywhere",
2053
+ userSelect: "all"
2054
+ })));
2055
+ }
2056
+ hide() {
2057
+ this.dialog?.close(), this.dialog = null, this.qrSlot = null;
2058
+ }
2059
+ createText(e, t) {
2060
+ let n = document.createElement("p");
2061
+ return n.textContent = e, Object.assign(n.style, {
2062
+ margin: "0",
2063
+ textAlign: "center",
2064
+ ...t
2065
+ }), n;
2066
+ }
2067
+ createQrSlot() {
2068
+ let e = document.createElement("div");
2069
+ return e.className = "ui-qr-slot ui-qr-slot--loading", e;
2070
+ }
2071
+ createQrImage(e) {
2072
+ let t = document.createElement("img");
2073
+ return t.src = e, t.alt = Lt(), t;
2074
+ }
2075
+ };
2076
+ // ../client-core/src/ui/wireframe-cube.ts
2077
+ function bn(e) {
2078
+ let t = Math.max(0, Math.min(1, e));
2079
+ return t * t * (3 - 2 * t);
2080
+ }
2081
+ function xn(e, t, n, r, i, a) {
2082
+ let [o, s, c] = e, l = Math.cos(t), u = Math.sin(t), ee = l * o + u * c, d = -u * o + l * c, f = Math.cos(n), p = Math.sin(n), te = f * s - p * d, ne = p * s + f * d, m = .55;
2083
+ return [
2084
+ r + ee * a * m,
2085
+ i - te * a * m,
2086
+ ne
2087
+ ];
2088
+ }
2089
+ function Sn(e, t, n) {
2090
+ e.beginPath(), e.moveTo(t[0], t[1]), e.lineTo(n[0], n[1]), e.strokeStyle = "#000", e.lineWidth = 2, e.stroke();
2091
+ }
2092
+ function Cn(e, t, n, r, i) {
2093
+ let a = t(n), o = t(r);
2094
+ Sn(e, a, [
2095
+ a[0] + (o[0] - a[0]) * i,
2096
+ a[1] + (o[1] - a[1]) * i,
2097
+ a[2]
2098
+ ]);
2099
+ }
2100
+ var R = [
2101
+ -1,
2102
+ -1,
2103
+ 1
2104
+ ], z = [
2105
+ 1,
2106
+ -1,
2107
+ 1
2108
+ ], B = [
2109
+ 1,
2110
+ -1,
2111
+ -1
2112
+ ], V = [
2113
+ -1,
2114
+ -1,
2115
+ -1
2116
+ ], H = [
2117
+ -1,
2118
+ 1,
2119
+ 1
2120
+ ], U = [
2121
+ 1,
2122
+ 1,
2123
+ 1
2124
+ ], W = [
2125
+ 1,
2126
+ 1,
2127
+ -1
2128
+ ], G = [
2129
+ -1,
2130
+ 1,
2131
+ -1
2132
+ ], K = [
2133
+ [R, z],
2134
+ [z, B],
2135
+ [B, V],
2136
+ [V, R],
2137
+ [R, H],
2138
+ [z, U],
2139
+ [B, W],
2140
+ [V, G],
2141
+ [H, U],
2142
+ [U, W],
2143
+ [W, G],
2144
+ [G, H]
2145
+ ];
2146
+ function wn(e) {
2147
+ let t = e % 6 / 6, n = K.length;
2148
+ return t < .35 ? bn(t / .35) * n : t < .55 ? n : t < .9 ? (1 - bn((t - .55) / .35)) * n : 0;
2149
+ }
2150
+ function Tn(e, { width: t, height: n }, r) {
2151
+ let i = t / 2, a = n * .44, o = Math.min(t, n) * .08, s = (e) => xn(e, .785, .615, i, a, o), c = wn(r);
2152
+ e.shadowBlur = 5, e.shadowColor = "rgba(30,30,30,0.08)";
2153
+ for (let t = 0; t < K.length && !(t >= c); t++) {
2154
+ let n = K[t];
2155
+ if (n === void 0) continue;
2156
+ let r = Math.min(1, c - t);
2157
+ Cn(e, s, n[0], n[1], r);
2158
+ }
2159
+ e.shadowBlur = 0;
2160
+ }
2161
+ // ../client-core/src/ui/loading.ts
2162
+ var En = 400, Dn = 3e4, On = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif";
2163
+ function kn() {
2164
+ let e = document.createElement("div");
2165
+ Object.assign(e.style, {
2166
+ position: "absolute",
2167
+ inset: "0",
2168
+ display: "flex",
2169
+ flexDirection: "column",
2170
+ alignItems: "center",
2171
+ justifyContent: "center",
2172
+ background: "#fff",
2173
+ zIndex: "20",
2174
+ opacity: "0",
2175
+ transition: "opacity 0.3s ease-in-out"
2176
+ });
2177
+ let t = document.createElement("canvas");
2178
+ Object.assign(t.style, {
2179
+ width: "100%",
2180
+ height: "100%",
2181
+ position: "absolute",
2182
+ inset: "0"
2183
+ });
2184
+ let n = document.createElement("div");
2185
+ Object.assign(n.style, {
2186
+ position: "absolute",
2187
+ top: "56%",
2188
+ left: "0",
2189
+ right: "0",
2190
+ textAlign: "center",
2191
+ pointerEvents: "none"
2192
+ });
2193
+ let r = document.createElement("span");
2194
+ Object.assign(r.style, {
2195
+ fontSize: "13px",
2196
+ fontWeight: "400",
2197
+ color: "#000",
2198
+ fontVariantNumeric: "tabular-nums",
2199
+ fontFamily: On
2200
+ });
2201
+ let i = document.createElement("span");
2202
+ return Object.assign(i.style, {
2203
+ fontSize: "13px",
2204
+ color: "#000",
2205
+ fontFamily: On
2206
+ }), i.textContent = Ut(), n.appendChild(r), n.appendChild(i), e.appendChild(t), e.appendChild(n), {
2207
+ overlay: e,
2208
+ canvas: t,
2209
+ pctEl: r,
2210
+ phaseEl: i
2211
+ };
2212
+ }
2213
+ var An = class {
2214
+ overlay;
2215
+ canvas;
2216
+ ctx;
2217
+ pctEl;
2218
+ phaseEl;
2219
+ animationId = null;
2220
+ showTimerId = null;
2221
+ noSignalTimerId = null;
2222
+ startTime;
2223
+ hidden = !1;
2224
+ visible = !1;
2225
+ errored = !1;
2226
+ noSignalErrored = !1;
638
2227
  constructor(e) {
639
- this.send = e;
2228
+ this.startTime = performance.now();
2229
+ let t = kn();
2230
+ this.overlay = t.overlay, this.canvas = t.canvas, this.pctEl = t.pctEl, this.phaseEl = t.phaseEl, e.appendChild(this.overlay), this.ctx = this.canvas.getContext("2d"), this.ctx !== null && (this.sizeCanvas(), this.tick()), this.showTimerId = setTimeout(() => {
2231
+ this.reveal();
2232
+ }, En), this.noSignalTimerId = setTimeout(() => {
2233
+ this.hidden || (this.noSignalErrored = !0, this.showError(Wt()));
2234
+ }, Dn);
2235
+ }
2236
+ viewerAlive() {
2237
+ this.cancelNoSignalTimer(), this.noSignalErrored && this.recoverFromNoSignal();
2238
+ }
2239
+ recoverFromNoSignal() {
2240
+ this.noSignalErrored = !1, this.errored = !1, this.pctEl.textContent = "", this.phaseEl.textContent = Ut(), this.phaseEl.style.color = "#000", this.ctx !== null && this.animationId === null && !this.hidden && this.tick();
2241
+ }
2242
+ updateProgress(e, t) {
2243
+ this.cancelNoSignalTimer(), !(this.hidden || this.errored) && (this.pctEl.textContent = `${Math.round(e)}%`, this.phaseEl.textContent = t === "" ? "" : ` \u00B7 ${t}`);
2244
+ }
2245
+ showError(e) {
2246
+ if (this.cancelNoSignalTimer(), this.hidden = !1, this.errored = !0, this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.ctx !== null) {
2247
+ let e = Math.min(devicePixelRatio, 2), t = this.canvas.width / e, n = this.canvas.height / e;
2248
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, t * e, n * e);
2249
+ }
2250
+ this.pctEl.textContent = "", this.phaseEl.textContent = e, this.phaseEl.style.color = "#b91c1c", this.reveal();
640
2251
  }
641
- sendInitialConfig(e) {
642
- let t = this.buildUIConfig(e), n = this.buildViewerConfig(t, e.camera);
643
- n !== void 0 && this.sendConfig(n);
2252
+ hide() {
2253
+ this.hidden || (this.hidden = !0, this.cancelShowTimer(), this.visible ? (this.overlay.style.opacity = "0", this.overlay.addEventListener("transitionend", () => {
2254
+ this.destroy();
2255
+ }, { once: !0 }), setTimeout(() => {
2256
+ this.destroy();
2257
+ }, 500)) : this.destroy());
644
2258
  }
645
- buildUIConfig(e) {
646
- let t = this.extractUIValues(e);
647
- if (Object.values(t).some((e) => e !== void 0)) return {
648
- ...t.arEnabled !== void 0 && { arEnabled: t.arEnabled },
649
- ...t.fullscreenEnabled !== void 0 && { fullscreenEnabled: t.fullscreenEnabled },
650
- ...t.loadingIndicatorEnabled !== void 0 && { loadingIndicatorEnabled: t.loadingIndicatorEnabled }
651
- };
2259
+ destroy() {
2260
+ this.cancelShowTimer(), this.cancelNoSignalTimer(), this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.overlay.remove();
652
2261
  }
653
- extractUIValues(e) {
654
- return {
655
- arEnabled: e.arEnabled ?? e.ui?.arEnabled,
656
- fullscreenEnabled: e.fullscreenEnabled ?? e.ui?.fullscreenEnabled,
657
- loadingIndicatorEnabled: e.loadingIndicatorEnabled ?? e.ui?.loadingIndicatorEnabled
658
- };
2262
+ reveal() {
2263
+ this.hidden || this.visible || (this.visible = !0, this.overlay.style.opacity = "1");
659
2264
  }
660
- buildViewerConfig(e, t) {
661
- if (!(e === void 0 && t === void 0)) return {
662
- ...e !== void 0 && { ui: e },
663
- ...t !== void 0 && { camera: t }
664
- };
2265
+ cancelShowTimer() {
2266
+ this.showTimerId !== null && (clearTimeout(this.showTimerId), this.showTimerId = null);
665
2267
  }
666
- setArEnabled(e) {
667
- p.debug(`Setting AR button enabled: ${e}`), this.updateUIConfig({ arEnabled: e });
2268
+ cancelNoSignalTimer() {
2269
+ this.noSignalTimerId !== null && (clearTimeout(this.noSignalTimerId), this.noSignalTimerId = null);
668
2270
  }
669
- setFullscreenEnabled(e) {
670
- p.debug(`Setting fullscreen button enabled: ${e}`), this.updateUIConfig({ fullscreenEnabled: e });
2271
+ sizeCanvas() {
2272
+ if (this.ctx === null) return;
2273
+ let e = Math.min(devicePixelRatio, 2), t = this.overlay.clientWidth || 440, n = this.overlay.clientHeight || 360;
2274
+ this.canvas.width = t * e, this.canvas.height = n * e, this.ctx.scale(e, e), this.ctx.lineCap = "round", this.ctx.lineJoin = "round";
671
2275
  }
672
- setLoadingIndicatorEnabled(e) {
673
- p.debug(`Setting loading indicator enabled: ${e}`), this.updateUIConfig({ loadingIndicatorEnabled: e });
2276
+ tick = () => {
2277
+ this.hidden || (this.drawFrame(), this.animationId = requestAnimationFrame(this.tick));
2278
+ };
2279
+ drawFrame() {
2280
+ if (this.ctx === null) return;
2281
+ let e = this.canvas.width / Math.min(devicePixelRatio, 2), t = this.canvas.height / Math.min(devicePixelRatio, 2), n = (performance.now() - this.startTime) / 1e3;
2282
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, e, t), Tn(this.ctx, {
2283
+ width: e,
2284
+ height: t
2285
+ }, n);
2286
+ }
2287
+ }, jn = class {
2288
+ options;
2289
+ element;
2290
+ arBtn = null;
2291
+ fullscreenBtn = null;
2292
+ annotationsBtn = null;
2293
+ annotationsShown = !1;
2294
+ onFullscreenChange;
2295
+ constructor(e) {
2296
+ this.options = e, this.element = document.createElement("div"), Object.assign(this.element.style, {
2297
+ display: "flex",
2298
+ gap: "0.375rem"
2299
+ }), this.onFullscreenChange = () => {
2300
+ this.setFullscreenIcon(document.fullscreenElement !== null);
2301
+ }, document.addEventListener("fullscreenchange", this.onFullscreenChange);
2302
+ }
2303
+ setFullscreenIcon(e) {
2304
+ this.fullscreenBtn !== null && this.fullscreenBtn.setIcon(e ? M.exitFullscreen() : M.fullscreen());
2305
+ }
2306
+ syncButton(e, t, n) {
2307
+ if (t && e === null) {
2308
+ let e = n();
2309
+ return this.element.appendChild(e.element), e;
2310
+ }
2311
+ return !t && e !== null ? (e.element.remove(), null) : e;
2312
+ }
2313
+ applySettings(e) {
2314
+ this.arBtn = this.syncButton(this.arBtn, e.ar !== !1 && this.options.onAr !== void 0, () => new N({
2315
+ variant: "outline",
2316
+ size: "icon",
2317
+ label: Tt(),
2318
+ icon: M.ar(),
2319
+ onClick: () => {
2320
+ this.options.onAr?.();
2321
+ }
2322
+ })), this.fullscreenBtn = this.syncButton(this.fullscreenBtn, e.fullscreen !== !1 && this.options.onFullscreen !== void 0, () => new N({
2323
+ variant: "outline",
2324
+ size: "icon",
2325
+ label: Ft(),
2326
+ icon: M.fullscreen(),
2327
+ onClick: () => {
2328
+ this.options.onFullscreen?.();
2329
+ }
2330
+ }));
2331
+ let t = e.annotations === "toggle-shown" || e.annotations === "toggle-hidden";
2332
+ this.annotationsBtn = this.syncButton(this.annotationsBtn, t && this.options.onAnnotationsToggle !== void 0, () => {
2333
+ this.annotationsShown = e.annotations === "toggle-shown";
2334
+ let t = new N({
2335
+ variant: "outline",
2336
+ size: "icon",
2337
+ label: wt(),
2338
+ icon: this.annotationsShown ? M.annotations() : M.annotationsOff(),
2339
+ onClick: () => {
2340
+ this.flipAnnotations();
2341
+ }
2342
+ });
2343
+ return t.element.setAttribute("aria-pressed", String(this.annotationsShown)), t;
2344
+ });
2345
+ }
2346
+ flipAnnotations() {
2347
+ this.annotationsShown = !this.annotationsShown, this.annotationsBtn?.element.setAttribute("aria-pressed", String(this.annotationsShown)), this.annotationsBtn?.setIcon(this.annotationsShown ? M.annotations() : M.annotationsOff()), this.options.onAnnotationsToggle?.(this.annotationsShown);
674
2348
  }
675
- updateUIConfig(e) {
676
- p.debug("Updating UI configuration", e), Object.keys(e).length > 0 && this.sendConfig({ ui: e });
2349
+ destroy() {
2350
+ document.removeEventListener("fullscreenchange", this.onFullscreenChange);
2351
+ }
2352
+ }, q = .5, Mn = class e {
2353
+ element;
2354
+ constructor(t, n) {
2355
+ this.element = Object.assign(document.createElement("div"), { className: "vd-camera-controls" });
2356
+ let r = {
2357
+ targetRotation: n.rotation,
2358
+ targetTilt: n.tilt,
2359
+ targetZoom: n.zoom,
2360
+ thetaOffset: 0
2361
+ }, i = Ln(t, n, r);
2362
+ n.onChange((e) => {
2363
+ r.targetRotation = e.rotation - r.thetaOffset, r.targetTilt = e.tilt, r.targetZoom = e.zoom, Rn(i, n, e);
2364
+ }), this.element.appendChild(e.buildZoom(i.zoomOut, i.zoomIn)), this.element.appendChild(e.buildDpad(i.tiltUp, i.rotateLeft, i.reset, i.rotateRight, i.tiltDown));
2365
+ }
2366
+ static buildZoom(e, t) {
2367
+ let n = document.createElement("div");
2368
+ return n.className = "vd-zoom", n.appendChild(e.element), n.appendChild(t.element), n;
2369
+ }
2370
+ static buildDpad(e, t, n, r, i) {
2371
+ let a = document.createElement("div");
2372
+ a.className = "vd-dpad";
2373
+ for (let [o, s] of [
2374
+ [e, "up"],
2375
+ [t, "left"],
2376
+ [n, "center"],
2377
+ [r, "right"],
2378
+ [i, "down"]
2379
+ ]) o.element.classList.add(`vd-dpad-${s}`), a.appendChild(o.element);
2380
+ return a;
677
2381
  }
678
- hideAllUI() {
679
- p.debug("Hiding all UI elements"), this.sendUIConfig({
680
- arEnabled: !1,
681
- fullscreenEnabled: !1,
682
- loadingIndicatorEnabled: !1
2382
+ };
2383
+ function Nn(e, t) {
2384
+ let n = e;
2385
+ for (; n < t;) n += 360;
2386
+ for (; n >= t + 360;) n -= 360;
2387
+ return n;
2388
+ }
2389
+ function Pn(e, t, n) {
2390
+ let r = Nn(e, t);
2391
+ return r <= n ? r : r - n <= t + 360 - r ? n : t;
2392
+ }
2393
+ function J(e, t, n) {
2394
+ return new N({
2395
+ variant: "outline",
2396
+ size: "icon",
2397
+ label: e,
2398
+ icon: t,
2399
+ onClick: n
2400
+ });
2401
+ }
2402
+ function Fn(e, t) {
2403
+ return Math.max(e.limits?.minZoom ?? 1, Math.min(e.limits?.maxZoom ?? Infinity, t));
2404
+ }
2405
+ function In(e, t) {
2406
+ let n = e.limits?.minRotate, r = e.limits?.maxRotate;
2407
+ return n === void 0 || r === void 0 || r - n >= 360 ? t : Pn(t, n, r);
2408
+ }
2409
+ function Y(e, t, n, r) {
2410
+ let i = t.limits?.minTilt, a = t.limits?.maxTilt;
2411
+ if (i !== void 0 || a !== void 0) {
2412
+ n.targetTilt = Math.max(i ?? 0, Math.min(a ?? 180, n.targetTilt + r)), e.tilt(n.targetTilt).set();
2413
+ return;
2414
+ }
2415
+ let o = n.targetTilt + r;
2416
+ o > 180 || o < 0 ? (n.targetTilt = o > 180 ? 360 - o : -o, n.thetaOffset = (n.thetaOffset + 180) % 360, e.tilt(n.targetTilt).rotate(n.targetRotation + n.thetaOffset).set()) : (n.targetTilt = o, e.tilt(n.targetTilt).set());
2417
+ }
2418
+ function Ln(e, t, n) {
2419
+ let r = (r) => {
2420
+ n.targetZoom = Fn(t, n.targetZoom + r), e.zoom(n.targetZoom).set();
2421
+ }, i = (r) => {
2422
+ n.targetRotation = In(t, n.targetRotation + r), e.rotate(n.targetRotation + n.thetaOffset).set();
2423
+ };
2424
+ return {
2425
+ zoomOut: J(Mt(), M.zoomOut(), () => {
2426
+ r(-10);
2427
+ }),
2428
+ zoomIn: J(jt(), M.zoomIn(), () => {
2429
+ r(10);
2430
+ }),
2431
+ tiltUp: J(At(), M.tiltUp(), () => {
2432
+ Y(e, t, n, -15);
2433
+ }),
2434
+ rotateLeft: J(Dt(), M.rotateLeft(), () => {
2435
+ i(-15);
2436
+ }),
2437
+ reset: J(Et(), M.reset(), () => {
2438
+ n.thetaOffset = 0, e.reset();
2439
+ }),
2440
+ rotateRight: J(Ot(), M.rotateRight(), () => {
2441
+ i(15);
2442
+ }),
2443
+ tiltDown: J(kt(), M.tiltDown(), () => {
2444
+ Y(e, t, n, 15);
2445
+ })
2446
+ };
2447
+ }
2448
+ function Rn(e, t, n) {
2449
+ let r = t.limits;
2450
+ if (r === void 0) return;
2451
+ e.zoomOut.element.disabled = r.minZoom !== void 0 && n.zoom <= r.minZoom + q, e.zoomIn.element.disabled = r.maxZoom !== void 0 && n.zoom >= r.maxZoom - q, e.tiltUp.element.disabled = r.minTilt !== void 0 && n.tilt <= r.minTilt + q, e.tiltDown.element.disabled = r.maxTilt !== void 0 && n.tilt >= r.maxTilt - q;
2452
+ let { minRotate: i, maxRotate: a } = r;
2453
+ if (i === void 0 || a === void 0 || a - i >= 360) {
2454
+ e.rotateLeft.element.disabled = !1, e.rotateRight.element.disabled = !1;
2455
+ return;
2456
+ }
2457
+ let o = Nn(n.rotation, i);
2458
+ e.rotateLeft.element.disabled = o <= i + q, e.rotateRight.element.disabled = o >= a - q;
2459
+ }
2460
+ // ../client-core/src/client/events.ts
2461
+ var X = {
2462
+ SELECT: "vd:select",
2463
+ SELECTED: "vd:selected",
2464
+ FOCUS_CHANGE: "vd:focus-change",
2465
+ CONFIGURATION_CHANGE: "vd:configuration-change",
2466
+ CONFIGURATION_SUBMIT: "vd:configuration-submit"
2467
+ }, zn = {
2468
+ mm: 1,
2469
+ cm: 10,
2470
+ m: 1e3,
2471
+ in: 25.4
2472
+ }, Bn = 3;
2473
+ function Vn(e) {
2474
+ let t = e.stepMm / zn[e.displayUnit];
2475
+ for (let e = 0; e <= Bn; e++) {
2476
+ let n = t * 10 ** e;
2477
+ if (Math.abs(n - Math.round(n)) < 1e-9) return e;
2478
+ }
2479
+ return Bn;
2480
+ }
2481
+ function Hn(e, t, n) {
2482
+ let r = Vn(t), i = e / zn[t.displayUnit];
2483
+ return new Intl.NumberFormat(n, {
2484
+ minimumFractionDigits: r,
2485
+ maximumFractionDigits: r
2486
+ }).format(i);
2487
+ }
2488
+ // ../client-core/src/ui/controls/measurement-group.ts
2489
+ function Un(e, t, n, r, i, a, o) {
2490
+ let s = v(), c = e.values.map((e) => Hn(Number(e), t, s)), l = e.values.find((t) => n.getValue(e.name, t)?.isSelected === !0) ?? e.defaultValue, u = new fn(e.values, {
2491
+ label: e.name,
2492
+ labels: c,
2493
+ unit: t.displayUnit,
2494
+ ...l !== void 0 && { initialSelected: l }
2495
+ });
2496
+ return u.onSelect((t) => {
2497
+ i(e.name, t);
2498
+ }), u.onSettle((t) => {
2499
+ r.dispatch(X.SELECT, {
2500
+ attribute: e.name,
2501
+ value: t
2502
+ }, !0).defaultPrevented || a(e.name, t);
2503
+ }), o(e.name, (t) => {
2504
+ u.setSelected(t.value), r.dispatch(X.SELECTED, {
2505
+ attribute: e.name,
2506
+ value: t.value
2507
+ });
2508
+ }), {
2509
+ element: u.element,
2510
+ setSelectable: () => {}
2511
+ };
2512
+ }
2513
+ // ../client-core/src/ui/controls/attribute-group.ts
2514
+ var Wn = 5, Gn = 0;
2515
+ function Kn(e, t, n, r, i, a) {
2516
+ let o = document.createElement("div");
2517
+ o.className = "vd-attribute", o.dataset.name = e.name;
2518
+ let s = document.createElement("span");
2519
+ s.className = "vd-attribute-label", s.textContent = S(e.name), s.id = `vd-attribute-label-${Gn++}`, o.appendChild(s), o.setAttribute("role", "group"), o.setAttribute("aria-labelledby", s.id);
2520
+ let c = (e) => {
2521
+ o.style.display = e ? "none" : "";
2522
+ }, l = e.dimension === void 0 ? qn(e, t, n, r, a) : Un(e, e.dimension, t, n, i, r, a);
2523
+ return o.appendChild(l.element), {
2524
+ element: o,
2525
+ setSelectable: l.setSelectable,
2526
+ setHidden: c
2527
+ };
2528
+ }
2529
+ function qn(e, t, n, r, i) {
2530
+ let a = e.values.find((n) => t.getValue(e.name, n)?.isSelected === !0), o = e.values.map((t) => Fe(t, e.labels)), s = e.values.length <= Wn ? new un(e.values, a, o) : new dn(e.name, e.values, S(e.name), a, o);
2531
+ return s.onSelect((t) => {
2532
+ n.dispatch(X.SELECT, {
2533
+ attribute: e.name,
2534
+ value: t
2535
+ }, !0).defaultPrevented || r(e.name, t);
2536
+ }), i(e.name, (t) => {
2537
+ s.setSelected(t.value), n.dispatch(X.SELECTED, {
2538
+ attribute: e.name,
2539
+ value: t.value
683
2540
  });
2541
+ }), {
2542
+ element: s.element,
2543
+ setSelectable: (e) => {
2544
+ s.setEnabled(e);
2545
+ }
2546
+ };
2547
+ }
2548
+ // ../client-core/src/ui/commerce-panel.ts
2549
+ var Jn = 80, Yn = 2500, Xn = class {
2550
+ configuration;
2551
+ element;
2552
+ priceEl;
2553
+ vatEl;
2554
+ button;
2555
+ unsubscribe;
2556
+ state = "idle";
2557
+ resetTimer;
2558
+ constructor(e) {
2559
+ this.configuration = e, this.element = document.createElement("div"), this.element.className = "vd-commerce vd-hidden";
2560
+ let t = document.createElement("div");
2561
+ t.className = "vd-commerce-price", this.priceEl = document.createElement("span"), this.priceEl.className = "vd-price", this.vatEl = document.createElement("span"), this.vatEl.className = "vd-vat", t.append(this.priceEl, this.vatEl), this.button = new N({
2562
+ variant: "default",
2563
+ onClick: () => {
2564
+ this.handleSubmit();
2565
+ }
2566
+ }), this.button.element.classList.add("vd-cta", "vd-hidden"), this.element.append(t, this.button.element), this.unsubscribe = this.configuration.onRender(() => {
2567
+ this.update();
2568
+ }), this.update();
2569
+ }
2570
+ update() {
2571
+ this.renderPrice(this.configuration.current), this.renderButton();
2572
+ let e = this.configuration.current !== void 0 || this.configuration.submitEnabled;
2573
+ this.element.classList.toggle("vd-hidden", !e);
684
2574
  }
685
- showAllUI() {
686
- p.debug("Showing all UI elements"), this.sendUIConfig({
687
- arEnabled: !0,
688
- fullscreenEnabled: !0,
689
- loadingIndicatorEnabled: !0
2575
+ destroy() {
2576
+ this.unsubscribe(), this.resetTimer !== void 0 && clearTimeout(this.resetTimer), this.element.remove();
2577
+ }
2578
+ renderPrice(e) {
2579
+ e === void 0 ? (this.priceEl.textContent = "", this.vatEl.textContent = "") : (this.priceEl.textContent = Zn(e.price, e.currency), this.vatEl.textContent = Z(this.configuration.vatDisplay));
2580
+ }
2581
+ renderButton() {
2582
+ let e = this.configuration.submitEnabled;
2583
+ this.button.element.classList.toggle("vd-hidden", !e), e && this.state === "idle" && this.setButtonText(Z(this.configuration.buttonLabel));
2584
+ }
2585
+ async handleSubmit() {
2586
+ if (this.state !== "busy") {
2587
+ this.setState("busy", xt());
2588
+ try {
2589
+ await this.configuration.submit(), this.setState("success", Ct());
2590
+ } catch (e) {
2591
+ if (e instanceof DOMException && e.name === "AbortError") {
2592
+ this.setState("idle", Z(this.configuration.buttonLabel));
2593
+ return;
2594
+ }
2595
+ A.warn("Commerce submit failed", { error: e }), this.setState("failed", St());
2596
+ }
2597
+ this.scheduleReset();
2598
+ }
2599
+ }
2600
+ scheduleReset() {
2601
+ this.resetTimer !== void 0 && clearTimeout(this.resetTimer), this.resetTimer = setTimeout(() => {
2602
+ this.state = "idle", this.update();
2603
+ }, Yn);
2604
+ }
2605
+ setState(e, t) {
2606
+ this.state = e, this.element.dataset.submit = e, this.button.element.disabled = e === "busy", this.setButtonText(t);
2607
+ }
2608
+ setButtonText(e) {
2609
+ this.button.element.textContent = e, this.button.element.setAttribute("aria-label", e);
2610
+ }
2611
+ };
2612
+ function Z(e) {
2613
+ return (e ?? "").slice(0, Jn);
2614
+ }
2615
+ function Zn(e, t) {
2616
+ try {
2617
+ let n = new Intl.NumberFormat(v(), {
2618
+ style: "currency",
2619
+ currency: t
2620
+ }), r = n.resolvedOptions().maximumFractionDigits ?? 2;
2621
+ return n.format(e / 10 ** r);
2622
+ } catch {
2623
+ return `${t} ${(e / 100).toFixed(2)}`;
2624
+ }
2625
+ }
2626
+ // ../client-core/src/ui/layouts/inframe-layout.ts
2627
+ var Qn = class {
2628
+ slot = null;
2629
+ panel = null;
2630
+ attach(e, t) {
2631
+ this.panel = t, t.style.display = "none", this.slot = document.createElement("div"), this.slot.className = "vd-slot-bottom-left", this.slot.appendChild(t), e.appendChild(this.slot);
2632
+ }
2633
+ setVisible(e) {
2634
+ this.panel !== null && (this.panel.style.display = e ? "flex" : "none");
2635
+ }
2636
+ destroy() {
2637
+ this.slot?.remove(), this.slot = null, this.panel = null;
2638
+ }
2639
+ }, $n = 572, er = class {
2640
+ actionBarSlot;
2641
+ element = null;
2642
+ body = null;
2643
+ openButton = null;
2644
+ closeButton = null;
2645
+ container = null;
2646
+ observer = null;
2647
+ collapsible = !1;
2648
+ expanded = !0;
2649
+ visible = !1;
2650
+ constructor(e) {
2651
+ this.actionBarSlot = e;
2652
+ }
2653
+ attach(e, t) {
2654
+ this.container = e, t.classList.add("vd-attributes-sidebar"), this.closeButton = new N({
2655
+ variant: "ghost",
2656
+ size: "icon-sm",
2657
+ label: Nt(),
2658
+ icon: M.close({ size: 18 }),
2659
+ onClick: () => {
2660
+ this.expanded = !1, this.render();
2661
+ }
690
2662
  });
2663
+ let n = document.createElement("div");
2664
+ n.className = "vd-sidebar-header", n.appendChild(this.closeButton.element), this.body = document.createElement("div"), this.body.className = "vd-sidebar-body", this.body.appendChild(t), this.element = document.createElement("div"), this.element.className = "vd-sidebar vd-hidden", this.element.append(n, this.body), e.appendChild(this.element), this.openButton = new N({
2665
+ variant: "outline",
2666
+ size: "icon",
2667
+ label: Pt(),
2668
+ icon: M.configurator({ size: 18 }),
2669
+ onClick: () => {
2670
+ this.expanded = !0, this.render();
2671
+ }
2672
+ }), this.openButton.element.classList.add("vd-configurator-toggle", "vd-hidden"), this.actionBarSlot.appendChild(this.openButton.element), this.observe();
691
2673
  }
692
- sendUIConfig(e) {
693
- this.sendConfig({ ui: e });
2674
+ setVisible(e) {
2675
+ this.visible = e, this.render();
694
2676
  }
695
- sendConfig(e) {
696
- let t = {
697
- type: b.CONFIG,
698
- config: e
2677
+ destroy() {
2678
+ this.observer?.disconnect(), this.observer = null, this.openButton?.element.remove(), this.element?.remove(), this.container?.classList.remove("vd-layout-sidebar-right", "vd-layout-sidebar-permanent"), this.container = null;
2679
+ }
2680
+ observe() {
2681
+ typeof ResizeObserver < "u" && this.container !== null && (this.observer = new ResizeObserver((e) => {
2682
+ let t = e[0]?.contentRect.width ?? 0;
2683
+ this.setCollapsible(t > 0 && t < $n);
2684
+ }), this.observer.observe(this.container));
2685
+ }
2686
+ setCollapsible(e) {
2687
+ e !== this.collapsible && (this.collapsible = e, this.expanded = !e, this.render());
2688
+ }
2689
+ render() {
2690
+ if (this.element === null || this.openButton === null || this.closeButton === null || this.container === null) return;
2691
+ let e = this.visible && this.expanded, t = this.visible && !this.expanded;
2692
+ this.element.classList.toggle("vd-hidden", !this.visible), this.element.classList.toggle("vd-sidebar-collapsed", t), this.openButton.element.classList.toggle("vd-hidden", !t), this.closeButton.element.style.display = this.collapsible ? "" : "none", this.container.classList.toggle("vd-layout-sidebar-right", e), this.container.classList.toggle("vd-layout-sidebar-permanent", e && !this.collapsible);
2693
+ }
2694
+ }, tr = class {
2695
+ attach(e, t) {}
2696
+ setVisible(e) {}
2697
+ destroy() {}
2698
+ }, nr = {
2699
+ "inframe-bottom-left": () => new Qn(),
2700
+ "sidebar-right": (e) => new er(e),
2701
+ hidden: () => new tr()
2702
+ };
2703
+ function rr(e, t) {
2704
+ return (nr[e ?? ""] ?? (() => new Qn()))(t);
2705
+ }
2706
+ // ../client-core/src/ui/styles.ts
2707
+ var Q = I.fontFamily.sans.join(", "), $ = "\n position: absolute;\n display: flex;\n z-index: 10;\n transition: inset-inline-end 0.18s ease;\n", ir = `
2708
+ /* Override browser's default black fullscreen backdrop */
2709
+ .vd-container:fullscreen {
2710
+ background: oklch(100% 0 0);
2711
+ }
2712
+
2713
+ /* Layout slots — four corners */
2714
+ .vd-slot-top-right {
2715
+ ${$}
2716
+ inset-block-start: ${F[3]};
2717
+ inset-inline-end: ${F[3]};
2718
+ flex-direction: column;
2719
+ align-items: flex-end;
2720
+ gap: ${F[1.5]};
2721
+ }
2722
+
2723
+ .vd-slot-bottom-left {
2724
+ ${$}
2725
+ inset-block-end: ${F[3]};
2726
+ inset-inline-start: ${F[3]};
2727
+ flex-direction: column;
2728
+ align-items: flex-start;
2729
+ gap: ${F[1.5]};
2730
+ }
2731
+
2732
+ .vd-slot-bottom-right {
2733
+ ${$}
2734
+ inset-block-end: ${F[3]};
2735
+ inset-inline-end: ${F[3]};
2736
+ flex-direction: row;
2737
+ gap: ${F[1.5]};
2738
+ }
2739
+
2740
+ /* Component styles — no positioning */
2741
+ .vd-camera-controls {
2742
+ display: flex;
2743
+ flex-direction: column;
2744
+ gap: ${F[1]};
2745
+ background: color-mix(in oklch, ${P.white} 85%, transparent);
2746
+ backdrop-filter: blur(8px);
2747
+ border-radius: ${F[2]};
2748
+ padding: ${F[2]};
2749
+ }
2750
+
2751
+ .vd-zoom {
2752
+ display: flex;
2753
+ gap: ${F[.5]};
2754
+ }
2755
+
2756
+ .vd-dpad {
2757
+ display: grid;
2758
+ grid-template-columns: repeat(3, ${F[9]});
2759
+ grid-template-rows: repeat(3, ${F[9]});
2760
+ gap: ${F[.5]};
2761
+ }
2762
+
2763
+ .vd-dpad-up { grid-column: 2; grid-row: 1; }
2764
+ .vd-dpad-left { grid-column: 1; grid-row: 2; }
2765
+ .vd-dpad-center { grid-column: 2; grid-row: 2; }
2766
+ .vd-dpad-right { grid-column: 3; grid-row: 2; }
2767
+ .vd-dpad-down { grid-column: 2; grid-row: 3; }
2768
+
2769
+ .vd-attributes {
2770
+ display: flex;
2771
+ flex-direction: column;
2772
+ gap: ${F[3]};
2773
+ background: color-mix(in oklch, ${P.white} 85%, transparent);
2774
+ backdrop-filter: blur(8px);
2775
+ border: 1px solid color-mix(in oklch, ${P.neutral[900]} 8%, transparent);
2776
+ border-radius: ${F[3]};
2777
+ padding: ${F[3]};
2778
+ box-shadow: 0 ${F[1]} ${F[4]} color-mix(in oklch, ${P.neutral[900]} 6%, transparent);
2779
+ font-family: ${Q};
2780
+ }
2781
+
2782
+ .vd-attribute {
2783
+ display: flex;
2784
+ flex-direction: column;
2785
+ gap: ${F[1.5]};
2786
+ }
2787
+
2788
+ /* Micro-label: recedes so the controls lead. */
2789
+ .vd-attribute-label {
2790
+ font-size: ${I.fontSize.xs};
2791
+ font-weight: ${I.fontWeight.medium};
2792
+ color: ${P.neutral[500]};
2793
+ text-transform: uppercase;
2794
+ letter-spacing: 0.05em;
2795
+ font-family: ${Q};
2796
+ }
2797
+
2798
+ /* sidebar-right layout — a docked, full-height right panel */
2799
+ .vd-container { --vd-sidebar-width: 300px; }
2800
+
2801
+ .vd-container > iframe {
2802
+ width: 100%;
2803
+ height: 100%;
2804
+ border: none;
2805
+ display: block;
2806
+ }
2807
+
2808
+ /* Permanently docked panel (wide embeds, not collapsible): the viewer fills the
2809
+ remaining width instead of the panel floating over it. On narrow embeds the
2810
+ panel is a temporary tap-to-reveal overlay instead (vd-layout-sidebar-right
2811
+ without -permanent), where covering part of the model briefly is expected. */
2812
+ .vd-container.vd-layout-sidebar-permanent > iframe {
2813
+ width: calc(100% - var(--vd-sidebar-width));
2814
+ }
2815
+
2816
+ .vd-hidden { display: none !important; }
2817
+
2818
+ /* the "open configurator" toggle joins the shared bottom-right slot as a flex child;
2819
+ order pins it after AR/fullscreen regardless of DOM insertion order (MAPPING data,
2820
+ which mounts this button, always arrives before the toolbar mounts on ready). */
2821
+ .vd-configurator-toggle { order: 1; }
2822
+
2823
+ .vd-sidebar {
2824
+ position: absolute;
2825
+ inset-block: 0;
2826
+ inset-inline-end: 0;
2827
+ inline-size: var(--vd-sidebar-width);
2828
+ z-index: 11;
2829
+ display: flex;
2830
+ flex-direction: column;
2831
+ background: color-mix(in oklch, ${P.white} 90%, transparent);
2832
+ backdrop-filter: blur(10px);
2833
+ border-inline-start: 1px solid color-mix(in oklch, ${P.neutral[900]} 8%, transparent);
2834
+ box-shadow: -${F[1]} 0 ${F[4]} color-mix(in oklch, ${P.neutral[900]} 5%, transparent);
2835
+ }
2836
+
2837
+ @media (prefers-reduced-motion: no-preference) {
2838
+ .vd-sidebar { transition: transform 0.18s ease; }
2839
+ }
2840
+
2841
+ .vd-sidebar-collapsed { transform: translateX(100%); }
2842
+
2843
+ .vd-sidebar-header {
2844
+ display: flex;
2845
+ justify-content: flex-end;
2846
+ padding: ${F[1.5]} ${F[1.5]} 0;
2847
+ }
2848
+
2849
+ .vd-sidebar-body {
2850
+ flex: 1;
2851
+ overflow-y: auto;
2852
+ padding: ${F[4]};
2853
+ }
2854
+
2855
+ /* attributes inside the sidebar: flat and full-width — the panel provides the frosting */
2856
+ .vd-attributes.vd-attributes-sidebar {
2857
+ background: transparent;
2858
+ backdrop-filter: none;
2859
+ border: none;
2860
+ border-radius: 0;
2861
+ padding: 0;
2862
+ box-shadow: none;
2863
+ gap: ${F[5]};
2864
+ }
2865
+
2866
+ /* while the panel is open, keep the corner controls clear of it */
2867
+ .vd-layout-sidebar-right .vd-slot-top-right { inset-inline-end: calc(var(--vd-sidebar-width) + ${F[3]}); }
2868
+ .vd-layout-sidebar-right .vd-slot-bottom-right { inset-inline-end: calc(var(--vd-sidebar-width) + ${F[3]}); }
2869
+
2870
+ /* Price + CTA — sit under the attribute controls, separated by a hairline */
2871
+ .vd-commerce {
2872
+ display: flex;
2873
+ flex-direction: column;
2874
+ gap: ${F[2]};
2875
+ padding-block-start: ${F[3]};
2876
+ border-block-start: 1px solid color-mix(in oklch, ${P.neutral[900]} 8%, transparent);
2877
+ font-family: ${Q};
2878
+ }
2879
+
2880
+ .vd-commerce-price {
2881
+ display: flex;
2882
+ align-items: baseline;
2883
+ gap: ${F[1.5]};
2884
+ }
2885
+
2886
+ .vd-price {
2887
+ font-size: ${I.fontSize.lg};
2888
+ font-weight: ${I.fontWeight.semibold};
2889
+ color: ${P.neutral[900]};
2890
+ }
2891
+
2892
+ .vd-vat {
2893
+ font-size: ${I.fontSize.xs};
2894
+ color: ${P.neutral[500]};
2895
+ }
2896
+
2897
+ .vd-cta {
2898
+ inline-size: 100%;
2899
+ justify-content: center;
2900
+ }
2901
+
2902
+ /* Failed submit is the one place red belongs: a clear, recoverable error state. */
2903
+ .vd-commerce[data-submit="failed"] .vd-cta {
2904
+ background: ${P.semantic.error};
2905
+ border-color: ${P.semantic.error};
2906
+ color: ${P.white};
2907
+ }
2908
+
2909
+ `;
2910
+ function ar() {
2911
+ if (_n(), document.getElementById("vd-styles") !== null) return;
2912
+ let e = document.createElement("style");
2913
+ e.id = "vd-styles", e.textContent = ir, document.head.appendChild(e);
2914
+ }
2915
+ // ../client-core/src/ui/standard-interface.ts
2916
+ function or(e, t) {
2917
+ let n = document.createElement("a");
2918
+ n.href = `data:image/png;base64,${e}`, n.download = t, n.click();
2919
+ }
2920
+ var sr = class {
2921
+ options;
2922
+ settings = {};
2923
+ container = null;
2924
+ loading = null;
2925
+ slots = {
2926
+ topRight: null,
2927
+ bottomRight: null
2928
+ };
2929
+ screenshotBtn = null;
2930
+ actionBar = null;
2931
+ cameraControls = null;
2932
+ attributes = null;
2933
+ groupHandles = /* @__PURE__ */ new Map();
2934
+ lastSelectable = null;
2935
+ layout = null;
2936
+ commerce = null;
2937
+ constructor(e) {
2938
+ this.options = e;
2939
+ }
2940
+ attach(e) {
2941
+ ar(), this.container = e, this.loading = new An(e), this.slots.topRight = this.createSlot(e, "vd-slot-top-right"), this.slots.bottomRight = this.createSlot(e, "vd-slot-bottom-right");
2942
+ }
2943
+ onReady() {
2944
+ this.loading?.hide(), this.loading = null, this.mountToolbar(), this.cameraControls !== null && (this.cameraControls.element.style.display = "flex"), this.layout?.setVisible(!0);
2945
+ }
2946
+ mountToolbar() {
2947
+ if (this.actionBar !== null || this.slots.bottomRight === null || this.container === null) return;
2948
+ let e = typeof this.container.requestFullscreen == "function" && document.fullscreenEnabled !== !1;
2949
+ this.actionBar = new jn({
2950
+ onAr: this.options.onAr,
2951
+ onFullscreen: e ? () => {
2952
+ this.toggleFullscreen();
2953
+ } : void 0,
2954
+ onAnnotationsToggle: this.options.onAnnotationsToggle
2955
+ }), this.actionBar.applySettings(this.settings), this.slots.bottomRight.appendChild(this.actionBar.element), this.syncScreenshotButton(), this.placeScreenshotButton();
2956
+ }
2957
+ toggleFullscreen() {
2958
+ this.container !== null && (document.fullscreenElement === null ? (this.options.onFullscreenEnter?.(), this.container.requestFullscreen().catch((e) => {
2959
+ A.warn("Fullscreen request refused", { error: e });
2960
+ })) : document.exitFullscreen());
2961
+ }
2962
+ updateProgress(e, t) {
2963
+ this.loading?.updateProgress(e, t);
2964
+ }
2965
+ showError(e) {
2966
+ this.loading?.showError(e);
2967
+ }
2968
+ viewerAlive() {
2969
+ this.loading?.viewerAlive();
2970
+ }
2971
+ applyViewerSettings(e) {
2972
+ this.settings = e, e.language !== void 0 && ye(e.language, { reload: !1 }), this.syncScreenshotButton(), this.actionBar?.applySettings(e), this.cameraControls !== null && (this.cameraControls.element.style.display = e.cameraControls === !0 ? "flex" : "none"), this.placeScreenshotButton();
2973
+ }
2974
+ renderModelControls(e, t) {
2975
+ if (this.container !== null && this.slots.topRight !== null && this.slots.bottomRight !== null && (this.cameraControls?.element.remove(), this.cameraControls = null, this.teardownConfigurator(), this.settings.cameraControls === !0 && (this.cameraControls = new Mn(this.options.cameraApi, this.options.cameraPositionApi), this.cameraControls.element.style.display = "none", this.slots.topRight.appendChild(this.cameraControls.element)), this.placeScreenshotButton(), t.length !== 0)) {
2976
+ this.attributes = document.createElement("div"), this.attributes.className = "vd-attributes", this.groupHandles = /* @__PURE__ */ new Map();
2977
+ for (let n of t) {
2978
+ let t = Kn(n, e, this.options.eventBus, this.options.onSelect, this.options.onSelectTransient, this.options.onSubscribe);
2979
+ this.groupHandles.set(n.name, t), this.attributes.appendChild(t.element);
2980
+ }
2981
+ this.commerce = new Xn(this.options.configuration), this.attributes.appendChild(this.commerce.element), this.layout = rr(this.settings.configuratorLayout, this.slots.bottomRight), this.layout.attach(this.container, this.attributes), this.lastSelectable !== null && this.applySelectable(this.lastSelectable);
2982
+ }
2983
+ }
2984
+ applySelectable(e) {
2985
+ this.lastSelectable = e;
2986
+ for (let [t, n] of this.groupHandles) {
2987
+ let r = e.find((e) => e.name === t);
2988
+ n.setSelectable(r?.selectable ?? null), n.setHidden(r?.hidden === !0);
2989
+ }
2990
+ }
2991
+ get viewerSettings() {
2992
+ return this.settings;
2993
+ }
2994
+ teardownConfigurator() {
2995
+ this.commerce?.destroy(), this.commerce = null, this.attributes?.remove(), this.attributes = null, this.groupHandles = /* @__PURE__ */ new Map(), this.layout?.destroy(), this.layout = null;
2996
+ }
2997
+ destroy() {
2998
+ this.loading?.destroy(), this.screenshotBtn?.element.remove(), this.actionBar?.destroy(), this.actionBar?.element.remove(), this.cameraControls?.element.remove(), this.teardownConfigurator();
2999
+ for (let e of Object.values(this.slots)) e?.remove();
3000
+ this.container = null;
3001
+ }
3002
+ createSlot(e, t) {
3003
+ let n = document.createElement("div");
3004
+ return n.className = t, e.appendChild(n), n;
3005
+ }
3006
+ syncScreenshotButton() {
3007
+ let e = this.settings.screenshot === !0;
3008
+ e && this.screenshotBtn === null && (this.screenshotBtn = new N({
3009
+ variant: "outline",
3010
+ size: "icon",
3011
+ label: Ht(),
3012
+ icon: M.screenshot(),
3013
+ onClick: () => {
3014
+ this.takeScreenshot();
3015
+ }
3016
+ })), !e && this.screenshotBtn !== null && (this.screenshotBtn.element.remove(), this.screenshotBtn = null);
3017
+ }
3018
+ takeScreenshot() {
3019
+ let e = this.screenshotBtn;
3020
+ e === null || e.element.disabled || (e.element.disabled = !0, this.options.snapshotApi.take("snapshot.png").onDeveloped(({ data: t, filename: n }) => {
3021
+ e.element.disabled = !1, or(t, n);
3022
+ }).onError((t) => {
3023
+ e.element.disabled = !1, A.warn("Screenshot failed", { error: t });
3024
+ }));
3025
+ }
3026
+ placeScreenshotButton() {
3027
+ this.screenshotBtn !== null && this.slots.topRight !== null && (this.cameraControls === null ? this.slots.topRight.appendChild(this.screenshotBtn.element) : this.cameraControls.element.appendChild(this.screenshotBtn.element));
3028
+ }
3029
+ }, cr = class {
3030
+ defaultServer = "https://server.virtualdisplay.io";
3031
+ options;
3032
+ serverHost;
3033
+ extraParams;
3034
+ constructor(e, t, n) {
3035
+ this.options = e, this.serverHost = t, this.extraParams = n;
3036
+ }
3037
+ create() {
3038
+ let e = this.createContainer(), t = this.createIframe();
3039
+ return e.appendChild(t), this.attachToParent(e), {
3040
+ container: e,
3041
+ iframe: t
699
3042
  };
700
- p.debug("Sending config", t), this.send(t);
701
3043
  }
702
- }, L = class {
3044
+ createContainer() {
3045
+ let e = document.createElement("div");
3046
+ return e.className = "vd-container", Object.assign(e.style, {
3047
+ position: "relative",
3048
+ width: "100%",
3049
+ height: "100%",
3050
+ overflow: "hidden"
3051
+ }), e;
3052
+ }
3053
+ createIframe() {
3054
+ let e = document.createElement("iframe");
3055
+ return e.src = this.buildUrl(), e.setAttribute("allow", "xr-spatial-tracking; fullscreen"), e;
3056
+ }
3057
+ attachToParent(e) {
3058
+ this.getParentElement().appendChild(e);
3059
+ }
3060
+ buildUrl() {
3061
+ let e = this.getServerUrl(), t = new URL(e);
3062
+ this.setAddressParams(t), this.options.language !== void 0 && t.searchParams.set("language", this.options.language), this.options.session !== void 0 && t.searchParams.set("session", this.options.session), this.options.debug === !0 && t.searchParams.set("debug", "true"), this.options.traceparent !== void 0 && t.searchParams.set("traceparent", this.options.traceparent);
3063
+ for (let [e, n] of Object.entries(this.extraParams ?? {})) t.searchParams.set(e, n);
3064
+ return t.toString();
3065
+ }
3066
+ setAddressParams(e) {
3067
+ this.options.app === void 0 ? (e.searchParams.set("license", this.options.license), e.searchParams.set("model", this.options.model)) : (e.searchParams.set("app", this.options.app), (this.options.license !== void 0 || this.options.model !== void 0) && A.warn("Both `app` and `license`/`model` were supplied: `app` wins, the pair was ignored"));
3068
+ }
3069
+ getServerUrl() {
3070
+ return this.serverHost === void 0 ? this.defaultServer : this.serverHost;
3071
+ }
3072
+ getParentElement() {
3073
+ if (typeof this.options.parent != "string") return this.options.parent;
3074
+ let e = document.querySelector(this.options.parent);
3075
+ if (e === null) throw w.parentNotFound(this.options.parent);
3076
+ return e;
3077
+ }
3078
+ };
3079
+ // ../client-core/src/client/incoming-message-routing.ts
3080
+ function lr(e) {
3081
+ let { messenger: n } = e;
3082
+ n.on(t.HANDSHAKE, (t) => {
3083
+ l(t) && (A.info("Server handshake received, flushing publisher queue"), n.markReady(), e.ui.viewerAlive());
3084
+ }), n.on(t.SESSION, (t) => {
3085
+ u(t) && e.onSession(t.sessionId);
3086
+ }), n.on(t.MUTATION, (t) => {
3087
+ d(t) && (e.attributes.handleMutationResult(t), e.configuration.handleSelectionChanged(e.attributes.getSelection()));
3088
+ }), n.on(t.SNAPSHOT, (t) => {
3089
+ f(t) && e.snapshot.handleSnapshotResult(t);
3090
+ }), n.on(t.CAMERA, (t) => {
3091
+ p(t) && e.cameraPosition.handlePositionUpdate(t);
3092
+ }), n.on(t.LOADING_INDICATOR, (t) => {
3093
+ ne(t) && (t.error === void 0 ? e.ui.updateProgress(t.progress, t.phase) : e.ui.showError(t.error));
3094
+ }), n.on(t.CONFIG, (t) => {
3095
+ ie(t) && e.applyConfig(t);
3096
+ }), n.on(t.MAPPING, (t) => {
3097
+ te(t) && (e.attributes.handleMappingReceived(t), e.configuration.handleSelectionChanged(e.attributes.getSelection()), e.ui.renderModelControls(e.attributes, t.attributes), e.readyStateTracker.handleMappingReceived());
3098
+ }), ur(e);
3099
+ }
3100
+ function ur(e) {
3101
+ let { messenger: n } = e;
3102
+ n.on(t.FOCUS, (t) => {
3103
+ ae(t) && e.focus.handleStateReceived(t);
3104
+ }), n.on(t.PRICING, (t) => {
3105
+ oe(t) && e.configuration.handlePricingReceived(t.pricing);
3106
+ }), n.on(t.SYNC, (t) => {
3107
+ m(t) && e.onSync(t.connected);
3108
+ }), n.on(t.SELECTABLE, (t) => {
3109
+ re(t) && (e.selectable.handleSnapshot(t), e.ui.applySelectable(t.attributes));
3110
+ }), n.on(t.APP_RESOLVED, (t) => {
3111
+ ee(t) && e.onAppResolved(t);
3112
+ });
3113
+ }
3114
+ // ../client-core/src/client/ready-state-tracker.ts
3115
+ var dr = class {
3116
+ ready = !1;
3117
+ onReady;
3118
+ constructor(e) {
3119
+ this.onReady = e;
3120
+ }
3121
+ handleMappingReceived() {
3122
+ this.ready || (this.ready = !0, A.info("Mapping received from server, client is now ready"), this.onReady?.());
3123
+ }
3124
+ get isReady() {
3125
+ return this.ready;
3126
+ }
3127
+ clear() {
3128
+ this.ready = !1;
3129
+ }
3130
+ };
3131
+ // ../client-core/src/client/base-client.ts
3132
+ function fr(e) {
3133
+ if (e === void 0) return;
3134
+ let { annotations: t, ...n } = e;
3135
+ return t === void 0 ? n : t === !0 ? {
3136
+ ...n,
3137
+ annotations: "show"
3138
+ } : t === !1 ? {
3139
+ ...n,
3140
+ annotations: "hide"
3141
+ } : {
3142
+ ...n,
3143
+ annotations: t
3144
+ };
3145
+ }
3146
+ function pr(e) {
3147
+ if (!Array.isArray(e)) return {};
3148
+ let t = e.filter((e) => typeof e == "string");
3149
+ return t.length > 0 ? { features: t } : {};
3150
+ }
3151
+ var mr = class {
703
3152
  attributes;
3153
+ selectableState;
704
3154
  readyStateTracker;
705
3155
  messenger;
706
- viewerService;
707
3156
  cameraApi;
708
3157
  cameraPositionApi;
3158
+ focusApi;
3159
+ configurationApi;
709
3160
  snapshotApi;
3161
+ annotationsApi;
3162
+ eventBus;
3163
+ ui;
710
3164
  options;
3165
+ serverHost;
3166
+ iframeParams;
3167
+ arActivation;
3168
+ syncTracker;
711
3169
  iframe;
3170
+ container;
712
3171
  readyCallbacks = [];
713
3172
  mutationFailedCallbacks = [];
714
- constructor(e) {
715
- this.options = e, this.initializeLogging(), this.messenger = new A(() => this.iframe?.contentWindow ?? null);
716
- let t = (e) => {
3173
+ viewerSessionId;
3174
+ resolvedApp;
3175
+ appResolvedCallbacks = [];
3176
+ constructor(e, t) {
3177
+ this.options = e;
3178
+ let r = t ?? {};
3179
+ this.serverHost = r.serverHost, this.iframeParams = r.iframeParams, this.initializeLogging(), this.messenger = new ce(() => this.iframe?.contentWindow ?? null, {
3180
+ onHandlerError: (e, t) => A.error(`Error handling ${e.type}`, t),
3181
+ ...r.messengerOptions
3182
+ });
3183
+ let i = (e) => {
717
3184
  this.messenger.send(e);
718
3185
  };
719
- this.attributes = new j(t, (e, t) => {
720
- for (let n of this.mutationFailedCallbacks) n({
721
- attribute: e,
722
- value: t
723
- });
724
- }), this.readyStateTracker = new g(() => {
725
- let e = this.readyCallbacks;
726
- this.readyCallbacks = [];
727
- for (let t of e) t(this);
728
- }), this.viewerService = new I(t), this.cameraApi = new M(t), this.cameraPositionApi = new N(), this.snapshotApi = new F(t), this.registerHandlers(), this.messenger.start(), this.createAndConnectIframe();
729
- }
730
- registerHandlers() {
731
- this.messenger.on(b.READY, (e) => {
732
- C(e) && (p.info("Server ready — flushing publisher queue"), this.messenger.markReady(), this.viewerService.sendInitialConfig(this.options));
733
- }), this.messenger.on(b.MUTATION, (e) => {
734
- w(e) && (this.attributes.handleMutationResult(e), this.readyStateTracker.handleMutationResult(e));
735
- }), this.messenger.on(b.SNAPSHOT, (e) => {
736
- T(e) && this.snapshotApi.handleSnapshotResult(e);
737
- }), this.messenger.on(b.CAMERA, (e) => {
738
- E(e) && this.cameraPositionApi.handlePositionUpdate(e);
739
- }), this.messenger.on(b.MAPPING, (e) => {
740
- D(e) && (this.attributes.handleMappingReceived(e), this.readyStateTracker.handleMappingReceived());
3186
+ this.attributes = new ct(i, n, (e, t) => {
3187
+ this.notifyMutationFailed(e, t);
3188
+ }), this.selectableState = new lt(), this.snapshotApi = new yt(i), this.annotationsApi = new ue(i, n), this.cameraApi = new ut(i), this.cameraPositionApi = new dt(), this.focusApi = new gt(i, (e) => {
3189
+ this.dispatchFocusChange(e);
3190
+ }, n), this.configurationApi = new ht((e) => {
3191
+ this.dispatchConfigurationChange(e);
3192
+ }, (e) => {
3193
+ this.dispatchConfigurationSubmit(e);
3194
+ }), this.eventBus = new bt(), this.arActivation = r.createArActivation?.(i), this.syncTracker = r.createSyncTracker?.(), this.readyStateTracker = new dr(() => {
3195
+ this.notifyReady();
3196
+ }), this.ui = this.createStandardInterface(), this.wireIncomingMessages(), r.registerHandlers?.(this.messenger, i), this.messenger.start(), this.mountIframe();
3197
+ }
3198
+ createStandardInterface() {
3199
+ let e = this.arActivation;
3200
+ return new sr({
3201
+ snapshotApi: this.snapshotApi,
3202
+ cameraApi: this.cameraApi,
3203
+ cameraPositionApi: this.cameraPositionApi,
3204
+ eventBus: this.eventBus,
3205
+ configuration: this.configurationApi,
3206
+ onSelect: (e, t) => this.attributes.select(e, t),
3207
+ onSelectTransient: (e, t) => this.attributes.selectTransient(e, t),
3208
+ onSubscribe: (e, t) => this.attributes.onChange(e, t),
3209
+ ...e !== void 0 && { onAr: () => {
3210
+ e.activate();
3211
+ } },
3212
+ onFullscreenEnter: () => {
3213
+ this.trackFullscreen();
3214
+ },
3215
+ onAnnotationsToggle: (e) => {
3216
+ this.sendControlAnnotationToggle(e);
3217
+ }
741
3218
  });
742
3219
  }
743
- initializeLogging() {
744
- this.options.debug === !0 && m(), p.info("Initializing Virtualdisplay client", {
745
- model: this.options.model,
746
- debug: this.options.debug,
747
- language: this.options.language
3220
+ sendControlAnnotationToggle(e) {
3221
+ this.sendAnnotationToggle(e, "control");
3222
+ }
3223
+ sendAnnotationToggle(e, n) {
3224
+ this.messenger.send({
3225
+ type: t.ANNOTATION_TOGGLE,
3226
+ enabled: e,
3227
+ ...n !== void 0 && { origin: n }
748
3228
  });
749
3229
  }
750
- createAndConnectIframe() {
751
- p.debug("Creating iframe for Virtualdisplay viewer"), this.iframe = new n(this.options).create(), p.debug("Iframe created, waiting for server READY");
3230
+ wireIncomingMessages() {
3231
+ lr({
3232
+ messenger: this.messenger,
3233
+ attributes: this.attributes,
3234
+ selectable: this.selectableState,
3235
+ configuration: this.configurationApi,
3236
+ snapshot: this.snapshotApi,
3237
+ cameraPosition: this.cameraPositionApi,
3238
+ focus: this.focusApi,
3239
+ ui: this.ui,
3240
+ readyStateTracker: this.readyStateTracker,
3241
+ applyConfig: (e) => {
3242
+ this.handleConfig(e);
3243
+ },
3244
+ onSession: (e) => {
3245
+ this.handleSession(e);
3246
+ },
3247
+ onSync: (e) => {
3248
+ this.syncTracker?.handleSync(e);
3249
+ },
3250
+ onAppResolved: (e) => {
3251
+ this.handleAppResolved({
3252
+ appType: e.appType,
3253
+ name: e.name,
3254
+ ...e.slug !== void 0 && { slug: e.slug },
3255
+ ...e.submit !== void 0 && { submit: e.submit },
3256
+ ...pr(e.features)
3257
+ });
3258
+ }
3259
+ });
3260
+ }
3261
+ handleAppResolved(e) {
3262
+ this.resolvedApp = e;
3263
+ for (let t of this.appResolvedCallbacks) t(e);
3264
+ }
3265
+ handleSession(e) {
3266
+ this.viewerSessionId = e, this.arActivation?.onSession(e);
3267
+ }
3268
+ notifyMutationFailed(e, t) {
3269
+ for (let n of this.mutationFailedCallbacks) n({
3270
+ attribute: e,
3271
+ value: t
3272
+ });
3273
+ }
3274
+ notifyReady() {
3275
+ let e = this.readyCallbacks;
3276
+ this.readyCallbacks = [];
3277
+ for (let t of e) t(this);
3278
+ this.ui.onReady();
3279
+ }
3280
+ dispatchFocusChange(e) {
3281
+ this.container?.dispatchEvent(new CustomEvent(X.FOCUS_CHANGE, {
3282
+ detail: e,
3283
+ bubbles: !0
3284
+ }));
3285
+ }
3286
+ dispatchConfigurationChange(e) {
3287
+ this.container?.dispatchEvent(new CustomEvent(X.CONFIGURATION_CHANGE, {
3288
+ detail: e ?? null,
3289
+ bubbles: !0
3290
+ }));
3291
+ }
3292
+ dispatchConfigurationSubmit(e) {
3293
+ this.container?.dispatchEvent(new CustomEvent(X.CONFIGURATION_SUBMIT, {
3294
+ detail: e,
3295
+ bubbles: !0
3296
+ }));
3297
+ }
3298
+ handleConfig(e) {
3299
+ let t = e.viewerSettings, n = fr(this.options.viewerSettings);
3300
+ if (t !== void 0 || n !== void 0) {
3301
+ let e = n === void 0 ? t : {
3302
+ ...t ?? {},
3303
+ ...n
3304
+ };
3305
+ if (n !== void 0 && t !== void 0) {
3306
+ let e = Object.entries(n).filter(([e, n]) => t[e] !== n).map(([e, n]) => `${e} (metadata: ${t[e] ?? "undefined"}, override: ${n})`);
3307
+ e.length > 0 && A.warn(`Viewer settings override active: ${e.join(", ")}`);
3308
+ }
3309
+ this.ui.applyViewerSettings(e), this.echoAnnotationsDifference(t?.annotations, e.annotations);
3310
+ }
3311
+ this.cameraPositionApi.applyLimits(e.cameraLimits);
3312
+ }
3313
+ echoAnnotationsDifference(e, t) {
3314
+ t !== void 0 && t !== e && this.sendAnnotationToggle(t === "show" || t === "toggle-shown");
3315
+ }
3316
+ trackFullscreen() {
3317
+ this.messenger.send({ type: t.FULLSCREEN });
3318
+ }
3319
+ initializeLogging() {
3320
+ this.options.debug === !0 && ot(), A.info("Initializing Virtualdisplay client", { ...this.options.app === void 0 ? { model: this.options.model } : { app: this.options.app } });
3321
+ }
3322
+ mountIframe() {
3323
+ let e = new cr(this.options, this.serverHost, this.iframeParams).create();
3324
+ this.container = e.container, this.iframe = e.iframe, this.eventBus.attach(this.container), this.ui.attach(this.container);
3325
+ try {
3326
+ let e = new URL(this.iframe.src).origin;
3327
+ this.messenger.setTargetOrigin(e);
3328
+ } catch {}
752
3329
  }
753
3330
  select(e, t) {
754
3331
  this.attributes.select(e, t);
@@ -769,30 +3346,137 @@ var j = class {
769
3346
  return this.readyStateTracker.isReady;
770
3347
  }
771
3348
  onReady(e) {
772
- if (this.readyStateTracker.isReady) {
773
- e(this);
774
- return;
775
- }
776
- this.readyCallbacks.push(e);
3349
+ this.readyStateTracker.isReady ? e(this) : this.readyCallbacks.push(e);
777
3350
  }
778
3351
  onMutationFailed(e) {
779
3352
  this.mutationFailedCallbacks.push(e);
780
3353
  }
781
- get viewer() {
782
- return this.viewerService;
3354
+ onSelectableChange(e) {
3355
+ this.selectableState.onChange(e);
3356
+ }
3357
+ onAppResolved(e) {
3358
+ this.appResolvedCallbacks.push(e), this.resolvedApp !== void 0 && e(this.resolvedApp);
3359
+ }
3360
+ getSelectable(e) {
3361
+ return this.selectableState.selectableFor(e);
3362
+ }
3363
+ activateAr() {
3364
+ this.arActivation?.activate();
3365
+ }
3366
+ on(e, t) {
3367
+ this.eventBus.on(e, t);
3368
+ }
3369
+ get annotations() {
3370
+ return this.annotationsApi;
783
3371
  }
784
3372
  get camera() {
785
3373
  return this.cameraApi;
786
3374
  }
3375
+ get focus() {
3376
+ return this.focusApi;
3377
+ }
3378
+ get configuration() {
3379
+ return this.configurationApi;
3380
+ }
787
3381
  get cameraPosition() {
788
3382
  return this.cameraPositionApi;
789
3383
  }
790
3384
  get snapshot() {
791
3385
  return this.snapshotApi;
792
3386
  }
3387
+ get sessionId() {
3388
+ return this.viewerSessionId;
3389
+ }
3390
+ get viewerSettings() {
3391
+ return this.ui.viewerSettings;
3392
+ }
793
3393
  destroy() {
794
- p.info("Destroying Virtualdisplay client"), this.readyStateTracker.clear(), this.readyCallbacks = [], this.messenger.destroy(), this.iframe?.parentElement !== null && this.iframe?.parentElement !== void 0 && this.iframe.parentElement.removeChild(this.iframe), this.iframe = void 0, p.debug("Virtualdisplay client destroyed");
3394
+ this.readyStateTracker.clear(), this.readyCallbacks = [], this.mutationFailedCallbacks.length = 0, this.snapshotApi.clear(), this.messenger.destroy(), this.arActivation?.destroy(), this.ui.destroy(), this.container?.parentElement !== null && this.container?.parentElement !== void 0 && this.container.parentElement.removeChild(this.container), this.container = void 0, this.iframe = void 0;
3395
+ }
3396
+ }, hr = "ar.virtualdisplay.io";
3397
+ function gr(e) {
3398
+ let t = new URL(`https://${hr}/go`);
3399
+ return e.sessionId === void 0 ? e.app === void 0 ? e.license !== void 0 && e.model !== void 0 && (t.searchParams.set("license", e.license), t.searchParams.set("model", e.model)) : t.searchParams.set("app", e.app) : t.searchParams.set("session", e.sessionId), t.toString();
3400
+ }
3401
+ // src/ar/ar-activation.ts
3402
+ var _r = 1500, vr = class {
3403
+ options;
3404
+ qrModal = new yn();
3405
+ sessionId;
3406
+ waiters = [];
3407
+ pendingTimers = /* @__PURE__ */ new Set();
3408
+ destroyed = !1;
3409
+ constructor(e) {
3410
+ this.options = e;
3411
+ }
3412
+ onSession(e) {
3413
+ this.sessionId = e;
3414
+ let t = this.waiters;
3415
+ this.waiters = [];
3416
+ for (let n of t) n(e);
3417
+ }
3418
+ activate() {
3419
+ tt() ? this.options.send({ type: t.AR_ACTIVATE }) : this.showQrCode();
3420
+ }
3421
+ destroy() {
3422
+ this.destroyed = !0;
3423
+ for (let e of this.pendingTimers) clearTimeout(e);
3424
+ this.pendingTimers.clear(), this.waiters = [], this.qrModal.hide();
3425
+ }
3426
+ async showQrCode() {
3427
+ this.qrModal.show();
3428
+ let e = await this.waitForSessionId();
3429
+ if (this.destroyed) return;
3430
+ let n = gr({
3431
+ sessionId: e,
3432
+ app: this.options.app,
3433
+ license: this.options.license,
3434
+ model: this.options.model
3435
+ }), r;
3436
+ try {
3437
+ r = await this.qrModal.update(n);
3438
+ } catch (e) {
3439
+ A.warn("AR QR code could not be rendered", { error: e });
3440
+ return;
3441
+ }
3442
+ this.destroyed ? this.qrModal.hide() : r && this.options.send({ type: t.QR_SHOWN });
3443
+ }
3444
+ waitForSessionId() {
3445
+ return this.sessionId === void 0 ? new Promise((e) => {
3446
+ let t = setTimeout(() => {
3447
+ this.pendingTimers.delete(t), e(void 0);
3448
+ }, _r);
3449
+ this.pendingTimers.add(t), this.waiters.push((n) => {
3450
+ this.pendingTimers.delete(t), clearTimeout(t), e(n);
3451
+ });
3452
+ }) : Promise.resolve(this.sessionId);
3453
+ }
3454
+ }, yr = class {
3455
+ callbacks = [];
3456
+ lastKnown;
3457
+ handleSync(e) {
3458
+ this.lastKnown = e;
3459
+ for (let t of this.callbacks) t(e);
3460
+ }
3461
+ onChange(e) {
3462
+ this.callbacks.push(e), this.lastKnown !== void 0 && e(this.lastKnown);
3463
+ }
3464
+ }, br = class extends mr {
3465
+ syncStatusTracker;
3466
+ constructor(e) {
3467
+ let t = new yr();
3468
+ super(e, {
3469
+ createArActivation: (t) => new vr({
3470
+ app: e.app,
3471
+ license: e.license,
3472
+ model: e.model,
3473
+ send: t
3474
+ }),
3475
+ createSyncTracker: () => t
3476
+ }), this.syncStatusTracker = t;
3477
+ }
3478
+ onSyncChange(e) {
3479
+ this.syncStatusTracker.onChange(e);
795
3480
  }
796
3481
  };
797
- //#endregion
798
- export { v as Attribute, _ as AttributeValue, M as Camera, e as ERROR_CODES, P as Photo, F as Snapshot, L as VirtualdisplayClient, t as VirtualdisplayError, I as VirtualdisplayViewerService, m as enableDebugMode, h as isDebugMode, p as logger };
3482
+ export { a as AVAILABILITY, ue as Annotations, Le as Attribute, de as AttributeValue, ut as Camera, dt as CameraPosition, ht as Configuration, C as ERROR_CODES, gt as Focus, _t as Photo, yt as Snapshot, X as VD_EVENTS, br as VirtualdisplayClient, w as VirtualdisplayError, ot as enableDebugMode, st as isDebugMode, A as logger };