@wix/showcase-react-components 0.0.0-stage → 1.1.0

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 (28) hide show
  1. package/README.md +27 -2
  2. package/dist/esm/SpatialImage/SpatialImage.js +12 -0
  3. package/dist/esm/SpatialImage/spatialAsset.js +50 -0
  4. package/dist/esm/SpatialImage/viewer/spatial-2K3S733F.js +2 -0
  5. package/dist/esm/SpatialImage/viewer/spatial-AL6PHL7B.js +2 -0
  6. package/dist/esm/SpatialImage/viewer/spatial-CDWKCSGF.js +2 -0
  7. package/dist/esm/SpatialImage/viewer/spatial-GZVXIGKN.js +2 -0
  8. package/dist/esm/SpatialImage/viewer/spatial-IC5HXWEQ.js +2 -0
  9. package/dist/esm/SpatialImage/viewer/spatial-LAYZ53II.js +2 -0
  10. package/dist/esm/SpatialImage/viewer/spatial-LSZJ6I7J.js +2 -0
  11. package/dist/esm/SpatialImage/viewer/spatial-NBE3T2VB.js +2 -0
  12. package/dist/esm/SpatialImage/viewer/spatial-QDMH4O6I.js +182 -0
  13. package/dist/esm/SpatialImage/viewer/spatial-RRKAKJ56.js +2 -0
  14. package/dist/esm/SpatialImage/viewer/spatial-UQSQV6DC.js +2 -0
  15. package/dist/esm/SpatialImage/viewer/spatial-WDCZLPPJ.js +240 -0
  16. package/dist/esm/SpatialImage/viewer/spatial.bundle.d.ts +17 -0
  17. package/dist/esm/SpatialImage/viewer/spatial.bundle.js +2 -0
  18. package/dist/esm/SpatialImage/viewer/workers/depth-gallery-fill-worker.mjs +2 -0
  19. package/dist/esm/SpatialImage/viewer/workers/depth-gallery-quality-worker.mjs +2 -0
  20. package/dist/esm/SpatialImage/viewer/workers/depth-gallery-subject-worker.mjs +2 -0
  21. package/dist/esm/SpatialImage/viewer/workers/depth-gallery-worker.mjs +2 -0
  22. package/dist/esm/SpatialImage/workers.js +17 -0
  23. package/dist/esm/index.js +2 -0
  24. package/dist/types/SpatialImage/SpatialImage.d.ts +21 -0
  25. package/dist/types/SpatialImage/spatialAsset.d.ts +28 -0
  26. package/dist/types/SpatialImage/workers.d.ts +1 -0
  27. package/dist/types/index.d.ts +4 -0
  28. package/package.json +48 -3
