shotops-mcp 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -24
- package/dist/local.js +19127 -18624
- package/harness-dist/assets/index-MiEp7Ev8.js +1 -0
- package/harness-dist/index.html +1 -1
- package/package.json +8 -1
- package/harness-dist/assets/index-gGXe4d5n.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as qt,G as Wt,A as Vt,D as ae,M as Kt,R as Yt,a as Xt,C as Jt,b as Zt,c as Qt}from"./three-addons-CvX38LO6.js";import{aa as en,P as Qe,b as tn,V as K,a4 as nn,C as O,ab as rn,J as Ce,$ as W,M as on,a2 as an,B as sn,l as ln}from"./three-webgl-DCw804Ic.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&o(s)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();const cn=""+new URL("iphone-optimized-BdcJzlHi.glb",import.meta.url).href,dn="data:model/gltf-binary;base64,Z2xURgIAAADUAAAAwAAAAEpTT057ImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJTaG90T3BzIHBsYWNlaG9sZGVyIOKAlCB0aGUgUGl4ZWwgbW9kZWwgaXMgZGVsaWJlcmF0ZWx5IG5vdCBidW5kbGVkIGluIHRoZSBucG0gcGFja2FnZSJ9LCJzY2VuZXMiOlt7Im5hbWUiOiJwaXhlbC1ub3QtYnVuZGxlZCIsIm5vZGVzIjpbXX1dLCJzY2VuZSI6MH0gICA=",un=[{id:"grey",label:"Grey",color:11579568},{id:"white",label:"White",color:15263458},{id:"charcoal",label:"Charcoal",color:3815996}],pn=[{id:"silver",label:"Silver",frame:16119285,back:16514043,accent:13816532},{id:"blue",label:"Deep Blue",frame:3290954,back:4409953,accent:2172211},{id:"orange",label:"Cosmic Orange",frame:16219693,back:16487504,accent:12738072}],fn=[{id:"silver",label:"Moonstone",frame:9345186,back:8621205,accent:7041658},{id:"blue",label:"Obsidian",frame:2763308,back:2565928,accent:2105377},{id:"orange",label:"Porcelain",frame:15920872,back:14670805,accent:11973550},{id:"green",label:"Jade",frame:9412240,back:8688005,accent:7042924}],Y={id:"iphone-17-pro",glb:cn,fitSize:3.6,baseEuler:{x:0,y:0,z:0},pruneNamePrefixes:[],screen:{material:"OLED",repeat:{x:-1,y:1},coverFill:1.001,coverCornerRatio:.18,coverRepeat:{x:-1,y:-1}},body:{frame:["Anodized aluminum"],back:["Frosted glass"],accent:["Plastic antena","PaletteMaterial001"]},colorways:pn},ke={id:"pixel-10-pro",glb:dn,fitSize:3.6,baseEuler:{x:0,y:Math.PI,z:0},pruneNamePrefixes:["Google Pixel 10 Pro Moonstone.1"],screen:{material:"GP10p_display",repeat:{x:1,y:1},coverFill:1.001,coverCornerRatio:.14,coverRepeat:{x:1,y:-1}},body:{frame:["GP10p_mid","GP10p_camera"],back:["GP10p_back","GP10p_plastic"],accent:["GP10p_logo"]},colorways:fn},hn={[Y.id]:Y,[ke.id]:ke};function we(e){return e&&hn[e]||Y}const mn=7,Oe={front:{x:0,y:0,z:-1},left:{x:-2.6,y:.6,z:-5.2},right:{x:2.6,y:.6,z:-5.2}};function et(e){const t=Oe[e]??Oe.front,n=Math.hypot(t.x,t.y,t.z),o=mn/n;return{x:t.x*o,y:t.y*o,z:t.z*o}}function gn(e){return Math.atan2(e.x,-e.z)*180/Math.PI}function fe(e){const t=et(e);return new K(t.x,t.y,t.z)}const yn=1.4;function bn(e,t){return(t?new K(t.x,t.y,t.z):fe(e)).multiplyScalar(yn)}const tt=35;function vn(e){e.traverse(t=>{const n=t;n.geometry?.dispose();const o=n.material;Array.isArray(o)?o.forEach(Te):o&&Te(o)}),e.clear()}function Te(e){for(const t of Object.values(e))t&&typeof t=="object"&&t.isTexture&&t.dispose();e.dispose()}function nt(e,t){const n=document.createElement("canvas"),o=new en({canvas:n,antialias:!0,preserveDrawingBuffer:!0,alpha:!0});o.setSize(e,t,!1),o.shadowMap.enabled=!0;const r=new qt;r.background=null;const{ambient:a,directionals:s}=wn(r),i=new Qe(tt,e/t,.01,100);i.position.copy(fe("front")),i.lookAt(0,0,0);function l(p){i.position.copy(fe(p)),i.lookAt(0,0,0)}function d(p){i.position.set(p.x,p.y,p.z),i.lookAt(0,0,0)}function u(p){s.forEach(c=>c.visible=p),a.intensity=p?.75:1.6}let h=!1;function f(){h||(h=!0,r.environment?.dispose(),r.environment=null,vn(r),o.dispose(),o.forceContextLoss())}return{scene:r,camera:i,renderer:o,dispose:f,setAngle:l,setCameraPosition:d,getCameraPosition:()=>({x:i.position.x,y:i.position.y,z:i.position.z}),onUserPose:()=>{},previewOrbit:()=>{},commitOrbit:()=>{},setOrbitEnabled:()=>{},setBackground:()=>{},setStudioLighting:u,resize:()=>{}}}function wn(e){const t=new Vt(16777215,.75);e.add(t);const n=new ae(16777215,2.2);n.position.set(3,5,5);const o=new ae(12571903,.7);o.position.set(-4,1,2);const r=new ae(16777215,1);r.position.set(0,3,-5);const a=[n,o,r];return a.forEach(s=>e.add(s)),{ambient:t,directionals:a}}function rt(e,t){const n=we(t);return new Promise((o,r)=>{new Wt().load(n.glb,a=>{const s=a.scene;if(n.pruneNamePrefixes.length){const f=[];s.traverse(p=>{n.pruneNamePrefixes.some(c=>p.name.startsWith(c))&&f.push(p)}),f.forEach(p=>p.removeFromParent())}s.rotation.set(n.baseEuler.x,n.baseEuler.y,n.baseEuler.z),s.updateMatrixWorld(!0);const i=new tn().setFromObject(s),l=i.getSize(new K),d=i.getCenter(new K),u=n.fitSize/Math.max(l.x,l.y,l.z);s.scale.multiplyScalar(u),s.position.copy(d.multiplyScalar(-u));const h=new nn;h.add(s),e.scene.add(h),e.setAngle("front"),o(h)},void 0,r)})}function Sn(e){return rt(e)}Y.colorways;class ot{frame=[];back=[];accent=[];allBody=[];model;scene;renderer;profile;envTexture=null;constructor(t,n,o,r){this.model=t,this.scene=n,this.renderer=o,this.profile=we(r);const{frame:a,back:s,accent:i}=this.profile.body;t.traverse(l=>{const d=l;if(!d.isMesh)return;(Array.isArray(d.material)?d.material:[d.material]).forEach(h=>{const f=h;!f||!f.name||(a.includes(f.name)?this.push(this.frame,f):s.includes(f.name)?this.push(this.back,f):i.includes(f.name)&&this.push(this.accent,f),this.push(this.allBody,f))})})}push(t,n){t.includes(n)||t.push(n)}get finishMaterials(){return[...this.frame,...this.back]}paint(t,n){const o=n instanceof O?n:new O(n);t.forEach(r=>{r.color=o.clone(),r.needsUpdate=!0})}setColorway(t){if(t==="custom")return;const n=this.profile.colorways.find(o=>o.id===t)??this.profile.colorways[0];n&&(this.paint(this.frame,n.frame),this.paint(this.back,n.back),this.paint(this.accent,n.accent))}setCustomColor(t){const n=new O(t),o=n.clone().lerp(new O(16777215),.2),r=n.clone().multiplyScalar(.6);this.paint(this.frame,n),this.paint(this.back,o),this.paint(this.accent,r)}setFinish(t){const n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0;this.finishMaterials.forEach(o=>{o.userData.baseRoughness===void 0&&(o.userData.baseRoughness=o.roughness);const r=o.userData.baseRoughness;o.roughness=r*(1-n),o.needsUpdate=!0})}clearcoatUpgraded=!1;setClearcoat(t){const n=Math.max(0,Math.min(1,t));n>0&&!this.clearcoatUpgraded&&this.upgradeFinishToPhysical(),this.clearcoatUpgraded&&this.finishMaterials.forEach(o=>{const r=o;r.clearcoat=n,r.clearcoatRoughness=.1,r.needsUpdate=!0})}upgradeFinishToPhysical(){this.clearcoatUpgraded=!0;const t=new Map;for(const o of Array.from(new Set(this.finishMaterials))){const r=new Kt;r.color.copy(o.color),r.metalness=o.metalness,r.roughness=o.roughness,r.map=o.map,r.normalMap=o.normalMap,r.normalScale.copy(o.normalScale),r.roughnessMap=o.roughnessMap,r.metalnessMap=o.metalnessMap,r.aoMap=o.aoMap,r.aoMapIntensity=o.aoMapIntensity,r.emissive.copy(o.emissive),r.emissiveMap=o.emissiveMap,r.emissiveIntensity=o.emissiveIntensity,r.envMapIntensity=o.envMapIntensity,r.transparent=o.transparent,r.opacity=o.opacity,r.alphaTest=o.alphaTest,r.side=o.side,r.flatShading=o.flatShading,r.vertexColors=o.vertexColors,r.name=o.name,r.userData={...o.userData},r.clearcoat=0,t.set(o,r)}this.model.traverse(o=>{const r=o;if(r.isMesh)if(Array.isArray(r.material))r.material=r.material.map(a=>t.get(a)??a);else{const a=t.get(r.material);a&&(r.material=a)}});const n=o=>o.map(r=>t.get(r)??r);this.frame=n(this.frame),this.back=n(this.back),this.allBody=n(this.allBody)}environmentMap(){if(!this.envTexture){const t=new rn(this.renderer);this.envTexture=t.fromScene(new Yt,.04).texture,t.dispose()}return this.envTexture}setClay(t){const n=un.find(r=>r.id===t),o=n?new O(n.color):new O(t);this.allBody.forEach(r=>{r.userData.clayStashedMap===void 0&&(r.userData.clayStashedMap=r.map),r.color=o.clone(),r.metalness=0,r.roughness=.85,r.map=null,r.needsUpdate=!0})}clayOff(){this.allBody.forEach(t=>{t.userData.clayStashedMap!==void 0&&(t.map=t.userData.clayStashedMap,t.userData.clayStashedMap=void 0,t.needsUpdate=!0)})}setReflections(t){this.scene.environment=t?this.environmentMap():null}setRoll(t){this.model.rotation.z=t*Math.PI/180}}const Ee=1.35;class at{materials=[];glassOverlays=[];screenMesh=null;flatPlane=null;texture=null;appliedBytes=null;flatScreenOn=!1;profile;constructor(t,n){this.profile=we(n);const o=this.profile.screen.material;t.traverse(r=>{const a=r;if(!a.isMesh)return;(Array.isArray(a.material)?a.material:[a.material]).forEach(i=>{const l=i;l&&l.name===o&&!this.materials.includes(l)&&(this.materials.push(l),this.screenMesh=a)})}),this.dimGlassOverlay(t),this.prepareScreen(),this.setGlare(null)}get found(){return this.materials.length>0}setGlare(t){this.materials.forEach(n=>{n.envMap=t,n.envMapIntensity=t?.6:0,n.metalness=t?.5:0,n.roughness=t?.15:1,n.needsUpdate=!0}),this.glassOverlays.forEach(n=>{n.opacity=t?.08:0,n.needsUpdate=!0})}setFlatScreen(t){if(this.flatScreenOn=t,!this.screenMesh)return;if(this.flatPlane){this.screenMesh.remove(this.flatPlane);const w=this.flatPlane.material;w.map?.dispose(),w.dispose(),this.flatPlane.geometry.dispose(),this.flatPlane=null}if(!t||!this.texture)return;this.screenMesh.geometry.computeBoundingBox();const n=this.screenMesh.geometry.boundingBox;if(!n)return;const o=n.max.x-n.min.x,r=n.max.y-n.min.y,a=n.max.z-n.min.z,s=(n.max.x+n.min.x)/2,i=(n.max.y+n.min.y)/2,l=(n.max.z+n.min.z)/2,d=r<=o&&r<=a?"y":a<=o&&a<=r?"z":"x",u=this.texture.clone();u.needsUpdate=!0,u.colorSpace=Ce,u.flipY=!1,u.center.set(.5,.5),u.wrapS=W,u.wrapT=W;const{coverFill:h,coverCornerRatio:f,coverRepeat:p}=this.profile.screen;u.repeat.set(p.x,p.y);const c=d==="x"?a:o,g=d==="z"||d==="x"?r:a,m=Math.min(c,g)*h*f,b=In(c*h,g*h,m),y=new on(b,new Xt({map:u,emissive:new O(16777215),emissiveMap:u,emissiveIntensity:Ee,color:new O(0),toneMapped:!1,side:an,depthTest:!0,depthWrite:!0,polygonOffset:!0,polygonOffsetFactor:-4,polygonOffsetUnits:-4}));d==="y"?(y.rotation.x=-Math.PI/2,y.position.set(s,n.max.y,l)):d==="x"?(y.rotation.y=Math.PI/2,y.position.set(n.max.x,i,l)):y.position.set(s,i,n.max.z),y.renderOrder=999,this.screenMesh.add(y),this.flatPlane=y}restoreScreen(){this.prepareScreen()}prepareScreen(){this.materials.forEach(t=>{t.color=new O(0),t.emissive=new O(16777215),t.emissiveIntensity=1,t.roughness=1,t.metalness=0,t.needsUpdate=!0})}dimGlassOverlay(t){t.traverse(n=>{const o=n;if(!o.isMesh)return;(Array.isArray(o.material)?o.material:[o.material]).forEach(a=>{const s=a;s&&s.name!==this.profile.screen.material&&s.transparent&&s.opacity>.2&&this.glassOverlays.push(s)})})}applyWallpaper(t){if(this.appliedBytes===t&&this.texture)return this.lightScreen(this.texture),Promise.resolve();const n=new Blob([t],{type:"image/png"}),o=URL.createObjectURL(n),r=this.texture;return new Promise((a,s)=>{const i=new Image;i.onload=()=>{try{const l=document.createElement("canvas");l.width=i.naturalWidth,l.height=i.naturalHeight;const d=l.getContext("2d");if(!d)throw new Error("2D context unavailable for wallpaper blit");d.drawImage(i,0,0);const u=new Jt(l);u.colorSpace=Ce,u.flipY=!1,u.center.set(.5,.5),u.wrapS=W,u.wrapT=W,u.repeat.set(this.profile.screen.repeat.x,this.profile.screen.repeat.y),u.needsUpdate=!0,this.texture=u,this.appliedBytes=t,this.lightScreen(u),this.flatScreenOn&&this.setFlatScreen(!0),r?.dispose(),URL.revokeObjectURL(o),a()}catch(l){URL.revokeObjectURL(o),s(l)}},i.onerror=()=>{URL.revokeObjectURL(o),s(new Error("wallpaper image failed to decode"))},i.src=o})}lightScreen(t){this.materials.forEach(n=>{n.map=t,n.emissiveMap=t,n.emissive=new O(16777215),n.emissiveIntensity=Ee,n.color=new O(0),n.needsUpdate=!0})}}function In(e,t,n){const o=e/2,r=t/2,a=Math.min(n,o,r),s=new Zt;s.moveTo(-o+a,-r),s.lineTo(o-a,-r),s.quadraticCurveTo(o,-r,o,-r+a),s.lineTo(o,r-a),s.quadraticCurveTo(o,r,o-a,r),s.lineTo(-o+a,r),s.quadraticCurveTo(-o,r,-o,r-a),s.lineTo(-o,-r+a),s.quadraticCurveTo(-o,-r,-o+a,-r);const i=new Qt(s,16),l=i.attributes.position,d=new Float32Array(l.count*2);for(let u=0;u<l.count;u++)d[u*2]=(l.getX(u)+o)/e,d[u*2+1]=(l.getY(u)+r)/t;return i.setAttribute("uv",new sn(d,2)),i}const Le={sizePt:{min:20,max:120},maxWidth:{min:.1,max:1},lineHeight:{min:.5,max:3},bandInset:{min:0,max:.5},bandCenter:{min:0,max:1},reserveLines:{min:1,max:8}},X=[{id:"inter",family:"Inter",label:"Inter"},{id:"manrope",family:"Manrope",label:"Manrope"},{id:"poppins",family:"Poppins",label:"Poppins"},{id:"fraunces",family:"Fraunces",label:"Fraunces"},{id:"space-grotesk",family:"Space Grotesk",label:"Space Grotesk"}],Me=X.map(e=>e.id);[Me[0],...Me.slice(1)];const J=600;function st(e){return X.find(t=>t.id===e)??X[0]}const it=[{id:"standard",label:"Standard",blurb:"Headline on top, whole device below it, nothing cropped. The safe default.",character:"safe",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2}},{id:"bleed",label:"Bleed",blurb:"Room for a longer headline, device running off the bottom edge. What the market looks like.",character:"conventional",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4}},{id:"top-bleed",label:"Top bleed",blurb:"Device running off the TOP edge, headline underneath it. Inverts the usual reading order.",character:"expressive",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"-38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,band:"bottom"}}],lt=it,Fe=lt.map(e=>e.id);[Fe[0],...Fe.slice(1)];Array.from(new Set(it.flatMap(e=>Object.keys(e.caption))));const ct=[{id:"noir",label:"Noir",blurb:"Near-black, high contrast, glossy silver device. The default premium look.",background:{gradientFrom:"#1C1C22",gradientTo:"#08080B",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"silver",finish:"0",clearcoat:"1",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"inter",color:"FFFFFF"}},{id:"daylight",label:"Daylight",blurb:"Warm off-white, soft matte white device, dark text. Calm and editorial.",background:{gradientFrom:"#F4F1EA",gradientTo:"#DFD8CA",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"white",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"front"},caption:{fontId:"manrope",color:"1A1A1F"}},{id:"pop",label:"Pop",blurb:"Saturated indigo-to-violet, orange device. Loud, consumer, scroll-stopping.",background:{gradientFrom:"#5B6CF0",gradientTo:"#9B4DF0",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"orange",finish:"0",clearcoat:"0.4",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"poppins",color:"FFFFFF"}},{id:"clay",label:"Clay",blurb:"Warm terracotta, matte charcoal device, tilted. Reads like a product photo.",background:{gradientFrom:"#E9BBAC",gradientTo:"#C88C7C",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"charcoal",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"left",roll:"-6"},caption:{fontId:"fraunces",color:"2A2320"}},{id:"studio",label:"Studio",blurb:"Mid slate with a floor reflection, blue device. Technical and precise.",background:{gradientFrom:"#5A6678",gradientTo:"#2B3340",gradientDir:"vertical",shadow:!0,floorReflection:!0},shotLook:{material:"real",colorway:"blue",finish:"0",clearcoat:"0",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"space-grotesk",color:"FFFFFF"}}],Re=ct.map(e=>e.id);[Re[0],...Re.slice(1)];const x={angle:"front",cameraPos:null,roll:"0",phoneHeight:"82",hOffset:"0",vOffset:"18",material:"real",colorway:"silver",customColor:"F5F5F5",finish:"0",clearcoat:"0",clayTone:"grey",clayCustom:"B0B0B0",flatScreen:!1,glare:!1,lighting:!0,reflections:!1},_e={angle:{valueKind:"angle",required:!0,defaultSource:"default-look",defaultValue:x.angle,persistence:"always"},cameraPos:{valueKind:"camera-position",required:!0,defaultSource:"default-look",defaultValue:x.cameraPos,persistence:"always"},roll:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.roll,persistence:"always"},phoneHeight:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.phoneHeight,persistence:"always"},hOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.hOffset,persistence:"always"},vOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.vOffset,persistence:"always"},material:{valueKind:"material",required:!0,defaultSource:"default-look",defaultValue:x.material,persistence:"always"},colorway:{valueKind:"colorway",required:!0,defaultSource:"default-look",defaultValue:x.colorway,persistence:"always"},customColor:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.customColor,persistence:"always"},finish:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.finish,persistence:"always"},clearcoat:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.clearcoat,persistence:"always"},clayTone:{valueKind:"clay-tone",required:!0,defaultSource:"default-look",defaultValue:x.clayTone,persistence:"always"},clayCustom:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.clayCustom,persistence:"always"},flatScreen:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.flatScreen,persistence:"always"},clipToFrame:{valueKind:"boolean",required:!1,defaultSource:"explicit",defaultValue:!1,persistence:"omit-default"},glare:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.glare,persistence:"always"},lighting:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.lighting,persistence:"always"},reflections:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.reflections,persistence:"always"}},xn={roll:{min:-45,max:45},phoneHeight:{min:20,max:200},hOffset:{min:-100,max:100},vOffset:{min:-200,max:200},finish:{min:0,max:1},clearcoat:{min:0,max:1}},H={x:.5,y:.06},Se={fontId:"inter",sizePt:32,color:"FFFFFF",align:"center",anchor:H,maxWidth:.86},Ne=[{id:"r69",label:"6.9″ iPhone · 1290×2796",width:1290,height:2796},{id:"r65",label:"6.5″ iPhone · 1242×2688",width:1242,height:2688},{id:"r55",label:"5.5″ iPhone · 1242×2208",width:1242,height:2208},{id:"pixel69",label:"Pixel 10 Pro · 1290×2796",width:1290,height:2796},{id:"ipad13",label:"13″ iPad · 2064×2752",width:2064,height:2752},{id:"ipad129",label:"12.9″ iPad · 2048×2732",width:2048,height:2732}];function G(e){return Ne.find(t=>t.id===e)??Ne[0]}const te=932,he="iphone-17-pro",An="pixel-10-pro",Ie=[{id:"iphone-6-9",label:"iPhone 17 Pro",panelPresetId:"r69",ascDeviceSet:"APP_IPHONE_67",modelId:he,screenFamily:"iphone"},{id:"iphone-6-5",label:"iPhone 17",panelPresetId:"r65",ascDeviceSet:"APP_IPHONE_65",modelId:he,screenFamily:"iphone"},{id:"pixel-10-pro",label:"Pixel 10 Pro",panelPresetId:"pixel69",ascDeviceSet:"ANDROID_PHONE",modelId:An,screenFamily:"android"},{id:"ipad-13",label:"iPad 13″",panelPresetId:"ipad13",ascDeviceSet:"APP_IPAD_PRO_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0},{id:"ipad-12-9",label:"iPad 12.9″",panelPresetId:"ipad129",ascDeviceSet:"APP_IPAD_PRO_3GEN_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0}];function Pn(e){return Ie.find(t=>t.panelPresetId===e)?.modelId??he}const dt="iphone-6-9";function ut(e){return Ie.find(t=>t.id===e)}function Cn(){return Ie.filter(e=>!e.comingSoon)}function pt(e,t){if(Array.isArray(e)){const n=new Set,o=[];for(const r of e){const a=ut(r);a&&!a.comingSoon&&!n.has(r)&&(n.add(r),o.push(r))}if(o.length>0)return o}if(t){const n=Cn().find(o=>o.panelPresetId===t);if(n)return[n.id]}return[dt]}const ft=ut(dt)?.screenFamily??"iphone";function ht(e){const t={...e},n=t,o=Object.keys(_e);for(const r of o){const a=_e[r];a.persistence==="omit-default"&&(n[r]===void 0||n[r]===a.defaultValue)&&delete n[r]}return t}function mt(e){return ht({...x,...e})}function gt(e){return ht(mt(e))}function R(e,t){const{min:n,max:o}=xn[e],r=typeof t=="number"?t:Number.parseFloat(t??""),a=Number.isFinite(r)?r:Number.parseFloat(x[e]);return Math.max(n,Math.min(o,a))}const kn=90,yt=180,On="#1b1b2e",Tn="#0a0a14";function En(e){return e==="horizontal"?kn:yt}function Ln(e){const t=(e%180+180)%180;return t>=45&&t<135?"horizontal":"vertical"}function bt(e,t,n){return{stops:[{color:e,at:0},{color:t,at:1}],angle:En(n)}}const vt=e=>e*Math.PI/180;function Mn(e){const t=vt(e);return{dx:Math.sin(t),dy:-Math.cos(t)}}function Fn(e,t,n){const o=vt(n);return Math.abs(e*Math.sin(o))+Math.abs(t*Math.cos(o))}function Rn(e,t,n){const{dx:o,dy:r}=Mn(n),a=Fn(e,t,n)/2,s=e/2,i=t/2;return{x0:s-a*o,y0:i-a*r,x1:s+a*o,y1:i+a*r}}function Z(e){const t=e.stops[0],n=e.stops[e.stops.length-1];return{gradientFrom:t?.color??"#000000",gradientTo:n?.color??t?.color??"#000000",gradientDir:Ln(e.angle)}}function ne(e,t){if(!Array.isArray(e)||e.length===0)return null;const n=[];for(const r of e){if(!r||typeof r!="object")return null;const a=r;if(typeof a.color!="string"||typeof a.at!="number"||!Number.isFinite(a.at))return null;n.push({color:a.color,at:Math.min(1,Math.max(0,a.at))})}const o=typeof t=="number"&&Number.isFinite(t)?_n(t):yt;return{stops:n,angle:o}}function Q(e){return ne(e.gradientStops,e.gradientAngle)??bt(e.gradientFrom,e.gradientTo,e.gradientDir)}function _n(e){return(e%360+360)%360}function Nn(e){return ne(e.stops,e.angle)??bt(e.from,e.to,e.dir)}function wt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(!o||typeof o!="object")continue;const r=o;if(r.kind==="solid"&&typeof r.color=="string")t[n]={kind:"solid",color:r.color};else if(r.kind==="gradient"&&typeof r.from=="string"&&typeof r.to=="string"&&(r.dir==="horizontal"||r.dir==="vertical")){const a=ne(r.stops,r.angle);t[n]=a?{kind:"gradient",stops:a.stops,angle:a.angle,from:r.from,to:r.to,dir:r.dir}:{kind:"gradient",from:r.from,to:r.to,dir:r.dir}}}return t}function j(e){return!e||e.x===H.x&&e.y===H.y}function jn(e){const{text:t,anchor:n,...o}=e;return j(n)?{...o}:{...o,anchor:n}}const Bn=.55,L="en-US";function zn(e){const t=e.lastIndexOf(":");if(t<=0)return null;const n=Number(e.slice(t+1));return!Number.isSafeInteger(n)||n<0?null:{panelId:e.slice(0,t),index:n}}function St(e,t,n,o){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[a,s]of Object.entries(e)){if(a===n||!t.includes(a)||!Array.isArray(s))continue;const i=Array.from(new Set(s.filter(l=>typeof l=="string"))).filter(l=>{const d=zn(l);return!!d&&!!o[n]?.[d.panelId]?.[d.index]&&!!o[a]?.[d.panelId]?.[d.index]});i.length>0&&(r[a]=i)}return r}function It(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))!o||typeof o!="object"||Array.isArray(o)||(t[n]=B(o));return t}function Dn(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.x=="number"?t.x:H.x,o=typeof t.y=="number"?t.y:H.y;return j({x:n,y:o})?void 0:{x:n,y:o}}function B(e){const t=e&&typeof e=="object"?e:{},n=Dn(t.anchor),o={...Se,...t};n?o.anchor=n:delete o.anchor,typeof o.reserveLines!="number"||!Number.isFinite(o.reserveLines)||o.reserveLines<Le.reserveLines.min?delete o.reserveLines:o.reserveLines=Math.floor(o.reserveLines),o.band!=="bottom"&&delete o.band;for(const r of["bandInset","lineHeight","bandCenter"]){const{min:a,max:s}=Le[r],i=o[r];(typeof i!="number"||!Number.isFinite(i)||i<a||i>s)&&delete o[r]}return o}function Hn(e){const t=e&&typeof e=="object"?e:{};return{...B(t),text:typeof t.text=="string"?t.text:""}}function xt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))Array.isArray(o)?o.length>0&&(t[n]=o.map(B)):o&&typeof o=="object"&&(t[n]=[B(o)]);return t}function Gn(e,t){const n=[{...B(t),text:e.headline}];return typeof e.subtitle=="string"&&e.subtitle.trim()&&n.push({...B({...t,sizePt:t.sizePt*Bn}),text:e.subtitle}),n}function re(e,t,n=L){if(!e||typeof e!="object"||Array.isArray(e))return{};const o=It(t),r=d=>o[d]??Se,a=d=>{if(!d||typeof d!="object"||Array.isArray(d))return{};const u={};for(const[h,f]of Object.entries(d))Array.isArray(f)?f.length>0&&(u[h]=f.map(Hn)):f&&typeof f=="object"&&typeof f.headline=="string"&&(u[h]=Gn(f,r(h)));return u},s=Object.entries(e);if(s.some(([,d])=>!!d&&typeof d=="object"&&typeof d.headline=="string"))return{[n]:a(e)};const l={};for(const[d,u]of s)l[d]=a(u);return l}function At(e,t,n=L){return e[t??n]??e[n]??{}}function Pt(e,t=L){const n={},o=Object.keys(e),r=o.includes(t)?[t,...o.filter(a=>a!==t)]:o;for(const a of r){const s=e[a];if(s)for(const[i,l]of Object.entries(s))n[i]===void 0&&Array.isArray(l)&&l.length>0&&(n[i]=l.map(jn))}return n}function me(e,t=L,n=ft){const o={};return e.family!==void 0&&e.family!==n&&(o.family=e.family),e.locale!==void 0&&e.locale!==t&&(o.locale=e.locale),o}function je(e){const t=[];return e.family!==void 0&&t.push(`family=${e.family}`),e.locale!==void 0&&t.push(`locale=${e.locale}`),t.join("&")}function ge(e){const t=[];return e.family!==void 0&&t.push("family"),e.locale!==void 0&&t.push("locale"),t}const Un={order:["family","locale"],reach:"strict"};function $n(e,t){const n=ge(e),o=t.order.filter(a=>n.includes(a));return Array.from({length:o.length+1},(a,s)=>o.slice(0,o.length-s)).map(a=>{const s={};return a.includes("family")&&(s.family=e.family),a.includes("locale")&&(s.locale=e.locale),s})}function qn(e,t,n,o,r=L){const a=me(n,r);for(const s of $n(a,o)){const i=e(t,s);if(i!==void 0){const l=ge(s);return{value:i,resolvedAt:s,inherited:ge(a).filter(d=>!l.includes(d))}}}return{value:void 0,resolvedAt:null,inherited:[]}}function ye(e,t,n=L){const o=r=>{const a=new Set,s=[];for(const i of r)typeof i=="string"&&i&&!a.has(i)&&(a.add(i),s.push(i));return s};if(Array.isArray(t)){const r=o(t);if(r.length>0)return r.includes(n)?r:[...r,n]}return o([n,...Object.keys(e)])}function xe(e,t=L){return typeof e=="string"&&e.length>0?e:t}const z={"it-IT":"it"};function Be(e,t){if(!(Object.keys(e).some(r=>r in z)||(t?.some(r=>r in z)??!1)))return{captionText:e,locales:t};const o={};for(const[r,a]of Object.entries(e))r in z||(o[r]=a);for(const[r,a]of Object.entries(e)){const s=z[r];s&&(o[s]={...a,...o[s]??{}})}return{captionText:o,locales:t?.map(r=>z[r]??r)}}function Ct(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(n===ft||!o||typeof o!="object"||Array.isArray(o))continue;const r={};for(const[a,s]of Object.entries(o)){if(!s||typeof s!="object"||Array.isArray(s))continue;const i={};for(const[l,d]of Object.entries(s))Array.isArray(d)&&d.every(u=>typeof u=="string")&&(i[l]=d);Object.keys(i).length>0&&(r[a]=i)}Object.keys(r).length>0&&(t[n]=r)}return t}function U(e,t){return t.flatMap(n=>e.filter(o=>o.panelId===n.id))}let ze=0,De=0;function Wn(e,t){const n=(o,r)=>o.reduce((a,s)=>{const i=s.startsWith(r)?Number.parseInt(s.slice(r.length),10):NaN;return Number.isFinite(i)&&i>a?i:a},0);ze=Math.max(ze,n(e,"shot-")),De=Math.max(De,n(t,"panel-"))}const Vn=.49;function Kn(e,t,n){return e.map(o=>{const r=n*o.heightFrac,a=o.height>0?o.width*(r/o.height):r*Vn,i=o.panelIndex*t+t/2-a/2+o.hOffset*(t/2),d=(n-r)/2+o.vOffset*(n/2);return{shotId:o.shotId,bytes:o.bytes,x:Math.round(i),y:Math.round(d),width:Math.round(a),height:Math.round(r),panelIndex:o.panelIndex,...o.clipToFrame===!0?{clipToFrame:!0}:{}}})}function Yn(e,t){const n=t.panelBackgrounds??{},o=Q(t),r=o.stops[0]?.color??t.gradientFrom;if(!e.some(l=>n[l.id]))return t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"perPanel",colors:e.map(l=>t.panelColors[l.id]??r)};const s=l=>t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"solid",color:t.panelColors[l]??r};return{kind:"perPanelFills",fills:e.map(l=>{const d=n[l.id];return d?d.kind==="solid"?{kind:"solid",color:d.color}:{kind:"gradient",gradient:Nn(d)}:s(l.id)})}}function kt(e,t,n=L){const o=Q(e);return{panelPresetId:e.panelPresetId,bgMode:e.bgMode,gradientStops:o.stops,gradientAngle:o.angle,...Z(o),panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection??!1,panelBackgrounds:e.panelBackgrounds??{},captionStyles:{...xt(e.captionStyles),...Pt(t,n)}}}function Xn(e){const t=e.caption.reserveLines,n=typeof t=="number"?` Reserves ${t} headline lines (~${t*18} characters).`:"";return`${e.blurb}${n} Character: [${e.character}].`}function Jn(e,t){return Math.max(0,...Object.values(e).map(n=>n[t]?.length??0))}function Ot(e,t,n){const o=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=new Set,a={};let s=1;const i=()=>{for(;r.has(`caption-${s}`);)s+=1;const l=`caption-${s}`;return r.add(l),s+=1,l};for(const l of n){const d=Jn(t,l);if(d===0)continue;const u=Array.isArray(o[l])?o[l]:[];a[l]=Array.from({length:d},(h,f)=>{const p=u[f];return typeof p=="string"&&p.length>0&&!r.has(p)?(r.add(p),p):i()})}return a}const Tt=2;function Zn(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return t.schema===Tt&&Array.isArray(t.shots)&&Array.isArray(t.panels)&&t.panels.length>0}function Qn(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Zn(t))return null;const n=t,o=new Set(n.panels.map(c=>c.id)),r=n.shots.filter(c=>o.has(c.panelId)).map(c=>({id:c.id,panelId:c.panelId,frameNodeId:c.frameNodeId,frameName:c.frameName,bytes:null,look:mt(c.look),thumb:null,thumbStale:!0,inputAnchor:c.inputAnchor,outputAnchor:c.outputAnchor})),a=n.panels.map(c=>({id:c.id}));Wn(r.map(c=>c.id),a.map(c=>c.id));const s=n.selectedShotId&&r.some(c=>c.id===n.selectedShotId)?n.selectedShotId:r[0]?.id??null,i=xe(n.baseLocale),{captionText:l,locales:d}=Be(re(n.captionText,n.captionStyles,i),n.locales),u=Be({[i]:{}},[i]).locales?.[0]??i,h=ye(l,d,u),f=Ot(n.captionSlotIds,l,a.map(c=>c.id)),p=ne(n.gradientStops,n.gradientAngle);return{shots:U(r,a),panels:a,selectedShotId:s,panelPresetId:G(n.panelPresetId).id,bgMode:n.bgMode==="perPanel"?"perPanel":"gradient",...p?{gradientStops:p.stops,gradientAngle:p.angle,...Z(p)}:{gradientFrom:n.gradientFrom??"5B6CF0",gradientTo:n.gradientTo??"9B4DF0",gradientDir:n.gradientDir==="vertical"?"vertical":"horizontal"},panelColors:n.panelColors??{},shadow:n.shadow!==!1,floorReflection:n.floorReflection===!0,panelBackgrounds:wt(n.panelBackgrounds),captionText:l,locales:h,baseLocale:u,staleCaptionTranslations:St(n.staleCaptionTranslations,h,u,l),outputs:pt(n.outputs,n.panelPresetId),localeScreens:{},outputCaptions:Ct(n.outputCaptions),captionSlotIds:f,frameTemplates:n.frameTemplates&&typeof n.frameTemplates=="object"&&!Array.isArray(n.frameTemplates)?n.frameTemplates:{}}}function Et(e){const n=U(e.shots,e.panels).map(a=>({id:a.id,panelId:a.panelId,frameNodeId:a.frameNodeId,frameName:a.frameName,look:gt(a.look),inputAnchor:a.inputAnchor,outputAnchor:a.outputAnchor})),o=xe(e.baseLocale),r=re(e.captionText,e.captionStyles,o);return{schema:Tt,shots:n,panels:e.panels,selectedShotId:e.selectedShotId,captionText:r,locales:ye(r,e.locales,o),baseLocale:o,staleCaptionTranslations:St(e.staleCaptionTranslations,ye(r,e.locales,o),o,r),outputs:pt(e.outputs,e.panelPresetId),outputCaptions:Ct(e.outputCaptions),...(()=>{const a=Ot(e.captionSlotIds,r,e.panels.map(s=>s.id));return Object.keys(a).length>0?{captionSlotIds:a}:{}})(),...e.frameTemplates&&Object.keys(e.frameTemplates).length>0?{frameTemplates:e.frameTemplates}:{},...kt(e,r,o)}}const er=1;function tr(e){const t=U(e.shots,e.panels),n=re(e.captionText,e.captionStyles),o=xe(e.baseLocale);return{schema:er,shots:t.map(r=>({panelId:r.panelId,look:gt(r.look)})),...kt(e,n,o)}}ct.map(e=>({id:e.id,label:e.label,description:e.blurb}));lt.map(e=>({id:e.id,label:e.label,description:Xn(e),character:e.character}));const nr=[{id:"frame.add",domain:"frame",status:"supported",summary:"Add a blank frame after an existing one.",studioCommands:["addPanel"],wire:"structuralOperations: frame.add",targets:["project"],requiredInputs:["afterFrameId (optional)","frameRef (optional, patch-local alias)"],limits:["Declare frameRef to address the new frame in later ordered operations of the same patch. Without frameRef, wait until the next cycle reports its id."],evaluationCases:["structure-add-closing-frame"]},{id:"frame.remove",domain:"frame",status:"supported",summary:"Remove a frame, with the devices and captions it holds.",studioCommands:["removePanel"],wire:"structuralOperations: frame.remove",targets:["frame"],requiredInputs:["frameId"],limits:["The last remaining frame cannot be removed."],evaluationCases:["structure-remove-empty-frame"]},{id:"frame.reorder",domain:"frame",status:"supported",summary:"Change the order frames tell the story in.",studioCommands:["movePanel","reorderPanels"],wire:"structuralOperations: frame.move, or the complete screenOrder permutation",targets:["project","frame"],requiredInputs:["frameId and its new position"],evaluationCases:["project-reorder-strongest"]},{id:"frame.split",domain:"frame",status:"supported",summary:"Split a multi-device frame by moving some of its devices into a new frame beside it.",studioCommands:[],wire:"structuralOperations: frame.split",targets:["frame","device"],requiredInputs:["frameId","a non-empty proper subset of its deviceIds"],evaluationCases:["structure-split-two-device-frame"]},{id:"frame.combine",domain:"frame",status:"supported",summary:"Merge one frame into another, keeping every device and caption, and drop the emptied frame.",studioCommands:[],wire:"structuralOperations: frame.combine",targets:["frame"],requiredInputs:["frameId (the survivor)","fromFrameId (the frame merged into it)"],evaluationCases:["structure-combine-last-two-frames"]},{id:"device.look",domain:"device",status:"supported",summary:"Restyle a device: camera angle, tilt, size, position, body colour, finish and lighting.",studioCommands:["patchShotLook"],wire:"patch.devices (by deviceId) or the complete patch.shots array",targets:["device"],requiredInputs:["deviceId","the look fields to change"],limits:["The raw camera 3-vector is not writable; the Agent moves the sanctioned orbit axis instead."],evaluationCases:["device-frame-two-clockwise","device-frame-one-gold","device-alternating-clay","device-frame-three-smaller-right","multi-device-spread-frame-two"]},{id:"device.move-to-frame",domain:"device",status:"supported",summary:"Move a device into an existing frame, at a chosen position among that frame’s devices.",studioCommands:["moveShotToFrame","moveShotToPanel","reorderShots"],wire:"structuralOperations: device.move",targets:["device","frame"],requiredInputs:["deviceId","toFrameId","toIndex (optional)"],evaluationCases:["structure-combine-last-two-frames"]},{id:"device.reorder",domain:"device",status:"supported",summary:"Reorder devices inside their own frame.",studioCommands:["moveShot"],wire:"structuralOperations: device.reorder",targets:["device"],requiredInputs:["deviceId","toIndex"],evaluationCases:["structure-reorder-devices-in-frame"]},{id:"device.remove",domain:"device",status:"supported",summary:"Remove one device while its frame stays.",studioCommands:["removeShot"],wire:"structuralOperations: device.remove",targets:["device"],requiredInputs:["deviceId"],limits:["The project’s last remaining device cannot be removed."],evaluationCases:["structure-remove-second-device"]},{id:"device.add",domain:"device",status:"supported",summary:"Add a device to a frame from a screenshot supplied with this instruction.",studioCommands:["addShot"],wire:"structuralOperations: device.add, naming an assetId from AVAILABLE SCREENSHOTS",targets:["frame"],requiredInputs:["toFrameId","assetId","toIndex (optional)"],withoutAssetReason:"Adding a device needs a screenshot sent with the instruction. This one carried none.",limits:["Only a screenshot listed in AVAILABLE SCREENSHOTS; the Agent never invents or generates pixels.","A device created this cycle can only be addressed in the next one, after Studio reports its id.","It lands on the default device look, exactly as dropping a screenshot into Studio does."],evaluationCases:["asset-add-supplied-screenshot"]},{id:"device.replace-pixels",domain:"asset",status:"supported",summary:"Replace the screenshot shown on an existing device, keeping the device untouched.",studioCommands:["reskinShots","ordinalReskin"],wire:"structuralOperations: device.replace, naming an assetId from AVAILABLE SCREENSHOTS",targets:["device"],requiredInputs:["deviceId","assetId"],withoutAssetReason:"Changing what a device shows needs the new screenshot sent with the instruction. This one carried none.",limits:["Only a screenshot listed in AVAILABLE SCREENSHOTS; the Agent never invents or generates pixels.","The device keeps its look, placement, captions, translated screens and Output membership — only the pixels change."],evaluationCases:["asset-replace-device-screenshot"]},{id:"caption.text",domain:"caption",status:"supported",summary:"Rewrite the headline words on frames.",studioCommands:["patchCaptionLayer"],wire:"the complete patch.captions array",targets:["project","frame","caption"],requiredInputs:["the complete caption list, one entry per frame"],limits:["Only each frame’s first caption layer, in the base language.","Hand-placed caption anchors and per-language fit overrides stay manual."],evaluationCases:["copy-all-six-words","copy-frame-two-exact","selection-caption-shorter"]},{id:"caption.style",domain:"caption",status:"supported",summary:"Restyle captions: font, size, colour, alignment, width and band placement.",studioCommands:[],wire:"patch.captionStyle (strip-wide) or patch.frames[i].captionStyle (one frame)",targets:["project","frame"],requiredInputs:["the style fields to change"],evaluationCases:["style-all-captions-smaller","style-selected-caption-white-left"]},{id:"caption.add",domain:"caption",status:"supported",summary:"Add a caption layer to a frame, with its words and styling.",studioCommands:["addCaptionLayer"],wire:"structuralOperations: caption.add",targets:["frame"],requiredInputs:["frameId","the caption text"],evaluationCases:["structure-subtitle-each-frame"]},{id:"caption.remove",domain:"caption",status:"supported",summary:"Remove one caption layer from a frame.",studioCommands:["removeCaptionLayer"],wire:"structuralOperations: caption.remove",targets:["caption"],requiredInputs:["frameId","the caption position"],evaluationCases:["structure-remove-subtitle"]},{id:"caption.reorder",domain:"caption",status:"supported",summary:"Reorder the caption layers stacked on one frame.",studioCommands:["reorderCaptionLayer"],wire:"structuralOperations: caption.move within one frame",targets:["caption"],requiredInputs:["frameId","the caption position","its new position"],evaluationCases:["structure-reorder-captions"]},{id:"caption.move-to-frame",domain:"caption",status:"supported",summary:"Move a caption layer to another frame.",studioCommands:["moveCaptionLayerToFrame"],wire:"structuralOperations: caption.move across frames",targets:["caption","frame"],requiredInputs:["frameId","the caption position","toFrameId"],evaluationCases:["structure-move-caption-to-frame"]},{id:"caption.translate",domain:"caption",status:"intentionally_excluded",summary:"Write caption words in a non-base language.",studioCommands:["applyCaptionTranslations","clearCaptionTranslation"],wire:null,targets:["project"],requiredInputs:[],reason:"Translation is its own metered Studio pass over the whole strip, not a refinement edit.",evaluationCases:["unsupported-translate-captions"]},{id:"caption.output-override",domain:"caption",status:"intentionally_excluded",summary:"Give one output size its own caption wording.",studioCommands:["patchOutputCaption","clearOutputCaption"],wire:null,targets:["caption"],requiredInputs:[],reason:"Per-output wording is a manual override of the shared caption; the Agent edits the shared one.",evaluationCases:["unsupported-per-output-caption"]},{id:"strip.background",domain:"look",status:"supported",summary:"Change the strip backdrop: gradient stops, angle, shadow and floor reflection.",studioCommands:["patchConfig"],wire:"patch.background",targets:["project"],requiredInputs:["the backdrop fields to change"],limits:["The canvas size a Look is authored against stays a Studio control.","Per-frame solid colours are set through the frame backdrop, not the strip config."],evaluationCases:["background-strip-navy-gradient"]},{id:"frame.background",domain:"look",status:"supported",summary:"Give one frame its own backdrop.",studioCommands:["setPanelBackground"],wire:"patch.frames[i].background",targets:["frame"],requiredInputs:["the frame","the backdrop fields"],evaluationCases:["background-frame-one-amber","inspection-correct-frame-two-background"]},{id:"frame.background-reset",domain:"look",status:"intentionally_excluded",summary:"Drop a frame’s own backdrop so it inherits the strip again.",studioCommands:["clearPanelBackground"],wire:null,targets:["frame"],requiredInputs:[],reason:"The Agent sets backdrops; returning one frame to the strip default is a Studio control.",evaluationCases:["unsupported-frame-background-reset"]},{id:"strip.background-broadcast",domain:"look",status:"intentionally_excluded",summary:"Copy one frame’s backdrop onto every frame, or promote it to the strip default.",studioCommands:["applyBackgroundToAllPanels","setStripDefaultBackground"],wire:null,targets:["project"],requiredInputs:[],reason:"No Studio surface offers these transfers today; the Agent writes the strip backdrop directly.",evaluationCases:["unsupported-background-broadcast"]},{id:"frame.color",domain:"look",status:"intentionally_excluded",summary:"Set a frame’s flat panel colour.",studioCommands:["setPanelColor"],wire:null,targets:["frame"],requiredInputs:[],reason:"No Studio surface dispatches it; the frame backdrop is the live control for the same result.",evaluationCases:["unsupported-frame-flat-color"]},{id:"look.design-preset",domain:"look",status:"supported",summary:"Apply a named design preset to the whole strip.",studioCommands:[],wire:"patch.designPreset",targets:["project"],requiredInputs:["a catalog preset id"],evaluationCases:["styling-apply-design-preset"]},{id:"look.layout-template",domain:"look",status:"supported",summary:"Apply a layout template, or explicitly reset one frame to its saved template baseline.",studioCommands:["applyLayoutTemplate","resetFrameTemplate"],wire:"patch.layout, or structuralOperations: frame.template-reset",targets:["project","frame"],requiredInputs:["a catalog layout id, or the frameId named in an explicit reset/revert request"],limits:["Never reset implicitly while handling a general styling request; reset may remove added screens or captions."],evaluationCases:["layout-use-bleed"]},{id:"look.apply-saved",domain:"look",status:"intentionally_excluded",summary:"Restore the project’s saved Look, or one of its earlier versions.",studioCommands:["applyLook"],wire:null,targets:["project"],requiredInputs:[],reason:"Saved Looks are versioned styling the user owns; restoring one is a Studio control, not an edit.",evaluationCases:["unsupported-restore-saved-look"]},{id:"output.membership",domain:"output",status:"supported",summary:"Add or remove the store output sizes this project renders for.",studioCommands:["addOutput","removeOutput","setActiveOutput"],wire:"patch.outputs",targets:["project"],requiredInputs:["the complete output list"],limits:["Which output is previewed on screen stays a Studio cursor."],evaluationCases:["project-add-android-output"]},{id:"locale.membership",domain:"locale",status:"supported",summary:"Add or remove the languages this project is captioned in.",studioCommands:["addLocale","removeLocale"],wire:"patch.locales",targets:["project"],requiredInputs:["the complete language list"],limits:["The base language cannot be removed, and which language is being authored stays a Studio cursor."],evaluationCases:["project-add-german-locale","geometry-clear-while-adding-a-language"]},{id:"strip.add-screens",domain:"asset",status:"requires_asset",summary:"Add new screenshots to the project, each in its own frame.",studioCommands:["addShots"],wire:null,targets:["project"],requiredInputs:["one or more authorized screenshot references"],reason:"New frames need new pixels. Supply the screenshots and the Agent will place them.",evaluationCases:["asset-add-screens-needs-pixels"]},{id:"strip.replace-screens",domain:"asset",status:"requires_asset",summary:"Replace every screenshot in the project, keeping the styling.",studioCommands:["loadScreens"],wire:null,targets:["project"],requiredInputs:["a complete authorized screenshot set"],reason:"Replacing the whole strip needs the whole new set of pixels.",evaluationCases:["asset-replace-all-screens-needs-pixels"]},{id:"locale.load-screens",domain:"asset",status:"requires_asset",summary:"Supply localized screenshots for one language.",studioCommands:["loadLocaleScreens"],wire:null,targets:["project"],requiredInputs:["the language","authorized screenshots named for the frames they belong to"],reason:"A localized screen is different pixels, not a different caption.",evaluationCases:["asset-locale-screens-needs-pixels"]},{id:"output.load-screens",domain:"asset",status:"requires_asset",summary:"Supply screenshots captured at another output size.",studioCommands:["loadOutputScreens"],wire:null,targets:["project"],requiredInputs:["the output family","authorized screenshots named for the frames they belong to"],reason:"A second output size needs its own captures; the Agent cannot resample one from another.",evaluationCases:["asset-output-screens-needs-pixels"]}],rr="unknown",or=[{id:"image.generate",summary:"Generate or paint new imagery rather than arranging supplied screenshots."},{id:"animation.create",summary:"Animate a screen, produce a video, or export motion instead of stills."},{id:"device.unsupported-model",summary:"Use a device model ShotOps does not offer."}],ar=nr.map(e=>e.id);[...ar,...or.map(e=>e.id)];const Lt=[{code:"ar-SA",name:"Arabic",script:"rtl",dir:"rtl"},{code:"bn-BD",name:"Bengali",script:"indic",dir:"ltr"},{code:"ca",name:"Catalan",script:"latin",dir:"ltr"},{code:"cs",name:"Czech",script:"latin",dir:"ltr"},{code:"da",name:"Danish",script:"latin",dir:"ltr"},{code:"de-DE",name:"German",script:"latin",dir:"ltr"},{code:"el",name:"Greek",script:"greek",dir:"ltr"},{code:"en-AU",name:"English (Australia)",script:"latin",dir:"ltr"},{code:"en-CA",name:"English (Canada)",script:"latin",dir:"ltr"},{code:"en-GB",name:"English (U.K.)",script:"latin",dir:"ltr"},{code:"en-US",name:"English (U.S.)",script:"latin",dir:"ltr"},{code:"es-ES",name:"Spanish (Spain)",script:"latin",dir:"ltr"},{code:"es-MX",name:"Spanish (Mexico)",script:"latin",dir:"ltr"},{code:"fi",name:"Finnish",script:"latin",dir:"ltr"},{code:"fr-CA",name:"French (Canada)",script:"latin",dir:"ltr"},{code:"fr-FR",name:"French",script:"latin",dir:"ltr"},{code:"gu-IN",name:"Gujarati",script:"indic",dir:"ltr"},{code:"he",name:"Hebrew",script:"rtl",dir:"rtl"},{code:"hi",name:"Hindi",script:"indic",dir:"ltr"},{code:"hr",name:"Croatian",script:"latin",dir:"ltr"},{code:"hu",name:"Hungarian",script:"latin",dir:"ltr"},{code:"id",name:"Indonesian",script:"latin",dir:"ltr"},{code:"it",name:"Italian",script:"latin",dir:"ltr"},{code:"ja",name:"Japanese",script:"cjk",dir:"ltr"},{code:"kn-IN",name:"Kannada",script:"indic",dir:"ltr"},{code:"ko",name:"Korean",script:"cjk",dir:"ltr"},{code:"ml-IN",name:"Malayalam",script:"indic",dir:"ltr"},{code:"mr-IN",name:"Marathi",script:"indic",dir:"ltr"},{code:"ms",name:"Malay",script:"latin",dir:"ltr"},{code:"nl-NL",name:"Dutch",script:"latin",dir:"ltr"},{code:"no",name:"Norwegian",script:"latin",dir:"ltr"},{code:"or-IN",name:"Odia",script:"indic",dir:"ltr"},{code:"pa-IN",name:"Punjabi",script:"indic",dir:"ltr"},{code:"pl",name:"Polish",script:"latin",dir:"ltr"},{code:"pt-BR",name:"Portuguese (Brazil)",script:"latin",dir:"ltr"},{code:"pt-PT",name:"Portuguese (Portugal)",script:"latin",dir:"ltr"},{code:"ro",name:"Romanian",script:"latin",dir:"ltr"},{code:"ru",name:"Russian",script:"cyrillic",dir:"ltr"},{code:"sk",name:"Slovak",script:"latin",dir:"ltr"},{code:"sl-SI",name:"Slovenian",script:"latin",dir:"ltr"},{code:"sv",name:"Swedish",script:"latin",dir:"ltr"},{code:"ta-IN",name:"Tamil",script:"indic",dir:"ltr"},{code:"te-IN",name:"Telugu",script:"indic",dir:"ltr"},{code:"th",name:"Thai",script:"thai",dir:"ltr"},{code:"tr",name:"Turkish",script:"latin",dir:"ltr"},{code:"uk",name:"Ukrainian",script:"cyrillic",dir:"ltr"},{code:"ur-PK",name:"Urdu",script:"rtl",dir:"rtl"},{code:"vi",name:"Vietnamese",script:"latin",dir:"ltr"},{code:"zh-Hans",name:"Chinese (Simplified)",script:"cjk",dir:"ltr"},{code:"zh-Hant",name:"Chinese (Traditional)",script:"cjk",dir:"ltr"}];Lt.map(e=>e.code);Object.fromEntries(Lt.map(e=>[e.code,e]));({...Se});function sr(e,t){t.material==="clay"?(e.setClay(t.clayTone==="custom"?`#${t.clayCustom}`:t.clayTone),e.setClearcoat(0)):(e.clayOff(),t.colorway==="custom"?e.setCustomColor(`#${t.customColor}`):e.setColorway(t.colorway),e.setFinish(R("finish",t.finish)),e.setClearcoat(R("clearcoat",t.clearcoat)))}function ir(e,t,n){e.screen.setGlare(!n&&t?e.device.environmentMap():null)}async function lr(e,t,n,o){const{stage:r,device:a,screen:s}=e,i=t.material==="clay";if(t.cameraPos?r.setCameraPosition(t.cameraPos):r.setAngle(t.angle),a.setRoll(R("roll",t.roll)),sr(a,t),s.restoreScreen(),r.setStudioLighting(t.lighting),a.setReflections(t.reflections),s.setFlatScreen(t.flatScreen),n){try{await s.applyWallpaper(n)}catch{}if(!o())return}ir(e,t.glare,i)}async function cr(e,t,n,o={}){const r=e.renderer,a=r.getPixelRatio(),s=r.getSize(new ln);r.setPixelRatio(1),r.setSize(t,n,!1);let i;o.cameraPosition?(i=new Qe(tt,t/n,.01,100),i.position.copy(o.cameraPosition),i.lookAt(0,0,0)):(i=e.camera.clone(),i.aspect=t/n,i.updateProjectionMatrix());try{const l=e.scene.background;e.scene.background=null;try{r.render(e.scene,i)}finally{e.scene.background=l}const d=document.createElement("canvas");let u=null;try{d.width=t,d.height=n,d.getContext("2d")?.drawImage(r.domElement,0,0);const h=dr(d),f=h.canvas;u=f;const p=f.width,c=f.height;return{bytes:await new Promise((m,b)=>{f.toBlob(y=>{if(!y){b(new Error("toBlob returned null"));return}y.arrayBuffer().then(w=>m(new Uint8Array(w)),b)},"image/png")}),width:p,height:c,frame:{width:t,height:n,cropX:h.x,cropY:h.y}}}finally{u&&u!==d&&(u.width=0,u.height=0),d.width=0,d.height=0}}finally{r.setPixelRatio(a),r.setSize(s.x,s.y,!1)}}function dr(e,t=2){const n=e.getContext("2d",{willReadFrequently:!0});if(!n)return{canvas:e,x:0,y:0};const{width:o,height:r}=e,a=n.getImageData(0,0,o,r).data;let s=o,i=r,l=-1,d=-1;for(let p=0;p<r;p++)for(let c=0;c<o;c++)a[(p*o+c)*4+3]!==0&&(c<s&&(s=c),c>l&&(l=c),p<i&&(i=p),p>d&&(d=p));if(l<s||d<i)return{canvas:e,x:0,y:0};s=Math.max(0,s-t),i=Math.max(0,i-t),l=Math.min(o-1,l+t),d=Math.min(r-1,d+t);const u=l-s+1,h=d-i+1,f=document.createElement("canvas");return f.width=u,f.height=h,f.getContext("2d")?.drawImage(e,s,i,u,h,0,0,u,h),{canvas:f,x:s,y:i}}const ur=1900,pr=2800;function fr(e,t,n){return{shots:e,panels:t,selectedShotId:n.selectedShotId,panelPresetId:n.panelPresetId,bgMode:n.bgMode,gradientStops:n.gradientStops,gradientAngle:n.gradientAngle,gradientFrom:n.gradientFrom,gradientTo:n.gradientTo,gradientDir:n.gradientDir,panelColors:n.panelColors,shadow:n.shadow,floorReflection:n.floorReflection,panelBackgrounds:n.panelBackgrounds,captionStyles:n.captionStyles,captionText:n.captionText,locales:n.locales,baseLocale:n.baseLocale,outputs:n.outputs}}async function hr(e,t,n,o,r,a){const s=U(n,o),i=s.filter(p=>p.bytes);if(i.length===0)return null;const l=G(r.panelPresetId),d=new Map(o.map((p,c)=>[p.id,c])),u=r.renderScale!=null&&r.renderScale>0&&r.renderScale<=1?r.renderScale:1,h=Math.max(1,Math.round(ur*u)),f=Math.max(1,Math.round(pr*u));{const p=[];for(let b=0;b<i.length;b++){const y=i[b];a(`Rendering ${b+1}/${i.length}${r.locale?` (${r.locale})`:""}…`),await lr(t,y.look,y.bytes,()=>!0);const w=await cr(e,h,f,{cameraPosition:bn(y.look.angle,y.look.cameraPos)});p.push({shotId:y.id,bytes:w.bytes,width:w.width,height:w.height,heightFrac:R("phoneHeight",y.look.phoneHeight)/100,hOffset:R("hOffset",y.look.hOffset)/100,vOffset:R("vOffset",y.look.vOffset)/100,panelIndex:d.get(y.panelId)??0,...y.look.clipToFrame===!0?{clipToFrame:!0}:{}})}const c=Kn(p,l.width,l.height),g=Yn(o,r);a("Saving project…");const m=Et(fr(s,o,r));return{panelWidth:l.width,panelHeight:l.height,count:o.length,background:g,phones:c,shadow:r.shadow,floorReflection:r.floorReflection??!1,project:JSON.stringify(m)}}}function ee(e){return/^[0-9a-fA-F]{3,8}$/.test(e)?`#${e}`:e}function be(e,t,n,o){const{x0:r,y0:a,x1:s,y1:i}=Rn(n,o,t.angle),l=e.createLinearGradient(r,a,s,i);for(const d of t.stops)l.addColorStop(Math.min(1,Math.max(0,d.at)),ee(d.color));return l}function He(e,t,n,o,r,a,s){const i=document.createElement("canvas");i.width=n,i.height=o;try{const l=i.getContext("2d");if(!l)return;l.fillStyle=be(l,t,n,o),l.fillRect(r,0,a,o),e.drawImage(i,r,0,a,o,s,0,a,o)}finally{i.width=0,i.height=0}}function Ge(e){return URL.createObjectURL(new Blob([e],{type:"image/png"}))}function Ue(e){return new Promise((t,n)=>{const o=new Image;o.onload=()=>t(o),o.onerror=n,o.src=e})}const mr=16*1024*1024;function $e(e){const t=(e.img.naturalWidth||0)*(e.img.naturalHeight||0),n=e.reflection?e.reflection.canvas.width*e.reflection.canvas.height:0;return t+n}function Mt(){let e=0;const t=Array.from(F.values());for(const r of t)e+=$e(r);const n=Array.from(F.keys());let o=0;for(;e>mr&&o<n.length-1;){const r=F.get(n[o]);r&&(e-=$e(r),r.reflection&&(r.reflection.canvas.width=0,r.reflection.canvas.height=0),F.delete(n[o])),o++}}const F=new Map,se=new Map;async function gr(e,t){if(!t){const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}}const n=F.get(e);if(n)return F.delete(e),F.set(e,n),n;const o=se.get(e);if(o)return o;const r=(async()=>{const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}})();se.set(e,r);try{const a=await r;return F.set(e,a),Mt(),a}finally{se.delete(e)}}async function yr(e,t,n){const o=t.panelWidth*t.count,r=t.panelHeight,a=n?.backgroundViewport,s=a?t.panelWidth*a.panelCount:o,i=a?t.panelWidth*a.panelIndex:0;e.width=o,e.height=r;const l=e.getContext("2d");if(!l)return;n?.transparentBg?l.clearRect(0,0,o,r):t.background.kind==="gradient"?a?He(l,t.background.gradient,s,r,i,o,0):(l.fillStyle=be(l,t.background.gradient,o,r),l.fillRect(0,0,o,r)):t.background.kind==="perPanel"?t.background.colors.forEach((f,p)=>{l.fillStyle=ee(f),l.fillRect(p*t.panelWidth,0,t.panelWidth,r)}):t.background.fills.forEach((f,p)=>{const c=p*t.panelWidth;f.kind==="solid"?(l.fillStyle=ee(f.color),l.fillRect(c,0,t.panelWidth,r)):a?He(l,f.gradient,s,r,i+c,t.panelWidth,c):(l.fillStyle=be(l,f.gradient,o,r),l.fillRect(c,0,t.panelWidth,r))});const d=n?.phoneRange?Math.max(0,n.phoneRange.start):0,u=n?.phoneRange?Math.min(t.phones.length,n.phoneRange.end):t.phones.length,h=n?.reuseDecodedPhones===!0;for(let f=d;f<u;f++){const p=t.phones[f],c=n?.clip==="panel"||n?.clip===void 0&&p.clipToFrame===!0;if(n?.skipShotId&&p.shotId===n.skipShotId)continue;const g=await gr(p.bytes,h),m=g.img;c&&(l.save(),l.beginPath(),l.rect(p.panelIndex*t.panelWidth,0,t.panelWidth,r),l.clip()),t.floorReflection&&br(l,g,p,h),l.save(),t.shadow&&(l.shadowColor="rgba(0,0,0,0.35)",l.shadowBlur=t.panelWidth*.03,l.shadowOffsetY=t.panelHeight*.012),l.drawImage(m,p.x,p.y,p.width,p.height),l.restore(),c&&l.restore()}}function br(e,t,n,o){const r=Math.round(n.height*.45);if(r<=0||n.width<=0)return;const a=o?t.reflection:void 0;if(a&&a.width===n.width&&a.height===n.height){e.drawImage(a.canvas,n.x,n.y+n.height,n.width,r);return}const s=t.img,i=document.createElement("canvas");i.width=Math.max(1,n.width),i.height=Math.max(1,r);const l=i.getContext("2d");if(!l)return;l.save(),l.translate(0,r),l.scale(1,-1),l.drawImage(s,0,r-n.height,n.width,n.height),l.restore();const d=l.createLinearGradient(0,0,0,r);d.addColorStop(0,"rgba(0,0,0,0.34)"),d.addColorStop(1,"rgba(0,0,0,0)"),l.globalCompositeOperation="destination-in",l.fillStyle=d,l.fillRect(0,0,n.width,r),e.drawImage(i,n.x,n.y+n.height,n.width,r),o&&(t.reflection={width:n.width,height:n.height,canvas:i},Mt())}const Ft=1.2,vr=.06,wr=.5;function Sr(e,t,n){if(!e)return[t];const o=t.split(/\s+/).filter(Boolean),r=[];let a="";for(const s of o){const i=a?`${a} ${s}`:s;a&&e.measureText(i).width>n?(r.push(a),a=s):a=i}return a&&r.push(a),r}function Ir(e,t){if(!e)return 0;let n=0;for(const o of t)n=Math.max(n,e.measureText(o).width);return n}function Rt(e,t,n,o){const r=t.map((c,g)=>{const m=c.text.trim();if(!m)return null;const b=c.sizePt*(o/te),y=st(c.fontId).family;e&&(e.font=`${J} ${b}px "${y}"`);const w=Sr(e,m,c.maxWidth*n),I=b*(c.lineHeight??Ft),A=w.length*I,C=Math.max(A,(c.reserveLines??w.length)*I);return{layer:c,index:g,lines:w,px:b,lineH:I,width:Ir(e,w),blockH:A,reservedH:C}}),a=r.filter(c=>c!==null&&j(c.layer.anchor)),s=a.filter(c=>c.layer.band==="bottom"),i=a.filter(c=>c.layer.band!=="bottom"),l=c=>c[0]?.layer.bandInset??vr,d=(1-l(s))*o-s.reduce((c,g)=>c+g.reservedH,0);let u=l(i)*o,h=d,f=0;const p=[];for(const c of r){if(!c)continue;if(!j(c.layer.anchor)){p.push({layer:c.layer,index:c.index,x:c.layer.anchor.x*n,yTop:c.layer.anchor.y*o,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width});continue}const g=c.layer.band==="bottom",b=(g?h:u)+(c.reservedH-c.blockH)/2,y=(c.layer.bandCenter??wr)*n;p.push({layer:c.layer,index:c.index,x:y,yTop:b,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width}),g?h+=c.reservedH:u+=c.reservedH,f=Math.max(f,b+c.blockH)}return{placed:p,bandBottom:f}}function xr(e,t,n,o){e.save(),e.textBaseline="top";const{placed:r}=Rt(e,t,n,o);for(const a of r){e.fillStyle=ee(a.layer.color),e.textAlign=a.layer.align,e.font=`${J} ${a.px}px "${st(a.layer.fontId).family}"`;let s=a.yTop;for(const i of a.lines)e.fillText(i,a.x,s),s+=a.lineH}e.restore()}function Ar(e,t){return e.sizePt*(t/te)*(e.lineHeight??Ft)}function Pr(e,t,n,o){return Rt(e,t,n,o).placed.map(r=>{const a=r.layer.align==="center"?r.x-r.width/2:r.layer.align==="right"?r.x-r.width:r.x;return{index:r.index,anchorX:r.x/n,top:r.yTop/o,left:a/n,width:r.width/n,height:r.lines.length*r.lineH/o}})}function Cr(e,t,n,o,r){t.forEach((a,s)=>{const i=r[a];!i||i.length===0||(e.save(),e.translate(s*n,0),xr(e,i,n,o),e.restore())})}const kr=""+new URL("Inter-SemiBold-B9yU462S.woff2",import.meta.url).href,Or=""+new URL("Manrope-SemiBold-44nuYf5T.woff2",import.meta.url).href,Tr=""+new URL("Poppins-SemiBold-DJVo7c07.woff2",import.meta.url).href,Er=""+new URL("Fraunces-SemiBold-BtraKtER.woff2",import.meta.url).href,Lr=""+new URL("SpaceGrotesk-SemiBold-CyO2J5Y0.woff2",import.meta.url).href,Mr={inter:kr,manrope:Or,poppins:Tr,fraunces:Er,"space-grotesk":Lr},Fr=X.map(e=>({...e,url:Mr[e.id]}));let V=null;function Rr(){return V||(V=(async()=>{const e=new Set;document.fonts.forEach(t=>e.add(`${t.family}/${t.weight}`)),await Promise.all(Fr.filter(t=>!e.has(`${t.family}/${String(J)}`)).map(async t=>{const n=new FontFace(t.family,`url(${t.url})`,{weight:String(J)});await n.load(),document.fonts.add(n)})),await document.fonts.ready})().catch(e=>{throw V=null,e})),V}const _r=22/te,Nr=1.1,qe=4,jr=.063,Br=.167,We=Math.round(_r*te),zr=.02,Dr=50;function D(e,t){const n=Math.max(e.x,t.x),o=Math.max(e.y,t.y),r=Math.min(e.x+e.width,t.x+t.width),a=Math.min(e.y+e.height,t.y+t.height);return r<=n||a<=o?null:{x:n,y:o,width:r-n,height:a-o}}function _t(e,t){return D(e,t)}function Ve(e,t){const n=Math.abs(e-t)%360;return n>180?360-n:n}function Ke(e){return gn(e.cameraPos??et(e.angle))}function Nt(e,t,n,o,r,a,s){if(t.length===0)return[];const i=[];for(const l of Pr(e,t,r,a)){const d=t[l.index];if(!d)continue;const u=_t({x:(o+l.left)*r,y:l.top*a,width:l.width*r,height:l.height*a},s);u&&i.push({ref:{panelId:n,panelIndex:o,layerIndex:l.index,band:j(d.anchor)},box:u,lineHeightPx:Ar(d,a)})}return i}function Hr(e,t,n,o){return n.height<=ve(e,t,o)}function ve(e,t,n){const o=e.y+e.height/2<=t.y+t.height/2;return n*(o?Br:jr)}function Gr(e,t,n){return n.height<=ve(e.box,t.box,e.lineHeightPx)+ve(t.box,e.box,t.lineHeightPx)}function Ur(e){const{ctx:t,panelIds:n,panelWidth:o,panelHeight:r}=e,a={x:0,y:0,width:o*n.length,height:r};t?.save();try{return $r(e,a)}finally{t?.restore()}}function $r(e,t){const{ctx:n,panelIds:o,panelWidth:r,panelHeight:a,captionLayers:s}=e,i=o.flatMap((c,g)=>Nt(n,s[c]??[],c,g,r,a,t)),l=[];for(const c of e.phones){const m=e.clip==="panel"||e.clip===void 0&&c.clipToFrame===!0?{x:c.panelIndex*r,y:0,width:r,height:a}:t,b=_t({x:c.x,y:c.y,width:c.width,height:c.height},m);b&&l.push({shotId:c.shotId,box:b,layoutHeight:c.height})}const d=[];for(let c=0;c<i.length;c++)for(let g=c+1;g<i.length;g++){const m=D(i[c].box,i[g].box);m&&d.push({a:i[c].ref,b:i[g].ref,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Gr(i[c],i[g],m)})}const u=[];for(const c of i)for(const g of l){const m=D(c.box,g.box);m&&u.push({caption:c.ref,shotId:g.shotId,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Hr(c.box,g.box,m,c.lineHeightPx)})}const h=[];for(let c=0;c<l.length;c++)for(let g=c+1;g<l.length;g++){const m=l[c],b=l[g],y=D(m.box,b.box);if(!y)continue;const w=[],I=Math.max(m.layoutHeight,b.layoutHeight),A=Math.min(m.layoutHeight,b.layoutHeight);A>0&&I/A>=Nr&&w.push("scale");const C=e.looks?.[m.shotId],v=e.looks?.[b.shotId],S=!!C&&!!v;C&&v&&(Ve(Ke(C),Ke(v))>=qe&&w.push("angle"),Ve(R("roll",C.roll),R("roll",v.roll))>=qe&&w.push("roll"));const P=Math.min(m.box.width*m.box.height,b.box.width*b.box.height);h.push({shotIdA:m.shotId,shotIdB:b.shotId,overlap:y,coverage:P>0?y.width*y.height/P:0,deliberate:w.length>0,signals:w,anglesKnown:S})}const f=new Set;for(const c of d)c.a.band&&f.add(c.a.panelId),c.b.band&&f.add(c.b.panelId);for(const c of u)c.caption.band&&f.add(c.caption.panelId);const p=[];return o.forEach((c,g)=>{if(!f.has(c))return;const m=qr(e,c,g,t,i,l);m&&p.push(m)}),{captionsMeasurable:n!=null,captionOverlaps:d,captionDeviceOverlaps:u,deviceOverlaps:h,suggestions:p,hasWarning:d.length>0||u.length>0||h.some(c=>!c.deliberate)}}function qr(e,t,n,o,r,a){const s=e.captionLayers[t]??[],i=s.flatMap((u,h)=>j(u.anchor)&&u.text.trim()?[h]:[]);if(i.length===0||!(Math.min(...i.map(u=>s[u].sizePt))>We))return null;const d=[...a.map(u=>u.box),...r.filter(u=>!(u.ref.panelId===t&&u.ref.band)).map(u=>u.box)];for(let u=1;u<=Dr;u++){const h=1-u*zr,f=new Map(i.map(m=>[m,Math.floor(s[m].sizePt*h)]));if(Math.min(...f.values())<We)return null;const p=s.map((m,b)=>{const y=f.get(b);return y===void 0?m:{...m,sizePt:y}});if(Nt(e.ctx,p,t,n,e.panelWidth,e.panelHeight,o).filter(m=>m.ref.band).every(m=>!d.some(b=>D(m.box,b))))return{panelId:t,scale:h,layers:i.map(m=>({layerIndex:m,fromSizePt:s[m].sizePt,toSizePt:f.get(m)}))}}return null}function jt(e){return`build-shot-${e+1}`}function Bt(e){const t=[];for(const n of e.shots)t.includes(n.panelId)||t.push(n.panelId);return t}const Wr=1900,Vr=2800;function Kr(e){return new Promise((t,n)=>{e.toBlob(o=>{if(!o)return n(new Error("canvas toBlob returned null"));o.arrayBuffer().then(r=>t(new Uint8Array(r)),n)},"image/png")})}async function zt(e,t,n,o={}){await Rr();const r=Bt(e),a=r.map(f=>({id:f})),s=e.shots.map((f,p)=>({id:jt(p),panelId:f.panelId,frameNodeId:"",frameName:`screen-${p+1}`,bytes:t.screens[p]??null,look:f.look,thumb:null,thumbStale:!1})),l={panelPresetId:G(e.panelPresetId).id,bgMode:e.bgMode,gradientStops:e.gradientStops,gradientAngle:e.gradientAngle,gradientFrom:e.gradientFrom,gradientTo:e.gradientTo,gradientDir:e.gradientDir,panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection,panelBackgrounds:e.panelBackgrounds,selectedShotId:null,renderScale:o.renderScale,locale:n},d=o.onProgress??(()=>{});let u=o.refs,h=null;if(!u){const f=Pn(e.panelPresetId);h=nt(Wr,Vr);const p=await rt(h,f);u={stage:h,device:new ot(p,h.scene,h.renderer,f),screen:new at(p,f)}}try{const f=await hr(u.stage,u,s,a,l,d);if(!f)throw new Error("buildStrip: nothing renderable (no shots)");const p=document.createElement("canvas");try{const c={...o.clip?{clip:o.clip}:{},...o.backgroundViewport?{backgroundViewport:o.backgroundViewport}:{}};await yr(p,f,Object.keys(c).length>0?c:void 0);const g=f.panelWidth,m=f.panelHeight,b=p.getContext("2d");if(!b)throw new Error("buildStrip: 2D context unavailable");Cr(b,r,g,m,t.captionText??{});const y=Ur({phones:f.phones,panelIds:r,captionLayers:t.captionText??{},panelWidth:g,panelHeight:m,ctx:b,looks:Object.fromEntries(s.map(A=>[A.id,A.look])),clip:o.clip}),w=o.renderScale&&o.renderScale>0&&o.renderScale<1?o.renderScale:1,I=[];for(let A=0;A<f.count;A++){const C=document.createElement("canvas");C.width=Math.max(1,Math.round(g*w)),C.height=Math.max(1,Math.round(m*w));const v=C.width,S=C.height;try{const P=C.getContext("2d");if(!P)throw new Error("buildStrip: 2D context unavailable");P.drawImage(p,A*g,0,g,m,0,0,v,S),I.push({index:A,bytes:await Kr(C),width:v,height:S})}finally{C.width=0,C.height=0}}return{panels:I,layout:y}}finally{p.width=0,p.height=0}}finally{h&&h.dispose()}}const Ye=.005,Yr=.02,Xr=12;function ie(e){return Math.max(1,Math.round(e*100))}function le(e){return`${e.kind}:${e.frames.join(",")}:${e.anchored}`}function Jr(e,t){const n=new Map(e.suggestions.map(a=>[a.panelId,a.scale])),o=[];for(const a of e.captionOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const[s,i]=a.a.band===a.b.band?[a.a,a.b]:a.a.band?[a.a,a.b]:[a.b,a.a],l=ce([t.frameOfPanel(s.panelId),t.frameOfPanel(i.panelId)]);l.length!==0&&o.push({kind:"caption-caption",frames:l,depthPct:ie(a.depthFrac),anchored:!a.a.band||!a.b.band,...Xe(n,s.band?s.panelId:null)})}for(const a of e.captionDeviceOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const s=ce([t.frameOfPanel(a.caption.panelId),t.frameOfShot(a.shotId)]);s.length!==0&&o.push({kind:"caption-device",frames:s,depthPct:ie(a.depthFrac),anchored:!a.caption.band,...Xe(n,a.caption.band?a.caption.panelId:null)})}for(const a of e.deviceOverlaps){if(a.deliberate||a.coverage<Yr)continue;const s=ce([t.frameOfShot(a.shotIdA),t.frameOfShot(a.shotIdB)]);s.length!==0&&o.push({kind:"device-device",frames:s,depthPct:ie(a.coverage),anchored:!1})}const r=new Map;for(const a of o){const s=r.get(le(a));s?a.depthPct>s.depthPct?r.set(le(a),{...a,clearAtScale:a.clearAtScale??s.clearAtScale}):s.clearAtScale==null&&a.clearAtScale!=null&&(s.clearAtScale=a.clearAtScale):r.set(le(a),a)}return[...r.values()].sort((a,s)=>s.depthPct-a.depthPct).slice(0,Xr).sort((a,s)=>a.frames[0]-s.frames[0]||a.kind.localeCompare(s.kind))}function ce(e){const[t,...n]=e;return t<0?[]:[t,...n.filter(o=>o>=0&&o!==t)]}function Xe(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtScale:Math.round(n*100)/100}}function Zr(e,t){if(t.length===0)return e;const n=e.captionStyles;if(!n)return e;const o={};for(const[r,a]of Object.entries(n))o[r]=a.map(s=>({...s}));for(const r of t){const a=o[r.panelId];if(a)for(const s of r.layers){const i=a[s.layerIndex];i&&(i.sizePt=s.toSizePt)}}return{...e,captionStyles:o}}function de(e){return e.frames.map(t=>`frame ${t+1}`).join(" + ")}function Qr(e,t,n){if(!e.captionsMeasurable)return null;const o=Jr(e,t);if(o.length===0)return null;const r=o.filter(d=>d.kind==="device-device"),a=o.filter(d=>d.kind!=="device-device"&&d.clearAtScale==null);if(r.length>0||a.length>0||!n){const d=[];r.length>0&&d.push(`${r.map(de).join(", ")}: two devices overlap without reading as composed. There is no deterministic repair — give the pair a real difference in scale, camera angle or roll (that is what makes an overlap read as deliberate), or leave it if it already is.`);for(const u of a)d.push(u.anchored?`${de(u)}: the caption is anchored by hand, so it is reported and never rewritten — move it, or drop its \`anchor\` to let the band place it.`:`${de(u)}: no caption size above the legibility floor clears it — shorten the headline, or pick a layout with a bigger text region (\`style.layout: "bleed"\`).`);return d.length===0&&d.push("The rendered look was not available here, so no corrected look could be built."),{findings:o,note:`No corrected look is offered. ${d.join(" ")}`}}const s=new Set(o.filter(d=>d.kind!=="device-device").map(d=>d.frames[0])),i=e.suggestions.filter(d=>s.has(t.frameOfPanel(d.panelId))),l=i.flatMap(d=>{const u=t.frameOfPanel(d.panelId)+1;return d.layers.map(h=>`frame ${u} layer ${h.layerIndex+1}: sizePt ${h.fromSizePt} → ${h.toSizePt}`)}).join("; ");return{findings:o,correctedLook:Zr(n,i),note:`\`layout.correctedLook\` is this same look with every caption shrunk to the largest size that was verified to clear the overlap (${l}). Nothing here is applied for you — re-render with it, or show the user the finding and let them decide. On render_strip pass it back as \`look\` together with the SAME \`style.captions\` you just sent: the words come from \`style.captions\` and every style field you leave out of them now comes from this look. The one thing that still beats it is a field you set EXPLICITLY on a caption entry, so if your \`style.captions\` pin \`sizePt\` at the original numbers, drop them or set them to the sizes above.`}}function eo(e){return String(Math.max(-100,Math.min(100,e*35)))}const to=["roll","phoneHeight","hOffset","vOffset","finish","clearcoat"];function Je(e){if(!e||typeof e!="object")return{};const t={...e};for(const n of to)typeof t[n]=="number"&&(t[n]=String(t[n]));return t}function no(e){const{subtitle:t,sizePt:n,maxWidth:o,...r}=e;return{...r,...n!==void 0?{sizePt:Number(n)}:{},...o!==void 0?{maxWidth:Number(o)}:{}}}function Ze(e){const t={};for(const[n,o]of Object.entries(e))o!==void 0&&(t[n]=o);return t}function ue(e,t,n){const o={};return e&&t.forEach((r,a)=>{const s=n[a];s!==void 0&&e[r]!==void 0&&(o[s]=e[r])}),o}function ro(e){const t=e.slotSizes.filter(v=>Number.isInteger(v)&&v>0),n=t.map((v,S)=>`panel-${S+1}`);let o={};const r=e.look&&typeof e.look=="object"?e.look:null,a=e.style??null,s=r&&Array.isArray(r.shots)?r.shots:[],i=s.length>0,l=a?Je(a.shotLook):null,d=[];let u=0;t.forEach((v,S)=>{for(let P=0;P<v;P++){const k=i?Je(s[u]?.look??s[0]?.look??{}):l??{},$=k.hOffset!==void 0&&k.hOffset!==null?String(k.hOffset):eo(P);d.push({panelId:n[S],look:{...x,...k,hOffset:$}}),u++}});let h={},f={},p={},c="gradient",g=On,m=Tn,b="vertical",y,w,I=!0,A=!1;if(r){c=r.bgMode==="perPanel"?"perPanel":"gradient";const v=Q({gradientStops:Array.isArray(r.gradientStops)?r.gradientStops:void 0,gradientAngle:typeof r.gradientAngle=="number"?r.gradientAngle:void 0,gradientFrom:typeof r.gradientFrom=="string"?r.gradientFrom:g,gradientTo:typeof r.gradientTo=="string"?r.gradientTo:m,gradientDir:r.gradientDir==="horizontal"?"horizontal":b});y=v.stops,w=v.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(v),r.shadow===!1&&(I=!1),r.floorReflection===!0&&(A=!0);const S=[];for(const P of s){const k=typeof P?.panelId=="string"?P.panelId:null;k&&!S.includes(k)&&S.push(k)}h=ue(r.panelColors,S,n),f=ue(xt(r.captionStyles),S,n),p=ue(wt(r.panelBackgrounds),S,n)}else if(a){const v=a.background??{};c=v.mode==="perPanel"?"perPanel":"gradient";const S=Q({gradientStops:v.stops,gradientAngle:v.angle,gradientFrom:g,gradientTo:m,gradientDir:b});y=S.stops,w=S.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(S),v.shadow===!1&&(I=!1),v.floorReflection===!0&&(A=!0),Array.isArray(v.panelColors)&&v.panelColors.forEach((P,k)=>{typeof P=="string"&&P&&n[k]!==void 0&&(h[n[k]]=P)})}if(a&&Array.isArray(a.captions)){const v=f,S=(M,oe)=>({...v[M]?.[oe]??{}}),P={},k={};a.captions.forEach((M,oe)=>{const _=n[oe];if(!(_===void 0||M===null||M===void 0)){if(Array.isArray(M)){const N=M.filter(E=>!!E&&typeof E=="object"&&typeof E.text=="string"&&E.text.trim()!=="").map((E,q)=>({...S(_,q),...Ze(no(E))}));N.length>0&&(k[_]=N)}else if(typeof M=="object"){const{text:N,subtitle:E,sizePt:q,maxWidth:Pe,...$t}=M;P[_]={...S(_,0),...Ze({...$t,...q!==void 0?{sizePt:Number(q)}:{},...Pe!==void 0?{maxWidth:Number(Pe)}:{}})},typeof N=="string"&&N.trim()&&(k[_]={headline:N,...typeof E=="string"&&E?{subtitle:E}:{}})}}});const $=re({[L]:k},It(P));f=Pt($),o=At($)}return{look:{panelPresetId:e.panelPresetId??(typeof r?.panelPresetId=="string"?r.panelPresetId:void 0)??"r69",bgMode:c,gradientStops:y,gradientAngle:w,gradientFrom:g,gradientTo:m,gradientDir:b,panelColors:h,shadow:I,floorReflection:A,panelBackgrounds:p,captionStyles:f,shots:d},panelIds:n,captionText:o}}function oo(e){const t=e?.trim();return t?/\.(png|jpe?g|webp)$/i.test(t)?t:`${t}.png`:null}function ao(e,t){const n=new Map;return e.map((o,r)=>{const a=oo(t?.[r]);if(!a)return`screen-${r+1}.png`;const s=n.get(a)??0;if(n.set(a,s+1),s===0)return a;const i=a.lastIndexOf(".");return`${a.slice(0,i)}-${s+1}${a.slice(i)}`})}function so(e){const{look:t,panelIds:n,captionText:o,locale:r,shotNames:a,outputs:s}=e,i=n.map(u=>({id:u})),l=ao(t.shots,a),d=t.shots.map((u,h)=>({id:`shot-${h+1}`,panelId:u.panelId,frameNodeId:l[h],frameName:l[h],bytes:null,look:u.look,thumb:null,thumbStale:!0}));return Et({shots:d,panels:i,selectedShotId:d[0]?.id??null,panelPresetId:t.panelPresetId,bgMode:t.bgMode,gradientStops:t.gradientStops,gradientAngle:t.gradientAngle,gradientFrom:t.gradientFrom,gradientTo:t.gradientTo,gradientDir:t.gradientDir,panelColors:t.panelColors,shadow:t.shadow,floorReflection:t.floorReflection,panelBackgrounds:t.panelBackgrounds,captionStyles:t.captionStyles,outputs:s,captionText:o&&Object.keys(o).length>0?{[r||L]:o}:{}})}function T(e){console.log("[render]",e)}const io=1900,lo=2800;let pe=null;async function Dt(){return pe||(pe=(async()=>{T("booting headless stage + loading model (once)…");const e=nt(io,lo),t=await Sn(e);return{stage:e,device:new ot(t,e.scene,e.renderer),screen:new at(t)}})()),pe}function Ht(e){const t=atob(e),n=new Uint8Array(t.length);for(let o=0;o<t.length;o++)n[o]=t.charCodeAt(o);return n}function Ae(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function co(e){return new Promise((t,n)=>{e.toBlob(async o=>{if(!o)return n(new Error("toBlob null"));t(Ae(new Uint8Array(await o.arrayBuffer())))},"image/png")})}function Gt(e,t){const n=Bt(t),o=new Map(t.shots.map((a,s)=>[jt(s),n.indexOf(a.panelId)])),r=Qr(e.layout,{frameOfPanel:a=>n.indexOf(a),frameOfShot:a=>o.get(a)??-1},t);if(r)return{layout:{findings:r.findings,...r.correctedLook?{correctedLook:r.correctedLook}:{}},layoutNote:r.note}}async function Ut(e){const t=await Promise.all(e.map(a=>createImageBitmap(new Blob([new Uint8Array(a.bytes)],{type:"image/png"})))),n=document.createElement("canvas");n.width=t.reduce((a,s)=>a+s.width,0),n.height=t[0]?.height??1;const o=n.getContext("2d");let r=0;for(const a of t)o.drawImage(a,r,0),r+=a.width,a.close();return{base64:await co(n),width:n.width,height:n.height}}window.renderStrip=async function(t){try{if(!t||!Array.isArray(t.screenshots)||t.screenshots.length===0)throw new Error("renderStrip: input.screenshots must be a non-empty nested array of slots");const n=await Dt(),o=t.screenshots.map(I=>Array.isArray(I)?I:[I]),{look:r,panelIds:a,captionText:s}=ro({slotSizes:o.map(I=>I.length),panelPresetId:t.panelPresetId,look:t.look??void 0,style:t.style??void 0}),i=o.flat().map(Ht),l=Array.isArray(t.captionLayers)&&a[0]?{[a[0]]:t.captionLayers}:s,d=t.preview?.25:1;T(`buildStrip (${o.length} panel${o.length===1?"":"s"}${t.locale?`, ${t.locale}`:""})…`);const u=performance.now(),h=await zt(r,{screens:i,captionText:l},t.locale,{refs:n,renderScale:d,onProgress:I=>T(" "+I),...t.clip?{clip:t.clip}:{},...t.backgroundViewport?{backgroundViewport:t.backgroundViewport}:{}});T("encoding panels + stitching the strip preview…");const f=h.panels.map(I=>Ae(I.bytes)),p=await Ut(h.panels),c=G(r.panelPresetId),g=Math.round(performance.now()-u),m=Gt(h,r),b=h.layout.captionsMeasurable,y=Array.isArray(t.screenshotNames)?t.screenshotNames.flat():void 0,w=t.buildRecord?so({look:r,panelIds:a,captionText:s,locale:t.locale,shotNames:y,outputs:t.outputs}):void 0;return T(`done: ${h.panels.length} panels, strip ${p.width}x${p.height}, ${g}ms`),{ok:!0,ms:g,count:h.panels.length,panelPresetId:c.id,panelWidth:c.width,panelHeight:c.height,stripWidth:p.width,stripHeight:p.height,panels:f,stripBase64:p.base64,preview:d<1,...w?{projectRecord:w}:{},...m??{},layoutMeasured:b}}catch(n){const o=n&&n.stack?n.stack:String(n);return T("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.renderProject=async function(t){try{if(!t||t.projectFile==null||typeof t.projectFile!="object")throw new Error("renderProject: input.projectFile (the saved ProjectFile) is required");const n=Qn(JSON.stringify(t.projectFile));if(!n)throw new Error("renderProject: the saved project could not be restored (wrong schema or malformed record)");const o=await Dt(),r=t.locale||n.baseLocale||n.locales?.[0]||"en-US",a=U(n.shots,n.panels),s=me(t.variant||{},n.baseLocale),i=new Map((t.screenManifest||[]).map(v=>[`${v.shotId} ${je(me(v.variant||{},n.baseLocale))}`,v])),l=(v,S)=>i.get(`${v} ${je(S)}`),d=a.map(v=>qn(l,v.id,s,Un,n.baseLocale)),u=a.map((v,S)=>({frameName:v.frameName,matched:d[S].value!==void 0,requestedVariant:s,resolvedAt:d[S].resolvedAt,inherited:d[S].inherited,...d[S].value?.source?{source:d[S].value.source}:{}})),h=d.map(({value:v})=>v?.bytes?Ht(v.bytes):null),f=At(n.captionText,r,n.baseLocale),p=tr({...n,selectedShotId:null,panelPresetId:t.panelPresetId??n.panelPresetId,captionText:{[r]:f}}),c=t.preview?.25:1;T(`renderProject (${a.length} shot${a.length===1?"":"s"}, ${r})…`);const g=performance.now(),m=await zt(p,{screens:h,captionText:f},r,{refs:o,renderScale:c,onProgress:v=>T(" "+v)});T("encoding panels + stitching the strip preview…");const b=m.panels.map(v=>Ae(v.bytes)),y=await Ut(m.panels),w=G(p.panelPresetId),I=Math.round(performance.now()-g),A=Gt(m,p),C=m.layout.captionsMeasurable;return T(`done: ${m.panels.length} panels, strip ${y.width}x${y.height}, ${I}ms`),{ok:!0,ms:I,count:m.panels.length,panelPresetId:w.id,panelWidth:w.width,panelHeight:w.height,stripWidth:y.width,stripHeight:y.height,panels:b,stripBase64:y.base64,preview:c<1,shots:u,locale:r,...A??{},layoutMeasured:C}}catch(n){const o=n&&n.stack?n.stack:String(n);return T("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.__RENDER_READY=!0;T("harness ready ✓");
|
package/harness-dist/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<title>shotops-mcp headless render harness</title>
|
|
6
|
-
<script type="module" crossorigin src="./assets/index-
|
|
6
|
+
<script type="module" crossorigin src="./assets/index-MiEp7Ev8.js"></script>
|
|
7
7
|
<link rel="modulepreload" crossorigin href="./assets/three-webgl-DCw804Ic.js">
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/three-addons-CvX38LO6.js">
|
|
9
9
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shotops-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The bundle-emitting MCP server over the @engine/@sync spine. Exposes ShotOps tools to ChatGPT, Claude Code, Cursor, and CI over Streamable HTTP with OAuth or personal API tokens. Hosted and MCP tool paths never accept, store, or forward a store-signing credential; the explicit local release CLI validates the user's existing key directly with Apple and records only its path. Renders via headless Playwright Chromium, the engine's native non-browser habitat (same path as mockup-mcp). Also ships as a free LOCAL stdio server (`npx shotops-mcp`) — same render, on your own machine, no account needed.",
|
|
@@ -60,5 +60,12 @@
|
|
|
60
60
|
"rollup": "^4.62.5",
|
|
61
61
|
"typescript": "^5.6.0",
|
|
62
62
|
"vite": "^7.3.6"
|
|
63
|
+
},
|
|
64
|
+
"allowScripts": {
|
|
65
|
+
"@swc/core@1.15.3": true,
|
|
66
|
+
"cbor-extract@2.2.2": true,
|
|
67
|
+
"esbuild@0.28.1": true,
|
|
68
|
+
"fsevents@2.3.2": true,
|
|
69
|
+
"fsevents@2.3.3": true
|
|
63
70
|
}
|
|
64
71
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{S as Wt,G as Vt,A as Kt,D as ae,M as qt,R as Yt,a as Xt,C as Jt,b as Zt,c as Qt}from"./three-addons-CvX38LO6.js";import{aa as en,P as Qe,b as tn,V as q,a4 as nn,C as k,ab as rn,J as Oe,$ as V,M as on,a2 as an,B as sn,l as ln}from"./three-webgl-DCw804Ic.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();const cn=""+new URL("iphone-optimized-BdcJzlHi.glb",import.meta.url).href,dn="data:model/gltf-binary;base64,Z2xURgIAAADUAAAAwAAAAEpTT057ImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJTaG90T3BzIHBsYWNlaG9sZGVyIOKAlCB0aGUgUGl4ZWwgbW9kZWwgaXMgZGVsaWJlcmF0ZWx5IG5vdCBidW5kbGVkIGluIHRoZSBucG0gcGFja2FnZSJ9LCJzY2VuZXMiOlt7Im5hbWUiOiJwaXhlbC1ub3QtYnVuZGxlZCIsIm5vZGVzIjpbXX1dLCJzY2VuZSI6MH0gICA=",un=[{id:"grey",label:"Grey",color:11579568},{id:"white",label:"White",color:15263458},{id:"charcoal",label:"Charcoal",color:3815996}],fn=[{id:"silver",label:"Silver",frame:16119285,back:16514043,accent:13816532},{id:"blue",label:"Deep Blue",frame:3290954,back:4409953,accent:2172211},{id:"orange",label:"Cosmic Orange",frame:16219693,back:16487504,accent:12738072}],hn=[{id:"silver",label:"Moonstone",frame:9345186,back:8621205,accent:7041658},{id:"blue",label:"Obsidian",frame:2763308,back:2565928,accent:2105377},{id:"orange",label:"Porcelain",frame:15920872,back:14670805,accent:11973550},{id:"green",label:"Jade",frame:9412240,back:8688005,accent:7042924}],Y={id:"iphone-17-pro",glb:cn,fitSize:3.6,baseEuler:{x:0,y:0,z:0},pruneNamePrefixes:[],screen:{material:"OLED",repeat:{x:-1,y:1},coverFill:1.001,coverCornerRatio:.18,coverRepeat:{x:-1,y:-1}},body:{frame:["Anodized aluminum"],back:["Frosted glass"],accent:["Plastic antena","PaletteMaterial001"]},colorways:fn},Te={id:"pixel-10-pro",glb:dn,fitSize:3.6,baseEuler:{x:0,y:Math.PI,z:0},pruneNamePrefixes:["Google Pixel 10 Pro Moonstone.1"],screen:{material:"GP10p_display",repeat:{x:1,y:1},coverFill:1.001,coverCornerRatio:.14,coverRepeat:{x:1,y:-1}},body:{frame:["GP10p_mid","GP10p_camera"],back:["GP10p_back","GP10p_plastic"],accent:["GP10p_logo"]},colorways:hn},pn={[Y.id]:Y,[Te.id]:Te};function ve(e){return e&&pn[e]||Y}const mn=7,ke={front:{x:0,y:0,z:-1},left:{x:-2.6,y:.6,z:-5.2},right:{x:2.6,y:.6,z:-5.2}};function et(e){const t=ke[e]??ke.front,n=Math.hypot(t.x,t.y,t.z),o=mn/n;return{x:t.x*o,y:t.y*o,z:t.z*o}}function gn(e){return Math.atan2(e.x,-e.z)*180/Math.PI}function he(e){const t=et(e);return new q(t.x,t.y,t.z)}const yn=1.4;function bn(e,t){return(t?new q(t.x,t.y,t.z):he(e)).multiplyScalar(yn)}const tt=35;function Sn(e){e.traverse(t=>{const n=t;n.geometry?.dispose();const o=n.material;Array.isArray(o)?o.forEach(Ce):o&&Ce(o)}),e.clear()}function Ce(e){for(const t of Object.values(e))t&&typeof t=="object"&&t.isTexture&&t.dispose();e.dispose()}function nt(e,t){const n=document.createElement("canvas"),o=new en({canvas:n,antialias:!0,preserveDrawingBuffer:!0,alpha:!0});o.setSize(e,t,!1),o.shadowMap.enabled=!0;const r=new Wt;r.background=null;const{ambient:a,directionals:i}=vn(r),s=new Qe(tt,e/t,.01,100);s.position.copy(he("front")),s.lookAt(0,0,0);function l(f){s.position.copy(he(f)),s.lookAt(0,0,0)}function d(f){s.position.set(f.x,f.y,f.z),s.lookAt(0,0,0)}function u(f){i.forEach(c=>c.visible=f),a.intensity=f?.75:1.6}let p=!1;function h(){p||(p=!0,r.environment?.dispose(),r.environment=null,Sn(r),o.dispose(),o.forceContextLoss())}return{scene:r,camera:s,renderer:o,dispose:h,setAngle:l,setCameraPosition:d,getCameraPosition:()=>({x:s.position.x,y:s.position.y,z:s.position.z}),onUserPose:()=>{},previewOrbit:()=>{},commitOrbit:()=>{},setOrbitEnabled:()=>{},setBackground:()=>{},setStudioLighting:u,resize:()=>{}}}function vn(e){const t=new Kt(16777215,.75);e.add(t);const n=new ae(16777215,2.2);n.position.set(3,5,5);const o=new ae(12571903,.7);o.position.set(-4,1,2);const r=new ae(16777215,1);r.position.set(0,3,-5);const a=[n,o,r];return a.forEach(i=>e.add(i)),{ambient:t,directionals:a}}function rt(e,t){const n=ve(t);return new Promise((o,r)=>{new Vt().load(n.glb,a=>{const i=a.scene;if(n.pruneNamePrefixes.length){const h=[];i.traverse(f=>{n.pruneNamePrefixes.some(c=>f.name.startsWith(c))&&h.push(f)}),h.forEach(f=>f.removeFromParent())}i.rotation.set(n.baseEuler.x,n.baseEuler.y,n.baseEuler.z),i.updateMatrixWorld(!0);const s=new tn().setFromObject(i),l=s.getSize(new q),d=s.getCenter(new q),u=n.fitSize/Math.max(l.x,l.y,l.z);i.scale.multiplyScalar(u),i.position.copy(d.multiplyScalar(-u));const p=new nn;p.add(i),e.scene.add(p),e.setAngle("front"),o(p)},void 0,r)})}function wn(e){return rt(e)}Y.colorways;class ot{frame=[];back=[];accent=[];allBody=[];model;scene;renderer;profile;envTexture=null;constructor(t,n,o,r){this.model=t,this.scene=n,this.renderer=o,this.profile=ve(r);const{frame:a,back:i,accent:s}=this.profile.body;t.traverse(l=>{const d=l;if(!d.isMesh)return;(Array.isArray(d.material)?d.material:[d.material]).forEach(p=>{const h=p;!h||!h.name||(a.includes(h.name)?this.push(this.frame,h):i.includes(h.name)?this.push(this.back,h):s.includes(h.name)&&this.push(this.accent,h),this.push(this.allBody,h))})})}push(t,n){t.includes(n)||t.push(n)}get finishMaterials(){return[...this.frame,...this.back]}paint(t,n){const o=n instanceof k?n:new k(n);t.forEach(r=>{r.color=o.clone(),r.needsUpdate=!0})}setColorway(t){if(t==="custom")return;const n=this.profile.colorways.find(o=>o.id===t)??this.profile.colorways[0];n&&(this.paint(this.frame,n.frame),this.paint(this.back,n.back),this.paint(this.accent,n.accent))}setCustomColor(t){const n=new k(t),o=n.clone().lerp(new k(16777215),.2),r=n.clone().multiplyScalar(.6);this.paint(this.frame,n),this.paint(this.back,o),this.paint(this.accent,r)}setFinish(t){const n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0;this.finishMaterials.forEach(o=>{o.userData.baseRoughness===void 0&&(o.userData.baseRoughness=o.roughness);const r=o.userData.baseRoughness;o.roughness=r*(1-n),o.needsUpdate=!0})}clearcoatUpgraded=!1;setClearcoat(t){const n=Math.max(0,Math.min(1,t));n>0&&!this.clearcoatUpgraded&&this.upgradeFinishToPhysical(),this.clearcoatUpgraded&&this.finishMaterials.forEach(o=>{const r=o;r.clearcoat=n,r.clearcoatRoughness=.1,r.needsUpdate=!0})}upgradeFinishToPhysical(){this.clearcoatUpgraded=!0;const t=new Map;for(const o of Array.from(new Set(this.finishMaterials))){const r=new qt;r.color.copy(o.color),r.metalness=o.metalness,r.roughness=o.roughness,r.map=o.map,r.normalMap=o.normalMap,r.normalScale.copy(o.normalScale),r.roughnessMap=o.roughnessMap,r.metalnessMap=o.metalnessMap,r.aoMap=o.aoMap,r.aoMapIntensity=o.aoMapIntensity,r.emissive.copy(o.emissive),r.emissiveMap=o.emissiveMap,r.emissiveIntensity=o.emissiveIntensity,r.envMapIntensity=o.envMapIntensity,r.transparent=o.transparent,r.opacity=o.opacity,r.alphaTest=o.alphaTest,r.side=o.side,r.flatShading=o.flatShading,r.vertexColors=o.vertexColors,r.name=o.name,r.userData={...o.userData},r.clearcoat=0,t.set(o,r)}this.model.traverse(o=>{const r=o;if(r.isMesh)if(Array.isArray(r.material))r.material=r.material.map(a=>t.get(a)??a);else{const a=t.get(r.material);a&&(r.material=a)}});const n=o=>o.map(r=>t.get(r)??r);this.frame=n(this.frame),this.back=n(this.back),this.allBody=n(this.allBody)}environmentMap(){if(!this.envTexture){const t=new rn(this.renderer);this.envTexture=t.fromScene(new Yt,.04).texture,t.dispose()}return this.envTexture}setClay(t){const n=un.find(r=>r.id===t),o=n?new k(n.color):new k(t);this.allBody.forEach(r=>{r.userData.clayStashedMap===void 0&&(r.userData.clayStashedMap=r.map),r.color=o.clone(),r.metalness=0,r.roughness=.85,r.map=null,r.needsUpdate=!0})}clayOff(){this.allBody.forEach(t=>{t.userData.clayStashedMap!==void 0&&(t.map=t.userData.clayStashedMap,t.userData.clayStashedMap=void 0,t.needsUpdate=!0)})}setReflections(t){this.scene.environment=t?this.environmentMap():null}setRoll(t){this.model.rotation.z=t*Math.PI/180}}const Ee=1.35;class at{materials=[];glassOverlays=[];screenMesh=null;flatPlane=null;texture=null;appliedBytes=null;flatScreenOn=!1;profile;constructor(t,n){this.profile=ve(n);const o=this.profile.screen.material;t.traverse(r=>{const a=r;if(!a.isMesh)return;(Array.isArray(a.material)?a.material:[a.material]).forEach(s=>{const l=s;l&&l.name===o&&!this.materials.includes(l)&&(this.materials.push(l),this.screenMesh=a)})}),this.dimGlassOverlay(t),this.prepareScreen(),this.setGlare(null)}get found(){return this.materials.length>0}setGlare(t){this.materials.forEach(n=>{n.envMap=t,n.envMapIntensity=t?.6:0,n.metalness=t?.5:0,n.roughness=t?.15:1,n.needsUpdate=!0}),this.glassOverlays.forEach(n=>{n.opacity=t?.08:0,n.needsUpdate=!0})}setFlatScreen(t){if(this.flatScreenOn=t,!this.screenMesh)return;if(this.flatPlane){this.screenMesh.remove(this.flatPlane);const S=this.flatPlane.material;S.map?.dispose(),S.dispose(),this.flatPlane.geometry.dispose(),this.flatPlane=null}if(!t||!this.texture)return;this.screenMesh.geometry.computeBoundingBox();const n=this.screenMesh.geometry.boundingBox;if(!n)return;const o=n.max.x-n.min.x,r=n.max.y-n.min.y,a=n.max.z-n.min.z,i=(n.max.x+n.min.x)/2,s=(n.max.y+n.min.y)/2,l=(n.max.z+n.min.z)/2,d=r<=o&&r<=a?"y":a<=o&&a<=r?"z":"x",u=this.texture.clone();u.needsUpdate=!0,u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V;const{coverFill:p,coverCornerRatio:h,coverRepeat:f}=this.profile.screen;u.repeat.set(f.x,f.y);const c=d==="x"?a:o,g=d==="z"||d==="x"?r:a,m=Math.min(c,g)*p*h,b=In(c*p,g*p,m),y=new on(b,new Xt({map:u,emissive:new k(16777215),emissiveMap:u,emissiveIntensity:Ee,color:new k(0),toneMapped:!1,side:an,depthTest:!0,depthWrite:!0,polygonOffset:!0,polygonOffsetFactor:-4,polygonOffsetUnits:-4}));d==="y"?(y.rotation.x=-Math.PI/2,y.position.set(i,n.max.y,l)):d==="x"?(y.rotation.y=Math.PI/2,y.position.set(n.max.x,s,l)):y.position.set(i,s,n.max.z),y.renderOrder=999,this.screenMesh.add(y),this.flatPlane=y}restoreScreen(){this.prepareScreen()}prepareScreen(){this.materials.forEach(t=>{t.color=new k(0),t.emissive=new k(16777215),t.emissiveIntensity=1,t.roughness=1,t.metalness=0,t.needsUpdate=!0})}dimGlassOverlay(t){t.traverse(n=>{const o=n;if(!o.isMesh)return;(Array.isArray(o.material)?o.material:[o.material]).forEach(a=>{const i=a;i&&i.name!==this.profile.screen.material&&i.transparent&&i.opacity>.2&&this.glassOverlays.push(i)})})}applyWallpaper(t){if(this.appliedBytes===t&&this.texture)return this.lightScreen(this.texture),Promise.resolve();const n=new Blob([t],{type:"image/png"}),o=URL.createObjectURL(n),r=this.texture;return new Promise((a,i)=>{const s=new Image;s.onload=()=>{try{const l=document.createElement("canvas");l.width=s.naturalWidth,l.height=s.naturalHeight;const d=l.getContext("2d");if(!d)throw new Error("2D context unavailable for wallpaper blit");d.drawImage(s,0,0);const u=new Jt(l);u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V,u.repeat.set(this.profile.screen.repeat.x,this.profile.screen.repeat.y),u.needsUpdate=!0,this.texture=u,this.appliedBytes=t,this.lightScreen(u),this.flatScreenOn&&this.setFlatScreen(!0),r?.dispose(),URL.revokeObjectURL(o),a()}catch(l){URL.revokeObjectURL(o),i(l)}},s.onerror=()=>{URL.revokeObjectURL(o),i(new Error("wallpaper image failed to decode"))},s.src=o})}lightScreen(t){this.materials.forEach(n=>{n.map=t,n.emissiveMap=t,n.emissive=new k(16777215),n.emissiveIntensity=Ee,n.color=new k(0),n.needsUpdate=!0})}}function In(e,t,n){const o=e/2,r=t/2,a=Math.min(n,o,r),i=new Zt;i.moveTo(-o+a,-r),i.lineTo(o-a,-r),i.quadraticCurveTo(o,-r,o,-r+a),i.lineTo(o,r-a),i.quadraticCurveTo(o,r,o-a,r),i.lineTo(-o+a,r),i.quadraticCurveTo(-o,r,-o,r-a),i.lineTo(-o,-r+a),i.quadraticCurveTo(-o,-r,-o+a,-r);const s=new Qt(i,16),l=s.attributes.position,d=new Float32Array(l.count*2);for(let u=0;u<l.count;u++)d[u*2]=(l.getX(u)+o)/e,d[u*2+1]=(l.getY(u)+r)/t;return s.setAttribute("uv",new sn(d,2)),s}const Me={sizePt:{min:20,max:120},maxWidth:{min:.1,max:1},lineHeight:{min:.5,max:3},bandInset:{min:0,max:.5},bandCenter:{min:0,max:1},reserveLines:{min:1,max:8}},X=[{id:"inter",family:"Inter",label:"Inter"},{id:"manrope",family:"Manrope",label:"Manrope"},{id:"poppins",family:"Poppins",label:"Poppins"},{id:"fraunces",family:"Fraunces",label:"Fraunces"},{id:"space-grotesk",family:"Space Grotesk",label:"Space Grotesk"}],Le=X.map(e=>e.id);[Le[0],...Le.slice(1)];const J=600;function it(e){return X.find(t=>t.id===e)??X[0]}const st=[{id:"standard",label:"Standard",blurb:"Headline on top, whole device below it, nothing cropped. The safe default.",character:"safe",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2}},{id:"bleed",label:"Bleed",blurb:"Room for a longer headline, device running off the bottom edge. What the market looks like.",character:"conventional",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4}},{id:"top-bleed",label:"Top bleed",blurb:"Device running off the TOP edge, headline underneath it. Inverts the usual reading order.",character:"expressive",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"-38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,band:"bottom"}}],lt=st,Fe=lt.map(e=>e.id);[Fe[0],...Fe.slice(1)];Array.from(new Set(st.flatMap(e=>Object.keys(e.caption))));const ct=[{id:"noir",label:"Noir",blurb:"Near-black, high contrast, glossy silver device. The default premium look.",background:{gradientFrom:"#1C1C22",gradientTo:"#08080B",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"silver",finish:"0",clearcoat:"1",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"inter",color:"FFFFFF"}},{id:"daylight",label:"Daylight",blurb:"Warm off-white, soft matte white device, dark text. Calm and editorial.",background:{gradientFrom:"#F4F1EA",gradientTo:"#DFD8CA",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"white",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"front"},caption:{fontId:"manrope",color:"1A1A1F"}},{id:"pop",label:"Pop",blurb:"Saturated indigo-to-violet, orange device. Loud, consumer, scroll-stopping.",background:{gradientFrom:"#5B6CF0",gradientTo:"#9B4DF0",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"orange",finish:"0",clearcoat:"0.4",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"poppins",color:"FFFFFF"}},{id:"clay",label:"Clay",blurb:"Warm terracotta, matte charcoal device, tilted. Reads like a product photo.",background:{gradientFrom:"#E9BBAC",gradientTo:"#C88C7C",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"charcoal",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"left",roll:"-6"},caption:{fontId:"fraunces",color:"2A2320"}},{id:"studio",label:"Studio",blurb:"Mid slate with a floor reflection, blue device. Technical and precise.",background:{gradientFrom:"#5A6678",gradientTo:"#2B3340",gradientDir:"vertical",shadow:!0,floorReflection:!0},shotLook:{material:"real",colorway:"blue",finish:"0",clearcoat:"0",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"space-grotesk",color:"FFFFFF"}}],Re=ct.map(e=>e.id);[Re[0],...Re.slice(1)];const x={angle:"front",cameraPos:null,roll:"0",phoneHeight:"82",hOffset:"0",vOffset:"18",material:"real",colorway:"silver",customColor:"F5F5F5",finish:"0",clearcoat:"0",clayTone:"grey",clayCustom:"B0B0B0",flatScreen:!1,glare:!1,lighting:!0,reflections:!1},_e={angle:{valueKind:"angle",required:!0,defaultSource:"default-look",defaultValue:x.angle,persistence:"always"},cameraPos:{valueKind:"camera-position",required:!0,defaultSource:"default-look",defaultValue:x.cameraPos,persistence:"always"},roll:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.roll,persistence:"always"},phoneHeight:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.phoneHeight,persistence:"always"},hOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.hOffset,persistence:"always"},vOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.vOffset,persistence:"always"},material:{valueKind:"material",required:!0,defaultSource:"default-look",defaultValue:x.material,persistence:"always"},colorway:{valueKind:"colorway",required:!0,defaultSource:"default-look",defaultValue:x.colorway,persistence:"always"},customColor:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.customColor,persistence:"always"},finish:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.finish,persistence:"always"},clearcoat:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.clearcoat,persistence:"always"},clayTone:{valueKind:"clay-tone",required:!0,defaultSource:"default-look",defaultValue:x.clayTone,persistence:"always"},clayCustom:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.clayCustom,persistence:"always"},flatScreen:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.flatScreen,persistence:"always"},clipToFrame:{valueKind:"boolean",required:!1,defaultSource:"explicit",defaultValue:!1,persistence:"omit-default"},glare:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.glare,persistence:"always"},lighting:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.lighting,persistence:"always"},reflections:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.reflections,persistence:"always"}},xn={roll:{min:-45,max:45},phoneHeight:{min:20,max:200},hOffset:{min:-100,max:100},vOffset:{min:-200,max:200},finish:{min:0,max:1},clearcoat:{min:0,max:1}},H={x:.5,y:.06},we={fontId:"inter",sizePt:32,color:"FFFFFF",align:"center",anchor:H,maxWidth:.86},Ne=[{id:"r69",label:"6.9″ iPhone · 1290×2796",width:1290,height:2796},{id:"r65",label:"6.5″ iPhone · 1242×2688",width:1242,height:2688},{id:"r55",label:"5.5″ iPhone · 1242×2208",width:1242,height:2208},{id:"pixel69",label:"Pixel 10 Pro · 1290×2796",width:1290,height:2796},{id:"ipad13",label:"13″ iPad · 2064×2752",width:2064,height:2752},{id:"ipad129",label:"12.9″ iPad · 2048×2732",width:2048,height:2732}];function G(e){return Ne.find(t=>t.id===e)??Ne[0]}const te=932,pe="iphone-17-pro",Pn="pixel-10-pro",Ie=[{id:"iphone-6-9",label:"iPhone 17 Pro",panelPresetId:"r69",ascDeviceSet:"APP_IPHONE_67",modelId:pe,screenFamily:"iphone"},{id:"iphone-6-5",label:"iPhone 17",panelPresetId:"r65",ascDeviceSet:"APP_IPHONE_65",modelId:pe,screenFamily:"iphone"},{id:"pixel-10-pro",label:"Pixel 10 Pro",panelPresetId:"pixel69",ascDeviceSet:"ANDROID_PHONE",modelId:Pn,screenFamily:"android"},{id:"ipad-13",label:"iPad 13″",panelPresetId:"ipad13",ascDeviceSet:"APP_IPAD_PRO_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0},{id:"ipad-12-9",label:"iPad 12.9″",panelPresetId:"ipad129",ascDeviceSet:"APP_IPAD_PRO_3GEN_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0}];function An(e){return Ie.find(t=>t.panelPresetId===e)?.modelId??pe}const dt="iphone-6-9";function ut(e){return Ie.find(t=>t.id===e)}function On(){return Ie.filter(e=>!e.comingSoon)}function ft(e,t){if(Array.isArray(e)){const n=new Set,o=[];for(const r of e){const a=ut(r);a&&!a.comingSoon&&!n.has(r)&&(n.add(r),o.push(r))}if(o.length>0)return o}if(t){const n=On().find(o=>o.panelPresetId===t);if(n)return[n.id]}return[dt]}const ht=ut(dt)?.screenFamily??"iphone";function pt(e){const t={...e},n=t,o=Object.keys(_e);for(const r of o){const a=_e[r];a.persistence==="omit-default"&&(n[r]===void 0||n[r]===a.defaultValue)&&delete n[r]}return t}function mt(e){return pt({...x,...e})}function gt(e){return pt(mt(e))}function R(e,t){const{min:n,max:o}=xn[e],r=typeof t=="number"?t:Number.parseFloat(t??""),a=Number.isFinite(r)?r:Number.parseFloat(x[e]);return Math.max(n,Math.min(o,a))}const Tn=90,yt=180,kn="#1b1b2e",Cn="#0a0a14";function En(e){return e==="horizontal"?Tn:yt}function Mn(e){const t=(e%180+180)%180;return t>=45&&t<135?"horizontal":"vertical"}function bt(e,t,n){return{stops:[{color:e,at:0},{color:t,at:1}],angle:En(n)}}const St=e=>e*Math.PI/180;function Ln(e){const t=St(e);return{dx:Math.sin(t),dy:-Math.cos(t)}}function Fn(e,t,n){const o=St(n);return Math.abs(e*Math.sin(o))+Math.abs(t*Math.cos(o))}function Rn(e,t,n){const{dx:o,dy:r}=Ln(n),a=Fn(e,t,n)/2,i=e/2,s=t/2;return{x0:i-a*o,y0:s-a*r,x1:i+a*o,y1:s+a*r}}function Z(e){const t=e.stops[0],n=e.stops[e.stops.length-1];return{gradientFrom:t?.color??"#000000",gradientTo:n?.color??t?.color??"#000000",gradientDir:Mn(e.angle)}}function ne(e,t){if(!Array.isArray(e)||e.length===0)return null;const n=[];for(const r of e){if(!r||typeof r!="object")return null;const a=r;if(typeof a.color!="string"||typeof a.at!="number"||!Number.isFinite(a.at))return null;n.push({color:a.color,at:Math.min(1,Math.max(0,a.at))})}const o=typeof t=="number"&&Number.isFinite(t)?_n(t):yt;return{stops:n,angle:o}}function Q(e){return ne(e.gradientStops,e.gradientAngle)??bt(e.gradientFrom,e.gradientTo,e.gradientDir)}function _n(e){return(e%360+360)%360}function Nn(e){return ne(e.stops,e.angle)??bt(e.from,e.to,e.dir)}function vt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(!o||typeof o!="object")continue;const r=o;if(r.kind==="solid"&&typeof r.color=="string")t[n]={kind:"solid",color:r.color};else if(r.kind==="gradient"&&typeof r.from=="string"&&typeof r.to=="string"&&(r.dir==="horizontal"||r.dir==="vertical")){const a=ne(r.stops,r.angle);t[n]=a?{kind:"gradient",stops:a.stops,angle:a.angle,from:r.from,to:r.to,dir:r.dir}:{kind:"gradient",from:r.from,to:r.to,dir:r.dir}}}return t}function B(e){return!e||e.x===H.x&&e.y===H.y}function Bn(e){const{text:t,anchor:n,...o}=e;return B(n)?{...o}:{...o,anchor:n}}const zn=.55,M="en-US";function Dn(e){const t=e.lastIndexOf(":");if(t<=0)return null;const n=Number(e.slice(t+1));return!Number.isSafeInteger(n)||n<0?null:{panelId:e.slice(0,t),index:n}}function wt(e,t,n,o){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[a,i]of Object.entries(e)){if(a===n||!t.includes(a)||!Array.isArray(i))continue;const s=Array.from(new Set(i.filter(l=>typeof l=="string"))).filter(l=>{const d=Dn(l);return!!d&&!!o[n]?.[d.panelId]?.[d.index]&&!!o[a]?.[d.panelId]?.[d.index]});s.length>0&&(r[a]=s)}return r}function It(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))!o||typeof o!="object"||Array.isArray(o)||(t[n]=z(o));return t}function jn(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.x=="number"?t.x:H.x,o=typeof t.y=="number"?t.y:H.y;return B({x:n,y:o})?void 0:{x:n,y:o}}function z(e){const t=e&&typeof e=="object"?e:{},n=jn(t.anchor),o={...we,...t};n?o.anchor=n:delete o.anchor,typeof o.reserveLines!="number"||!Number.isFinite(o.reserveLines)||o.reserveLines<Me.reserveLines.min?delete o.reserveLines:o.reserveLines=Math.floor(o.reserveLines),o.band!=="bottom"&&delete o.band;for(const r of["bandInset","lineHeight","bandCenter"]){const{min:a,max:i}=Me[r],s=o[r];(typeof s!="number"||!Number.isFinite(s)||s<a||s>i)&&delete o[r]}return o}function Hn(e){const t=e&&typeof e=="object"?e:{};return{...z(t),text:typeof t.text=="string"?t.text:""}}function xt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))Array.isArray(o)?o.length>0&&(t[n]=o.map(z)):o&&typeof o=="object"&&(t[n]=[z(o)]);return t}function Gn(e,t){const n=[{...z(t),text:e.headline}];return typeof e.subtitle=="string"&&e.subtitle.trim()&&n.push({...z({...t,sizePt:t.sizePt*zn}),text:e.subtitle}),n}function re(e,t,n=M){if(!e||typeof e!="object"||Array.isArray(e))return{};const o=It(t),r=d=>o[d]??we,a=d=>{if(!d||typeof d!="object"||Array.isArray(d))return{};const u={};for(const[p,h]of Object.entries(d))Array.isArray(h)?h.length>0&&(u[p]=h.map(Hn)):h&&typeof h=="object"&&typeof h.headline=="string"&&(u[p]=Gn(h,r(p)));return u},i=Object.entries(e);if(i.some(([,d])=>!!d&&typeof d=="object"&&typeof d.headline=="string"))return{[n]:a(e)};const l={};for(const[d,u]of i)l[d]=a(u);return l}function Pt(e,t,n=M){return e[t??n]??e[n]??{}}function At(e,t=M){const n={},o=Object.keys(e),r=o.includes(t)?[t,...o.filter(a=>a!==t)]:o;for(const a of r){const i=e[a];if(i)for(const[s,l]of Object.entries(i))n[s]===void 0&&Array.isArray(l)&&l.length>0&&(n[s]=l.map(Bn))}return n}function me(e,t=M,n=ht){const o={};return e.family!==void 0&&e.family!==n&&(o.family=e.family),e.locale!==void 0&&e.locale!==t&&(o.locale=e.locale),o}function Be(e){const t=[];return e.family!==void 0&&t.push(`family=${e.family}`),e.locale!==void 0&&t.push(`locale=${e.locale}`),t.join("&")}function ge(e){const t=[];return e.family!==void 0&&t.push("family"),e.locale!==void 0&&t.push("locale"),t}const Un={order:["family","locale"],reach:"strict"};function $n(e,t){const n=ge(e),o=t.order.filter(a=>n.includes(a));return Array.from({length:o.length+1},(a,i)=>o.slice(0,o.length-i)).map(a=>{const i={};return a.includes("family")&&(i.family=e.family),a.includes("locale")&&(i.locale=e.locale),i})}function Wn(e,t,n,o,r=M){const a=me(n,r);for(const i of $n(a,o)){const s=e(t,i);if(s!==void 0){const l=ge(i);return{value:s,resolvedAt:i,inherited:ge(a).filter(d=>!l.includes(d))}}}return{value:void 0,resolvedAt:null,inherited:[]}}function ye(e,t,n=M){const o=r=>{const a=new Set,i=[];for(const s of r)typeof s=="string"&&s&&!a.has(s)&&(a.add(s),i.push(s));return i};if(Array.isArray(t)){const r=o(t);if(r.length>0)return r.includes(n)?r:[...r,n]}return o([n,...Object.keys(e)])}function xe(e,t=M){return typeof e=="string"&&e.length>0?e:t}const D={"it-IT":"it"};function ze(e,t){if(!(Object.keys(e).some(r=>r in D)||(t?.some(r=>r in D)??!1)))return{captionText:e,locales:t};const o={};for(const[r,a]of Object.entries(e))r in D||(o[r]=a);for(const[r,a]of Object.entries(e)){const i=D[r];i&&(o[i]={...a,...o[i]??{}})}return{captionText:o,locales:t?.map(r=>D[r]??r)}}function Ot(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(n===ht||!o||typeof o!="object"||Array.isArray(o))continue;const r={};for(const[a,i]of Object.entries(o)){if(!i||typeof i!="object"||Array.isArray(i))continue;const s={};for(const[l,d]of Object.entries(i))Array.isArray(d)&&d.every(u=>typeof u=="string")&&(s[l]=d);Object.keys(s).length>0&&(r[a]=s)}Object.keys(r).length>0&&(t[n]=r)}return t}function U(e,t){return t.flatMap(n=>e.filter(o=>o.panelId===n.id))}let De=0,je=0;function Vn(e,t){const n=(o,r)=>o.reduce((a,i)=>{const s=i.startsWith(r)?Number.parseInt(i.slice(r.length),10):NaN;return Number.isFinite(s)&&s>a?s:a},0);De=Math.max(De,n(e,"shot-")),je=Math.max(je,n(t,"panel-"))}const Kn=.49;function qn(e,t,n){return e.map(o=>{const r=n*o.heightFrac,a=o.height>0?o.width*(r/o.height):r*Kn,s=o.panelIndex*t+t/2-a/2+o.hOffset*(t/2),d=(n-r)/2+o.vOffset*(n/2);return{shotId:o.shotId,bytes:o.bytes,x:Math.round(s),y:Math.round(d),width:Math.round(a),height:Math.round(r),panelIndex:o.panelIndex,...o.clipToFrame===!0?{clipToFrame:!0}:{}}})}function Yn(e,t){const n=t.panelBackgrounds??{},o=Q(t),r=o.stops[0]?.color??t.gradientFrom;if(!e.some(l=>n[l.id]))return t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"perPanel",colors:e.map(l=>t.panelColors[l.id]??r)};const i=l=>t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"solid",color:t.panelColors[l]??r};return{kind:"perPanelFills",fills:e.map(l=>{const d=n[l.id];return d?d.kind==="solid"?{kind:"solid",color:d.color}:{kind:"gradient",gradient:Nn(d)}:i(l.id)})}}function Tt(e,t,n=M){const o=Q(e);return{panelPresetId:e.panelPresetId,bgMode:e.bgMode,gradientStops:o.stops,gradientAngle:o.angle,...Z(o),panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection??!1,panelBackgrounds:e.panelBackgrounds??{},captionStyles:{...xt(e.captionStyles),...At(t,n)}}}function Xn(e){const t=e.caption.reserveLines,n=typeof t=="number"?` Reserves ${t} headline lines (~${t*18} characters).`:"";return`${e.blurb}${n} Character: [${e.character}].`}function Jn(e,t){return Math.max(0,...Object.values(e).map(n=>n[t]?.length??0))}function kt(e,t,n){const o=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=new Set,a={};let i=1;const s=()=>{for(;r.has(`caption-${i}`);)i+=1;const l=`caption-${i}`;return r.add(l),i+=1,l};for(const l of n){const d=Jn(t,l);if(d===0)continue;const u=Array.isArray(o[l])?o[l]:[];a[l]=Array.from({length:d},(p,h)=>{const f=u[h];return typeof f=="string"&&f.length>0&&!r.has(f)?(r.add(f),f):s()})}return a}const Ct=2;function Zn(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return t.schema===Ct&&Array.isArray(t.shots)&&Array.isArray(t.panels)&&t.panels.length>0}function Qn(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Zn(t))return null;const n=t,o=new Set(n.panels.map(c=>c.id)),r=n.shots.filter(c=>o.has(c.panelId)).map(c=>({id:c.id,panelId:c.panelId,frameNodeId:c.frameNodeId,frameName:c.frameName,bytes:null,look:mt(c.look),thumb:null,thumbStale:!0,inputAnchor:c.inputAnchor,outputAnchor:c.outputAnchor})),a=n.panels.map(c=>({id:c.id}));Vn(r.map(c=>c.id),a.map(c=>c.id));const i=n.selectedShotId&&r.some(c=>c.id===n.selectedShotId)?n.selectedShotId:r[0]?.id??null,s=xe(n.baseLocale),{captionText:l,locales:d}=ze(re(n.captionText,n.captionStyles,s),n.locales),u=ze({[s]:{}},[s]).locales?.[0]??s,p=ye(l,d,u),h=kt(n.captionSlotIds,l,a.map(c=>c.id)),f=ne(n.gradientStops,n.gradientAngle);return{shots:U(r,a),panels:a,selectedShotId:i,panelPresetId:G(n.panelPresetId).id,bgMode:n.bgMode==="perPanel"?"perPanel":"gradient",...f?{gradientStops:f.stops,gradientAngle:f.angle,...Z(f)}:{gradientFrom:n.gradientFrom??"5B6CF0",gradientTo:n.gradientTo??"9B4DF0",gradientDir:n.gradientDir==="vertical"?"vertical":"horizontal"},panelColors:n.panelColors??{},shadow:n.shadow!==!1,floorReflection:n.floorReflection===!0,panelBackgrounds:vt(n.panelBackgrounds),captionText:l,locales:p,baseLocale:u,staleCaptionTranslations:wt(n.staleCaptionTranslations,p,u,l),outputs:ft(n.outputs,n.panelPresetId),localeScreens:{},outputCaptions:Ot(n.outputCaptions),captionSlotIds:h,frameTemplates:n.frameTemplates&&typeof n.frameTemplates=="object"&&!Array.isArray(n.frameTemplates)?n.frameTemplates:{}}}function Et(e){const n=U(e.shots,e.panels).map(a=>({id:a.id,panelId:a.panelId,frameNodeId:a.frameNodeId,frameName:a.frameName,look:gt(a.look),inputAnchor:a.inputAnchor,outputAnchor:a.outputAnchor})),o=xe(e.baseLocale),r=re(e.captionText,e.captionStyles,o);return{schema:Ct,shots:n,panels:e.panels,selectedShotId:e.selectedShotId,captionText:r,locales:ye(r,e.locales,o),baseLocale:o,staleCaptionTranslations:wt(e.staleCaptionTranslations,ye(r,e.locales,o),o,r),outputs:ft(e.outputs,e.panelPresetId),outputCaptions:Ot(e.outputCaptions),...(()=>{const a=kt(e.captionSlotIds,r,e.panels.map(i=>i.id));return Object.keys(a).length>0?{captionSlotIds:a}:{}})(),...e.frameTemplates&&Object.keys(e.frameTemplates).length>0?{frameTemplates:e.frameTemplates}:{},...Tt(e,r,o)}}const er=1;function tr(e){const t=U(e.shots,e.panels),n=re(e.captionText,e.captionStyles),o=xe(e.baseLocale);return{schema:er,shots:t.map(r=>({panelId:r.panelId,look:gt(r.look)})),...Tt(e,n,o)}}ct.map(e=>({id:e.id,label:e.label,description:e.blurb}));lt.map(e=>({id:e.id,label:e.label,description:Xn(e),character:e.character}));const Mt=[{code:"ar-SA",name:"Arabic",script:"rtl",dir:"rtl"},{code:"bn-BD",name:"Bengali",script:"indic",dir:"ltr"},{code:"ca",name:"Catalan",script:"latin",dir:"ltr"},{code:"cs",name:"Czech",script:"latin",dir:"ltr"},{code:"da",name:"Danish",script:"latin",dir:"ltr"},{code:"de-DE",name:"German",script:"latin",dir:"ltr"},{code:"el",name:"Greek",script:"greek",dir:"ltr"},{code:"en-AU",name:"English (Australia)",script:"latin",dir:"ltr"},{code:"en-CA",name:"English (Canada)",script:"latin",dir:"ltr"},{code:"en-GB",name:"English (U.K.)",script:"latin",dir:"ltr"},{code:"en-US",name:"English (U.S.)",script:"latin",dir:"ltr"},{code:"es-ES",name:"Spanish (Spain)",script:"latin",dir:"ltr"},{code:"es-MX",name:"Spanish (Mexico)",script:"latin",dir:"ltr"},{code:"fi",name:"Finnish",script:"latin",dir:"ltr"},{code:"fr-CA",name:"French (Canada)",script:"latin",dir:"ltr"},{code:"fr-FR",name:"French",script:"latin",dir:"ltr"},{code:"gu-IN",name:"Gujarati",script:"indic",dir:"ltr"},{code:"he",name:"Hebrew",script:"rtl",dir:"rtl"},{code:"hi",name:"Hindi",script:"indic",dir:"ltr"},{code:"hr",name:"Croatian",script:"latin",dir:"ltr"},{code:"hu",name:"Hungarian",script:"latin",dir:"ltr"},{code:"id",name:"Indonesian",script:"latin",dir:"ltr"},{code:"it",name:"Italian",script:"latin",dir:"ltr"},{code:"ja",name:"Japanese",script:"cjk",dir:"ltr"},{code:"kn-IN",name:"Kannada",script:"indic",dir:"ltr"},{code:"ko",name:"Korean",script:"cjk",dir:"ltr"},{code:"ml-IN",name:"Malayalam",script:"indic",dir:"ltr"},{code:"mr-IN",name:"Marathi",script:"indic",dir:"ltr"},{code:"ms",name:"Malay",script:"latin",dir:"ltr"},{code:"nl-NL",name:"Dutch",script:"latin",dir:"ltr"},{code:"no",name:"Norwegian",script:"latin",dir:"ltr"},{code:"or-IN",name:"Odia",script:"indic",dir:"ltr"},{code:"pa-IN",name:"Punjabi",script:"indic",dir:"ltr"},{code:"pl",name:"Polish",script:"latin",dir:"ltr"},{code:"pt-BR",name:"Portuguese (Brazil)",script:"latin",dir:"ltr"},{code:"pt-PT",name:"Portuguese (Portugal)",script:"latin",dir:"ltr"},{code:"ro",name:"Romanian",script:"latin",dir:"ltr"},{code:"ru",name:"Russian",script:"cyrillic",dir:"ltr"},{code:"sk",name:"Slovak",script:"latin",dir:"ltr"},{code:"sl-SI",name:"Slovenian",script:"latin",dir:"ltr"},{code:"sv",name:"Swedish",script:"latin",dir:"ltr"},{code:"ta-IN",name:"Tamil",script:"indic",dir:"ltr"},{code:"te-IN",name:"Telugu",script:"indic",dir:"ltr"},{code:"th",name:"Thai",script:"thai",dir:"ltr"},{code:"tr",name:"Turkish",script:"latin",dir:"ltr"},{code:"uk",name:"Ukrainian",script:"cyrillic",dir:"ltr"},{code:"ur-PK",name:"Urdu",script:"rtl",dir:"rtl"},{code:"vi",name:"Vietnamese",script:"latin",dir:"ltr"},{code:"zh-Hans",name:"Chinese (Simplified)",script:"cjk",dir:"ltr"},{code:"zh-Hant",name:"Chinese (Traditional)",script:"cjk",dir:"ltr"}];Mt.map(e=>e.code);Object.fromEntries(Mt.map(e=>[e.code,e]));({...we});function nr(e,t){t.material==="clay"?(e.setClay(t.clayTone==="custom"?`#${t.clayCustom}`:t.clayTone),e.setClearcoat(0)):(e.clayOff(),t.colorway==="custom"?e.setCustomColor(`#${t.customColor}`):e.setColorway(t.colorway),e.setFinish(R("finish",t.finish)),e.setClearcoat(R("clearcoat",t.clearcoat)))}function rr(e,t,n){e.screen.setGlare(!n&&t?e.device.environmentMap():null)}async function or(e,t,n,o){const{stage:r,device:a,screen:i}=e,s=t.material==="clay";if(t.cameraPos?r.setCameraPosition(t.cameraPos):r.setAngle(t.angle),a.setRoll(R("roll",t.roll)),nr(a,t),i.restoreScreen(),r.setStudioLighting(t.lighting),a.setReflections(t.reflections),i.setFlatScreen(t.flatScreen),n){try{await i.applyWallpaper(n)}catch{}if(!o())return}rr(e,t.glare,s)}async function ar(e,t,n,o={}){const r=e.renderer,a=r.getPixelRatio(),i=r.getSize(new ln);r.setPixelRatio(1),r.setSize(t,n,!1);let s;o.cameraPosition?(s=new Qe(tt,t/n,.01,100),s.position.copy(o.cameraPosition),s.lookAt(0,0,0)):(s=e.camera.clone(),s.aspect=t/n,s.updateProjectionMatrix());try{const l=e.scene.background;e.scene.background=null;try{r.render(e.scene,s)}finally{e.scene.background=l}const d=document.createElement("canvas");let u=null;try{d.width=t,d.height=n,d.getContext("2d")?.drawImage(r.domElement,0,0);const p=ir(d),h=p.canvas;u=h;const f=h.width,c=h.height;return{bytes:await new Promise((m,b)=>{h.toBlob(y=>{if(!y){b(new Error("toBlob returned null"));return}y.arrayBuffer().then(S=>m(new Uint8Array(S)),b)},"image/png")}),width:f,height:c,frame:{width:t,height:n,cropX:p.x,cropY:p.y}}}finally{u&&u!==d&&(u.width=0,u.height=0),d.width=0,d.height=0}}finally{r.setPixelRatio(a),r.setSize(i.x,i.y,!1)}}function ir(e,t=2){const n=e.getContext("2d",{willReadFrequently:!0});if(!n)return{canvas:e,x:0,y:0};const{width:o,height:r}=e,a=n.getImageData(0,0,o,r).data;let i=o,s=r,l=-1,d=-1;for(let f=0;f<r;f++)for(let c=0;c<o;c++)a[(f*o+c)*4+3]!==0&&(c<i&&(i=c),c>l&&(l=c),f<s&&(s=f),f>d&&(d=f));if(l<i||d<s)return{canvas:e,x:0,y:0};i=Math.max(0,i-t),s=Math.max(0,s-t),l=Math.min(o-1,l+t),d=Math.min(r-1,d+t);const u=l-i+1,p=d-s+1,h=document.createElement("canvas");return h.width=u,h.height=p,h.getContext("2d")?.drawImage(e,i,s,u,p,0,0,u,p),{canvas:h,x:i,y:s}}const sr=1900,lr=2800;function cr(e,t,n){return{shots:e,panels:t,selectedShotId:n.selectedShotId,panelPresetId:n.panelPresetId,bgMode:n.bgMode,gradientStops:n.gradientStops,gradientAngle:n.gradientAngle,gradientFrom:n.gradientFrom,gradientTo:n.gradientTo,gradientDir:n.gradientDir,panelColors:n.panelColors,shadow:n.shadow,floorReflection:n.floorReflection,panelBackgrounds:n.panelBackgrounds,captionStyles:n.captionStyles,captionText:n.captionText,locales:n.locales,baseLocale:n.baseLocale,outputs:n.outputs}}async function dr(e,t,n,o,r,a){const i=U(n,o),s=i.filter(f=>f.bytes);if(s.length===0)return null;const l=G(r.panelPresetId),d=new Map(o.map((f,c)=>[f.id,c])),u=r.renderScale!=null&&r.renderScale>0&&r.renderScale<=1?r.renderScale:1,p=Math.max(1,Math.round(sr*u)),h=Math.max(1,Math.round(lr*u));{const f=[];for(let b=0;b<s.length;b++){const y=s[b];a(`Rendering ${b+1}/${s.length}${r.locale?` (${r.locale})`:""}…`),await or(t,y.look,y.bytes,()=>!0);const S=await ar(e,p,h,{cameraPosition:bn(y.look.angle,y.look.cameraPos)});f.push({shotId:y.id,bytes:S.bytes,width:S.width,height:S.height,heightFrac:R("phoneHeight",y.look.phoneHeight)/100,hOffset:R("hOffset",y.look.hOffset)/100,vOffset:R("vOffset",y.look.vOffset)/100,panelIndex:d.get(y.panelId)??0,...y.look.clipToFrame===!0?{clipToFrame:!0}:{}})}const c=qn(f,l.width,l.height),g=Yn(o,r);a("Saving project…");const m=Et(cr(i,o,r));return{panelWidth:l.width,panelHeight:l.height,count:o.length,background:g,phones:c,shadow:r.shadow,floorReflection:r.floorReflection??!1,project:JSON.stringify(m)}}}function ee(e){return/^[0-9a-fA-F]{3,8}$/.test(e)?`#${e}`:e}function be(e,t,n,o){const{x0:r,y0:a,x1:i,y1:s}=Rn(n,o,t.angle),l=e.createLinearGradient(r,a,i,s);for(const d of t.stops)l.addColorStop(Math.min(1,Math.max(0,d.at)),ee(d.color));return l}function He(e,t,n,o,r,a,i){const s=document.createElement("canvas");s.width=n,s.height=o;try{const l=s.getContext("2d");if(!l)return;l.fillStyle=be(l,t,n,o),l.fillRect(r,0,a,o),e.drawImage(s,r,0,a,o,i,0,a,o)}finally{s.width=0,s.height=0}}function Ge(e){return URL.createObjectURL(new Blob([e],{type:"image/png"}))}function Ue(e){return new Promise((t,n)=>{const o=new Image;o.onload=()=>t(o),o.onerror=n,o.src=e})}const ur=16*1024*1024;function $e(e){const t=(e.img.naturalWidth||0)*(e.img.naturalHeight||0),n=e.reflection?e.reflection.canvas.width*e.reflection.canvas.height:0;return t+n}function Lt(){let e=0;const t=Array.from(F.values());for(const r of t)e+=$e(r);const n=Array.from(F.keys());let o=0;for(;e>ur&&o<n.length-1;){const r=F.get(n[o]);r&&(e-=$e(r),r.reflection&&(r.reflection.canvas.width=0,r.reflection.canvas.height=0),F.delete(n[o])),o++}}const F=new Map,ie=new Map;async function fr(e,t){if(!t){const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}}const n=F.get(e);if(n)return F.delete(e),F.set(e,n),n;const o=ie.get(e);if(o)return o;const r=(async()=>{const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}})();ie.set(e,r);try{const a=await r;return F.set(e,a),Lt(),a}finally{ie.delete(e)}}async function hr(e,t,n){const o=t.panelWidth*t.count,r=t.panelHeight,a=n?.backgroundViewport,i=a?t.panelWidth*a.panelCount:o,s=a?t.panelWidth*a.panelIndex:0;e.width=o,e.height=r;const l=e.getContext("2d");if(!l)return;n?.transparentBg?l.clearRect(0,0,o,r):t.background.kind==="gradient"?a?He(l,t.background.gradient,i,r,s,o,0):(l.fillStyle=be(l,t.background.gradient,o,r),l.fillRect(0,0,o,r)):t.background.kind==="perPanel"?t.background.colors.forEach((h,f)=>{l.fillStyle=ee(h),l.fillRect(f*t.panelWidth,0,t.panelWidth,r)}):t.background.fills.forEach((h,f)=>{const c=f*t.panelWidth;h.kind==="solid"?(l.fillStyle=ee(h.color),l.fillRect(c,0,t.panelWidth,r)):a?He(l,h.gradient,i,r,s+c,t.panelWidth,c):(l.fillStyle=be(l,h.gradient,o,r),l.fillRect(c,0,t.panelWidth,r))});const d=n?.phoneRange?Math.max(0,n.phoneRange.start):0,u=n?.phoneRange?Math.min(t.phones.length,n.phoneRange.end):t.phones.length,p=n?.reuseDecodedPhones===!0;for(let h=d;h<u;h++){const f=t.phones[h],c=n?.clip==="panel"||n?.clip===void 0&&f.clipToFrame===!0;if(n?.skipShotId&&f.shotId===n.skipShotId)continue;const g=await fr(f.bytes,p),m=g.img;c&&(l.save(),l.beginPath(),l.rect(f.panelIndex*t.panelWidth,0,t.panelWidth,r),l.clip()),t.floorReflection&&pr(l,g,f,p),l.save(),t.shadow&&(l.shadowColor="rgba(0,0,0,0.35)",l.shadowBlur=t.panelWidth*.03,l.shadowOffsetY=t.panelHeight*.012),l.drawImage(m,f.x,f.y,f.width,f.height),l.restore(),c&&l.restore()}}function pr(e,t,n,o){const r=Math.round(n.height*.45);if(r<=0||n.width<=0)return;const a=o?t.reflection:void 0;if(a&&a.width===n.width&&a.height===n.height){e.drawImage(a.canvas,n.x,n.y+n.height,n.width,r);return}const i=t.img,s=document.createElement("canvas");s.width=Math.max(1,n.width),s.height=Math.max(1,r);const l=s.getContext("2d");if(!l)return;l.save(),l.translate(0,r),l.scale(1,-1),l.drawImage(i,0,r-n.height,n.width,n.height),l.restore();const d=l.createLinearGradient(0,0,0,r);d.addColorStop(0,"rgba(0,0,0,0.34)"),d.addColorStop(1,"rgba(0,0,0,0)"),l.globalCompositeOperation="destination-in",l.fillStyle=d,l.fillRect(0,0,n.width,r),e.drawImage(s,n.x,n.y+n.height,n.width,r),o&&(t.reflection={width:n.width,height:n.height,canvas:s},Lt())}const Ft=1.2,mr=.06,gr=.5;function yr(e,t,n){if(!e)return[t];const o=t.split(/\s+/).filter(Boolean),r=[];let a="";for(const i of o){const s=a?`${a} ${i}`:i;a&&e.measureText(s).width>n?(r.push(a),a=i):a=s}return a&&r.push(a),r}function br(e,t){if(!e)return 0;let n=0;for(const o of t)n=Math.max(n,e.measureText(o).width);return n}function Rt(e,t,n,o){const r=t.map((c,g)=>{const m=c.text.trim();if(!m)return null;const b=c.sizePt*(o/te),y=it(c.fontId).family;e&&(e.font=`${J} ${b}px "${y}"`);const S=yr(e,m,c.maxWidth*n),T=b*(c.lineHeight??Ft),P=S.length*T,w=Math.max(P,(c.reserveLines??S.length)*T);return{layer:c,index:g,lines:S,px:b,lineH:T,width:br(e,S),blockH:P,reservedH:w}}),a=r.filter(c=>c!==null&&B(c.layer.anchor)),i=a.filter(c=>c.layer.band==="bottom"),s=a.filter(c=>c.layer.band!=="bottom"),l=c=>c[0]?.layer.bandInset??mr,d=(1-l(i))*o-i.reduce((c,g)=>c+g.reservedH,0);let u=l(s)*o,p=d,h=0;const f=[];for(const c of r){if(!c)continue;if(!B(c.layer.anchor)){f.push({layer:c.layer,index:c.index,x:c.layer.anchor.x*n,yTop:c.layer.anchor.y*o,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width});continue}const g=c.layer.band==="bottom",b=(g?p:u)+(c.reservedH-c.blockH)/2,y=(c.layer.bandCenter??gr)*n;f.push({layer:c.layer,index:c.index,x:y,yTop:b,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width}),g?p+=c.reservedH:u+=c.reservedH,h=Math.max(h,b+c.blockH)}return{placed:f,bandBottom:h}}function Sr(e,t,n,o){e.save(),e.textBaseline="top";const{placed:r}=Rt(e,t,n,o);for(const a of r){e.fillStyle=ee(a.layer.color),e.textAlign=a.layer.align,e.font=`${J} ${a.px}px "${it(a.layer.fontId).family}"`;let i=a.yTop;for(const s of a.lines)e.fillText(s,a.x,i),i+=a.lineH}e.restore()}function vr(e,t){return e.sizePt*(t/te)*(e.lineHeight??Ft)}function wr(e,t,n,o){return Rt(e,t,n,o).placed.map(r=>{const a=r.layer.align==="center"?r.x-r.width/2:r.layer.align==="right"?r.x-r.width:r.x;return{index:r.index,anchorX:r.x/n,top:r.yTop/o,left:a/n,width:r.width/n,height:r.lines.length*r.lineH/o}})}function Ir(e,t,n,o,r){t.forEach((a,i)=>{const s=r[a];!s||s.length===0||(e.save(),e.translate(i*n,0),Sr(e,s,n,o),e.restore())})}const xr=""+new URL("Inter-SemiBold-B9yU462S.woff2",import.meta.url).href,Pr=""+new URL("Manrope-SemiBold-44nuYf5T.woff2",import.meta.url).href,Ar=""+new URL("Poppins-SemiBold-DJVo7c07.woff2",import.meta.url).href,Or=""+new URL("Fraunces-SemiBold-BtraKtER.woff2",import.meta.url).href,Tr=""+new URL("SpaceGrotesk-SemiBold-CyO2J5Y0.woff2",import.meta.url).href,kr={inter:xr,manrope:Pr,poppins:Ar,fraunces:Or,"space-grotesk":Tr},Cr=X.map(e=>({...e,url:kr[e.id]}));let K=null;function Er(){return K||(K=(async()=>{const e=new Set;document.fonts.forEach(t=>e.add(`${t.family}/${t.weight}`)),await Promise.all(Cr.filter(t=>!e.has(`${t.family}/${String(J)}`)).map(async t=>{const n=new FontFace(t.family,`url(${t.url})`,{weight:String(J)});await n.load(),document.fonts.add(n)})),await document.fonts.ready})().catch(e=>{throw K=null,e})),K}const Mr=22/te,Lr=1.1,We=4,Fr=.063,Rr=.167,Ve=Math.round(Mr*te),_r=.02,Nr=50;function j(e,t){const n=Math.max(e.x,t.x),o=Math.max(e.y,t.y),r=Math.min(e.x+e.width,t.x+t.width),a=Math.min(e.y+e.height,t.y+t.height);return r<=n||a<=o?null:{x:n,y:o,width:r-n,height:a-o}}function _t(e,t){return j(e,t)}function Ke(e,t){const n=Math.abs(e-t)%360;return n>180?360-n:n}function qe(e){return gn(e.cameraPos??et(e.angle))}function Nt(e,t,n,o,r,a,i){if(t.length===0)return[];const s=[];for(const l of wr(e,t,r,a)){const d=t[l.index];if(!d)continue;const u=_t({x:(o+l.left)*r,y:l.top*a,width:l.width*r,height:l.height*a},i);u&&s.push({ref:{panelId:n,panelIndex:o,layerIndex:l.index,band:B(d.anchor)},box:u,lineHeightPx:vr(d,a)})}return s}function Br(e,t,n,o){return n.height<=Se(e,t,o)}function Se(e,t,n){const o=e.y+e.height/2<=t.y+t.height/2;return n*(o?Rr:Fr)}function zr(e,t,n){return n.height<=Se(e.box,t.box,e.lineHeightPx)+Se(t.box,e.box,t.lineHeightPx)}function Dr(e){const{ctx:t,panelIds:n,panelWidth:o,panelHeight:r}=e,a={x:0,y:0,width:o*n.length,height:r};t?.save();try{return jr(e,a)}finally{t?.restore()}}function jr(e,t){const{ctx:n,panelIds:o,panelWidth:r,panelHeight:a,captionLayers:i}=e,s=o.flatMap((c,g)=>Nt(n,i[c]??[],c,g,r,a,t)),l=[];for(const c of e.phones){const m=e.clip==="panel"||e.clip===void 0&&c.clipToFrame===!0?{x:c.panelIndex*r,y:0,width:r,height:a}:t,b=_t({x:c.x,y:c.y,width:c.width,height:c.height},m);b&&l.push({shotId:c.shotId,box:b,layoutHeight:c.height})}const d=[];for(let c=0;c<s.length;c++)for(let g=c+1;g<s.length;g++){const m=j(s[c].box,s[g].box);m&&d.push({a:s[c].ref,b:s[g].ref,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:zr(s[c],s[g],m)})}const u=[];for(const c of s)for(const g of l){const m=j(c.box,g.box);m&&u.push({caption:c.ref,shotId:g.shotId,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Br(c.box,g.box,m,c.lineHeightPx)})}const p=[];for(let c=0;c<l.length;c++)for(let g=c+1;g<l.length;g++){const m=l[c],b=l[g],y=j(m.box,b.box);if(!y)continue;const S=[],T=Math.max(m.layoutHeight,b.layoutHeight),P=Math.min(m.layoutHeight,b.layoutHeight);P>0&&T/P>=Lr&&S.push("scale");const w=e.looks?.[m.shotId],v=e.looks?.[b.shotId],I=!!w&&!!v;w&&v&&(Ke(qe(w),qe(v))>=We&&S.push("angle"),Ke(R("roll",w.roll),R("roll",v.roll))>=We&&S.push("roll"));const A=Math.min(m.box.width*m.box.height,b.box.width*b.box.height);p.push({shotIdA:m.shotId,shotIdB:b.shotId,overlap:y,coverage:A>0?y.width*y.height/A:0,deliberate:S.length>0,signals:S,anglesKnown:I})}const h=new Set;for(const c of d)c.a.band&&h.add(c.a.panelId),c.b.band&&h.add(c.b.panelId);for(const c of u)c.caption.band&&h.add(c.caption.panelId);const f=[];return o.forEach((c,g)=>{if(!h.has(c))return;const m=Hr(e,c,g,t,s,l);m&&f.push(m)}),{captionsMeasurable:n!=null,captionOverlaps:d,captionDeviceOverlaps:u,deviceOverlaps:p,suggestions:f,hasWarning:d.length>0||u.length>0||p.some(c=>!c.deliberate)}}function Hr(e,t,n,o,r,a){const i=e.captionLayers[t]??[],s=i.flatMap((u,p)=>B(u.anchor)&&u.text.trim()?[p]:[]);if(s.length===0||!(Math.min(...s.map(u=>i[u].sizePt))>Ve))return null;const d=[...a.map(u=>u.box),...r.filter(u=>!(u.ref.panelId===t&&u.ref.band)).map(u=>u.box)];for(let u=1;u<=Nr;u++){const p=1-u*_r,h=new Map(s.map(m=>[m,Math.floor(i[m].sizePt*p)]));if(Math.min(...h.values())<Ve)return null;const f=i.map((m,b)=>{const y=h.get(b);return y===void 0?m:{...m,sizePt:y}});if(Nt(e.ctx,f,t,n,e.panelWidth,e.panelHeight,o).filter(m=>m.ref.band).every(m=>!d.some(b=>j(m.box,b))))return{panelId:t,scale:p,layers:s.map(m=>({layerIndex:m,fromSizePt:i[m].sizePt,toSizePt:h.get(m)}))}}return null}function Bt(e){return`build-shot-${e+1}`}function zt(e){const t=[];for(const n of e.shots)t.includes(n.panelId)||t.push(n.panelId);return t}const Gr=1900,Ur=2800;function $r(e){return new Promise((t,n)=>{e.toBlob(o=>{if(!o)return n(new Error("canvas toBlob returned null"));o.arrayBuffer().then(r=>t(new Uint8Array(r)),n)},"image/png")})}async function Dt(e,t,n,o={}){await Er();const r=zt(e),a=r.map(h=>({id:h})),i=e.shots.map((h,f)=>({id:Bt(f),panelId:h.panelId,frameNodeId:"",frameName:`screen-${f+1}`,bytes:t.screens[f]??null,look:h.look,thumb:null,thumbStale:!1})),l={panelPresetId:G(e.panelPresetId).id,bgMode:e.bgMode,gradientStops:e.gradientStops,gradientAngle:e.gradientAngle,gradientFrom:e.gradientFrom,gradientTo:e.gradientTo,gradientDir:e.gradientDir,panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection,panelBackgrounds:e.panelBackgrounds,selectedShotId:null,renderScale:o.renderScale,locale:n},d=o.onProgress??(()=>{});let u=o.refs,p=null;if(!u){const h=An(e.panelPresetId);p=nt(Gr,Ur);const f=await rt(p,h);u={stage:p,device:new ot(f,p.scene,p.renderer,h),screen:new at(f,h)}}try{const h=await dr(u.stage,u,i,a,l,d);if(!h)throw new Error("buildStrip: nothing renderable (no shots)");const f=document.createElement("canvas");try{const c={...o.clip?{clip:o.clip}:{},...o.backgroundViewport?{backgroundViewport:o.backgroundViewport}:{}};await hr(f,h,Object.keys(c).length>0?c:void 0);const g=h.panelWidth,m=h.panelHeight,b=f.getContext("2d");if(!b)throw new Error("buildStrip: 2D context unavailable");Ir(b,r,g,m,t.captionText??{});const y=Dr({phones:h.phones,panelIds:r,captionLayers:t.captionText??{},panelWidth:g,panelHeight:m,ctx:b,looks:Object.fromEntries(i.map(P=>[P.id,P.look])),clip:o.clip}),S=o.renderScale&&o.renderScale>0&&o.renderScale<1?o.renderScale:1,T=[];for(let P=0;P<h.count;P++){const w=document.createElement("canvas");w.width=Math.max(1,Math.round(g*S)),w.height=Math.max(1,Math.round(m*S));const v=w.width,I=w.height;try{const A=w.getContext("2d");if(!A)throw new Error("buildStrip: 2D context unavailable");A.drawImage(f,P*g,0,g,m,0,0,v,I),T.push({index:P,bytes:await $r(w),width:v,height:I})}finally{w.width=0,w.height=0}}return{panels:T,layout:y}}finally{f.width=0,f.height=0}}finally{p&&p.dispose()}}const Ye=.005,Wr=.02,Vr=12;function se(e){return Math.max(1,Math.round(e*100))}function le(e){return`${e.kind}:${e.frames.join(",")}:${e.anchored}`}function Kr(e,t){const n=new Map(e.suggestions.map(a=>[a.panelId,a.scale])),o=[];for(const a of e.captionOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const[i,s]=a.a.band===a.b.band?[a.a,a.b]:a.a.band?[a.a,a.b]:[a.b,a.a],l=ce([t.frameOfPanel(i.panelId),t.frameOfPanel(s.panelId)]);l.length!==0&&o.push({kind:"caption-caption",frames:l,depthPct:se(a.depthFrac),anchored:!a.a.band||!a.b.band,...Xe(n,i.band?i.panelId:null)})}for(const a of e.captionDeviceOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const i=ce([t.frameOfPanel(a.caption.panelId),t.frameOfShot(a.shotId)]);i.length!==0&&o.push({kind:"caption-device",frames:i,depthPct:se(a.depthFrac),anchored:!a.caption.band,...Xe(n,a.caption.band?a.caption.panelId:null)})}for(const a of e.deviceOverlaps){if(a.deliberate||a.coverage<Wr)continue;const i=ce([t.frameOfShot(a.shotIdA),t.frameOfShot(a.shotIdB)]);i.length!==0&&o.push({kind:"device-device",frames:i,depthPct:se(a.coverage),anchored:!1})}const r=new Map;for(const a of o){const i=r.get(le(a));i?a.depthPct>i.depthPct?r.set(le(a),{...a,clearAtScale:a.clearAtScale??i.clearAtScale}):i.clearAtScale==null&&a.clearAtScale!=null&&(i.clearAtScale=a.clearAtScale):r.set(le(a),a)}return[...r.values()].sort((a,i)=>i.depthPct-a.depthPct).slice(0,Vr).sort((a,i)=>a.frames[0]-i.frames[0]||a.kind.localeCompare(i.kind))}function ce(e){const[t,...n]=e;return t<0?[]:[t,...n.filter(o=>o>=0&&o!==t)]}function Xe(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtScale:Math.round(n*100)/100}}function qr(e,t){if(t.length===0)return e;const n=e.captionStyles;if(!n)return e;const o={};for(const[r,a]of Object.entries(n))o[r]=a.map(i=>({...i}));for(const r of t){const a=o[r.panelId];if(a)for(const i of r.layers){const s=a[i.layerIndex];s&&(s.sizePt=i.toSizePt)}}return{...e,captionStyles:o}}function de(e){return e.frames.map(t=>`frame ${t+1}`).join(" + ")}function Yr(e,t,n){if(!e.captionsMeasurable)return null;const o=Kr(e,t);if(o.length===0)return null;const r=o.filter(d=>d.kind==="device-device"),a=o.filter(d=>d.kind!=="device-device"&&d.clearAtScale==null);if(r.length>0||a.length>0||!n){const d=[];r.length>0&&d.push(`${r.map(de).join(", ")}: two devices overlap without reading as composed. There is no deterministic repair — give the pair a real difference in scale, camera angle or roll (that is what makes an overlap read as deliberate), or leave it if it already is.`);for(const u of a)d.push(u.anchored?`${de(u)}: the caption is anchored by hand, so it is reported and never rewritten — move it, or drop its \`anchor\` to let the band place it.`:`${de(u)}: no caption size above the legibility floor clears it — shorten the headline, or pick a layout with a bigger text region (\`style.layout: "bleed"\`).`);return d.length===0&&d.push("The rendered look was not available here, so no corrected look could be built."),{findings:o,note:`No corrected look is offered. ${d.join(" ")}`}}const i=new Set(o.filter(d=>d.kind!=="device-device").map(d=>d.frames[0])),s=e.suggestions.filter(d=>i.has(t.frameOfPanel(d.panelId))),l=s.flatMap(d=>{const u=t.frameOfPanel(d.panelId)+1;return d.layers.map(p=>`frame ${u} layer ${p.layerIndex+1}: sizePt ${p.fromSizePt} → ${p.toSizePt}`)}).join("; ");return{findings:o,correctedLook:qr(n,s),note:`\`layout.correctedLook\` is this same look with every caption shrunk to the largest size that was verified to clear the overlap (${l}). Nothing here is applied for you — re-render with it, or show the user the finding and let them decide. On render_strip pass it back as \`look\` together with the SAME \`style.captions\` you just sent: the words come from \`style.captions\` and every style field you leave out of them now comes from this look. The one thing that still beats it is a field you set EXPLICITLY on a caption entry, so if your \`style.captions\` pin \`sizePt\` at the original numbers, drop them or set them to the sizes above.`}}function Xr(e){return String(Math.max(-100,Math.min(100,e*35)))}const Jr=["roll","phoneHeight","hOffset","vOffset","finish","clearcoat"];function Je(e){if(!e||typeof e!="object")return{};const t={...e};for(const n of Jr)typeof t[n]=="number"&&(t[n]=String(t[n]));return t}function Zr(e){const{subtitle:t,sizePt:n,maxWidth:o,...r}=e;return{...r,...n!==void 0?{sizePt:Number(n)}:{},...o!==void 0?{maxWidth:Number(o)}:{}}}function Ze(e){const t={};for(const[n,o]of Object.entries(e))o!==void 0&&(t[n]=o);return t}function ue(e,t,n){const o={};return e&&t.forEach((r,a)=>{const i=n[a];i!==void 0&&e[r]!==void 0&&(o[i]=e[r])}),o}function Qr(e){const t=e.slotSizes.filter(v=>Number.isInteger(v)&&v>0),n=t.map((v,I)=>`panel-${I+1}`);let o={};const r=e.look&&typeof e.look=="object"?e.look:null,a=e.style??null,i=r&&Array.isArray(r.shots)?r.shots:[],s=i.length>0,l=a?Je(a.shotLook):null,d=[];let u=0;t.forEach((v,I)=>{for(let A=0;A<v;A++){const O=s?Je(i[u]?.look??i[0]?.look??{}):l??{},$=O.hOffset!==void 0&&O.hOffset!==null?String(O.hOffset):Xr(A);d.push({panelId:n[I],look:{...x,...O,hOffset:$}}),u++}});let p={},h={},f={},c="gradient",g=kn,m=Cn,b="vertical",y,S,T=!0,P=!1;if(r){c=r.bgMode==="perPanel"?"perPanel":"gradient";const v=Q({gradientStops:Array.isArray(r.gradientStops)?r.gradientStops:void 0,gradientAngle:typeof r.gradientAngle=="number"?r.gradientAngle:void 0,gradientFrom:typeof r.gradientFrom=="string"?r.gradientFrom:g,gradientTo:typeof r.gradientTo=="string"?r.gradientTo:m,gradientDir:r.gradientDir==="horizontal"?"horizontal":b});y=v.stops,S=v.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(v),r.shadow===!1&&(T=!1),r.floorReflection===!0&&(P=!0);const I=[];for(const A of i){const O=typeof A?.panelId=="string"?A.panelId:null;O&&!I.includes(O)&&I.push(O)}p=ue(r.panelColors,I,n),h=ue(xt(r.captionStyles),I,n),f=ue(vt(r.panelBackgrounds),I,n)}else if(a){const v=a.background??{};c=v.mode==="perPanel"?"perPanel":"gradient";const I=Q({gradientStops:v.stops,gradientAngle:v.angle,gradientFrom:g,gradientTo:m,gradientDir:b});y=I.stops,S=I.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(I),v.shadow===!1&&(T=!1),v.floorReflection===!0&&(P=!0),Array.isArray(v.panelColors)&&v.panelColors.forEach((A,O)=>{typeof A=="string"&&A&&n[O]!==void 0&&(p[n[O]]=A)})}if(a&&Array.isArray(a.captions)){const v=h,I=(L,oe)=>({...v[L]?.[oe]??{}}),A={},O={};a.captions.forEach((L,oe)=>{const _=n[oe];if(!(_===void 0||L===null||L===void 0)){if(Array.isArray(L)){const N=L.filter(E=>!!E&&typeof E=="object"&&typeof E.text=="string"&&E.text.trim()!=="").map((E,W)=>({...I(_,W),...Ze(Zr(E))}));N.length>0&&(O[_]=N)}else if(typeof L=="object"){const{text:N,subtitle:E,sizePt:W,maxWidth:Ae,...$t}=L;A[_]={...I(_,0),...Ze({...$t,...W!==void 0?{sizePt:Number(W)}:{},...Ae!==void 0?{maxWidth:Number(Ae)}:{}})},typeof N=="string"&&N.trim()&&(O[_]={headline:N,...typeof E=="string"&&E?{subtitle:E}:{}})}}});const $=re({[M]:O},It(A));h=At($),o=Pt($)}return{look:{panelPresetId:e.panelPresetId??(typeof r?.panelPresetId=="string"?r.panelPresetId:void 0)??"r69",bgMode:c,gradientStops:y,gradientAngle:S,gradientFrom:g,gradientTo:m,gradientDir:b,panelColors:p,shadow:T,floorReflection:P,panelBackgrounds:f,captionStyles:h,shots:d},panelIds:n,captionText:o}}function eo(e){const t=e?.trim();return t?/\.(png|jpe?g|webp)$/i.test(t)?t:`${t}.png`:null}function to(e,t){const n=new Map;return e.map((o,r)=>{const a=eo(t?.[r]);if(!a)return`screen-${r+1}.png`;const i=n.get(a)??0;if(n.set(a,i+1),i===0)return a;const s=a.lastIndexOf(".");return`${a.slice(0,s)}-${i+1}${a.slice(s)}`})}function no(e){const{look:t,panelIds:n,captionText:o,locale:r,shotNames:a,outputs:i}=e,s=n.map(u=>({id:u})),l=to(t.shots,a),d=t.shots.map((u,p)=>({id:`shot-${p+1}`,panelId:u.panelId,frameNodeId:l[p],frameName:l[p],bytes:null,look:u.look,thumb:null,thumbStale:!0}));return Et({shots:d,panels:s,selectedShotId:d[0]?.id??null,panelPresetId:t.panelPresetId,bgMode:t.bgMode,gradientStops:t.gradientStops,gradientAngle:t.gradientAngle,gradientFrom:t.gradientFrom,gradientTo:t.gradientTo,gradientDir:t.gradientDir,panelColors:t.panelColors,shadow:t.shadow,floorReflection:t.floorReflection,panelBackgrounds:t.panelBackgrounds,captionStyles:t.captionStyles,outputs:i,captionText:o&&Object.keys(o).length>0?{[r||M]:o}:{}})}function C(e){console.log("[render]",e)}const ro=1900,oo=2800;let fe=null;async function jt(){return fe||(fe=(async()=>{C("booting headless stage + loading model (once)…");const e=nt(ro,oo),t=await wn(e);return{stage:e,device:new ot(t,e.scene,e.renderer),screen:new at(t)}})()),fe}function Ht(e){const t=atob(e),n=new Uint8Array(t.length);for(let o=0;o<t.length;o++)n[o]=t.charCodeAt(o);return n}function Pe(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function ao(e){return new Promise((t,n)=>{e.toBlob(async o=>{if(!o)return n(new Error("toBlob null"));t(Pe(new Uint8Array(await o.arrayBuffer())))},"image/png")})}function Gt(e,t){const n=zt(t),o=new Map(t.shots.map((a,i)=>[Bt(i),n.indexOf(a.panelId)])),r=Yr(e.layout,{frameOfPanel:a=>n.indexOf(a),frameOfShot:a=>o.get(a)??-1},t);if(r)return{layout:{findings:r.findings,...r.correctedLook?{correctedLook:r.correctedLook}:{}},layoutNote:r.note}}async function Ut(e){const t=await Promise.all(e.map(a=>createImageBitmap(new Blob([new Uint8Array(a.bytes)],{type:"image/png"})))),n=document.createElement("canvas");n.width=t.reduce((a,i)=>a+i.width,0),n.height=t[0]?.height??1;const o=n.getContext("2d");let r=0;for(const a of t)o.drawImage(a,r,0),r+=a.width,a.close();return{base64:await ao(n),width:n.width,height:n.height}}window.renderStrip=async function(t){try{if(!t||!Array.isArray(t.screenshots)||t.screenshots.length===0)throw new Error("renderStrip: input.screenshots must be a non-empty nested array of slots");const n=await jt(),o=t.screenshots.map(S=>Array.isArray(S)?S:[S]),{look:r,panelIds:a,captionText:i}=Qr({slotSizes:o.map(S=>S.length),panelPresetId:t.panelPresetId,look:t.look??void 0,style:t.style??void 0}),s=o.flat().map(Ht),l=Array.isArray(t.captionLayers)&&a[0]?{[a[0]]:t.captionLayers}:i,d=t.preview?.25:1;C(`buildStrip (${o.length} panel${o.length===1?"":"s"}${t.locale?`, ${t.locale}`:""})…`);const u=performance.now(),p=await Dt(r,{screens:s,captionText:l},t.locale,{refs:n,renderScale:d,onProgress:S=>C(" "+S),...t.clip?{clip:t.clip}:{},...t.backgroundViewport?{backgroundViewport:t.backgroundViewport}:{}});C("encoding panels + stitching the strip preview…");const h=p.panels.map(S=>Pe(S.bytes)),f=await Ut(p.panels),c=G(r.panelPresetId),g=Math.round(performance.now()-u),m=Gt(p,r),b=Array.isArray(t.screenshotNames)?t.screenshotNames.flat():void 0,y=t.buildRecord?no({look:r,panelIds:a,captionText:i,locale:t.locale,shotNames:b,outputs:t.outputs}):void 0;return C(`done: ${p.panels.length} panels, strip ${f.width}x${f.height}, ${g}ms`),{ok:!0,ms:g,count:p.panels.length,panelPresetId:c.id,panelWidth:c.width,panelHeight:c.height,stripWidth:f.width,stripHeight:f.height,panels:h,stripBase64:f.base64,preview:d<1,...y?{projectRecord:y}:{},...m??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.renderProject=async function(t){try{if(!t||t.projectFile==null||typeof t.projectFile!="object")throw new Error("renderProject: input.projectFile (the saved ProjectFile) is required");const n=Qn(JSON.stringify(t.projectFile));if(!n)throw new Error("renderProject: the saved project could not be restored (wrong schema or malformed record)");const o=await jt(),r=t.locale||n.baseLocale||n.locales?.[0]||"en-US",a=U(n.shots,n.panels),i=me(t.variant||{},n.baseLocale),s=new Map((t.screenManifest||[]).map(w=>[`${w.shotId} ${Be(me(w.variant||{},n.baseLocale))}`,w])),l=(w,v)=>s.get(`${w} ${Be(v)}`),d=a.map(w=>Wn(l,w.id,i,Un,n.baseLocale)),u=a.map((w,v)=>({frameName:w.frameName,matched:d[v].value!==void 0,requestedVariant:i,resolvedAt:d[v].resolvedAt,inherited:d[v].inherited,...d[v].value?.source?{source:d[v].value.source}:{}})),p=d.map(({value:w})=>w?.bytes?Ht(w.bytes):null),h=Pt(n.captionText,r,n.baseLocale),f=tr({...n,selectedShotId:null,panelPresetId:t.panelPresetId??n.panelPresetId,captionText:{[r]:h}}),c=t.preview?.25:1;C(`renderProject (${a.length} shot${a.length===1?"":"s"}, ${r})…`);const g=performance.now(),m=await Dt(f,{screens:p,captionText:h},r,{refs:o,renderScale:c,onProgress:w=>C(" "+w)});C("encoding panels + stitching the strip preview…");const b=m.panels.map(w=>Pe(w.bytes)),y=await Ut(m.panels),S=G(f.panelPresetId),T=Math.round(performance.now()-g),P=Gt(m,f);return C(`done: ${m.panels.length} panels, strip ${y.width}x${y.height}, ${T}ms`),{ok:!0,ms:T,count:m.panels.length,panelPresetId:S.id,panelWidth:S.width,panelHeight:S.height,stripWidth:y.width,stripHeight:y.height,panels:b,stripBase64:y.base64,preview:c<1,shots:u,locale:r,...P??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.__RENDER_READY=!0;C("harness ready ✓");
|