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

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 (44) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/README.md +36 -161
  3. package/dist/browser-DbgxVbZp.js +1227 -0
  4. package/dist/browser-DiwLZG-y.cjs +8 -0
  5. package/dist/chunk-350yNsax.cjs +1 -0
  6. package/dist/chunk-efA98nb6.js +13 -0
  7. package/dist/src/attributes/attributes.d.ts.map +1 -1
  8. package/dist/src/camera/position.d.ts +16 -3
  9. package/dist/src/camera/position.d.ts.map +1 -1
  10. package/dist/src/client/client.d.ts +10 -47
  11. package/dist/src/client/client.d.ts.map +1 -1
  12. package/dist/src/client/error.d.ts.map +1 -1
  13. package/dist/src/client/iframe-builder.d.ts +11 -9
  14. package/dist/src/client/iframe-builder.d.ts.map +1 -1
  15. package/dist/src/client/options.d.ts +0 -6
  16. package/dist/src/client/options.d.ts.map +1 -1
  17. package/dist/src/client/ready-state-tracker.d.ts +2 -15
  18. package/dist/src/client/ready-state-tracker.d.ts.map +1 -1
  19. package/dist/src/client/types.d.ts +14 -0
  20. package/dist/src/client/types.d.ts.map +1 -1
  21. package/dist/src/index.d.ts +2 -2
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/ui/action-bar.d.ts +17 -0
  24. package/dist/src/ui/action-bar.d.ts.map +1 -0
  25. package/dist/src/ui/camera-controls.d.ts +8 -0
  26. package/dist/src/ui/camera-controls.d.ts.map +1 -0
  27. package/dist/src/ui/controls/attribute-group.d.ts +6 -0
  28. package/dist/src/ui/controls/attribute-group.d.ts.map +1 -0
  29. package/dist/src/ui/event-bus.d.ts +12 -0
  30. package/dist/src/ui/event-bus.d.ts.map +1 -0
  31. package/dist/src/ui/loading.d.ts +43 -0
  32. package/dist/src/ui/loading.d.ts.map +1 -0
  33. package/dist/src/ui/standard-interface.d.ts +35 -0
  34. package/dist/src/ui/standard-interface.d.ts.map +1 -0
  35. package/dist/src/ui/styles.d.ts +3 -0
  36. package/dist/src/ui/styles.d.ts.map +1 -0
  37. package/dist/src/ui/toolbar.d.ts +14 -0
  38. package/dist/src/ui/toolbar.d.ts.map +1 -0
  39. package/dist/virtualdisplay.client.cjs.js +225 -1
  40. package/dist/virtualdisplay.client.es.js +1384 -186
  41. package/dist/virtualdisplay.client.umd.js +232 -1
  42. package/package.json +1 -1
  43. package/dist/src/ui/viewer-config.d.ts +0 -63
  44. package/dist/src/ui/viewer-config.d.ts.map +0 -1
@@ -1,50 +1,64 @@
1
+ import { n as e } from "./chunk-efA98nb6.js";
1
2
  //#region src/client/error.ts