package/README.md CHANGED
@@ -1,3 +1,28 @@
1
- # Temporary Holding Version
1
+ # @wix/showcase-react-components
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Showcase React components for other Wix hosts (first consumer: editor-react-components `Image3`).
4
+
5
+ ## SpatialImage
6
+
7
+ A photo with spatial depth (3D). The 3D version (`spatialAsset`) is made on the server by the showcase
8
+ media service (`POST /_api/showcase-media-service/v1/create-spatial-image`, the editor's **Make 3D**
9
+ action); the component only loads it, it never prepares depth in the browser.
10
+
11
+ ```tsx
12
+ import { SpatialImage, shouldRenderSpatialImage } from '@wix/showcase-react-components';
13
+
14
+ shouldRenderSpatialImage(image, spatialAsset, depthEnabled)
15
+ ? <SpatialImage id={id} image={image} spatialAsset={spatialAsset} depthStrength={depthStrength} />
16
+ : <FlatImage />;
17
+ ```
18
+
19
+ - `fit`: `cover` (default) or `contain` (the whole photo, letterboxed). `role`: kept on the image, which
20
+ stays in the accessibility tree (with its `alt`) while the canvas draws over it.
21
+ - `shouldRenderSpatialImage(image, spatialAsset, depthEnabled)`: true while the asset was made from this
22
+ very photo (same media id, no crop), points at site media, and the effect is not turned off.
23
+ - `mediaId(uri)`: the Wix media id inside any of the editor's image forms.
24
+
25
+ The viewer under `src/SpatialImage/viewer` is a fixed, pre-minified bundle; see its `PATCHES.md`. The
26
+ package ships ESM only: the viewer loads its chunks with `import()` and its workers with
27
+ `new URL(..., import.meta.url)`. Published to public npm, because editor-react-components (a public
28
+ package) depends on it.
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { createSpatialImage } from './viewer/spatial.bundle.js';
3
+ import { workerUrl } from './workers.js';
4
+ /*
5
+ * A photo with spatial depth (3D): the fixed, validated Spatial Image viewer bundle (./viewer) fed with
6
+ * the photo and its saved `spatialAsset`. The asset is always made by the showcase media service (the
7
+ * editor's Make 3D action), so the viewer never prepares depth in the browser, in the editor either: it
8
+ * would download the models and show its progress on the image, and the host has no way to keep the
9
+ * result.
10
+ */
11
+ const Viewer = createSpatialImage({ workerUrl, prepareInEditor: false });
12
+ export const SpatialImage = ({ id, className, image, spatialAsset, depthStrength, fit, role, }) => (React.createElement(Viewer, { id: id, className: className, image: image, spatialAsset: spatialAsset, depthEnabled: true, depthStrength: depthStrength, fit: fit, role: role }));
@@ -0,0 +1,50 @@
1
+ /** The Wix media id inside any of the editor's image forms (bare id, wix:image://v1/<id>/..., static URL). */
2
+ export const mediaId = (uri) => {
3
+ if (!uri) {
4
+ return null;
5
+ }
6
+ const id = uri
7
+ .replace(/#.*$/, '')
8
+ .replace(/^wix:image:\/\/v1\//, '')
9
+ .replace(/^wix:\/\/v1\//, '')
10
+ .replace(/\/.*$/, '');
11
+ if (/^\w[\w.~-]*$/.test(id)) {
12
+ return id;
13
+ }
14
+ return (/^https:\/\/static\.wixstatic\.com\/media\/(\w[\w.~-]*)/.exec(uri)?.[1] ??
15
+ null);
16
+ };
17
+ const isUncropped = (image) => !image.crop ||
18
+ !(image.crop.x ||
19
+ image.crop.y ||
20
+ (image.crop.width ?? 1) !== 1 ||
21
+ (image.crop.height ?? 1) !== 1);
22
+ const isSpatialAssetUrl = (url) => {
23
+ try {
24
+ const parsed = new URL(url);
25
+ return (parsed.protocol === 'https:' &&
26
+ parsed.hostname === 'static.wixstatic.com' &&
27
+ /^\/media\/[^/]+$/.test(parsed.pathname) &&
28
+ !parsed.search &&
29
+ !parsed.hash);
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ };
35
+ /**
36
+ * True when the host should render `SpatialImage`: the Depth effect is on and the saved asset
37
+ * was made from this very photo (same media id, no crop) and points at site media.
38
+ */
39
+ export const shouldRenderSpatialImage = (image, spatialAsset, depthEnabled) => {
40
+ if (depthEnabled === false || !image || !spatialAsset) {
41
+ return false;
42
+ }
43
+ const id = mediaId(image.uri);
44
+ return Boolean(id &&
45
+ spatialAsset.sourceUri === id &&
46
+ spatialAsset.sourceVersion &&
47
+ spatialAsset.key &&
48
+ isSpatialAssetUrl(spatialAsset.url) &&
49
+ isUncropped(image));
50
+ };
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{a as R}from"./spatial-LAYZ53II.js";import{a as x}from"./spatial-NBE3T2VB.js";import{c as E,p as v,q as P,r as I,t as B,w as M,x as H}from"./spatial-QDMH4O6I.js";import{b as S,c as z}from"./spatial-GZVXIGKN.js";var q="depthpro-9e65e4d-3eb35ca6-fp16-max1600-inverse-v1";var W="onnx-community/depth-anything-v2-small",V="4472b7362082ad9968fee890ca0f1e5aca36b93d",k={fast:{label:"Fast",size:322},balanced:{label:"Balanced",size:518},detail:{label:"Detail",size:770},local:{label:"Local HQ",size:1536}};function L(t,a,e){if(!k[a])throw new Error("Unknown quality preset");return a==="local"?`${t}|${q}`:`${t}|${W}|${V}|transformers-3.8.1|v1|${k[a].size}|${e}`}async function O(t){let a=await crypto.subtle.digest("SHA-256",await t.arrayBuffer());return Array.from(new Uint8Array(a),e=>e.toString(16).padStart(2,"0")).join("")}var K=new Promise((t,a)=>{let e=indexedDB.open("ari-depth-gallery",3);e.onupgradeneeded=()=>{e.result.objectStoreNames.contains("images")||e.result.createObjectStore("images",{keyPath:"id"}),e.result.objectStoreNames.contains("results")||e.result.createObjectStore("results",{keyPath:"key"}),e.result.objectStoreNames.contains("subjects")||e.result.createObjectStore("subjects",{keyPath:"key"}),e.result.objectStoreNames.contains("fills")||e.result.createObjectStore("fills",{keyPath:"key"})},e.onsuccess=()=>{e.result.onversionchange=()=>e.result.close(),t(e.result)},e.onblocked=()=>a(new Error("Close older gallery tabs to enable the storage upgrade.")),e.onerror=()=>a(e.error)});async function m(t,a,e){let n=await K;return new Promise((i,w)=>{let h=n.transaction(t,a==="get"||a==="getAll"?"readonly":"readwrite"),r=h.objectStore(t)[a](e);h.oncomplete=()=>i(r.result),h.onerror=()=>w(h.error),h.onabort=()=>w(h.error||new Error("Storage transaction aborted"))})}var T=R({createWorker:t=>new Worker(new URL(t,import.meta.url),{type:"module"})});window.addEventListener("pagehide",()=>T.dispose());function _(t="balanced",a=null,e=!1){if(!k[t])throw new Error("Unknown preparation quality.");return JSON.stringify(["spatial-preparation-v1","oriented-max1600",...e?[x]:[],L("photo",t,"auto"),E({key:S("photo",a?.model,a?.revision)},"balanced")])}async function A(t,a=1600){if(t.size>50*1024*1024)throw new Error("Choose an image smaller than 50 MB.");let e=await createImageBitmap(t);try{if(e.width*e.height>64e6)throw new Error("Choose an image below 64 megapixels.");let n=Math.min(1,a/Math.max(e.width,e.height)),i=document.createElement("canvas");return i.width=Math.max(1,Math.round(e.width*n)),i.height=Math.max(1,Math.round(e.height*n)),i.getContext("2d").drawImage(e,0,0,i.width,i.height),i}finally{e.close()}}async function le(t){let a=await O(t),e=await I(t),n=e?.photo||await A(t),i=n.getContext("2d").getImageData(0,0,n.width,n.height).data,w=await O(new Blob([`${n.width},${n.height}:`,i]));return{id:a,version:w,name:t.name||e?.manifest.name||"Photo",blob:t,originalBlob:e?.photoBlob||t,width:n.width,height:n.height,prepared:!!e}}var y=t=>({width:t.width,height:t.height,rgba:t.getContext("2d").getImageData(0,0,t.width,t.height).data.buffer}),F=Promise.resolve();function ce({tier:t="balanced",stats:a={jobs:0,depth:0,subject:0,fill:0},pool:e=T,localDepth:n=null,cutout:i=null,refine:w=!1}={}){_(t);let h=(r,s,j)=>e.run(r,s,j);return(r,s=()=>{},j=null)=>{s("Waiting to prepare this photo\u2026");let $=F.then(async()=>{if(r.prepared){let o=await I(r.blob,{required:!0});return{blob:r.blob,settings:o.settings,bytes:r.blob.size,warnings:[],imported:!0}}a.jobs++;let u=await A(r.blob),b=[],C=L(r.id,t,"auto"),l=await m("results","get",C);if(l||(a.depth++,l=t==="local"?await(n??(()=>{throw new Error("The local tier needs localDepth.")}))(u,s):await h("./depth-gallery-worker.js",{...y(u),tier:t,device:"auto"},s),Object.assign(l,{key:C,imageId:r.id,requestedDevice:"auto"}),await m("results","put",l)),j)try{let o=H(M(l,null),!1,u.width/u.height),d=v({effect:"spatial",material:"photo",motion:!0,...o}),{blob:f}=await P({photo:u,depth:l,subject:null,fill:null,settings:d,name:r.name});j({blob:f,settings:d,bytes:f.size,warnings:[],partial:!0})}catch(o){b.push(`Early preview unavailable: ${o.message}`)}s("Refining \xB7 foreground and background\u2026");let D=S(r.id,i?.model,i?.revision),c=await m("subjects","get",D),p=null,g=await A(r.blob,1024);try{if(!c){if(a.subject++,i){let{mask:o,...d}=await i(g,s),f=y(g);c=z(o,new Uint8ClampedArray(f.rgba),g.width,g.height,d)}else c=await h("./depth-gallery-subject-worker.js",y(g),s);Object.assign(c,{key:D,imageId:r.id}),await m("subjects","put",c)}if(c.usable){let o=E(c,"balanced");p=await m("fills","get",o),p||(a.fill++,p=await h("./depth-gallery-fill-worker.js",{...y(g),mask:c.mask.buffer},s),Object.assign(p,{key:o,imageId:r.id,tier:"balanced"}),await m("fills","put",p))}else b.push("No clear foreground was found. Using whole-image depth.")}catch(o){b.push(`${o.message} Using the available depth and layers.`)}if(w){s("Refining edges and surface depth\u2026");try{let o=c||{width:1,height:1,mask:new Uint8Array(1)},d=await h("./depth-gallery-quality-worker.js",{...y(u),key:r.id+"|"+x,mask:o.mask,maskWidth:o.width,maskHeight:o.height,depth:l.values,depthWidth:l.width,depthHeight:l.height},s),f=d.subject.usable?B(p,d.subject):null;l=d.depth,c=d.subject.usable?d.subject:null,p=f}catch(o){b.push(`Edge and depth refinement unavailable: ${o.message}`)}}let U=H(M(l,c),!!p,u.width/u.height),N=v({effect:"spatial",material:"photo",motion:!0,...U});s("Saving your spatial photo\u2026");let{blob:Q}=await P({photo:u,depth:l,subject:c,fill:p,settings:N,name:r.name});return{blob:Q,settings:N,bytes:Q.size,warnings:b}});return F=$.catch(()=>{}),$}}export{ce as createSpatialPreparer,A as photoPixels,_ as preparationVersion,le as sourceFromFile};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ function l(c="ari-spatial-integration-v1"){let n=new Promise((r,o)=>{let e=indexedDB.open(c,1);e.onupgradeneeded=()=>e.result.createObjectStore("records"),e.onsuccess=()=>{e.result.onversionchange=()=>e.result.close(),r(e.result)},e.onerror=()=>o(e.error),e.onblocked=()=>o(new Error("Close older spatial preview tabs and retry."))});async function s(r,o,e){let i=await n;return new Promise((u,a)=>{let t=i.transaction("records",r==="get"?"readonly":"readwrite"),d=r==="get"?t.objectStore("records").get(o):t.objectStore("records").put(e,o);t.oncomplete=()=>u(d.result),t.onerror=()=>a(t.error),t.onabort=()=>a(t.error||new Error("Storage aborted."))})}return{get:r=>s("get",r),put:(r,o)=>s("put",r,o),close:async()=>(await n).close()}}export{l as createSpatialStore};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ function f(a,t,e){if(!a||!t?.id||!t?.version||!e)throw new Error("A site, media ID, source version and pipeline are required.");return JSON.stringify(["ari-spatial",1,a,t.id,t.version,e])}var d=class{constructor({siteId:t,store:e,prepare:n,pipeline:s}){this.siteId=t,this.store=e,this.prepare=n,this.pipeline=s,this.pending=new Map}ensure(t,e=()=>{},n=null){let s=f(this.siteId,t,this.pipeline);if(this.pending.has(s)){let r=this.pending.get(s);return r.listeners.add(e),r.promise.finally(()=>r.listeners.delete(e))}let i={listeners:new Set([e])},o=r=>{for(let h of i.listeners)h(r)};return i.promise=Promise.resolve().then(async()=>{let r=await this.store.get(s);if(r)return r;let h=await this.prepare(t,o,n?u=>n({...u,key:s+"#depth",sourceId:t.id,sourceVersion:t.version,pipeline:this.pipeline}):null);if(!h||!h.blob&&!h.url)throw new Error("Preparation did not produce a spatial asset.");let l={...h,key:s,sourceId:t.id,sourceVersion:t.version,pipeline:this.pipeline};return await this.store.put(s,l),l}).finally(()=>this.pending.delete(s)),this.pending.set(s,i),i.promise}},p=class{constructor({registry:t,source:e=null,enabled:n=!1,settings:s={},onChange:i=()=>{}}){this.registry=t,this.onChange=i,this.revision=0,this.disposed=!1,this.state={source:e,enabled:!1,settings:{...s},asset:null,status:"off",message:""},this.ready=n?this.setEnabled(!0):Promise.resolve()}emit(){this.disposed||this.onChange(this.snapshot())}snapshot(){return{...this.state,settings:{...this.state.settings}}}serialize(){return{enabled:this.state.enabled,settings:{...this.state.settings},assetKey:this.state.asset?.key||null}}setSource(t,{enabled:e=this.state.enabled}={}){return this.revision++,this.state={...this.state,source:t,asset:null,status:"off",message:"",enabled:!1},this.setEnabled(e)}setSettings(t){Object.assign(this.state.settings,t),this.emit()}replaceSettings(t={}){this.state.settings={...t},this.emit()}async setEnabled(t){if(this.disposed)return;let e=++this.revision;if(this.state.enabled=!!t,!t){this.state.status="off",this.state.message="",this.emit();return}if(!this.state.source){this.state.enabled=!1,this.state.status="off",this.emit();return}let n=this.state.source,s=()=>!this.disposed&&e===this.revision&&this.state.enabled;this.state.status="preparing",this.state.message="Preparing depth\u2026",this.emit();try{let i=await this.registry.ensure(n,o=>{s()&&(this.state.message=o,this.emit())});if(!s())return;this.state.asset=i,this.state.status="ready",this.state.message="",this.emit()}catch(i){if(!s())return;this.state.status="error",this.state.message=i.message||String(i),this.emit()}}dispose(){this.disposed=!0,this.revision++}};function c(a,t="inherit"){if(!["inherit","on","off"].includes(t))throw new Error("Unknown gallery depth override.");return t==="inherit"?!!a:t==="on"}export{d as SpatialAssetRegistry,p as SpatialPhotoUsage,c as galleryDepthEnabled,f as spatialAssetKey};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ var y="studioludens/birefnet-lite-512",x="962e67969ec6acec9c19fab76acee4f5e22e990c";function D(e,t=y,n=x){return`${e}|${t}|${n}|matte-v1-wasm-fp32-fill-v1`}function v(e,t,n,a,f={}){for(let r=0;r<e.length;r++)e[r]=Math.round(e[r]*t[r*4+3]/255);return{...M(e,t,n,a),mask:e,width:n,height:a,...f,created:Date.now()}}function b(e,t,n,a){if(e.length!==t*n)throw new Error("Mask dimensions do not match.");let f=new Int32Array(e.length).fill(-1),r=new Int32Array(e.length),i=0,s=0;for(let o=0;o<e.length;o++)(a?e[o]>=240:e[o]<=4)&&(f[o]=o,r[s++]=o);if(!s)return null;for(;i<s;){let o=r[i++],c=o%t,u=l=>{f[l]<0&&(f[l]=f[o],r[s++]=l)};c&&u(o-1),c+1<t&&u(o+1),o>=t&&u(o-t),o+t<e.length&&u(o+t)}return f}function M(e,t,n,a){if(t.length!==n*a*4)throw new Error("Image dimensions do not match.");let f=0;for(let l of e)l>127&&f++;let r=f/e.length;if(r<.003||r>.985)return{usable:!1,coverage:r,reason:"No clear foreground/background split. Use the full image, or try a photo with a distinct subject."};let i=b(e,n,a,!0),s=b(e,n,a,!1);if(!i||!s)return{usable:!1,coverage:r,reason:"The subject boundary is uncertain. The full image is still available."};let o=new Uint8ClampedArray(t.length);for(let l=0;l<e.length;l++){let d=s[l]*4;o.set(t.subarray(d,d+4),l*4)}let c=o,u=new Uint8ClampedArray(o.length);for(let l=0;l<8;l++){u.set(c);for(let d=1;d<a-1;d++)for(let p=1;p<n-1;p++){let h=d*n+p;if(!(e[h]<=4))for(let g=0;g<3;g++)u[h*4+g]=(c[(h-1)*4+g]+c[(h+1)*4+g]+c[(h-n)*4+g]+c[(h+n)*4+g])/4}[c,u]=[u,c]}return{usable:!0,coverage:r,foregroundDonors:i,backgroundDonors:s,background:c}}function A(e,t){let{width:n,height:a,foregroundDonors:f,backgroundDonors:r}=t,i=c=>{let u=Math.min(e.width-1,Math.floor((c%n+.5)/n*e.width)),l=Math.min(e.height-1,Math.floor((Math.floor(c/n)+.5)/a*e.height));return e.values[l*e.width+u]},s=Float32Array.from(f,i),o=Float32Array.from(r,i);return{foreground:s,background:o,width:n,height:a}}export{y as a,D as b,v as c,b as d,M as e,A as f};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{a as U}from"./spatial-NBE3T2VB.js";import{a as M}from"./spatial-GZVXIGKN.js";var I=[{key:"enabled",label:"Depth",choices:[["off","Off"],["on","On"]],value:"off"},{key:"effect",label:"Presentation",choices:[["spatial","Spatial photo"],["dof","Depth of field only"],["parallax","Parallax"],["relief","3D relief"]],value:"spatial"},{key:"strength",label:"Depth strength",min:0,max:100,value:45},{key:"separation",label:"Layer separation",min:0,max:100,value:0},{key:"orbitRange",label:"Orbit range",min:0,max:8,value:3},{key:"orbitPeriod",label:"Orbit duration (seconds)",min:8,max:120,value:30},{key:"motion",label:"Automatic orbit",choices:[["on","On"],["off","Off"]],value:"on"},{key:"adaptive",label:"Protect fragile edges",choices:[["on","On"],["off","Off"]],value:"on"},{key:"background",label:"Background",choices:[["show","Show"],["remove","Remove"]],value:"show"},{key:"color",label:"Color",choices:[["color","Color"],["mono","Black & white"]],value:"color"},{key:"material",label:"Material",choices:[["photo","Photo"],["particles","Particles"]],value:"photo"},{key:"lens-enabled",label:"Lens blur",choices:[["off","Off"],["on","On"]],value:"off"},{key:"lens-look",label:"Lens look",choices:[["auto","Automatic"],["subtle","Subtle"],["cinematic","Cinematic"],["custom","Custom"]],value:"auto"},{key:"lens-mode",label:"Focus",choices:[["auto","Foreground"],["mouse","Follow mouse"],["manual","Manual"]],value:"auto"},{key:"lens-focus",label:"Focus: far to near",min:0,max:100,value:70},{key:"lens-bias",label:"Mouse focus: farther to nearer",min:-50,max:50,value:0},{key:"lens-blur",label:"Blur amount",min:0,max:100,value:26},{key:"lens-shape",label:"Bokeh shape",choices:[["round","Circular"],["hex","Hexagonal"],["oval","Anamorphic oval"]],value:"round"},{key:"lens-highlights",label:"Highlight glow",min:0,max:100,value:30}],j=e=>"spatial-"+e.replace(/[A-Z]/g,t=>"-"+t.toLowerCase()),ee=[{dataKey:"depth",key:"enabled"},{dataKey:"depthEnabled",key:"enabled"},{dataKey:"depthStrength",key:"strength"},{dataKey:"layerSeparation",key:"separation"},{dataKey:"orbitRange",key:"orbitRange"},{dataKey:"orbitPeriod",key:"orbitPeriod"},{dataKey:"background",key:"background"}];function A(e,t={},a={}){let i={...e.values},n=!1;for(let{dataKey:o,key:s}of ee){if(t[o]===void 0||t[o]===null||t[o]==="")continue;let l=I.find(c=>c.key===s),u=typeof t[o]=="boolean"?t[o]?"on":"off":t[o];i[s]=N(l,u,a[s]??l.value),n=!0}return n?V(o=>i[I.find(s=>j(s.key)===o)?.key],a):e}function N(e,t,a=e.value){let i=String(t??"").trim().replace(/^["']|["']$/g,"");if(!i)return a;if(e.choices)return e.choices.some(([o])=>o===i)?i:a;let n=Number(i);return Number.isFinite(n)?Math.max(e.min,Math.min(e.max,n)):a}function V(e,t={}){let a=Object.fromEntries(I.map(n=>[n.key,N(n,e(j(n.key)),t[n.key]??n.value)]));{let n=a.strength,o=Math.max(0,(n-45)/55);a.orbitRange=n<=45?n/15:3+o*5,a.separation=o*70}let i={lens:{}};for(let[n,o]of Object.entries(a)){if(n==="enabled")continue;let s=["motion","adaptive","lens-enabled"].includes(n)?o==="on":o;n==="background"?i.subjectView=o==="remove"?"foreground":"scene":n.startsWith("lens-")?i.lens[n.slice(5)]=s:i[n]=s}return{enabled:a.enabled==="on",settings:i,values:a}}function B(e,t,a){let i="",n=!1,o=()=>{if(n)return;let u=getComputedStyle(e),c=V(r=>u.getPropertyValue("--"+r),a),f=JSON.stringify(c);f!==i&&(i=f,t(c))},s=new MutationObserver(o);s.observe(e,{attributes:!0,attributeFilter:["class","style"]}),e.parentElement&&s.observe(e.parentElement,{attributes:!0,attributeFilter:["class","style"]});let l=setInterval(o,250);return o(),()=>{n=!0,clearInterval(l),s.disconnect()}}var R=e=>!e.crop||!(e.crop.x||e.crop.y||(e.crop.width??1)!==1||(e.crop.height??1)!==1);function te(e,t){if(!e?.uri||t?.sourceUri!==e.uri||!t.sourceVersion||!t.key||!R(e))return null;try{let a=new URL(t.url);return a.protocol!=="https:"||a.hostname!=="static.wixstatic.com"||!/^\/media\/[^/]+$/.test(a.pathname)||a.search||a.hash||a.username||a.password?null:{key:t.key,url:a.href}}catch{return null}}function T(e,{Image:t,loadHost:a,emptyState:i,defaults:n,prepare:o,onPrepared:s,isStale:l}){return function({id:c,className:f="",image:r,spatialAsset:L,wix:_,fit:ft="cover",role:rl,...D}){let x=e.useRef(null),v=e.useRef(null),S=e.useRef(null),[$,Q]=e.useState({enabled:!1,settings:{},values:{}}),Y=JSON.stringify(D,(b,p)=>b!==""&&(typeof p=="function"||p&&typeof p=="object")?void 0:p),h=e.useMemo(()=>A($,D,n),[$,Y]),[Z,q]=e.useState(!1),[y,O]=e.useState(null),[C,g]=e.useState(null),P=te(r,L),E=!!P&&!!l?.(L),w=!!o&&(!P||E)&&!!r?.uri&&R(r)&&h.enabled,m=(w&&y?.uri===r.uri&&y.key&&!(E&&y.partial)?{key:y.key,blob:y.blob,upgradeOf:y.upgradeOf}:null)||P;S.current={source:r,asset:m,enabled:h.enabled,status:m?"ready":"off",settings:h.settings},e.useEffect(()=>B(x.current,Q,n),[]),e.useEffect(()=>{if(!w||y?.uri===r.uri)return;let b=r.uri,p=!0;g("Preparing depth\u2026");let K=null;return o(r,d=>{p&&g(d)},d=>{p&&(K=d.key,O({uri:b,...d,partial:E}))}).then(d=>{p&&(O({uri:b,...d,upgradeOf:K}),g(null),s?.(d,{id:c,image:r}))},d=>{p&&(O({uri:b,error:d}),g(d.message||"Depth preparation failed."))}),()=>{p=!1}},[w,r?.uri]),e.useEffect(()=>{if(!m||!h.enabled)return;let b=!1;return a().then(({SpatialPhotoHost:p})=>{b||(v.current=new p({element:x.current,fit:ft,onRenderingChange:q}),v.current.update(S.current))}).catch(()=>{}),()=>{b=!0,v.current?.dispose(),v.current=null}},[!!m&&h.enabled,ft]),e.useEffect(()=>{v.current?.update(S.current)},[r?.uri,m?.key,m?.url,h]);let F=_?.presetsWrapperProps??{},G=r?.crop&&r.width&&r.height?{x:(r.crop.x||0)*r.width,y:(r.crop.y||0)*r.height,width:(r.crop.width??1)*r.width,height:(r.crop.height??1)*r.height}:void 0,X=r?.width&&r?.height?`${r.width} / ${r.height}`:"4 / 5";return e.createElement("div",{...F,style:{...F.style,display:"contents"}},e.createElement("div",{id:c,className:`wix-spatial-image ${f}`,ref:x,"data-depth-ready":!!m,style:{position:"relative",overflow:"hidden",boxSizing:"border-box",isolation:"isolate","--spatial-ratio":X}},e.createElement("style",null,".wix-spatial-image{aspect-ratio:var(--spatial-ratio,4 / 5)}"),r?.uri?e.createElement(t,{uri:r.uri,width:r.width||1,height:r.height||1,alt:r.alt||"",role:rl,name:r.name||"",crop:G,displayMode:ft==="contain"?"fit":"fill",shouldUseLQIP:!0,style:{position:"absolute",inset:0,display:"block",width:"100%",height:"100%",objectFit:ft,transform:h.enabled&&(h.settings.effect??"spatial")==="spatial"?"scale(1.12)":"none",transformOrigin:"50% 50%",opacity:Z?0:1}}):i?e.createElement(i):null,w&&C?e.createElement("div",{role:"status",style:{position:"absolute",left:12,bottom:12,zIndex:1,padding:"6px 10px",borderRadius:6,background:"rgba(20,22,28,.72)",color:"#fff",font:"12px/1.4 system-ui,sans-serif",pointerEvents:"none"}},C):null))}}import*as k from"react";var W={enabled:"on"};function z(e){let t=e?.uri??"",a=e?.url??"",i=t.replace(/#.*$/,"").replace(/^wix:image:\/\/v1\//,"").replace(/^wix:\/\/v1\//,"").replace(/\/.*$/,"");return i&&/^[\w.~-]+$/.test(i)?i:/^https:\/\/static\.wixstatic\.com\/media\/([\w.~-]+)/.exec(/^https?:/.test(t)?t:a)?.[1]??null}var H=e=>{let t=z(e);return t?"https://static.wixstatic.com/media/"+t:null};function re({uri:e,alt:t,role:r,style:a}){let i=H({uri:e});return i?k.createElement("img",{src:i,alt:t,role:r,loading:"lazy",decoding:"async",draggable:!1,style:a}):null}function ne(){return k.createElement("div",{role:"note",style:{position:"absolute",inset:0,display:"grid",placeItems:"center",padding:24,textAlign:"center",background:"#eef0f4",color:"#3b4252",font:"14px/1.5 system-ui,sans-serif",border:"2px dashed #b6bccb"}},"Spatial Image \u2014 choose a photo with Replace Image.")}function oe(e){let t=URL.createObjectURL(new Blob([`import ${JSON.stringify(e.href)};`],{type:"text/javascript"}));try{return new Worker(t,{type:"module"})}finally{setTimeout(()=>URL.revokeObjectURL(t),6e4)}}var ae=()=>typeof location<"u"&&/\.editor\.wix\.com$/.test(location.hostname);function J(e){let t;function a(){return t||(t=Promise.all([import("./spatial-CDWKCSGF.js"),import("./spatial-AL6PHL7B.js"),import("./spatial-2K3S733F.js"),import("./spatial-UQSQV6DC.js")]).then(([{SpatialAssetRegistry:i},{createSpatialStore:n},{createSpatialPreparer:o,preparationVersion:s},{createWorkerSessionPool:l}])=>{let u=l({createWorker:c=>oe(e(c.replace(/^\.\/|\.js$/g,"")))});return new i({siteId:"wix-dev-spatial-image",store:n("ari-wix-spatial-image"),prepare:o({pool:u,refine:!0}),pipeline:s("balanced",null,!0)})}).catch(i=>{throw t=null,i})),t}return async function(n,o,s){o("Loading photo\u2026");let l=await fetch(H(n));if(!l.ok)throw new Error(`Photo unavailable (${l.status}).`);let[{sourceFromFile:u},c]=await Promise.all([import("./spatial-2K3S733F.js"),l.blob()]),f=await(await a()).ensure(await u(c),o,s?r=>s({key:r.key,blob:r.blob}):null);return{key:f.key,blob:f.blob}}}function be({workerUrl:e,authorization:t}){let a=J(e);return async function(n,o){let{key:s,blob:l}=await a(n,o);o("Saving the 3D image to your site\u2026");let{uploadToSiteMedia:u,whenServed:c}=await import("./spatial-RRKAKJ56.js"),f=String(n.name||"photo").replace(/\.[^.]*$/,"").slice(0,80)+".spatial.png",{url:r}=await u(l,{fileName:f,authorization:await t()});return await c(r),{sourceUri:n.uri,sourceVersion:s,url:r,key:s}}}function ye({workerUrl:e,prepareInEditor:ie=!0}){let t=ie&&ae()?J(e):void 0,n=T(k,{Image:re,emptyState:ne,defaults:W,prepare:t,onPrepared:t?(o,s)=>import("./spatial-LSZJ6I7J.js").then(({saveFromEditor:l})=>l(o,s)).catch(l=>console.warn("Spatial Image: the 3D version was not saved for visitors.",l)):void 0,isStale:t?o=>![M,U].every(s=>String(o?.key??"").includes(s)):void 0,loadHost:()=>import("./spatial-WDCZLPPJ.js")});return function({image:s,...l}){let u=z(s);return k.createElement(n,{...l,image:u?{...s,uri:u}:void 0})}}export{I as a,j as b,N as c,V as d,te as e,z as f,be as g,ye as h};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ function E({createWorker:w,timeoutMs:f=3e5,idleMs:m=3e5}){let i=new Map,d=0;function s(o,t){t.retired=!0,clearTimeout(t.idle),t.worker?.terminate(),t.worker=null,i.get(o)===t&&i.delete(o)}function k(o,t,g=()=>{}){let e=i.get(o);e||(e={tail:Promise.resolve(),worker:null,queued:0},i.set(o,e)),clearTimeout(e.idle),e.queued++;let l=e.tail.then(()=>new Promise((h,u)=>{if(e.retired){u(new Error("Preparation stopped."));return}let c=++d,a,p=!1,n=(r,P)=>{p||(p=!0,clearTimeout(a),e.cancel=null,r?(e.worker?.terminate(),e.worker=null,u(r)):h(P))};e.cancel=()=>n(new Error("Preparation stopped."));try{e.worker||(e.worker=w(o)),e.worker.onmessage=({data:r})=>{r.id===c&&(r.type==="progress"?g(r.message):r.type==="result"?n(null,r.result):r.type==="error"&&n(new Error(r.message)))},e.worker.onerror=r=>n(new Error(r.message||"Preparation worker stopped.")),a=setTimeout(()=>n(new Error("Preparation timed out. Retry or choose another quality.")),f),e.worker.postMessage({id:c,...t})}catch(r){n(r)}})).finally(()=>{--e.queued===0&&!e.retired&&(e.idle=setTimeout(()=>s(o,e),m))});return e.tail=l.catch(()=>{}),l}return{run:k,dispose(){for(let[o,t]of i)t.cancel?.(),s(o,t)},get size(){return i.size}}}export{E as a};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{e as f,f as m}from"./spatial-IC5HXWEQ.js";import"./spatial-NBE3T2VB.js";import"./spatial-GZVXIGKN.js";var v="b2e8b4cb-1799-4b3c-bace-d55b503f7334",p=new Map;function D(){try{let t=window.top?.repluggableAppDebug,n=t&&[...t.readyAPIs].find(a=>a.name==="Document Services API");return n?t.host.getAPI(n):null}catch{return null}}function k(t,{id:n,image:a}){if(!n||!a?.uri||!t?.key||!t?.blob)return null;let e=p.get(t.key)??P(t,{id:n,image:a}).finally(()=>p.delete(t.key));return p.set(t.key,e),e}async function P({key:t,blob:n},{id:a,image:e}){let r=D();if(!r)return null;let s={id:a,type:"DESKTOP"},w=r.components.getType(s);if(!/^wixGalleriesElements\.(Floating)?SpatialImage$/.test(w??""))return null;let i=()=>r.components.data.getMerged(s)??{},u=()=>{let o=i().image;return o?{...o,uri:m(o)}:null};if(u()?.uri!==e.uri)return null;if(i().spatialAsset?.key===t&&f(e,i().spatialAsset))return i().spatialAsset;let d=r.platform.getAppDataByAppDefId(v)?.instance;if(!d)throw Error("Could not read this app's access to your site.");let{uploadToSiteMedia:y,whenServed:A}=await import("./spatial-RRKAKJ56.js"),g=String(e.name||"photo").replace(/\.[^.]*$/,"").slice(0,80)+".spatial.png",{url:c}=await y(n,{fileName:g,authorization:d});if(await A(c),u()?.uri!==e.uri)return null;let b={sourceUri:e.uri,sourceVersion:t,url:c,key:t};await r.components.data.updatePartially(s,{spatialAsset:b}),await r.waitForChangesAppliedAsync?.();for(let o=0;o<10;o++){let l=i().spatialAsset;if(l?.key===t&&l?.url===c)return l;await new Promise(h=>setTimeout(h,300))}throw Error("Wix did not keep the saved 3D image.")}export{k as saveFromEditor};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ var e="spatial-edges-depth-v1";export{e as a};
@@ -0,0 +1,182 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{d as lt,e as ht}from"./spatial-GZVXIGKN.js";var Vt="andraniksargsyan/migan",Bt="406830d0fa60666da0071c342ad2fbc8f30c5c64";function be(t,e){if(!["fast","balanced"].includes(e))throw new Error("Unknown background fill.");return`${t.key}|fill-${e}|${e==="balanced"?`${Vt}@${Bt}|pad512-margin-v1`:"donor-v1"}`}function G(t,e,o,n=Math.max(2,Math.ceil(Math.max(e,o)*.004))){if(t.length!==e*o)throw new Error("Mask dimensions do not match.");let r=e+1,a=new Uint32Array(r*(o+1));for(let i=0;i<o;i++){let f=0;for(let l=0;l<e;l++)f+=t[i*e+l]>4?1:0,a[(i+1)*r+l+1]=a[i*r+l+1]+f}let s=new Uint8Array(t.length);for(let i=0;i<o;i++)for(let f=0;f<e;f++){let l=Math.max(0,f-n),p=Math.min(e,f+n+1),d=Math.max(0,i-n),g=Math.min(o,i+n+1);a[g*r+p]-a[d*r+p]-a[g*r+l]+a[d*r+l]&&(s[i*e+f]=255)}let c=lt(s,e,o,!1);if(!c)throw new Error("Not enough visible background to reconstruct. Use Fast fill or a wider photo.");return{hole:s,donors:c,radius:n}}var T={sizeClampMin:2,sizeClampMax:90,colorGain:1.7,glowExp:1.6,twinkleBase:.65,twinkleAmp:.35,twinkleFreq:2.3,blend:"src-alpha,one"},Se=Math.PI/180;function Wt(t){if(t>.5)return-1;let e=pt(.5,0,t);return Math.pow(e,T.glowExp)}var qt=.8660254037844386,Le=.5*qt,Zt=.03;var Ae=3*Math.sqrt(3)/8;var U=9;function ut(t,e,o,n){let r=Math.max(3,Math.min(U,Math.round(o))),a=2*Math.PI/r,s=-1e9;for(let c=0;c<U&&!(c>=r);c++){let i=n+c*a;s=Math.max(s,t*Math.cos(i)+e*Math.sin(i))}return s}function Yt(t){let e=Math.max(3,Math.min(U,Math.round(t)));return .5*Math.cos(Math.PI/e)}function ft(t,e,o,n=6,r=0,a=0,s=0,c=0,i=0,f=1,l=1){let p=Wt(Math.sqrt(t*t+e*e));if(o<=0)return p;let d=Math.max(p,0),g=Yt(n),w=h=>1-pt(g-Zt,g,h),x=f>0?f:1;x!==1&&(t/=x,e/=x),l!==1&&l>0&&(t*=l);let u=w(ut(t,e,n,r));if(a>0){let h=a*.5;u=Math.min(u,w(ut(t+s*h,e+c*h,n,r)))}if(i!==0&&u>0){let h=Math.min(1,(t*t+e*e)/.25);u*=1+i*(h-.5)*2,u<0&&(u=0)}x!==1&&(u/=x*x);let m=d*(1-o)+u*o;return m>0?m:-1}function pt(t,e,o){let n=Math.min(1,Math.max(0,(o-t)/(e-t)));return n*n*(3-2*n)}var Xt=`${T.twinkleBase} + ${T.twinkleAmp} * sin(time * ${T.twinkleFreq} + seed * 6.2831853)`,Kt=`clamp(pointScale * size * tw * factor, ${T.sizeClampMin}.0, ${T.sizeClampMax}.0)`,Jt=`pow(smoothstep(0.5, 0.0, r), ${T.glowExp})`,Ee=`${T.colorGain}`,Ce=`float pointTwinkle(float time, float seed){ return ${Xt}; }`,Pe=`float pointSizePx(float size, float depth, float focal, float zoom, float pointScale, float tw){ float factor = (focal/depth) * zoom; return ${Kt}; }`,ke=`float pointGlow(vec2 coord){ vec2 c = coord - 0.5; float r = length(c); if (r > 0.5) return -1.0; return ${Jt}; }`;var Te=`
3
+ float apDist(vec2 p, float blades, float rot){
4
+ float step = 6.2831853 / blades;
5
+ float d = -1e9;
6
+ for (int k = 0; k < ${U}; k++) {
7
+ if (float(k) >= blades) break;
8
+ float a = rot + float(k) * step;
9
+ d = max(d, p.x * cos(a) + p.y * sin(a));
10
+ }
11
+ return d;
12
+ }
13
+ float pointAperture(vec2 coord, float mixv, float blades, float rot, float vig, vec2 field, float spherical, float k, float anamorphic){
14
+ float g = pointGlow(coord); // the canonical soft glow, called not restated
15
+ if (mixv <= 0.0) return g;
16
+ vec2 c = (coord - 0.5) / max(k, 1e-6); // longitudinal CA: this channel's own aperture radius
17
+ c.x *= max(anamorphic, 1e-6); // anamorphic: an oval aperture, compressed in x
18
+ float glow = max(g, 0.0);
19
+ float apothem = 0.5 * cos(3.14159265 / blades);
20
+ float hard = 1.0 - smoothstep(apothem - 0.03, apothem, apDist(c, blades, rot));
21
+ if (vig > 0.0) {
22
+ float k = vig * 0.5;
23
+ float d2 = apDist(c + field * k, blades, rot);
24
+ hard = min(hard, 1.0 - smoothstep(apothem - 0.03, apothem, d2));
25
+ }
26
+ // Mirror of the spherical-aberration term: over-corrected (>0) piles light at the rim (soap bubble),
27
+ // under-corrected (<0) pulls it to the centre (soft halo). Quadratic, because the wavefront error is.
28
+ if (spherical != 0.0 && hard > 0.0) {
29
+ float rr = min(1.0, dot(c, c) / 0.25);
30
+ hard = max(0.0, hard * (1.0 + spherical * (rr - 0.5) * 2.0));
31
+ }
32
+ hard /= max(k * k, 1e-6); // keep this channel's energy unchanged as its radius shrinks
33
+ float a = glow * (1.0 - mixv) + hard * mixv;
34
+ return a > 0.0 ? a : -1.0;
35
+ }`;function dt(t){let e=t.radiusPx;if(!(e>0))throw new Error(`rasterizeAperture: radiusPx must be > 0, got ${e}`);let o=t.size??2*Math.ceil(e)+1;if(o%2===0)throw new Error(`rasterizeAperture: size must be odd (centre on a pixel), got ${o}`);let n=Math.max(1,Math.round(t.supersample??4)),r=(o-1)/2,a=t.blades??6,s=t.bladeRotation??0,c=t.opticalVignette??0,i=t.fieldX??1,f=t.fieldY??0,l=t.sphericalAberration??0,p=t.anamorphic??1,d=t.radiusScale??1,g=t.mix??1,w=1/(2*e),x=new Float64Array(o*o),u=1/(n*n);for(let m=0;m<o;m++)for(let h=0;h<o;h++){let v=0;for(let M=0;M<n;M++)for(let b=0;b<n;b++){let C=h-r+(b+.5)/n-.5,P=m-r+(M+.5)/n-.5,E=ft(C*w,P*w,g,a,s,c,i,f,l,d,p);E>0&&(v+=E)}x[m*o+h]=v*u}return{width:o,height:o,data:x,cx:r,cy:r}}var R={lit:t=>Number.isInteger(t)?t.toFixed(1):String(t),add:(t,e)=>`(${t} + ${e})`,sub:(t,e)=>`(${t} - ${e})`,mul:(t,e)=>`(${t} * ${e})`,div:(t,e)=>`(${t} / ${e})`,sqrt:t=>`sqrt(${t})`,pow:(t,e)=>`pow(${t}, ${e})`,max:(t,e)=>`max(${t}, ${e})`,min:(t,e)=>`min(${t}, ${e})`},Qt=3.141592653589793;function jt(t,e,o){let n=t.mul(o,o),r=t.add(t.mul(t.mul(e,e),t.sub(n,t.lit(1))),t.lit(1));return t.div(n,t.mul(t.lit(Qt),t.mul(r,r)))}function te(t,e,o,n){let r=t.mul(n,n),a=t.lit(1),s=t.mul(e,t.sqrt(t.add(t.mul(t.mul(o,o),t.sub(a,r)),r))),c=t.mul(o,t.sqrt(t.add(t.mul(t.mul(e,e),t.sub(a,r)),r)));return t.div(t.lit(.5),t.max(t.lit(1e-6),t.add(s,c)))}function ee(t,e){return t.pow(t.sub(t.lit(1),e),t.lit(5))}var oe={NoH:"NoH",NoL:"NoL",NoV:"NoV",VoH:"VoH",a:"a"};function mt(t={}){let e={...oe,...t};return{D:jt(R,e.NoH,e.a),V:te(R,e.NoL,e.NoV,e.a),fresnelWeight:ee(R,e.VoH)}}function xt(t){return{scalar:R,scale:(e,o)=>`(${e} * ${o})`,vadd:(e,o)=>`(${e} + ${o})`,max0:e=>t.max0(e)}}function gt(t,e,o){let n=t.scalar,{x:r,y:a,z:s}=o,c=n.mul(r,r),i=n.mul(a,a),f=n.mul(s,s),l=n.lit(.429043),p=n.lit(.511664),d=n.lit(.743125),g=n.lit(.886227),w=n.lit(.247708),x=n.lit(2),u=t.scale(e(8),n.mul(l,n.sub(c,i)));u=t.vadd(u,t.scale(e(6),n.sub(n.mul(d,f),w))),u=t.vadd(u,t.scale(e(0),g));let m=n.mul(x,l);u=t.vadd(u,t.scale(e(4),n.mul(m,n.mul(r,a)))),u=t.vadd(u,t.scale(e(7),n.mul(m,n.mul(r,s)))),u=t.vadd(u,t.scale(e(5),n.mul(m,n.mul(a,s))));let h=n.mul(x,p);return u=t.vadd(u,t.scale(e(3),n.mul(h,r))),u=t.vadd(u,t.scale(e(1),n.mul(h,a))),u=t.vadd(u,t.scale(e(2),n.mul(h,s))),t.max0(t.scale(u,n.lit(.3183098861837907)))}function vt(t,e,o){let n=t.add(t.mul(o,t.lit(-1)),t.lit(1)),r=t.add(t.mul(o,t.lit(-.0275)),t.lit(.0425)),a=t.add(t.mul(o,t.lit(-.572)),t.lit(1.04)),s=t.add(t.mul(o,t.lit(.022)),t.lit(-.04)),c=t.pow(t.lit(2),t.mul(t.lit(-9.28),e)),i=t.add(t.mul(t.min(t.mul(n,n),c),n),r);return{a:t.max(t.lit(0),t.add(t.mul(t.lit(-1.04),i),a)),b:t.max(t.lit(0),t.add(t.mul(t.lit(1.04),i),s))}}var Ie=Math.PI*2,ne=gt(xt({max0:t=>`max(vec3(0.0), ${t})`}),t=>`uSH[${t}]`,{x:"n.x",y:"n.y",z:"n.z"}),Mt=vt(R,"NoV","roughness");var ze=1/Math.PI;var Ge=`
36
+ // EMITTED from model/brdf.ts (Ramamoorthi-Hanrahan) \u2014 the same source the WGSL twin and the CPU oracle
37
+ // use. The two implementations had DIVERGED: the WGSL one used the A0/A1/A2 convolution form with no
38
+ // 1/pi, making its ambient ~3.8x too bright with a direction-dependent error. One source, no drift.
39
+ // NOTE: the emitted form clamps to >= 0. This GLSL version did not; negative irradiance is never
40
+ // physically meaningful, and the shipping mesh goldens are unmoved by adding it.
41
+ vec3 irradianceSH(vec3 n) {
42
+ return ${ne};
43
+ }`;var Oe=`
44
+ // EMITTED from model/brdf.ts \u2014 Karis's analytic split-sum fit, so the WGSL ambient path can have the
45
+ // same env-BRDF without a DFG texture. exp2(-9.28*NoV) is emitted as pow(2, ...) because one expression
46
+ // then serves both languages.
47
+ vec2 envBrdfApprox(float NoV, float roughness) {
48
+ return vec2(${Mt.a}, ${Mt.b});
49
+ }`;var wt=mt({NoH:"nh",NoL:"nl",NoV:"nv",VoH:"vh",a:"a"}),Ne=`
50
+ float rangeAttenuation(float range, float dist) {
51
+ float inv = 1.0 / max(dist * dist, 1e-8);
52
+ if (range <= 0.0) return inv;
53
+ float f = dist / range;
54
+ float w = clamp(1.0 - f * f * f * f, 0.0, 1.0);
55
+ return w * w * inv;
56
+ }
57
+ float spotAttenuation(vec3 toLight, vec3 spotDir, float innerCos, float outerCos) {
58
+ float cosA = dot(normalize(spotDir), -toLight);
59
+ return smoothstep(outerCos, innerCos, cosA);
60
+ }
61
+ vec3 punctualContribution(int type, vec3 lcolor, float lintensity, vec3 lpos, vec3 ldir,
62
+ float lrange, vec2 lcone, vec3 fragPos, vec3 N, vec3 V, vec3 albedo, float metallic, float roughness,
63
+ float clearcoat, float ccRoughness, vec3 dielF0, float specW) {
64
+ if (lintensity <= 0.0) return vec3(0.0);
65
+ vec3 L; float atten = 1.0;
66
+ if (type == 0) {
67
+ L = normalize(-ldir);
68
+ } else {
69
+ vec3 toLight = lpos - fragPos;
70
+ float dist = max(length(toLight), 1e-4);
71
+ L = toLight / dist;
72
+ atten = rangeAttenuation(lrange, dist);
73
+ if (type == 2) atten *= spotAttenuation(L, ldir, lcone.x, lcone.y);
74
+ }
75
+ if (atten <= 0.0) return vec3(0.0);
76
+ float nl = dot(N, L);
77
+ if (nl <= 0.0) return vec3(0.0);
78
+ float nv = max(dot(N, V), 1e-4);
79
+ vec3 H = normalize(L + V);
80
+ float nh = max(dot(N, H), 0.0);
81
+ float vh = max(dot(V, H), 0.0);
82
+ float a = roughness * roughness;
83
+ // P2 (docs/pixel-parity-plan.md): D and the height-correlated Smith visibility are EMITTED from
84
+ // model/brdf.ts \u2014 the same source the WGSL twin and the CPU oracle come from. Do not hand-edit;
85
+ // edit the source and every backend follows. The WGSL copy of this lobe was separable for months
86
+ // and no test could tell, because the tests pinned a NAME.
87
+ float D = ${wt.D};
88
+ float vis = ${wt.V};
89
+ // fc stays hand-written: this twin does not clamp 1.0-vh and the WGSL one does. On valid input the
90
+ // clamp is a no-op (vh is already max(dot(V,H), 0.0)) and P1's colour gate confirms the pixels
91
+ // agree, so unifying it would mean editing a shipping shader for zero behavioural gain.
92
+ float fc = pow(1.0 - vh, 5.0);
93
+ // M3b: dielectric f0 from IOR + specular weight (mirrors the IBL F0/specW). dielF0 0.04 / specW 1 \u21D2 old.
94
+ vec3 f0 = mix(dielF0, albedo, metallic);
95
+ float dWeight = mix(specW, 1.0, metallic);
96
+ vec3 F = f0 + (1.0 - f0) * fc;
97
+ vec3 spec = vec3(D * vis) * F * dWeight;
98
+ vec3 kd = (1.0 - F) * (1.0 - metallic);
99
+ vec3 diffuse = kd * albedo / 3.141592653589793;
100
+ // M4b clear-coat lobe (KHR_materials_clearcoat) \u2014 a 2nd GGX lobe at fixed IOR 1.5 (f0 0.04) with
101
+ // Kelemen visibility; energy removed from the base by the coat Fresnel Fcc (= coatFresnel(cc, LoH)).
102
+ // ccRoughness clamped to a finite-sharp min. clearcoat=0 \u21D2 Fcc=0, coat=0 \u21D2 EXACT no-op.
103
+ float ccA = max(ccRoughness, 0.045); ccA *= ccA;
104
+ float ccA2 = ccA * ccA;
105
+ float ccdd = nh * nh * (ccA2 - 1.0) + 1.0;
106
+ float Dc = ccA2 / (3.141592653589793 * ccdd * ccdd);
107
+ float Vc = 0.25 / max(vh * vh, 1e-4);
108
+ float Fcc = clearcoat * (0.04 + 0.96 * fc);
109
+ float coat = Dc * Vc * Fcc;
110
+ return ((diffuse + spec) * (1.0 - Fcc) + vec3(coat)) * lcolor * (lintensity * atten) * nl;
111
+ }`;function bt(t){let e=`uShadow${t}`;return`
112
+ float shadowFactor${t}(vec3 fragPos, float nDotL) {
113
+ vec3 d = fragPos - ${e}Pos;
114
+ vec3 cam = vec3(dot(d, ${e}Right), dot(d, ${e}Up), dot(d, ${e}Forward));
115
+ float depth = cam.z; // d\xB7forward = positive light-space depth in front
116
+ if (depth <= ${e}Near) return 1.0;
117
+ // ORTHO (directional/cascade): depth-independent screen scale (factor = zoom), matching the depth
118
+ // pass's orthographic projection. Perspective (point/spot): factor = focal/depth\xB7zoom.
119
+ float factor = ${e}Ortho > 0.5 ? ${e}Zoom : (${e}Focal / depth) * ${e}Zoom;
120
+ // uv.y uses +cam.y: the depth pass writes through the rasterizer, and GL FBO texture rows run
121
+ // BOTTOM-UP (clip.y = +cam.y\xB7factor \u21D2 v\u21921), while projectToLight's v is screen top-down. An
122
+ // analytic sample must flip v or the silhouette reads upside-down (shadow head at the feet).
123
+ vec2 uv = vec2(0.5) + vec2(cam.x * factor, cam.y * factor) / ${e}Viewport;
124
+ if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1.0;
125
+ float frag = cam.z / ${e}Far;
126
+ float texelWorld = ${e}Ortho > 0.5 ? 1.0 / max(${e}Zoom, 1e-6) : depth / max(${e}Focal * ${e}Zoom, 1e-6);
127
+ float bias = (max(0.25, 1.0 * (1.0 - clamp(nDotL, 0.0, 1.0))) * texelWorld) / max(${e}Far, 1e-6);
128
+ // 5\xD75 PCF (25 taps) for a soft penumbra \u2014 a wider, smoother kernel than the old 3\xD73 single-tap
129
+ // edge. The 1.4-texel tap spacing widens the penumbra to \u2248 5 texels so edges read soft, not hard.
130
+ const float pcfStep = 1.4;
131
+ // PCSS (P1, gated by uPcssOn): a 3\xD73 blocker search averages the occluder depths nearer than this
132
+ // fragment, then the similar-triangles ratio (frag\u2212blocker)/blocker \xD7 LightSize sets how far the
133
+ // 5\xD75 kernel spreads \u2014 sharp at contact, soft when the caster floats high. Off \u21D2 tapStep == pcfStep
134
+ // and the search is skipped, so the loop below is byte-identical to the fixed-radius PCF path.
135
+ float tapStep = pcfStep;
136
+ if (uPcssOn > 0.5) {
137
+ float blockerSum = 0.0, blockerCount = 0.0;
138
+ const float searchStep = 2.0;
139
+ for (int a = -1; a <= 1; a++) {
140
+ for (int b = -1; b <= 1; b++) {
141
+ vec2 so = vec2(float(a), float(b)) * ${e}Texel * searchStep;
142
+ float sd = unpackDepth(texture2D(uShadowAtlas, atlasUvRemap(uv + so, ${e}Rect, uShadowAtlasSize)));
143
+ if (sd <= 1.0 && frag - bias > sd) { blockerSum += sd; blockerCount += 1.0; }
144
+ }
145
+ }
146
+ if (blockerCount < 0.5) return 1.0; // no occluder in the search radius \u21D2 fully lit (cheap interior)
147
+ float blockerAvg = blockerSum / blockerCount;
148
+ float penumbra;
149
+ if (${e}Ortho > 0.5) {
150
+ // Directional/cascade (P4): the light is effectively at infinity, so the penumbra scales with
151
+ // the ABSOLUTE receiver\u2194blocker gap \xD7 angular size \u2014 NOT the perspective ratio, which \u2192 0 for a
152
+ // far light. (frag\u2212blocker)\xB7Far is the world gap; dividing by this cascade's world-per-texel
153
+ // makes the TEXEL spread vary per cascade so the WORLD penumbra is identical across splits
154
+ // (a near cascade has finer texels \u21D2 more of them) \u2014 that's what keeps the soft edge seamless.
155
+ float gapWorld = max(0.0, frag - blockerAvg) * ${e}Far;
156
+ penumbra = clamp(gapWorld * ${e}LightSize / max(texelWorld, 1e-6), 0.0, 3.0);
157
+ } else {
158
+ penumbra = clamp((frag - blockerAvg) / max(blockerAvg, 1e-6) * ${e}LightSize, 0.0, 3.0);
159
+ }
160
+ tapStep = pcfStep * (1.0 + penumbra); // bounded so the kernel stays within the atlas tile inset
161
+ }
162
+ // Rotated 12-tap Poisson PCF (shared helper). radiusTexels = 2\xB7tapStep matches the old 5\xD75 grid's
163
+ // outer reach; taps remap into THIS caster's atlas tile (atlasUvRemap) so one sampler serves all.
164
+ float lit = pcfPoissonLit(uv, ${e}Rect, ${e}Texel, frag, bias, 2.0 * tapStep);
165
+ return 1.0 - (1.0 - lit) * ${e}Strength;
166
+ }`}var Ue=bt(0),He=bt(1);function Y(t,e){let o=e??[0,0],n=Math.round(Math.max(0,Math.min(1,t))*65535);return o[0]=Math.floor(n/256)/255,o[1]=n%256/255,o}function H(t,e){return(Math.round(t*255)*256+Math.round(e*255))/65535}var We={encoding:"linear",channel:"a",source:"sharp",far:1};var ae=`float unpack16(vec2 c) {
167
+ return (c.x * 255.0 * 256.0 + c.y * 255.0) / 65535.0;
168
+ }`;function yt(t){let{encoding:e}=t;if(e!=="unit"&&e!=="linear"&&e!=="packed16")throw new Error(`depth-field: unknown encoding ${JSON.stringify(e)}`);if(e==="unit"){if(t.nearIs!=="white"&&t.nearIs!=="black")throw new Error('depth-field: unit encoding requires nearIs ("white" | "black") \u2014 no default');if(!Number.isFinite(t.near)||!Number.isFinite(t.far))throw new Error("depth-field: unit encoding requires finite near and far");if(t.near===t.far)throw new Error("depth-field: unit encoding requires far \u2260 near")}else{if(t.nearIs!==void 0)throw new Error(`depth-field: nearIs is only valid on unit encoding (got ${e})`);if(!Number.isFinite(t.far)||t.far<=0)throw new Error(`depth-field: ${e} encoding requires far > 0`)}let o=X(t);if(e==="packed16"&&o!=="ba")throw new Error('depth-field: packed16 encoding requires channel "ba"');if(e!=="packed16"&&o==="ba")throw new Error('depth-field: channel "ba" is packed16-only');if(V(t)==="sharp"&&e==="packed16")throw new Error("depth-field: packed16 cannot be packed in the sharp colour (no two channels free)")}function X(t){return t.channel?t.channel:t.encoding==="packed16"?"ba":t.encoding==="linear"&&V(t)==="sharp"?"a":"r"}function V(t){return t.source?t.source:t.encoding==="packed16"?"prepass":t.channel==="r"?"depth":t.encoding==="linear"?"sharp":"depth"}function re(t,e){let o=X(e);return o==="ba"?H(t[2]??0,t[3]??0):o==="a"?t[3]??0:t[0]??0}function ce(t,e){yt(e);let o=re(t,e);if(e.encoding==="unit"){let n=e.nearIs==="white"?1-o:o;return e.near+n*(e.far-e.near)}return o*e.far}function St(t,e){return Math.max(0,ce(e,t))}function se(t){return t===300?"texture":"texture2D"}function ie(t,e,o){let n=X(t),r=V(t);if(r==="sharp")return n==="a"?"s.a":"s.r";let a=r==="prepass"?"uPrepass":"uDepth",s=n==="ba"?"ba":n,c=`${se(e)}(${a}, ${o}).${s}`;return n==="ba"?`unpack16(${c})`:c}function qe(t,e){yt(t);let o=e.uv??"vUv",n=ie(t,e.dialect,o),r;t.encoding==="unit"?r=`mix(uNear, uFar, ${t.nearIs==="white"?`1.0 - ${n}`:n})`:r=`${n} * uFar`;let a=[],s=V(t);return s==="depth"&&a.push("uniform sampler2D uDepth;"),s==="prepass"&&a.push("uniform sampler2D uPrepass;"),t.encoding==="unit"&&a.push("uniform float uNear;"),t.encoding==="packed16"&&a.push(ae),{depthExpr:r,depthUniforms:a.join(`
169
+ `)}}var K=.5,le=.75,he=256,ue=4,fe=8,pe=1.5;function $(t,e,o){return .2126*t+.7152*e+.0722*o}function de(t,e,o){if(e<=t)return o>=e?1:0;let n=Math.min(1,Math.max(0,(o-t)/(e-t)));return n*n*(3-2*n)}function J(t,e,o=.08){return de(e-o,e+o,t)}var Ye=`
170
+ float scatterRamp(float e0, float e1, float x) {
171
+ if (e1 <= e0) return x >= e1 ? 1.0 : 0.0;
172
+ float t = clamp((x - e0) / (e1 - e0), 0.0, 1.0);
173
+ return t * t * (3.0 - 2.0 * t);
174
+ }
175
+ float scatterWeight(float sourcePx, float coc, float compactness) {
176
+ float focus = scatterRamp(${K}, ${K} * 3.0, coc);
177
+ if (focus <= 0.0) return 0.0;
178
+ float allowed = max(2.0, compactness * max(coc, ${K}));
179
+ float compact = 1.0 - scatterRamp(allowed * 0.75, allowed * 1.25, max(1.0, sourcePx));
180
+ return focus * compact;
181
+ }`;function Lt(t,e,o,n,r){let a=r.threshold??le,s=r.maxCount??he,c=r.isolation??ue,i=r.maxExtentPx??fe,f=r.compactness??pe,l=o*n,p=new Uint8Array(l),d=[],g=(x,u)=>u*o+x,w=(x,u)=>x>=0&&u>=0&&x<o&&u<n;for(let x=0;x<n;x++)for(let u=0;u<o;u++){let m=g(u,x);if(p[m])continue;let h=m*4;if($(t[h],t[h+1],t[h+2])<=a){p[m]=1;continue}let M=[m];p[m]=1;let b=[],C=u,P=u,E=x,D=x,_=0,j=0,tt=0,et=0,ot=0,nt=0;for(;M.length;){let A=M.pop(),S=A%o,L=A/o|0;b.push(A);let k=A*4,N=$(t[k],t[k+1],t[k+2]);_+=N,j+=S*N,tt+=L*N;let q=J(N,a);et+=t[k]*q,ot+=t[k+1]*q,nt+=t[k+2]*q,S<C&&(C=S),S>P&&(P=S),L<E&&(E=L),L>D&&(D=L);let Nt=[A-1,A+1,A-o,A+o],it=[[S-1,L],[S+1,L],[S,L-1],[S,L+1]];for(let I=0;I<4;I++){let Ut=it[I][0],Ht=it[I][1];if(!w(Ut,Ht))continue;let z=Nt[I];if(p[z])continue;let Z=z*4;if($(t[Z],t[Z+1],t[Z+2])<=a){p[z]=1;continue}p[z]=1,M.push(z)}}let Dt=P-C+1,Rt=D-E+1,at=Math.max(Dt,Rt),rt=_>0?j/_:u,ct=_>0?tt/_:x,_t=e[g(Math.round(rt),Math.round(ct))]??e[m];if(at>i)continue;let O=4,st=0,W=0,It=new Set(b);for(let A=E-O;A<=D+O;A++)for(let S=C-O;S<=P+O;S++){if(!w(S,A))continue;let L=g(S,A);if(It.has(L))continue;let k=L*4;st+=$(t[k],t[k+1],t[k+2]),W++}let zt=b.reduce((A,S)=>{let L=S*4;return Math.max(A,$(t[L],t[L+1],t[L+2]))},0),Gt=W?st/W:0;if(zt<c*Math.max(Gt,1e-4))continue;let Ot=b.length;d.push({uv:[(rt+.5)/o,(ct+.5)/n],rgb:[et,ot,nt],z:_t,sourcePx:Math.max(1,Math.sqrt(Ot)),extentPx:at,footprint:b})}return d.sort((x,u)=>$(u.rgb[0],u.rgb[1],u.rgb[2])-$(x.rgb[0],x.rgb[1],x.rgb[2])),d.slice(0,s)}var me={encoding:"unit",source:"depth",channel:"r",near:1,far:8,nearIs:"white"},xe={enabled:!1,mode:"auto",look:"auto",focus:70,bias:0,blur:26,shape:"round",highlights:30},ge=8,je=.065,to=1.15,eo=36e3;function At(t={}){t||(t={});let e=n=>Math.max(0,Math.min(100,Number.isFinite(t[n])?t[n]:xe[n])),o=t.look==null;return{enabled:t.enabled===!0,mode:o&&t.mode!=="manual"?"auto":["mouse","auto","manual"].includes(t.mode)?t.mode:"auto",look:["auto","subtle","cinematic","custom"].includes(t.look)?t.look:"auto",focus:e("focus"),bias:Math.max(-50,Math.min(50,Number.isFinite(t.bias)?t.bias:0)),blur:e("blur"),highlights:e("highlights"),shape:["round","hex","oval"].includes(t.shape)?t.shape:"round"}}function oo(t,e,o){if(t.mode==="manual")return t.focus/100;let n=t.mode==="mouse"&&e!=null?e:o;return Math.max(0,Math.min(1,n+(t.mode==="mouse"?(t.bias||0)/100:0)))}function no(t,e,o){let n=new Float32Array(e*o*4),r=new Float32Array(e*o).fill(4);for(let i=0;i<t.length;i+=4){let f=t[i+3]/255;for(let l=0;l<3;l++){let p=t[i+l]/255;n[i+l]=(p<=.04045?p/12.92:((p+.055)/1.055)**2.4)*f}n[i+3]=f}let a=.55,s=Lt(n,r,e,o,{focusDistance:2,focalLength:.05,aperture:.05,threshold:a,isolation:2.5,maxExtentPx:12,maxCount:256}),c=new Uint8Array(e*o);for(let i of s)for(let f of i.footprint){let l=f*4,p=n[l]*.2126+n[l+1]*.7152+n[l+2]*.0722;c[f]=Math.round(255*J(p,a))}return{mask:c,highlights:s,width:e,height:o}}function ao(t={},e,o,n="auto"){let r=(p,d,g)=>Math.max(d,Math.min(g,p)),a=(e?.highlights||[]).filter(p=>{if(!o?.usable)return!0;let d=Math.min(o.width-1,Math.floor(p.uv[0]*o.width)),g=Math.min(o.height-1,Math.floor(p.uv[1]*o.height));return o.mask[g*o.width+d]<128}).length,s=r((t.soft||0)*1.4+(t.border||0)*.4+(t.detail||0)*2,0,1),c=r((t.gap||t.span||0)/.35,.45,1);return{blur:Math.round((n==="subtle"?16:n==="cinematic"?52:a?30:24)*(1-s*.4)*c),highlights:a?n==="subtle"?18:n==="cinematic"?75:42:0,backgroundLights:a}}function ro(t){return St(me,[Math.max(0,Math.min(1,t)),0,0,1])}function co(t,e,o=!1){if(!t)return .7;let n=[],r=Math.max(1,Math.ceil(Math.sqrt(t.width*t.height/8e3)));for(let s=0;s<t.height;s+=r)for(let c=0;c<t.width;c+=r){if(e?.usable){let f=Math.min(e.width-1,Math.floor(c/t.width*e.width)),l=Math.min(e.height-1,Math.floor(s/t.height*e.height));if(e.mask[l*e.width+f]<240)continue}else if(c<t.width*.3||c>t.width*.7||s<t.height*.3||s>t.height*.7)continue;let i=t.values[s*t.width+c];Number.isFinite(i)&&n.push(i)}n.sort((s,c)=>s-c);let a=n.length?n[Math.floor(n.length/2)]:.7;return o?1-a:a}function so(t){let e=ge,o=dt({radiusPx:e,blades:t==="hex"?6:9,anamorphic:t==="oval"?1.8:1,supersample:4}),n=o.data.reduce((a,s)=>a+s,0),r=[];for(let a=0;a<o.height;a++)for(let s=0;s<o.width;s++){let c=o.data[a*o.width+s]/n;c>0&&r.push([(s-o.cx)/e,(a-o.cy)/e,c])}return r}var Et=`ARI-SPATIAL-PNG:1
182
+ `,y=32;function Q(t={}){t||(t={});let e=(n,r,a,s)=>Math.max(a,Math.min(s,Number.isFinite(t[n])?t[n]:r)),o=(n,r,a)=>r.includes(t[n])?t[n]:a;return{effect:o("effect",["spatial","dof","relief","parallax","flat"],"spatial"),material:o("material",["photo","particles"],"photo"),color:o("color",["color","mono"],"color"),strength:e("strength",35,0,100),separation:e("separation",0,0,100),orbitRange:e("orbitRange",3,0,75),orbitPeriod:e("orbitPeriod",30,8,120),size:e("size",2,.5,5),density:e("density",260,80,500),edge:e("edge",50,0,100),invert:t.invert===!0,motion:t.motion!==!1,adaptive:t.adaptive!==!1,cleanEdges:t.cleanEdges!==!1,subjectView:o("subjectView",["scene","foreground","background","off"],"scene"),lens:At(t.effect==="dof"?{...t.lens,enabled:!0}:t.lens),compare:"animated",frozen:!1,map:!1}}function ve(t,e,o){if(t?.format!=="ari-spatial-photo"||t.version!==1||t.depthEncoding!=="rg16-near-white")throw new Error("Unsupported spatial PNG version.");if(t.width!==e||t.height!==o||e<64||e>8192||o>8192||e*o>32e6)throw new Error("Spatial PNG dimensions changed or exceed the supported size. Use the original file.");if(t.foregroundRefined!==void 0&&(t.foregroundRefined!==!0||!t.tiles?.mask))throw new Error("Invalid refined foreground metadata.");let n=Object.entries(t.tiles||{});if(!t.tiles?.photo||!t.tiles?.depth||n.some(([a])=>!["photo","depth","mask","background"].includes(a)))throw new Error("Spatial PNG is missing its photo or depth.");for(let[,a]of n)if(!a||![a.x,a.y,a.width,a.height].every(Number.isSafeInteger)||a.x<0||a.y<y||a.width<1||a.height<1||a.x+a.width>e||a.y+a.height>o)throw new Error("Invalid spatial PNG tile bounds.");for(let a=0;a<n.length;a++)for(let s=a+1;s<n.length;s++){let c=n[a][1],i=n[s][1];if(c.x<i.x+i.width&&c.x+c.width>i.x&&c.y<i.y+i.height&&c.y+c.height>i.y)throw new Error("Spatial PNG tiles overlap.")}if(t.tiles.background&&(!t.tiles.mask||t.tiles.background.width!==t.tiles.mask.width||t.tiles.background.height!==t.tiles.mask.height))throw new Error("Background and mask dimensions differ.");let r=t.tiles.photo.width/t.tiles.photo.height;for(let[,a]of n)if(Math.abs(a.width/a.height/r-1)>.02)throw new Error("Spatial PNG layers use different crops.");if(!t.hashes||!/^[a-f0-9]{64}$/.test(t.hashes.depth)||t.tiles.mask&&!/^[a-f0-9]{64}$/.test(t.hashes.mask))throw new Error("Spatial PNG is missing map integrity checks.");return t}function Ct(t,e){let o=new TextEncoder().encode(Et),n=new TextEncoder().encode(JSON.stringify(t)),r=new Uint8Array(o.length+4+n.length);if(r.set(o),new DataView(r.buffer).setUint32(o.length,n.length),r.set(n,o.length+4),r.length>e*y*3)throw new Error("Spatial PNG metadata is too large.");let a=new Uint8ClampedArray(e*y*4);for(let s=0;s<e*y;s++){a[s*4+3]=255;for(let c=0;c<3;c++)a[s*4+c]=r[s*3+c]||0}return a}function Pt(t,e,o){let n=new Uint8Array(Math.floor(t.length/4)*3);for(let c=0;c<t.length/4;c++)n.set(t.subarray(c*4,c*4+3),c*3);let r=new TextEncoder().encode(Et);if(!r.every((c,i)=>n[i]===c))return null;let a=new DataView(n.buffer).getUint32(r.length);if(a<2||a>n.length-r.length-4)throw new Error("Spatial PNG header is damaged.");let s=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(n.subarray(r.length+4,r.length+4+a)));return ve(s,e,o)}function kt(t){let e=new Uint8ClampedArray(t.length*4),o=[0,0];return t.forEach((n,r)=>{if(!Number.isFinite(n)||n<0||n>1)throw new Error("Spatial depth must be finite and normalized.");Y(n,o),e.set([Math.round(o[0]*255),Math.round(o[1]*255),0,255],r*4)}),e}function Tt(t){return Float32Array.from({length:t.length/4},(e,o)=>H(t[o*4]/255,t[o*4+1]/255))}var F=(t,e)=>{let o=document.createElement("canvas");return o.width=t,o.height=e,o},$t=t=>new Promise((e,o)=>t.toBlob(n=>n?e(n):o(new Error("Could not encode spatial PNG.")),"image/png")),B=async t=>Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256",t))).map(e=>e.toString(16).padStart(2,"0")).join("");async function xo({photo:t,depth:e,subject:o,fill:n,settings:r,name:a="Spatial photo"}){if(!t||!e)throw new Error("Load a photo and its depth first.");let s=Q(r),c=Math.max(32,t.width,e.width,o?.width||0),i=Math.max(t.height,e.height,o?.height||0),f=c*2,l=y+i*(o?2:1);if(f>8192||l>8192||f*l>32e6)throw new Error("This photo is too large for a spatial PNG.");let p=F(f,l),d=p.getContext("2d"),g={photo:{x:0,y,width:t.width,height:t.height},depth:{x:c,y,width:e.width,height:e.height}},w=!!o?.foreground;if(w){if(o.foreground.length!==o.width*o.height*4)throw Error("Refined foreground dimensions do not match.");let h=F(o.width,o.height);h.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(o.foreground),o.width,o.height),0,0),d.drawImage(h,0,y,t.width,t.height)}else d.drawImage(t,0,y);let x=kt(e.values);d.putImageData(new ImageData(x,e.width,e.height),c,y);let u={depth:await B(x)};if(o){let{width:h,height:v}=o,M=new Uint8ClampedArray(h*v*4);o.mask.forEach((b,C)=>M.set([b,b,b,255],C*4)),g.mask={x:0,y:y+i,width:h,height:v},d.putImageData(new ImageData(M,h,v),0,y+i),u.mask=await B(M),o.usable&&(g.background={x:c,y:y+i,width:h,height:v},d.putImageData(new ImageData(new Uint8ClampedArray(n?.background||o.background),h,v),c,y+i))}let m={format:"ari-spatial-photo",version:1,name:String(a).slice(0,200),width:f,height:l,tiles:g,hashes:u,depthEncoding:"rg16-near-white",settings:s,fillRadius:n?.radius||0,...w?{foregroundRefined:!0}:{}};return d.putImageData(new ImageData(Ct(m,f),f,y),0,0),{blob:await $t(p),manifest:m}}async function spatialSplit(t){let n=new Uint8Array(await t.arrayBuffer());if(![137,80,78,71,13,10,26,10].every((e,o)=>n[o]===e))return[t,null];let r=new DataView(n.buffer,n.byteOffset,n.byteLength),o=8;for(;o+12<=n.length;){let e=r.getUint32(o),a=String.fromCharCode(n[o+4],n[o+5],n[o+6],n[o+7]);if(a==="spAt")return[new Blob([n.subarray(o+8,o+8+e)],{type:"image/png"}),t];if(a==="IEND")break;o+=12+e}return[t,null]}async function spatialPhotoTile(t,e){let n=await createImageBitmap(t,{colorSpaceConversion:"none",premultiplyAlpha:"none"});try{let r=F(e.width,e.height);return r.getContext("2d").drawImage(n,0,0,e.width,e.height),r}finally{n.close()}}async function Me(t,{required:e=!1}={}){if(t.size>50*1024*1024)throw new Error("Spatial PNG must be smaller than 50 MB.");let[tg,tp]=await spatialSplit(t);t=tg;let o=new Uint8Array(await t.slice(0,24).arrayBuffer());if(![137,80,78,71,13,10,26,10].every((i,f)=>o[f]===i)){if(e)throw new Error("Use the original spatial PNG file.");return null}if(o.length<24)throw new Error("Incomplete PNG file.");let r=new DataView(o.buffer),a=r.getUint32(16),s=r.getUint32(20);if(a>8192||s>8192||a*s>32e6){if(e)throw new Error("Spatial PNG dimensions exceed the supported size.");return null}if(a<64||s<y){if(e)throw new Error("This is not a spatial PNG.");return null}let c=await createImageBitmap(t,{colorSpaceConversion:"none",premultiplyAlpha:"none"});try{let i=F(a,y),f=i.getContext("2d",{willReadFrequently:!0});f.drawImage(c,0,0);let l=Pt(f.getImageData(0,0,a,y).data,a,s);if(!l){if(e)throw new Error("Spatial data is missing. Use the exported PNG without resizing or image optimization.");return null}let p=h=>{let v=F(h.width,h.height);return v.getContext("2d",{willReadFrequently:!0}).drawImage(c,h.x,h.y,h.width,h.height,0,0,h.width,h.height),v},d=h=>h.getContext("2d",{willReadFrequently:!0}).getImageData(0,0,h.width,h.height).data,g=tp?await spatialPhotoTile(tp,l.tiles.photo):p(l.tiles.photo),w=d(p(l.tiles.depth));if(await B(w)!==l.hashes.depth)throw new Error("Depth pixels changed. Load the original lossless spatial PNG.");let x={key:`spatial-depth-${l.hashes.depth}`,width:l.tiles.depth.width,height:l.tiles.depth.height,values:Tt(w),source:"import",name:l.name,device:"asset",inferenceMs:0,tier:"spatial"},u=null,m=null;if(l.tiles.mask){let h=d(p(l.tiles.mask));if(await B(h)!==l.hashes.mask)throw new Error("Mask pixels changed. Load the original lossless spatial PNG.");let{width:v,height:M}=l.tiles.mask,b=F(v,M);b.getContext("2d").drawImage(g,0,0,v,M);let C=d(b),P=Uint8Array.from({length:v*M},(E,D)=>h[D*4]);if(u={...ht(P,C,v,M),key:`spatial-mask-${l.hashes.mask}`,mask:P,width:v,height:M,device:"asset",inferenceMs:0,edited:!0},l.foregroundRefined&&(u.foreground=C),l.tiles.background&&u.usable){let E=Math.max(0,Math.min(64,Number.isInteger(l.fillRadius)?l.fillRadius:0));m={key:"spatial-background",background:d(p(l.tiles.background)),backgroundDonors:E?G(P,v,M,E).donors:u.backgroundDonors,width:v,height:M,radius:E,device:"asset",inferenceMs:0,tier:"spatial"}}}return{manifest:l,photo:g,photoBlob:await $t(g),depth:x,subject:u,fill:m,settings:Q(l.settings)}}finally{c.close()}}async function go(t){let e=new URL(t,location.href);if(!["https:","http:","blob:"].includes(e.protocol))throw new Error("Use an HTTP(S) spatial PNG URL.");let o=await fetch(e,{credentials:"omit"});if(!o.ok)throw new Error(`Could not load spatial PNG (${o.status}).`);let n=o.body.getReader(),r=[],a=0;try{for(;;){let{value:c,done:i}=await n.read();if(i)break;if(a+=c.length,a>50*1024*1024)throw new Error("Spatial PNG must be smaller than 50 MB.");r.push(c)}}finally{await n.cancel()}let s=new Blob(r,{type:"image/png"});return{blob:s,asset:await Me(s,{required:!0})}}function vo(t,e){if(!t||t.width===e.width&&t.height===e.height)return t;let o=F(t.width,t.height),n=F(e.width,e.height);o.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(t.background),t.width,t.height),0,0);let r=n.getContext("2d");r.drawImage(o,0,0,n.width,n.height);let a=Math.max(2,Math.ceil((t.radius||0)*n.width/t.width));return{...t,key:t.key+"|"+e.key,width:n.width,height:n.height,radius:a,background:r.getImageData(0,0,n.width,n.height).data,backgroundDonors:G(e.mask,n.width,n.height,a).donors}}function bo(t,e,o){let n=Math.min(e*.8,t*.8/o),r=n*o;return{x:(t-r)/2,y:(e-n)/2,width:r,height:n}}function yo(t){let e=Uint8Array.from(t.mask,o=>o<240?255:0);try{return G(e,t.width,t.height,Math.max(1,Math.ceil(Math.max(t.width,t.height)*.002))).donors}catch{return t.foregroundDonors}}function So(t,e){if(!t)return{gap:0,span:0,detail:0,soft:0,border:0,separated:!1};let o=e?.usable?e.width:t.width,n=e?.usable?e.height:t.height,r=Math.max(1,Math.ceil(Math.sqrt(o*n/4e4))),a=[],s=[],c=[],i=0,f=0,l=0,p=0,d=0,g=0;for(let m=0;m<n;m+=r)for(let h=0;h<o;h+=r){let v=t.values[Math.min(t.height-1,Math.floor(m/n*t.height))*t.width+Math.min(t.width-1,Math.floor(h/o*t.width))];if(!Number.isFinite(v))continue;if(c.push(v),h+r<o){let b=t.values[Math.min(t.height-1,Math.floor(m/n*t.height))*t.width+Math.min(t.width-1,Math.floor((h+r)/o*t.width))];Number.isFinite(b)&&(g++,Math.abs(v-b)>.12&&d++)}let M=e?.usable?e.mask[m*o+h]:0;M>4&&(f++,M<240&&i++),M>=240?a.push(v):M<=4&&s.push(v),(h<r||h>=o-r||m<r||m>=n-r)&&(p++,M>127&&l++)}let w=(m,h)=>(m.sort((v,M)=>v-M),m[Math.min(m.length-1,Math.floor(m.length*h))]||0),x=!!e?.usable&&a.length>0&&s.length>0;return{gap:x?Math.abs(w(a,.5)-w(s,.5)):w(c,.9)-w(c,.1),span:w(c,.9)-w(c,.1),detail:d/Math.max(1,g),soft:i/Math.max(1,f),border:l/Math.max(1,p),separated:x}}function Lo(t,e=!1,o=1){let n=(p,d,g)=>Math.max(d,Math.min(g,p)),r=n(t.soft*1.4+t.border*.65+(t.detail||0)*2.5,0,1),a=t.span??t.gap,s=Math.round(n(44/Math.max(.3,a),32,64)*(1-r*.22)*(t.separated?1:.9)),c=n(Math.sqrt(Math.max(.1,o)),.72,1.05),i=(t.separated?e?4.2:2.6:2.2)*(1-r*.35)*c,f=Math.round(n(Math.min(i,Ft(t,s,e)),.8,4.4)*10)/10,l=Math.round(28+r*10+(o<.85?3:0));return{strength:s,orbitRange:f,orbitPeriod:l}}function Ao({phase:t,time:e,scale:o,pointer:n,tilt:r,interacting:a,motion:s,reduced:c}){if(c)return[0,0];let i=1-Math.exp(-Math.max(0,e)/1800),f=a||Math.hypot(...r)>.04;return(s?[Math.sin(t)*.65,Math.cos(t)*.65]:[0,0]).map((p,d)=>Math.max(-1,Math.min(1,n[d]+r[d]+p*i*(f?.15:1)))*o)}function Ft(t,e,o=!1,n=0){let r=(o?.055:.024)*(1-.65*t.soft)*(1-.7*t.border)/(1+4*(t.detail||0)),a=t.gap+(t.separated?Math.max(0,Math.min(100,n))/100*.7:0),s=Math.max(.001,a*Math.max(0,e)/100*1.6*.7),c=Math.asin(Math.min(1,r/s))*180/Math.PI;return Math.max(.25,Math.floor(Math.min(t.separated?8:3,c)*100)/100)}function Eo(t,{strength:e=35,separation:o=0,orbitRange:n=3,effect:r="spatial",aspect:a=1,filled:s=!1}={}){let c=Math.max(0,Math.min(100,e))/100,i=r==="spatial"?Math.max(0,Math.min(100,o)):0,f=Math.min(Math.max(0,n),8,Ft(t,e,s,i)),l=1.16;if(r==="parallax"){let g=f*Math.PI/180/.23*.055*1.6*c;return{range:f,zoom:Math.min(l,1/(1-g))}}let p=1.5*(.8+c*(.6+.7*i/100))/Math.min(1,Math.max(.25,a)),d=Math.asin(Math.min(1,(l-1)/p))*180/Math.PI;return f=Math.min(f,d),{range:f,zoom:Math.min(l,1+Math.sin(f*Math.PI/180)*p)}}export{We as a,qe as b,be as c,me as d,ge as e,je as f,to as g,eo as h,At as i,oo as j,no as k,ao as l,ro as m,co as n,so as o,Q as p,xo as q,Me as r,go as s,vo as t,bo as u,yo as v,So as w,Lo as x,Ao as y,Ft as z,Eo as A};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ var d="https://www.wixapis.com/site-media/v1";async function c(e,{fileName:i,authorization:n,request:t=(...o)=>fetch(...o)}){let o=await t(d+"/files/generate-upload-url",{method:"POST",headers:{Authorization:n,"content-type":"application/json"},body:JSON.stringify({mimeType:e.type||"image/png",fileName:i})});if(!o.ok)throw Error(`Could not save to your site's media (${o.status}).`);let{uploadUrl:r}=await o.json(),s=await t(r+"?filename="+encodeURIComponent(i),{method:"PUT",headers:{"content-type":e.type||"image/png"},body:e});if(!s.ok)throw Error(`Uploading to your site's media failed (${s.status}).`);let{file:a}=await s.json();if(!a?.id||!a?.url)throw Error("Wix did not return the saved file.");return{id:a.id,url:"https://static.wixstatic.com/media/"+a.id}}async function p(e,{request:i=(...t)=>fetch(...t),attempts:n=8}={}){for(let t=0;t<n;t++){if((await i(e,{method:"HEAD",cache:"no-store"}).catch(()=>null))?.ok)return e;await new Promise(r=>setTimeout(r,500*2**Math.min(t,4)))}throw Error("The saved 3D image is not available yet. Try again in a minute.")}export{c as uploadToSiteMedia,p as whenServed};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{a}from"./spatial-LAYZ53II.js";export{a as createWorkerSessionPool};
@@ -0,0 +1,240 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{A as dt,a as K,b as M,d as W,e as J,f as $,g as Z,h as Q,i as tt,j as et,k as it,l as st,m as rt,n as ot,o as nt,p as at,r as ht,s as lt,u as ut,v as ct,w as U,y as ft,z as pt}from"./spatial-QDMH4O6I.js";import{f as X}from"./spatial-GZVXIGKN.js";var Ct={width:800,height:512,widthSegments:16,heightSegments:16,center:[0,0]};function mt(n){return Number.isFinite(n)?Math.max(1,Math.floor(n)):1}function gt(n={}){let e={...Ct,...n},t=mt(e.widthSegments),i=mt(e.heightSegments),s=t+1,o=i+1,[r,h]=e.center,p=[];for(let a=0;a<o;a++)for(let c=0;c<s;c++){let f=c/t,u=a/i;p.push({index:a*s+c,col:c,row:a,u:f,v:u,x:r+(f-.5)*e.width,y:h+(u-.5)*e.height,z:0})}let l=[];for(let a=0;a<i;a++)for(let c=0;c<t;c++){let f=a*s+c,u=a*s+c+1,E=(a+1)*s+c,d=(a+1)*s+c+1;l.push(f,E,u,u,E,d)}return{vertices:p,indices:l,cols:s,rows:o}}var Lt=`
3
+ vec3 camSpace(vec3 p, vec3 camPos, vec3 right, vec3 up, vec3 forward) {
4
+ vec3 d = p - camPos;
5
+ return vec3(dot(d, right), dot(d, up), dot(d, forward));
6
+ }`,Nt=`
7
+ vec4 projectClip(vec3 cam, float focal, float zoom, vec2 viewport) {
8
+ float factor = (focal / cam.z) * zoom;
9
+ vec2 screen = viewport * 0.5 + vec2(cam.x * factor, -cam.y * factor);
10
+ return vec4(screen.x / viewport.x * 2.0 - 1.0, 1.0 - screen.y / viewport.y * 2.0, 0.0, 1.0);
11
+ }`,Et=`${Lt}${Nt}`;var F=(n,e,t)=>n+(e-n)*t;function vt(n,e,t){let i=((e-n+180)%360+360)%360-180;return n+i*t}function Tt(n,e,t){if(t>=1)return e;let i=n.position,s=e.position,o=n.orientation??{},r=e.orientation??{},h={};return s&&(h.position=i?[F(i[0],s[0],t),F(i[1],s[1],t),F(i[2],s[2],t)]:[...s]),h.orientation={pan:vt(o.pan??r.pan??0,r.pan??0,t),tilt:F(o.tilt??r.tilt??0,r.tilt??0,t),roll:vt(o.roll??r.roll??0,r.roll??0,t)},e.fov!==void 0&&(h.fov=F(n.fov??e.fov,e.fov,t)),e.zoom!==void 0&&(h.zoom=F(n.zoom??e.zoom,e.zoom,t)),h}var It=1e3/60,kt={position:.5,angle:.1,fov:.05,zoom:.005};function C(n){let e=n.orientation??{};return{position:n.position?[...n.position]:[0,0,-800],orientation:{pan:e.pan??0,tilt:e.tilt??0,roll:e.roll??0},fov:n.fov??50,zoom:n.zoom??1}}var xt=(n,e)=>Math.abs(((e-n+180)%360+360)%360-180);function P(n){let e=C(n.pose),t=null,i=bt(n.damping??.12),s=n.maxDtMs??64,o={...kt,...n.rest??{}},r=!0,h=()=>{if(!t)return!0;let p=C(t);return Math.hypot(e.position[0]-p.position[0],e.position[1]-p.position[1],e.position[2]-p.position[2])<=o.position&&xt(e.orientation.pan,p.orientation.pan)<=o.angle&&Math.abs(e.orientation.tilt-p.orientation.tilt)<=o.angle&&xt(e.orientation.roll,p.orientation.roll)<=o.angle&&Math.abs(e.fov-p.fov)<=o.fov&&Math.abs(e.zoom-p.zoom)<=o.zoom};return{get pose(){return e},get destination(){return t},get settled(){return r},goTo(p,l){t=C(p),l?.damping!==void 0&&(i=bt(l.damping)),r=h()},step(p){if(!t||r)return e;let l=Math.max(0,Math.min(s,p)),a=1-Math.pow(1-i,l/It);return e=C(Tt(e,t,a)),r=h(),r&&(e=C(t)),e}}}function bt(n){return Math.min(.999,Math.max(.001,n))}var Bt=n=>n===300?`#version 300 es
12
+ precision highp float;
13
+ in vec2 vUv;
14
+ out vec4 fragColor;`:`precision highp float;
15
+ varying vec2 vUv;`,Ot=n=>n===300?"texture":"texture2D",H=(n,e)=>n===300?`fragColor = ${e};`:`gl_FragColor = ${e};`,Rt=`
16
+ vec3 srgbToLinear(vec3 c) {
17
+ return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c));
18
+ }
19
+ vec3 linearToSrgb(vec3 c) {
20
+ return mix(c * 12.92, 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, step(vec3(0.0031308), c));
21
+ }`,Dt=`
22
+ float cocPx(float camz, float focus, float focalLen, float aperture, float sensor, float maxCoC) {
23
+ if (camz <= 0.0) return aperture > 0.0 ? maxCoC : 0.0; // pinhole \u21D2 no blur, background included
24
+ return min(abs(aperture * focalLen * (camz - focus) / (camz * (focus - focalLen))) * sensor, maxCoC);
25
+ }`;function N(n){let{dialect:e,depthExpr:t}=n,i=Ot(e),s=n.uvSource??"vUv",o=n.uvTarget??"vUv",r=n.header??Bt(e),h=n.encoding==="srgb",p=n.depthUniforms??"",l=h?"srgbToLinear(%)":"%",a=h?"linearToSrgb(%)":"%",c=(d,T)=>d.replace("%",T),f=`${r}
26
+ uniform sampler2D uSharp;
27
+ ${p}
28
+ uniform float uFocus, uFocalLen, uAperture, uSensor, uMaxCoC, uFar, uNearSide;
29
+ ${h?Rt:""}
30
+ ${Dt}
31
+ void main() {
32
+ vec4 s = ${i}(uSharp, ${s});
33
+ float camz = ${t};
34
+ float w = cocPx(camz, uFocus, uFocalLen, uAperture, uSensor, uMaxCoC) / uMaxCoC;
35
+ float isNear = camz < uFocus ? 1.0 : 0.0;
36
+ w *= (uNearSide > 0.5) ? isNear : 1.0 - isNear;
37
+ // Premultiplied colour, coverage in alpha: one blur carries both, so they cannot drift apart.
38
+ // Also premultiplied by the SOURCE alpha, so a transparent region contributes nothing rather than
39
+ // smearing black \u2014 the reason this pass wants a real alpha channel and a separate depth texture.
40
+ vec3 lin = ${c(l,"s.rgb")};
41
+ ${H(e,"vec4(lin * w * s.a, w * s.a)")}
42
+ }`,u=d=>`${r}
43
+ uniform sampler2D uTex;
44
+ uniform vec2 uDir;
45
+ void main() {
46
+ vec4 sum = vec4(0.0);
47
+ for (int i = -${d}; i <= ${d}; i++) sum += ${i}(uTex, ${o} + uDir * float(i));
48
+ ${H(e,`sum / float(${2*d+1})`)}
49
+ }`,E=`${r}
50
+ uniform sampler2D uSharp, uFarTex, uNearTex;
51
+ ${p}
52
+ uniform float uFocus, uFocalLen, uAperture, uSensor, uMaxCoC, uFar;
53
+ ${h?Rt:""}
54
+ ${Dt}
55
+ void main() {
56
+ vec4 s = ${i}(uSharp, ${s});
57
+ vec3 sharp = ${c(l,"s.rgb")};
58
+ float camz = ${t};
59
+ float w = cocPx(camz, uFocus, uFocalLen, uAperture, uSensor, uMaxCoC) / uMaxCoC;
60
+ float wFar = camz < uFocus ? 0.0 : w;
61
+
62
+ // Far behind: only the pixel's OWN far weight lets the far layer through, so a sharp subject in
63
+ // front of a blurred background takes none of its colour. That is what stops the halo.
64
+ vec4 F = ${i}(uFarTex, ${o});
65
+ vec3 farC = F.a > 1e-4 ? F.rgb / F.a : sharp;
66
+ vec3 outC = mix(sharp, farC, wFar);
67
+ float outA = mix(s.a, min(F.a, 1.0), wFar);
68
+
69
+ // Near in front: its COVERAGE spreads, so it composites over whatever is already there \u2014 the half a
70
+ // single sharp\u2192blurred lerp cannot express at all, because that only ever mixes a pixel with a
71
+ // blurred copy of itself.
72
+ vec4 N = ${i}(uNearTex, ${o});
73
+ float aN = min(N.a, 1.0);
74
+ vec3 nearC = N.a > 1e-4 ? N.rgb / N.a : sharp;
75
+ outC = mix(outC, nearC, aN);
76
+ outA = max(outA, aN);
77
+
78
+ ${H(e,`vec4(${c(a,"outC")}, outA)`)}
79
+ }`;return{split:f,hex:u,composite:E}}var St=M(K,{dialect:100}),Qt=N({dialect:100,depthExpr:St.depthExpr,depthUniforms:St.depthUniforms});var Gt=`#version 300 es
80
+ out vec2 vUv;
81
+ void main(){
82
+ vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));
83
+ vUv=p;gl_Position=vec4(p*2.-1.,0.,1.);
84
+ }`,_t=`#version 300 es
85
+ precision highp float;
86
+ in vec2 vUv;
87
+ out vec4 fragColor;
88
+ `,zt=_t+`uniform sampler2D uTex;
89
+ void main(){vec4 s=texture(uTex,vUv);fragColor=vec4(s.a>0.0001?s.rgb/s.a:vec3(0.),s.a);}`,I=class{constructor(e){this.gl=e,this.targets=[],this.programs=[],this.kernels=new Map,this.hdr=!!e.getExtension("EXT_color_buffer_float"),this.vao=e.createVertexArray();let t=M(W,{dialect:300}),i={dialect:300,depthUniforms:t.depthUniforms+`
90
+ uniform float uFocusBand;
91
+ float softFocusDepth(float depth, float focus) {
92
+ float delta=depth-focus;
93
+ return focus+delta*abs(delta)/(abs(delta)+uFocusBand);
94
+ }`,depthExpr:`softFocusDepth((${t.depthExpr}),uFocus)`,encoding:"srgb"},s=N({...i,header:`#version 300 es
95
+ precision highp float;
96
+ in vec2 vUv;
97
+ vec4 lensColor;
98
+ #define fragColor lensColor
99
+ #define main lensSplit
100
+ `}).split+`
101
+ #undef main
102
+ #undef fragColor
103
+ out vec4 fragColor;
104
+ uniform float uHighlights;
105
+ void main(){
106
+ lensSplit();
107
+ float compactHighlight=texture(uDepth,vUv).g;
108
+ fragColor=vec4(lensColor.rgb*(1.+uHighlights*compactHighlight),lensColor.a);
109
+ }`;this.split=this.program(s);let o=N({...i,header:`#version 300 es
110
+ precision highp float;
111
+ in vec2 vUv;
112
+ vec4 lensColor;
113
+ #define fragColor lensColor
114
+ #define main lensComposite
115
+ `}).composite+`
116
+ #undef main
117
+ #undef fragColor
118
+ out vec4 fragColor;
119
+ uniform bool uPhotoFrame;
120
+ void main(){lensComposite();fragColor=vec4(lensColor.rgb,uPhotoFrame?texture(uSharp,vUv).a:lensColor.a);}`;this.composite=this.program(o),this.normalize=this.program(zt),this.scene=this.target(!1,2,!0),this.sharp=this.target(!1),this.a=this.target(this.hdr),this.far=this.target(this.hdr),this.near=this.target(this.hdr),this.focusPlayer=P({pose:{position:[.7,0,0]},damping:$,rest:{position:1e-4}}),this.focus=.7,this.mouseDepth=null,this.lastPick=-1/0,this.frames=0}program(e){let t=this.gl,i=t.createProgram();for(let[o,r]of[[t.VERTEX_SHADER,Gt],[t.FRAGMENT_SHADER,e]]){let h=t.createShader(o);if(t.shaderSource(h,r),t.compileShader(h),!t.getShaderParameter(h,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(h));t.attachShader(i,h),t.deleteShader(h)}if(t.linkProgram(i),!t.getProgramParameter(i,t.LINK_STATUS))throw new Error(t.getProgramInfoLog(i));let s={p:i,uniforms:new Map};return this.programs.push(s),s}uniform(e,t){return e.uniforms.has(t)||e.uniforms.set(t,this.gl.getUniformLocation(e.p,t)),e.uniforms.get(t)}target(e,t=1,i=!1){let s=this.gl,o={fbo:s.createFramebuffer(),textures:[],hdr:e,w:0,h:0};for(let r=0;r<t;r++){let h=s.createTexture();s.bindTexture(s.TEXTURE_2D,h),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MAG_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE),o.textures.push(h)}return i&&(o.depth=s.createRenderbuffer()),this.targets.push(o),o}resize(e,t,i){if(e.w===t&&e.h===i)return;let s=this.gl;if(e.w=t,e.h=i,s.bindFramebuffer(s.FRAMEBUFFER,e.fbo),e.textures.forEach((o,r)=>{s.bindTexture(s.TEXTURE_2D,o),s.texImage2D(s.TEXTURE_2D,0,e.hdr?s.RGBA16F:s.RGBA8,t,i,0,s.RGBA,e.hdr?s.HALF_FLOAT:s.UNSIGNED_BYTE,null),s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+r,s.TEXTURE_2D,o,0)}),e.depth&&(s.bindRenderbuffer(s.RENDERBUFFER,e.depth),s.renderbufferStorage(s.RENDERBUFFER,s.DEPTH_COMPONENT24,t,i),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,e.depth)),s.drawBuffers(e.textures.map((o,r)=>s.COLOR_ATTACHMENT0+r)),s.checkFramebufferStatus(s.FRAMEBUFFER)!==s.FRAMEBUFFER_COMPLETE)throw new Error("Lens render target is unavailable.")}reset(e){this.focus=e,this.mouseDepth=null,this.lastPick=-1/0,this.focusPlayer=P({pose:{position:[e,0,0]},damping:$,rest:{position:1e-4}})}begin(e,t){let i=this.gl,s=Math.min(.5,768/Math.max(e,t));this.resize(this.scene,e,t),this.resize(this.sharp,e,t);for(let o of[this.a,this.far,this.near])this.resize(o,Math.max(1,Math.round(e*s)),Math.max(1,Math.round(t*s)));i.bindFramebuffer(i.FRAMEBUFFER,this.scene.fbo),i.viewport(0,0,e,t),i.clearColor(0,0,0,0),i.clear(i.COLOR_BUFFER_BIT|i.DEPTH_BUFFER_BIT)}samplePointer(e,t){if(!e){this.mouseDepth=null;return}if(t-this.lastPick<50)return;this.lastPick=t;let i=this.gl,s=new Uint8Array(4),o=Math.floor(e[0]*this.scene.w),r=Math.floor((1-e[1])*this.scene.h);if(o<0||o>=this.scene.w||r<0||r>=this.scene.h){this.mouseDepth=null;return}i.bindFramebuffer(i.READ_FRAMEBUFFER,this.scene.fbo),i.readBuffer(i.COLOR_ATTACHMENT1),i.readPixels(o,r,1,1,i.RGBA,i.UNSIGNED_BYTE,s),i.readBuffer(i.COLOR_ATTACHMENT0),this.mouseDepth=s[3]>127?Math.max(0,Math.min(1,s[0]/s[3])):null}texture(e,t,i,s){let o=this.gl;o.activeTexture(o.TEXTURE0+i),o.bindTexture(o.TEXTURE_2D,s),o.uniform1i(this.uniform(e,t),i)}paint(e,t){let i=this.gl;i.useProgram(e.p),i.bindFramebuffer(i.FRAMEBUFFER,t?.fbo||null),i.viewport(0,0,t?.w||this.scene.w,t?.h||this.scene.h),i.bindVertexArray(this.vao)}optics(e,t,i,s){let o=this.gl;for(let[r,h]of Object.entries({uNear:1,uFar:8,uFocus:t,uFocusBand:Z,uFocalLen:.05,uAperture:i,uSensor:Q*this.a.w/768,uMaxCoC:s}))o.uniform1f(this.uniform(e,r),h);this.texture(e,"uSharp",0,this.sharp.textures[0]),this.texture(e,"uDepth",1,this.scene.textures[1])}kernel(e){if(!this.kernels.has(e)){let t=nt(e),i=_t+`uniform sampler2D uTex;
121
+ uniform vec2 uRadius;
122
+ uniform float uLod;
123
+ vec4 tap(vec2 offset){
124
+ return textureLod(uTex,vUv-offset*uRadius,uLod);
125
+ }
126
+ void main(){vec4 sum=vec4(0.);
127
+ ${t.map(([s,o,r])=>`sum+=tap(vec2(${s.toFixed(7)},${o.toFixed(7)}))*${r.toFixed(9)};`).join(`
128
+ `)}
129
+ fragColor=sum;}`;this.kernels.set(e,this.program(i))}return this.kernels.get(e)}finish(e,{pointer:t,autoFocus:i,time:s,elapsed:o,frozen:r,reduced:h,photoFrame:p,bounds:l}){let a=this.gl;e.mode==="mouse"&&!r&&this.samplePointer(t,s);let c=et(e,this.mouseDepth,i);(!r||e.mode==="manual")&&(this.focusPlayer.goTo({position:[c,0,0]}),this.focus=h?c:this.focusPlayer.step(o).position[0]);let f=rt(this.focus),u=Math.max(.5,32*e.blur/100*this.a.w/768),E=e.blur/100*.05;a.disable(a.SCISSOR_TEST),a.disable(a.DEPTH_TEST),a.disable(a.BLEND),this.paint(this.normalize,this.sharp),this.texture(this.normalize,"uTex",0,this.scene.textures[0]),a.drawArrays(a.TRIANGLES,0,3);let d=this.kernel(e.shape);for(let[T,R]of[[0,this.far],[1,this.near]])this.paint(this.split,this.a),this.optics(this.split,f,E,u),a.uniform1f(this.uniform(this.split,"uHighlights"),this.hdr?e.highlights/100*48:0),a.uniform1f(this.uniform(this.split,"uNearSide"),T),a.drawArrays(a.TRIANGLES,0,3),this.paint(d,R),this.texture(d,"uTex",0,this.a.textures[0]),a.generateMipmap(a.TEXTURE_2D),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR_MIPMAP_LINEAR),a.uniform2f(this.uniform(d,"uRadius"),u/this.a.w,u/this.a.h),a.uniform1f(this.uniform(d,"uLod"),Math.max(0,Math.log2(Math.max(1,u/J)))),a.drawArrays(a.TRIANGLES,0,3);this.paint(this.composite,null),this.optics(this.composite,f,E,u),this.texture(this.composite,"uFarTex",2,this.far.textures[0]),this.texture(this.composite,"uNearTex",3,this.near.textures[0]),a.uniform1i(this.uniform(this.composite,"uPhotoFrame"),p),a.clearColor(0,0,0,0),a.clear(a.COLOR_BUFFER_BIT),p&&(a.enable(a.SCISSOR_TEST),a.scissor(l.x,l.y,l.width,l.height)),a.enable(a.BLEND),a.blendFuncSeparate(a.SRC_ALPHA,a.ONE_MINUS_SRC_ALPHA,a.ONE,a.ONE_MINUS_SRC_ALPHA),a.drawArrays(a.TRIANGLES,0,3),a.activeTexture(a.TEXTURE0),this.frames++}dispose(){let e=this.gl;for(let t of this.targets){for(let i of t.textures)e.deleteTexture(i);e.deleteFramebuffer(t.fbo),t.depth&&e.deleteRenderbuffer(t.depth)}for(let t of this.programs)e.deleteProgram(t.p);e.deleteVertexArray(this.vao)}};var k=.23,At=M({encoding:"unit",source:"depth",channel:"r",near:0,far:1,nearIs:"white"},{dialect:300,uv:"uv"}),yt=`
130
+ uniform float uFar;
131
+ ${At.depthUniforms}
132
+ uniform bool uInvert;
133
+ float nearDepth(vec2 uv) {
134
+ float d = 1.0 - (${At.depthExpr});
135
+ return uInvert ? 1.0-d : d;
136
+ }`,Xt=`#version 300 es
137
+ precision highp float;
138
+ precision highp int;
139
+ in vec2 aUv;
140
+ out vec2 vUv;
141
+ uniform vec2 uImageSize, uViewport, uMotion;
142
+ uniform float uStrength, uPointSize, uFocal, uShift;
143
+ uniform int uEffect;
144
+ uniform bool uParticles, uMap;
145
+ ${yt}
146
+ ${Et}
147
+ void main() {
148
+ vUv=aUv;
149
+ float d=nearDepth(aUv)+uShift; // layer separation: background back, subject forward
150
+ vec3 p=vec3((aUv.x-.5)*uImageSize.x,(.5-aUv.y)*uImageSize.y,0.);
151
+ if(uEffect!=0 && !uMap && (uEffect==1 || uEffect==3 || uParticles)) {
152
+ p.z=(d-.5)*uStrength;
153
+ p.xy*= (3.-p.z)/3.;
154
+ if(uEffect==1 || uEffect==3) {
155
+ float yaw=uMotion.x*${k}, pitch=-uMotion.y*${k};
156
+ p.xz=mat2(cos(yaw),-sin(yaw),sin(yaw),cos(yaw))*p.xz;
157
+ p.yz=mat2(cos(pitch),-sin(pitch),sin(pitch),cos(pitch))*p.yz;
158
+ if(uEffect==3) p.xy+=uMotion*vec2(.16,-.16)*(d-.5)*uStrength;
159
+ } else {
160
+ p.xy+=uMotion*(d-.5)*uStrength*.18;
161
+ }
162
+ }
163
+ vec3 cam=camSpace(p,vec3(0.,0.,3.),vec3(1.,0.,0.),vec3(0.,1.,0.),vec3(0.,0.,-1.));
164
+ vec4 clip=projectClip(cam,uFocal,1.,uViewport);
165
+ // Shared ARI projection gives screen coordinates; restore perspective W and
166
+ // a monotonic depth for opaque photo splats and indexed triangles.
167
+ gl_Position=vec4(clip.xy*cam.z, ((cam.z-1.)/4.*2.-1.)*cam.z, cam.z);
168
+ gl_PointSize=uPointSize*3./cam.z;
169
+ }`,$t=`#version 300 es
170
+ precision highp float;
171
+ precision highp int;
172
+ in vec2 vUv;
173
+ layout(location=0) out vec4 outColor;
174
+ layout(location=1) out vec4 outDepth;
175
+ uniform sampler2D uPhoto;
176
+ uniform sampler2D uMask;
177
+ uniform sampler2D uEdgeBackground;
178
+ uniform sampler2D uHighlights;
179
+ uniform bool uCleanEdges, uFlatDof;
180
+ uniform int uLayer;
181
+ uniform float uEdge;
182
+ uniform float uLensSubjectDepth;
183
+ uniform bool uMaskView;
184
+ uniform bool uMono, uParticles, uMap;
185
+ uniform int uEffect;
186
+ uniform vec2 uMotion;
187
+ uniform float uStrength;
188
+ ${yt}
189
+ void main() {
190
+ vec2 uv=vUv;
191
+ if(uEffect==2 && !uParticles && !uMap) {
192
+ // A bounded depth traversal; inference never runs in this render path.
193
+ vec2 travel=uMotion*vec2(.055,-.055)*uStrength;
194
+ float t=0.;
195
+ for(int i=0;i<32;i++){
196
+ uv=vUv+travel*(t-.5);
197
+ if(t>=1.-nearDepth(clamp(uv,0.,1.))) break;
198
+ t+=1./31.;
199
+ }
200
+ float low=max(0.,t-1./31.),high=t;
201
+ for(int j=0;j<5;j++){
202
+ float mid=(low+high)*.5;
203
+ vec2 probe=vUv+travel*(mid-.5);
204
+ if(mid>=1.-nearDepth(clamp(probe,0.,1.))) high=mid; else low=mid;
205
+ }
206
+ uv=vUv+travel*((low+high)*.5-.5);
207
+ }
208
+ if(any(lessThan(uv,vec2(0.))) || any(greaterThan(uv,vec2(1.)))) discard;
209
+ vec4 color=texture(uPhoto,uv);
210
+ float alpha=pow(texture(uMask,uv).r,exp2((uEdge-.5)*4.));
211
+ if(uMaskView) color=vec4(vec3(alpha),1.);
212
+ else if(uMap) color=vec4(vec3(nearDepth(uv)),1.);
213
+ else if(uMono) color.rgb=vec3(dot(color.rgb,vec3(.2126,.7152,.0722)));
214
+ if(uLayer==2 && !uMaskView) {
215
+ if(alpha<.02) discard;
216
+ if(uCleanEdges && alpha<.98) {
217
+ // Unmix some of the background baked into soft edge pixels. Limit the
218
+ // correction near transparent pixels, where unmixing is poorly conditioned.
219
+ vec3 bg=texture(uEdgeBackground,uv).rgb;
220
+ if(uMono) bg=vec3(dot(bg,vec3(.2126,.7152,.0722)));
221
+ vec3 clean=clamp((color.rgb-bg*(1.-alpha))/max(alpha,.15),0.,1.);
222
+ color.rgb=mix(color.rgb,clean,.65*smoothstep(.02,.2,alpha));
223
+ }
224
+ color.a=alpha;
225
+ }
226
+ if(uParticles && !uMap) {
227
+ float radius=length(gl_PointCoord-.5);
228
+ if(radius>.5) discard;
229
+ color.a*=1.-smoothstep(.35,.5,radius);
230
+ }
231
+ outColor=color;
232
+ // Treat a segmented subject as one focus region while retaining some internal
233
+ // depth. This avoids a sharp face surrounded by blurry hair/shoulder seams.
234
+ float focusDepth=uLayer==2?mix(nearDepth(uv),uLensSubjectDepth,.65):nearDepth(uv);
235
+ if(uFlatDof) focusDepth=mix(focusDepth,uLensSubjectDepth,.65*alpha);
236
+ // G carries only real compact highlights, moving with the same geometry/UVs.
237
+ // Removed foreground lights must not reappear in the repaired background.
238
+ float highlight=texture(uHighlights,uv).r*(uLayer==1?1.-alpha:1.);
239
+ outDepth=vec4(focusDepth,highlight,focusDepth,color.a);
240
+ }`,B=class{constructor(e,{autoStart:t=!0,fit:i="demo",eventTarget:s=e}={}){this.canvas=e,this.fit=i,this.running=!1,this.disposed=!1,this.contextLost=!1,this.listeners=[];let o=(c,f,u)=>{c.addEventListener(f,u),this.listeners.push(()=>c.removeEventListener(f,u))};if(this.gl=e.getContext("webgl2",{alpha:!0,antialias:!0}),!this.gl)throw new Error("This gallery needs WebGL 2. Please enable hardware acceleration.");let r=this.gl,h=(c,f)=>{let u=r.createShader(c);if(r.shaderSource(u,f),r.compileShader(u),!r.getShaderParameter(u,r.COMPILE_STATUS))throw new Error(r.getShaderInfoLog(u));return u},p=h(r.VERTEX_SHADER,Xt),l=h(r.FRAGMENT_SHADER,$t);if(this.program=r.createProgram(),r.attachShader(this.program,p),r.attachShader(this.program,l),r.linkProgram(this.program),!r.getProgramParameter(this.program,r.LINK_STATUS))throw new Error(r.getProgramInfoLog(this.program));r.deleteShader(p),r.deleteShader(l),this.uniforms=new Map,this.photo=r.createTexture(),this.depth=r.createTexture(),this.highlightMap=r.createTexture(),r.bindTexture(r.TEXTURE_2D,this.highlightMap),r.texImage2D(r.TEXTURE_2D,0,r.R8,1,1,0,r.RED,r.UNSIGNED_BYTE,new Uint8Array([0])),this.textureParams(r.LINEAR),this.mask=r.createTexture(),this.backgroundPhoto=r.createTexture(),this.fastPhoto=r.createTexture(),this.fastDepth=r.createTexture(),r.bindTexture(r.TEXTURE_2D,this.fastPhoto),r.texImage2D(r.TEXTURE_2D,0,r.RGBA,1,1,0,r.RGBA,r.UNSIGNED_BYTE,new Uint8Array([0,0,0,0])),this.textureParams(r.LINEAR),this.foregroundDepth=r.createTexture(),this.backgroundDepth=r.createTexture(),r.bindTexture(r.TEXTURE_2D,this.mask),r.texImage2D(r.TEXTURE_2D,0,r.R8,1,1,0,r.RED,r.UNSIGNED_BYTE,new Uint8Array([255])),this.textureParams(r.LINEAR),this.vao=r.createVertexArray(),this.vertices=r.createBuffer(),this.indices=r.createBuffer(),this.settings={effect:"spatial",compare:"animated",split:50,frozen:!1,adaptive:!0,cleanEdges:!0,material:"photo",color:"color",strength:35,size:2,density:260,motion:!0,orbitRange:5,invert:!1,map:!1,subjectView:"scene",edge:50},this.settings.orbitPeriod=30,this.settings.lens=tt(),this.lens=null,this.pointerCanvas=null,this.motionTime=0,this.orbitPhase=0,this.motionProfile=U(null,null),this.pointerActive=!1,this.motionPlayer=P({pose:{position:[0,0,this.settings.strength]},damping:.045,rest:{position:1e-5}}),this.renderStrength=this.settings.strength,this.tilt=[0,0],this.layerDepthCache=new Map,this.pointer=[0,0],this.smoothed=[0,0],this.hasImage=!1,this.hasDepth=!1,this.floatLinear=!!r.getExtension("OES_texture_float_linear"),this.setDepth({width:1,height:1,values:new Float32Array([.5])}),this.hasDepth=!1,this.rebuild(260),o(s,"pointermove",c=>{this.pointerActive=!0;let f=e.getBoundingClientRect();this.pointerCanvas=[(c.clientX-f.left)/f.width,(c.clientY-f.top)/f.height];let u=this.imageRect(f.width,f.height);this.pointer=[(c.clientX-f.left-u.x)/Math.max(1,u.width)*2-1,(c.clientY-f.top-u.y)/Math.max(1,u.height)*2-1].map(E=>Math.max(-1,Math.min(1,E)))});let a=()=>{this.pointer=[0,0],this.pointerActive=!1,this.pointerCanvas=null};o(s,"pointerleave",a),o(s,"pointercancel",a),o(s,"pointerup",c=>{c.pointerType==="touch"&&a()}),o(e,"webglcontextlost",c=>{c.preventDefault(),this.contextLost=!0,this.pause(),this.onContextLost?.()}),o(e,"webglcontextrestored",()=>this.onContextRestored?.()),this.draw=this.draw.bind(this),t&&this.resume()}imageRect(e,t){if(this.fit==="demo")return ut(e,t,this.aspect||1);let i=this.aspect||1,s=this.fit==="cover"?Math.max(t,e/i):Math.min(t,e/i);return{x:(e-s*i)/2,y:(t-s)/2,width:s*i,height:s}}pause(){this.running=!1,cancelAnimationFrame(this.frame),this.frame=null,this.lastTime=null}resume(){this.running||this.disposed||this.contextLost||(this.running=!0,this.frame=requestAnimationFrame(this.draw))}dispose(){if(this.disposed)return;this.disposed=!0,this.pause(),this.listeners.splice(0).forEach(t=>t()),this.lens?.dispose(),this.lens=null;let e=this.gl;for(let t of["photo","depth","highlightMap","mask","backgroundPhoto","fastPhoto","fastDepth","foregroundDepth","backgroundDepth","foregroundPhoto"])this[t]&&e.deleteTexture(this[t]);e.deleteBuffer(this.vertices),e.deleteBuffer(this.indices),e.deleteVertexArray(this.vao),e.deleteProgram(this.program),this.layerDepthCache.clear(),this.subject=null,this.depthResult=null,this.fill=null,this.lensAnalysis=null,e.getExtension("WEBGL_lose_context")?.loseContext()}uniform(e){return this.uniforms.has(e)||this.uniforms.set(e,this.gl.getUniformLocation(this.program,e)),this.uniforms.get(e)}textureParams(e){let t=this.gl;t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE)}setImage(e,{preserveMotion:t=!1}={}){let i=this.gl,s=document.createElement("canvas"),o=Math.min(1,512/Math.max(e.width,e.height));s.width=Math.max(1,Math.round(e.width*o)),s.height=Math.max(1,Math.round(e.height*o));let r=s.getContext("2d",{willReadFrequently:!0});r.drawImage(e,0,0,s.width,s.height);let h=r.getImageData(0,0,s.width,s.height).data;this.opaqueImage=h.every((p,l)=>l%4!==3||p===255),this.lensAnalysis=it(h,s.width,s.height),i.pixelStorei(i.UNPACK_ALIGNMENT,1),i.bindTexture(i.TEXTURE_2D,this.highlightMap),i.texImage2D(i.TEXTURE_2D,0,i.R8,this.lensAnalysis.width,this.lensAnalysis.height,0,i.RED,i.UNSIGNED_BYTE,this.lensAnalysis.mask),this.aspect=e.width/e.height,this.hasImage=!0,this.hasDepth=!1,this.hasSubject=!1,this.hasMask=!1,this.hasRefinedForeground=!1,this.subject=null,this.depthResult=null,this.fill=null,this.layerDepthCache.clear(),this.motionProfile=U(null,null),this.interior=null,this.lensAutoKey=null,this.lensImageReset=!0,this.pointerCanvas=null,t||(this.motionTime=0,this.orbitPhase=0,this.smoothed=[0,0],this.pointer=[0,0],this.pointerActive=!1,this.lastTime=null,this.motionPlayer=P({pose:{position:[0,0,this.settings.strength]},damping:.045,rest:{position:1e-5}}),this.renderStrength=this.settings.strength),i.bindTexture(i.TEXTURE_2D,this.photo),i.pixelStorei(i.UNPACK_FLIP_Y_WEBGL,!1),i.texImage2D(i.TEXTURE_2D,0,i.RGBA,i.RGBA,i.UNSIGNED_BYTE,e),this.textureParams(i.LINEAR),this.rebuild(this.settings.density)}setDepth(e){this.depthResult=e,this.uploadDepth(this.depth,e.width,e.height,e.values),this.hasDepth=!0,this.motionProfile=U(e,this.subject),this.hasSubject&&this.updateLayerDepth()}uploadDepth(e,t,i,s){let o=this.gl;o.bindTexture(o.TEXTURE_2D,e),o.texImage2D(o.TEXTURE_2D,0,o.R32F,t,i,0,o.RED,o.FLOAT,s),this.textureParams(this.floatLinear?o.LINEAR:o.NEAREST)}setSubject(e){let t=this.gl;if(e.foreground&&e.foreground.length!==e.width*e.height*4)throw Error("Foreground color dimensions do not match.");this.hasRefinedForeground=!!e.foreground,e.foreground&&(this.foregroundPhoto||(this.foregroundPhoto=t.createTexture()),t.bindTexture(t.TEXTURE_2D,this.foregroundPhoto),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,e.width,e.height,0,t.RGBA,t.UNSIGNED_BYTE,e.foreground),this.textureParams(t.LINEAR)),this.subject=e,this.hasMask=!0,this.hasSubject=e.usable,this.fill=null,this.interior=e.usable?ct(e):null,this.motionProfile=U(this.depthResult,e),t.pixelStorei(t.UNPACK_ALIGNMENT,1),t.bindTexture(t.TEXTURE_2D,this.mask),t.texImage2D(t.TEXTURE_2D,0,t.R8,e.width,e.height,0,t.RED,t.UNSIGNED_BYTE,e.mask),this.textureParams(t.LINEAR),e.usable&&(t.bindTexture(t.TEXTURE_2D,this.fastPhoto),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,e.width,e.height,0,t.RGBA,t.UNSIGNED_BYTE,e.background),this.textureParams(t.LINEAR),t.bindTexture(t.TEXTURE_2D,this.backgroundPhoto),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,e.width,e.height,0,t.RGBA,t.UNSIGNED_BYTE,e.background),this.textureParams(t.LINEAR),this.hasDepth&&this.updateLayerDepth())}setFill(e){if(this.fill=e,!this.hasSubject)return;let t=this.gl,i=e?.background||this.subject.background;t.bindTexture(t.TEXTURE_2D,this.backgroundPhoto),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.subject.width,this.subject.height,0,t.RGBA,t.UNSIGNED_BYTE,i),this.textureParams(t.LINEAR),this.hasDepth&&this.updateLayerDepth()}updateLayerDepth(){let e={...this.subject,foregroundDonors:this.interior||this.subject.foregroundDonors,backgroundDonors:this.fill?.backgroundDonors||this.subject.backgroundDonors},t=`${this.depthResult.key}|${this.subject.key}|${this.fill?.key||"fast"}`,i=this.layerDepthCache.get(t);i||(i=X(this.depthResult,e),this.layerDepthCache.size>=4&&this.layerDepthCache.delete(this.layerDepthCache.keys().next().value),this.layerDepthCache.set(t,i)),this.uploadDepth(this.foregroundDepth,i.width,i.height,i.foreground),this.uploadDepth(this.backgroundDepth,i.width,i.height,i.background);let s=`${this.depthResult.key}|${this.subject.key}|fast`,o=this.layerDepthCache.get(s);o||(o=X(this.depthResult,{...e,backgroundDonors:this.subject.backgroundDonors}),this.layerDepthCache.set(s,o)),this.uploadDepth(this.fastDepth,o.width,o.height,o.background)}suggestedRange(){return pt(this.motionProfile,this.settings.strength,!!this.fill&&this.settings.compare!=="split",this.settings.separation)}framing(e=Math.max(this.settings.strength,this.renderStrength||0)){let t=this.settings;return dt(this.motionProfile,{...t,strength:e,aspect:this.aspect,filled:!!this.fill&&t.compare!=="split"})}effectiveRange(){let e=this.settings;return e.effect==="dof"?0:e.adaptive&&["spatial","parallax"].includes(e.effect)?this.framing().range:e.effect==="spatial"?Math.min(e.orbitRange,8):e.orbitRange}rebuild(e){let t=this.gl,i=this.aspect||1,s=gt({widthSegments:Math.max(2,Math.round(e*Math.min(i,1))),heightSegments:Math.max(2,Math.round(e/Math.max(i,1)))});this.count=s.vertices.length,this.indexCount=s.indices.length,t.bindVertexArray(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.vertices),t.bufferData(t.ARRAY_BUFFER,Float32Array.from(s.vertices.flatMap(r=>[r.u,r.v])),t.STATIC_DRAW);let o=t.getAttribLocation(this.program,"aUv");t.enableVertexAttribArray(o),t.vertexAttribPointer(o,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indices),t.bufferData(t.ELEMENT_ARRAY_BUFFER,new Uint32Array(s.indices),t.STATIC_DRAW)}draw(e){if(this.disposed||this.contextLost)return;if(this.running&&(this.frame=requestAnimationFrame(this.draw)),document.hidden){this.lastTime=e;return}let t=this.gl,i=this.settings,s=Math.min(devicePixelRatio,2),o=Math.round(this.canvas.clientWidth*s),r=Math.round(this.canvas.clientHeight*s);if(!o||!r||((this.canvas.width!==o||this.canvas.height!==r)&&(this.canvas.width=o,this.canvas.height=r),t.bindFramebuffer(t.FRAMEBUFFER,null),t.disable(t.SCISSOR_TEST),t.viewport(0,0,o,r),t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT|t.DEPTH_BUFFER_BIT),!this.hasImage))return;let h=matchMedia("(prefers-reduced-motion: reduce)").matches,p=i.effect==="spatial",l=i.effect==="dof",a=Math.min(64,Math.max(0,e-(this.lastTime||e)));this.lastTime=e,!i.frozen&&!h&&!l&&(this.motionTime+=a,this.orbitPhase+=a*Math.PI*2/(Math.max(8,i.orbitPeriod)*1e3));let c=this.effectiveRange()*Math.PI/180/k;if(i.frozen){this.lastStrengthSetting!==i.strength&&(this.renderStrength=i.strength);let m=[...this.smoothed,this.renderStrength];m.some((x,b)=>x!==this.motionPlayer.pose.position[b])&&(this.motionPlayer=P({pose:{position:m},damping:.045,rest:{position:1e-5}}))}else{let m=ft({phase:this.orbitPhase,time:this.motionTime,scale:c,pointer:this.pointer,tilt:this.tilt,interacting:this.pointerActive,motion:i.motion,reduced:h}),x=this.pointerActive||Math.hypot(...this.tilt)>.04;this.motionPlayer.goTo({position:[...m,i.strength]},{damping:x?.16:.045});let b=this.motionPlayer.step(a).position;this.smoothed=b.slice(0,2),this.renderStrength=b[2],h&&(this.smoothed=[0,0],(b[0]!==0||b[1]!==0)&&(this.motionPlayer=P({pose:{position:[0,0,this.renderStrength]},damping:.045,rest:{position:1e-5}})))}if(this.lastStrengthSetting=i.strength,i.adaptive&&["spatial","parallax"].includes(i.effect)){let m=this.effectiveRange()*Math.PI/180/k;this.smoothed=this.smoothed.map(x=>Math.max(-m,Math.min(m,x)))}t.useProgram(this.program),t.bindVertexArray(this.vao);let f=i.compare==="split"&&!!this.fill&&this.hasSubject,u=i.compare!=="animated"&&!f,E=i.compare==="original"||i.effect==="flat"&&!u,d=(i.compare==="mask"||!u&&!l&&i.subjectView==="mask")&&this.hasMask,T=(i.compare==="depth"||!u&&i.map)&&this.hasDepth,R=i.compare==="background"?"background":i.subjectView,G=(!l||u||f)&&this.hasSubject&&this.hasDepth&&R!=="off"&&!d&&!T&&!E,g=i.lens.enabled&&i.lens.blur>0&&this.hasDepth&&!u&&!E&&!d&&!T&&!f;if(g){try{this.lens||(this.lens=new I(t),this.onLensReady?.());let m=[this.depthResult,this.subject,i.invert];(!this.lensAutoKey||m.some((x,b)=>x!==this.lensAutoKey[b]))&&(this.lensAutoFocus=ot(this.depthResult,this.subject,i.invert),this.lensAutoKey=m),this.lensImageReset&&(this.lens.reset(this.lensAutoFocus),this.lensImageReset=!1),this.lens.begin(o,r)}catch(m){this.lens?.dispose(),this.lens=null,i.lens.enabled=!1,g=!1,t.bindFramebuffer(t.FRAMEBUFFER,null),t.viewport(0,0,o,r),this.onLensError?.(m)}t.useProgram(this.program),t.bindVertexArray(this.vao)}t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,this.mask),t.uniform1i(this.uniform("uMask"),2),t.activeTexture(t.TEXTURE3),t.bindTexture(t.TEXTURE_2D,this.fastPhoto),t.uniform1i(this.uniform("uEdgeBackground"),3),t.uniform1i(this.uniform("uCleanEdges"),i.cleanEdges&&!this.hasRefinedForeground),t.uniform1f(this.uniform("uEdge"),i.edge/100),t.uniform1f(this.uniform("uLensSubjectDepth"),this.lensAutoFocus??.7),t.uniform1i(this.uniform("uFlatDof"),l&&this.hasSubject&&!u),t.activeTexture(t.TEXTURE4),t.bindTexture(t.TEXTURE_2D,this.highlightMap),t.uniform1i(this.uniform("uHighlights"),4),t.uniform1i(this.uniform("uMaskView"),d),t.uniform1i(this.uniform("uPhoto"),0),t.uniform1i(this.uniform("uDepth"),1),t.uniform1f(this.uniform("uNear"),0),t.uniform1f(this.uniform("uFar"),1),t.uniform1i(this.uniform("uInvert"),i.invert),t.uniform1i(this.uniform("uMono"),!u&&!E&&i.color==="mono"),t.uniform1i(this.uniform("uMap"),T);let v=!l&&i.material==="particles"&&!u&&!E&&!d&&!T;t.uniform1i(this.uniform("uParticles"),v);let D=this.hasDepth&&!u&&!d?{flat:0,dof:0,relief:1,parallax:2,spatial:3}[i.effect]:0;t.uniform1i(this.uniform("uEffect"),D),t.uniform1f(this.uniform("uStrength"),this.renderStrength/100*1.6),t.uniform1f(this.uniform("uPointSize"),i.size*s),t.uniform2f(this.uniform("uViewport"),o,r);let w=r*.95,S=this.imageRect(o,r).height,_=(p||i.adaptive&&i.effect==="parallax")&&!u&&!d&&!T;this.frameZoom=_?i.adaptive?this.framing().zoom:1.12:1;let A=S*3/w*this.frameZoom,y=_?{x:Math.round((o-S*this.aspect)/2),y:Math.round((r-S)/2),width:Math.round(S*this.aspect),height:Math.round(S)}:{x:0,y:0,width:o,height:r};t.uniform2f(this.uniform("uImageSize"),A*this.aspect,A),t.uniform2fv(this.uniform("uMotion"),l?[0,0]:this.smoothed),t.uniform1f(this.uniform("uFocal"),w),t.enable(t.DEPTH_TEST),t.enable(t.BLEND),t.blendFuncSeparate(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA,t.ONE,t.ONE_MINUS_SRC_ALPHA);let q=(i.separation??0)/100*.7,L=(m,x,b)=>{t.uniform1i(this.uniform("uLayer"),m),t.uniform1f(this.uniform("uShift"),m===1?-q*.7:m===2?q*.3:0),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,x),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,b),v?t.drawArrays(t.POINTS,0,this.count):t.drawElements(t.TRIANGLES,this.indexCount,t.UNSIGNED_INT,0)},z=(m,x,b=0,wt=o)=>{let Y=Math.max(y.x,b),Ut=Math.min(y.x+y.width,wt);t.enable(t.SCISSOR_TEST),t.scissor(Y,y.y,Math.max(0,Ut-Y),y.height),t.clear(t.DEPTH_BUFFER_BIT),G?(i.adaptive&&_&&R==="scene"&&!v&&this.opaqueImage&&(t.uniform1i(this.uniform("uEffect"),0),L(1,m,x),t.clear(t.DEPTH_BUFFER_BIT),t.uniform1i(this.uniform("uEffect"),D)),R!=="foreground"&&L(1,m,x),t.clear(t.DEPTH_BUFFER_BIT),R!=="background"&&L(2,this.hasRefinedForeground?this.foregroundPhoto:this.photo,this.foregroundDepth)):L(0,this.photo,this.depth)};if(f){let m=Math.round(o*i.split/100);z(this.fastPhoto,this.fastDepth,0,m),z(this.backgroundPhoto,this.backgroundDepth,m,o)}else z(this.backgroundPhoto,this.backgroundDepth);g&&this.lens.finish(i.lens,{pointer:this.pointerCanvas,autoFocus:this.lensAutoFocus,time:e,elapsed:a,frozen:i.frozen,reduced:h,photoFrame:!v,bounds:y})}};function Ht(n){let e=new Set,t=n.DeviceOrientationEvent,i=n.isSecureContext&&!!t&&n.navigator.maxTouchPoints>0,o=!(typeof t?.requestPermission=="function"),r=i?"prompt":"unsupported",h=null,p=!1,l=null,a=[0,0],c,f=g=>{r=g;for(let v of e)v.onState(r)},u=()=>{l=null,a=[0,0];for(let{renderer:g}of e)g.tilt=a},E=()=>Number(n.screen?.orientation?.angle??n.orientation??0),d=E(),T=g=>{if(!Number.isFinite(g.beta)||!Number.isFinite(g.gamma))return;let v=E();v!==d&&(d=v,u()),l||(l=[g.gamma,g.beta]);let D=(A,y)=>(A-y+540)%360-180,w=D(g.gamma,l[0])/15,S=D(g.beta,l[1])/15,_=v*Math.PI/180;a=[w*Math.cos(_)+S*Math.sin(_),S*Math.cos(_)-w*Math.sin(_)].map(A=>Math.max(-1,Math.min(1,A)));for(let{renderer:A}of e)A.tilt=a;n.clearTimeout(c),r!=="active"&&f("active")},R=()=>{!e.size||!i||(u(),d=E(),p||(n.addEventListener("deviceorientation",T),p=!0),f("waiting"),n.clearTimeout(c),c=n.setTimeout(()=>f("unavailable"),3e3))};function G(){if(!i||!e.size)return Promise.resolve();if(h)return h;if(o)return R(),Promise.resolve();f("requesting");let g;try{g=t.requestPermission()}catch{return f("error"),Promise.resolve()}return h=Promise.resolve(g).then(v=>{v==="granted"?(o=!0,R()):f("denied")},()=>f("error")).finally(()=>{h=null}),h}return{enable:G,subscribe(g,v=()=>{}){let D={renderer:g,onState:v};return e.add(D),g.tilt=a,i&&o&&!p?R():v(r),()=>{e.delete(D),g.tilt=[0,0],e.size||(n.removeEventListener("deviceorientation",T),p=!1,n.clearTimeout(c),u(),i&&!h&&(r="prompt"))}}}}var jt;function Pt(n,e,t=jt??(jt=Ht(window))){let i=n.ownerDocument,s=i.createElement("div");s.className="spatial-photo-tilt",Object.assign(s.style,{position:"absolute",left:"12px",bottom:"12px",zIndex:"1",maxWidth:"calc(100% - 24px)",padding:"6px 10px",borderRadius:"8px",background:"rgba(20,22,28,.85)",color:"#fff",font:"13px/1.4 system-ui,sans-serif"});let o=i.createElement("span"),r=i.createElement("button");o.setAttribute("role","status"),r.type="button",Object.assign(r.style,{font:"inherit",color:"inherit",background:"transparent",border:"1px solid currentColor",borderRadius:"5px",padding:"8px 12px",minHeight:"44px",cursor:"pointer"}),r.onclick=l=>{l.preventDefault(),l.stopPropagation(),t.enable()},s.append(o,r);let h={requesting:"Allow motion to enable tilt.",waiting:"Tilt your phone.",denied:"Motion access is off. ",error:"Motion access could not start. ",unavailable:"No phone motion detected. "},p=t.subscribe(e,l=>{if(l==="active"||l==="unsupported"){s.remove();return}o.textContent=h[l]??"",r.textContent=l==="prompt"?"Enable tilt":"Try tilt again",r.hidden=l==="requesting"||l==="waiting",s.parentNode||n.append(s)});return()=>{p(),s.remove()}}var V=class{constructor(e=3){this.limit=e,this.active=new Set,this.waiting=new Set}setLimit(e){this.limit=Math.max(1,Math.floor(e));for(let t of[...this.active].sort((i,s)=>s.visibility-i.visibility).slice(this.limit))t.unmount(),t.refresh();for(let t of[...this.waiting])t.refresh()}acquire(e){if(this.active.has(e))return!0;if(this.active.size>=this.limit){let t=[...this.active].sort((i,s)=>i.visibility-s.visibility)[0];return e.visibility>(t?.visibility||0)+.05?(t.unmount(),this.waiting.delete(e),this.active.add(e),queueMicrotask(()=>t.refresh()),!0):(this.waiting.add(e),!1)}return this.waiting.delete(e),this.active.add(e),!0}release(e){if(this.waiting.delete(e),!this.active.delete(e))return;let t=this.waiting.values().next().value;t&&(this.waiting.delete(t),queueMicrotask(()=>t.refresh()))}},j=new Map;function Mt(n){let e=j.get(n.key);e||(e={refs:0,promise:n.blob?ht(n.blob,{required:!0}):lt(n.url).then(i=>i.asset)},j.set(n.key,e)),e.refs++;let t=!1;return{promise:e.promise,release:()=>{t||(t=!0,--e.refs===0&&j.delete(n.key))}}}var O;function Vt(){if(!O){let n=matchMedia("(max-width: 600px)");O=new V(n.matches?1:3),n.addEventListener("change",()=>O.setLimit(n.matches?1:3))}return O}var Ft=class{constructor({element:e,fit:t="cover",pool:i=Vt(),onError:s=()=>{},onReady:o=()=>{},onRenderingChange:r=()=>{}}){this.element=e,this.fit=t,this.pool=i,this.onError=s,this.onReady=o,this.onRenderingChange=r,this.visible=!1,this.visibility=0,this.disposed=!1,this.generation=0,this.state=null,this.reduced=matchMedia("(prefers-reduced-motion: reduce)"),this.changed=()=>this.refresh(),this.reduced.addEventListener("change",this.changed),document.addEventListener("visibilitychange",this.changed),this.observer=new IntersectionObserver(h=>{this.visible=h[0].isIntersecting,this.visibility=h[0].intersectionRatio,this.refresh()},{threshold:[0,.1,.25,.5,.75,1]}),this.observer.observe(e)}update(e){let t=this.state?.asset,i=t?.key!==e.asset?.key;if(this.state=e,i){if(this.renderer&&e.asset?.upgradeOf&&e.asset.upgradeOf===t?.key){this.upgrade();return}this.unmount()}this.renderer&&this.applySettings(),this.refresh()}applySettings(){let e=at({...this.asset.settings,...this.state.settings,lens:{...this.asset.settings?.lens,...this.state.settings?.lens}}),t=this.renderer.settings.density;e.lens.look!=="custom"&&Object.assign(e.lens,st(this.renderer.motionProfile,this.renderer.lensAnalysis,this.renderer.subject,e.lens.look)),Object.assign(this.renderer.settings,e),t!==e.density&&this.renderer.rebuild(e.density)}async upgrade(){let e=++this.generation,t=Mt(this.state.asset);try{let i=await t.promise;if(this.disposed||e!==this.generation||!this.renderer){t.release();return}let s=this.handle;this.handle=t,this.asset=i,this.renderer.setImage(i.photo,{preserveMotion:!0}),this.renderer.setDepth(i.depth),i.subject&&this.renderer.setSubject(i.subject),this.renderer.setFill(i.fill),this.applySettings(),s?.release()}catch{t.release()}}async refresh(){if(this.disposed)return;if(!(this.visible&&!document.hidden&&!this.reduced.matches&&this.state?.enabled&&this.state?.status==="ready")){this.unmount();return}if(this.renderer||this.loading||this.failedKey===this.state.asset.key||!this.pool.acquire(this))return;let t=++this.generation;this.loading=!0;let i=Mt(this.state.asset);this.handle=i;try{let s=await i.promise;if(this.disposed||t!==this.generation)return;this.asset=s;let o=document.createElement("canvas");this.canvas=o,o.setAttribute("aria-hidden","true"),o.className="spatial-photo-canvas",Object.assign(o.style,{position:"absolute",inset:"0",width:"100%",height:"100%",pointerEvents:"none",display:"block"}),this.element.append(o);let r=new B(o,{autoStart:!1,fit:this.fit,eventTarget:this.element});this.renderer=r,r.onContextLost=()=>{o.style.visibility="hidden",this.onRenderingChange(!1)},r.onContextRestored=()=>{this.unmount(),this.refresh()},this.applySettings(),r.setImage(s.photo),r.setDepth(s.depth),s.subject&&r.setSubject(s.subject),s.fill&&r.setFill(s.fill),this.applySettings(),this.releaseTilt=Pt(this.element,r),r.draw(performance.now()),r.resume(),this.onRenderingChange(!0),this.onReady()}catch(s){t===this.generation&&(this.failedKey=this.state.asset.key,this.unmount(),this.onError(s))}finally{t===this.generation&&(this.loading=!1)}}unmount(){this.generation++,this.loading=!1,this.onRenderingChange(!1),this.releaseTilt?.(),this.releaseTilt=null,this.renderer?.dispose(),this.renderer=null,this.canvas?.remove(),this.canvas=null,this.asset=null,this.handle?.release(),this.handle=null,this.pool.release(this)}retry(){this.failedKey=null,this.refresh()}dispose(){this.disposed||(this.disposed=!0,this.unmount(),this.observer.disconnect(),this.reduced.removeEventListener("change",this.changed),document.removeEventListener("visibilitychange",this.changed))}};export{Ft as SpatialPhotoHost,V as SpatialRenderPool};
@@ -0,0 +1,17 @@
1
+ /** Types of the fixed Spatial Image viewer bundle. */
2
+ import type {FC} from 'react';
3
+ /**
4
+ * `workerUrl(name)` = the URL of preparation worker `name` (depth-gallery-worker, -subject-worker, -fill-worker, -quality-worker).
5
+ * `prepareInEditor` (default true): on an editor host, prepare depth in the browser when the photo has no current
6
+ * `spatialAsset`, showing progress on the image. Hosts whose assets are always made elsewhere pass false.
7
+ */
8
+ export function createSpatialImage(options: {workerUrl(name: string): URL | string; prepareInEditor?: boolean}): FC<Record<string, unknown>>;
9
+ /** The saved 3D version the component loads instead of preparing (its `spatialAsset` data item). */
10
+ export interface SpatialAsset {sourceUri: string; sourceVersion: string; url: string; key: string}
11
+ /** Editor only: prepares a photo, saves it to the site's media, returns its spatialAsset. `authorization()` = the app instance. */
12
+ export function createSpatialImageSaver(options: {workerUrl(name: string): URL | string; authorization(): Promise<string>}):
13
+ (photo: {uri: string; name?: string}, progress: (message: string) => void) => Promise<SpatialAsset>;
14
+ /** The editor panel that saves the 3D version. `editor` = @wix/editor reactElements. */
15
+ export function createSpatialSavePanel(react: unknown, options: {editor: unknown; prepare: ReturnType<typeof createSpatialImageSaver>}): FC;
16
+ /** The Wix media id inside any of the editor's image forms (bare id, wix:image://, wix://, static URL). */
17
+ export function mediaID(image: {uri?: string; url?: string} | null | undefined): string | null;
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ import{a as x,b as w,c as S,d as k,e as P,f as b,g as I,h as U}from"./spatial-IC5HXWEQ.js";import"./spatial-NBE3T2VB.js";import"./spatial-GZVXIGKN.js";function C({editor:t,prepare:s,onStatus:i=()=>{},matchAsset:u=P}){let o=0,n=!1,y=Promise.resolve(),p=e=>{let a=y.catch(()=>{}).then(()=>{if(n)throw Error("Panel closed.");return e()});return y=a,a},d=async e=>await t.getResolvedData({dataItemKey:e})??(await t.getData?.())?.[e],l=()=>d("image");async function h(){let[e,a]=await Promise.all([l(),t.getStyles()]),g=k(r=>{let f=a.cssCustomProperties?.[r];return f?.value??f});return{image:e,...g}}async function m(e,a){let g=x.find(f=>f.key===e);if(!g)throw Error("Unknown spatial control.");if(e==="enabled")return c(a==="on");let r={[w(e)]:String(S(g,a))};["lens-blur","lens-highlights"].includes(e)&&(r[w("lens-look")]="custom"),await p(()=>t.setStyles({cssCustomProperties:r}))}async function c(e){let a=++o;if(await p(()=>t.setStyles({cssCustomProperties:{[w("enabled")]:e?"on":"off"}})),!e){i("Original image");return}return v(a)}async function v(e=++o){let a=await l();if(!a?.uri){i("Choose an image first.");return}let g=await d("spatialAsset");if(!(n||e!==o)){if(u(a,g)){i("Depth ready");return}i("Preparing depth\u2026");try{let r=await s(a,f=>{!n&&e===o&&i(f)});if(n||e!==o)return;await p(async()=>{if(n||e!==o)return;let f=await l();if(n||e!==o||f?.uri!==a.uri)return;if(r.sourceUri!==a.uri||!r.sourceVersion||!r.key||!r.url)throw Error("Prepared asset does not match the native image.");if(!u(a,r))throw Error("The prepared asset needs an original, untransformed media URL and a matching crop.");await t.setData({spatialAsset:{sourceUri:r.sourceUri,sourceVersion:r.sourceVersion,url:r.url,key:r.key}});let D=await d("spatialAsset");if(D?.key!==r.key||D?.sourceUri!==a.uri||D?.url!==r.url)throw Error("Wix did not retain the prepared asset. The original image is still available.");!n&&e===o&&i("Depth ready")})}catch(r){if(n||e!==o)return;throw i(r.message),r}}}return{refresh:h,setControl:m,setDepth:c,saveDepth:()=>v(),async chooseImage(){return o++,await t.selectMedia({dataItemKey:"image"}),(await h()).enabled&&await c(!0),h()},dispose(){n=!0,o++}}}var E=t=>{let s=b(t);return s?{...t,uri:s}:t};function V(t){return{...t,getResolvedData:async s=>{let i=await t.getResolvedData(s);return s?.dataItemKey==="image"&&i?E(i):i},getData:async()=>{let s=await t.getData?.();return s?.image?{...s,image:E(s.image)}:s}}}var A="Depth ready";function W(t,{editor:s,prepare:i}){let u=t.createElement;return function(){let[n,y]=t.useState("Checking this photo\u2026"),[p,d]=t.useState(!0),l=t.useRef(null),h=t.useCallback(async()=>{let c=l.current;if(c){d(!0);try{await c.saveDepth()}catch{}finally{l.current===c&&d(!1)}}},[]);t.useEffect(()=>{let c=!0;return l.current=C({editor:V(s),prepare:i,onStatus:v=>{c&&y(v)}}),h(),()=>{c=!1,l.current.dispose(),l.current=null}},[]);let m=n===A;return u("section",{style:{font:"14px/1.5 system-ui,sans-serif",padding:24,color:"#20303c"}},u("h2",{style:{font:"600 18px/1.3 system-ui,sans-serif",margin:"0 0 8px"}},"3D for visitors"),u("p",{style:{margin:"0 0 16px",color:"#51606e"}},"The 3D version of this photo is made once, here in the editor, and saved to your site. Visitors load it instantly; nothing is computed in their browser."),u("p",{role:"status","aria-live":"polite",style:{margin:"0 0 16px",padding:"10px 12px",borderRadius:6,background:m?"#e6f4ea":"#f2f4f7",color:m?"#1e6b34":"#20303c"}},m?"Saved. Visitors see this photo in 3D.":n),u("button",{type:"button",disabled:p,onClick:h,style:{padding:"10px 14px",borderRadius:18,border:0,background:p?"#a9b8c9":"#116dff",color:"#fff",font:"600 14px system-ui,sans-serif",cursor:p?"default":"pointer"}},p?"Working\u2026":m?"Check again":"Save 3D for visitors"))}}export{U as createSpatialImage,I as createSpatialImageSaver,W as createSpatialSavePanel,b as mediaID};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ function z(c,r,s,a){if(c.length!==r*s)throw new Error("Mask dimensions do not match.");let e=new Int32Array(c.length).fill(-1),n=new Int32Array(c.length),g=0,m=0;for(let t=0;t<c.length;t++)(a?c[t]>=240:c[t]<=4)&&(e[t]=t,n[m++]=t);if(!m)return null;for(;g<m;){let t=n[g++],i=t%r,l=o=>{e[o]<0&&(e[o]=e[t],n[m++]=o)};i&&l(t-1),i+1<r&&l(t+1),t>=r&&l(t-r),t+r<c.length&&l(t+r)}return e}var D="andraniksargsyan/migan",F="406830d0fa60666da0071c342ad2fbc8f30c5c64",H="migan.onnx",V="593eba0b7e04730f1b61c0a3cbca68d97d8d6a7ff5c6a44a7b9d7fcd880fc5ae";function G(c,r,s,a=Math.max(2,Math.ceil(Math.max(r,s)*.004))){if(c.length!==r*s)throw new Error("Mask dimensions do not match.");let e=r+1,n=new Uint32Array(e*(s+1));for(let t=0;t<s;t++){let i=0;for(let l=0;l<r;l++)i+=c[t*r+l]>4?1:0,n[(t+1)*e+l+1]=n[t*e+l+1]+i}let g=new Uint8Array(c.length);for(let t=0;t<s;t++)for(let i=0;i<r;i++){let l=Math.max(0,i-a),o=Math.min(r,i+a+1),p=Math.max(0,t-a),d=Math.min(s,t+a+1);n[d*e+o]-n[p*e+o]-n[d*e+l]+n[p*e+l]&&(g[t*r+i]=255)}let m=z(g,r,s,!1);if(!m)throw new Error("Not enough visible background to reconstruct. Use Fast fill or a wider photo.");return{hole:g,donors:m,radius:a}}function J(c,r,s,a){if(c.length!==s.length*4||r.length!==c.length||a.length!==s.length)throw new Error("Fill dimensions do not match.");let e=new Uint8ClampedArray(c);for(let n=0;n<s.length;n++)s[n]&&(e.set(r.subarray(n*4,n*4+3),n*4),e[n*4+3]=c[a[n]*4+3]);return e}var K="https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0/dist/",y,u,x;async function q(c){let r=`https://huggingface.co/${D}/resolve/${F}/${H}`,s;try{s=await caches.open("ari-depth-fill-models-v1")}catch{}let a=await s?.match(r),e=!!a;if(a||(a=await fetch(r)),!a.ok)throw new Error(`Model download failed (${a.status}).`);let n=a.clone(),g=a.body.getReader(),m=Number(a.headers.get("content-length"))||29546882,t=[],i=0;for(;;){let{value:d,done:h}=await g.read();if(h)break;t.push(d),i+=d.length,c(`${e?"Loading saved":"Downloading"} background model \xB7 ${Math.min(100,Math.round(i/m*100))}%`)}let l=new Uint8Array(i),o=0;for(let d of t)l.set(d,o),o+=d.length;if([...new Uint8Array(await crypto.subtle.digest("SHA-256",l))].map(d=>d.toString(16).padStart(2,"0")).join("")!==V)throw await s?.delete(r),new Error("Background model integrity check failed. Retry the download.");if(!e)try{await s?.put(r,n)}catch{}return l}self.onmessage=async({data:c})=>{let r=(a,e={})=>self.postMessage({id:c.id,type:a,...e}),s=a=>r("progress",{message:a});try{let a=performance.now(),{width:e,height:n}=c,g=new Uint8ClampedArray(c.rgba),m=new Uint8Array(c.mask),{hole:t,donors:i,radius:l}=G(m,e,n);if(!u){y||(y=await import(`${K}ort.webgpu.min.mjs`)),y.env.wasm.wasmPaths=K,y.env.wasm.numThreads=self.crossOriginIsolated?Math.min(4,navigator.hardwareConcurrency||1):1;let f=await q(s);if(s("Preparing background model\u2026"),navigator.gpu)try{u=await y.InferenceSession.create(f,{executionProviders:["webgpu"],graphOptimizationLevel:"all"}),x="webgpu"}catch{s("Using compatible CPU background processing\u2026")}u||(u=await y.InferenceSession.create(f,{executionProviders:["wasm"],graphOptimizationLevel:"all"}),x="wasm")}let o=512,p=o*o,d=Math.min(o/e,o/n),h=Math.max(1,Math.round(e*d)),b=Math.max(1,Math.round(n*d)),C=Math.floor((o-h)/2),A=Math.floor((o-b)/2),S=new OffscreenCanvas(e,n);S.getContext("2d").putImageData(new ImageData(g,e,n),0,0);let Q=new OffscreenCanvas(h,b),$=Q.getContext("2d");$.drawImage(S,0,0,h,b);let W=$.getImageData(0,0,h,b).data,O=new Uint8Array(3*p),P=new Uint8Array(p);for(let f=0;f<o;f++)for(let w=0;w<o;w++){let M=Math.max(0,Math.min(h-1,w-C)),j=Math.max(0,Math.min(b-1,f-A)),ee=Math.min(n-1,Math.floor((j+.5)/b*n))*e+Math.min(e-1,Math.floor((M+.5)/h*e)),te=t[ee]?0:1,B=f*o+w,ne=(j*h+M)*4;P[B]=te*255;for(let I=0;I<3;I++)O[I*p+B]=W[ne+I]}s(`Reconstructing hidden background \xB7 ${x==="webgpu"?"GPU":"CPU"}\u2026`);let X=performance.now(),L=new y.Tensor("uint8",O,[1,3,o,o]),U=new y.Tensor("uint8",P,[1,1,o,o]),v;try{try{v=await u.run({image:L,mask:U})}catch(f){if(x!=="webgpu")throw f;await u.release().catch(()=>{}),u=null,s("GPU fill unavailable. Reconstructing on CPU\u2026"),u=await y.InferenceSession.create(await q(s),{executionProviders:["wasm"],graphOptimizationLevel:"all"}),x="wasm",v=await u.run({image:L,mask:U})}}finally{L.dispose(),U.dispose()}let k=v[u.outputNames[0]],T=k.data;if(T.length!==3*p||k.dims[2]!==o||k.dims[3]!==o)throw new Error("Unexpected background model output.");let E=new ImageData(o,o);for(let f=0;f<p;f++){for(let w=0;w<3;w++){let M=T[w*p+f];if(!Number.isFinite(M))throw new Error("Background model returned invalid pixels.");E.data[f*4+w]=M}E.data[f*4+3]=255}for(let f of Object.values(v))f.dispose();let _=new OffscreenCanvas(o,o);_.getContext("2d").putImageData(E,0,0);let Y=new OffscreenCanvas(e,n),N=Y.getContext("2d");N.drawImage(_,C,A,h,b,0,0,e,n);let R=J(g,N.getImageData(0,0,e,n).data,t,i),Z={background:R,backgroundDonors:i,width:e,height:n,radius:l,device:x,inferenceMs:performance.now()-X,totalMs:performance.now()-a,model:D,revision:F,created:Date.now()};await u.release(),u=null,self.postMessage({id:c.id,type:"result",result:Z},[R.buffer,i.buffer])}catch(a){u&&await u.release().catch(()=>{}),u=null,r("error",{message:a.message||String(a)})}};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ function U(e,t,n,a){if(e.length!==t*n)throw new Error("Mask dimensions do not match.");let i=new Int32Array(e.length).fill(-1),c=new Int32Array(e.length),x=0,l=0;for(let o=0;o<e.length;o++)(a?e[o]>=240:e[o]<=4)&&(i[o]=o,c[l++]=o);if(!l)return null;for(;x<l;){let o=c[x++],f=o%t,u=r=>{i[r]<0&&(i[r]=i[o],c[l++]=r)};f&&u(o-1),f+1<t&&u(o+1),o>=t&&u(o-t),o+t<e.length&&u(o+t)}return i}function N(e,t,n,a){if(t.length!==n*a*4)throw new Error("Image dimensions do not match.");let i=0;for(let r of e)r>127&&i++;let c=i/e.length;if(c<.003||c>.985)return{usable:!1,coverage:c,reason:"No clear foreground/background split. Use the full image, or try a photo with a distinct subject."};let x=U(e,n,a,!0),l=U(e,n,a,!1);if(!x||!l)return{usable:!1,coverage:c,reason:"The subject boundary is uncertain. The full image is still available."};let o=new Uint8ClampedArray(t.length);for(let r=0;r<e.length;r++){let h=l[r]*4;o.set(t.subarray(h,h+4),r*4)}let f=o,u=new Uint8ClampedArray(o.length);for(let r=0;r<8;r++){u.set(f);for(let h=1;h<a-1;h++)for(let p=1;p<n-1;p++){let s=h*n+p;if(!(e[s]<=4))for(let m=0;m<3;m++)u[s*4+m]=(f[(s-1)*4+m]+f[(s+1)*4+m]+f[(s-n)*4+m]+f[(s+n)*4+m])/4}[f,u]=[u,f]}return{usable:!0,coverage:c,foregroundDonors:x,backgroundDonors:l,background:f}}var C=(e,t=0,n=1)=>Math.max(t,Math.min(n,e));function v(e,t,n,a=1){if(!Number.isSafeInteger(e)||!Number.isSafeInteger(t)||e<1||t<1||e*t>16e6||n!==e*t*a)throw Error("Quality input dimensions do not match.")}function k(e,t,n,a,{radius:i=24,amount:c=.7}={}){if(v(n,a,e.length),v(n,a,t.length,4),!Number.isFinite(i)||i<1||!Number.isFinite(c)||c<0||c>1)throw Error("Invalid edge refinement.");let x=U(e,n,a,!0),l=U(e,n,a,!1),o=new Uint8Array(e),f=new Uint8ClampedArray(t),u=0;if(!x||!l)return{mask:o,foreground:f,changed:u};for(let r=0;r<e.length;r++){let h=e[r]/255;if(h<=4/255||h>=240/255)continue;let p=x[r],s=l[r],m=r%n,D=Math.floor(r/n);if(Math.hypot(p%n-m,Math.floor(p/n)-D)>i||Math.hypot(s%n-m,Math.floor(s/n)-D)>i)continue;let g=0,y=0;for(let d=0;d<3;d++){let I=(t[p*4+d]-t[s*4+d])/255;g+=I*I,y+=(t[r*4+d]-t[s*4+d])/255*I}let A=C((g-.015)/.12),S=C((y+.035*h)/(g+.035)),M=C(h+C(S-h,-.2,.2)*c*A,0,t[r*4+3]/255);o[r]=Math.round(M*255),o[r]!==e[r]&&u++;for(let d=0;d<3;d++){let I=(t[r*4+d]-t[s*4+d]*(1-M))/Math.max(.15,M),W=t[p*4+d],E=C(M/.35),F=C(I*E+W*(1-E),0,255);f[r*4+d]=t[r*4+d]+(F-t[r*4+d])*c*A}}return{mask:o,foreground:f,changed:u}}function T(e,t,n,a,i){if(v(a,i,e.length),v(a,i,t.length),v(a,i,n.length,4),e.some(l=>!Number.isFinite(l)||l<0||l>1))throw Error("Depth must be finite and normalized.");let c=new Float32Array(e),x=0;for(let l=0;l<i;l++)for(let o=0;o<a;o++){let f=l*a+o,u=e[f],r=u*2,h=2;for(let p=-2;p<=2;p++)for(let s=-2;s<=2;s++){if(!s&&!p)continue;let m=o+s,D=l+p;if(m<0||m>=a||D<0||D>=i)continue;let g=D*a+m,y=e[g]-u;if(t[f]>=128!=t[g]>=128||Math.abs(y)>.08)continue;let A=0;for(let M=0;M<3;M++)A+=((n[f*4+M]-n[g*4+M])/255)**2;let S=Math.exp(-(s*s+p*p)/4-y*y/.0025-A/.06);r+=e[g]*S,h+=S}c[f]=C(u*.25+r/h*.75),Math.abs(c[f]-u)>1e-4&&x++}return{values:c,changed:x}}self.onmessage=({data:e})=>{try{let{width:t,height:n}=e,a=new Uint8ClampedArray(e.rgba),i=performance.now(),c=new OffscreenCanvas(e.maskWidth,e.maskHeight),x=new Uint8ClampedArray(e.mask.length*4);e.mask.forEach((g,y)=>x.set([g,g,g,255],y*4)),c.getContext("2d").putImageData(new ImageData(x,e.maskWidth,e.maskHeight),0,0);let l=new OffscreenCanvas(t,n),o=l.getContext("2d");o.drawImage(c,0,0,t,n);let f=o.getImageData(0,0,t,n).data,u=Uint8Array.from({length:t*n},(g,y)=>Math.min(f[y*4],a[y*4+3])),r=k(u,a,t,n,{radius:Math.max(12,Math.ceil(Math.max(t,n)*.025))}),h=new Float32Array(t*n);for(let g=0;g<n;g++)for(let y=0;y<t;y++){let A=Math.max(0,Math.min(e.depthWidth-1,(y+.5)/t*e.depthWidth-.5)),S=Math.max(0,Math.min(e.depthHeight-1,(g+.5)/n*e.depthHeight-.5)),M=Math.floor(A),d=Math.floor(S),I=Math.min(e.depthWidth-1,M+1),W=Math.min(e.depthHeight-1,d+1),E=A-M,F=S-d;h[g*t+y]=(e.depth[d*e.depthWidth+M]*(1-E)+e.depth[d*e.depthWidth+I]*E)*(1-F)+(e.depth[W*e.depthWidth+M]*(1-E)+e.depth[W*e.depthWidth+I]*E)*F}let p=T(h,r.mask,a,t,n),s={...N(r.mask,a,t,n),mask:r.mask,foreground:r.foreground,width:t,height:n,key:e.key+"|edges-v1"},m={subject:s,depth:{values:p.values,width:t,height:n,key:e.key+"|depth-v1"},stats:{edgePixels:r.changed,depthPixels:p.changed,prepareMs:Math.round(performance.now()-i)}},D=[s.mask.buffer,s.foreground.buffer,m.depth.values.buffer];s.usable&&D.push(s.foregroundDonors.buffer,s.backgroundDonors.buffer,s.background.buffer),self.postMessage({id:e.id,type:"result",result:m},D)}catch(t){self.postMessage({id:e.id,type:"error",message:t.message})}};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ var M="studioludens/birefnet-lite-512",v="962e67969ec6acec9c19fab76acee4f5e22e990c";function I(e,r,n,s,c={}){for(let o=0;o<e.length;o++)e[o]=Math.round(e[o]*r[o*4+3]/255);return{...T(e,r,n,s),mask:e,width:n,height:s,...c,created:Date.now()}}function U(e,r,n,s){if(e.length!==r*n)throw new Error("Mask dimensions do not match.");let c=new Int32Array(e.length).fill(-1),o=new Int32Array(e.length),p=0,l=0;for(let t=0;t<e.length;t++)(s?e[t]>=240:e[t]<=4)&&(c[t]=t,o[l++]=t);if(!l)return null;for(;p<l;){let t=o[p++],i=t%r,u=a=>{c[a]<0&&(c[a]=c[t],o[l++]=a)};i&&u(t-1),i+1<r&&u(t+1),t>=r&&u(t-r),t+r<e.length&&u(t+r)}return c}function T(e,r,n,s){if(r.length!==n*s*4)throw new Error("Image dimensions do not match.");let c=0;for(let a of e)a>127&&c++;let o=c/e.length;if(o<.003||o>.985)return{usable:!1,coverage:o,reason:"No clear foreground/background split. Use the full image, or try a photo with a distinct subject."};let p=U(e,n,s,!0),l=U(e,n,s,!1);if(!p||!l)return{usable:!1,coverage:o,reason:"The subject boundary is uncertain. The full image is still available."};let t=new Uint8ClampedArray(r.length);for(let a=0;a<e.length;a++){let f=l[a]*4;t.set(r.subarray(f,f+4),a*4)}let i=t,u=new Uint8ClampedArray(t.length);for(let a=0;a<8;a++){u.set(i);for(let f=1;f<s-1;f++)for(let w=1;w<n-1;w++){let m=f*n+w;if(!(e[m]<=4))for(let g=0;g<3;g++)u[m*4+g]=(i[(m-1)*4+g]+i[(m+1)*4+g]+i[(m-n)*4+g]+i[(m+n)*4+g])/4}[i,u]=[u,i]}return{usable:!0,coverage:o,foregroundDonors:p,backgroundDonors:l,background:i}}var h,d,x,y;self.onmessage=async({data:e})=>{let r=(n,s={})=>self.postMessage({id:e.id,type:n,...s});try{h||(h=await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1/dist/transformers.min.js")),h.env.allowLocalModels=!1,h.env.backends.onnx.wasm.numThreads=self.crossOriginIsolated?Math.min(4,navigator.hardwareConcurrency||1):1;let n=performance.now(),s=async(A,_)=>{let C=E=>{E.status==="progress"&&r("progress",{message:`Downloading subject model \xB7 ${Math.round(E.progress||0)}%`})};r("progress",{message:"Loading general subject model\u2026"}),d=await h.AutoModel.from_pretrained(M,{device:A,dtype:_,revision:v,progress_callback:C}),y=A},o=e.device==="webgpu"&&!!navigator.gpu&&!!await navigator.gpu.requestAdapter()?"webgpu":"wasm",p=e.dtype??"fp32",l=o+":"+p;d&&y!==l&&(await d.dispose().catch(()=>{}),d=null),d||(await s(o,p),y=l),x||(x=await h.AutoProcessor.from_pretrained(M,{revision:v})),r("progress",{message:`Finding foreground \xB7 ${y.startsWith("webgpu")?"GPU":"CPU"}\u2026`});let t=new Uint8ClampedArray(e.rgba),i=new h.RawImage(t,e.width,e.height,4),u=performance.now(),{pixel_values:a}=await x(i),f=await d({input_image:a}),w=f.logits||f.output_image;if(!w)throw new Error("Subject model returned no mask.");let m=w[0].sigmoid().mul(255).to("uint8"),g=await h.RawImage.fromTensor(m).resize(e.width,e.height),S=performance.now()-u;r("progress",{message:"Preparing separate layers and filling hidden background\u2026"});let b=I(new Uint8Array(g.data),t,e.width,e.height,{model:M,revision:v,device:y.split(":")[0],dtype:y.split(":")[1],inferenceMs:S,totalMs:performance.now()-n}),D=[b.mask.buffer];b.usable&&D.push(b.foregroundDonors.buffer,b.backgroundDonors.buffer,b.background.buffer),self.postMessage({id:e.id,type:"result",result:b},D)}catch(n){d&&await d.dispose().catch(()=>{}),d=null,x=null,r("error",{message:n.message||String(n)})}};
@@ -0,0 +1,2 @@
1
+ /* ARI Spatial Image viewer — Monty Alon */
2
+ var l="onnx-community/depth-anything-v2-small",p="4472b7362082ad9968fee890ca0f1e5aca36b93d",c={fast:{label:"Fast",size:322},balanced:{label:"Balanced",size:518},detail:{label:"Detail",size:770},local:{label:"Local HQ",size:1536}};function y(e){let t=1/0,r=-1/0;for(let s of e)Number.isFinite(s)&&(t=Math.min(t,s),r=Math.max(r,s));if(!Number.isFinite(t))throw new Error("The model returned no valid depth values.");let a=r-t;return Float32Array.from(e,s=>Number.isFinite(s)&&a>1e-8?(s-t)/a:.5)}var o,n,d;self.onmessage=async({data:e})=>{let t=(r,a={})=>self.postMessage({id:e.id,type:r,...a});try{t("progress",{message:"Loading depth engine\u2026"}),o||(o=await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1/dist/transformers.min.js")),o.env.allowLocalModels=!1,o.env.backends.onnx.wasm.numThreads=self.crossOriginIsolated?Math.min(4,navigator.hardwareConcurrency||1):1;let r=e.device!=="wasm"&&navigator.gpu?await navigator.gpu.requestAdapter():null;if(e.device==="webgpu"&&!r)throw new Error("WebGPU is unavailable. Select Auto or CPU.");let a=r?"webgpu":"wasm",s=e.dtype??(a==="webgpu"?"fp32":"q8"),m=`${a}:${s}`,v=performance.now();d!==m&&(n&&await n.dispose(),n=null,d=null,n=await o.pipeline("depth-estimation",l,{device:a,dtype:s,revision:p,progress_callback:h=>{h.status==="progress"&&t("progress",{message:`Downloading model \xB7 ${Math.round(h.progress||0)}%`})}}),d=m);let f=n.processor.image_processor||n.processor.feature_extractor;if(!f)throw new Error("Unsupported model processor.");f.size={width:c[e.tier].size,height:c[e.tier].size};let b=new o.RawImage(new Uint8ClampedArray(e.rgba),e.width,e.height,4);t("progress",{message:`Estimating depth \xB7 ${a==="webgpu"?"GPU":"CPU"}\u2026`});let E=performance.now(),u=await n(b),i=Float32Array.from(u.predicted_depth.data,Number),g=u.predicted_depth.dims,w=y(i);self.postMessage({id:e.id,type:"result",result:{values:w,raw:i,width:g.at(-1),height:g.at(-2),device:a,dtype:s,model:l,revision:p,tier:e.tier,inferenceMs:performance.now()-E,totalMs:performance.now()-v,created:Date.now()}},[w.buffer,i.buffer])}catch(r){t("error",{message:r.message||String(r)})}};
@@ -0,0 +1,17 @@
1
+ // The viewer bundle asks for these only when it prepares depth in the browser, which `SpatialImage` never
2
+ // does (it passes `prepareInEditor: false`): depth is made by the showcase media service and
3
+ // arrives as `spatialAsset`. The bundle still requires the map; it is kept complete in case in-editor
4
+ // preparation is ever enabled for this host.
5
+ const WORKERS = {
6
+ 'depth-gallery-worker': new URL('./viewer/workers/depth-gallery-worker.mjs', import.meta.url),
7
+ 'depth-gallery-subject-worker': new URL('./viewer/workers/depth-gallery-subject-worker.mjs', import.meta.url),
8
+ 'depth-gallery-fill-worker': new URL('./viewer/workers/depth-gallery-fill-worker.mjs', import.meta.url),
9
+ 'depth-gallery-quality-worker': new URL('./viewer/workers/depth-gallery-quality-worker.mjs', import.meta.url),
10
+ };
11
+ export const workerUrl = (name) => {
12
+ const url = WORKERS[name];
13
+ if (!url) {
14
+ throw new Error(`Unknown preparation worker: ${name}`);
15
+ }
16
+ return url;
17
+ };
@@ -0,0 +1,2 @@
1
+ export { SpatialImage } from './SpatialImage/SpatialImage.js';
2
+ export { mediaId, shouldRenderSpatialImage, } from './SpatialImage/spatialAsset.js';
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import type { SpatialAsset } from './spatialAsset.js';
3
+ export type SpatialImageProps = {
4
+ id: string;
5
+ className?: string;
6
+ image: {
7
+ uri?: string;
8
+ name?: string;
9
+ width?: number;
10
+ height?: number;
11
+ alt?: string;
12
+ };
13
+ spatialAsset: SpatialAsset;
14
+ /** 0–100, how strong the depth/parallax is. */
15
+ depthStrength?: number;
16
+ /** How the photo fills the box: `cover` crops it (default), `contain` shows it whole. */
17
+ fit?: 'cover' | 'contain';
18
+ /** The image's role (e.g. `img`, `presentation`), kept on the accessible fallback image. */
19
+ role?: string;
20
+ };
21
+ export declare const SpatialImage: React.FC<SpatialImageProps>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The saved 3D (depth) version of a photo. The editor's "Add Depth" action asks the showcase media
3
+ * service to make it and writes it on the component as `spatialAsset`; hosts render `SpatialImage`
4
+ * only while the asset still describes the current photo (`shouldRenderSpatialImage`).
5
+ */
6
+ export type SpatialAsset = {
7
+ sourceUri: string;
8
+ sourceVersion: string;
9
+ url: string;
10
+ key: string;
11
+ };
12
+ type ImageLike = {
13
+ uri?: string;
14
+ crop?: {
15
+ width?: number;
16
+ height?: number;
17
+ x?: number;
18
+ y?: number;
19
+ };
20
+ };
21
+ /** The Wix media id inside any of the editor's image forms (bare id, wix:image://v1/<id>/..., static URL). */
22
+ export declare const mediaId: (uri: string | undefined) => string | null;
23
+ /**
24
+ * True when the host should render `SpatialImage`: the Depth effect is on and the saved asset
25
+ * was made from this very photo (same media id, no crop) and points at site media.
26
+ */
27
+ export declare const shouldRenderSpatialImage: (image: ImageLike | null | undefined, spatialAsset: SpatialAsset | null | undefined, depthEnabled: boolean | null | undefined) => boolean;
28
+ export {};
@@ -0,0 +1 @@
1
+ export declare const workerUrl: (name: string) => URL;
@@ -0,0 +1,4 @@
1
+ export { SpatialImage } from './SpatialImage/SpatialImage.js';
2
+ export type { SpatialImageProps } from './SpatialImage/SpatialImage.js';
3
+ export { mediaId, shouldRenderSpatialImage, } from './SpatialImage/spatialAsset.js';
4
+ export type { SpatialAsset } from './SpatialImage/spatialAsset.js';
package/package.json CHANGED
@@ -1,6 +1,51 @@
1
1
  {
2
2
  "name": "@wix/showcase-react-components",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.1.0",
4
+ "license": "UNLICENSED",
5
+ "type": "module",
6
+ "description": "Showcase React components for other Wix hosts. SpatialImage: a photo with spatial depth (3D), rendered from the spatialAsset the showcase media service made for it.",
7
+ "author": {
8
+ "name": "yoavt",
9
+ "email": "yoavt@wix.com"
10
+ },
11
+ "main": "dist/esm/index.js",
12
+ "module": "dist/esm/index.js",
13
+ "types": "dist/types/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/types/index.d.ts",
17
+ "import": "./dist/esm/index.js"
18
+ }
19
+ },
20
+ "sideEffects": false,
21
+ "files": [
22
+ "dist/esm/**",
23
+ "dist/types/**"
24
+ ],
25
+ "scripts": {
26
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && cp -R src/SpatialImage/viewer dist/esm/SpatialImage/viewer && rm dist/esm/SpatialImage/viewer/PATCHES.md",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "lint": "tsc --noEmit"
30
+ },
31
+ "peerDependencies": {
32
+ "react": ">=18.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/react": "^18.3.23",
36
+ "react": "^18.3.1",
37
+ "typescript": "^5.9.2",
38
+ "vitest": "^3.0.0"
39
+ },
40
+ "wix": {
41
+ "artifact": {
42
+ "groupId": "com.wixpress",
43
+ "artifactId": "showcase-react-components"
44
+ }
45
+ },
46
+ "publishConfig": {
47
+ "registry": "https://registry.npmjs.org/",
48
+ "access": "public"
49
+ },
50
+ "falconPackageHash": "56b981c3370542df99595bcd724e68f7f1eb478fd805e88f631db582"
6
51
  }