shotops-mcp 0.9.6 → 0.9.7

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.
@@ -0,0 +1 @@
1
+ import{S as qt,G as Vt,A as Kt,D as oe,M as Yt,R as Xt,a as Jt,C as Zt,b as Qt,c as en}from"./three-addons-CvX38LO6.js";import{aa as tn,P as nt,b as nn,V as Y,a4 as rn,C as O,ab as an,J as Oe,$ as V,M as on,a2 as sn,B as ln,l as cn}from"./three-webgl-DCw804Ic.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))r(a);new MutationObserver(a=>{for(const o of a)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(a){const o={};return a.integrity&&(o.integrity=a.integrity),a.referrerPolicy&&(o.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?o.credentials="include":a.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(a){if(a.ep)return;a.ep=!0;const o=n(a);fetch(a.href,o)}})();const dn=""+new URL("iphone-optimized-BdcJzlHi.glb",import.meta.url).href,un="data:model/gltf-binary;base64,Z2xURgIAAADUAAAAwAAAAEpTT057ImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJTaG90T3BzIHBsYWNlaG9sZGVyIOKAlCB0aGUgUGl4ZWwgbW9kZWwgaXMgZGVsaWJlcmF0ZWx5IG5vdCBidW5kbGVkIGluIHRoZSBucG0gcGFja2FnZSJ9LCJzY2VuZXMiOlt7Im5hbWUiOiJwaXhlbC1ub3QtYnVuZGxlZCIsIm5vZGVzIjpbXX1dLCJzY2VuZSI6MH0gICA=",pn=[{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}],X={id:"iphone-17-pro",glb:dn,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:un,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},mn={[X.id]:X,[Te.id]:Te};function we(e){return e&&mn[e]||X}const gn=7,Ee={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 rt(e){const t=Ee[e]??Ee.front,n=Math.hypot(t.x,t.y,t.z),r=gn/n;return{x:t.x*r,y:t.y*r,z:t.z*r}}function yn(e){return Math.atan2(e.x,-e.z)*180/Math.PI}function fe(e){const t=rt(e);return new Y(t.x,t.y,t.z)}const bn=1.4;function vn(e,t){return(t?new Y(t.x,t.y,t.z):fe(e)).multiplyScalar(bn)}const at=35;function wn(e){e.traverse(t=>{const n=t;n.geometry?.dispose();const r=n.material;Array.isArray(r)?r.forEach(Le):r&&Le(r)}),e.clear()}function Le(e){for(const t of Object.values(e))t&&typeof t=="object"&&t.isTexture&&t.dispose();e.dispose()}function ot(e,t){const n=document.createElement("canvas"),r=new tn({canvas:n,antialias:!0,preserveDrawingBuffer:!0,alpha:!0});r.setSize(e,t,!1),r.shadowMap.enabled=!0;const a=new qt;a.background=null;const{ambient:o,directionals:i}=Sn(a),s=new nt(at,e/t,.01,100);s.position.copy(fe("front")),s.lookAt(0,0,0);function l(f){s.position.copy(fe(f)),s.lookAt(0,0,0)}function c(f){s.position.set(f.x,f.y,f.z),s.lookAt(0,0,0)}function d(f){i.forEach(u=>u.visible=f),o.intensity=f?.75:1.6}let h=!1;function p(){h||(h=!0,a.environment?.dispose(),a.environment=null,wn(a),r.dispose(),r.forceContextLoss())}return{scene:a,camera:s,renderer:r,dispose:p,setAngle:l,setCameraPosition:c,getCameraPosition:()=>({x:s.position.x,y:s.position.y,z:s.position.z}),onUserPose:()=>{},previewOrbit:()=>{},commitOrbit:()=>{},setOrbitEnabled:()=>{},setBackground:()=>{},setStudioLighting:d,resize:()=>{},invalidate:()=>{}}}function Sn(e){const t=new Kt(16777215,.75);e.add(t);const n=new oe(16777215,2.2);n.position.set(3,5,5);const r=new oe(12571903,.7);r.position.set(-4,1,2);const a=new oe(16777215,1);a.position.set(0,3,-5);const o=[n,r,a];return o.forEach(i=>e.add(i)),{ambient:t,directionals:o}}function it(e,t){const n=we(t);return new Promise((r,a)=>{new Vt().load(n.glb,o=>{const i=o.scene;if(n.pruneNamePrefixes.length){const p=[];i.traverse(f=>{n.pruneNamePrefixes.some(u=>f.name.startsWith(u))&&p.push(f)}),p.forEach(f=>f.removeFromParent())}i.rotation.set(n.baseEuler.x,n.baseEuler.y,n.baseEuler.z),i.updateMatrixWorld(!0);const s=new nn().setFromObject(i),l=s.getSize(new Y),c=s.getCenter(new Y),d=n.fitSize/Math.max(l.x,l.y,l.z);i.scale.multiplyScalar(d),i.position.copy(c.multiplyScalar(-d));const h=new rn;h.add(i),e.scene.add(h),e.setAngle("front"),r(h)},void 0,a)})}function In(e){return it(e)}X.colorways;class st{frame=[];back=[];accent=[];allBody=[];model;scene;renderer;profile;envTexture=null;constructor(t,n,r,a){this.model=t,this.scene=n,this.renderer=r,this.profile=we(a);const{frame:o,back:i,accent:s}=this.profile.body;t.traverse(l=>{const c=l;if(!c.isMesh)return;(Array.isArray(c.material)?c.material:[c.material]).forEach(h=>{const p=h;!p||!p.name||(o.includes(p.name)?this.push(this.frame,p):i.includes(p.name)?this.push(this.back,p):s.includes(p.name)&&this.push(this.accent,p),this.push(this.allBody,p))})})}push(t,n){t.includes(n)||t.push(n)}get finishMaterials(){return[...this.frame,...this.back]}paint(t,n){const r=n instanceof O?n:new O(n);t.forEach(a=>{a.color=r.clone(),a.needsUpdate=!0})}setColorway(t){if(t==="custom")return;const n=this.profile.colorways.find(r=>r.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),r=n.clone().lerp(new O(16777215),.2),a=n.clone().multiplyScalar(.6);this.paint(this.frame,n),this.paint(this.back,r),this.paint(this.accent,a)}setFinish(t){const n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0;this.finishMaterials.forEach(r=>{r.userData.baseRoughness===void 0&&(r.userData.baseRoughness=r.roughness);const a=r.userData.baseRoughness;r.roughness=a*(1-n),r.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(r=>{const a=r;a.clearcoat=n,a.clearcoatRoughness=.1,a.needsUpdate=!0})}upgradeFinishToPhysical(){this.clearcoatUpgraded=!0;const t=new Map;for(const r of Array.from(new Set(this.finishMaterials))){const a=new Yt;a.color.copy(r.color),a.metalness=r.metalness,a.roughness=r.roughness,a.map=r.map,a.normalMap=r.normalMap,a.normalScale.copy(r.normalScale),a.roughnessMap=r.roughnessMap,a.metalnessMap=r.metalnessMap,a.aoMap=r.aoMap,a.aoMapIntensity=r.aoMapIntensity,a.emissive.copy(r.emissive),a.emissiveMap=r.emissiveMap,a.emissiveIntensity=r.emissiveIntensity,a.envMapIntensity=r.envMapIntensity,a.transparent=r.transparent,a.opacity=r.opacity,a.alphaTest=r.alphaTest,a.side=r.side,a.flatShading=r.flatShading,a.vertexColors=r.vertexColors,a.name=r.name,a.userData={...r.userData},a.clearcoat=0,t.set(r,a)}this.model.traverse(r=>{const a=r;if(a.isMesh)if(Array.isArray(a.material))a.material=a.material.map(o=>t.get(o)??o);else{const o=t.get(a.material);o&&(a.material=o)}});const n=r=>r.map(a=>t.get(a)??a);this.frame=n(this.frame),this.back=n(this.back),this.allBody=n(this.allBody)}environmentMap(){if(!this.envTexture){const t=new an(this.renderer);this.envTexture=t.fromScene(new Xt,.04).texture,t.dispose()}return this.envTexture}setClay(t){const n=pn.find(a=>a.id===t),r=n?new O(n.color):new O(t);this.allBody.forEach(a=>{a.userData.clayStashedMap===void 0&&(a.userData.clayStashedMap=a.map),a.color=r.clone(),a.metalness=0,a.roughness=.85,a.map=null,a.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 Me=1.35;class lt{materials=[];glassOverlays=[];screenMesh=null;flatPlane=null;texture=null;appliedBytes=null;flatScreenOn=!1;profile;constructor(t,n){this.profile=we(n);const r=this.profile.screen.material;t.traverse(a=>{const o=a;if(!o.isMesh)return;(Array.isArray(o.material)?o.material:[o.material]).forEach(s=>{const l=s;l&&l.name===r&&!this.materials.includes(l)&&(this.materials.push(l),this.screenMesh=o)})}),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 r=n.max.x-n.min.x,a=n.max.y-n.min.y,o=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,c=a<=r&&a<=o?"y":o<=r&&o<=a?"z":"x",d=this.texture.clone();d.needsUpdate=!0,d.colorSpace=Oe,d.flipY=!1,d.center.set(.5,.5),d.wrapS=V,d.wrapT=V;const{coverFill:h,coverCornerRatio:p,coverRepeat:f}=this.profile.screen;d.repeat.set(f.x,f.y);const u=c==="x"?o:r,m=c==="z"||c==="x"?a:o,g=Math.min(u,m)*h*p,y=xn(u*h,m*h,g),b=new on(y,new Jt({map:d,emissive:new O(16777215),emissiveMap:d,emissiveIntensity:Me,color:new O(0),toneMapped:!1,side:sn,depthTest:!0,depthWrite:!0,polygonOffset:!0,polygonOffsetFactor:-4,polygonOffsetUnits:-4}));c==="y"?(b.rotation.x=-Math.PI/2,b.position.set(i,n.max.y,l)):c==="x"?(b.rotation.y=Math.PI/2,b.position.set(n.max.x,s,l)):b.position.set(i,s,n.max.z),b.renderOrder=999,this.screenMesh.add(b),this.flatPlane=b}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 r=n;if(!r.isMesh)return;(Array.isArray(r.material)?r.material:[r.material]).forEach(o=>{const i=o;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"}),r=URL.createObjectURL(n),a=this.texture;return new Promise((o,i)=>{const s=new Image;s.onload=()=>{try{const l=document.createElement("canvas");l.width=s.naturalWidth,l.height=s.naturalHeight;const c=l.getContext("2d");if(!c)throw new Error("2D context unavailable for wallpaper blit");c.drawImage(s,0,0);const d=new Zt(l);d.colorSpace=Oe,d.flipY=!1,d.center.set(.5,.5),d.wrapS=V,d.wrapT=V,d.repeat.set(this.profile.screen.repeat.x,this.profile.screen.repeat.y),d.needsUpdate=!0,this.texture=d,this.appliedBytes=t,this.lightScreen(d),this.flatScreenOn&&this.setFlatScreen(!0),a?.dispose(),URL.revokeObjectURL(r),o()}catch(l){URL.revokeObjectURL(r),i(l)}},s.onerror=()=>{URL.revokeObjectURL(r),i(new Error("wallpaper image failed to decode"))},s.src=r})}lightScreen(t){this.materials.forEach(n=>{n.map=t,n.emissiveMap=t,n.emissive=new O(16777215),n.emissiveIntensity=Me,n.color=new O(0),n.needsUpdate=!0})}}function xn(e,t,n){const r=e/2,a=t/2,o=Math.min(n,r,a),i=new Qt;i.moveTo(-r+o,-a),i.lineTo(r-o,-a),i.quadraticCurveTo(r,-a,r,-a+o),i.lineTo(r,a-o),i.quadraticCurveTo(r,a,r-o,a),i.lineTo(-r+o,a),i.quadraticCurveTo(-r,a,-r,a-o),i.lineTo(-r,-a+o),i.quadraticCurveTo(-r,-a,-r+o,-a);const s=new en(i,16),l=s.attributes.position,c=new Float32Array(l.count*2);for(let d=0;d<l.count;d++)c[d*2]=(l.getX(d)+r)/e,c[d*2+1]=(l.getY(d)+a)/t;return s.setAttribute("uv",new ln(c,2)),s}const Fe={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}},J=[{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"}],Re=J.map(e=>e.id);[Re[0],...Re.slice(1)];const G=600;function Se(e){return J.find(t=>t.id===e)??J[0]}const ct=[{id:"standard",label:"Standard",blurb:"Headline on top, whole device below it, nothing cropped. The safe default.",character:"safe",bleedEdge:"none",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2,align:"center",bandCenter:.5}},{id:"bleed",label:"Bleed",blurb:"Room for a longer headline, device running off the bottom edge. What the market looks like.",character:"conventional",bleedEdge:"bottom",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,align:"center",bandCenter:.5}},{id:"top-bleed",label:"Top bleed",blurb:"Device running off the TOP edge, headline underneath it. Inverts the usual reading order.",character:"expressive",bleedEdge:"top",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"-38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,band:"bottom",align:"center",bandCenter:.5}},{id:"lean-right",label:"Lean right",blurb:"Device pushed off the RIGHT edge, headline in a column down the left. Reads as one continuous swipe.",character:"expressive",bleedEdge:"right",shotLook:{phoneHeight:"82",hOffset:"46",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.61,reserveLines:2,align:"left",bandCenter:.375}},{id:"lean-left",label:"Lean left",blurb:"Device pushed off the LEFT edge, headline in a column down the right. The mirror of Lean right.",character:"expressive",bleedEdge:"left",shotLook:{phoneHeight:"82",hOffset:"-46",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.61,reserveLines:2,align:"right",bandCenter:.625}},{id:"swipe",label:"Swipe",blurb:"A third of the device carried into the next panel, headline full width above it. Only honest applied to every frame.",character:"expressive",bleedEdge:"right",shotLook:{phoneHeight:"82",hOffset:"78",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2,align:"center",bandCenter:.5}},{id:"corner-bleed",label:"Corner bleed",blurb:"Off the bottom AND the right at once, headline in a column down the left. The most opinionated panel here.",character:"expressive",bleedEdge:"bottom-right",shotLook:{phoneHeight:"82",hOffset:"46",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.61,reserveLines:4,align:"left",bandCenter:.375}}],dt=ct,Ne=dt.map(e=>e.id);[Ne[0],...Ne.slice(1)];Array.from(new Set(ct.flatMap(e=>Object.keys(e.caption))));const ut=[{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"}}],_e=ut.map(e=>e.id);[_e[0],..._e.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},Be={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"}},An={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}},U={x:.5,y:.06},Ie={fontId:"inter",sizePt:32,color:"FFFFFF",align:"center",anchor:U,maxWidth:.86},je=[{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 W(e){return je.find(t=>t.id===e)??je[0]}const te=932,he="iphone-17-pro",Pn="pixel-10-pro",xe=[{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: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 Cn(e){return xe.find(t=>t.panelPresetId===e)?.modelId??he}const pt="iphone-6-9";function ft(e){return xe.find(t=>t.id===e)}function kn(){return xe.filter(e=>!e.comingSoon)}function ht(e,t){if(Array.isArray(e)){const n=new Set,r=[];for(const a of e){const o=ft(a);o&&!o.comingSoon&&!n.has(a)&&(n.add(a),r.push(a))}if(r.length>0)return r}if(t){const n=kn().find(r=>r.panelPresetId===t);if(n)return[n.id]}return[pt]}const mt=ft(pt)?.screenFamily??"iphone";function gt(e){const t={...e},n=t,r=Object.keys(Be);for(const a of r){const o=Be[a];o.persistence==="omit-default"&&(n[a]===void 0||n[a]===o.defaultValue)&&delete n[a]}return t}function yt(e){return gt({...x,...e})}function bt(e){return gt(yt(e))}function R(e,t){const{min:n,max:r}=An[e],a=typeof t=="number"?t:Number.parseFloat(t??""),o=Number.isFinite(a)?a:Number.parseFloat(x[e]);return Math.max(n,Math.min(r,o))}const On=90,vt=180,Tn="#1b1b2e",En="#0a0a14";function Ln(e){return e==="horizontal"?On:vt}function Mn(e){const t=(e%180+180)%180;return t>=45&&t<135?"horizontal":"vertical"}function wt(e,t,n){return{stops:[{color:e,at:0},{color:t,at:1}],angle:Ln(n)}}const St=e=>e*Math.PI/180;function Fn(e){const t=St(e);return{dx:Math.sin(t),dy:-Math.cos(t)}}function Rn(e,t,n){const r=St(n);return Math.abs(e*Math.sin(r))+Math.abs(t*Math.cos(r))}function Nn(e,t,n){const{dx:r,dy:a}=Fn(n),o=Rn(e,t,n)/2,i=e/2,s=t/2;return{x0:i-o*r,y0:s-o*a,x1:i+o*r,y1:s+o*a}}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 a of e){if(!a||typeof a!="object")return null;const o=a;if(typeof o.color!="string"||typeof o.at!="number"||!Number.isFinite(o.at))return null;n.push({color:o.color,at:Math.min(1,Math.max(0,o.at))})}const r=typeof t=="number"&&Number.isFinite(t)?_n(t):vt;return{stops:n,angle:r}}function Q(e){return ne(e.gradientStops,e.gradientAngle)??wt(e.gradientFrom,e.gradientTo,e.gradientDir)}function _n(e){return(e%360+360)%360}function Bn(e){return ne(e.stops,e.angle)??wt(e.from,e.to,e.dir)}function It(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;const a=r;if(a.kind==="solid"&&typeof a.color=="string")t[n]={kind:"solid",color:a.color};else if(a.kind==="gradient"&&typeof a.from=="string"&&typeof a.to=="string"&&(a.dir==="horizontal"||a.dir==="vertical")){const o=ne(a.stops,a.angle);t[n]=o?{kind:"gradient",stops:o.stops,angle:o.angle,from:a.from,to:a.to,dir:a.dir}:{kind:"gradient",from:a.from,to:a.to,dir:a.dir}}}return t}function _(e){return!e||e.x===U.x&&e.y===U.y}function jn(e){const{text:t,anchor:n,...r}=e;return _(n)?{...r}:{...r,anchor:n}}const zn=.55,L="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 xt(e,t,n,r){if(!e||typeof e!="object"||Array.isArray(e))return{};const a={};for(const[o,i]of Object.entries(e)){if(o===n||!t.includes(o)||!Array.isArray(i))continue;const s=Array.from(new Set(i.filter(l=>typeof l=="string"))).filter(l=>{const c=Dn(l);return!!c&&!!r[n]?.[c.panelId]?.[c.index]&&!!r[o]?.[c.panelId]?.[c.index]});s.length>0&&(a[o]=s)}return a}function At(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,r]of Object.entries(e))!r||typeof r!="object"||Array.isArray(r)||(t[n]=z(r));return t}function Hn(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.x=="number"?t.x:U.x,r=typeof t.y=="number"?t.y:U.y;return _({x:n,y:r})?void 0:{x:n,y:r}}function z(e){const t=e&&typeof e=="object"?e:{},n=Hn(t.anchor),r={...Ie,...t};n?r.anchor=n:delete r.anchor,typeof r.reserveLines!="number"||!Number.isFinite(r.reserveLines)||r.reserveLines<Fe.reserveLines.min?delete r.reserveLines:r.reserveLines=Math.floor(r.reserveLines),r.band!=="bottom"&&delete r.band;for(const a of["bandInset","lineHeight","bandCenter"]){const{min:o,max:i}=Fe[a],s=r[a];(typeof s!="number"||!Number.isFinite(s)||s<o||s>i)&&delete r[a]}return r}function Gn(e){const t=e&&typeof e=="object"?e:{};return{...z(t),text:typeof t.text=="string"?t.text:""}}function Pt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,r]of Object.entries(e))Array.isArray(r)?r.length>0&&(t[n]=r.map(z)):r&&typeof r=="object"&&(t[n]=[z(r)]);return t}function Un(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=L){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=At(t),a=c=>r[c]??Ie,o=c=>{if(!c||typeof c!="object"||Array.isArray(c))return{};const d={};for(const[h,p]of Object.entries(c))Array.isArray(p)?p.length>0&&(d[h]=p.map(Gn)):p&&typeof p=="object"&&typeof p.headline=="string"&&(d[h]=Un(p,a(h)));return d},i=Object.entries(e);if(i.some(([,c])=>!!c&&typeof c=="object"&&typeof c.headline=="string"))return{[n]:o(e)};const l={};for(const[c,d]of i)l[c]=o(d);return l}function Ct(e,t,n=L){return e[t??n]??e[n]??{}}function kt(e,t=L){const n={},r=Object.keys(e),a=r.includes(t)?[t,...r.filter(o=>o!==t)]:r;for(const o of a){const i=e[o];if(i)for(const[s,l]of Object.entries(i))n[s]===void 0&&Array.isArray(l)&&l.length>0&&(n[s]=l.map(jn))}return n}function me(e,t=L,n=mt){const r={};return e.family!==void 0&&e.family!==n&&(r.family=e.family),e.locale!==void 0&&e.locale!==t&&(r.locale=e.locale),r}function ze(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 Wn={order:["family","locale"],reach:"strict"};function $n(e,t){const n=ge(e),r=t.order.filter(o=>n.includes(o));return Array.from({length:r.length+1},(o,i)=>r.slice(0,r.length-i)).map(o=>{const i={};return o.includes("family")&&(i.family=e.family),o.includes("locale")&&(i.locale=e.locale),i})}function qn(e,t,n,r,a=L){const o=me(n,a);for(const i of $n(o,r)){const s=e(t,i);if(s!==void 0){const l=ge(i);return{value:s,resolvedAt:i,inherited:ge(o).filter(c=>!l.includes(c))}}}return{value:void 0,resolvedAt:null,inherited:[]}}function ye(e,t,n=L){const r=a=>{const o=new Set,i=[];for(const s of a)typeof s=="string"&&s&&!o.has(s)&&(o.add(s),i.push(s));return i};if(Array.isArray(t)){const a=r(t);if(a.length>0)return a.includes(n)?a:[...a,n]}return r([n,...Object.keys(e)])}function Ae(e,t=L){return typeof e=="string"&&e.length>0?e:t}const H={"it-IT":"it"};function De(e,t){if(!(Object.keys(e).some(a=>a in H)||(t?.some(a=>a in H)??!1)))return{captionText:e,locales:t};const r={};for(const[a,o]of Object.entries(e))a in H||(r[a]=o);for(const[a,o]of Object.entries(e)){const i=H[a];i&&(r[i]={...o,...r[i]??{}})}return{captionText:r,locales:t?.map(a=>H[a]??a)}}function Ot(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,r]of Object.entries(e)){if(n===mt||!r||typeof r!="object"||Array.isArray(r))continue;const a={};for(const[o,i]of Object.entries(r)){if(!i||typeof i!="object"||Array.isArray(i))continue;const s={};for(const[l,c]of Object.entries(i))Array.isArray(c)&&c.every(d=>typeof d=="string")&&(s[l]=c);Object.keys(s).length>0&&(a[o]=s)}Object.keys(a).length>0&&(t[n]=a)}return t}function $(e,t){return t.flatMap(n=>e.filter(r=>r.panelId===n.id))}let He=0,Ge=0;function Vn(e,t){const n=(r,a)=>r.reduce((o,i)=>{const s=i.startsWith(a)?Number.parseInt(i.slice(a.length),10):NaN;return Number.isFinite(s)&&s>o?s:o},0);He=Math.max(He,n(e,"shot-")),Ge=Math.max(Ge,n(t,"panel-"))}const Kn=.49;function Yn(e,t,n){return e.map(r=>{const a=n*r.heightFrac,o=r.height>0?r.width*(a/r.height):a*Kn,s=r.panelIndex*t+t/2-o/2+r.hOffset*(t/2),c=(n-a)/2+r.vOffset*(n/2);return{shotId:r.shotId,bytes:r.bytes,x:Math.round(s),y:Math.round(c),width:Math.round(o),height:Math.round(a),panelIndex:r.panelIndex,...r.clipToFrame===!0?{clipToFrame:!0}:{}}})}function Xn(e,t){const n=t.panelBackgrounds??{},r=Q(t),a=r.stops[0]?.color??t.gradientFrom;if(!e.some(l=>n[l.id]))return t.bgMode==="gradient"?{kind:"gradient",gradient:r}:{kind:"perPanel",colors:e.map(l=>t.panelColors[l.id]??a)};const i=l=>t.bgMode==="gradient"?{kind:"gradient",gradient:r}:{kind:"solid",color:t.panelColors[l]??a};return{kind:"perPanelFills",fills:e.map(l=>{const c=n[l.id];return c?c.kind==="solid"?{kind:"solid",color:c.color}:{kind:"gradient",gradient:Bn(c)}:i(l.id)})}}function Tt(e,t,n=L){const r=Q(e);return{panelPresetId:e.panelPresetId,bgMode:e.bgMode,gradientStops:r.stops,gradientAngle:r.angle,...Z(r),panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection??!1,panelBackgrounds:e.panelBackgrounds??{},captionStyles:{...Pt(e.captionStyles),...kt(t,n)}}}function Jn(e){const t=e.caption.reserveLines,n=typeof t=="number"?` Reserves ${t} headline lines (~${Math.round(t*18*(e.caption.maxWidth??.86)/.86)} characters).`:"";return`${e.blurb}${n} Character: [${e.character}]. Bleeds: [${e.bleedEdge}].`}function Zn(e,t){return Math.max(0,...Object.values(e).map(n=>n[t]?.length??0))}function Et(e,t,n){const r=e&&typeof e=="object"&&!Array.isArray(e)?e:{},a=new Set,o={};let i=1;const s=()=>{for(;a.has(`caption-${i}`);)i+=1;const l=`caption-${i}`;return a.add(l),i+=1,l};for(const l of n){const c=Zn(t,l);if(c===0)continue;const d=Array.isArray(r[l])?r[l]:[];o[l]=Array.from({length:c},(h,p)=>{const f=d[p];return typeof f=="string"&&f.length>0&&!a.has(f)?(a.add(f),f):s()})}return o}const Lt=2;function Qn(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return t.schema===Lt&&Array.isArray(t.shots)&&Array.isArray(t.panels)&&t.panels.length>0}function er(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Qn(t))return null;const n=t,r=new Set(n.panels.map(u=>u.id)),a=n.shots.filter(u=>r.has(u.panelId)).map(u=>({id:u.id,panelId:u.panelId,frameNodeId:u.frameNodeId,frameName:u.frameName,bytes:null,look:yt(u.look),thumb:null,thumbStale:!0,inputAnchor:u.inputAnchor,outputAnchor:u.outputAnchor})),o=n.panels.map(u=>({id:u.id}));Vn(a.map(u=>u.id),o.map(u=>u.id));const i=n.selectedShotId&&a.some(u=>u.id===n.selectedShotId)?n.selectedShotId:a[0]?.id??null,s=Ae(n.baseLocale),{captionText:l,locales:c}=De(re(n.captionText,n.captionStyles,s),n.locales),d=De({[s]:{}},[s]).locales?.[0]??s,h=ye(l,c,d),p=Et(n.captionSlotIds,l,o.map(u=>u.id)),f=ne(n.gradientStops,n.gradientAngle);return{shots:$(a,o),panels:o,selectedShotId:i,panelPresetId:W(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:It(n.panelBackgrounds),captionText:l,locales:h,baseLocale:d,staleCaptionTranslations:xt(n.staleCaptionTranslations,h,d,l),outputs:ht(n.outputs,n.panelPresetId),localeScreens:{},outputCaptions:Ot(n.outputCaptions),captionSlotIds:p,frameTemplates:n.frameTemplates&&typeof n.frameTemplates=="object"&&!Array.isArray(n.frameTemplates)?n.frameTemplates:{}}}function Mt(e){const n=$(e.shots,e.panels).map(o=>({id:o.id,panelId:o.panelId,frameNodeId:o.frameNodeId,frameName:o.frameName,look:bt(o.look),inputAnchor:o.inputAnchor,outputAnchor:o.outputAnchor})),r=Ae(e.baseLocale),a=re(e.captionText,e.captionStyles,r);return{schema:Lt,shots:n,panels:e.panels,selectedShotId:e.selectedShotId,captionText:a,locales:ye(a,e.locales,r),baseLocale:r,staleCaptionTranslations:xt(e.staleCaptionTranslations,ye(a,e.locales,r),r,a),outputs:ht(e.outputs,e.panelPresetId),outputCaptions:Ot(e.outputCaptions),...(()=>{const o=Et(e.captionSlotIds,a,e.panels.map(i=>i.id));return Object.keys(o).length>0?{captionSlotIds:o}:{}})(),...e.frameTemplates&&Object.keys(e.frameTemplates).length>0?{frameTemplates:e.frameTemplates}:{},...Tt(e,a,r)}}const tr=1;function nr(e){const t=$(e.shots,e.panels),n=re(e.captionText,e.captionStyles),r=Ae(e.baseLocale);return{schema:tr,shots:t.map(a=>({panelId:a.panelId,look:bt(a.look)})),...Tt(e,n,r)}}ut.map(e=>({id:e.id,label:e.label,description:e.blurb}));dt.map(e=>({id:e.id,label:e.label,description:Jn(e),character:e.character}));const rr=[{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","structure-remove-populated-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:["patchAllCaptionStyles"],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:["moveCaptionLayer","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:"Agent refinement cannot translate captions. Open Translate in Studio to create German or other language versions while retaining the base words.",evaluationCases:["unsupported-translate-captions"]},{id:"caption.output-override",domain:"caption",status:"intentionally_excluded",summary:"Give an output family its own caption wording.",studioCommands:["patchOutputCaption","clearOutputCaption"],wire:null,targets:["caption"],requiredInputs:[],reason:"Agent refinement edits shared captions. Studio has manual output-family caption wording; both iPhone sizes share one family and cannot have separate words.",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","background-frame-three-flat-teal","inspection-correct-frame-two-background"]},{id:"frame.background-reset",domain:"look",status:"intentionally_excluded",summary:"Drop one or all frame backdrops so they inherit the strip again.",studioCommands:["clearPanelBackground","clearAllPanelBackgrounds"],wire:null,targets:["frame"],requiredInputs:[],reason:"Agent refinement cannot reset backdrop inheritance. Use the frame background reset control in Studio to return it to the strip default.",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 the legacy per-panel colour that sits under a frame’s backdrop.",studioCommands:["setPanelColor"],wire:null,targets:["frame"],requiredInputs:[],reason:"No Studio surface dispatches it. A flat colour is a one-stop frame backdrop (`frame.background`), so a request for one is carried out there, never refused.",evaluationCases:["background-frame-three-flat-teal"]},{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:"Agent refinement cannot restore a saved Look. Restore the saved version through Look version history in Studio.",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:"supported",summary:"Add new screenshots to the project, each in its own frame.",studioCommands:["addShots"],wire:"structuralOperations: frame.add with a frameRef, then device.add into it, once per screenshot from AVAILABLE SCREENSHOTS",targets:["project"],requiredInputs:["one or more authorized screenshot references"],withoutAssetReason:"New frames need new pixels. Send the screenshots with the instruction and the Agent will place them.",limits:["Only screenshots listed in AVAILABLE SCREENSHOTS; the Agent never invents or generates pixels.","Each new frame starts without a caption and its device on the default look, exactly as adding screenshots in Studio does."],evaluationCases:["asset-add-screens-supplied","asset-add-screens-needs-pixels"]},{id:"strip.replace-screens",domain:"asset",status:"supported",summary:"Replace every screenshot in the project, keeping the styling.",studioCommands:["loadScreens"],wire:"structuralOperations: device.replace on every device in strip order, each naming its own assetId from AVAILABLE SCREENSHOTS",targets:["project"],requiredInputs:["a complete authorized screenshot set"],withoutAssetReason:"Replacing the whole strip needs the whole new set of pixels. This instruction carried none.",limits:["Only screenshots listed in AVAILABLE SCREENSHOTS; the Agent never invents or generates pixels.","Every device keeps its look, placement, captions, translated screens and Output membership — only the pixels change.","When the supplied screenshots do not match the devices one to one, the Agent asks which goes where instead of swapping some of them."],evaluationCases:["asset-replace-all-screens-supplied","asset-replace-all-screens-needs-pixels","asset-replace-all-screens-count-mismatch"]},{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"]}],ar="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."}],ir=rr.map(e=>e.id);[...ir,...or.map(e=>e.id)];const Ft=[{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"}];Ft.map(e=>e.code);Object.fromEntries(Ft.map(e=>[e.code,e]));({...Ie});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 lr(e,t,n){e.screen.setGlare(!n&&t?e.device.environmentMap():null)}async function cr(e,t,n,r){const{stage:a,device:o,screen:i}=e,s=t.material==="clay";if(t.cameraPos?a.setCameraPosition(t.cameraPos):a.setAngle(t.angle),o.setRoll(R("roll",t.roll)),sr(o,t),i.restoreScreen(),a.setStudioLighting(t.lighting),o.setReflections(t.reflections),i.setFlatScreen(t.flatScreen),n){try{await i.applyWallpaper(n)}catch{}if(!r())return}lr(e,t.glare,s)}async function dr(e,t,n,r={}){const a=e.renderer,o=a.getPixelRatio(),i=a.getSize(new cn);a.setDrawingBufferSize(t,n,1);let s;r.cameraPosition?(s=new nt(at,t/n,.01,100),s.position.copy(r.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{a.render(e.scene,s)}finally{e.scene.background=l}const c=document.createElement("canvas");let d=null;try{c.width=t,c.height=n,c.getContext("2d")?.drawImage(a.domElement,0,0);const h=ur(c),p=h.canvas;d=p;const f=p.width,u=p.height;return{bytes:await new Promise((g,y)=>{p.toBlob(b=>{if(!b){y(new Error("toBlob returned null"));return}b.arrayBuffer().then(w=>g(new Uint8Array(w)),y)},"image/png")}),width:f,height:u,frame:{width:t,height:n,cropX:h.x,cropY:h.y}}}finally{d&&d!==c&&(d.width=0,d.height=0),c.width=0,c.height=0}}finally{a.setDrawingBufferSize(i.x,i.y,o),e.invalidate()}}function ur(e,t=2){const n=e.getContext("2d",{willReadFrequently:!0});if(!n)return{canvas:e,x:0,y:0};const{width:r,height:a}=e,o=n.getImageData(0,0,r,a).data;let i=r,s=a,l=-1,c=-1;for(let f=0;f<a;f++)for(let u=0;u<r;u++)o[(f*r+u)*4+3]!==0&&(u<i&&(i=u),u>l&&(l=u),f<s&&(s=f),f>c&&(c=f));if(l<i||c<s)return{canvas:e,x:0,y:0};i=Math.max(0,i-t),s=Math.max(0,s-t),l=Math.min(r-1,l+t),c=Math.min(a-1,c+t);const d=l-i+1,h=c-s+1,p=document.createElement("canvas");return p.width=d,p.height=h,p.getContext("2d")?.drawImage(e,i,s,d,h,0,0,d,h),{canvas:p,x:i,y:s}}const pr=1900,fr=2800;function hr(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 mr(e,t,n,r,a,o){const i=$(n,r),s=i.filter(f=>f.bytes);if(s.length===0)return null;const l=W(a.panelPresetId),c=new Map(r.map((f,u)=>[f.id,u])),d=a.renderScale!=null&&a.renderScale>0&&a.renderScale<=1?a.renderScale:1,h=Math.max(1,Math.round(pr*d)),p=Math.max(1,Math.round(fr*d));{const f=[];for(let y=0;y<s.length;y++){const b=s[y];o(`Rendering ${y+1}/${s.length}${a.locale?` (${a.locale})`:""}…`),await cr(t,b.look,b.bytes,()=>!0);const w=await dr(e,h,p,{cameraPosition:vn(b.look.angle,b.look.cameraPos)});f.push({shotId:b.id,bytes:w.bytes,width:w.width,height:w.height,heightFrac:R("phoneHeight",b.look.phoneHeight)/100,hOffset:R("hOffset",b.look.hOffset)/100,vOffset:R("vOffset",b.look.vOffset)/100,panelIndex:c.get(b.panelId)??0,...b.look.clipToFrame===!0?{clipToFrame:!0}:{}})}const u=Yn(f,l.width,l.height),m=Xn(r,a);o("Saving project…");const g=Mt(hr(i,r,a));return{panelWidth:l.width,panelHeight:l.height,count:r.length,background:m,phones:u,shadow:a.shadow,floorReflection:a.floorReflection??!1,project:JSON.stringify(g)}}}function ee(e){return/^[0-9a-fA-F]{3,8}$/.test(e)?`#${e}`:e}function be(e,t,n,r){const{x0:a,y0:o,x1:i,y1:s}=Nn(n,r,t.angle),l=e.createLinearGradient(a,o,i,s);for(const c of t.stops)l.addColorStop(Math.min(1,Math.max(0,c.at)),ee(c.color));return l}function Ue(e,t,n,r,a,o,i){const s=document.createElement("canvas");s.width=n,s.height=r;try{const l=s.getContext("2d");if(!l)return;l.fillStyle=be(l,t,n,r),l.fillRect(a,0,o,r),e.drawImage(s,a,0,o,r,i,0,o,r)}finally{s.width=0,s.height=0}}function We(e){return URL.createObjectURL(new Blob([e],{type:"image/png"}))}function $e(e){return new Promise((t,n)=>{const r=new Image;r.onload=()=>t(r),r.onerror=n,r.src=e})}const gr=16*1024*1024;function qe(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 Rt(){let e=0;const t=Array.from(F.values());for(const a of t)e+=qe(a);const n=Array.from(F.keys());let r=0;for(;e>gr&&r<n.length-1;){const a=F.get(n[r]);a&&(e-=qe(a),a.reflection&&(a.reflection.canvas.width=0,a.reflection.canvas.height=0),F.delete(n[r])),r++}}const F=new Map,ie=new Map;async function yr(e,t){if(!t){const o=We(e);try{return{img:await $e(o)}}finally{URL.revokeObjectURL(o)}}const n=F.get(e);if(n)return F.delete(e),F.set(e,n),n;const r=ie.get(e);if(r)return r;const a=(async()=>{const o=We(e);try{return{img:await $e(o)}}finally{URL.revokeObjectURL(o)}})();ie.set(e,a);try{const o=await a;return F.set(e,o),Rt(),o}finally{ie.delete(e)}}async function br(e,t,n){const r=t.panelWidth*t.count,a=t.panelHeight,o=n?.backgroundViewport,i=o?t.panelWidth*o.panelCount:r,s=o?t.panelWidth*o.panelIndex:0;e.width=r,e.height=a;const l=e.getContext("2d");if(!l)return;n?.transparentBg?l.clearRect(0,0,r,a):t.background.kind==="gradient"?o?Ue(l,t.background.gradient,i,a,s,r,0):(l.fillStyle=be(l,t.background.gradient,r,a),l.fillRect(0,0,r,a)):t.background.kind==="perPanel"?t.background.colors.forEach((p,f)=>{l.fillStyle=ee(p),l.fillRect(f*t.panelWidth,0,t.panelWidth,a)}):t.background.fills.forEach((p,f)=>{const u=f*t.panelWidth;p.kind==="solid"?(l.fillStyle=ee(p.color),l.fillRect(u,0,t.panelWidth,a)):o?Ue(l,p.gradient,i,a,s+u,t.panelWidth,u):(l.fillStyle=be(l,p.gradient,r,a),l.fillRect(u,0,t.panelWidth,a))});const c=n?.phoneRange?Math.max(0,n.phoneRange.start):0,d=n?.phoneRange?Math.min(t.phones.length,n.phoneRange.end):t.phones.length,h=n?.reuseDecodedPhones===!0;for(let p=c;p<d;p++){const f=t.phones[p],u=n?.clip==="panel"||n?.clip===void 0&&f.clipToFrame===!0;if(n?.skipShotId&&f.shotId===n.skipShotId)continue;const m=await yr(f.bytes,h),g=m.img;u&&(l.save(),l.beginPath(),l.rect(f.panelIndex*t.panelWidth,0,t.panelWidth,a),l.clip()),t.floorReflection&&vr(l,m,f,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(g,f.x,f.y,f.width,f.height),l.restore(),u&&l.restore()}}function vr(e,t,n,r){const a=Math.round(n.height*.45);if(a<=0||n.width<=0)return;const o=r?t.reflection:void 0;if(o&&o.width===n.width&&o.height===n.height){e.drawImage(o.canvas,n.x,n.y+n.height,n.width,a);return}const i=t.img,s=document.createElement("canvas");s.width=Math.max(1,n.width),s.height=Math.max(1,a);const l=s.getContext("2d");if(!l)return;l.save(),l.translate(0,a),l.scale(1,-1),l.drawImage(i,0,a-n.height,n.width,n.height),l.restore();const c=l.createLinearGradient(0,0,0,a);c.addColorStop(0,"rgba(0,0,0,0.34)"),c.addColorStop(1,"rgba(0,0,0,0)"),l.globalCompositeOperation="destination-in",l.fillStyle=c,l.fillRect(0,0,n.width,a),e.drawImage(s,n.x,n.y+n.height,n.width,a),r&&(t.reflection={width:n.width,height:n.height,canvas:s},Rt())}const Nt=1.2,wr=.06,Sr=.5;function Ir(e,t,n){if(!e)return[t];const r=t.split(/\s+/).filter(Boolean),a=[];let o="";for(const i of r){const s=o?`${o} ${i}`:i;o&&e.measureText(s).width>n?(a.push(o),o=i):o=s}return o&&a.push(o),a}function xr(e,t){if(!e)return 0;let n=0;for(const r of t)n=Math.max(n,e.measureText(r).width);return n}function _t(e,t,n,r){const a=t.map((u,m)=>{const g=u.text.trim();if(!g)return null;const y=u.sizePt*(r/te),b=Se(u.fontId).family;e&&(e.font=`${G} ${y}px "${b}"`);const w=Ir(e,g,u.maxWidth*n),I=y*(u.lineHeight??Nt),A=w.length*I,k=Math.max(A,(u.reserveLines??w.length)*I);return{layer:u,index:m,lines:w,px:y,lineH:I,width:xr(e,w),blockH:A,reservedH:k}}),o=a.filter(u=>u!==null&&_(u.layer.anchor)),i=o.filter(u=>u.layer.band==="bottom"),s=o.filter(u=>u.layer.band!=="bottom"),l=u=>u[0]?.layer.bandInset??wr,c=(1-l(i))*r-i.reduce((u,m)=>u+m.reservedH,0);let d=l(s)*r,h=c,p=0;const f=[];for(const u of a){if(!u)continue;if(!_(u.layer.anchor)){f.push({layer:u.layer,index:u.index,x:u.layer.anchor.x*n,yTop:u.layer.anchor.y*r,lines:u.lines,px:u.px,lineH:u.lineH,width:u.width});continue}const m=u.layer.band==="bottom",y=(m?h:d)+(u.reservedH-u.blockH)/2,b=(u.layer.bandCenter??Sr)*n;f.push({layer:u.layer,index:u.index,x:b,yTop:y,lines:u.lines,px:u.px,lineH:u.lineH,width:u.width}),m?h+=u.reservedH:d+=u.reservedH,p=Math.max(p,y+u.blockH)}return{placed:f,bandBottom:p}}function Ar(e,t,n,r){e.save(),e.textBaseline="top";const{placed:a}=_t(e,t,n,r);for(const o of a){e.fillStyle=ee(o.layer.color),e.textAlign=o.layer.align,e.font=`${G} ${o.px}px "${Se(o.layer.fontId).family}"`;let i=o.yTop;for(const s of o.lines)e.fillText(s,o.x,i),i+=o.lineH}e.restore()}function Pr(e,t){return e.sizePt*(t/te)*(e.lineHeight??Nt)}function Cr(e,t,n,r){const a=_t(e,t,n,r).placed;e?.save(),e&&(e.textBaseline="top");const o=a.map(i=>{const s=i.layer.align==="center"?i.x-i.width/2:i.layer.align==="right"?i.x-i.width:i.x,l=i.lines.length*i.lineH;let c=i.yTop,d=i.yTop+l;if(e){e.font=`${G} ${i.px}px "${Se(i.layer.fontId).family}"`;const h=e.measureText(i.lines[0]).actualBoundingBoxAscent,p=e.measureText(i.lines[i.lines.length-1]).actualBoundingBoxDescent;Number.isFinite(h)&&Number.isFinite(p)&&(c=i.yTop-h,d=i.yTop+(i.lines.length-1)*i.lineH+p)}return{index:i.index,anchorX:i.x/n,top:i.yTop/r,left:s/n,width:i.width/n,height:l/r,inkTop:c/r,inkHeight:(d-c)/r}});return e?.restore(),o}function kr(e,t,n,r,a){t.forEach((o,i)=>{const s=a[o];!s||s.length===0||(e.save(),e.translate(i*n,0),Ar(e,s,n,r),e.restore())})}const Or=""+new URL("Inter-SemiBold-B9yU462S.woff2",import.meta.url).href,Tr=""+new URL("Manrope-SemiBold-44nuYf5T.woff2",import.meta.url).href,Er=""+new URL("Poppins-SemiBold-DJVo7c07.woff2",import.meta.url).href,Lr=""+new URL("Fraunces-SemiBold-BtraKtER.woff2",import.meta.url).href,Mr=""+new URL("SpaceGrotesk-SemiBold-CyO2J5Y0.woff2",import.meta.url).href,Fr={inter:Or,manrope:Tr,poppins:Er,fraunces:Lr,"space-grotesk":Mr},Rr=J.map(e=>({...e,url:Fr[e.id]}));let K=null;function Nr(){return K||(K=(async()=>{const e=new Set;document.fonts.forEach(t=>e.add(`${t.family}/${t.weight}`)),await Promise.all(Rr.filter(t=>!e.has(`${t.family}/${String(G)}`)).map(async t=>{const n=new FontFace(t.family,`url(${t.url})`,{weight:String(G)});await n.load(),document.fonts.add(n)})),await document.fonts.ready})().catch(e=>{throw K=null,e})),K}const _r=22/te,Br=1.1,Ve=4,jr=.063,zr=.167,Ke=Math.round(_r*te),Dr=.02,Hr=50;function N(e,t){const n=Math.max(e.x,t.x),r=Math.max(e.y,t.y),a=Math.min(e.x+e.width,t.x+t.width),o=Math.min(e.y+e.height,t.y+t.height);return a<=n||o<=r?null:{x:n,y:r,width:a-n,height:o-r}}function Bt(e,t){return N(e,t)}function Ye(e,t){const n=Math.abs(e-t)%360;return n>180?360-n:n}function Xe(e){return yn(e.cameraPos??rt(e.angle))}function Pe(e,t,n,r,a,o,i){if(t.length===0)return[];const s=[];for(const l of Cr(e,t,a,o)){const c=t[l.index];if(!c)continue;const d=Bt({x:(r+l.left)*a,y:l.top*o,width:l.width*a,height:l.height*o},i);d&&s.push({ref:{panelId:n,panelIndex:r,layerIndex:l.index,band:_(c.anchor)},box:d,lineHeightPx:Pr(c,o)})}return s}function Gr(e,t,n,r){return n.height<=ve(e,t,r)}function ve(e,t,n){const r=e.y+e.height/2<=t.y+t.height/2;return n*(r?zr:jr)}function Ur(e,t,n){return n.height<=ve(e.box,t.box,e.lineHeightPx)+ve(t.box,e.box,t.lineHeightPx)}function Wr(e){const{ctx:t,panelIds:n,panelWidth:r,panelHeight:a}=e,o={x:0,y:0,width:r*n.length,height:a};t?.save();try{return $r(e,o)}finally{t?.restore()}}function $r(e,t){const{ctx:n,panelIds:r,panelWidth:a,panelHeight:o,captionLayers:i}=e,s=r.flatMap((m,g)=>Pe(n,i[m]??[],m,g,a,o,t)),l=[];for(const m of e.phones){const y=e.clip==="panel"||e.clip===void 0&&m.clipToFrame===!0?{x:m.panelIndex*a,y:0,width:a,height:o}:t,b=Bt({x:m.x,y:m.y,width:m.width,height:m.height},y);b&&l.push({shotId:m.shotId,box:b,layoutHeight:m.height})}const c=[];for(let m=0;m<s.length;m++)for(let g=m+1;g<s.length;g++){const y=N(s[m].box,s[g].box);y&&c.push({a:s[m].ref,b:s[g].ref,overlap:y,depthFrac:o>0?y.height/o:0,inkUncertain:Ur(s[m],s[g],y)})}const d=[];for(const m of s)for(const g of l){const y=N(m.box,g.box);y&&d.push({caption:m.ref,shotId:g.shotId,overlap:y,depthFrac:o>0?y.height/o:0,inkUncertain:Gr(m.box,g.box,y,m.lineHeightPx)})}const h=[];for(let m=0;m<l.length;m++)for(let g=m+1;g<l.length;g++){const y=l[m],b=l[g],w=N(y.box,b.box);if(!w)continue;const I=[],A=Math.max(y.layoutHeight,b.layoutHeight),k=Math.min(y.layoutHeight,b.layoutHeight);k>0&&A/k>=Br&&I.push("scale");const v=e.looks?.[y.shotId],S=e.looks?.[b.shotId],P=!!v&&!!S;v&&S&&(Ye(Xe(v),Xe(S))>=Ve&&I.push("angle"),Ye(R("roll",v.roll),R("roll",S.roll))>=Ve&&I.push("roll"));const C=Math.min(y.box.width*y.box.height,b.box.width*b.box.height);h.push({shotIdA:y.shotId,shotIdB:b.shotId,overlap:w,coverage:C>0?w.width*w.height/C:0,deliberate:I.length>0,signals:I,anglesKnown:P})}const p=new Set;for(const m of c)m.a.band&&p.add(m.a.panelId),m.b.band&&p.add(m.b.panelId);for(const m of d)m.caption.band&&p.add(m.caption.panelId);const f=[],u=[];return r.forEach((m,g)=>{if(!p.has(m))return;const y=qr(e,m,g,t,s,l);y!==null&&u.push({panelId:m,maxWidth:y});const b=Vr(e,m,g,t,s,l);b&&f.push(b)}),{captionsMeasurable:n!=null,captionOverlaps:c,captionDeviceOverlaps:d,deviceOverlaps:h,suggestions:f,widthSuggestions:u,hasWarning:c.length>0||d.length>0||h.some(m=>!m.deliberate)}}function qr(e,t,n,r,a,o){const i=e.captionLayers[t]??[];if(!i.length||i.some(c=>!_(c.anchor)))return null;const s=[...o.map(c=>c.box),...a.filter(c=>c.ref.panelId!==t).map(c=>c.box)],l=Math.min(...i.map(c=>c.maxWidth));for(let c=Math.ceil(l*100)-1;c>=10;c--){const d=c/100,h=Pe(e.ctx,i.map(p=>({...p,maxWidth:d})),t,n,e.panelWidth,e.panelHeight,r);if(h.length&&h.every((p,f)=>!s.some(u=>N(p.box,u))&&!h.slice(f+1).some(u=>N(p.box,u.box))))return d}return null}function Vr(e,t,n,r,a,o){const i=e.captionLayers[t]??[],s=i.flatMap((d,h)=>_(d.anchor)&&d.text.trim()?[h]:[]);if(s.length===0||!(Math.min(...s.map(d=>i[d].sizePt))>Ke))return null;const c=[...o.map(d=>d.box),...a.filter(d=>!(d.ref.panelId===t&&d.ref.band)).map(d=>d.box)];for(let d=1;d<=Hr;d++){const h=Math.round((1-d*Dr)*100)/100,p=new Map(s.map(g=>[g,i[g].sizePt*h]));if(Math.min(...p.values())<Ke)return null;const f=i.map((g,y)=>{const b=p.get(y);return b===void 0?g:{...g,sizePt:b}});if(Pe(e.ctx,f,t,n,e.panelWidth,e.panelHeight,r).filter(g=>g.ref.band).every(g=>!c.some(y=>N(g.box,y))))return{panelId:t,scale:h,layers:s.map(g=>({layerIndex:g,fromSizePt:i[g].sizePt,toSizePt:p.get(g)}))}}return null}function jt(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 Kr=1900,Yr=2800;function Xr(e){return new Promise((t,n)=>{e.toBlob(r=>{if(!r)return n(new Error("canvas toBlob returned null"));r.arrayBuffer().then(a=>t(new Uint8Array(a)),n)},"image/png")})}async function Dt(e,t,n,r={}){await Nr();const a=zt(e),o=a.map(p=>({id:p})),i=e.shots.map((p,f)=>({id:jt(f),panelId:p.panelId,frameNodeId:"",frameName:`screen-${f+1}`,bytes:t.screens[f]??null,look:p.look,thumb:null,thumbStale:!1})),l={panelPresetId:W(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:r.renderScale,locale:n},c=r.onProgress??(()=>{});let d=r.refs,h=null;if(!d){const p=Cn(e.panelPresetId);h=ot(Kr,Yr);const f=await it(h,p);d={stage:h,device:new st(f,h.scene,h.renderer,p),screen:new lt(f,p)}}try{const p=await mr(d.stage,d,i,o,l,c);if(!p)throw new Error("buildStrip: nothing renderable (no shots)");const f=document.createElement("canvas");try{const u={...r.clip?{clip:r.clip}:{},...r.backgroundViewport?{backgroundViewport:r.backgroundViewport}:{}};await br(f,p,Object.keys(u).length>0?u:void 0);const m=p.panelWidth,g=p.panelHeight,y=f.getContext("2d");if(!y)throw new Error("buildStrip: 2D context unavailable");kr(y,a,m,g,t.captionText??{});const b=Wr({phones:p.phones,panelIds:a,captionLayers:t.captionText??{},panelWidth:m,panelHeight:g,ctx:y,looks:Object.fromEntries(i.map(A=>[A.id,A.look])),clip:r.clip}),w=r.renderScale&&r.renderScale>0&&r.renderScale<1?r.renderScale:1,I=[];for(let A=0;A<p.count;A++){const k=document.createElement("canvas");k.width=Math.max(1,Math.round(m*w)),k.height=Math.max(1,Math.round(g*w));const v=k.width,S=k.height;try{const P=k.getContext("2d");if(!P)throw new Error("buildStrip: 2D context unavailable");P.drawImage(f,A*m,0,m,g,0,0,v,S),I.push({index:A,bytes:await Xr(k),width:v,height:S})}finally{k.width=0,k.height=0}}return{panels:I,layout:b}}finally{f.width=0,f.height=0}}finally{h&&h.dispose()}}const Je=.005,Jr=.02,Zr=12;function se(e){return Math.max(1,Math.round(e*100))}function le(e){return`${e.kind}:${e.frames.join(",")}:${e.anchored}`}function Qr(e,t){const n=new Map(e.suggestions.map(i=>[i.panelId,i.scale])),r=new Map((e.widthSuggestions??[]).map(i=>[i.panelId,i.maxWidth])),a=[];for(const i of e.captionOverlaps){if(i.inkUncertain||i.depthFrac<Je)continue;const[s,l]=i.a.band===i.b.band?[i.a,i.b]:i.a.band?[i.a,i.b]:[i.b,i.a],c=ce([t.frameOfPanel(s.panelId),t.frameOfPanel(l.panelId)]);c.length!==0&&a.push({kind:"caption-caption",frames:c,depthPct:se(i.depthFrac),anchored:!i.a.band||!i.b.band,...Ze(n,s.band?s.panelId:null),...Qe(r,s.band?s.panelId:null)})}for(const i of e.captionDeviceOverlaps){if(i.inkUncertain||i.depthFrac<Je)continue;const s=ce([t.frameOfPanel(i.caption.panelId),t.frameOfShot(i.shotId)]);s.length!==0&&a.push({kind:"caption-device",frames:s,depthPct:se(i.depthFrac),anchored:!i.caption.band,...Ze(n,i.caption.band?i.caption.panelId:null),...Qe(r,i.caption.band?i.caption.panelId:null)})}for(const i of e.deviceOverlaps){if(i.deliberate||i.coverage<Jr)continue;const s=ce([t.frameOfShot(i.shotIdA),t.frameOfShot(i.shotIdB)]);s.length!==0&&a.push({kind:"device-device",frames:s,depthPct:se(i.coverage),anchored:!1})}const o=new Map;for(const i of a){const s=o.get(le(i));s?i.depthPct>s.depthPct?o.set(le(i),{...i,clearAtScale:i.clearAtScale??s.clearAtScale,clearAtMaxWidth:i.clearAtMaxWidth??s.clearAtMaxWidth}):s.clearAtScale==null&&i.clearAtScale!=null&&(s.clearAtScale=i.clearAtScale):o.set(le(i),i),s&&s.clearAtMaxWidth==null&&i.clearAtMaxWidth!=null&&(s.clearAtMaxWidth=i.clearAtMaxWidth)}return[...o.values()].sort((i,s)=>s.depthPct-i.depthPct).slice(0,Zr).sort((i,s)=>i.frames[0]-s.frames[0]||i.kind.localeCompare(s.kind))}function ce(e){const[t,...n]=e;return t<0?[]:[t,...n.filter(r=>r>=0&&r!==t)]}function Ze(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtScale:Math.round(n*100)/100}}function Qe(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtMaxWidth:n}}function ea(e,t){if(t.length===0)return e;const n=e.captionStyles;if(!n)return e;const r={};for(const[a,o]of Object.entries(n))r[a]=o.map(i=>({...i}));for(const a of t){const o=r[a.panelId];if(o)for(const i of a.layers){const s=o[i.layerIndex];s&&(s.sizePt=i.toSizePt)}}return{...e,captionStyles:r}}function de(e){return e.frames.map(t=>`frame ${t+1}`).join(" + ")}function ta(e,t,n){if(!e.captionsMeasurable)return null;const r=Qr(e,t);if(r.length===0)return null;const a=r.filter(c=>c.kind==="device-device"),o=r.filter(c=>c.kind!=="device-device"&&c.clearAtScale==null);if(a.length>0||o.length>0||!n){const c=[];a.length>0&&c.push(`${a.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 d of o)c.push(d.anchored?`${de(d)}: 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(d)}: 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 c.length===0&&c.push("The rendered look was not available here, so no corrected look could be built."),{findings:r,note:`No corrected look is offered. ${c.join(" ")}`}}const i=new Set(r.filter(c=>c.kind!=="device-device").map(c=>c.frames[0])),s=e.suggestions.filter(c=>i.has(t.frameOfPanel(c.panelId))),l=s.flatMap(c=>{const d=t.frameOfPanel(c.panelId)+1;return c.layers.map(h=>`frame ${d} layer ${h.layerIndex+1}: sizePt ${h.fromSizePt} → ${h.toSizePt}`)}).join("; ");return{findings:r,correctedLook:ea(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 na(e){return String(Math.max(-100,Math.min(100,e*35)))}const ra=["roll","phoneHeight","hOffset","vOffset","finish","clearcoat"];function et(e){if(!e||typeof e!="object")return{};const t={...e};for(const n of ra)typeof t[n]=="number"&&(t[n]=String(t[n]));return t}function aa(e){const{subtitle:t,sizePt:n,maxWidth:r,...a}=e;return{...a,...n!==void 0?{sizePt:Number(n)}:{},...r!==void 0?{maxWidth:Number(r)}:{}}}function tt(e){const t={};for(const[n,r]of Object.entries(e))r!==void 0&&(t[n]=r);return t}function ue(e,t,n){const r={};return e&&t.forEach((a,o)=>{const i=n[o];i!==void 0&&e[a]!==void 0&&(r[i]=e[a])}),r}function oa(e){const t=e.slotSizes.filter(v=>Number.isInteger(v)&&v>0),n=t.map((v,S)=>`panel-${S+1}`);let r={};const a=e.look&&typeof e.look=="object"?e.look:null,o=e.style??null,i=a&&Array.isArray(a.shots)?a.shots:[],s=i.length>0,l=o?et(o.shotLook):null,c=[];let d=0;t.forEach((v,S)=>{for(let P=0;P<v;P++){const C=s?et(i[d]?.look??i[0]?.look??{}):l??{},D=C.hOffset!==void 0&&C.hOffset!==null?String(C.hOffset):na(P);c.push({panelId:n[S],look:{...x,...C,hOffset:D}}),d++}});let h={},p={},f={},u="gradient",m=Tn,g=En,y="vertical",b,w,I=!0,A=!1;if(a){u=a.bgMode==="perPanel"?"perPanel":"gradient";const v=Q({gradientStops:Array.isArray(a.gradientStops)?a.gradientStops:void 0,gradientAngle:typeof a.gradientAngle=="number"?a.gradientAngle:void 0,gradientFrom:typeof a.gradientFrom=="string"?a.gradientFrom:m,gradientTo:typeof a.gradientTo=="string"?a.gradientTo:g,gradientDir:a.gradientDir==="horizontal"?"horizontal":y});b=v.stops,w=v.angle,{gradientFrom:m,gradientTo:g,gradientDir:y}=Z(v),a.shadow===!1&&(I=!1),a.floorReflection===!0&&(A=!0);const S=[];for(const P of i){const C=typeof P?.panelId=="string"?P.panelId:null;C&&!S.includes(C)&&S.push(C)}h=ue(a.panelColors,S,n),p=ue(Pt(a.captionStyles),S,n),f=ue(It(a.panelBackgrounds),S,n)}else if(o){const v=o.background??{};u=v.mode==="perPanel"?"perPanel":"gradient";const S=Q({gradientStops:v.stops,gradientAngle:v.angle,gradientFrom:m,gradientTo:g,gradientDir:y});b=S.stops,w=S.angle,{gradientFrom:m,gradientTo:g,gradientDir:y}=Z(S),v.shadow===!1&&(I=!1),v.floorReflection===!0&&(A=!0),Array.isArray(v.panelColors)&&v.panelColors.forEach((P,C)=>{typeof P=="string"&&P&&n[C]!==void 0&&(h[n[C]]=P)})}if(o&&Array.isArray(o.captions)){const v=p,S=(M,ae)=>({...v[M]?.[ae]??{}}),P={},C={};o.captions.forEach((M,ae)=>{const B=n[ae];if(!(B===void 0||M===null||M===void 0)){if(Array.isArray(M)){const j=M.filter(E=>!!E&&typeof E=="object"&&typeof E.text=="string"&&E.text.trim()!=="").map((E,q)=>({...S(B,q),...tt(aa(E))}));j.length>0&&(C[B]=j)}else if(typeof M=="object"){const{text:j,subtitle:E,sizePt:q,maxWidth:ke,...$t}=M;P[B]={...S(B,0),...tt({...$t,...q!==void 0?{sizePt:Number(q)}:{},...ke!==void 0?{maxWidth:Number(ke)}:{}})},typeof j=="string"&&j.trim()&&(C[B]={headline:j,...typeof E=="string"&&E?{subtitle:E}:{}})}}});const D=re({[L]:C},At(P));p=kt(D),r=Ct(D)}return{look:{panelPresetId:e.panelPresetId??(typeof a?.panelPresetId=="string"?a.panelPresetId:void 0)??"r69",bgMode:u,gradientStops:b,gradientAngle:w,gradientFrom:m,gradientTo:g,gradientDir:y,panelColors:h,shadow:I,floorReflection:A,panelBackgrounds:f,captionStyles:p,shots:c},panelIds:n,captionText:r}}function ia(e){const t=e?.trim();return t?/\.(png|jpe?g|webp)$/i.test(t)?t:`${t}.png`:null}function sa(e,t){const n=new Map;return e.map((r,a)=>{const o=ia(t?.[a]);if(!o)return`screen-${a+1}.png`;const i=n.get(o)??0;if(n.set(o,i+1),i===0)return o;const s=o.lastIndexOf(".");return`${o.slice(0,s)}-${i+1}${o.slice(s)}`})}function la(e){const{look:t,panelIds:n,captionText:r,locale:a,shotNames:o,outputs:i}=e,s=n.map(d=>({id:d})),l=sa(t.shots,o),c=t.shots.map((d,h)=>({id:`shot-${h+1}`,panelId:d.panelId,frameNodeId:l[h],frameName:l[h],bytes:null,look:d.look,thumb:null,thumbStale:!0}));return Mt({shots:c,panels:s,selectedShotId:c[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:r&&Object.keys(r).length>0?{[a||L]:r}:{}})}function T(e){console.log("[render]",e)}const ca=1900,da=2800;let pe=null;async function Ht(){return pe||(pe=(async()=>{T("booting headless stage + loading model (once)…");const e=ot(ca,da),t=await In(e);return{stage:e,device:new st(t,e.scene,e.renderer),screen:new lt(t)}})()),pe}function Gt(e){const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r++)n[r]=t.charCodeAt(r);return n}function Ce(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function ua(e){return new Promise((t,n)=>{e.toBlob(async r=>{if(!r)return n(new Error("toBlob null"));t(Ce(new Uint8Array(await r.arrayBuffer())))},"image/png")})}function Ut(e,t){const n=zt(t),r=new Map(t.shots.map((o,i)=>[jt(i),n.indexOf(o.panelId)])),a=ta(e.layout,{frameOfPanel:o=>n.indexOf(o),frameOfShot:o=>r.get(o)??-1},t);if(a)return{layout:{findings:a.findings,...a.correctedLook?{correctedLook:a.correctedLook}:{}},layoutNote:a.note}}async function Wt(e){const t=await Promise.all(e.map(o=>createImageBitmap(new Blob([new Uint8Array(o.bytes)],{type:"image/png"})))),n=document.createElement("canvas");n.width=t.reduce((o,i)=>o+i.width,0),n.height=t[0]?.height??1;const r=n.getContext("2d");let a=0;for(const o of t)r.drawImage(o,a,0),a+=o.width,o.close();return{base64:await ua(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 Ht(),r=t.screenshots.map(I=>Array.isArray(I)?I:[I]),{look:a,panelIds:o,captionText:i}=oa({slotSizes:r.map(I=>I.length),panelPresetId:t.panelPresetId,look:t.look??void 0,style:t.style??void 0}),s=r.flat().map(Gt),l=Array.isArray(t.captionLayers)&&o[0]?{[o[0]]:t.captionLayers}:i,c=t.preview?.25:1;T(`buildStrip (${r.length} panel${r.length===1?"":"s"}${t.locale?`, ${t.locale}`:""})…`);const d=performance.now(),h=await Dt(a,{screens:s,captionText:l},t.locale,{refs:n,renderScale:c,onProgress:I=>T(" "+I),...t.clip?{clip:t.clip}:{},...t.backgroundViewport?{backgroundViewport:t.backgroundViewport}:{}});T("encoding panels + stitching the strip preview…");const p=h.panels.map(I=>Ce(I.bytes)),f=await Wt(h.panels),u=W(a.panelPresetId),m=Math.round(performance.now()-d),g=Ut(h,a),y=h.layout.captionsMeasurable,b=Array.isArray(t.screenshotNames)?t.screenshotNames.flat():void 0,w=t.buildRecord?la({look:a,panelIds:o,captionText:i,locale:t.locale,shotNames:b,outputs:t.outputs}):void 0;return T(`done: ${h.panels.length} panels, strip ${f.width}x${f.height}, ${m}ms`),{ok:!0,ms:m,count:h.panels.length,panelPresetId:u.id,panelWidth:u.width,panelHeight:u.height,stripWidth:f.width,stripHeight:f.height,panels:p,stripBase64:f.base64,preview:c<1,...w?{projectRecord:w}:{},...g??{},layoutMeasured:y}}catch(n){const r=n&&n.stack?n.stack:String(n);return T("ERROR: "+r),{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=er(JSON.stringify(t.projectFile));if(!n)throw new Error("renderProject: the saved project could not be restored (wrong schema or malformed record)");const r=await Ht(),a=t.locale||n.baseLocale||n.locales?.[0]||"en-US",o=$(n.shots,n.panels),i=me(t.variant||{},n.baseLocale),s=new Map((t.screenManifest||[]).map(v=>[`${v.shotId} ${ze(me(v.variant||{},n.baseLocale))}`,v])),l=(v,S)=>s.get(`${v} ${ze(S)}`),c=o.map(v=>qn(l,v.id,i,Wn,n.baseLocale)),d=o.map((v,S)=>({frameName:v.frameName,matched:c[S].value!==void 0,requestedVariant:i,resolvedAt:c[S].resolvedAt,inherited:c[S].inherited,...c[S].value?.source?{source:c[S].value.source}:{}})),h=c.map(({value:v})=>v?.bytes?Gt(v.bytes):null),p=Ct(n.captionText,a,n.baseLocale),f=nr({...n,selectedShotId:null,panelPresetId:t.panelPresetId??n.panelPresetId,captionText:{[a]:p}}),u=t.preview?.25:1;T(`renderProject (${o.length} shot${o.length===1?"":"s"}, ${a})…`);const m=performance.now(),g=await Dt(f,{screens:h,captionText:p},a,{refs:r,renderScale:u,onProgress:v=>T(" "+v)});T("encoding panels + stitching the strip preview…");const y=g.panels.map(v=>Ce(v.bytes)),b=await Wt(g.panels),w=W(f.panelPresetId),I=Math.round(performance.now()-m),A=Ut(g,f),k=g.layout.captionsMeasurable;return T(`done: ${g.panels.length} panels, strip ${b.width}x${b.height}, ${I}ms`),{ok:!0,ms:I,count:g.panels.length,panelPresetId:w.id,panelWidth:w.width,panelHeight:w.height,stripWidth:b.width,stripHeight:b.height,panels:y,stripBase64:b.base64,preview:u<1,shots:d,locale:a,...A??{},layoutMeasured:k}}catch(n){const r=n&&n.stack?n.stack:String(n);return T("ERROR: "+r),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.__RENDER_READY=!0;T("harness ready ✓");
@@ -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-MiEp7Ev8.js"></script>
6
+ <script type="module" crossorigin src="./assets/index--FFK3psO.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.6",
3
+ "version": "0.9.7",
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.",
@@ -37,7 +37,8 @@
37
37
  "prepack": "node ../scripts/third-party-notices.mjs --check",
38
38
  "prepublishOnly": "npm run build",
39
39
  "typecheck": "tsc --noEmit",
40
- "build:widget": "node build.widget.mjs"
40
+ "build:widget": "node build.widget.mjs",
41
+ "build:mcpb": "tsx build.mcpb.ts"
41
42
  },
42
43
  "dependencies": {
43
44
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -51,7 +52,9 @@
51
52
  "zod": "^3.25.76"
52
53
  },
53
54
  "devDependencies": {
55
+ "@anthropic-ai/mcpb": "2.1.2",
54
56
  "@types/express": "^5.0.6",
57
+ "@vercel/blob": "2.8.0",
55
58
  "ai": "^7.0.48",
56
59
  "esbuild": "^0.28.1",
57
60
  "nitro": "3.0.260610-beta",
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var z="shotops/widgetPayload";var P=`
1
+ "use strict";(()=>{var T="shotops/widgetPayload";var $=`
2
2
  :root {
3
3
  color-scheme: light dark;
4
4
  --sf-ink: #15151a;
@@ -124,8 +124,8 @@ button, a { font: inherit; }
124
124
  @media (prefers-reduced-motion: reduce) {
125
125
  .sf-download, .sf-open { transition: none; }
126
126
  }
127
- `;function r(e,n,o){let t=document.createElement(e);return n&&(t.className=n),o!==void 0&&(t.textContent=o),t}function g(e,n,o){let t=r("div","sf-stat");t.append(r("div","sf-k",n),r("div","sf-v",o)),e.append(t)}async function L(e){if(window.openai?.openExternal){await window.openai.openExternal({href:e});return}window.open(e,"_blank","noopener,noreferrer")}function R(e,n,o,t){let i=n;try{let p=new URL(n);/\/storage\/v1\/object\/sign\//.test(p.pathname)&&(p.searchParams.set("download",o),i=p.toString())}catch{}let s=r("a",t,e);return s.href=i,s.download=o,s.rel="noopener noreferrer",/^https?:/i.test(n)&&(s.target="_blank",s.addEventListener("click",p=>{window.openai?.openExternal&&(p.preventDefault(),L(i))})),s}function M(e,n,o){let t=r("button",o,e);return t.type="button",t.addEventListener("click",()=>{L(n)}),t}function d(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}function D(e){if(!Array.isArray(e.content))return null;let n=[];for(let o of e.content){let t=d(o);if(!t)continue;if(t.type==="image"&&typeof t.data=="string"&&t.mimeType==="image/png"){n.push(t.data);continue}if(t.type==="resource_link"&&typeof t.uri=="string"&&t.mimeType==="image/png"){n.push({url:t.uri});continue}let i=d(t.resource);t.type==="resource"&&i&&typeof i.uri=="string"&&i.mimeType==="image/png"&&n.push({url:i.uri})}if(n.length>0)return{ok:!0,count:n.length,output:typeof n[0]=="string"?"inline":"urls",panels:n};if(e.isError===!0){let o=e.content.map(t=>d(t)).find(t=>t?.type==="text"&&typeof t.text=="string")?.text;return{ok:!1,...typeof o=="string"?{note:o}:{}}}return null}function C(e,n=0){let o=d(e);if(!o||n>6)return null;let t=d(o[z]);if(t)return t;let i=d(o._meta),s=d(i?.[z]);if(s)return s;for(let p of["mcp_tool_result","call_tool_result","tool_result","result"]){let c=C(o[p],n+1);if(c)return c}return null}function _(e,n=!0,o=0){let t=d(e);if(!t||o>6)return n?e:null;for(let s of["structuredContent","structured_content"]){let p=d(t[s]);if(p)return p}for(let s of["mcp_tool_result","call_tool_result","tool_result","result"]){if(t[s]===void 0)continue;let p=_(t[s],!1,o+1);if(p)return p}let i=D(t);return i||(n?e:null)}function B(e,n=0){let o=d(e);if(!o||n>6)return null;let t=typeof o.status=="string"?o.status.toLowerCase():"";if(["failed","failure","error","cancelled","canceled"].includes(t))return"failure";if(["complete","completed","success","succeeded"].includes(t))return"success";for(let i of["mcp_tool_result","call_tool_result","tool_result","result"]){let s=B(o[i],n+1);if(s)return s}return null}function j(e){let n=d(e.detail);return d(n?.globals)}function F(e){return C(e)??_(e,!1)}function U(e){if(!e||typeof e!="object")return e;let n=e;return n.structuredContent&&typeof n.structuredContent=="object"?n.structuredContent:_(e)}function I(e){let n=document.createElement("style");n.textContent=e.css;let o=r("div","sf-root");o.id=e.rootId,o.append(r("div","sf-empty",e.loading)),document.body.append(n,o);let t=!1,i=l=>{let a=U(l);!a||typeof a!="object"||e.accept&&!e.accept(a)||(t=!0,e.render(o,a))},s=l=>i(F(l));window.addEventListener("message",l=>{if(l.source!==window.parent)return;let a=l.data;!a||a.jsonrpc!=="2.0"||a.method!=="ui/notifications/tool-result"||(i(a.params?.structuredContent),t||s(a.params))},{passive:!0}),window.addEventListener("openai:set_globals",l=>{let a=j(l);a&&(i(a.toolOutput),t||s(a.toolResponseMetadata))},{passive:!0});let p=100,c=500,v=2e3,k=5*6e4,x=2e3,u=0,m=null,h=!1,w=()=>{if(i(window.openai?.toolOutput),t||s(window.openai?.toolResponseMetadata),t)return;let l=B(window.openai?.toolResponseMetadata);if(l&&m===null&&(m=u),l&&!h&&(l==="failure"||u-(m??u)>=x)&&(h=!0,o.replaceChildren(r("div","sf-empty",e.terminal??e.empty))),u>=k){h||o.replaceChildren(r("div","sf-empty",e.terminal??e.empty));return}let a=u<v?p:c;u+=a,setTimeout(w,a)};w()}function W(e){return e<1024?`${e} B`:e<1024*1024?`${Math.round(e/1024)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function S(e){if(typeof e.zipBase64=="string"&&e.zipBase64.length>0){let n=e.zipBase64.endsWith("==")?2:e.zipBase64.endsWith("=")?1:0;return{href:`data:application/zip;base64,${e.zipBase64}`,size:W(Math.max(0,e.zipBase64.length*3/4-n))}}return typeof e.zipUrl=="string"&&e.zipUrl.length>0?{href:e.zipUrl,size:null}:null}function $(e){if(!e)return"Anyone with the link can download it. Links last 14 days.";let n=new Date(e);if(Number.isNaN(n.getTime()))return"Anyone with the link can download it. Links last 14 days.";let o=n.toLocaleDateString(void 0,{day:"numeric",month:"short",year:"numeric"}),t=Math.ceil((n.getTime()-Date.now())/864e5);return t<=0?`This link has expired (${o}). Emit the bundle again for a fresh one.`:`Anyone with the link can download it. Expires ${o} \u2014 ${t} day${t===1?"":"s"} from now.`}function N(e){let n=e.length>0?e:["en-US"],o=["fastlane/"],t=n.slice(0,3);return t.forEach(i=>{o.push(` screenshots/${i}/ the PNGs, named in strip order`)}),n.length>t.length&&o.push(` screenshots/\u2026 +${n.length-t.length} more locale folder(s)`),o.push(" Deliverfile pre-filled, ready to run"),o.push("README.md how to upload with your own key"),o.join(`
128
- `)}function T(e){if(!e.operationId||!e.status)return null;let n=Math.max(0,Number(e.progress?.completed??0)),o=Math.max(n,Number(e.progress?.total??0)),t={accepted:["Production accepted","ShotOps is preparing the durable render.","Accepted",!1],queued:["Production queued","The render is safely queued and can outlive this conversation.","Queued",!1],running:["Production rendering","ShotOps is rendering and checkpointing the final assets.","Running",!1],cancel_requested:["Cancellation requested","Work already in flight will stop at the next safe checkpoint.","Cancelling",!1],failed:["Production could not finish","The operation stopped without returning a partial asset set.","Failed",!0],cancelled:["Production cancelled","Unfinished work stopped and the remaining reservation was released.","Cancelled",!0],succeeded:["Production finished","The durable operation succeeded; request its result for fresh download links.","Succeeded",!0]},[i,s,p,c]=t[e.status];return{title:i,detail:s,badge:p,terminal:c,completed:n,total:o}}var H=`${P}
127
+ `;function r(n,e,o){let t=document.createElement(n);return e&&(t.className=e),o!==void 0&&(t.textContent=o),t}function g(n,e,o){let t=r("div","sf-stat");t.append(r("div","sf-k",e),r("div","sf-v",o)),n.append(t)}var V="2026-01-26",Z="shotops-widget",Q=4e3;function M(){return typeof window.openai<"u"}function z(){return window.parent!==window}var J=1,A=new Map;window.addEventListener("message",n=>{if(n.source!==window.parent)return;let e=n.data;if(!e||e.jsonrpc!=="2.0"||e.method||typeof e.id!="number")return;let o=A.get(e.id);o&&(A.delete(e.id),e.error?o.reject(new Error(e.error?.message??"MCP Apps request failed")):o.resolve(e.result))},{passive:!0});function j(n,e){let o=J++;return new Promise((t,i)=>{A.set(o,{resolve:t,reject:i}),window.parent.postMessage({jsonrpc:"2.0",id:o,method:n,params:e},"*")})}function D(n,e){window.parent.postMessage({jsonrpc:"2.0",method:n,params:e},"*")}var _=null;function E(){if(M()||!z())return Promise.resolve();if(!_){let n=j("ui/initialize",{capabilities:{},clientInfo:{name:Z,version:"1.0.0"},protocolVersion:V,appCapabilities:{availableDisplayModes:["inline"]}});_=new Promise(e=>{let o=setTimeout(e,Q);n.then(()=>{clearTimeout(o),e()},()=>{clearTimeout(o),e()})}).then(()=>{D("ui/notifications/initialized",{}),X()})}return _}var N="";function H(){let n=Math.ceil(document.documentElement.clientWidth||0),e=Math.ceil(document.body?.getBoundingClientRect().height??0),o=`${n}x${e}`;o!==N&&(N=o,D("ui/notifications/size-changed",{width:n,height:e}))}function X(){if(typeof ResizeObserver>"u"||!document.body)return;let n=0;new ResizeObserver(()=>{n||(n=requestAnimationFrame(()=>{n=0,H()}))}).observe(document.body)}function ee(){return!!window.openai?.openExternal||!M()&&z()}function P(){if(window.openai?.notifyIntrinsicHeight){window.openai.notifyIntrinsicHeight();return}M()||!z()||E().then(H)}async function F(n){if(window.openai?.openExternal){await window.openai.openExternal({href:n});return}if(!M()&&z())try{await E(),await j("ui/open-link",{url:n});return}catch{}window.open(n,"_blank","noopener,noreferrer")}function U(n,e,o,t){let i=e;try{let p=new URL(e);/\/storage\/v1\/object\/sign\//.test(p.pathname)&&(p.searchParams.set("download",o),i=p.toString())}catch{}let s=r("a",t,n);return s.href=i,s.download=o,s.rel="noopener noreferrer",/^https?:/i.test(e)?(s.target="_blank",s.addEventListener("click",p=>{ee()&&(p.preventDefault(),F(i))}),s):(!M()&&z()&&(s.style.display="none"),s)}function O(n,e,o){let t=r("button",o,n);return t.type="button",t.addEventListener("click",()=>{F(e)}),t}function d(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:null}function ne(n){if(!Array.isArray(n.content))return null;let e=[];for(let o of n.content){let t=d(o);if(!t)continue;if(t.type==="image"&&typeof t.data=="string"&&t.mimeType==="image/png"){e.push(t.data);continue}if(t.type==="resource_link"&&typeof t.uri=="string"&&t.mimeType==="image/png"){e.push({url:t.uri});continue}let i=d(t.resource);t.type==="resource"&&i&&typeof i.uri=="string"&&i.mimeType==="image/png"&&e.push({url:i.uri})}if(e.length>0)return{ok:!0,count:e.length,output:typeof e[0]=="string"?"inline":"urls",panels:e};if(n.isError===!0){let o=n.content.map(t=>d(t)).find(t=>t?.type==="text"&&typeof t.text=="string")?.text;return{ok:!1,...typeof o=="string"?{note:o}:{}}}return null}function W(n,e=0){let o=d(n);if(!o||e>6)return null;let t=d(o[T]);if(t)return t;let i=d(o._meta),s=d(i?.[T]);if(s)return s;for(let p of["mcp_tool_result","call_tool_result","tool_result","result"]){let c=W(o[p],e+1);if(c)return c}return null}function R(n,e=!0,o=0){let t=d(n);if(!t||o>6)return e?n:null;for(let s of["structuredContent","structured_content"]){let p=d(t[s]);if(p)return p}for(let s of["mcp_tool_result","call_tool_result","tool_result","result"]){if(t[s]===void 0)continue;let p=R(t[s],!1,o+1);if(p)return p}let i=ne(t);return i||(e?n:null)}function q(n,e=0){let o=d(n);if(!o||e>6)return null;let t=typeof o.status=="string"?o.status.toLowerCase():"";if(["failed","failure","error","cancelled","canceled"].includes(t))return"failure";if(["complete","completed","success","succeeded"].includes(t))return"success";for(let i of["mcp_tool_result","call_tool_result","tool_result","result"]){let s=q(o[i],e+1);if(s)return s}return null}function te(n){let e=d(n.detail);return d(e?.globals)}function oe(n){return W(n)??R(n,!1)}function re(n){if(!n||typeof n!="object")return n;let e=n;return e.structuredContent&&typeof e.structuredContent=="object"?e.structuredContent:R(n)}function Y(n){E();let e=document.createElement("style");e.textContent=n.css;let o=r("div","sf-root");o.id=n.rootId,o.append(r("div","sf-empty",n.loading)),document.body.append(e,o);let t=!1,i=l=>{let a=re(l);!a||typeof a!="object"||n.accept&&!n.accept(a)||(t=!0,n.render(o,a))},s=l=>i(oe(l));window.addEventListener("message",l=>{if(l.source!==window.parent)return;let a=l.data;!a||a.jsonrpc!=="2.0"||a.method!=="ui/notifications/tool-result"||(i(a.params?.structuredContent),t||s(a.params))},{passive:!0}),window.addEventListener("openai:set_globals",l=>{let a=te(l);a&&(i(a.toolOutput),t||s(a.toolResponseMetadata))},{passive:!0});let p=100,c=500,x=2e3,b=5*6e4,w=2e3,u=0,m=null,h=!1,v=()=>{if(i(window.openai?.toolOutput),t||s(window.openai?.toolResponseMetadata),t)return;let l=q(window.openai?.toolResponseMetadata);if(l&&m===null&&(m=u),l&&!h&&(l==="failure"||u-(m??u)>=w)&&(h=!0,o.replaceChildren(r("div","sf-empty",n.terminal??n.empty))),u>=b){h||o.replaceChildren(r("div","sf-empty",n.terminal??n.empty));return}let a=u<x?p:c;u+=a,setTimeout(v,a)};v()}function ie(n){return n<1024?`${n} B`:n<1024*1024?`${Math.round(n/1024)} KB`:`${(n/(1024*1024)).toFixed(1)} MB`}function S(n){if(typeof n.zipBase64=="string"&&n.zipBase64.length>0){let e=n.zipBase64.endsWith("==")?2:n.zipBase64.endsWith("=")?1:0;return{href:`data:application/zip;base64,${n.zipBase64}`,size:ie(Math.max(0,n.zipBase64.length*3/4-e))}}return typeof n.zipUrl=="string"&&n.zipUrl.length>0?{href:n.zipUrl,size:null}:null}function G(n){if(!n)return"Anyone with the link can download it. Links last 14 days.";let e=new Date(n);if(Number.isNaN(e.getTime()))return"Anyone with the link can download it. Links last 14 days.";let o=e.toLocaleDateString(void 0,{day:"numeric",month:"short",year:"numeric"}),t=Math.ceil((e.getTime()-Date.now())/864e5);return t<=0?`This link has expired (${o}). Emit the bundle again for a fresh one.`:`Anyone with the link can download it. Expires ${o} \u2014 ${t} day${t===1?"":"s"} from now.`}function K(n){let e=n.length>0?n:["en-US"],o=["fastlane/"],t=e.slice(0,3);return t.forEach(i=>{o.push(` screenshots/${i}/ the PNGs, named in strip order`)}),e.length>t.length&&o.push(` screenshots/\u2026 +${e.length-t.length} more locale folder(s)`),o.push(" Deliverfile pre-filled, ready to run"),o.push("README.md how to upload with your own key"),o.join(`
128
+ `)}function C(n){if(!n.operationId||!n.status)return null;let e=n.progress,o=typeof e?.panelsTotal=="number"&&e.panelsTotal>0,t=Math.max(0,Number((o?e?.panelsCompleted:e?.completed)??0)),i=Math.max(t,Number((o?e?.panelsTotal:e?.total)??0)),s={accepted:["Production accepted","ShotOps is preparing the durable render.","Accepted",!1],queued:["Production queued","The render is safely queued and can outlive this conversation.","Queued",!1],running:["Production rendering","ShotOps is rendering and checkpointing the final assets.","Running",!1],cancel_requested:["Cancellation requested","Work already in flight will stop at the next safe checkpoint.","Cancelling",!1],failed:["Production could not finish","The operation stopped without returning a partial asset set.","Failed",!0],cancelled:["Production cancelled","Unfinished work stopped and the remaining reservation was released.","Cancelled",!0],succeeded:["Production finished","The durable operation succeeded; request its result for fresh download links.","Succeeded",!0]},[p,c,x,b]=s[n.status];return{title:p,detail:c,badge:x,terminal:b,completed:t,total:i}}var se=`${$}
129
129
  .sf-body { padding: 15px 16px 4px; }
130
130
  .sf-zip {
131
131
  display: flex;
@@ -218,4 +218,4 @@ button, a { font: inherit; }
218
218
  @media (prefers-reduced-motion: reduce) {
219
219
  .sf-get { transition: none; }
220
220
  }
221
- `,Y="You upload this yourself. ShotOps never holds your Apple credentials, never creates an App Store version, and never submits anything for review.";function q(e,n){return R("Download .zip",e.href,n,"sf-get")}function G(e,n){e.replaceChildren();let o=T(n);if(o&&!S(n)){let f=r("section","sf-shell"),b=r("header","sf-head"),O=r("div");O.append(r("p","sf-eyebrow","ShotOps / durable production"),r("h2","sf-title",o.title),r("p","sf-sub",o.detail));let A=r("span","sf-badge");A.append(r("span","sf-dot"),document.createTextNode(o.badge)),b.append(O,A),f.append(b);let y=r("footer","sf-meta");g(y,"Progress",o.total>0?`${o.completed}/${o.total}`:"Waiting"),typeof n.progress?.attempt=="number"&&g(y,"Attempt",String(n.progress.attempt)),typeof n.contract?.cost?.reserved=="number"&&g(y,"Reserved",String(n.contract.cost.reserved)),typeof n.contract?.cost?.released=="number"&&g(y,"Released",String(n.contract.cost.released)),f.append(y,r("p","sf-note",`Operation ${n.operationId}. Reconnect with production_operation.`)),e.append(f),window.openai?.notifyIntrinsicHeight?.();return}let t=Array.isArray(n.locales)&&n.locales.length>0?n.locales:n.locale?[n.locale]:[],i=typeof n.panelCount=="number"?n.panelCount:0,s=typeof n.totalPanelCount=="number"?n.totalPanelCount:i*Math.max(1,t.length),p=r("section","sf-shell"),c=r("header","sf-head"),v=r("div");v.append(r("p","sf-eyebrow","ShotOps"),r("h2","sf-title","Your screenshot bundle is ready"),r("p","sf-sub",t.length>1?`${s} screenshots across ${t.length} locales, laid out the way fastlane deliver expects.`:"Laid out the way fastlane deliver expects \u2014 download it and upload with your own key."));let k=r("span","sf-badge");k.append(r("span","sf-dot"),document.createTextNode(n.output==="urls"?"Download link":"Inline zip")),c.append(v,k),p.append(c);let x=r("div","sf-body"),u=S(n),m=r("div","sf-zip");m.append(r("div","sf-zip-icon","ZIP"));let h=r("div","sf-zip-text");h.append(r("div","sf-zip-name",n.filename??"fastlane-bundle.zip"));let w=u?.size?` \xB7 ${u.size}`:"";h.append(r("div","sf-zip-sub",s>0?`${s} screenshot${s===1?"":"s"}${w}`:`Bundle${w}`)),m.append(h),u&&m.append(q(u,n.filename??"fastlane-bundle.zip")),x.append(m);let l=r("div","sf-inside");if(l.append(r("p","sf-inside-k","What's inside")),l.append(r("pre","sf-tree",N(t))),x.append(l),n.shareLink?.url){let f=r("div","sf-share"),b=r("div","sf-share-text");b.append(document.createTextNode("Share link")),b.append(r("div","sf-share-sub",$(n.shareLink.expiresAt))),f.append(b,M("Open \u2197",n.shareLink.url,"sf-open")),x.append(f)}p.append(x);let a=r("footer","sf-meta");if(n.bundleId&&g(a,"Bundle ID",n.bundleId),t.length>0&&g(a,t.length===1?"Locale":"Locales",t.join(", ")),Array.isArray(n.outputs)&&n.outputs.length>0&&g(a,"Devices",n.outputs.join(", ")),i>0&&t.length>1&&g(a,"Per locale",String(i)),n.openUrl){let f=r("div","sf-actions");f.append(M("Open in ShotOps \u2197",n.openUrl,"sf-open")),a.append(f)}a.childElementCount>0&&p.append(a);let E=[n.note,n.projectNote].filter(Boolean).join(" ");E&&p.append(r("p","sf-note",E)),p.append(r("p","sf-custody",Y)),e.append(p),window.openai?.notifyIntrinsicHeight?.()}I({css:H,rootId:"shotops-bundle",loading:"Preparing your screenshot bundle\u2026",empty:"No ShotOps bundle result is available.",terminal:"The bundle finished, but ChatGPT did not deliver its result. Refresh ShotOps and try again.",accept:e=>!!(T(e)||S(e)),render:G});})();
221
+ `,ae="You upload this yourself. ShotOps never holds your Apple credentials, never creates an App Store version, and never submits anything for review.";function pe(n,e){return U("Download .zip",n.href,e,"sf-get")}function le(n,e){n.replaceChildren();let o=C(e);if(o&&!S(e)){let f=r("section","sf-shell"),y=r("header","sf-head"),I=r("div");I.append(r("p","sf-eyebrow","ShotOps / durable production"),r("h2","sf-title",o.title),r("p","sf-sub",o.detail));let B=r("span","sf-badge");B.append(r("span","sf-dot"),document.createTextNode(o.badge)),y.append(I,B),f.append(y);let k=r("footer","sf-meta");g(k,"Progress",o.total>0?`${o.completed}/${o.total}`:"Waiting"),typeof e.progress?.attempt=="number"&&g(k,"Attempt",String(e.progress.attempt)),typeof e.contract?.cost?.reserved=="number"&&g(k,"Reserved",String(e.contract.cost.reserved)),typeof e.contract?.cost?.released=="number"&&g(k,"Released",String(e.contract.cost.released)),f.append(k,r("p","sf-note",`Operation ${e.operationId}. Reconnect with production_operation.`)),n.append(f),P();return}let t=Array.isArray(e.locales)&&e.locales.length>0?e.locales:e.locale?[e.locale]:[],i=typeof e.panelCount=="number"?e.panelCount:0,s=typeof e.totalPanelCount=="number"?e.totalPanelCount:i*Math.max(1,t.length),p=r("section","sf-shell"),c=r("header","sf-head"),x=r("div");x.append(r("p","sf-eyebrow","ShotOps"),r("h2","sf-title","Your screenshot bundle is ready"),r("p","sf-sub",t.length>1?`${s} screenshots across ${t.length} locales, laid out the way fastlane deliver expects.`:"Laid out the way fastlane deliver expects \u2014 download it and upload with your own key."));let b=r("span","sf-badge");b.append(r("span","sf-dot"),document.createTextNode(e.output==="urls"?"Download link":"Inline zip")),c.append(x,b),p.append(c);let w=r("div","sf-body"),u=S(e),m=r("div","sf-zip");m.append(r("div","sf-zip-icon","ZIP"));let h=r("div","sf-zip-text");h.append(r("div","sf-zip-name",e.filename??"fastlane-bundle.zip"));let v=u?.size?` \xB7 ${u.size}`:"";h.append(r("div","sf-zip-sub",s>0?`${s} screenshot${s===1?"":"s"}${v}`:`Bundle${v}`)),m.append(h),u&&m.append(pe(u,e.filename??"fastlane-bundle.zip")),w.append(m);let l=r("div","sf-inside");if(l.append(r("p","sf-inside-k","What's inside")),l.append(r("pre","sf-tree",K(t))),w.append(l),e.shareLink?.url){let f=r("div","sf-share"),y=r("div","sf-share-text");y.append(document.createTextNode("Share link")),y.append(r("div","sf-share-sub",G(e.shareLink.expiresAt))),f.append(y,O("Open \u2197",e.shareLink.url,"sf-open")),w.append(f)}p.append(w);let a=r("footer","sf-meta");if(e.bundleId&&g(a,"Bundle ID",e.bundleId),t.length>0&&g(a,t.length===1?"Locale":"Locales",t.join(", ")),Array.isArray(e.outputs)&&e.outputs.length>0&&g(a,"Devices",e.outputs.join(", ")),i>0&&t.length>1&&g(a,"Per locale",String(i)),e.openUrl){let f=r("div","sf-actions");f.append(O("Open in ShotOps \u2197",e.openUrl,"sf-open")),a.append(f)}a.childElementCount>0&&p.append(a);let L=[e.note,e.projectNote].filter(Boolean).join(" ");L&&p.append(r("p","sf-note",L)),p.append(r("p","sf-custody",ae)),n.append(p),P()}Y({css:se,rootId:"shotops-bundle",loading:"Preparing your screenshot bundle\u2026",empty:"No ShotOps bundle result is available.",terminal:"The bundle finished, but ChatGPT did not deliver its result. Refresh ShotOps and try again.",accept:n=>!!(C(n)||S(n)),render:le});})();
@@ -1,4 +1,4 @@
1
- "use strict";(()=>{var k="shotops/widgetPayload";var _=`
1
+ "use strict";(()=>{var M="shotops/widgetPayload";var R=`
2
2
  :root {
3
3
  color-scheme: light dark;
4
4
  --sf-ink: #15151a;
@@ -124,7 +124,7 @@ button, a { font: inherit; }
124
124
  @media (prefers-reduced-motion: reduce) {
125
125
  .sf-download, .sf-open { transition: none; }
126
126
  }
127
- `;function o(t,e,r){let n=document.createElement(t);return e&&(n.className=e),r!==void 0&&(n.textContent=r),n}function h(t,e,r){let n=o("div","sf-stat");n.append(o("div","sf-k",e),o("div","sf-v",r)),t.append(n)}async function M(t){if(window.openai?.openExternal){await window.openai.openExternal({href:t});return}window.open(t,"_blank","noopener,noreferrer")}function O(t,e,r,n){let i=e;try{let p=new URL(e);/\/storage\/v1\/object\/sign\//.test(p.pathname)&&(p.searchParams.set("download",r),i=p.toString())}catch{}let s=o("a",n,t);return s.href=i,s.download=r,s.rel="noopener noreferrer",/^https?:/i.test(e)&&(s.target="_blank",s.addEventListener("click",p=>{window.openai?.openExternal&&(p.preventDefault(),M(i))})),s}function P(t,e,r){let n=o("button",r,t);return n.type="button",n.addEventListener("click",()=>{M(e)}),n}function u(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:null}function C(t){if(!Array.isArray(t.content))return null;let e=[];for(let r of t.content){let n=u(r);if(!n)continue;if(n.type==="image"&&typeof n.data=="string"&&n.mimeType==="image/png"){e.push(n.data);continue}if(n.type==="resource_link"&&typeof n.uri=="string"&&n.mimeType==="image/png"){e.push({url:n.uri});continue}let i=u(n.resource);n.type==="resource"&&i&&typeof i.uri=="string"&&i.mimeType==="image/png"&&e.push({url:i.uri})}if(e.length>0)return{ok:!0,count:e.length,output:typeof e[0]=="string"?"inline":"urls",panels:e};if(t.isError===!0){let r=t.content.map(n=>u(n)).find(n=>n?.type==="text"&&typeof n.text=="string")?.text;return{ok:!1,...typeof r=="string"?{note:r}:{}}}return null}function E(t,e=0){let r=u(t);if(!r||e>6)return null;let n=u(r[k]);if(n)return n;let i=u(r._meta),s=u(i?.[k]);if(s)return s;for(let p of["mcp_tool_result","call_tool_result","tool_result","result"]){let g=E(r[p],e+1);if(g)return g}return null}function S(t,e=!0,r=0){let n=u(t);if(!n||r>6)return e?t:null;for(let s of["structuredContent","structured_content"]){let p=u(n[s]);if(p)return p}for(let s of["mcp_tool_result","call_tool_result","tool_result","result"]){if(n[s]===void 0)continue;let p=S(n[s],!1,r+1);if(p)return p}let i=C(n);return i||(e?t:null)}function T(t,e=0){let r=u(t);if(!r||e>6)return null;let n=typeof r.status=="string"?r.status.toLowerCase():"";if(["failed","failure","error","cancelled","canceled"].includes(n))return"failure";if(["complete","completed","success","succeeded"].includes(n))return"success";for(let i of["mcp_tool_result","call_tool_result","tool_result","result"]){let s=T(r[i],e+1);if(s)return s}return null}function I(t){let e=u(t.detail);return u(e?.globals)}function H(t){return E(t)??S(t,!1)}function W(t){if(!t||typeof t!="object")return t;let e=t;return e.structuredContent&&typeof e.structuredContent=="object"?e.structuredContent:S(t)}function A(t){let e=document.createElement("style");e.textContent=t.css;let r=o("div","sf-root");r.id=t.rootId,r.append(o("div","sf-empty",t.loading)),document.body.append(e,r);let n=!1,i=l=>{let a=W(l);!a||typeof a!="object"||t.accept&&!t.accept(a)||(n=!0,t.render(r,a))},s=l=>i(H(l));window.addEventListener("message",l=>{if(l.source!==window.parent)return;let a=l.data;!a||a.jsonrpc!=="2.0"||a.method!=="ui/notifications/tool-result"||(i(a.params?.structuredContent),n||s(a.params))},{passive:!0}),window.addEventListener("openai:set_globals",l=>{let a=I(l);a&&(i(a.toolOutput),n||s(a.toolResponseMetadata))},{passive:!0});let p=100,g=500,w=2e3,m=5*6e4,y=2e3,d=0,x=null,c=!1,f=()=>{if(i(window.openai?.toolOutput),n||s(window.openai?.toolResponseMetadata),n)return;let l=T(window.openai?.toolResponseMetadata);if(l&&x===null&&(x=d),l&&!c&&(l==="failure"||d-(x??d)>=y)&&(c=!0,r.replaceChildren(o("div","sf-empty",t.terminal??t.empty))),d>=m){c||r.replaceChildren(o("div","sf-empty",t.terminal??t.empty));return}let a=d<w?p:g;d+=a,setTimeout(f,a)};f()}function R(t){if(!t.operationId||!t.status)return null;let e=Math.max(0,Number(t.progress?.completed??0)),r=Math.max(e,Number(t.progress?.total??0)),n={accepted:["Production accepted","ShotOps is preparing the durable render.","Accepted",!1],queued:["Production queued","The render is safely queued and can outlive this conversation.","Queued",!1],running:["Production rendering","ShotOps is rendering and checkpointing the final assets.","Running",!1],cancel_requested:["Cancellation requested","Work already in flight will stop at the next safe checkpoint.","Cancelling",!1],failed:["Production could not finish","The operation stopped without returning a partial asset set.","Failed",!0],cancelled:["Production cancelled","Unfinished work stopped and the remaining reservation was released.","Cancelled",!0],succeeded:["Production finished","The durable operation succeeded; request its result for fresh download links.","Succeeded",!0]},[i,s,p,g]=n[t.status];return{title:i,detail:s,badge:p,terminal:g,completed:e,total:r}}var j=`${_}
127
+ `;function r(n,e,o){let t=document.createElement(n);return e&&(t.className=e),o!==void 0&&(t.textContent=o),t}function h(n,e,o){let t=r("div","sf-stat");t.append(r("div","sf-k",e),r("div","sf-v",o)),n.append(t)}var D="2026-01-26",U="shotops-widget",B=4e3;function y(){return typeof window.openai<"u"}function v(){return window.parent!==window}var G=1,_=new Map;window.addEventListener("message",n=>{if(n.source!==window.parent)return;let e=n.data;if(!e||e.jsonrpc!=="2.0"||e.method||typeof e.id!="number")return;let o=_.get(e.id);o&&(_.delete(e.id),e.error?o.reject(new Error(e.error?.message??"MCP Apps request failed")):o.resolve(e.result))},{passive:!0});function C(n,e){let o=G++;return new Promise((t,i)=>{_.set(o,{resolve:t,reject:i}),window.parent.postMessage({jsonrpc:"2.0",id:o,method:n,params:e},"*")})}function I(n,e){window.parent.postMessage({jsonrpc:"2.0",method:n,params:e},"*")}var P=null;function A(){if(y()||!v())return Promise.resolve();if(!P){let n=C("ui/initialize",{capabilities:{},clientInfo:{name:U,version:"1.0.0"},protocolVersion:D,appCapabilities:{availableDisplayModes:["inline"]}});P=new Promise(e=>{let o=setTimeout(e,B);n.then(()=>{clearTimeout(o),e()},()=>{clearTimeout(o),e()})}).then(()=>{I("ui/notifications/initialized",{}),Y()})}return P}var E="";function L(){let n=Math.ceil(document.documentElement.clientWidth||0),e=Math.ceil(document.body?.getBoundingClientRect().height??0),o=`${n}x${e}`;o!==E&&(E=o,I("ui/notifications/size-changed",{width:n,height:e}))}function Y(){if(typeof ResizeObserver>"u"||!document.body)return;let n=0;new ResizeObserver(()=>{n||(n=requestAnimationFrame(()=>{n=0,L()}))}).observe(document.body)}function K(){return!!window.openai?.openExternal||!y()&&v()}function O(){if(window.openai?.notifyIntrinsicHeight){window.openai.notifyIntrinsicHeight();return}y()||!v()||A().then(L)}async function H(n){if(window.openai?.openExternal){await window.openai.openExternal({href:n});return}if(!y()&&v())try{await A(),await C("ui/open-link",{url:n});return}catch{}window.open(n,"_blank","noopener,noreferrer")}function z(n,e,o,t){let i=e;try{let p=new URL(e);/\/storage\/v1\/object\/sign\//.test(p.pathname)&&(p.searchParams.set("download",o),i=p.toString())}catch{}let s=r("a",t,n);return s.href=i,s.download=o,s.rel="noopener noreferrer",/^https?:/i.test(e)?(s.target="_blank",s.addEventListener("click",p=>{K()&&(p.preventDefault(),H(i))}),s):(!y()&&v()&&(s.style.display="none"),s)}function j(n,e,o){let t=r("button",o,n);return t.type="button",t.addEventListener("click",()=>{H(e)}),t}function c(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:null}function V(n){if(!Array.isArray(n.content))return null;let e=[];for(let o of n.content){let t=c(o);if(!t)continue;if(t.type==="image"&&typeof t.data=="string"&&t.mimeType==="image/png"){e.push(t.data);continue}if(t.type==="resource_link"&&typeof t.uri=="string"&&t.mimeType==="image/png"){e.push({url:t.uri});continue}let i=c(t.resource);t.type==="resource"&&i&&typeof i.uri=="string"&&i.mimeType==="image/png"&&e.push({url:i.uri})}if(e.length>0)return{ok:!0,count:e.length,output:typeof e[0]=="string"?"inline":"urls",panels:e};if(n.isError===!0){let o=n.content.map(t=>c(t)).find(t=>t?.type==="text"&&typeof t.text=="string")?.text;return{ok:!1,...typeof o=="string"?{note:o}:{}}}return null}function N(n,e=0){let o=c(n);if(!o||e>6)return null;let t=c(o[M]);if(t)return t;let i=c(o._meta),s=c(i?.[M]);if(s)return s;for(let p of["mcp_tool_result","call_tool_result","tool_result","result"]){let m=N(o[p],e+1);if(m)return m}return null}function T(n,e=!0,o=0){let t=c(n);if(!t||o>6)return e?n:null;for(let s of["structuredContent","structured_content"]){let p=c(t[s]);if(p)return p}for(let s of["mcp_tool_result","call_tool_result","tool_result","result"]){if(t[s]===void 0)continue;let p=T(t[s],!1,o+1);if(p)return p}let i=V(t);return i||(e?n:null)}function W(n,e=0){let o=c(n);if(!o||e>6)return null;let t=typeof o.status=="string"?o.status.toLowerCase():"";if(["failed","failure","error","cancelled","canceled"].includes(t))return"failure";if(["complete","completed","success","succeeded"].includes(t))return"success";for(let i of["mcp_tool_result","call_tool_result","tool_result","result"]){let s=W(o[i],e+1);if(s)return s}return null}function Q(n){let e=c(n.detail);return c(e?.globals)}function Z(n){return N(n)??T(n,!1)}function J(n){if(!n||typeof n!="object")return n;let e=n;return e.structuredContent&&typeof e.structuredContent=="object"?e.structuredContent:T(n)}function $(n){A();let e=document.createElement("style");e.textContent=n.css;let o=r("div","sf-root");o.id=n.rootId,o.append(r("div","sf-empty",n.loading)),document.body.append(e,o);let t=!1,i=l=>{let a=J(l);!a||typeof a!="object"||n.accept&&!n.accept(a)||(t=!0,n.render(o,a))},s=l=>i(Z(l));window.addEventListener("message",l=>{if(l.source!==window.parent)return;let a=l.data;!a||a.jsonrpc!=="2.0"||a.method!=="ui/notifications/tool-result"||(i(a.params?.structuredContent),t||s(a.params))},{passive:!0}),window.addEventListener("openai:set_globals",l=>{let a=Q(l);a&&(i(a.toolOutput),t||s(a.toolResponseMetadata))},{passive:!0});let p=100,m=500,x=2e3,f=5*6e4,k=2e3,d=0,b=null,u=!1,g=()=>{if(i(window.openai?.toolOutput),t||s(window.openai?.toolResponseMetadata),t)return;let l=W(window.openai?.toolResponseMetadata);if(l&&b===null&&(b=d),l&&!u&&(l==="failure"||d-(b??d)>=k)&&(u=!0,o.replaceChildren(r("div","sf-empty",n.terminal??n.empty))),d>=f){u||o.replaceChildren(r("div","sf-empty",n.terminal??n.empty));return}let a=d<x?p:m;d+=a,setTimeout(g,a)};g()}function q(n){if(!n.operationId||!n.status)return null;let e=n.progress,o=typeof e?.panelsTotal=="number"&&e.panelsTotal>0,t=Math.max(0,Number((o?e?.panelsCompleted:e?.completed)??0)),i=Math.max(t,Number((o?e?.panelsTotal:e?.total)??0)),s={accepted:["Production accepted","ShotOps is preparing the durable render.","Accepted",!1],queued:["Production queued","The render is safely queued and can outlive this conversation.","Queued",!1],running:["Production rendering","ShotOps is rendering and checkpointing the final assets.","Running",!1],cancel_requested:["Cancellation requested","Work already in flight will stop at the next safe checkpoint.","Cancelling",!1],failed:["Production could not finish","The operation stopped without returning a partial asset set.","Failed",!0],cancelled:["Production cancelled","Unfinished work stopped and the remaining reservation was released.","Cancelled",!0],succeeded:["Production finished","The durable operation succeeded; request its result for fresh download links.","Succeeded",!0]},[p,m,x,f]=s[n.status];return{title:p,detail:m,badge:x,terminal:f,completed:t,total:i}}var X=`${R}
128
128
  .sf-rail {
129
129
  display: grid;
130
130
  grid-auto-flow: column;
@@ -142,7 +142,7 @@ button, a { font: inherit; }
142
142
  aspect-ratio: var(--sf-ratio, 1290 / 2796);
143
143
  overflow: hidden;
144
144
  border: 1px solid var(--sf-line);
145
- border-radius: 13px;
145
+ border-radius: 0; /* square, as exported: the stores round corners when they display, not us */
146
146
  background:
147
147
  linear-gradient(135deg, rgba(118,87,220,.22), transparent 42%),
148
148
  linear-gradient(180deg, #1b1930, #0d111a);
@@ -185,4 +185,4 @@ button, a { font: inherit; }
185
185
  @media (max-width: 440px) {
186
186
  .sf-rail { padding-inline: 14px; grid-auto-columns: min(64vw, 190px); }
187
187
  }
188
- `;function L(t){if(typeof t=="string"){let e=t.startsWith("data:")?t:`data:image/png;base64,${t}`;return{src:e,href:e}}return typeof t?.url=="string"&&t.url.length>0?{src:t.url,href:t.url}:null}function N(t,e){return O("Download PNG",t,`shotops-panel-${String(e+1).padStart(2,"0")}.png`,"sf-download")}function $(t,e){t.replaceChildren();let r=R(e);if(r&&(!Array.isArray(e.panels)||e.panels.length===0)){let d=o("section","sf-shell"),x=o("header","sf-head"),c=o("div");c.append(o("p","sf-eyebrow","ShotOps / durable production"),o("h2","sf-title",r.title),o("p","sf-sub",r.detail));let f=o("span","sf-badge");f.append(o("span","sf-dot"),document.createTextNode(r.badge)),x.append(c,f),d.append(x);let l=o("footer","sf-meta");h(l,"Progress",r.total>0?`${r.completed}/${r.total}`:"Waiting"),typeof e.progress?.attempt=="number"&&h(l,"Attempt",String(e.progress.attempt)),typeof e.contract?.cost?.reserved=="number"&&h(l,"Reserved",String(e.contract.cost.reserved)),typeof e.contract?.cost?.released=="number"&&h(l,"Released",String(e.contract.cost.released)),d.append(l,o("p","sf-note",`Operation ${e.operationId}. Reconnect with production_operation.`)),t.append(d),window.openai?.notifyIntrinsicHeight?.();return}let n=o("section","sf-shell"),i=o("header","sf-head"),s=o("div"),p=Array.isArray(e.panels)?e.panels:[],g=typeof e.count=="number"?e.count:p.length;s.append(o("p","sf-eyebrow","ShotOps / App Store"),o("h2","sf-title",g===1?"Your panel is ready":`Your ${g}-panel strip is ready`),o("p","sf-sub","Swipe through the final panels, then open or download each PNG."));let w=o("span","sf-badge");if(w.append(o("span","sf-dot"),document.createTextNode(e.output==="urls"?"Full-size links":"Inline result")),i.append(s,w),n.append(i),p.length>0){let d=o("div","sf-rail");e.panelWidth&&e.panelHeight&&d.style.setProperty("--sf-ratio",`${e.panelWidth} / ${e.panelHeight}`),p.forEach((x,c)=>{let f=L(x),l=o("article","sf-panel"),a=o("div","sf-frame");if(a.append(o("span","sf-index",String(c+1).padStart(2,"0"))),f){let b=o("img");b.src=f.src,b.alt=`Rendered App Store panel ${c+1} of ${p.length}`,b.loading=c===0?"eager":"lazy",b.addEventListener("error",()=>a.classList.add("is-error")),a.append(b)}else a.classList.add("is-error");a.append(o("div","sf-expired","Preview unavailable. Ask ShotOps to render this panel again."));let v=o("div","sf-panel-foot");v.append(o("span","sf-panel-name",`Panel ${String(c+1).padStart(2,"0")}`)),f&&v.append(N(f.href,c)),l.append(a,v),d.append(l)}),n.append(d)}else n.append(o("div","sf-empty","The render completed, but no panel previews were returned."));let m=o("footer","sf-meta");if(e.panelWidth&&e.panelHeight&&h(m,"Canvas",`${e.panelWidth}\xD7${e.panelHeight}`),e.panelPresetId&&h(m,"Preset",e.panelPresetId),e.locale&&h(m,"Locale",e.locale),typeof e.renderMs=="number"&&h(m,"Render",`${(e.renderMs/1e3).toFixed(1)}s`),e.openUrl){let d=o("div","sf-actions");d.append(P("Open in ShotOps \u2197",e.openUrl,"sf-open")),m.append(d)}m.childElementCount>0&&n.append(m);let y=[e.note,e.projectNote].filter(Boolean).join(" ");y&&n.append(o("p","sf-note",y)),t.append(n),window.openai?.notifyIntrinsicHeight?.()}function F(t){if(typeof t.operationId=="string"&&typeof t.status=="string"||t.ok===!1)return!0;if(t.ok!==!0)return!1;let e=Array.isArray(t.panels)?t.panels:[];return(typeof t.count=="number"?t.count:e.length)===0||e.length>0&&e.every(n=>L(n)!==null)}A({css:j,rootId:"shotops-result",loading:"Loading ShotOps panels\u2026",empty:"No ShotOps render result is available.",terminal:"The render finished, but ChatGPT did not deliver its panel data. Refresh ShotOps and try again.",accept:F,render:$});})();
188
+ `;function F(n){if(typeof n=="string"){let e=n.startsWith("data:")?n:`data:image/png;base64,${n}`;return{src:e,href:e}}return typeof n?.url=="string"&&n.url.length>0?{src:n.url,href:n.url}:null}function ee(n,e){return z("Download PNG",n,`shotops-panel-${String(e+1).padStart(2,"0")}.png`,"sf-download")}function ne(n,e){n.replaceChildren();let o=q(e);if(o&&(!Array.isArray(e.panels)||e.panels.length===0)){let d=r("section","sf-shell"),b=r("header","sf-head"),u=r("div");u.append(r("p","sf-eyebrow","ShotOps / durable production"),r("h2","sf-title",o.title),r("p","sf-sub",o.detail));let g=r("span","sf-badge");g.append(r("span","sf-dot"),document.createTextNode(o.badge)),b.append(u,g),d.append(b);let l=r("footer","sf-meta");h(l,"Progress",o.total>0?`${o.completed}/${o.total}`:"Waiting"),typeof e.progress?.attempt=="number"&&h(l,"Attempt",String(e.progress.attempt)),typeof e.contract?.cost?.reserved=="number"&&h(l,"Reserved",String(e.contract.cost.reserved)),typeof e.contract?.cost?.released=="number"&&h(l,"Released",String(e.contract.cost.released)),d.append(l,r("p","sf-note",`Operation ${e.operationId}. Reconnect with production_operation.`)),n.append(d),O();return}let t=r("section","sf-shell"),i=r("header","sf-head"),s=r("div"),p=Array.isArray(e.panels)?e.panels:[],m=typeof e.count=="number"?e.count:p.length;s.append(r("p","sf-eyebrow","ShotOps / App Store"),r("h2","sf-title",m===1?"Your panel is ready":`Your ${m}-panel strip is ready`),r("p","sf-sub","Swipe through the final panels, then open or download each PNG."));let x=r("span","sf-badge");if(x.append(r("span","sf-dot"),document.createTextNode(e.output==="urls"?"Full-size links":"Inline result")),i.append(s,x),t.append(i),p.length>0){let d=r("div","sf-rail");e.panelWidth&&e.panelHeight&&d.style.setProperty("--sf-ratio",`${e.panelWidth} / ${e.panelHeight}`),p.forEach((b,u)=>{let g=F(b),l=r("article","sf-panel"),a=r("div","sf-frame");if(a.append(r("span","sf-index",String(u+1).padStart(2,"0"))),g){let w=r("img");w.src=g.src,w.alt=`Rendered App Store panel ${u+1} of ${p.length}`,w.loading=u===0?"eager":"lazy",w.addEventListener("error",()=>a.classList.add("is-error")),a.append(w)}else a.classList.add("is-error");a.append(r("div","sf-expired","Preview unavailable. Ask ShotOps to render this panel again."));let S=r("div","sf-panel-foot");S.append(r("span","sf-panel-name",`Panel ${String(u+1).padStart(2,"0")}`)),g&&S.append(ee(g.href,u)),l.append(a,S),d.append(l)}),t.append(d)}else t.append(r("div","sf-empty","The render completed, but no panel previews were returned."));let f=r("footer","sf-meta");if(e.panelWidth&&e.panelHeight&&h(f,"Canvas",`${e.panelWidth}\xD7${e.panelHeight}`),e.panelPresetId&&h(f,"Preset",e.panelPresetId),e.locale&&h(f,"Locale",e.locale),typeof e.renderMs=="number"&&h(f,"Render",`${(e.renderMs/1e3).toFixed(1)}s`),e.openUrl){let d=r("div","sf-actions");d.append(j("Open in ShotOps \u2197",e.openUrl,"sf-open")),f.append(d)}f.childElementCount>0&&t.append(f);let k=[e.note,e.projectNote].filter(Boolean).join(" ");k&&t.append(r("p","sf-note",k)),n.append(t),O()}function te(n){if(typeof n.operationId=="string"&&typeof n.status=="string"||n.ok===!1)return!0;if(n.ok!==!0)return!1;let e=Array.isArray(n.panels)?n.panels:[];return(typeof n.count=="number"?n.count:e.length)===0||e.length>0&&e.every(t=>F(t)!==null)}$({css:X,rootId:"shotops-result",loading:"Loading ShotOps panels\u2026",empty:"No ShotOps render result is available.",terminal:"The render finished, but ChatGPT did not deliver its panel data. Refresh ShotOps and try again.",accept:te,render:ne});})();