@neurodyn/core-ar 0.0.0-canary-20260714140041

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.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # `@neurodyn/core-ar`
2
+
3
+ Framework-agnostic Core AR runtime for the Vizbl and Arizo Web Components.
4
+
5
+ ## CDN installation
6
+
7
+ The embed bundle follows the same self-registering CDN contract as Room Viewer
8
+ and Model Viewer. Load it once and then add any number of branded elements:
9
+
10
+ ```html
11
+ <script
12
+ defer
13
+ src="https://cdn.neurodyn.ai/@neurodyn/core-ar/dist/core-ar.embed.min.js"
14
+ ></script>
15
+
16
+ <vizbl-core
17
+ api-key="PUBLIC_WIDGET_KEY"
18
+ type="ar-viewer"
19
+ object-id="OBJECT_TINUUID"
20
+ variant-id="OPTIONAL_MATERIAL_HID"
21
+ >
22
+ View in my room
23
+ </vizbl-core>
24
+ ```
25
+
26
+ The credential attribute is `api-key`, not `key`. React reserves `key` for its
27
+ own reconciliation and does not forward it to Custom Elements.
28
+
29
+ The tag selects the tenant:
30
+
31
+ - `<vizbl-core>` uses Vizbl;
32
+ - `<arizo-core>` uses Arizo.
33
+
34
+ Production is the default. Add the boolean `dev` attribute only for the dev
35
+ API and viewer:
36
+
37
+ ```html
38
+ <arizo-core
39
+ dev
40
+ api-key="PUBLIC_WIDGET_KEY"
41
+ type="advanced-ar"
42
+ object-id="OBJECT_TINUUID"
43
+ ></arizo-core>
44
+ ```
45
+
46
+ As with every HTML boolean attribute, its presence means `true`; do not use
47
+ `dev="false"`. Vizbl and Arizo elements can coexist on the same page because
48
+ each element resolves and retains its own runtime configuration.
49
+
50
+ The unversioned CDN path resolves to the latest published package version.
51
+ Consumers do not install the npm package and do not call an initialization
52
+ function.
53
+
54
+ ## Canary verification
55
+
56
+ Before a stable release, verify the package published from `develop` through
57
+ the `canary` dist-tag. Start a static server from the repository root:
58
+
59
+ ```sh
60
+ python3 -m http.server 4173 --directory packages/core-ar-widget/examples
61
+ ```
62
+
63
+ Then open `http://localhost:4173/cdn-smoke.html`. The page loads
64
+ `@neurodyn/core-ar@canary` by default and can also check the stable `latest`
65
+ channel. Add `localhost` to the domains allowed for the public widget key;
66
+ never commit a real key to the smoke page.
67
+
68
+ Supported types are `ar-viewer`, `ar-one-click`, and `advanced-ar`.
69
+ `size-type="cm|inch"` is supported by `ar-viewer`.
70
+
71
+ ## Package registration
72
+
73
+ Package consumers can register both branded elements explicitly:
74
+
75
+ ```ts
76
+ import { registerCoreArWidgets } from '@neurodyn/core-ar'
77
+
78
+ registerCoreArWidgets()
79
+ ```
80
+
81
+ ## Styling
82
+
83
+ Use `trigger-class` to apply CSS or generated Tailwind classes directly to the
84
+ internal trigger. Tailwind projects must keep dynamic classes in their source
85
+ scan or safelist.
86
+
87
+ ```html
88
+ <vizbl-core
89
+ trigger-class="rounded-full bg-black px-6 py-3 text-white"
90
+ unstyled
91
+ api-key="PUBLIC_WIDGET_KEY"
92
+ type="ar-viewer"
93
+ object-id="OBJECT_TINUUID"
94
+ ></vizbl-core>
95
+ ```
96
+
97
+ Without `unstyled`, these CSS variables customize the default trigger:
98
+
99
+ - `--core-ar-widget-background`
100
+ - `--core-ar-widget-background-hover`
101
+ - `--core-ar-widget-background-active`
102
+ - `--core-ar-widget-border-color`
103
+ - `--core-ar-widget-border-radius`
104
+ - `--core-ar-widget-color`
105
+ - `--core-ar-widget-focus-ring`
106
+ - `--core-ar-widget-font-family`
107
+ - `--core-ar-widget-font-size`
108
+ - `--core-ar-widget-font-weight`
109
+ - `--core-ar-widget-padding`
110
+
111
+ ## Events
112
+
113
+ Both branded elements dispatch bubbling `core-ar-ready` and `core-ar-error`
114
+ events. Error details contain a safe error code, object ID, and type; the
115
+ public key is never included.
116
+
117
+ ## Runtime flow
118
+
119
+ The first activation uses `POST /public/widget/activate-core`. Its short-lived
120
+ widget session is then used for object reads and analytics. `POST
121
+ /public/widget/token` is only used to refresh an existing session; a transient
122
+ refresh failure falls back to a fresh `activate-core` request.
@@ -0,0 +1,68 @@
1
+ (()=>{"use strict";let e,t,i;function r(e,t){let i;try{i=new URL(e)}catch{throw TypeError(`${t} must be an absolute HTTP(S) URL`)}if("http:"!==i.protocol&&"https:"!==i.protocol)throw TypeError(`${t} must be an absolute HTTP(S) URL`);return i.origin}function o(){if(!e)throw Error("Core AR widget runtime is not configured");return e}function n(){return"u"<typeof navigator?"":navigator.userAgent||navigator.vendor||""}function a(){if("u"<typeof window||"u"<typeof navigator)return!1;let e="MacIntel"===navigator.platform&&navigator.maxTouchPoints>1;return(/iPad|iPhone|iPod/.test(n())||e)&&!("MSStream"in window)}function s(){if("u"<typeof window||"u"<typeof navigator)return!1;let e=navigator.maxTouchPoints>1,t=window.matchMedia?.("(pointer: coarse)").matches??!1,i=/Android|iPhone|iPad|iPod|webOS|BlackBerry|Windows Phone|Opera Mini|IEMobile|Mobile/i.test(n());return e&&t&&(i||a())}function l(){let e=n();return[[/edg/i,"Microsoft Edge"],[/trident/i,"Microsoft Internet Explorer"],[/firefox|fxios/i,"Mozilla Firefox"],[/opr\//i,"Opera"],[/ucbrowser/i,"UC Browser"],[/samsungbrowser/i,"Samsung Browser"],[/chrome|chromium|crios/i,"Google Chrome"],[/safari/i,"Apple Safari"]].find(([t])=>t.test(e))?.[1]??"Other"}function c(){let e=function(){let e=n(),t=e.match(/\bOS (\d+)/)?.[1]??e.match(/Version\/(\d+)/)?.[1];if(!t)return null;let i=Number.parseInt(t,10);return Number.isNaN(i)?null:i}();return null!==e&&e>=17}function d(e){let t;try{t=new URL(e)}catch{throw TypeError("Native AR asset URL must be absolute HTTP(S)")}if("http:"!==t.protocol&&"https:"!==t.protocol)throw TypeError("Native AR asset URL must be absolute HTTP(S)");return t.href}function u(e,t){let i=d(e.glbUrl),r=d(e.usdzUrl),o=encodeURIComponent(i),n=`intent://arvr.google.com/scene-viewer/1.0?file=${o}&resizable=false${"wall"===t||"ceiling"===t?"&enable_vertical_placement=true":""}&mode=ar_preferred#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;end;`,s=`${r}#allowsContentScaling=0`;return a()?s:n}function g(e,t=0){let i=document.createElement("a"),r=document.createElement("img");i.rel="ar",i.href=e,i.hidden=!0,r.alt="",r.hidden=!0,i.appendChild(r),document.body.appendChild(i),window.setTimeout(()=>{i.click(),i.remove()},t)}function h(e,t){let i=function(e,t){let{brand:i}=t??o();if(a()){let t=new URL("https://appclip.apple.com/id");return t.searchParams.set("p","com.us.vizbl.Clip"),t.searchParams.set("tinuuid",e),t.searchParams.set("region","arizo"===i?"ru":"en"),t.toString()}return`intent:/${window.location.pathname}#Intent;scheme=open;package=com.us.vizbl;end`}(e,t);window.self===window.top||l().toLowerCase().includes("edge")?window.location.assign(i):window.open(i,"_top")}function p(e,t,i=!1,r){let{viewerOrigin:n}=r??o(),a=new URL("/ar/index.html",n);return a.searchParams.set("tinuuid",e),t&&a.searchParams.set("hid",t),i&&a.searchParams.set("appview","true"),a.toString()}function m(){let e=new WeakMap;return function(t){let i=e.get(t);i?.cleanup?.();let r={revision:(i?.revision??0)+1};return e.set(t,r),{isCurrent:()=>e.get(t)===r,register(i){let o=!1,n=()=>{o||(o=!0,i())};return e.get(t)!==r?(n(),n):(r.cleanup=n,()=>{e.get(t)===r&&e.delete(t),n()})}}}}let f=0;function w(){return window.QRCode}async function v(e,r="vizblQrModal",n,a){let s,l=++f;i?.(),document.querySelectorAll("[data-vizbl-qr-modal]").forEach(e=>e.remove());let c=function(e){let{brand:t,viewerOrigin:i}=e??o(),r="arizo"===t,n=document.createElement("div");n.dataset.vizblQrModal="",n.setAttribute("aria-label","View object in augmented reality"),n.setAttribute("aria-modal","true"),n.setAttribute("role","dialog"),Object.assign(n.style,{alignItems:"center",backgroundColor:"rgba(0,0,0,0.25)",display:"none",height:"100%",justifyContent:"center",left:"0",position:"fixed",top:"0",width:"100%",zIndex:"9999"});let a=document.createElement("div");Object.assign(a.style,{backgroundColor:"#fff",borderRadius:"12px",boxSizing:"border-box",color:"#000",fontFamily:"Inter, sans-serif",minHeight:"372px",padding:"48px 52px 40px",position:"relative",width:"min(460px, calc(100vw - 32px))"});let s=document.createElement("button");s.type="button",s.dataset.vizblQrClose="",s.setAttribute("aria-label","Close QR code"),s.textContent="\xd7",Object.assign(s.style,{background:"none",border:"none",color:"#AEB1B7",cursor:"pointer",fontSize:"24px",position:"absolute",right:"20px",top:"20px"});let l=document.createElement("div");l.dataset.vizblQrCode="",l.style.margin="0 auto",l.style.width="fit-content";let c=document.createElement("p");c.textContent="Scan this QR code with your phone camera to view the object in Augmented Reality",Object.assign(c.style,{fontSize:"16px",fontWeight:"500",lineHeight:"24px",margin:"24px 0 0",textAlign:"center"});let d=document.createElement("p");d.textContent=r?"*Требуется устройство и браузер с поддержкой AR. Рекомендуем iOS 12 или новее с Safari либо Android 7.0 или новее с Chrome.":"*Requires an AR-compatible device and browser. We recommend iOS 12 or later with Safari, or Android 7.0 or later with Chrome.",Object.assign(d.style,{color:"#93979F",fontSize:"12px",lineHeight:"16px",margin:"12px 0 0",textAlign:"center"});let u=document.createElement("p");u.textContent=`Powered by ${r?"Arizo":"Vizbl"}`,Object.assign(u.style,{color:"#93979F",fontSize:"11px",letterSpacing:"1.5px",margin:"24px 0 4px",textAlign:"center",textTransform:"uppercase"});let g=document.createElement("a");g.href=r?"https://arizo.ru":"https://vizbl.com",g.style.display="block",g.style.textAlign="center";let h=document.createElement("img");return h.alt=`${r?"Arizo":"Vizbl"} logo`,h.src=`${i}/${r?"logoArizo.svg":"logo.svg"}`,h.height=20,h.width=r?76:60,g.appendChild(h),a.append(s,l,c,d,u,g),n.appendChild(a),n}(a);c.id=r;let d=c.querySelector("[data-vizbl-qr-close]"),u=Array.from(c.querySelectorAll("button, a[href]")),g=!1,h=e=>{if("Escape"===e.key){e.preventDefault(),s();return}if("Tab"!==e.key||0===u.length)return;let t=u[0],i=u.at(-1)??t;e.shiftKey&&(document.activeElement===t||!c.contains(document.activeElement))?(e.preventDefault(),i.focus()):e.shiftKey||document.activeElement!==i||(e.preventDefault(),t.focus())};i=s=()=>{g||(g=!0,document.removeEventListener("keydown",h),c.remove(),i===s&&(i=void 0),n?.focus({preventScroll:!0}))},d?.addEventListener("click",s),c.addEventListener("click",e=>{e.target===c&&s()}),document.body.appendChild(c),document.addEventListener("keydown",h),function(){let e="https://fonts.googleapis.com/css2?family=Inter&display=swap";if(Array.from(document.querySelectorAll('link[rel="stylesheet"]')).some(t=>t.href===e||t.getAttribute("href")===e))return;let t=document.createElement("link");t.rel="stylesheet",t.href=e,document.head.appendChild(t)}();try{let i=await function(e){let i=w();if(i)return Promise.resolve(i);if(t)return t;let{qrCodeScriptUrl:r}=e??o();return t=new Promise((e,i)=>{let o,n=document.createElement("script"),a=()=>{o();let r=w();r?e(r):(n.remove(),t=void 0,i(Error("QR code runtime did not expose QRCode")))},s=()=>{o(),n.remove(),t=void 0,i(Error("Failed to load QR code runtime"))};o=()=>{n.removeEventListener("load",a),n.removeEventListener("error",s)},n.addEventListener("load",a,{once:!0}),n.addEventListener("error",s,{once:!0}),n.src=r,n.async=!0,n.dataset.vizblQrScript="true",document.head.appendChild(n)})}(a);if(l!==f||!c.isConnected)return s(),s;let r=c.querySelector("[data-vizbl-qr-code]");return r&&new i(r,{correctLevel:i.CorrectLevel.L,height:148,text:e,width:148}),c.style.display="flex",d?.focus({preventScroll:!0}),s}catch(e){throw s(),e}}class b extends Error{code;constructor(e){super(e),this.code=e,this.name="WidgetRuntimeError"}}function y(e){return e.runtimeConfig??o()}async function E(e,t,i){try{return await (i.fetchImpl??i.runtimeConfig?.fetchImpl??o().fetchImpl)(e,{...t,signal:i.signal})}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)throw e;throw new b("NETWORK_ERROR")}}async function A(e){try{let t=await e.json();return[t.code,t.error,t.message].flatMap(e=>Array.isArray(e)?e:[e]).filter(e=>"string"==typeof e).join(" ").toLowerCase()}catch{return""}}async function C(e,t){let i=await A(e);return new b(429===e.status?"RATE_LIMITED":e.status>=500?"API_UNAVAILABLE":404===e.status&&"object"===t?"OBJECT_NOT_FOUND":i.includes("domain")||i.includes("origin")?"DOMAIN_NOT_ALLOWED":i.includes("revoked")?"KEY_REVOKED":i.includes("expired")?"activation"===t?"KEY_EXPIRED":"SESSION_EXPIRED":i.includes("policy")||i.includes("not available for this object")?"POLICY_DENIED":401===e.status||403===e.status?"activation"===t?"INVALID_KEY":"SESSION_EXPIRED":"REQUEST_FAILED")}async function I(e){try{return await e.json()}catch{throw new b("INVALID_RESPONSE")}}function S(e){if(!e||"object"!=typeof e||"string"!=typeof e.token||!e.token.trim()||"string"!=typeof e.sid||!e.sid.trim()||"number"!=typeof e.expiresIn||!Number.isFinite(e.expiresIn)||e.expiresIn<=0)throw new b("INVALID_RESPONSE");return e}async function x(e,t={}){let{apiOrigin:i}=y(t),r=await E(`${i}/public/widget/activate-core`,{cache:"no-store",headers:{"x-api-key":e},method:"POST"},t);if(!r.ok)throw await C(r,"activation");let o=await I(r);if("string"!=typeof o.productType)throw new b("INVALID_RESPONSE");return{...S(o.widgetSession),productType:o.productType}}async function T(e,t,i={}){let{apiOrigin:r}=y(i),o=await E(`${r}/public/widget/token`,{body:JSON.stringify({token:t}),cache:"no-store",headers:{"Content-Type":"application/json","x-api-key":e},method:"POST"},i);if(!o.ok)throw await C(o,"token");return S(await I(o))}async function k(e,t,i={}){let{apiOrigin:r}=y(i),o=await E(`${r}/public/objects/by-tinuuid/${encodeURIComponent(e)}`,{cache:"no-store",headers:{"x-widget-session-token":t},method:"GET"},i);if(!o.ok)throw await C(o,"object");let n=await I(o);if(!n||"object"!=typeof n||Array.isArray(n))throw new b("INVALID_RESPONSE");return n}async function L(e,t,i,r={}){let{apiOrigin:o}=y(r),n=await E(`${o}/public/widget/events`,{body:JSON.stringify({event:e,...i?{objectId:i}:{},timestamp:new Date().toISOString()}),cache:"no-store",headers:{"Content-Type":"application/json","x-widget-session-token":t},method:"POST"},r);if(!n.ok)throw await C(n,"event");let a=await I(n);if(!a||"object"!=typeof a||Array.isArray(a)||"boolean"!=typeof a.ok)throw new b("INVALID_RESPONSE")}async function O(e,t,i){let r=i??o(),{fetchImpl:n,legacyApiOrigin:a}=r,s=await E(`${a}/obj/Fetch`,{body:JSON.stringify({tinuuid:e}),headers:{"Content-Type":"application/json"},method:"POST"},{fetchImpl:n,runtimeConfig:r,signal:t});if(!s.ok)throw await C(s,"object");let l=await I(s);if(!l||"object"!=typeof l||Array.isArray(l))throw new b("INVALID_RESPONSE");return l}async function z(e,t){let{fetchImpl:i,legacyApiOrigin:r}=t??o();await i(`${r}/obj/IncrViews`,{body:JSON.stringify({tinuuid:e}),headers:{"Content-Type":"application/json"},method:"POST"})}function R(e){try{let t=e?.();t instanceof Promise&&t.catch(()=>void 0)}catch{}}let N=m();function P(e){if(e?.aborted)throw new DOMException("Aborted","AbortError")}async function D(e,t={}){let i,r,o=N(e),n=e.getAttribute("data-native-link"),d=e.dataset.objectTinuuid?.trim(),m=e.dataset.objectHid?.trim()||void 0;if(!d)throw Error("Object ID is required");P(t.signal);let f=t.loadObject??((e,i)=>O(e,i,t.runtimeConfig)),w=t.object??await f(d,t.signal);if(P(t.signal),!o.isCurrent())return()=>void 0;let b=m?w.materials.find(e=>e.hid===m):w.materials[0];if(!b?.native?.glbUrl||!b.native.usdzUrl)throw Error("Native AR assets are unavailable");let y=u(b.native,w.type),E=s(),A=!1,C=0;e.querySelector("img")||((i=document.createElement("img")).alt="",i.hidden=!0,e.appendChild(i));let I=()=>t.trackView?t.trackView():z(d,t.runtimeConfig),S=i=>{i.preventDefault(),R(()=>t.onAction?.("widget_opened"));let n=++C;r?.(),r=void 0,v(p(d,m,!0,t.runtimeConfig),`vizbl-${d}`,e,t.runtimeConfig).then(e=>{n===C&&o.isCurrent()?r=e:e()}).catch(()=>void 0)},x=async e=>{let i;if(i=l().toLowerCase(),["firefox","edge","opera"].some(e=>i.includes(e))){if(e.preventDefault(),A)return;A=!0,R(()=>t.onAction?.("ar_launched"));try{await Promise.resolve(I()).catch(()=>void 0)}finally{A=!1,a()&&c()?h(d,t.runtimeConfig):g(y)}return}R(()=>t.onAction?.("ar_launched")),R(I),a()&&c()?(e.preventDefault(),h(d,t.runtimeConfig)):g(y)};return E?(e.dataset.nativeLink=y,e.addEventListener("click",x)):e.addEventListener("click",S),o.register(()=>{C+=1,e.removeEventListener("click",S),e.removeEventListener("click",x),r?.(),i?.remove(),null===n?e.removeAttribute("data-native-link"):e.setAttribute("data-native-link",n)})}var j=!1;if("u">typeof window){var _={get passive(){j=!0;return}};window.addEventListener("testPassive",null,_),window.removeEventListener("testPassive",null,_)}var M="u">typeof window&&window.navigator&&window.navigator.platform&&(/iP(ad|hone|od)/.test(window.navigator.platform)||"MacIntel"===window.navigator.platform&&window.navigator.maxTouchPoints>1),U=[],$=!1,V=-1,B=void 0,q=void 0,H=void 0,W=function(e){return U.some(function(t){return!!(t.options.allowTouchMove&&t.options.allowTouchMove(e))})},F=function(e){var t=e||window.event;return!!W(t.target)||t.touches.length>1||(t.preventDefault&&t.preventDefault(),!1)},Q=function(e){if(void 0===H){var t=!!e&&!0===e.reserveScrollBarGap,i=window.innerWidth-document.documentElement.clientWidth;if(t&&i>0){var r=parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right"),10);H=document.body.style.paddingRight,document.body.style.paddingRight=r+i+"px"}}void 0===B&&(B=document.body.style.overflow,document.body.style.overflow="hidden")},Y=function(){void 0!==H&&(document.body.style.paddingRight=H,H=void 0),void 0!==B&&(document.body.style.overflow=B,B=void 0)},K=function(){if(void 0!==q){var e=-parseInt(document.body.style.top,10),t=-parseInt(document.body.style.left,10);document.body.style.position=q.position,document.body.style.top=q.top,document.body.style.left=q.left,window.scrollTo(t,e),q=void 0}},X=function(e,t){var i=e.targetTouches[0].clientY-V;return!W(e.target)&&(t&&0===t.scrollTop&&i>0?F(e):t&&t.scrollHeight-t.scrollTop<=t.clientHeight&&i<0?F(e):(e.stopPropagation(),!0))},J=function(e,t){e?!U.some(function(t){return t.targetElement===e})&&(U=[].concat(function(e){if(!Array.isArray(e))return Array.from(e);for(var t=0,i=Array(e.length);t<e.length;t++)i[t]=e[t];return i}(U),[{targetElement:e,options:t||{}}]),M?window.requestAnimationFrame(function(){if(void 0===q){q={position:document.body.style.position,top:document.body.style.top,left:document.body.style.left};var e=window,t=e.scrollY,i=e.scrollX,r=e.innerHeight;document.body.style.position="fixed",document.body.style.top=-t,document.body.style.left=-i,setTimeout(function(){return window.requestAnimationFrame(function(){var e=r-window.innerHeight;e&&t>=r&&(document.body.style.top=-(t+e))})},300)}}):Q(t),M&&(e.ontouchstart=function(e){1===e.targetTouches.length&&(V=e.targetTouches[0].clientY)},e.ontouchmove=function(t){1===t.targetTouches.length&&X(t,e)},$||(document.addEventListener("touchmove",F,j?{passive:!1}:void 0),$=!0))):console.error("disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.")},G=function(e){e?(U=U.filter(function(t){return t.targetElement!==e}),M&&(e.ontouchstart=null,e.ontouchmove=null,$&&0===U.length&&(document.removeEventListener("touchmove",F,j?{passive:!1}:void 0),$=!1)),M?K():Y()):console.error("enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.")};let Z=["cm","inch"],ee="vizbl_viewer",et=m(),ei=m();function er(e){if(e?.aborted)throw new DOMException("Aborted","AbortError")}async function eo(e,t={}){let i,r,o=ei(e),n=e.getAttribute("href"),a=e.getAttribute("rel"),l=e.dataset.objectTinuuid?.trim(),c=e.dataset.objectHid?.trim()||void 0;if(!l)throw Error("Object ID is required");er(t.signal);let d=t.loadObject??((e,i)=>O(e,i,t.runtimeConfig)),g=t.object??await d(l,t.signal);if(er(t.signal),!o.isCurrent())return()=>void 0;let h=c?g.materials.find(e=>e.hid===c):g.materials[0];if(!h?.native?.glbUrl||!h.native.usdzUrl)throw Error("Native AR assets are unavailable");let m=u(h.native,g.type),f=s(),w=0;e.rel="ar",e.querySelector("img")||((i=document.createElement("img")).alt="",i.hidden=!0,e.appendChild(i));let b=i=>{i.preventDefault(),R(()=>t.onAction?.("widget_opened"));let n=++w;r?.(),r=void 0,v(p(l,c,!1,t.runtimeConfig),`vizbl-${l}`,e,t.runtimeConfig).then(e=>{n===w&&o.isCurrent()?r=e:e()}).catch(()=>void 0)},y=()=>{R(t.trackView??(()=>z(l,t.runtimeConfig))),R(()=>t.onAction?.("ar_launched"))};return f?(e.href=m,e.addEventListener("click",y)):(e.href="#",e.addEventListener("click",b)),o.register(()=>{w+=1,e.removeEventListener("click",b),e.removeEventListener("click",y),r?.(),i?.remove(),null===n?e.removeAttribute("href"):e.setAttribute("href",n),null===a?e.removeAttribute("rel"):e.setAttribute("rel",a)})}let en=["advanced-ar","ar-one-click","ar-viewer"],ea={arizo:"arizo-core",vizbl:"vizbl-core"},es=new Set(["core-ar","core-ar-shopify"]),el={"advanced-ar":"View in advanced AR","ar-one-click":"View in AR","ar-viewer":"View in 3D & AR"},ec=["api-key","dev","object-id","size-type","trigger-class","type","variant-id"],ed=new Set(["API_UNAVAILABLE","INVALID_RESPONSE","NETWORK_ERROR","REQUEST_FAILED","SESSION_EXPIRED"]),eu="core-ar-widget-styles";class eg extends Error{code;constructor(e){super(e),this.code=e,this.name="CoreArWidgetComponentError"}}function eh(e){return en.includes(e)}let ep="u"<typeof HTMLElement?class{}:HTMLElement;class em extends ep{static get observedAttributes(){return[...ec]}get triggerClass(){return this.getAttribute("trigger-class")??""}set triggerClass(e){let t="string"==typeof e?e.trim():"";t?this.setAttribute("trigger-class",t):this.removeAttribute("trigger-class")}get unstyled(){return this.hasAttribute("unstyled")}set unstyled(e){this.toggleAttribute("unstyled",!!e)}abortController;actionResetTimer;cleanup;customLabelNodes=null;initializationRevision=0;object;queued=!1;session;trigger;connectedCallback(){!function(){if(document.getElementById(eu))return;let e=document.createElement("style");e.id=eu,e.textContent=`
2
+ :where(vizbl-core, arizo-core) {
3
+ display: inline-block;
4
+ --core-ar-widget-background: #3556f0;
5
+ --core-ar-widget-background-hover: #2743cc;
6
+ --core-ar-widget-background-active: #2239ad;
7
+ --core-ar-widget-border-color: #3556f0;
8
+ --core-ar-widget-border-radius: 0.5rem;
9
+ --core-ar-widget-color: #ffffff;
10
+ --core-ar-widget-focus-ring: #3556f0;
11
+ --core-ar-widget-font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
12
+ --core-ar-widget-font-size: 0.875rem;
13
+ --core-ar-widget-font-weight: 500;
14
+ --core-ar-widget-padding: 0.375rem 0.75rem;
15
+ }
16
+
17
+ :where(vizbl-core, arizo-core):not([unstyled]) [data-core-ar-widget-trigger] {
18
+ all: unset;
19
+ align-items: center;
20
+ background: var(--core-ar-widget-background);
21
+ border: 1px solid var(--core-ar-widget-border-color);
22
+ border-radius: var(--core-ar-widget-border-radius);
23
+ box-sizing: border-box;
24
+ color: var(--core-ar-widget-color);
25
+ cursor: pointer;
26
+ display: inline-flex;
27
+ font-family: var(--core-ar-widget-font-family);
28
+ font-size: var(--core-ar-widget-font-size);
29
+ font-weight: var(--core-ar-widget-font-weight);
30
+ justify-content: center;
31
+ line-height: 1.25;
32
+ min-height: 2rem;
33
+ padding: var(--core-ar-widget-padding);
34
+ text-align: center;
35
+ text-decoration: none;
36
+ transition: background-color 150ms ease, border-color 150ms ease, transform 100ms ease;
37
+ -webkit-tap-highlight-color: transparent;
38
+ }
39
+
40
+ :where(vizbl-core, arizo-core):not([unstyled]) [data-core-ar-widget-trigger]:hover {
41
+ background: var(--core-ar-widget-background-hover);
42
+ border-color: var(--core-ar-widget-background-hover);
43
+ }
44
+
45
+ :where(vizbl-core, arizo-core):not([unstyled]) [data-core-ar-widget-trigger]:active {
46
+ background: var(--core-ar-widget-background-active);
47
+ border-color: var(--core-ar-widget-background-active);
48
+ transform: translateY(1px);
49
+ }
50
+
51
+ :where(vizbl-core, arizo-core):not([unstyled]) [data-core-ar-widget-trigger]:focus-visible {
52
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--core-ar-widget-focus-ring) 45%, transparent);
53
+ outline: 2px solid var(--core-ar-widget-focus-ring);
54
+ outline-offset: 2px;
55
+ }
56
+
57
+ :where(vizbl-core, arizo-core)[data-state="loading"] [data-core-ar-widget-trigger],
58
+ :where(vizbl-core, arizo-core)[data-state="opening"] [data-core-ar-widget-trigger],
59
+ :where(vizbl-core, arizo-core)[data-state="error"] [data-core-ar-widget-trigger] {
60
+ cursor: wait;
61
+ opacity: 0.65;
62
+ pointer-events: none;
63
+ }
64
+
65
+ :where(vizbl-core, arizo-core)[data-state="error"] [data-core-ar-widget-trigger] {
66
+ cursor: not-allowed;
67
+ }
68
+ `,document.head.appendChild(e)}(),this.captureCustomLabel(),this.queueInitialization()}disconnectedCallback(){this.initializationRevision+=1,this.abortController?.abort(),this.abortController=void 0,this.cleanup?.(),this.cleanup=void 0,this.clearActionResetTimer()}attributeChangedCallback(e,t,i){if(t!==i){if("trigger-class"===e)return void this.applyTriggerClass();("api-key"===e||"dev"===e)&&(this.session=void 0),this.isConnected&&(this.abortController?.abort(),this.cleanup?.(),this.cleanup=void 0,this.setState("loading"),this.queueInitialization())}}captureCustomLabel(){if(null!==this.customLabelNodes)return;let e=Array.from(this.childNodes),t=e.some(e=>e.nodeType!==Node.TEXT_NODE||!!e.textContent?.trim());this.customLabelNodes=t?e:[]}queueInitialization(){this.queued||(this.queued=!0,queueMicrotask(()=>{this.queued=!1,this.isConnected&&this.initialize()}))}async initialize(){let e=++this.initializationRevision;this.abortController?.abort(),this.cleanup?.(),this.cleanup=void 0,this.clearActionResetTimer();let t=new AbortController;this.abortController=t;let i=this.getAttribute("type")?.trim()??"",r=eh(i)?i:"ar-viewer",o=this.getAttribute("object-id")?.trim()??"",n=this.getAttribute("variant-id")?.trim()||void 0,a=this.getAttribute("api-key")?.trim()??"",s=this.getAttribute("size-type")?.trim()||void 0;this.removeAttribute("data-error-code"),this.setState("loading"),this.renderTrigger(r);try{let r=this.resolveRuntimeConfig();if(!a)throw new eg("MISSING_API_KEY");if(!o)throw new eg("MISSING_OBJECT_ID");if(!eh(i))throw new eg("INVALID_TYPE");if("ar-viewer"===i&&s&&"cm"!==s&&"inch"!==s)throw new eg("INVALID_SIZE_TYPE");let l=await this.ensureSession(a,r,t.signal);this.assertCurrent(e,t.signal);let c=await this.fetchObjectWithSession(o,a,l,r,t.signal);this.assertCurrent(e,t.signal);let d=function(e,t){let i=Array.isArray(e.materials)?e.materials:[],r=t?i.find(e=>e.hid===t):i[0];if(!r)throw new eg(t?"VARIANT_NOT_FOUND":"MODEL_UNAVAILABLE");return r}(c,n),u=c.policy;if(!("ar-viewer"===i?u?.ignViewerModal!==!1:"ar-one-click"===i?u?.instantArView!==!1:u?.appClip!==!1))throw new eg("POLICY_DENIED");if("ar-viewer"!==i&&(!d.native?.glbUrl||!d.native.usdzUrl))throw new eg("MODEL_UNAVAILABLE");this.object=c,this.applyIntegrationData(o,n,s);let g=await this.initializeTrigger(i,c,r,t.signal);try{this.assertCurrent(e,t.signal)}catch(e){throw g(),e}this.cleanup=g,this.setState("ready"),this.dispatchWidgetEvent("core-ar-ready",{objectId:o,type:i})}catch(n){if(e!==this.initializationRevision||t.signal.aborted||n instanceof DOMException&&"AbortError"===n.name)return;let r=n instanceof eg||n instanceof b?n.code:"INITIALIZATION_FAILED";this.setAttribute("data-error-code",r),this.setState("error"),this.dispatchWidgetEvent("core-ar-error",{code:r,objectId:o,type:i})}}async ensureSession(e,t,i,r=!1){let o=`${t.apiOrigin}|${t.viewerOrigin}`,n=this.session?.publicKey===e&&this.session.runtimeKey===o?this.session:void 0;if(n&&!r&&n.expiresAt-Date.now()>3e4)return n;if(n)try{let r=await T(e,n.token,{runtimeConfig:t,signal:i}),o={...n,...r,expiresAt:Date.now()+1e3*r.expiresIn};return this.session=o,o}catch(e){if(!(e instanceof b)||!ed.has(e.code))throw e;this.session=void 0}let a=await x(e,{runtimeConfig:t,signal:i});if(!es.has(a.productType))throw new eg("POLICY_DENIED");let s={expiresAt:Date.now()+1e3*a.expiresIn,productType:a.productType,publicKey:e,runtimeKey:o,sid:a.sid,token:a.token};return this.session=s,s}async fetchObjectWithSession(e,t,i,r,o){try{return await k(e,i.token,{runtimeConfig:r,signal:o})}catch(i){if(!(i instanceof b)||"SESSION_EXPIRED"!==i.code)throw i;return k(e,(await this.ensureSession(t,r,o,!0)).token,{runtimeConfig:r,signal:o})}}assertCurrent(e,t){if(e!==this.initializationRevision||t.aborted)throw new DOMException("Aborted","AbortError")}renderTrigger(e){let t="ar-one-click"===e?document.createElement("a"):document.createElement("button");if(t.dataset.coreArWidgetTrigger="",t.setAttribute("part","trigger"),t.addEventListener("click",e=>{"ready"!==this.dataset.state&&(e.preventDefault(),e.stopImmediatePropagation())}),t instanceof HTMLButtonElement&&(t.type="button"),t instanceof HTMLAnchorElement&&(t.href="#",t.rel="ar"),"ar-viewer"===e&&t.setAttribute("aria-haspopup","dialog"),"ar-viewer"!==e){let e=document.createElement("img");e.alt="",e.hidden=!0,t.appendChild(e)}let i=document.createElement("span");i.dataset.coreArWidgetLabel="",this.customLabelNodes?.length?i.append(...this.customLabelNodes):i.textContent=el[e],t.appendChild(i),this.trigger=t,this.applyTriggerClass(),this.replaceChildren(t),this.updateTriggerAccessibility(this.dataset.state)}applyTriggerClass(){if(!this.trigger)return;let e=this.triggerClass.trim();e?this.trigger.className=e:this.trigger.removeAttribute("class")}applyIntegrationData(e,t,i){this.trigger&&(this.trigger.dataset.objectTinuuid=e,t?this.trigger.dataset.objectHid=t:delete this.trigger.dataset.objectHid,i?this.trigger.dataset.sizeType=i:delete this.trigger.dataset.sizeType)}async initializeTrigger(e,t,i,r){if(!this.trigger)throw new eg("INITIALIZATION_FAILED");if("ar-viewer"===e)return function(e,t={}){let i,r=et(e),n=()=>{var r;if(i)return;let n=e.dataset.objectTinuuid?.trim();if(!n)return;let a=function(e){var t,i,r,n;let a,s,l,c,d;if(document.getElementById(ee))return null;let u=document.createElement("div"),g=document.createElement("div"),h=document.createElement("div"),p=e.containerClassName?document.getElementsByClassName(e.containerClassName)[0]:null,m=!!p,f=m?g:u,w=!1,v=e=>{"Escape"===e.key&&a()};a=()=>{w||(w=!0,document.removeEventListener("keydown",v),G(f),(m?g:u).remove(),e.onClose?.(),e.restoreFocusTo?.focus({preventScroll:!0}))},u.id=ee,Object.assign(u.style,{inset:"0",overflow:"auto",position:"fixed",zIndex:"11111"}),u.style.webkitOverflowScrolling="touch",g.setAttribute("role","dialog"),g.setAttribute("aria-modal","true"),g.setAttribute("aria-label","3D model viewer"),Object.assign(h.style,{animation:"vex-fadein 0.5s",background:"#fff",inset:"0",position:"fixed",zIndex:"11111"}),h.addEventListener("click",a),(e.width||e.height)&&(h.style.background="rgba(0, 0, 0, 0.8)",Object.assign(g.style,{borderRadius:"12px",height:e.height??"100%",left:"50%",overflow:"hidden",position:"absolute",top:"50%",transform:"translate(-50%, -50%)",width:e.width??"100%",zIndex:"11112"}));let b=(t=e.crossClassName,i=a,(s=document.createElement("button")).type="button",s.setAttribute("aria-label","Close 3D model viewer"),t&&(s.className=t),Object.assign(s.style,{alignItems:"center",background:"#fff",border:"1px solid #E3E7E7",borderRadius:"4px",boxSizing:"border-box",cursor:"pointer",display:"flex",height:"40px",inset:"24px 24px auto auto",justifyContent:"center",padding:"8px 16px",position:"absolute",width:"56px",zIndex:"11113"}),s.innerHTML='<svg aria-hidden="true" style="flex-shrink:0" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12L19 6.41Z" fill="#484D50"/></svg>',s.addEventListener("click",i),s);return g.append((l=e.viewerOrigin??o().viewerOrigin,(c=new URL(e.hid?`${encodeURIComponent(e.tinuuid)}/${encodeURIComponent(e.hid)}`:encodeURIComponent(e.tinuuid),`${l.replace(/\/$/,"")}/`)).searchParams.set("hide-title",e.hideTitle??""),c.searchParams.set("hide-preview",e.hidePreview??""),c.searchParams.set("button-bg-color",e.buttonBgColor??""),c.searchParams.set("button-border-color",e.buttonBorderColor??""),c.searchParams.set("button-text-color",e.buttonTextColor??""),c.searchParams.set("background-color",e.backgroundColor??""),c.searchParams.set("size_type",e.sizeType),r=c.toString(),n=e.backgroundColor,(d=document.createElement("iframe")).src=r,d.title="3D model viewer",d.allow="fullscreen; xr-spatial-tracking",Object.assign(d.style,{backgroundColor:n??"white",border:"none",height:"100%",inset:"0",position:"absolute",width:"100%",zIndex:"11112"}),d),b),p?(g.id=ee,p.appendChild(g)):(u.append(h,g),document.body.appendChild(u)),document.addEventListener("keydown",v),J(f),b.focus({preventScroll:!0}),a}({backgroundColor:e.dataset.background,buttonBgColor:e.dataset.buttonBgColor,buttonBorderColor:e.dataset.buttonBorderColor,buttonTextColor:e.dataset.buttonTextColor,containerClassName:e.dataset.container,crossClassName:e.dataset.crossClassName,height:e.dataset.height?.trim(),hid:e.dataset.objectHid,hidePreview:e.dataset.hidePreview,hideTitle:e.dataset.hideTitle,onClose:()=>{i=void 0,t.onClose?.()},restoreFocusTo:e,sizeType:(r=e.dataset.sizeType)&&Z.includes(r)?r:"inch",tinuuid:n,viewerOrigin:t.viewerOrigin,width:e.dataset.width?.trim()});a&&(i=a,t.onOpen?.(),R(()=>t.onAction?.("widget_opened")))};return e.addEventListener("click",n),r.register(()=>{e.removeEventListener("click",n),i?.(),i=void 0})}(this.trigger,{onAction:e=>this.handleAction(e,i),onClose:()=>{this.isConnected&&this.setState("ready")},onOpen:()=>this.setState("opening"),viewerOrigin:i.viewerOrigin});let n={object:t,onAction:e=>this.handleAction(e,i),runtimeConfig:i,signal:r,trackView:()=>this.trackEvent("ar_launched",i)};if("ar-one-click"===e){if(!(this.trigger instanceof HTMLAnchorElement))throw new eg("INITIALIZATION_FAILED");return eo(this.trigger,n)}return D(this.trigger,n)}handleAction(e,t){this.setState("opening"),"widget_opened"===e&&this.trackEvent(e,t),"ar-viewer"!==this.getAttribute("type")&&(this.clearActionResetTimer(),this.actionResetTimer=window.setTimeout(()=>{this.isConnected&&this.setState("ready")},300))}async trackEvent(e,t){let i=this.getAttribute("api-key")?.trim();if(i&&this.isConnected)try{let r=await this.ensureSession(i,t);await L(e,r.token,this.object?.uuid,{runtimeConfig:t})}catch{}}clearActionResetTimer(){void 0!==this.actionResetTimer&&(window.clearTimeout(this.actionResetTimer),this.actionResetTimer=void 0)}setState(e){this.dataset.state=e,this.updateTriggerAccessibility(e)}updateTriggerAccessibility(e){if(!this.trigger)return;let t="ready"===e;this.trigger.setAttribute("aria-busy",String("loading"===e)),this.trigger.setAttribute("aria-disabled",String(!t)),this.trigger.tabIndex=t?0:-1,this.trigger instanceof HTMLButtonElement&&(this.trigger.disabled=!t)}dispatchWidgetEvent(e,t){this.dispatchEvent(new CustomEvent(e,{bubbles:!0,composed:!0,detail:t}))}}function ef(e){if("u"<typeof customElements)return;let t=ea[e],i=customElements.get(t);if(i)return i;class o extends em{resolveRuntimeConfig(){return function(e,t="prod"){var i,o;let n,a,s,l,c,d="arizo"===e?"arizo.ru":"vizbl.com",u="dev"===t?"-dev":"";return n=r((i={apiOrigin:`https://api${u}.${d}`,viewerOrigin:`https://viewer${u}.${d}`}).apiOrigin,"apiOrigin"),a=r(i.viewerOrigin,"viewerOrigin"),c=i.legacyApiOrigin?(s=r(o=i.legacyApiOrigin,"legacyApiOrigin"),(l=new URL(o).pathname.replace(/\/+$/,""))?`${s}${l}`:s):`${n}/v1`,{apiOrigin:n,brand:new URL(a).hostname.endsWith("arizo.ru")?"arizo":"vizbl",fetchImpl:i.fetchImpl??fetch,legacyApiOrigin:c,qrCodeScriptUrl:i.qrCodeScriptUrl??`${a}/vendor/qrcode.min.js`,viewerOrigin:a}}(e,this.hasAttribute("dev")?"dev":"prod")}}return customElements.define(t,o),o}ef("arizo"),ef("vizbl")})();
@@ -0,0 +1,26 @@
1
+ export declare const CORE_AR_WIDGET_TAG_NAMES: {
2
+ readonly arizo: "arizo-core";
3
+ readonly vizbl: "vizbl-core";
4
+ };
5
+
6
+ export declare const CORE_AR_WIDGET_TYPES: readonly ["advanced-ar", "ar-one-click", "ar-viewer"];
7
+
8
+ declare type CoreArWidgetBrand = 'arizo' | 'vizbl';
9
+
10
+ export declare type CoreArWidgetErrorCode = WidgetRuntimeErrorCode | 'INITIALIZATION_FAILED' | 'INVALID_SIZE_TYPE' | 'INVALID_TYPE' | 'MISSING_API_KEY' | 'MISSING_OBJECT_ID' | 'MODEL_UNAVAILABLE' | 'POLICY_DENIED' | 'VARIANT_NOT_FOUND';
11
+
12
+ export declare interface CoreArWidgetEventDetail {
13
+ code?: CoreArWidgetErrorCode;
14
+ objectId: string;
15
+ type: string;
16
+ }
17
+
18
+ export declare type CoreArWidgetState = 'error' | 'loading' | 'opening' | 'ready';
19
+
20
+ export declare type CoreArWidgetType = (typeof CORE_AR_WIDGET_TYPES)[number];
21
+
22
+ export declare function registerCoreArWidgets(): Record<CoreArWidgetBrand, CustomElementConstructor | undefined>;
23
+
24
+ declare type WidgetRuntimeErrorCode = 'API_UNAVAILABLE' | 'DOMAIN_NOT_ALLOWED' | 'INVALID_KEY' | 'INVALID_RESPONSE' | 'KEY_EXPIRED' | 'KEY_REVOKED' | 'NETWORK_ERROR' | 'OBJECT_NOT_FOUND' | 'POLICY_DENIED' | 'RATE_LIMITED' | 'REQUEST_FAILED' | 'SESSION_EXPIRED';
25
+
26
+ export { }