2
- var e = {
3
+ var t = {
3
4
  NO_MAPPING: "NO_MAPPING",
4
5
  ATTRIBUTE_NOT_FOUND: "ATTRIBUTE_NOT_FOUND",
5
6
  VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
6
7
  PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
7
8
  INVALID_PARAMETER: "INVALID_PARAMETER",
8
9
  SNAPSHOT_FAILED: "SNAPSHOT_FAILED"
9
- }, t = class t extends Error {
10
+ }, n = class e extends Error {
10
11
  code;
11
- constructor(e, n) {
12
- super(e), this.name = "VirtualdisplayError", this.code = n, Error.captureStackTrace?.(this, t);
12
+ constructor(e, t) {
13
+ super(e), this.name = "VirtualdisplayError", this.code = t;
13
14
  }
14
15
  static noMapping() {
15
- return new t("No attributes available. Waiting for server metadata.", e.NO_MAPPING);
16
+ return new e("No attributes available. Waiting for server metadata.", t.NO_MAPPING);
16
17
  }
17
18
  static attributeNotFound(n) {
18
- return new t(`Attribute "${n}" not found in mapping`, e.ATTRIBUTE_NOT_FOUND);
19
+ return new e(`Attribute "${n}" not found in mapping`, t.ATTRIBUTE_NOT_FOUND);
19
20
  }
20
21
  static valueNotFound(n, r) {
21
- return new t(`Value "${r}" not found for attribute "${n}"`, e.VALUE_NOT_FOUND);
22
+ return new e(`Value "${r}" not found for attribute "${n}"`, t.VALUE_NOT_FOUND);
22
23
  }
23
24
  static parentNotFound(n) {
24
- return new t(`Parent element not found: ${n}`, e.PARENT_NOT_FOUND);
25
+ return new e(`Parent element not found: ${n}`, t.PARENT_NOT_FOUND);
25
26
  }
26
27
  static invalidParameter(n) {
27
- return new t(n, e.INVALID_PARAMETER);
28
+ return new e(n, t.INVALID_PARAMETER);
28
29
  }
29
30
  static snapshotFailed(n, r) {
30
- return new t(`Snapshot failed for "${n}": ${r}`, e.SNAPSHOT_FAILED);
31
+ return new e(`Snapshot failed for "${n}": ${r}`, t.SNAPSHOT_FAILED);
31
32
  }
32
- }, n = class {
33
+ }, r = class {
33
34
  defaultServer = "https://server.virtualdisplay.io";
34
35
  options;
35
36
  constructor(e) {
36
37
  this.options = e;
37
38
  }
38
39
  create() {
39
- let e = this.createIframe();
40
- return this.attachToParent(e), e;
40
+ let e = this.createContainer(), t = this.createIframe();
41
+ return e.appendChild(t), this.attachToParent(e), {
42
+ container: e,
43
+ iframe: t
44
+ };
45
+ }
46
+ createContainer() {
47
+ let e = document.createElement("div");
48
+ return e.className = "vd-container", Object.assign(e.style, {
49
+ position: "relative",
50
+ width: "100%",
51
+ height: "100%",
52
+ overflow: "hidden"
53
+ }), e;
41
54
  }
42
55
  createIframe() {
43
56
  let e = document.createElement("iframe");
44
57
  return e.src = this.buildUrl(), Object.assign(e.style, {
45
58
  width: "100%",
46
59
  height: "100%",
47
- border: "none"
60
+ border: "none",
61
+ display: "block"
48
62
  }), e.setAttribute("allow", "xr-spatial-tracking; fullscreen"), e;
49
63
  }
50
64
  attachToParent(e) {
@@ -52,7 +66,7 @@ var e = {
52
66
  }
53
67
  buildUrl() {
54
68
  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();
69
+ return t.searchParams.set("license", this.options.license), t.searchParams.set("model", this.options.model), this.options.language !== void 0 && t.searchParams.set("language", this.options.language), this.options.debug === !0 && t.searchParams.set("debug", "true"), t.toString();
56
70
  }
57
71
  getServerUrl() {
58
72
  return this.defaultServer;
@@ -60,13 +74,13 @@ var e = {
60
74
  getParentElement() {
61
75
  if (typeof this.options.parent != "string") return this.options.parent;
62
76
  let e = document.querySelector(this.options.parent);
63
- if (e === null) throw t.parentNotFound(this.options.parent);
77
+ if (e === null) throw n.parentNotFound(this.options.parent);
64
78
  return e;
65
79
  }
66
80
  };
67
81
  //#endregion
68
82
  //#region ../logger/src/format.ts
69
- function r(e) {
83
+ function i(e) {
70
84
  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
85
  "timestamp",
72
86
  "level",
@@ -77,33 +91,33 @@ function r(e) {
77
91
  ], o = Object.entries(e).filter(([e]) => !a.includes(e)).map(([e, t]) => `${e}=${JSON.stringify(t)}`).join(" ");
78
92
  return `${t} ${n} [${r}] ${i}${o ? " " + o : ""}`;
79
93
  }
80
- function i(e) {
94
+ function a(e) {
81
95
  return JSON.stringify(e);
82
96
  }
83
97
  //#endregion
84
98
  //#region ../logger/src/write.ts
85
- function a(e, t) {
86
- let n = t ? r(e) : i(e);
99
+ function o(e, t) {
100
+ let n = t ? i(e) : a(e);
87
101
  e.level === "error" ? console.error(n) : e.level === "warn" ? console.warn(n) : console.log(n);
88
102
  }
89
103
  //#endregion
90
104
  //#region ../utils/src/date.ts
91
- function o() {
105
+ function s() {
92
106
  return /* @__PURE__ */ new Date();
93
107
  }
94
- function s(e) {
108
+ function c(e) {
95
109
  return e.toISOString();
96
110
  }
97
111
  //#endregion
98
112
  //#region ../logger/src/logger.ts
99
- var c = class e {
113
+ var l = class e {
100
114
  constructor(e) {
101
115
  this.config = e;
102
116
  }
103
117
  log(e, t, n = {}) {
104
118
  let r = this.config.traceProvider?.() ?? {};
105
- a({
106
- timestamp: s(o()),
119
+ o({
120
+ timestamp: c(s()),
107
121
  level: e,
108
122
  msg: t,
109
123
  service: this.config.service,
@@ -133,8 +147,8 @@ var c = class e {
133
147
  }).bind(this), n;
134
148
  }
135
149
  };
136
- function l(e, t) {
137
- return new c({
150
+ function u(e, t) {
151
+ return new l({
138
152
  service: e,
139
153
  isDev: t?.isDev === void 0 ? typeof process < "u" ? process.env.NODE_ENV !== "production" : !0 : t.isDev,
140
154
  traceProvider: t?.traceProvider
@@ -142,37 +156,34 @@ function l(e, t) {
142
156
  }
143
157
  //#endregion
144
158
  //#region src/utils/logger.ts
145
- function u(e) {
159
+ function d(e) {
146
160
  if (e !== void 0) return typeof e == "object" && e && !Array.isArray(e) && !(e instanceof Error) ? e : e instanceof Error ? { error: e.message } : { value: e };
147
161
  }
148
- function d(e) {
162
+ function f(e) {
149
163
  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))
164
+ debug: (t, n) => e.debug(t, d(n)),
165
+ info: (t, n) => e.info(t, d(n)),
166
+ warn: (t, n) => e.warn(t, d(n)),
167
+ error: (t, n) => e.error(t, d(n))
154
168
  };
155
169
  }
156
- var f = !1, p = d(l("client", { isDev: !1 }));
157
- function m() {
158
- f = !0, p = d(l("client", { isDev: !0 }));
159
- }
170
+ var p = !1, m = f(u("client", { isDev: !1 }));
160
171
  function h() {
161
- return f;
172
+ p = !0, m = f(u("client", { isDev: !0 }));
173
+ }
174
+ function g() {
175
+ return p;
162
176
  }
163
177
  //#endregion
164
178
  //#region src/client/ready-state-tracker.ts
165
- var g = class {
179
+ var _ = class {
166
180
  ready = !1;
167
181
  onReady;
168
182
  constructor(e) {
169
183
  this.onReady = e;
170
184
  }
171
185
  handleMappingReceived() {
172
- this.markReady("Mapping received from server");
173
- }
174
- handleMutationResult(e) {
175
- e.isInitial && this.markReady("Initial mutations processed");
186
+ this.ready || (this.ready = !0, m.info("Mapping received from server, client is now ready"), this.onReady?.());
176
187
  }
177
188
  get isReady() {
178
189
  return this.ready;
@@ -180,10 +191,7 @@ var g = class {
180
191
  clear() {
181
192
  this.ready = !1;
182
193
  }
183
- markReady(e) {
184
- this.ready || (this.ready = !0, p.info(`${e} - client is now ready`), this.onReady?.());
185
- }
186
- }, _ = class {
194
+ }, v = class {
187
195
  isSelectedValue = !1;
188
196
  constructor(e, t) {
189
197
  this.value = e, this.attribute = t;
@@ -194,7 +202,7 @@ var g = class {
194
202
  setSelected(e) {
195
203
  this.isSelectedValue = e;
196
204
  }
197
- }, v = class {
205
+ }, ee = class {
198
206
  name;
199
207
  defaultValue;
200
208
  values;
@@ -204,7 +212,7 @@ var g = class {
204
212
  this.name = e.name, this.defaultValue = e.defaultValue, this.onSelect = e.onSelect, this.values = this.createValues(e.values);
205
213
  }
206
214
  select(e) {
207
- if (!this.values.has(e)) throw t.valueNotFound(this.name, e);
215
+ if (!this.values.has(e)) throw n.valueNotFound(this.name, e);
208
216
  this.onSelect(this.name, e);
209
217
  }
210
218
  getValues() {
@@ -227,17 +235,17 @@ var g = class {
227
235
  createValues(e) {
228
236
  let t = /* @__PURE__ */ new Map();
229
237
  for (let n of e) {
230
- let e = new _(n, this.name);
238
+ let e = new v(n, this.name);
231
239
  n === this.defaultValue && e.setSelected(!0), t.set(n, e);
232
240
  }
233
241
  return t;
234
242
  }
235
- }, y = class {
243
+ }, te = class {
236
244
  attributes = /* @__PURE__ */ new Map();
237
245
  constructor(e, t, n) {
238
246
  for (let n of e) {
239
247
  let e = this.buildOptions(n, t);
240
- this.attributes.set(n.name, new v(e));
248
+ this.attributes.set(n.name, new ee(e));
241
249
  }
242
250
  this.onMutationFailed = n;
243
251
  }
@@ -278,46 +286,57 @@ var g = class {
278
286
  defaultValue: e.defaultValue
279
287
  };
280
288
  }
281
- }, b = {
282
- READY: "ready",
289
+ }, y = {
290
+ HANDSHAKE: "handshake",
283
291
  MUTATION: "mutation",
284
292
  STATE: "state",
285
- CONFIG: "config",
293
+ LOADING_INDICATOR: "loading_indicator",
286
294
  CAMERA: "camera",
287
295
  SNAPSHOT: "snapshot",
288
- MAPPING: "mapping"
289
- }, x = new Set(Object.values(b));
290
- function S(e) {
296
+ MAPPING: "mapping",
297
+ AR_ACTIVATE: "ar_activate",
298
+ CONFIG: "config",
299
+ INTERACTION: "interaction"
300
+ }, ne = new Set(Object.values(y));
301
+ function re(e) {
291
302
  if (typeof e != "object" || !e) return !1;
292
303
  let t = e;
293
- return typeof t.target == "string" ? !1 : typeof t.type == "string" && x.has(t.type);
304
+ return typeof t.target == "string" ? !1 : typeof t.type == "string" && ne.has(t.type);
294
305
  }
295
- function C(e) {
296
- let t = O(e);
297
- return t !== null && t.type === b.READY;
306
+ function ie(e) {
307
+ let t = b(e);
308
+ return t !== null && t.type === y.HANDSHAKE;
298
309
  }
299
- function w(e) {
300
- let t = O(e);
301
- return t !== null && t.type === b.MUTATION && Array.isArray(t.results);
310
+ function ae(e) {
311
+ let t = b(e);
312
+ return t !== null && t.type === y.MUTATION && Array.isArray(t.results);
302
313
  }
303
- function T(e) {
304
- let t = O(e);
305
- return t !== null && t.type === b.SNAPSHOT && ("data" in t || "error" in t);
314
+ function oe(e) {
315
+ let t = b(e);
316
+ return t !== null && t.type === y.SNAPSHOT && ("data" in t || "error" in t);
317
+ }
318
+ function se(e) {
319
+ let t = b(e);
320
+ return t !== null && t.type === y.CAMERA && Array.isArray(t.transforms);
306
321
  }
307
- function E(e) {
308
- let t = O(e);
309
- return t !== null && t.type === b.CAMERA && Array.isArray(t.transforms);
322
+ function ce(e) {
323
+ let t = b(e);
324
+ return t !== null && t.type === y.MAPPING && Array.isArray(t.attributes);
310
325
  }
311
- function D(e) {
312
- let t = O(e);
313
- return t !== null && t.type === b.MAPPING && Array.isArray(t.attributes);
326
+ function le(e) {
327
+ let t = b(e);
328
+ return t !== null && t.type === y.LOADING_INDICATOR && typeof t.progress == "number";
314
329
  }
315
- function O(e) {
330
+ function ue(e) {
331
+ let t = b(e);
332
+ return t !== null && t.type === y.CONFIG && typeof t.viewerSettings == "object";
333
+ }
334
+ function b(e) {
316
335
  return typeof e != "object" || !e ? null : e;
317
336
  }
318
337
  //#endregion
319
338
  //#region ../messaging/src/queue.ts
320
- var k = class {
339
+ var de = class {
321
340
  pending = [];
322
341
  isReady = !1;
323
342
  constructor(e) {
@@ -348,7 +367,7 @@ var k = class {
348
367
  clear() {
349
368
  this.pending.length = 0;
350
369
  }
351
- }, A = class {
370
+ }, fe = class {
352
371
  handlers = /* @__PURE__ */ new Map();
353
372
  publisher;
354
373
  consumer;
@@ -356,7 +375,7 @@ var k = class {
356
375
  listener = null;
357
376
  sourceId;
358
377
  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();
378
+ this.getTarget = e, this.sourceId = `vd-${Math.random().toString(36).slice(2, 8)}`, this.publisher = new de((e) => this.post(e)), this.consumer = new de((e) => this.routeToHandler(e)), this.shouldBuffer = t?.shouldBuffer ?? null, t?.bufferIncoming !== !0 && this.consumer.markReady();
360
379
  }
361
380
  on(e, t) {
362
381
  this.handlers.set(e, t);
@@ -379,7 +398,7 @@ var k = class {
379
398
  start() {
380
399
  this.listener === null && (this.listener = (e) => {
381
400
  let { data: t } = e;
382
- if (!S(t) || t._source === this.sourceId) return;
401
+ if (!re(t) || t._source === this.sourceId) return;
383
402
  let n = t;
384
403
  if (this.shouldBuffer !== null && !this.shouldBuffer(n)) {
385
404
  this.routeToHandler(n);
@@ -414,19 +433,17 @@ var k = class {
414
433
  });
415
434
  }
416
435
  };
417
- new Set([b.CAMERA]);
436
+ new Set([y.CAMERA]);
418
437
  //#endregion
419
438
  //#region src/attributes/attributes.ts
420
- var j = class {
439
+ var pe = class {
421
440
  registry;
422
441
  pendingCallbacks = [];
423
442
  constructor(e, t) {
424
443
  this.send = e, this.onMutationFailed = t;
425
444
  }
426
445
  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();
446
+ m.debug("Initializing from server mapping", { attributeCount: e.attributes.length }), this.registry = new te(e.attributes, (e, t) => this.sendMutation(e, t), this.onMutationFailed), this.replayPendingCallbacks();
430
447
  }
431
448
  handleMutationResult(e) {
432
449
  this.registry?.handleResults(e.results);
@@ -453,7 +470,7 @@ var j = class {
453
470
  }
454
471
  let n = this.registry.get(e);
455
472
  if (n === void 0) {
456
- p.warn(`onChange: attribute "${e}" not found — callback will not fire`);
473
+ m.warn(`onChange: attribute "${e}" not found — callback will not fire`);
457
474
  return;
458
475
  }
459
476
  n.onChange(t);
@@ -466,7 +483,7 @@ var j = class {
466
483
  }
467
484
  sendMutation(e, t) {
468
485
  let n = {
469
- type: b.MUTATION,
486
+ type: y.MUTATION,
470
487
  mutations: e,
471
488
  isInitial: t
472
489
  };
@@ -475,11 +492,11 @@ var j = class {
475
492
  replayPendingCallbacks() {
476
493
  for (let { name: e, callback: t } of this.pendingCallbacks) {
477
494
  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);
495
+ n === void 0 && m.warn(`onChange: attribute "${e}" not found — buffered callback will not fire`), n?.onChange(t);
479
496
  }
480
497
  this.pendingCallbacks.length = 0;
481
498
  }
482
- }, M = class {
499
+ }, me = class {
483
500
  commands = [];
484
501
  constructor(e) {
485
502
  this.send = e;
@@ -503,31 +520,32 @@ var j = class {
503
520
  }), this;
504
521
  }
505
522
  set() {
506
- if (this.commands.length === 0) throw t.invalidParameter("No camera commands to execute. Call rotate(), tilt(), or zoom() before set().");
523
+ if (this.commands.length === 0) throw n.invalidParameter("No camera commands to execute. Call rotate(), tilt(), or zoom() before set().");
507
524
  let e = {
508
- type: b.CAMERA,
525
+ type: y.CAMERA,
509
526
  commands: this.commands
510
527
  };
511
528
  this.send(e), this.commands = [];
512
529
  }
513
- validateFiniteNumber(e, n) {
514
- if (!Number.isFinite(e)) throw t.invalidParameter(`${n}() requires a finite number, got ${String(e)}`);
530
+ validateFiniteNumber(e, t) {
531
+ if (!Number.isFinite(e)) throw n.invalidParameter(`${t}() requires a finite number, got ${String(e)}`);
515
532
  }
516
533
  reset() {
517
534
  let e = {
518
- type: b.CAMERA,
535
+ type: y.CAMERA,
519
536
  commands: [{ action: "reset" }]
520
537
  };
521
538
  this.send(e);
522
539
  }
523
- }, N = class {
540
+ }, he = class {
524
541
  state;
542
+ limitsState;
525
543
  changeCallbacks = [];
526
544
  constructor() {
527
545
  this.state = {
528
546
  rotation: 0,
529
547
  tilt: 0,
530
- zoom: 0
548
+ zoom: 100
531
549
  };
532
550
  }
533
551
  get rotation() {
@@ -542,6 +560,18 @@ var j = class {
542
560
  onChange(e) {
543
561
  this.changeCallbacks.push(e);
544
562
  }
563
+ get limits() {
564
+ return this.limitsState;
565
+ }
566
+ applyLimits(e) {
567
+ this.limitsState = e;
568
+ let t = {
569
+ rotation: this.rotation,
570
+ tilt: this.tilt,
571
+ zoom: this.zoom
572
+ };
573
+ for (let e of this.changeCallbacks) e(t);
574
+ }
545
575
  handlePositionUpdate(e) {
546
576
  this.update(e.transforms);
547
577
  }
@@ -568,7 +598,7 @@ var j = class {
568
598
  for (let t of this.changeCallbacks) t(e);
569
599
  }
570
600
  }
571
- }, P = class {
601
+ }, ge = class {
572
602
  developedCallbacks = /* @__PURE__ */ new Set();
573
603
  errorCallbacks = /* @__PURE__ */ new Set();
574
604
  developed = !1;
@@ -596,159 +626,1324 @@ var j = class {
596
626
  t(e);
597
627
  }), this.developedCallbacks.clear(), this.errorCallbacks.clear());
598
628
  }
599
- }, F = class {
629
+ }, _e = class {
600
630
  validExtensions = [".png"];
601
631
  pendingPhotos = /* @__PURE__ */ new Map();
602
632
  constructor(e) {
603
633
  this.send = e;
604
634
  }
605
635
  take(e) {
606
- this.validateFilename(e), p.debug("Taking snapshot", { filename: e });
607
- let t = new P(e);
636
+ this.validateFilename(e), m.debug("Taking snapshot", { filename: e });
637
+ let t = new ge(e);
608
638
  return this.pendingPhotos.set(t.getFilename(), t), this.send({
609
- type: b.SNAPSHOT,
639
+ type: y.SNAPSHOT,
610
640
  filename: e
611
641
  }), t;
612
642
  }
613
- handleSnapshotResult(n) {
614
- let r = this.pendingPhotos.get(n.filename);
643
+ handleSnapshotResult(e) {
644
+ let r = this.pendingPhotos.get(e.filename);
615
645
  if (r === void 0) {
616
- p.warn("No pending photo found for filename", {
617
- filename: n.filename,
646
+ m.warn("No pending photo found for filename", {
647
+ filename: e.filename,
618
648
  pendingFilenames: Array.from(this.pendingPhotos.keys())
619
649
  });
620
650
  return;
621
651
  }
622
- if (this.pendingPhotos.delete(n.filename), n.error !== void 0) {
623
- r.fail(new t(n.error, e.SNAPSHOT_FAILED));
652
+ if (this.pendingPhotos.delete(e.filename), e.error !== void 0) {
653
+ r.fail(new n(e.error, t.SNAPSHOT_FAILED));
624
654
  return;
625
655
  }
626
- n.data !== void 0 && r.develop({
627
- filename: n.filename,
628
- data: n.data
656
+ e.data !== void 0 && r.develop({
657
+ filename: e.filename,
658
+ data: e.data
629
659
  });
630
660
  }
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);
661
+ validateFilename(e) {
662
+ if (e === "" || typeof e != "string") throw new n("Filename must be a non-empty string", t.INVALID_PARAMETER);
663
+ if (!this.validExtensions.some((t) => e.toLowerCase().endsWith(t))) throw new n(`Invalid filename: "${e}". Filename must end with one of: ${this.validExtensions.join(", ")}`, t.INVALID_PARAMETER);
664
+ if (e.includes("..") || e.includes("/") || e.includes("\\")) throw new n("Filename cannot contain path separators or \"..\"", t.INVALID_PARAMETER);
635
665
  }
636
- }, I = class {
637
- send;
638
- constructor(e) {
639
- this.send = e;
666
+ }, ve = class {
667
+ container = null;
668
+ attach(e) {
669
+ this.container = e;
640
670
  }
641
- sendInitialConfig(e) {
642
- let t = this.buildUIConfig(e), n = this.buildViewerConfig(t, e.camera);
643
- n !== void 0 && this.sendConfig(n);
671
+ dispatch(e, t, n = !1) {
672
+ let r = new CustomEvent(e, {
673
+ detail: t,
674
+ cancelable: n
675
+ });
676
+ return this.container?.dispatchEvent(r), r;
644
677
  }
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 }
678
+ on(e, t) {
679
+ let n = (e) => {
680
+ let n = e;
681
+ t({
682
+ ...n.detail,
683
+ preventDefault: () => {
684
+ n.preventDefault();
685
+ }
686
+ });
651
687
  };
688
+ return this.container?.addEventListener(e, n), () => {
689
+ this.container?.removeEventListener(e, n);
690
+ };
691
+ }
692
+ }, ye = {}, be = [
693
+ "en",
694
+ "nl",
695
+ "de"
696
+ ], x = ["baseLocale"], S = [], C, w;
697
+ function xe(e) {
698
+ if (S.length === 0) return;
699
+ let t = typeof e == "string" ? e : e.href;
700
+ if (C === t) return w;
701
+ let n = new URL(t, "http://dummy.com"), r;
702
+ for (let e of S) if (new ye(e.match, n.href).exec(n.href)) {
703
+ r = e;
704
+ break;
705
+ }
706
+ return C = t, w = r, r;
707
+ }
708
+ function T(e) {
709
+ let t = xe(e);
710
+ return t && t.exclude !== !0 && Array.isArray(t.strategy) ? t.strategy : x;
711
+ }
712
+ var E = void 0, D = typeof window > "u";
713
+ globalThis.__paraglide = globalThis.__paraglide ?? {}, globalThis.__paraglide.ssr = globalThis.__paraglide.ssr ?? {};
714
+ var O = !1, k = () => {
715
+ if (E) {
716
+ let e = E?.getStore()?.locale;
717
+ if (e) return e;
718
+ }
719
+ let e = x;
720
+ !D && typeof window < "u" && window.location?.href && (e = T(window.location.href));
721
+ let t = Se(e, typeof window < "u" ? window.location?.href : void 0);
722
+ if (t) return O || (O = !0, A(t, { reload: !1 })), t;
723
+ throw Error("No locale found. Read the docs https://inlang.com/m/gerre34r/library-inlang-paraglideJs/errors#no-locale-found");
724
+ };
725
+ function Se(e, t) {
726
+ let n;
727
+ for (let t of e) {
728
+ if (t === "baseLocale") n = "en";
729
+ else if (N(t) && M.has(t)) {
730
+ let e = M.get(t);
731
+ if (e) {
732
+ let t = e.getLocale();
733
+ if (t instanceof Promise) continue;
734
+ if (t !== void 0) return we(t);
735
+ }
736
+ }
737
+ let e = j(n);
738
+ if (e) return e;
652
739
  }
653
- extractUIValues(e) {
740
+ }
741
+ var Ce = (e) => {
742
+ e ? window.location.href = e : window.location.reload();
743
+ }, A = (e, t) => {
744
+ let n = {
745
+ reload: !0,
746
+ ...t
747
+ }, r;
748
+ try {
749
+ r = k();
750
+ } catch {}
751
+ let i = [], a = x;
752
+ !D && typeof window < "u" && window.location?.href && (a = T(window.location.href));
753
+ for (let t of a) if (t === "baseLocale") continue;
754
+ else if (N(t) && M.has(t)) {
755
+ let n = M.get(t);
756
+ if (n) {
757
+ let r = n.setLocale(e);
758
+ r instanceof Promise && (r = r.catch((e) => {
759
+ throw Error(`Custom strategy "${t}" setLocale failed.`, { cause: e });
760
+ }), i.push(r));
761
+ }
762
+ }
763
+ let o = () => {
764
+ !D && n.reload && window.location && e !== r && Ce(void 0);
765
+ };
766
+ if (i.length) return Promise.all(i).then(() => {
767
+ o();
768
+ });
769
+ o();
770
+ };
771
+ function j(e) {
772
+ if (typeof e != "string") return;
773
+ let t = e.toLowerCase();
774
+ for (let e of be) if (e.toLowerCase() === t) return e;
775
+ }
776
+ function we(e) {
777
+ let t = j(e);
778
+ if (t) return t;
779
+ throw Error(`Invalid locale: ${e}. Expected one of: ${be.join(", ")}`);
780
+ }
781
+ var M = /* @__PURE__ */ new Map();
782
+ function N(e) {
783
+ return typeof e == "string" && /^custom-[A-Za-z0-9_-]+$/.test(e);
784
+ }
785
+ //#endregion
786
+ //#region ../i18n/src/paraglide-viewer/messages/controls_ar_buttonlabel1.js
787
+ var Te = () => "Activate AR", Ee = () => "AR activeren", De = () => "AR aktivieren", Oe = ((e = {}, t = {}) => {
788
+ let n = t.locale ?? k();
789
+ return n === "en" ? Te(e) : n === "nl" ? Ee(e) : De(e);
790
+ }), ke = () => "Scan to view in AR", Ae = () => "Scan om in AR te bekijken", je = () => "Scannen für AR-Ansicht", Me = ((e = {}, t = {}) => {
791
+ let n = t.locale ?? k();
792
+ return n === "en" ? ke(e) : n === "nl" ? Ae(e) : je(e);
793
+ }), Ne = () => "Use your smartphone camera to scan this QR code", Pe = () => "Gebruik de camera van uw smartphone om deze QR-code te scannen", Fe = () => "Verwenden Sie die Kamera Ihres Smartphones, um diesen QR-Code zu scannen", Ie = ((e = {}, t = {}) => {
794
+ let n = t.locale ?? k();
795
+ return n === "en" ? Ne(e) : n === "nl" ? Pe(e) : Fe(e);
796
+ }), Le = () => "The 3D model will open in your phone's AR mode", Re = () => "Het 3D-model wordt geopend in de AR-modus van uw telefoon", ze = () => "Das 3D-Modell wird im AR-Modus Ihres Telefons geöffnet", Be = ((e = {}, t = {}) => {
797
+ let n = t.locale ?? k();
798
+ return n === "en" ? Le(e) : n === "nl" ? Re(e) : ze(e);
799
+ }), Ve = () => "Close", He = () => "Sluiten", Ue = () => "Schließen", We = ((e = {}, t = {}) => {
800
+ let n = t.locale ?? k();
801
+ return n === "en" ? Ve(e) : n === "nl" ? He(e) : Ue(e);
802
+ }), Ge = () => "Toggle fullscreen", Ke = () => "Volledig scherm", qe = () => "Vollbild umschalten", Je = ((e = {}, t = {}) => {
803
+ let n = t.locale ?? k();
804
+ return n === "en" ? Ge(e) : n === "nl" ? Ke(e) : qe(e);
805
+ }), Ye = () => "Downloading", Xe = () => "Downloaden", Ze = () => "Herunterladen", P = ((e = {}, t = {}) => {
806
+ let n = t.locale ?? k();
807
+ return n === "en" ? Ye(e) : n === "nl" ? Xe(e) : Ze(e);
808
+ }), Qe = () => "Rendering", $e = () => "Renderen", et = () => "Rendern", tt = ((e = {}, t = {}) => {
809
+ let n = t.locale ?? k();
810
+ return n === "en" ? Qe(e) : n === "nl" ? $e(e) : et(e);
811
+ }), nt = () => "Screenshot", rt = () => "Screenshot", it = () => "Screenshot", at = ((e = {}, t = {}) => {
812
+ let n = t.locale ?? k();
813
+ return n === "en" ? nt(e) : n === "nl" ? rt(e) : it(e);
814
+ }), ot = () => "Reset camera", st = () => "Camera resetten", ct = () => "Kamera zurücksetzen", lt = ((e = {}, t = {}) => {
815
+ let n = t.locale ?? k();
816
+ return n === "en" ? ot(e) : n === "nl" ? st(e) : ct(e);
817
+ }), ut = () => "Zoom in", dt = () => "Inzoomen", ft = () => "Hineinzoomen", pt = ((e = {}, t = {}) => {
818
+ let n = t.locale ?? k();
819
+ return n === "en" ? ut(e) : n === "nl" ? dt(e) : ft(e);
820
+ }), mt = () => "Zoom out", ht = () => "Uitzoomen", gt = () => "Herauszoomen", _t = ((e = {}, t = {}) => {
821
+ let n = t.locale ?? k();
822
+ return n === "en" ? mt(e) : n === "nl" ? ht(e) : gt(e);
823
+ }), vt = () => "Rotate left", yt = () => "Naar links draaien", bt = () => "Nach links drehen", xt = ((e = {}, t = {}) => {
824
+ let n = t.locale ?? k();
825
+ return n === "en" ? vt(e) : n === "nl" ? yt(e) : bt(e);
826
+ }), St = () => "Rotate right", Ct = () => "Naar rechts draaien", wt = () => "Nach rechts drehen", Tt = ((e = {}, t = {}) => {
827
+ let n = t.locale ?? k();
828
+ return n === "en" ? St(e) : n === "nl" ? Ct(e) : wt(e);
829
+ }), Et = () => "Tilt up", Dt = () => "Omhoog kantelen", Ot = () => "Nach oben kippen", kt = ((e = {}, t = {}) => {
830
+ let n = t.locale ?? k();
831
+ return n === "en" ? Et(e) : n === "nl" ? Dt(e) : Ot(e);
832
+ }), At = () => "Tilt down", jt = () => "Omlaag kantelen", Mt = () => "Nach unten kippen", Nt = ((e = {}, t = {}) => {
833
+ let n = t.locale ?? k();
834
+ return n === "en" ? At(e) : n === "nl" ? jt(e) : Mt(e);
835
+ });
836
+ //#endregion
837
+ //#region src/ui/loading.ts
838
+ function F(e) {
839
+ let t = Math.max(0, Math.min(1, e));
840
+ return t * t * (3 - 2 * t);
841
+ }
842
+ function Pt(e, t, n, r, i, a) {
843
+ let [o, s, c] = e, l = Math.cos(t), u = Math.sin(t), d = l * o + u * c, f = -u * o + l * c, p = Math.cos(n), m = Math.sin(n), h = p * s - m * f, g = m * s + p * f, _ = .55;
844
+ return [
845
+ r + d * a * _,
846
+ i - h * a * _,
847
+ g
848
+ ];
849
+ }
850
+ function Ft(e, t, n) {
851
+ e.beginPath(), e.moveTo(t[0], t[1]), e.lineTo(n[0], n[1]), e.strokeStyle = "#000", e.lineWidth = 2, e.stroke();
852
+ }
853
+ function It(e, t, n, r, i) {
854
+ let a = t(n), o = t(r);
855
+ Ft(e, a, [
856
+ a[0] + (o[0] - a[0]) * i,
857
+ a[1] + (o[1] - a[1]) * i,
858
+ a[2]
859
+ ]);
860
+ }
861
+ var I = [
862
+ -1,
863
+ -1,
864
+ 1
865
+ ], L = [
866
+ 1,
867
+ -1,
868
+ 1
869
+ ], R = [
870
+ 1,
871
+ -1,
872
+ -1
873
+ ], z = [
874
+ -1,
875
+ -1,
876
+ -1
877
+ ], B = [
878
+ -1,
879
+ 1,
880
+ 1
881
+ ], V = [
882
+ 1,
883
+ 1,
884
+ 1
885
+ ], H = [
886
+ 1,
887
+ 1,
888
+ -1
889
+ ], U = [
890
+ -1,
891
+ 1,
892
+ -1
893
+ ], Lt = [
894
+ [I, L],
895
+ [L, R],
896
+ [R, z],
897
+ [z, I],
898
+ [I, B],
899
+ [L, V],
900
+ [R, H],
901
+ [z, U],
902
+ [B, V],
903
+ [V, H],
904
+ [H, U],
905
+ [U, B]
906
+ ], Rt = 400, zt = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif";
907
+ function Bt() {
908
+ let e = document.createElement("div");
909
+ Object.assign(e.style, {
910
+ position: "absolute",
911
+ inset: "0",
912
+ display: "flex",
913
+ flexDirection: "column",
914
+ alignItems: "center",
915
+ justifyContent: "center",
916
+ background: "#fff",
917
+ zIndex: "20",
918
+ opacity: "0",
919
+ transition: "opacity 0.3s ease-in-out"
920
+ });
921
+ let t = document.createElement("canvas");
922
+ Object.assign(t.style, {
923
+ width: "100%",
924
+ height: "100%",
925
+ position: "absolute",
926
+ inset: "0"
927
+ });
928
+ let n = document.createElement("div");
929
+ Object.assign(n.style, {
930
+ position: "absolute",
931
+ top: "56%",
932
+ left: "0",
933
+ right: "0",
934
+ textAlign: "center",
935
+ pointerEvents: "none"
936
+ });
937
+ let r = document.createElement("span");
938
+ Object.assign(r.style, {
939
+ fontSize: "13px",
940
+ fontWeight: "400",
941
+ color: "#000",
942
+ fontVariantNumeric: "tabular-nums",
943
+ fontFamily: zt
944
+ });
945
+ let i = document.createElement("span");
946
+ return Object.assign(i.style, {
947
+ fontSize: "13px",
948
+ color: "#000",
949
+ fontFamily: zt
950
+ }), i.textContent = P(), n.appendChild(r), n.appendChild(i), e.appendChild(t), e.appendChild(n), {
951
+ overlay: e,
952
+ canvas: t,
953
+ pctEl: r,
954
+ phaseEl: i
955
+ };
956
+ }
957
+ var Vt = class {
958
+ overlay;
959
+ canvas;
960
+ ctx;
961
+ pctEl;
962
+ phaseEl;
963
+ animationId = null;
964
+ showTimerId = null;
965
+ startTime;
966
+ hidden = !1;
967
+ visible = !1;
968
+ constructor(e, t = {}) {
969
+ this.startTime = performance.now();
970
+ let n = Bt();
971
+ this.overlay = n.overlay, this.canvas = n.canvas, this.pctEl = n.pctEl, this.phaseEl = n.phaseEl, e.appendChild(this.overlay), this.ctx = this.canvas.getContext("2d"), this.ctx !== null && (this.sizeCanvas(), this.tick());
972
+ let r = t.showDelay ?? Rt;
973
+ this.showTimerId = setTimeout(() => {
974
+ this.reveal();
975
+ }, r);
976
+ }
977
+ get loadingTexts() {
654
978
  return {
655
- arEnabled: e.arEnabled ?? e.ui?.arEnabled,
656
- fullscreenEnabled: e.fullscreenEnabled ?? e.ui?.fullscreenEnabled,
657
- loadingIndicatorEnabled: e.loadingIndicatorEnabled ?? e.ui?.loadingIndicatorEnabled
979
+ downloading: P(),
980
+ rendering: tt()
658
981
  };
659
982
  }
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
- };
983
+ updateProgress(e, t) {
984
+ this.hidden || (this.pctEl.textContent = `${Math.round(e)}%`, this.phaseEl.textContent = t === "" ? "" : ` \u00B7 ${t}`);
665
985
  }
666
- setArEnabled(e) {
667
- p.debug(`Setting AR button enabled: ${e}`), this.updateUIConfig({ arEnabled: e });
986
+ showError(e) {
987
+ if (this.hidden = !1, this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.ctx !== null) {
988
+ let e = Math.min(devicePixelRatio, 2), t = this.canvas.width / e, n = this.canvas.height / e;
989
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, t * e, n * e);
990
+ }
991
+ this.pctEl.textContent = "", this.phaseEl.textContent = e, this.phaseEl.style.color = "#b91c1c", this.reveal();
992
+ }
993
+ hide() {
994
+ if (!this.hidden) {
995
+ if (this.hidden = !0, this.cancelShowTimer(), !this.visible) {
996
+ this.destroy();
997
+ return;
998
+ }
999
+ this.overlay.style.opacity = "0", this.overlay.addEventListener("transitionend", () => {
1000
+ this.destroy();
1001
+ }, { once: !0 }), setTimeout(() => {
1002
+ this.destroy();
1003
+ }, 500);
1004
+ }
668
1005
  }
669
- setFullscreenEnabled(e) {
670
- p.debug(`Setting fullscreen button enabled: ${e}`), this.updateUIConfig({ fullscreenEnabled: e });
1006
+ destroy() {
1007
+ this.cancelShowTimer(), this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null), this.overlay.remove();
1008
+ }
1009
+ reveal() {
1010
+ this.hidden || this.visible || (this.visible = !0, this.overlay.style.opacity = "1");
671
1011
  }
672
- setLoadingIndicatorEnabled(e) {
673
- p.debug(`Setting loading indicator enabled: ${e}`), this.updateUIConfig({ loadingIndicatorEnabled: e });
1012
+ cancelShowTimer() {
1013
+ this.showTimerId !== null && (clearTimeout(this.showTimerId), this.showTimerId = null);
674
1014
  }
675
- updateUIConfig(e) {
676
- p.debug("Updating UI configuration", e), Object.keys(e).length > 0 && this.sendConfig({ ui: e });
1015
+ sizeCanvas() {
1016
+ if (this.ctx === null) return;
1017
+ let e = Math.min(devicePixelRatio, 2), t = this.overlay.clientWidth || 440, n = this.overlay.clientHeight || 360;
1018
+ this.canvas.width = t * e, this.canvas.height = n * e, this.ctx.scale(e, e), this.ctx.lineCap = "round", this.ctx.lineJoin = "round";
677
1019
  }
678
- hideAllUI() {
679
- p.debug("Hiding all UI elements"), this.sendUIConfig({
680
- arEnabled: !1,
681
- fullscreenEnabled: !1,
682
- loadingIndicatorEnabled: !1
1020
+ tick = () => {
1021
+ this.hidden || (this.drawFrame(), this.animationId = requestAnimationFrame(this.tick));
1022
+ };
1023
+ drawFrame() {
1024
+ if (this.ctx === null) return;
1025
+ let e = this.canvas.width / Math.min(devicePixelRatio, 2), t = this.canvas.height / Math.min(devicePixelRatio, 2), n = (performance.now() - this.startTime) / 1e3;
1026
+ this.ctx.fillStyle = "#fff", this.ctx.fillRect(0, 0, e, t);
1027
+ let r = e / 2, i = t * .44, a = Math.min(e, t) * .08, o = (e) => Pt(e, .785, .615, r, i, a), s = n % 6 / 6, c = Lt.length, l;
1028
+ l = s < .35 ? F(s / .35) * c : s < .55 ? c : s < .9 ? (1 - F((s - .55) / .35)) * c : 0, this.ctx.shadowBlur = 5, this.ctx.shadowColor = "rgba(30,30,30,0.08)";
1029
+ for (let e = 0; e < c && !(e >= l); e++) {
1030
+ let t = Lt[e];
1031
+ if (t === void 0) continue;
1032
+ let n = Math.min(1, l - e);
1033
+ It(this.ctx, o, t[0], t[1], n);
1034
+ }
1035
+ this.ctx.shadowBlur = 0;
1036
+ }
1037
+ }, Ht = {
1038
+ xmlns: "http://www.w3.org/2000/svg",
1039
+ width: 24,
1040
+ height: 24,
1041
+ viewBox: "0 0 24 24",
1042
+ fill: "none",
1043
+ stroke: "currentColor",
1044
+ "stroke-width": 2,
1045
+ "stroke-linecap": "round",
1046
+ "stroke-linejoin": "round"
1047
+ }, Ut = ([e, t, n]) => {
1048
+ let r = document.createElementNS("http://www.w3.org/2000/svg", e);
1049
+ return Object.keys(t).forEach((e) => {
1050
+ r.setAttribute(e, String(t[e]));
1051
+ }), n?.length && n.forEach((e) => {
1052
+ let t = Ut(e);
1053
+ r.appendChild(t);
1054
+ }), r;
1055
+ }, Wt = (e, t = {}) => Ut([
1056
+ "svg",
1057
+ {
1058
+ ...Ht,
1059
+ ...t
1060
+ },
1061
+ e
1062
+ ]), Gt = [
1063
+ ["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" }],
1064
+ ["path", { d: "m3.3 7 8.7 5 8.7-5" }],
1065
+ ["path", { d: "M12 22V12" }]
1066
+ ], Kt = [["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", {
1067
+ cx: "12",
1068
+ cy: "13",
1069
+ r: "3"
1070
+ }]], qt = [["path", { d: "m6 9 6 6 6-6" }]], Jt = [["path", { d: "m15 18-6-6 6-6" }]], Yt = [["path", { d: "m9 18 6-6-6-6" }]], Xt = [["path", { d: "m18 15-6-6-6 6" }]], Zt = [
1071
+ ["path", { d: "M15 3h6v6" }],
1072
+ ["path", { d: "m21 3-7 7" }],
1073
+ ["path", { d: "m3 21 7-7" }],
1074
+ ["path", { d: "M9 21H3v-6" }]
1075
+ ], Qt = [
1076
+ ["path", { d: "m14 10 7-7" }],
1077
+ ["path", { d: "M20 10h-6V4" }],
1078
+ ["path", { d: "m3 21 7-7" }],
1079
+ ["path", { d: "M4 14h6v6" }]
1080
+ ], $t = [["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" }]], en = [
1081
+ ["circle", {
1082
+ cx: "11",
1083
+ cy: "11",
1084
+ r: "8"
1085
+ }],
1086
+ ["line", {
1087
+ x1: "21",
1088
+ x2: "16.65",
1089
+ y1: "21",
1090
+ y2: "16.65"
1091
+ }],
1092
+ ["line", {
1093
+ x1: "11",
1094
+ x2: "11",
1095
+ y1: "8",
1096
+ y2: "14"
1097
+ }],
1098
+ ["line", {
1099
+ x1: "8",
1100
+ x2: "14",
1101
+ y1: "11",
1102
+ y2: "11"
1103
+ }]
1104
+ ], tn = [
1105
+ ["circle", {
1106
+ cx: "11",
1107
+ cy: "11",
1108
+ r: "8"
1109
+ }],
1110
+ ["line", {
1111
+ x1: "21",
1112
+ x2: "16.65",
1113
+ y1: "21",
1114
+ y2: "16.65"
1115
+ }],
1116
+ ["line", {
1117
+ x1: "8",
1118
+ x2: "14",
1119
+ y1: "11",
1120
+ y2: "11"
1121
+ }]
1122
+ ];
1123
+ //#endregion
1124
+ //#region ../ui/src/icons.ts
1125
+ function W(e, { size: t = 20, strokeWidth: n = 1.75 } = {}) {
1126
+ return Wt(e, {
1127
+ width: t,
1128
+ height: t,
1129
+ "stroke-width": n
1130
+ });
1131
+ }
1132
+ var G = {
1133
+ ar: (e) => W(Gt, e),
1134
+ fullscreen: (e) => W(Zt, e),
1135
+ exitFullscreen: (e) => W(Qt, e),
1136
+ screenshot: (e) => W(Kt, e),
1137
+ tiltUp: (e) => W(Xt, e),
1138
+ tiltDown: (e) => W(qt, e),
1139
+ rotateLeft: (e) => W(Jt, e),
1140
+ rotateRight: (e) => W(Yt, e),
1141
+ zoomIn: (e) => W(en, e),
1142
+ zoomOut: (e) => W(tn, e),
1143
+ reset: (e) => W($t, e)
1144
+ }, K = class {
1145
+ element;
1146
+ constructor({ variant: e = "default", size: t = "default", label: n, icon: r, onClick: i, disabled: a = !1 } = {}) {
1147
+ 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;
1148
+ }
1149
+ setIcon(e) {
1150
+ let t = this.element.querySelector("svg");
1151
+ t === null ? this.element.appendChild(e) : t.replaceWith(e);
1152
+ }
1153
+ }, q = {
1154
+ brand: {
1155
+ DEFAULT: "#171717",
1156
+ primary: "#171717",
1157
+ hover: "#262626",
1158
+ light: "#f5f5f5",
1159
+ 50: "#fafafa",
1160
+ 100: "#f5f5f5",
1161
+ 200: "#e5e5e5",
1162
+ 300: "#d4d4d4",
1163
+ 400: "#a3a3a3",
1164
+ 500: "#737373",
1165
+ 600: "#525252",
1166
+ 700: "#404040",
1167
+ 800: "#262626",
1168
+ 900: "#171717",
1169
+ 950: "#0a0a0a"
1170
+ },
1171
+ semantic: {
1172
+ success: "#16a34a",
1173
+ warning: "#ca8a04",
1174
+ error: "#dc2626",
1175
+ info: "#0891b2"
1176
+ },
1177
+ neutral: {
1178
+ 50: "#fafafa",
1179
+ 100: "#f5f5f5",
1180
+ 200: "#e5e5e5",
1181
+ 300: "#d4d4d4",
1182
+ 400: "#a3a3a3",
1183
+ 500: "#737373",
1184
+ 600: "#525252",
1185
+ 700: "#404040",
1186
+ 800: "#262626",
1187
+ 900: "#171717",
1188
+ 950: "#0a0a0a"
1189
+ },
1190
+ white: "#ffffff",
1191
+ black: "#000000"
1192
+ }, J = {
1193
+ px: "1px",
1194
+ 0: "0",
1195
+ .5: "0.125rem",
1196
+ 1: "0.25rem",
1197
+ 1.5: "0.375rem",
1198
+ 2: "0.5rem",
1199
+ 2.5: "0.625rem",
1200
+ 3: "0.75rem",
1201
+ 3.5: "0.875rem",
1202
+ 4: "1rem",
1203
+ 5: "1.25rem",
1204
+ 6: "1.5rem",
1205
+ 7: "1.75rem",
1206
+ 8: "2rem",
1207
+ 9: "2.25rem",
1208
+ 10: "2.5rem",
1209
+ 12: "3rem",
1210
+ 14: "3.5rem",
1211
+ 16: "4rem",
1212
+ 20: "5rem",
1213
+ 24: "6rem"
1214
+ };
1215
+ J[1], J[2], J[4], J[6], J[8], J[12];
1216
+ //#endregion
1217
+ //#region ../ui/src/vanilla/components/card/index.ts
1218
+ var nn = class {
1219
+ element;
1220
+ constructor() {
1221
+ this.element = document.createElement("div"), Object.assign(this.element.style, {
1222
+ background: q.white,
1223
+ borderRadius: J[3],
1224
+ padding: J[6],
1225
+ boxShadow: `0 ${J[2]} ${J[8]} oklch(0% 0 0 / 20%)`,
1226
+ maxWidth: "20rem",
1227
+ width: "100%"
683
1228
  });
684
1229
  }
685
- showAllUI() {
686
- p.debug("Showing all UI elements"), this.sendUIConfig({
687
- arEnabled: !0,
688
- fullscreenEnabled: !0,
689
- loadingIndicatorEnabled: !0
1230
+ }, rn = class {
1231
+ card;
1232
+ backdrop;
1233
+ keyHandler = null;
1234
+ constructor(e = {}) {
1235
+ this.options = e, this.card = new nn(), this.backdrop = this.createBackdrop(), this.backdrop.appendChild(this.card.element);
1236
+ }
1237
+ open() {
1238
+ document.body.appendChild(this.backdrop), this.keyHandler = (e) => {
1239
+ e.key === "Escape" && this.close();
1240
+ }, document.addEventListener("keydown", this.keyHandler);
1241
+ }
1242
+ close() {
1243
+ this.backdrop.remove(), this.keyHandler !== null && (document.removeEventListener("keydown", this.keyHandler), this.keyHandler = null), this.options.onClose?.();
1244
+ }
1245
+ createBackdrop() {
1246
+ let e = document.createElement("div");
1247
+ return Object.assign(e.style, {
1248
+ position: "fixed",
1249
+ inset: J[0],
1250
+ background: "oklch(0% 0 0 / 50%)",
1251
+ zIndex: "9999",
1252
+ display: "flex",
1253
+ alignItems: "center",
1254
+ justifyContent: "center"
1255
+ }), e.addEventListener("click", (t) => {
1256
+ t.target === e && this.close();
1257
+ }), e;
1258
+ }
1259
+ }, an = class {
1260
+ element;
1261
+ selectCallback = null;
1262
+ constructor(e, t, n) {
1263
+ this.element = document.createElement("div"), this.element.className = "ui-toggle-group";
1264
+ for (let r = 0; r < e.length; r++) {
1265
+ let i = e[r], a = document.createElement("button");
1266
+ a.className = "ui-toggle-group-item", a.dataset.value = i, a.textContent = n?.[r] ?? i, a.dataset.state = i === t ? "on" : "off", a.addEventListener("click", () => {
1267
+ this.selectCallback?.(i);
1268
+ }), this.element.appendChild(a);
1269
+ }
1270
+ }
1271
+ setSelected(e) {
1272
+ for (let t of this.element.querySelectorAll(".ui-toggle-group-item")) t.dataset.state = t.dataset.value === e ? "on" : "off";
1273
+ }
1274
+ onSelect(e) {
1275
+ this.selectCallback = e;
1276
+ }
1277
+ }, on = class {
1278
+ element;
1279
+ select;
1280
+ constructor(e, t, n, r, i) {
1281
+ this.select = document.createElement("select"), this.select.className = "ui-native-select", this.select.dataset.name = e;
1282
+ let a = document.createElement("option");
1283
+ a.value = "", a.disabled = !0, a.textContent = n, this.select.appendChild(a);
1284
+ let o = !1;
1285
+ for (let e = 0; e < t.length; e++) {
1286
+ let n = t[e], a = document.createElement("option");
1287
+ a.value = n, a.textContent = i?.[e] ?? n, n === r && (a.selected = !0, o = !0), this.select.appendChild(a);
1288
+ }
1289
+ o || (a.selected = !0);
1290
+ let s = Wt(qt, {
1291
+ width: 16,
1292
+ height: 16,
1293
+ "stroke-width": 2
690
1294
  });
1295
+ 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);
691
1296
  }
692
- sendUIConfig(e) {
693
- this.sendConfig({ ui: e });
1297
+ setSelected(e) {
1298
+ this.select.value = e;
694
1299
  }
695
- sendConfig(e) {
696
- let t = {
697
- type: b.CONFIG,
698
- config: e
1300
+ onSelect(e) {
1301
+ this.select.addEventListener("change", () => {
1302
+ e(this.select.value);
1303
+ });
1304
+ }
1305
+ }, Y = {
1306
+ fontFamily: {
1307
+ sans: [
1308
+ "Mona Sans",
1309
+ "ui-sans-serif",
1310
+ "system-ui",
1311
+ "sans-serif",
1312
+ "Apple Color Emoji",
1313
+ "Segoe UI Emoji",
1314
+ "Segoe UI Symbol",
1315
+ "Noto Color Emoji"
1316
+ ],
1317
+ mono: [
1318
+ "JetBrains Mono",
1319
+ "ui-monospace",
1320
+ "monospace"
1321
+ ]
1322
+ },
1323
+ fontSize: {
1324
+ xs: "0.75rem",
1325
+ sm: "0.875rem",
1326
+ base: "1rem",
1327
+ lg: "1.125rem",
1328
+ xl: "1.25rem",
1329
+ "2xl": "1.5rem",
1330
+ "3xl": "1.875rem",
1331
+ "4xl": "2.25rem"
1332
+ },
1333
+ fontWeight: {
1334
+ normal: "400",
1335
+ medium: "500",
1336
+ semibold: "600",
1337
+ bold: "700"
1338
+ },
1339
+ lineHeight: {
1340
+ tight: "1.25",
1341
+ normal: "1.5",
1342
+ relaxed: "1.75"
1343
+ }
1344
+ }, sn = Y.fontFamily.sans.join(", "), cn = class {
1345
+ qrLib = null;
1346
+ dialog = null;
1347
+ async show(t) {
1348
+ this.hide(), this.qrLib ??= await import("./browser-DbgxVbZp.js").then((t) => /* @__PURE__ */ e(t.default, 1));
1349
+ let n = await this.qrLib.toDataURL(t, {
1350
+ width: 256,
1351
+ margin: 4,
1352
+ errorCorrectionLevel: "M",
1353
+ color: {
1354
+ dark: q.black,
1355
+ light: q.white
1356
+ }
1357
+ });
1358
+ this.dialog = new rn({ onClose: () => {
1359
+ this.dialog = null;
1360
+ } }), Object.assign(this.dialog.card.element.style, {
1361
+ display: "flex",
1362
+ flexDirection: "column",
1363
+ alignItems: "center",
1364
+ gap: J[3],
1365
+ fontFamily: sn
1366
+ }), this.dialog.card.element.appendChild(this.createText(Me(), {
1367
+ fontSize: Y.fontSize.base,
1368
+ fontWeight: Y.fontWeight.semibold,
1369
+ color: q.neutral[900]
1370
+ })), this.dialog.card.element.appendChild(this.createText(Ie(), {
1371
+ fontSize: Y.fontSize.sm,
1372
+ color: q.neutral[600]
1373
+ })), this.dialog.card.element.appendChild(this.createQrImage(n)), this.dialog.card.element.appendChild(this.createText(Be(), {
1374
+ fontSize: Y.fontSize.xs,
1375
+ color: q.neutral[500]
1376
+ }));
1377
+ let r = new K({
1378
+ variant: "outline",
1379
+ label: We(),
1380
+ onClick: () => {
1381
+ this.hide();
1382
+ }
1383
+ });
1384
+ this.dialog.card.element.appendChild(r.element), this.dialog.open();
1385
+ }
1386
+ hide() {
1387
+ this.dialog?.close(), this.dialog = null;
1388
+ }
1389
+ createText(e, t) {
1390
+ let n = document.createElement("p");
1391
+ return n.textContent = e, Object.assign(n.style, {
1392
+ margin: "0",
1393
+ textAlign: "center",
1394
+ ...t
1395
+ }), n;
1396
+ }
1397
+ createQrImage(e) {
1398
+ let t = document.createElement("img");
1399
+ return t.src = e, t.alt = "QR code", Object.assign(t.style, {
1400
+ width: J[24],
1401
+ height: J[24],
1402
+ borderRadius: J[1]
1403
+ }), t;
1404
+ }
1405
+ }, X = Y.fontFamily.sans.join(", "), ln = `
1406
+ :root {
1407
+ --primary: oklch(5% 0 0);
1408
+ --primary-foreground: oklch(100% 0 0);
1409
+ --secondary: oklch(92% 0 0);
1410
+ --secondary-foreground: oklch(5% 0 0);
1411
+ --background: oklch(100% 0 0);
1412
+ --foreground: oklch(5% 0 0);
1413
+ --border: oklch(92% 0 0);
1414
+ --ring: oklch(44% 0 0);
1415
+ --radius: 0.625rem;
1416
+ --accent: oklch(92% 0 0);
1417
+ --accent-foreground: oklch(5% 0 0);
1418
+ --muted-foreground: oklch(44% 0 0);
1419
+ }
1420
+
1421
+ /* ── Button ──────────────────────────────────────────────── */
1422
+ .ui-btn {
1423
+ display: inline-flex;
1424
+ align-items: center;
1425
+ justify-content: center;
1426
+ gap: 0.5rem;
1427
+ border: 1px solid transparent;
1428
+ border-radius: var(--radius);
1429
+ font-family: ${X};
1430
+ font-size: 0.875rem;
1431
+ font-weight: 600;
1432
+ white-space: nowrap;
1433
+ cursor: pointer;
1434
+ transition: all 200ms;
1435
+ flex-shrink: 0;
1436
+ }
1437
+ .ui-btn:active { transform: scale(0.98); }
1438
+ .ui-btn:disabled { opacity: 0.5; pointer-events: none; }
1439
+ .ui-btn svg { inline-size: 1rem; block-size: 1rem; pointer-events: none; flex-shrink: 0; }
1440
+
1441
+ .ui-btn--default {
1442
+ background: var(--primary);
1443
+ color: var(--primary-foreground);
1444
+ box-shadow: 0 1px 3px oklch(5% 0 0 / 20%);
1445
+ }
1446
+ .ui-btn--default:hover { background: oklch(10% 0 0); }
1447
+
1448
+ .ui-btn--outline {
1449
+ background: var(--background);
1450
+ color: var(--foreground);
1451
+ border-color: var(--border);
1452
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1453
+ }
1454
+ .ui-btn--outline:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1455
+
1456
+ .ui-btn--secondary {
1457
+ background: var(--secondary);
1458
+ color: var(--secondary-foreground);
1459
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1460
+ }
1461
+ .ui-btn--secondary:hover { background: oklch(88% 0 0); }
1462
+
1463
+ .ui-btn--ghost { background: transparent; color: var(--foreground); }
1464
+ .ui-btn--ghost:hover { background: var(--accent); }
1465
+
1466
+ .ui-btn--size-default { block-size: 2.25rem; padding: 0 1rem; }
1467
+ .ui-btn--size-sm { block-size: 2rem; padding: 0 0.75rem; border-radius: calc(var(--radius) - 0.125rem); }
1468
+ .ui-btn--size-lg { block-size: 2.5rem; padding: 0 1.5rem; }
1469
+ .ui-btn--size-icon { inline-size: 2.25rem; block-size: 2.25rem; padding: 0; }
1470
+ .ui-btn--size-icon-sm { inline-size: 2rem; block-size: 2rem; padding: 0; }
1471
+ .ui-btn--size-icon-lg { inline-size: 2.5rem; block-size: 2.5rem; padding: 0; }
1472
+
1473
+ /* ── Toggle group ────────────────────────────────────────── */
1474
+ .ui-toggle-group { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; }
1475
+
1476
+ .ui-toggle-group-item {
1477
+ display: inline-flex;
1478
+ align-items: center;
1479
+ justify-content: center;
1480
+ block-size: 2rem;
1481
+ padding: 0 0.75rem;
1482
+ border: 1px solid var(--border);
1483
+ border-radius: var(--radius);
1484
+ background: var(--background);
1485
+ color: var(--foreground);
1486
+ font-family: ${X};
1487
+ font-size: 0.875rem;
1488
+ font-weight: 600;
1489
+ cursor: pointer;
1490
+ white-space: nowrap;
1491
+ transition: all 200ms;
1492
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1493
+ }
1494
+ .ui-toggle-group-item:hover { background: var(--accent); border-color: oklch(85% 0 0); }
1495
+ .ui-toggle-group-item:active { transform: scale(0.98); }
1496
+ .ui-toggle-group-item[data-state="on"] {
1497
+ background: var(--primary);
1498
+ color: var(--primary-foreground);
1499
+ border-color: var(--primary);
1500
+ }
1501
+ .ui-toggle-group-item[data-state="on"]:hover { background: oklch(10% 0 0); }
1502
+
1503
+ /* ── Native select ───────────────────────────────────────── */
1504
+ .ui-native-select-wrap {
1505
+ position: relative;
1506
+ display: inline-flex;
1507
+ min-inline-size: 8rem;
1508
+ }
1509
+
1510
+ .ui-native-select {
1511
+ display: block;
1512
+ inline-size: 100%;
1513
+ block-size: 2.25rem;
1514
+ padding: 0 2.25rem 0 0.75rem;
1515
+ border: 1px solid var(--border);
1516
+ border-radius: var(--radius);
1517
+ background: var(--background);
1518
+ appearance: none;
1519
+ color: var(--foreground);
1520
+ font-family: ${X};
1521
+ font-size: 0.875rem;
1522
+ font-weight: 400;
1523
+ cursor: pointer;
1524
+ transition: color 200ms, box-shadow 200ms, border-color 200ms;
1525
+ box-shadow: 0 1px 2px oklch(5% 0 0 / 8%);
1526
+ }
1527
+ .ui-native-select:hover { border-color: oklch(85% 0 0); }
1528
+ .ui-native-select:focus-visible {
1529
+ outline: none;
1530
+ border-color: var(--ring);
1531
+ box-shadow: 0 0 0 3px oklch(5% 0 0 / 12%);
1532
+ }
1533
+
1534
+ .ui-native-select-icon {
1535
+ position: absolute;
1536
+ inset-block-start: 50%;
1537
+ inset-inline-end: 0.625rem;
1538
+ transform: translateY(-50%);
1539
+ color: var(--muted-foreground);
1540
+ pointer-events: none;
1541
+ }
1542
+ `, Z = !1;
1543
+ function un() {
1544
+ if (Z || typeof document > "u") return;
1545
+ Z = !0;
1546
+ let e = document.createElement("style");
1547
+ e.id = "vd-ui-vanilla", e.textContent = ln, document.head.appendChild(e);
1548
+ }
1549
+ //#endregion
1550
+ //#region src/ui/toolbar.ts
1551
+ function dn(e, t) {
1552
+ let n = document.createElement("a");
1553
+ n.href = `data:image/png;base64,${e}`, n.download = t, n.click();
1554
+ }
1555
+ var fn = class {
1556
+ element;
1557
+ screenshotBtn = null;
1558
+ constructor(e) {
1559
+ this.options = e, this.element = document.createElement("div"), this.element.className = "vd-toolbar";
1560
+ }
1561
+ syncButton(e, t, n) {
1562
+ if (t && e === null) {
1563
+ let e = n();
1564
+ return this.element.appendChild(e.element), e;
1565
+ }
1566
+ return !t && e !== null ? (e.element.remove(), null) : e;
1567
+ }
1568
+ applySettings(e) {
1569
+ this.screenshotBtn = this.syncButton(this.screenshotBtn, e.screenshot === !0, () => new K({
1570
+ variant: "outline",
1571
+ size: "icon",
1572
+ label: at(),
1573
+ icon: G.screenshot(),
1574
+ onClick: () => {
1575
+ this.options.snapshotApi.take("snapshot.png").onDeveloped(({ data: e, filename: t }) => {
1576
+ dn(e, t);
1577
+ });
1578
+ }
1579
+ }));
1580
+ }
1581
+ }, pn = class {
1582
+ element;
1583
+ arBtn = null;
1584
+ fullscreenBtn = null;
1585
+ onFullscreenChange;
1586
+ constructor(e) {
1587
+ this.options = e, this.element = document.createElement("div"), this.element.className = "vd-action-bar", this.onFullscreenChange = () => {
1588
+ if (this.fullscreenBtn === null) return;
1589
+ let e = document.fullscreenElement !== null;
1590
+ this.fullscreenBtn.setIcon(e ? G.exitFullscreen() : G.fullscreen());
1591
+ }, document.addEventListener("fullscreenchange", this.onFullscreenChange);
1592
+ }
1593
+ syncButton(e, t, n) {
1594
+ if (t && e === null) {
1595
+ let e = n();
1596
+ return this.element.appendChild(e.element), e;
1597
+ }
1598
+ return !t && e !== null ? (e.element.remove(), null) : e;
1599
+ }
1600
+ applySettings(e) {
1601
+ this.arBtn = this.syncButton(this.arBtn, e.ar !== !1 && this.options.onAr !== void 0, () => new K({
1602
+ variant: "outline",
1603
+ size: "icon",
1604
+ label: Oe(),
1605
+ icon: G.ar(),
1606
+ onClick: () => {
1607
+ this.options.onAr?.();
1608
+ }
1609
+ })), this.fullscreenBtn = this.syncButton(this.fullscreenBtn, e.fullscreen !== !1 && this.options.onFullscreen !== void 0, () => new K({
1610
+ variant: "outline",
1611
+ size: "icon",
1612
+ label: Je(),
1613
+ icon: G.fullscreen(),
1614
+ onClick: () => {
1615
+ this.options.onFullscreen?.();
1616
+ }
1617
+ }));
1618
+ }
1619
+ destroy() {
1620
+ document.removeEventListener("fullscreenchange", this.onFullscreenChange);
1621
+ }
1622
+ }, Q = .5, mn = class e {
1623
+ element;
1624
+ constructor(t, n) {
1625
+ this.element = Object.assign(document.createElement("div"), { className: "vd-camera-controls" });
1626
+ let r = n.rotation, i = n.tilt, a = n.zoom, o = 0, s = (e) => Math.max(n.limits?.minZoom ?? 1, Math.min(n.limits?.maxZoom ?? Infinity, e)), c = (e) => {
1627
+ let t = n.limits?.minRotate, r = n.limits?.maxRotate;
1628
+ return t !== void 0 || r !== void 0 ? Math.max(t ?? -Infinity, Math.min(r ?? Infinity, e)) : e;
1629
+ }, l = (e) => {
1630
+ let a = n.limits?.minTilt, s = n.limits?.maxTilt;
1631
+ if (a !== void 0 || s !== void 0) {
1632
+ i = Math.max(a ?? 0, Math.min(s ?? 180, i + e)), t.tilt(i).set();
1633
+ return;
1634
+ }
1635
+ let c = i + e;
1636
+ c > 180 || c < 0 ? (i = c > 180 ? 360 - c : -c, o = (o + 180) % 360, t.tilt(i).rotate(r + o).set()) : (i = c, t.tilt(i).set());
1637
+ }, u = new K({
1638
+ variant: "outline",
1639
+ size: "icon",
1640
+ label: _t(),
1641
+ icon: G.zoomOut(),
1642
+ onClick: () => {
1643
+ a = s(a - 10), t.zoom(a).set();
1644
+ }
1645
+ }), d = new K({
1646
+ variant: "outline",
1647
+ size: "icon",
1648
+ label: pt(),
1649
+ icon: G.zoomIn(),
1650
+ onClick: () => {
1651
+ a = s(a + 10), t.zoom(a).set();
1652
+ }
1653
+ }), f = new K({
1654
+ variant: "outline",
1655
+ size: "icon",
1656
+ label: kt(),
1657
+ icon: G.tiltUp(),
1658
+ onClick: () => {
1659
+ l(-15);
1660
+ }
1661
+ }), p = new K({
1662
+ variant: "outline",
1663
+ size: "icon",
1664
+ label: xt(),
1665
+ icon: G.rotateLeft(),
1666
+ onClick: () => {
1667
+ r = c(r - 15), t.rotate(r + o).set();
1668
+ }
1669
+ }), m = new K({
1670
+ variant: "outline",
1671
+ size: "icon",
1672
+ label: lt(),
1673
+ icon: G.reset(),
1674
+ onClick: () => {
1675
+ o = 0, t.reset();
1676
+ }
1677
+ }), h = new K({
1678
+ variant: "outline",
1679
+ size: "icon",
1680
+ label: Tt(),
1681
+ icon: G.rotateRight(),
1682
+ onClick: () => {
1683
+ r = c(r + 15), t.rotate(r + o).set();
1684
+ }
1685
+ }), g = new K({
1686
+ variant: "outline",
1687
+ size: "icon",
1688
+ label: Nt(),
1689
+ icon: G.tiltDown(),
1690
+ onClick: () => {
1691
+ l(15);
1692
+ }
1693
+ }), _ = (e) => {
1694
+ let t = n.limits;
1695
+ t !== void 0 && (u.element.disabled = t.minZoom !== void 0 && e.zoom <= t.minZoom + Q, d.element.disabled = t.maxZoom !== void 0 && e.zoom >= t.maxZoom - Q, f.element.disabled = t.minTilt !== void 0 && e.tilt <= t.minTilt + Q, g.element.disabled = t.maxTilt !== void 0 && e.tilt >= t.maxTilt - Q, p.element.disabled = t.minRotate !== void 0 && e.rotation <= t.minRotate + Q, h.element.disabled = t.maxRotate !== void 0 && e.rotation >= t.maxRotate - Q);
699
1696
  };
700
- p.debug("Sending config", t), this.send(t);
1697
+ n.onChange((e) => {
1698
+ r = e.rotation - o, i = e.tilt, a = e.zoom, _(e);
1699
+ }), this.element.appendChild(e.buildZoom(u, d)), this.element.appendChild(e.buildDpad(f, p, m, h, g));
1700
+ }
1701
+ static buildZoom(e, t) {
1702
+ let n = document.createElement("div");
1703
+ return n.className = "vd-zoom", n.appendChild(e.element), n.appendChild(t.element), n;
1704
+ }
1705
+ static buildDpad(e, t, n, r, i) {
1706
+ let a = document.createElement("div");
1707
+ a.className = "vd-dpad";
1708
+ for (let [o, s] of [
1709
+ [e, "up"],
1710
+ [t, "left"],
1711
+ [n, "center"],
1712
+ [r, "right"],
1713
+ [i, "down"]
1714
+ ]) o.element.classList.add(`vd-dpad-${s}`), a.appendChild(o.element);
1715
+ return a;
1716
+ }
1717
+ }, hn = 5;
1718
+ function $(e) {
1719
+ let t = e.replace(/([A-Z])/g, " $1").trim();
1720
+ return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase();
1721
+ }
1722
+ function gn(e, t, n, r, i) {
1723
+ let a = document.createElement("div");
1724
+ a.className = "vd-attribute", a.dataset.name = e.name;
1725
+ let o = document.createElement("span");
1726
+ o.className = "vd-attribute-label", o.textContent = $(e.name), a.appendChild(o);
1727
+ let s = e.values.find((n) => t.getValue(e.name, n)?.isSelected === !0), c = e.values.map($), l = e.values.length <= hn ? new an(e.values, s, c) : new on(e.name, e.values, $(e.name), s, c);
1728
+ return l.onSelect((t) => {
1729
+ n.dispatch("ui:select", {
1730
+ attribute: e.name,
1731
+ value: t
1732
+ }, !0).defaultPrevented || r(e.name, t);
1733
+ }), i(e.name, (t) => {
1734
+ l.setSelected(t.value), n.dispatch("ui:selected", {
1735
+ attribute: e.name,
1736
+ value: t.value
1737
+ });
1738
+ }), a.appendChild(l.element), a;
1739
+ }
1740
+ //#endregion
1741
+ //#region src/ui/styles.ts
1742
+ var _n = Y.fontFamily.sans.join(", "), vn = `
1743
+ /* Override browser's default black fullscreen backdrop */
1744
+ .vd-container:fullscreen {
1745
+ background: oklch(100% 0 0);
1746
+ }
1747
+
1748
+ /* Top-right: screenshot + camera controls */
1749
+ .vd-toolbar {
1750
+ position: absolute;
1751
+ inset-block-start: ${J[3]};
1752
+ inset-inline-end: ${J[3]};
1753
+ display: flex;
1754
+ flex-direction: column;
1755
+ align-items: flex-end;
1756
+ gap: ${J[1.5]};
1757
+ z-index: 10;
1758
+ }
1759
+
1760
+ /* Bottom-right: AR + fullscreen */
1761
+ .vd-action-bar {
1762
+ position: absolute;
1763
+ inset-block-end: ${J[3]};
1764
+ inset-inline-end: ${J[3]};
1765
+ display: flex;
1766
+ flex-direction: row;
1767
+ gap: ${J[1.5]};
1768
+ z-index: 10;
1769
+ }
1770
+
1771
+ /* Camera controls card inside .vd-toolbar */
1772
+ .vd-camera-controls {
1773
+ display: flex;
1774
+ flex-direction: column;
1775
+ gap: ${J[1]};
1776
+ background: color-mix(in oklch, ${q.white} 85%, transparent);
1777
+ backdrop-filter: blur(8px);
1778
+ border-radius: ${J[2]};
1779
+ padding: ${J[2]};
1780
+ }
1781
+
1782
+ .vd-zoom {
1783
+ display: flex;
1784
+ gap: ${J[.5]};
1785
+ }
1786
+
1787
+ .vd-dpad {
1788
+ display: grid;
1789
+ grid-template-columns: repeat(3, ${J[9]});
1790
+ grid-template-rows: repeat(3, ${J[9]});
1791
+ gap: ${J[.5]};
1792
+ }
1793
+
1794
+ .vd-dpad-up { grid-column: 2; grid-row: 1; }
1795
+ .vd-dpad-left { grid-column: 1; grid-row: 2; }
1796
+ .vd-dpad-center { grid-column: 2; grid-row: 2; }
1797
+ .vd-dpad-right { grid-column: 3; grid-row: 2; }
1798
+ .vd-dpad-down { grid-column: 2; grid-row: 3; }
1799
+
1800
+ /* Bottom-left: attribute selectors */
1801
+ .vd-attributes {
1802
+ position: absolute;
1803
+ inset-block-end: ${J[3]};
1804
+ inset-inline-start: ${J[3]};
1805
+ display: flex;
1806
+ flex-direction: column;
1807
+ gap: ${J[2]};
1808
+ background: color-mix(in oklch, ${q.white} 85%, transparent);
1809
+ backdrop-filter: blur(8px);
1810
+ border-radius: ${J[2]};
1811
+ padding: ${J[2]};
1812
+ font-family: ${_n};
1813
+ z-index: 10;
1814
+ }
1815
+
1816
+ .vd-attribute {
1817
+ display: flex;
1818
+ flex-direction: column;
1819
+ gap: ${J[1]};
1820
+ }
1821
+
1822
+ .vd-attribute-label {
1823
+ font-size: ${Y.fontSize.xs};
1824
+ font-weight: ${Y.fontWeight.medium};
1825
+ color: ${q.neutral[900]};
1826
+ font-family: ${_n};
1827
+ }
1828
+
1829
+ `;
1830
+ function yn() {
1831
+ if (un(), document.getElementById("vd-styles") !== null) return;
1832
+ let e = document.createElement("style");
1833
+ e.id = "vd-styles", e.textContent = vn, document.head.appendChild(e);
1834
+ }
1835
+ //#endregion
1836
+ //#region src/ui/standard-interface.ts
1837
+ var bn = class {
1838
+ options;
1839
+ settings = {};
1840
+ container = null;
1841
+ loading = null;
1842
+ toolbar = null;
1843
+ actionBar = null;
1844
+ cameraControls = null;
1845
+ attributes = null;
1846
+ constructor(e) {
1847
+ this.options = e;
1848
+ }
1849
+ attach(e) {
1850
+ yn(), this.container = e, this.loading = new Vt(e), this.toolbar = new fn({ snapshotApi: this.options.snapshotApi }), this.toolbar.applySettings(this.settings), e.appendChild(this.toolbar.element), this.actionBar = new pn({
1851
+ onAr: this.options.onAr,
1852
+ onFullscreen: () => {
1853
+ document.fullscreenElement === null ? this.container?.requestFullscreen() : document.exitFullscreen();
1854
+ }
1855
+ }), this.actionBar.applySettings(this.settings), e.appendChild(this.actionBar.element);
1856
+ }
1857
+ onReady() {
1858
+ this.loading?.hide(), this.loading = null, this.cameraControls !== null && (this.cameraControls.element.style.display = "flex"), this.attributes !== null && (this.attributes.style.display = "flex");
1859
+ }
1860
+ updateProgress(e, t) {
1861
+ this.loading?.updateProgress(e, t);
1862
+ }
1863
+ showError(e) {
1864
+ this.loading?.showError(e);
1865
+ }
1866
+ applyViewerSettings(e) {
1867
+ this.settings = e, e.language !== void 0 && A(e.language), this.toolbar?.applySettings(e), this.actionBar?.applySettings(e), this.cameraControls !== null && (this.cameraControls.element.style.display = e.cameraControls === !0 ? "flex" : "none");
701
1868
  }
702
- }, L = class {
1869
+ renderAttributeControls(e, t) {
1870
+ if (!(this.container === null || this.toolbar === null) && (this.cameraControls?.element.remove(), this.cameraControls = null, this.attributes?.remove(), this.attributes = null, this.settings.cameraControls === !0 && (this.cameraControls = new mn(this.options.cameraApi, this.options.cameraPositionApi), this.cameraControls.element.style.display = "none", this.toolbar.element.appendChild(this.cameraControls.element)), t.length !== 0)) {
1871
+ this.attributes = document.createElement("div"), this.attributes.className = "vd-attributes", this.attributes.style.display = "none";
1872
+ for (let n of t) this.attributes.appendChild(gn(n, e, this.options.eventBus, this.options.onSelect, this.options.onSubscribe));
1873
+ this.container.appendChild(this.attributes);
1874
+ }
1875
+ }
1876
+ get viewerSettings() {
1877
+ return this.settings;
1878
+ }
1879
+ destroy() {
1880
+ this.loading?.destroy(), this.toolbar?.element.remove(), this.actionBar?.destroy(), this.actionBar?.element.remove(), this.cameraControls?.element.remove(), this.attributes?.remove(), this.container = null;
1881
+ }
1882
+ }, xn = class {
703
1883
  attributes;
704
1884
  readyStateTracker;
705
1885
  messenger;
706
- viewerService;
707
1886
  cameraApi;
708
1887
  cameraPositionApi;
709
1888
  snapshotApi;
1889
+ eventBus;
1890
+ ui;
1891
+ qrModal;
710
1892
  options;
711
1893
  iframe;
1894
+ container;
712
1895
  readyCallbacks = [];
713
1896
  mutationFailedCallbacks = [];
714
1897
  constructor(e) {
715
- this.options = e, this.initializeLogging(), this.messenger = new A(() => this.iframe?.contentWindow ?? null);
1898
+ this.options = e, this.initializeLogging(), this.messenger = new fe(() => this.iframe?.contentWindow ?? null);
716
1899
  let t = (e) => {
717
1900
  this.messenger.send(e);
718
1901
  };
719
- this.attributes = new j(t, (e, t) => {
1902
+ this.attributes = new pe(t, (e, t) => {
720
1903
  for (let n of this.mutationFailedCallbacks) n({
721
1904
  attribute: e,
722
1905
  value: t
723
1906
  });
724
- }), this.readyStateTracker = new g(() => {
1907
+ }), this.snapshotApi = new _e(t), this.cameraApi = new me(t), this.cameraPositionApi = new he(), this.eventBus = new ve(), this.qrModal = new cn(), this.readyStateTracker = new _(() => {
725
1908
  let e = this.readyCallbacks;
726
1909
  this.readyCallbacks = [];
727
1910
  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();
1911
+ this.ui.onReady();
1912
+ }), this.ui = new bn({
1913
+ snapshotApi: this.snapshotApi,
1914
+ cameraApi: this.cameraApi,
1915
+ cameraPositionApi: this.cameraPositionApi,
1916
+ eventBus: this.eventBus,
1917
+ onSelect: (e, t) => this.attributes.select(e, t),
1918
+ onSubscribe: (e, t) => this.attributes.onChange(e, t),
1919
+ onAr: () => {
1920
+ this.qrModal.show(this.iframe?.src ?? "");
1921
+ }
1922
+ }), this.registerHandlers(), this.messenger.start(), this.createAndConnectIframe();
729
1923
  }
730
1924
  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());
1925
+ this.messenger.on(y.HANDSHAKE, (e) => {
1926
+ ie(e) && (m.info("Server handshake received, flushing publisher queue"), this.messenger.markReady());
1927
+ }), this.messenger.on(y.MUTATION, (e) => {
1928
+ ae(e) && this.attributes.handleMutationResult(e);
1929
+ }), this.messenger.on(y.SNAPSHOT, (e) => {
1930
+ oe(e) && this.snapshotApi.handleSnapshotResult(e);
1931
+ }), this.messenger.on(y.CAMERA, (e) => {
1932
+ se(e) && this.cameraPositionApi.handlePositionUpdate(e);
1933
+ }), this.messenger.on(y.LOADING_INDICATOR, (e) => {
1934
+ le(e) && (e.error === void 0 ? this.ui.updateProgress(e.progress, e.phase) : this.ui.showError(e.error));
1935
+ }), this.messenger.on(y.CONFIG, (e) => {
1936
+ ue(e) && (e.viewerSettings !== void 0 && this.ui.applyViewerSettings(e.viewerSettings), this.cameraPositionApi.applyLimits(e.cameraLimits));
1937
+ }), this.messenger.on(y.MAPPING, (e) => {
1938
+ ce(e) && (this.attributes.handleMappingReceived(e), this.ui.renderAttributeControls(this.attributes, e.attributes), this.readyStateTracker.handleMappingReceived());
741
1939
  });
742
1940
  }
743
1941
  initializeLogging() {
744
- this.options.debug === !0 && m(), p.info("Initializing Virtualdisplay client", {
745
- model: this.options.model,
746
- debug: this.options.debug,
747
- language: this.options.language
748
- });
1942
+ this.options.debug === !0 && h(), m.info("Initializing Virtualdisplay client", { model: this.options.model });
749
1943
  }
750
1944
  createAndConnectIframe() {
751
- p.debug("Creating iframe for Virtualdisplay viewer"), this.iframe = new n(this.options).create(), p.debug("Iframe created, waiting for server READY");
1945
+ let e = new r(this.options).create();
1946
+ this.container = e.container, this.iframe = e.iframe, this.eventBus.attach(this.container), this.ui.attach(this.container);
752
1947
  }
753
1948
  select(e, t) {
754
1949
  this.attributes.select(e, t);
@@ -778,8 +1973,8 @@ var j = class {
778
1973
  onMutationFailed(e) {
779
1974
  this.mutationFailedCallbacks.push(e);
780
1975
  }
781
- get viewer() {
782
- return this.viewerService;
1976
+ on(e, t) {
1977
+ this.eventBus.on(e, t);
783
1978
  }
784
1979
  get camera() {
785
1980
  return this.cameraApi;
@@ -790,9 +1985,12 @@ var j = class {
790
1985
  get snapshot() {
791
1986
  return this.snapshotApi;
792
1987
  }
1988
+ get viewerSettings() {
1989
+ return this.ui.viewerSettings;
1990
+ }
793
1991
  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");
1992
+ this.readyStateTracker.clear(), this.readyCallbacks = [], this.messenger.destroy(), this.qrModal.hide(), this.ui.destroy(), this.container?.parentElement !== null && this.container?.parentElement !== void 0 && this.container.parentElement.removeChild(this.container), this.container = void 0, this.iframe = void 0;
795
1993
  }
796
1994
  };
797
1995
  //#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 };
1996
+ export { ee as Attribute, v as AttributeValue, me as Camera, t as ERROR_CODES, ge as Photo, _e as Snapshot, xn as VirtualdisplayClient, n as VirtualdisplayError, h as enableDebugMode, g as isDebugMode, m as logger };