@virtualdisplay.io/client 4.0.0-alpha.1 → 4.0.0-alpha.10

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