shotops-mcp 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/local.js
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
{
|
|
3
|
+
try {
|
|
4
|
+
const sourceDirectory = new URL('../src/', import.meta.url);
|
|
5
|
+
const { existsSync } = await import('node:fs');
|
|
6
|
+
if (existsSync(sourceDirectory) && await (async function hashSourceTree(sourceDirectory) {
|
|
7
|
+
const { createHash } = await import('node:crypto')
|
|
8
|
+
const { readdir, readFile } = await import('node:fs/promises')
|
|
9
|
+
const { join } = await import('node:path')
|
|
10
|
+
const { fileURLToPath } = await import('node:url')
|
|
11
|
+
const root = fileURLToPath(sourceDirectory)
|
|
12
|
+
const hash = createHash('sha256')
|
|
13
|
+
async function visit(relativeDirectory) {
|
|
14
|
+
const entries = await readdir(join(root, relativeDirectory), { withFileTypes: true })
|
|
15
|
+
entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const relativePath = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name
|
|
18
|
+
if (entry.isDirectory()) await visit(relativePath)
|
|
19
|
+
else if (entry.isFile()) {
|
|
20
|
+
hash.update(JSON.stringify(relativePath))
|
|
21
|
+
hash.update(createHash('sha256').update(await readFile(join(root, relativePath))).digest('hex'))
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
await visit('')
|
|
26
|
+
return hash.digest('hex')
|
|
27
|
+
})(sourceDirectory) !== "52a1fd61284709b7c8443c0e991c4130a424aaa04cc3393fb351ad2b4b7db70e") {
|
|
28
|
+
console.error('shotops-mcp: dist/local.js is stale; run npm run build in shotops-mcp.');
|
|
29
|
+
}
|
|
30
|
+
} catch { /* An unreadable checkout must not break an otherwise working CLI. */ }
|
|
31
|
+
}
|
|
2
32
|
var __defProp = Object.defineProperty;
|
|
3
33
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
34
|
var __esm = (fn, res, err) => function __init() {
|
|
@@ -4124,6 +4154,13 @@ function evaluateProductionReadiness(request) {
|
|
|
4124
4154
|
}
|
|
4125
4155
|
sourceCellFindings(snapshot, surface, raw);
|
|
4126
4156
|
captionFindings(snapshot, surface, raw);
|
|
4157
|
+
if (surface === "release" && request.appleLocales !== void 0) {
|
|
4158
|
+
for (let i = 0; i < snapshot.scope.locales.length; i += 1) {
|
|
4159
|
+
const locale2 = snapshot.scope.locales[i];
|
|
4160
|
+
if (request.appleLocales.indexOf(locale2) !== -1) continue;
|
|
4161
|
+
raw.push(makeFinding(snapshot, surface, "locale_not_live_on_apple", { locale: locale2 }, { liveOnApple: false }, `${locale2}.`));
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4127
4164
|
const measured = request.measured ?? null;
|
|
4128
4165
|
if (measured) measuredFindings(snapshot, surface, measured, raw);
|
|
4129
4166
|
const findings = dedupe(raw);
|
|
@@ -4282,6 +4319,8 @@ var init_productionReadiness = __esm({
|
|
|
4282
4319
|
// ── Advisory: reported, never blocking ──
|
|
4283
4320
|
/** The caller narrowed the locale or output scope on purpose. Partial, and said so. */
|
|
4284
4321
|
"scope_filtered",
|
|
4322
|
+
/** A selected release locale does not exist on the target App Store version yet. */
|
|
4323
|
+
"locale_not_live_on_apple",
|
|
4285
4324
|
/** A preserved-but-inactive Free look hold. A retained preference, not an instruction. */
|
|
4286
4325
|
"look_hold_inactive",
|
|
4287
4326
|
/** A caption layer is explicitly empty. VALID content — a cleared layer is a decision. */
|
|
@@ -4316,6 +4355,7 @@ var init_productionReadiness = __esm({
|
|
|
4316
4355
|
device_device_collision: { severity: "block", disposition: "waivable", applies: ALL_SURFACES },
|
|
4317
4356
|
caption_legibility_unresolved: { severity: "block", disposition: "waivable", applies: ALL_SURFACES },
|
|
4318
4357
|
scope_filtered: { severity: "info", disposition: "advisory", applies: ALL_SURFACES },
|
|
4358
|
+
locale_not_live_on_apple: { severity: "info", disposition: "advisory", applies: ["release"] },
|
|
4319
4359
|
look_hold_inactive: { severity: "info", disposition: "advisory", applies: ALL_SURFACES },
|
|
4320
4360
|
caption_text_empty: { severity: "info", disposition: "advisory", applies: ALL_SURFACES }
|
|
4321
4361
|
};
|
|
@@ -4352,6 +4392,7 @@ var init_productionReadiness = __esm({
|
|
|
4352
4392
|
device_device_collision: "Two devices overlap without reading as composed.",
|
|
4353
4393
|
caption_legibility_unresolved: "A caption is below the legibility floor and no verified shrink clears it.",
|
|
4354
4394
|
scope_filtered: "This run covers part of the project on purpose.",
|
|
4395
|
+
locale_not_live_on_apple: "Locale not yet live on Apple:",
|
|
4355
4396
|
look_hold_inactive: "The held look version is preserved on the record but not honoured on this plan.",
|
|
4356
4397
|
caption_text_empty: "A caption layer is deliberately empty."
|
|
4357
4398
|
};
|
|
@@ -4376,6 +4417,7 @@ var init_productionReadiness = __esm({
|
|
|
4376
4417
|
device_device_collision: "Separate the devices, or give the pair a real difference in scale or angle.",
|
|
4377
4418
|
caption_legibility_unresolved: "Shorten the caption \u2014 no size above the legibility floor clears it.",
|
|
4378
4419
|
scope_filtered: null,
|
|
4420
|
+
locale_not_live_on_apple: "Add fastlane metadata name.txt for the new locale, or select only locales already on Apple.",
|
|
4379
4421
|
look_hold_inactive: null,
|
|
4380
4422
|
caption_text_empty: null
|
|
4381
4423
|
};
|
|
@@ -10860,7 +10902,7 @@ var init_package = __esm({
|
|
|
10860
10902
|
"package.json"() {
|
|
10861
10903
|
package_default = {
|
|
10862
10904
|
name: "shotops-mcp",
|
|
10863
|
-
version: "0.9.
|
|
10905
|
+
version: "0.9.4",
|
|
10864
10906
|
private: false,
|
|
10865
10907
|
type: "module",
|
|
10866
10908
|
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`) \u2014 same render, on your own machine, no account needed.",
|
|
@@ -18047,20 +18089,33 @@ async function readLocalization(client, localization) {
|
|
|
18047
18089
|
previews: previews.sort((left, right) => left.previewType.localeCompare(right.previewType))
|
|
18048
18090
|
};
|
|
18049
18091
|
}
|
|
18050
|
-
async function
|
|
18051
|
-
const token2 = await mintAppleJwt(setup);
|
|
18052
|
-
const client = new ReadOnlyAppleClient(token2, fetchImpl, origin);
|
|
18092
|
+
async function resolveReleaseTarget(client, desired) {
|
|
18053
18093
|
const apps = await client.list(
|
|
18054
|
-
`/v1/apps?filter[bundleId]=${encodeURIComponent(desired.
|
|
18094
|
+
`/v1/apps?filter[bundleId]=${encodeURIComponent(desired.bundleId)}&limit=2&fields[apps]=name,bundleId,sku,primaryLocale,contentRightsDeclaration`,
|
|
18055
18095
|
"apps"
|
|
18056
18096
|
);
|
|
18057
|
-
const app = oneByBundleId(apps, desired.
|
|
18097
|
+
const app = oneByBundleId(apps, desired.bundleId);
|
|
18058
18098
|
const appId = encodeURIComponent(app.id);
|
|
18059
18099
|
const versions = await client.list(
|
|
18060
18100
|
`/v1/apps/${appId}/appStoreVersions?filter[platform]=IOS&limit=200&fields[appStoreVersions]=platform,versionString,appStoreState,copyright,releaseType,earliestReleaseDate,usesIdfa,createdDate`,
|
|
18061
18101
|
"app store versions"
|
|
18062
18102
|
);
|
|
18063
|
-
|
|
18103
|
+
return { app, version: editableVersion(versions, desired.version) };
|
|
18104
|
+
}
|
|
18105
|
+
async function readReleaseVersionLocales(app, setup, fetchImpl = fetch) {
|
|
18106
|
+
const client = new ReadOnlyAppleClient(await mintAppleJwt(setup), fetchImpl);
|
|
18107
|
+
const { version: version2 } = await resolveReleaseTarget(client, { ...app, buildNumber: null });
|
|
18108
|
+
const localizations = await client.list(
|
|
18109
|
+
`/v1/appStoreVersions/${encodeURIComponent(version2.id)}/appStoreVersionLocalizations?limit=200&fields[appStoreVersionLocalizations]=locale`,
|
|
18110
|
+
"version localizations"
|
|
18111
|
+
);
|
|
18112
|
+
return Array.from(uniqueLocales(localizations, "version localization").keys()).sort();
|
|
18113
|
+
}
|
|
18114
|
+
async function readRemoteReleaseSnapshot(desired, setup, fetchImpl = fetch, origin = APP_STORE_CONNECT_ORIGIN) {
|
|
18115
|
+
const token2 = await mintAppleJwt(setup);
|
|
18116
|
+
const client = new ReadOnlyAppleClient(token2, fetchImpl, origin);
|
|
18117
|
+
const { app, version: version2 } = await resolveReleaseTarget(client, desired.app);
|
|
18118
|
+
const appId = encodeURIComponent(app.id);
|
|
18064
18119
|
const versionId = encodeURIComponent(version2.id);
|
|
18065
18120
|
const appInfos = await client.list(
|
|
18066
18121
|
`/v1/apps/${appId}/appInfos?limit=200&fields[appInfos]=appStoreState,state`,
|
|
@@ -19887,12 +19942,15 @@ async function assembleDesiredRelease(input) {
|
|
|
19887
19942
|
}))
|
|
19888
19943
|
});
|
|
19889
19944
|
if (!resolvedInput.ok) throw new Error(resolvedInput.failure.message);
|
|
19945
|
+
const appleLocales = await input.readAppleLocales?.({ bundleId, version: build?.version ?? null });
|
|
19890
19946
|
const preReadiness = evaluateProductionReadiness({
|
|
19891
19947
|
snapshot: resolvedInput.snapshot,
|
|
19892
19948
|
surface: "release",
|
|
19893
19949
|
actor: input.actor ?? null,
|
|
19950
|
+
...appleLocales ? { appleLocales } : {},
|
|
19894
19951
|
...input.waivers ? { waivers: input.waivers } : {}
|
|
19895
19952
|
});
|
|
19953
|
+
input.onReadiness?.(preReadiness);
|
|
19896
19954
|
if (!preReadiness.mayProceed) throw new Error(describeReadiness(preReadiness));
|
|
19897
19955
|
const sealed = /* @__PURE__ */ new Map();
|
|
19898
19956
|
const seal = (digest4, content) => {
|
|
@@ -20061,6 +20119,7 @@ async function assembleDesiredRelease(input) {
|
|
|
20061
20119
|
snapshot,
|
|
20062
20120
|
surface: "release",
|
|
20063
20121
|
actor: input.actor ?? null,
|
|
20122
|
+
...appleLocales ? { appleLocales } : {},
|
|
20064
20123
|
...input.waivers ? { waivers: input.waivers } : {},
|
|
20065
20124
|
measured: {
|
|
20066
20125
|
panels: measuredPanels.map((panel) => ({ validPng: true, ...panel })),
|
|
@@ -27375,6 +27434,19 @@ async function runReleasePrepareCommand(argv2, options = {}) {
|
|
|
27375
27434
|
...args.locales ? { locales: args.locales } : {},
|
|
27376
27435
|
...args.outputs ? { outputs: args.outputs } : {},
|
|
27377
27436
|
...args.destination ? { destination: args.destination } : {},
|
|
27437
|
+
readAppleLocales: async (app) => {
|
|
27438
|
+
try {
|
|
27439
|
+
return await readReleaseVersionLocales(app, await readReleaseConfig());
|
|
27440
|
+
} catch (error) {
|
|
27441
|
+
log(`Apple locale check unavailable: ${error.message}. Continuing preparation; release plan will check Apple.`);
|
|
27442
|
+
return void 0;
|
|
27443
|
+
}
|
|
27444
|
+
},
|
|
27445
|
+
onReadiness: (report) => {
|
|
27446
|
+
for (const finding of report.findings) {
|
|
27447
|
+
if (finding.code === "locale_not_live_on_apple") log(` advisory ${finding.message} ${finding.remedy ?? ""}`.trimEnd());
|
|
27448
|
+
}
|
|
27449
|
+
},
|
|
27378
27450
|
// #665 — the local door's authenticated principal, the same one every local render and
|
|
27379
27451
|
// bundle is judged against. A waiver granted by any other account is rejected.
|
|
27380
27452
|
actor: LOCAL_ACTOR,
|
|
@@ -27387,7 +27459,7 @@ async function runReleasePrepareCommand(argv2, options = {}) {
|
|
|
27387
27459
|
log(` app ${workspace.manifest.app.bundleId}${workspace.manifest.app.version ? ` ${workspace.manifest.app.version} (${workspace.manifest.app.buildNumber})` : " \xB7 no build"}`);
|
|
27388
27460
|
log(` readiness ${workspace.manifest.readiness.state} \xB7 ${workspace.manifest.readiness.findings.length} finding(s) \xB7 ${workspace.manifest.readiness.waivers.length} waived`);
|
|
27389
27461
|
log(` sealed ${workspace.manifest.artifacts.length} artifact(s) \xB7 ${workspace.manifest.artifacts.reduce((sum, artifact) => sum + artifact.bytes, 0)} bytes, verified`);
|
|
27390
|
-
log("No App Store Connect state was
|
|
27462
|
+
log("No App Store Connect state was changed.");
|
|
27391
27463
|
return 0;
|
|
27392
27464
|
} finally {
|
|
27393
27465
|
await loaded.close();
|
|
@@ -27406,6 +27478,8 @@ var init_releasePrepare = __esm({
|
|
|
27406
27478
|
init_tools();
|
|
27407
27479
|
init_toolsRender();
|
|
27408
27480
|
init_productionInput2();
|
|
27481
|
+
init_releaseApple();
|
|
27482
|
+
init_releaseSetup();
|
|
27409
27483
|
RELEASE_PREPARE_USAGE = `Assemble a deterministic desired App Store release locally.
|
|
27410
27484
|
|
|
27411
27485
|
npx shotops-mcp release prepare --project <id-or-name> [options]
|
|
@@ -27424,7 +27498,9 @@ Options:
|
|
|
27424
27498
|
finding by id and digest; prose alone waives nothing.
|
|
27425
27499
|
|
|
27426
27500
|
The command reads Fastlane metadata and app previews but never executes Fastfile or Deliverfile.
|
|
27427
|
-
|
|
27501
|
+
Before rendering, it checks the target Apple version's locales and reports missing locales as
|
|
27502
|
+
advisories. If Apple access is unavailable it reports the skipped check and continues. It uploads
|
|
27503
|
+
nothing and never changes App Store Connect state.
|
|
27428
27504
|
|
|
27429
27505
|
Every commit-required byte \u2014 the exact Project record, the rendered screenshots, the app previews,
|
|
27430
27506
|
the review attachment, compliance files and the optional signed build \u2014 is SEALED into the private
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{S as Wt,G as Vt,A as Kt,D as ae,M as qt,R as Yt,a as Xt,C as Jt,b as Zt,c as Qt}from"./three-addons-CvX38LO6.js";import{aa as en,P as Qe,b as tn,V as q,a4 as nn,C as k,ab as rn,J as Oe,$ as V,M as on,a2 as an,B as sn,l as ln}from"./three-webgl-DCw804Ic.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();const cn=""+new URL("iphone-optimized-BdcJzlHi.glb",import.meta.url).href,dn="data:model/gltf-binary;base64,Z2xURgIAAADUAAAAwAAAAEpTT057ImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJTaG90T3BzIHBsYWNlaG9sZGVyIOKAlCB0aGUgUGl4ZWwgbW9kZWwgaXMgZGVsaWJlcmF0ZWx5IG5vdCBidW5kbGVkIGluIHRoZSBucG0gcGFja2FnZSJ9LCJzY2VuZXMiOlt7Im5hbWUiOiJwaXhlbC1ub3QtYnVuZGxlZCIsIm5vZGVzIjpbXX1dLCJzY2VuZSI6MH0gICA=",un=[{id:"grey",label:"Grey",color:11579568},{id:"white",label:"White",color:15263458},{id:"charcoal",label:"Charcoal",color:3815996}],fn=[{id:"silver",label:"Silver",frame:16119285,back:16514043,accent:13816532},{id:"blue",label:"Deep Blue",frame:3290954,back:4409953,accent:2172211},{id:"orange",label:"Cosmic Orange",frame:16219693,back:16487504,accent:12738072}],hn=[{id:"silver",label:"Moonstone",frame:9345186,back:8621205,accent:7041658},{id:"blue",label:"Obsidian",frame:2763308,back:2565928,accent:2105377},{id:"orange",label:"Porcelain",frame:15920872,back:14670805,accent:11973550},{id:"green",label:"Jade",frame:9412240,back:8688005,accent:7042924}],Y={id:"iphone-17-pro",glb:cn,fitSize:3.6,baseEuler:{x:0,y:0,z:0},pruneNamePrefixes:[],screen:{material:"OLED",repeat:{x:-1,y:1},coverFill:1.001,coverCornerRatio:.18,coverRepeat:{x:-1,y:-1}},body:{frame:["Anodized aluminum"],back:["Frosted glass"],accent:["Plastic antena","PaletteMaterial001"]},colorways:fn},Te={id:"pixel-10-pro",glb:dn,fitSize:3.6,baseEuler:{x:0,y:Math.PI,z:0},pruneNamePrefixes:["Google Pixel 10 Pro Moonstone.1"],screen:{material:"GP10p_display",repeat:{x:1,y:1},coverFill:1.001,coverCornerRatio:.14,coverRepeat:{x:1,y:-1}},body:{frame:["GP10p_mid","GP10p_camera"],back:["GP10p_back","GP10p_plastic"],accent:["GP10p_logo"]},colorways:hn},pn={[Y.id]:Y,[Te.id]:Te};function ve(e){return e&&pn[e]||Y}const mn=7,ke={front:{x:0,y:0,z:-1},left:{x:-2.6,y:.6,z:-5.2},right:{x:2.6,y:.6,z:-5.2}};function et(e){const t=ke[e]??ke.front,n=Math.hypot(t.x,t.y,t.z),o=mn/n;return{x:t.x*o,y:t.y*o,z:t.z*o}}function gn(e){return Math.atan2(e.x,-e.z)*180/Math.PI}function he(e){const t=et(e);return new q(t.x,t.y,t.z)}const yn=1.4;function bn(e,t){return(t?new q(t.x,t.y,t.z):he(e)).multiplyScalar(yn)}const tt=35;function Sn(e){e.traverse(t=>{const n=t;n.geometry?.dispose();const o=n.material;Array.isArray(o)?o.forEach(Ce):o&&Ce(o)}),e.clear()}function Ce(e){for(const t of Object.values(e))t&&typeof t=="object"&&t.isTexture&&t.dispose();e.dispose()}function nt(e,t){const n=document.createElement("canvas"),o=new en({canvas:n,antialias:!0,preserveDrawingBuffer:!0,alpha:!0});o.setSize(e,t,!1),o.shadowMap.enabled=!0;const r=new Wt;r.background=null;const{ambient:a,directionals:i}=vn(r),s=new Qe(tt,e/t,.01,100);s.position.copy(he("front")),s.lookAt(0,0,0);function l(f){s.position.copy(he(f)),s.lookAt(0,0,0)}function d(f){s.position.set(f.x,f.y,f.z),s.lookAt(0,0,0)}function u(f){i.forEach(c=>c.visible=f),a.intensity=f?.75:1.6}let p=!1;function h(){p||(p=!0,r.environment?.dispose(),r.environment=null,Sn(r),o.dispose(),o.forceContextLoss())}return{scene:r,camera:s,renderer:o,dispose:h,setAngle:l,setCameraPosition:d,getCameraPosition:()=>({x:s.position.x,y:s.position.y,z:s.position.z}),onUserPose:()=>{},previewOrbit:()=>{},commitOrbit:()=>{},setOrbitEnabled:()=>{},setBackground:()=>{},setStudioLighting:u,resize:()=>{}}}function vn(e){const t=new Kt(16777215,.75);e.add(t);const n=new ae(16777215,2.2);n.position.set(3,5,5);const o=new ae(12571903,.7);o.position.set(-4,1,2);const r=new ae(16777215,1);r.position.set(0,3,-5);const a=[n,o,r];return a.forEach(i=>e.add(i)),{ambient:t,directionals:a}}function rt(e,t){const n=ve(t);return new Promise((o,r)=>{new Vt().load(n.glb,a=>{const i=a.scene;if(n.pruneNamePrefixes.length){const h=[];i.traverse(f=>{n.pruneNamePrefixes.some(c=>f.name.startsWith(c))&&h.push(f)}),h.forEach(f=>f.removeFromParent())}i.rotation.set(n.baseEuler.x,n.baseEuler.y,n.baseEuler.z),i.updateMatrixWorld(!0);const s=new tn().setFromObject(i),l=s.getSize(new q),d=s.getCenter(new q),u=n.fitSize/Math.max(l.x,l.y,l.z);i.scale.multiplyScalar(u),i.position.copy(d.multiplyScalar(-u));const p=new nn;p.add(i),e.scene.add(p),e.setAngle("front"),o(p)},void 0,r)})}function wn(e){return rt(e)}Y.colorways;class ot{frame=[];back=[];accent=[];allBody=[];model;scene;renderer;profile;envTexture=null;constructor(t,n,o,r){this.model=t,this.scene=n,this.renderer=o,this.profile=ve(r);const{frame:a,back:i,accent:s}=this.profile.body;t.traverse(l=>{const d=l;if(!d.isMesh)return;(Array.isArray(d.material)?d.material:[d.material]).forEach(p=>{const h=p;!h||!h.name||(a.includes(h.name)?this.push(this.frame,h):i.includes(h.name)?this.push(this.back,h):s.includes(h.name)&&this.push(this.accent,h),this.push(this.allBody,h))})})}push(t,n){t.includes(n)||t.push(n)}get finishMaterials(){return[...this.frame,...this.back]}paint(t,n){const o=n instanceof k?n:new k(n);t.forEach(r=>{r.color=o.clone(),r.needsUpdate=!0})}setColorway(t){if(t==="custom")return;const n=this.profile.colorways.find(o=>o.id===t)??this.profile.colorways[0];n&&(this.paint(this.frame,n.frame),this.paint(this.back,n.back),this.paint(this.accent,n.accent))}setCustomColor(t){const n=new k(t),o=n.clone().lerp(new k(16777215),.2),r=n.clone().multiplyScalar(.6);this.paint(this.frame,n),this.paint(this.back,o),this.paint(this.accent,r)}setFinish(t){const n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0;this.finishMaterials.forEach(o=>{o.userData.baseRoughness===void 0&&(o.userData.baseRoughness=o.roughness);const r=o.userData.baseRoughness;o.roughness=r*(1-n),o.needsUpdate=!0})}clearcoatUpgraded=!1;setClearcoat(t){const n=Math.max(0,Math.min(1,t));n>0&&!this.clearcoatUpgraded&&this.upgradeFinishToPhysical(),this.clearcoatUpgraded&&this.finishMaterials.forEach(o=>{const r=o;r.clearcoat=n,r.clearcoatRoughness=.1,r.needsUpdate=!0})}upgradeFinishToPhysical(){this.clearcoatUpgraded=!0;const t=new Map;for(const o of Array.from(new Set(this.finishMaterials))){const r=new qt;r.color.copy(o.color),r.metalness=o.metalness,r.roughness=o.roughness,r.map=o.map,r.normalMap=o.normalMap,r.normalScale.copy(o.normalScale),r.roughnessMap=o.roughnessMap,r.metalnessMap=o.metalnessMap,r.aoMap=o.aoMap,r.aoMapIntensity=o.aoMapIntensity,r.emissive.copy(o.emissive),r.emissiveMap=o.emissiveMap,r.emissiveIntensity=o.emissiveIntensity,r.envMapIntensity=o.envMapIntensity,r.transparent=o.transparent,r.opacity=o.opacity,r.alphaTest=o.alphaTest,r.side=o.side,r.flatShading=o.flatShading,r.vertexColors=o.vertexColors,r.name=o.name,r.userData={...o.userData},r.clearcoat=0,t.set(o,r)}this.model.traverse(o=>{const r=o;if(r.isMesh)if(Array.isArray(r.material))r.material=r.material.map(a=>t.get(a)??a);else{const a=t.get(r.material);a&&(r.material=a)}});const n=o=>o.map(r=>t.get(r)??r);this.frame=n(this.frame),this.back=n(this.back),this.allBody=n(this.allBody)}environmentMap(){if(!this.envTexture){const t=new rn(this.renderer);this.envTexture=t.fromScene(new Yt,.04).texture,t.dispose()}return this.envTexture}setClay(t){const n=un.find(r=>r.id===t),o=n?new k(n.color):new k(t);this.allBody.forEach(r=>{r.userData.clayStashedMap===void 0&&(r.userData.clayStashedMap=r.map),r.color=o.clone(),r.metalness=0,r.roughness=.85,r.map=null,r.needsUpdate=!0})}clayOff(){this.allBody.forEach(t=>{t.userData.clayStashedMap!==void 0&&(t.map=t.userData.clayStashedMap,t.userData.clayStashedMap=void 0,t.needsUpdate=!0)})}setReflections(t){this.scene.environment=t?this.environmentMap():null}setRoll(t){this.model.rotation.z=t*Math.PI/180}}const Ee=1.35;class at{materials=[];glassOverlays=[];screenMesh=null;flatPlane=null;texture=null;appliedBytes=null;flatScreenOn=!1;profile;constructor(t,n){this.profile=ve(n);const o=this.profile.screen.material;t.traverse(r=>{const a=r;if(!a.isMesh)return;(Array.isArray(a.material)?a.material:[a.material]).forEach(s=>{const l=s;l&&l.name===o&&!this.materials.includes(l)&&(this.materials.push(l),this.screenMesh=a)})}),this.dimGlassOverlay(t),this.prepareScreen(),this.setGlare(null)}get found(){return this.materials.length>0}setGlare(t){this.materials.forEach(n=>{n.envMap=t,n.envMapIntensity=t?.6:0,n.metalness=t?.5:0,n.roughness=t?.15:1,n.needsUpdate=!0}),this.glassOverlays.forEach(n=>{n.opacity=t?.08:0,n.needsUpdate=!0})}setFlatScreen(t){if(this.flatScreenOn=t,!this.screenMesh)return;if(this.flatPlane){this.screenMesh.remove(this.flatPlane);const S=this.flatPlane.material;S.map?.dispose(),S.dispose(),this.flatPlane.geometry.dispose(),this.flatPlane=null}if(!t||!this.texture)return;this.screenMesh.geometry.computeBoundingBox();const n=this.screenMesh.geometry.boundingBox;if(!n)return;const o=n.max.x-n.min.x,r=n.max.y-n.min.y,a=n.max.z-n.min.z,i=(n.max.x+n.min.x)/2,s=(n.max.y+n.min.y)/2,l=(n.max.z+n.min.z)/2,d=r<=o&&r<=a?"y":a<=o&&a<=r?"z":"x",u=this.texture.clone();u.needsUpdate=!0,u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V;const{coverFill:p,coverCornerRatio:h,coverRepeat:f}=this.profile.screen;u.repeat.set(f.x,f.y);const c=d==="x"?a:o,g=d==="z"||d==="x"?r:a,m=Math.min(c,g)*p*h,b=In(c*p,g*p,m),y=new on(b,new Xt({map:u,emissive:new k(16777215),emissiveMap:u,emissiveIntensity:Ee,color:new k(0),toneMapped:!1,side:an,depthTest:!0,depthWrite:!0,polygonOffset:!0,polygonOffsetFactor:-4,polygonOffsetUnits:-4}));d==="y"?(y.rotation.x=-Math.PI/2,y.position.set(i,n.max.y,l)):d==="x"?(y.rotation.y=Math.PI/2,y.position.set(n.max.x,s,l)):y.position.set(i,s,n.max.z),y.renderOrder=999,this.screenMesh.add(y),this.flatPlane=y}restoreScreen(){this.prepareScreen()}prepareScreen(){this.materials.forEach(t=>{t.color=new k(0),t.emissive=new k(16777215),t.emissiveIntensity=1,t.roughness=1,t.metalness=0,t.needsUpdate=!0})}dimGlassOverlay(t){t.traverse(n=>{const o=n;if(!o.isMesh)return;(Array.isArray(o.material)?o.material:[o.material]).forEach(a=>{const i=a;i&&i.name!==this.profile.screen.material&&i.transparent&&i.opacity>.2&&this.glassOverlays.push(i)})})}applyWallpaper(t){if(this.appliedBytes===t&&this.texture)return this.lightScreen(this.texture),Promise.resolve();const n=new Blob([t],{type:"image/png"}),o=URL.createObjectURL(n),r=this.texture;return new Promise((a,i)=>{const s=new Image;s.onload=()=>{try{const l=document.createElement("canvas");l.width=s.naturalWidth,l.height=s.naturalHeight;const d=l.getContext("2d");if(!d)throw new Error("2D context unavailable for wallpaper blit");d.drawImage(s,0,0);const u=new Jt(l);u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V,u.repeat.set(this.profile.screen.repeat.x,this.profile.screen.repeat.y),u.needsUpdate=!0,this.texture=u,this.appliedBytes=t,this.lightScreen(u),this.flatScreenOn&&this.setFlatScreen(!0),r?.dispose(),URL.revokeObjectURL(o),a()}catch(l){URL.revokeObjectURL(o),i(l)}},s.onerror=()=>{URL.revokeObjectURL(o),i(new Error("wallpaper image failed to decode"))},s.src=o})}lightScreen(t){this.materials.forEach(n=>{n.map=t,n.emissiveMap=t,n.emissive=new k(16777215),n.emissiveIntensity=Ee,n.color=new k(0),n.needsUpdate=!0})}}function In(e,t,n){const o=e/2,r=t/2,a=Math.min(n,o,r),i=new Zt;i.moveTo(-o+a,-r),i.lineTo(o-a,-r),i.quadraticCurveTo(o,-r,o,-r+a),i.lineTo(o,r-a),i.quadraticCurveTo(o,r,o-a,r),i.lineTo(-o+a,r),i.quadraticCurveTo(-o,r,-o,r-a),i.lineTo(-o,-r+a),i.quadraticCurveTo(-o,-r,-o+a,-r);const s=new Qt(i,16),l=s.attributes.position,d=new Float32Array(l.count*2);for(let u=0;u<l.count;u++)d[u*2]=(l.getX(u)+o)/e,d[u*2+1]=(l.getY(u)+r)/t;return s.setAttribute("uv",new sn(d,2)),s}const Me={sizePt:{min:20,max:120},maxWidth:{min:.1,max:1},lineHeight:{min:.5,max:3},bandInset:{min:0,max:.5},bandCenter:{min:0,max:1},reserveLines:{min:1,max:8}},X=[{id:"inter",family:"Inter",label:"Inter"},{id:"manrope",family:"Manrope",label:"Manrope"},{id:"poppins",family:"Poppins",label:"Poppins"},{id:"fraunces",family:"Fraunces",label:"Fraunces"},{id:"space-grotesk",family:"Space Grotesk",label:"Space Grotesk"}],Le=X.map(e=>e.id);[Le[0],...Le.slice(1)];const J=600;function it(e){return X.find(t=>t.id===e)??X[0]}const st=[{id:"standard",label:"Standard",blurb:"Headline on top, whole device below it, nothing cropped. The safe default.",character:"safe",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2}},{id:"bleed",label:"Bleed",blurb:"Room for a longer headline, device running off the bottom edge. What the market looks like.",character:"conventional",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4}},{id:"top-bleed",label:"Top bleed",blurb:"Device running off the TOP edge, headline underneath it. Inverts the usual reading order.",character:"expressive",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"-38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,band:"bottom"}}],lt=st,Fe=lt.map(e=>e.id);[Fe[0],...Fe.slice(1)];Array.from(new Set(st.flatMap(e=>Object.keys(e.caption))));const ct=[{id:"noir",label:"Noir",blurb:"Near-black, high contrast, glossy silver device. The default premium look.",background:{gradientFrom:"#1C1C22",gradientTo:"#08080B",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"silver",finish:"0",clearcoat:"1",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"inter",color:"FFFFFF"}},{id:"daylight",label:"Daylight",blurb:"Warm off-white, soft matte white device, dark text. Calm and editorial.",background:{gradientFrom:"#F4F1EA",gradientTo:"#DFD8CA",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"white",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"front"},caption:{fontId:"manrope",color:"1A1A1F"}},{id:"pop",label:"Pop",blurb:"Saturated indigo-to-violet, orange device. Loud, consumer, scroll-stopping.",background:{gradientFrom:"#5B6CF0",gradientTo:"#9B4DF0",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"orange",finish:"0",clearcoat:"0.4",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"poppins",color:"FFFFFF"}},{id:"clay",label:"Clay",blurb:"Warm terracotta, matte charcoal device, tilted. Reads like a product photo.",background:{gradientFrom:"#E9BBAC",gradientTo:"#C88C7C",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"charcoal",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"left",roll:"-6"},caption:{fontId:"fraunces",color:"2A2320"}},{id:"studio",label:"Studio",blurb:"Mid slate with a floor reflection, blue device. Technical and precise.",background:{gradientFrom:"#5A6678",gradientTo:"#2B3340",gradientDir:"vertical",shadow:!0,floorReflection:!0},shotLook:{material:"real",colorway:"blue",finish:"0",clearcoat:"0",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"space-grotesk",color:"FFFFFF"}}],Re=ct.map(e=>e.id);[Re[0],...Re.slice(1)];const x={angle:"front",cameraPos:null,roll:"0",phoneHeight:"82",hOffset:"0",vOffset:"18",material:"real",colorway:"silver",customColor:"F5F5F5",finish:"0",clearcoat:"0",clayTone:"grey",clayCustom:"B0B0B0",flatScreen:!1,glare:!1,lighting:!0,reflections:!1},_e={angle:{valueKind:"angle",required:!0,defaultSource:"default-look",defaultValue:x.angle,persistence:"always"},cameraPos:{valueKind:"camera-position",required:!0,defaultSource:"default-look",defaultValue:x.cameraPos,persistence:"always"},roll:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.roll,persistence:"always"},phoneHeight:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.phoneHeight,persistence:"always"},hOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.hOffset,persistence:"always"},vOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.vOffset,persistence:"always"},material:{valueKind:"material",required:!0,defaultSource:"default-look",defaultValue:x.material,persistence:"always"},colorway:{valueKind:"colorway",required:!0,defaultSource:"default-look",defaultValue:x.colorway,persistence:"always"},customColor:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.customColor,persistence:"always"},finish:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.finish,persistence:"always"},clearcoat:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.clearcoat,persistence:"always"},clayTone:{valueKind:"clay-tone",required:!0,defaultSource:"default-look",defaultValue:x.clayTone,persistence:"always"},clayCustom:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.clayCustom,persistence:"always"},flatScreen:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.flatScreen,persistence:"always"},clipToFrame:{valueKind:"boolean",required:!1,defaultSource:"explicit",defaultValue:!1,persistence:"omit-default"},glare:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.glare,persistence:"always"},lighting:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.lighting,persistence:"always"},reflections:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.reflections,persistence:"always"}},xn={roll:{min:-45,max:45},phoneHeight:{min:20,max:200},hOffset:{min:-100,max:100},vOffset:{min:-200,max:200},finish:{min:0,max:1},clearcoat:{min:0,max:1}},H={x:.5,y:.06},we={fontId:"inter",sizePt:32,color:"FFFFFF",align:"center",anchor:H,maxWidth:.86},Ne=[{id:"r69",label:"6.9″ iPhone · 1290×2796",width:1290,height:2796},{id:"r65",label:"6.5″ iPhone · 1242×2688",width:1242,height:2688},{id:"r55",label:"5.5″ iPhone · 1242×2208",width:1242,height:2208},{id:"pixel69",label:"Pixel 10 Pro · 1290×2796",width:1290,height:2796},{id:"ipad13",label:"13″ iPad · 2064×2752",width:2064,height:2752},{id:"ipad129",label:"12.9″ iPad · 2048×2732",width:2048,height:2732}];function G(e){return Ne.find(t=>t.id===e)??Ne[0]}const te=932,pe="iphone-17-pro",Pn="pixel-10-pro",Ie=[{id:"iphone-6-9",label:"iPhone 17 Pro",panelPresetId:"r69",ascDeviceSet:"APP_IPHONE_67",modelId:pe,screenFamily:"iphone"},{id:"iphone-6-5",label:"iPhone 17",panelPresetId:"r65",ascDeviceSet:"APP_IPHONE_65",modelId:pe,screenFamily:"iphone"},{id:"pixel-10-pro",label:"Pixel 10 Pro",panelPresetId:"pixel69",ascDeviceSet:"ANDROID_PHONE",modelId:Pn,screenFamily:"android"},{id:"ipad-13",label:"iPad 13″",panelPresetId:"ipad13",ascDeviceSet:"APP_IPAD_PRO_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0},{id:"ipad-12-9",label:"iPad 12.9″",panelPresetId:"ipad129",ascDeviceSet:"APP_IPAD_PRO_3GEN_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0}];function An(e){return Ie.find(t=>t.panelPresetId===e)?.modelId??pe}const dt="iphone-6-9";function ut(e){return Ie.find(t=>t.id===e)}function On(){return Ie.filter(e=>!e.comingSoon)}function ft(e,t){if(Array.isArray(e)){const n=new Set,o=[];for(const r of e){const a=ut(r);a&&!a.comingSoon&&!n.has(r)&&(n.add(r),o.push(r))}if(o.length>0)return o}if(t){const n=On().find(o=>o.panelPresetId===t);if(n)return[n.id]}return[dt]}const ht=ut(dt)?.screenFamily??"iphone";function pt(e){const t={...e},n=t,o=Object.keys(_e);for(const r of o){const a=_e[r];a.persistence==="omit-default"&&(n[r]===void 0||n[r]===a.defaultValue)&&delete n[r]}return t}function mt(e){return pt({...x,...e})}function gt(e){return pt(mt(e))}function R(e,t){const{min:n,max:o}=xn[e],r=typeof t=="number"?t:Number.parseFloat(t??""),a=Number.isFinite(r)?r:Number.parseFloat(x[e]);return Math.max(n,Math.min(o,a))}const Tn=90,yt=180,kn="#1b1b2e",Cn="#0a0a14";function En(e){return e==="horizontal"?Tn:yt}function Mn(e){const t=(e%180+180)%180;return t>=45&&t<135?"horizontal":"vertical"}function bt(e,t,n){return{stops:[{color:e,at:0},{color:t,at:1}],angle:En(n)}}const St=e=>e*Math.PI/180;function Ln(e){const t=St(e);return{dx:Math.sin(t),dy:-Math.cos(t)}}function Fn(e,t,n){const o=St(n);return Math.abs(e*Math.sin(o))+Math.abs(t*Math.cos(o))}function Rn(e,t,n){const{dx:o,dy:r}=Ln(n),a=Fn(e,t,n)/2,i=e/2,s=t/2;return{x0:i-a*o,y0:s-a*r,x1:i+a*o,y1:s+a*r}}function Z(e){const t=e.stops[0],n=e.stops[e.stops.length-1];return{gradientFrom:t?.color??"#000000",gradientTo:n?.color??t?.color??"#000000",gradientDir:Mn(e.angle)}}function ne(e,t){if(!Array.isArray(e)||e.length===0)return null;const n=[];for(const r of e){if(!r||typeof r!="object")return null;const a=r;if(typeof a.color!="string"||typeof a.at!="number"||!Number.isFinite(a.at))return null;n.push({color:a.color,at:Math.min(1,Math.max(0,a.at))})}const o=typeof t=="number"&&Number.isFinite(t)?_n(t):yt;return{stops:n,angle:o}}function Q(e){return ne(e.gradientStops,e.gradientAngle)??bt(e.gradientFrom,e.gradientTo,e.gradientDir)}function _n(e){return(e%360+360)%360}function Nn(e){return ne(e.stops,e.angle)??bt(e.from,e.to,e.dir)}function vt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(!o||typeof o!="object")continue;const r=o;if(r.kind==="solid"&&typeof r.color=="string")t[n]={kind:"solid",color:r.color};else if(r.kind==="gradient"&&typeof r.from=="string"&&typeof r.to=="string"&&(r.dir==="horizontal"||r.dir==="vertical")){const a=ne(r.stops,r.angle);t[n]=a?{kind:"gradient",stops:a.stops,angle:a.angle,from:r.from,to:r.to,dir:r.dir}:{kind:"gradient",from:r.from,to:r.to,dir:r.dir}}}return t}function B(e){return!e||e.x===H.x&&e.y===H.y}function Bn(e){const{text:t,anchor:n,...o}=e;return B(n)?{...o}:{...o,anchor:n}}const zn=.55,M="en-US";function Dn(e){const t=e.lastIndexOf(":");if(t<=0)return null;const n=Number(e.slice(t+1));return!Number.isSafeInteger(n)||n<0?null:{panelId:e.slice(0,t),index:n}}function wt(e,t,n,o){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[a,i]of Object.entries(e)){if(a===n||!t.includes(a)||!Array.isArray(i))continue;const s=Array.from(new Set(i.filter(l=>typeof l=="string"))).filter(l=>{const d=Dn(l);return!!d&&!!o[n]?.[d.panelId]?.[d.index]&&!!o[a]?.[d.panelId]?.[d.index]});s.length>0&&(r[a]=s)}return r}function It(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))!o||typeof o!="object"||Array.isArray(o)||(t[n]=z(o));return t}function jn(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.x=="number"?t.x:H.x,o=typeof t.y=="number"?t.y:H.y;return B({x:n,y:o})?void 0:{x:n,y:o}}function z(e){const t=e&&typeof e=="object"?e:{},n=jn(t.anchor),o={...we,...t};n?o.anchor=n:delete o.anchor,typeof o.reserveLines!="number"||!Number.isFinite(o.reserveLines)||o.reserveLines<Me.reserveLines.min?delete o.reserveLines:o.reserveLines=Math.floor(o.reserveLines),o.band!=="bottom"&&delete o.band;for(const r of["bandInset","lineHeight","bandCenter"]){const{min:a,max:i}=Me[r],s=o[r];(typeof s!="number"||!Number.isFinite(s)||s<a||s>i)&&delete o[r]}return o}function Hn(e){const t=e&&typeof e=="object"?e:{};return{...z(t),text:typeof t.text=="string"?t.text:""}}function xt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))Array.isArray(o)?o.length>0&&(t[n]=o.map(z)):o&&typeof o=="object"&&(t[n]=[z(o)]);return t}function Gn(e,t){const n=[{...z(t),text:e.headline}];return typeof e.subtitle=="string"&&e.subtitle.trim()&&n.push({...z({...t,sizePt:t.sizePt*zn}),text:e.subtitle}),n}function re(e,t,n=M){if(!e||typeof e!="object"||Array.isArray(e))return{};const o=It(t),r=d=>o[d]??we,a=d=>{if(!d||typeof d!="object"||Array.isArray(d))return{};const u={};for(const[p,h]of Object.entries(d))Array.isArray(h)?h.length>0&&(u[p]=h.map(Hn)):h&&typeof h=="object"&&typeof h.headline=="string"&&(u[p]=Gn(h,r(p)));return u},i=Object.entries(e);if(i.some(([,d])=>!!d&&typeof d=="object"&&typeof d.headline=="string"))return{[n]:a(e)};const l={};for(const[d,u]of i)l[d]=a(u);return l}function Pt(e,t,n=M){return e[t??n]??e[n]??{}}function At(e,t=M){const n={},o=Object.keys(e),r=o.includes(t)?[t,...o.filter(a=>a!==t)]:o;for(const a of r){const i=e[a];if(i)for(const[s,l]of Object.entries(i))n[s]===void 0&&Array.isArray(l)&&l.length>0&&(n[s]=l.map(Bn))}return n}function me(e,t=M,n=ht){const o={};return e.family!==void 0&&e.family!==n&&(o.family=e.family),e.locale!==void 0&&e.locale!==t&&(o.locale=e.locale),o}function Be(e){const t=[];return e.family!==void 0&&t.push(`family=${e.family}`),e.locale!==void 0&&t.push(`locale=${e.locale}`),t.join("&")}function ge(e){const t=[];return e.family!==void 0&&t.push("family"),e.locale!==void 0&&t.push("locale"),t}const Un={order:["family","locale"],reach:"strict"};function $n(e,t){const n=ge(e),o=t.order.filter(a=>n.includes(a));return Array.from({length:o.length+1},(a,i)=>o.slice(0,o.length-i)).map(a=>{const i={};return a.includes("family")&&(i.family=e.family),a.includes("locale")&&(i.locale=e.locale),i})}function Wn(e,t,n,o,r=M){const a=me(n,r);for(const i of $n(a,o)){const s=e(t,i);if(s!==void 0){const l=ge(i);return{value:s,resolvedAt:i,inherited:ge(a).filter(d=>!l.includes(d))}}}return{value:void 0,resolvedAt:null,inherited:[]}}function ye(e,t,n=M){const o=r=>{const a=new Set,i=[];for(const s of r)typeof s=="string"&&s&&!a.has(s)&&(a.add(s),i.push(s));return i};if(Array.isArray(t)){const r=o(t);if(r.length>0)return r.includes(n)?r:[...r,n]}return o([n,...Object.keys(e)])}function xe(e,t=M){return typeof e=="string"&&e.length>0?e:t}const D={"it-IT":"it"};function ze(e,t){if(!(Object.keys(e).some(r=>r in D)||(t?.some(r=>r in D)??!1)))return{captionText:e,locales:t};const o={};for(const[r,a]of Object.entries(e))r in D||(o[r]=a);for(const[r,a]of Object.entries(e)){const i=D[r];i&&(o[i]={...a,...o[i]??{}})}return{captionText:o,locales:t?.map(r=>D[r]??r)}}function Ot(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(n===ht||!o||typeof o!="object"||Array.isArray(o))continue;const r={};for(const[a,i]of Object.entries(o)){if(!i||typeof i!="object"||Array.isArray(i))continue;const s={};for(const[l,d]of Object.entries(i))Array.isArray(d)&&d.every(u=>typeof u=="string")&&(s[l]=d);Object.keys(s).length>0&&(r[a]=s)}Object.keys(r).length>0&&(t[n]=r)}return t}function U(e,t){return t.flatMap(n=>e.filter(o=>o.panelId===n.id))}let De=0,je=0;function Vn(e,t){const n=(o,r)=>o.reduce((a,i)=>{const s=i.startsWith(r)?Number.parseInt(i.slice(r.length),10):NaN;return Number.isFinite(s)&&s>a?s:a},0);De=Math.max(De,n(e,"shot-")),je=Math.max(je,n(t,"panel-"))}const Kn=.49;function qn(e,t,n){return e.map(o=>{const r=n*o.heightFrac,a=o.height>0?o.width*(r/o.height):r*Kn,s=o.panelIndex*t+t/2-a/2+o.hOffset*(t/2),d=(n-r)/2+o.vOffset*(n/2);return{shotId:o.shotId,bytes:o.bytes,x:Math.round(s),y:Math.round(d),width:Math.round(a),height:Math.round(r),panelIndex:o.panelIndex,...o.clipToFrame===!0?{clipToFrame:!0}:{}}})}function Yn(e,t){const n=t.panelBackgrounds??{},o=Q(t),r=o.stops[0]?.color??t.gradientFrom;if(!e.some(l=>n[l.id]))return t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"perPanel",colors:e.map(l=>t.panelColors[l.id]??r)};const i=l=>t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"solid",color:t.panelColors[l]??r};return{kind:"perPanelFills",fills:e.map(l=>{const d=n[l.id];return d?d.kind==="solid"?{kind:"solid",color:d.color}:{kind:"gradient",gradient:Nn(d)}:i(l.id)})}}function Tt(e,t,n=M){const o=Q(e);return{panelPresetId:e.panelPresetId,bgMode:e.bgMode,gradientStops:o.stops,gradientAngle:o.angle,...Z(o),panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection??!1,panelBackgrounds:e.panelBackgrounds??{},captionStyles:{...xt(e.captionStyles),...At(t,n)}}}function Xn(e){const t=e.caption.reserveLines,n=typeof t=="number"?` Reserves ${t} headline lines (~${t*18} characters).`:"";return`${e.blurb}${n} Character: [${e.character}].`}function Jn(e,t){return Math.max(0,...Object.values(e).map(n=>n[t]?.length??0))}function kt(e,t,n){const o=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=new Set,a={};let i=1;const s=()=>{for(;r.has(`caption-${i}`);)i+=1;const l=`caption-${i}`;return r.add(l),i+=1,l};for(const l of n){const d=Jn(t,l);if(d===0)continue;const u=Array.isArray(o[l])?o[l]:[];a[l]=Array.from({length:d},(p,h)=>{const f=u[h];return typeof f=="string"&&f.length>0&&!r.has(f)?(r.add(f),f):s()})}return a}const Ct=2;function Zn(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return t.schema===Ct&&Array.isArray(t.shots)&&Array.isArray(t.panels)&&t.panels.length>0}function Qn(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Zn(t))return null;const n=t,o=new Set(n.panels.map(c=>c.id)),r=n.shots.filter(c=>o.has(c.panelId)).map(c=>({id:c.id,panelId:c.panelId,frameNodeId:c.frameNodeId,frameName:c.frameName,bytes:null,look:mt(c.look),thumb:null,thumbStale:!0,inputAnchor:c.inputAnchor,outputAnchor:c.outputAnchor})),a=n.panels.map(c=>({id:c.id}));Vn(r.map(c=>c.id),a.map(c=>c.id));const i=n.selectedShotId&&r.some(c=>c.id===n.selectedShotId)?n.selectedShotId:r[0]?.id??null,s=xe(n.baseLocale),{captionText:l,locales:d}=ze(re(n.captionText,n.captionStyles,s),n.locales),u=ze({[s]:{}},[s]).locales?.[0]??s,p=ye(l,d,u),h=kt(n.captionSlotIds,l,a.map(c=>c.id)),f=ne(n.gradientStops,n.gradientAngle);return{shots:U(r,a),panels:a,selectedShotId:i,panelPresetId:G(n.panelPresetId).id,bgMode:n.bgMode==="perPanel"?"perPanel":"gradient",...f?{gradientStops:f.stops,gradientAngle:f.angle,...Z(f)}:{gradientFrom:n.gradientFrom??"5B6CF0",gradientTo:n.gradientTo??"9B4DF0",gradientDir:n.gradientDir==="vertical"?"vertical":"horizontal"},panelColors:n.panelColors??{},shadow:n.shadow!==!1,floorReflection:n.floorReflection===!0,panelBackgrounds:vt(n.panelBackgrounds),captionText:l,locales:p,baseLocale:u,staleCaptionTranslations:wt(n.staleCaptionTranslations,p,u,l),outputs:ft(n.outputs,n.panelPresetId),localeScreens:{},outputCaptions:Ot(n.outputCaptions),captionSlotIds:h,frameTemplates:n.frameTemplates&&typeof n.frameTemplates=="object"&&!Array.isArray(n.frameTemplates)?n.frameTemplates:{}}}function Et(e){const n=U(e.shots,e.panels).map(a=>({id:a.id,panelId:a.panelId,frameNodeId:a.frameNodeId,frameName:a.frameName,look:gt(a.look),inputAnchor:a.inputAnchor,outputAnchor:a.outputAnchor})),o=xe(e.baseLocale),r=re(e.captionText,e.captionStyles,o);return{schema:Ct,shots:n,panels:e.panels,selectedShotId:e.selectedShotId,captionText:r,locales:ye(r,e.locales,o),baseLocale:o,staleCaptionTranslations:wt(e.staleCaptionTranslations,ye(r,e.locales,o),o,r),outputs:ft(e.outputs,e.panelPresetId),outputCaptions:Ot(e.outputCaptions),...(()=>{const a=kt(e.captionSlotIds,r,e.panels.map(i=>i.id));return Object.keys(a).length>0?{captionSlotIds:a}:{}})(),...e.frameTemplates&&Object.keys(e.frameTemplates).length>0?{frameTemplates:e.frameTemplates}:{},...Tt(e,r,o)}}const er=1;function tr(e){const t=U(e.shots,e.panels),n=re(e.captionText,e.captionStyles),o=xe(e.baseLocale);return{schema:er,shots:t.map(r=>({panelId:r.panelId,look:gt(r.look)})),...Tt(e,n,o)}}ct.map(e=>({id:e.id,label:e.label,description:e.blurb}));lt.map(e=>({id:e.id,label:e.label,description:Xn(e),character:e.character}));const Mt=[{code:"ar-SA",name:"Arabic",script:"rtl",dir:"rtl"},{code:"bn-BD",name:"Bengali",script:"indic",dir:"ltr"},{code:"ca",name:"Catalan",script:"latin",dir:"ltr"},{code:"cs",name:"Czech",script:"latin",dir:"ltr"},{code:"da",name:"Danish",script:"latin",dir:"ltr"},{code:"de-DE",name:"German",script:"latin",dir:"ltr"},{code:"el",name:"Greek",script:"greek",dir:"ltr"},{code:"en-AU",name:"English (Australia)",script:"latin",dir:"ltr"},{code:"en-CA",name:"English (Canada)",script:"latin",dir:"ltr"},{code:"en-GB",name:"English (U.K.)",script:"latin",dir:"ltr"},{code:"en-US",name:"English (U.S.)",script:"latin",dir:"ltr"},{code:"es-ES",name:"Spanish (Spain)",script:"latin",dir:"ltr"},{code:"es-MX",name:"Spanish (Mexico)",script:"latin",dir:"ltr"},{code:"fi",name:"Finnish",script:"latin",dir:"ltr"},{code:"fr-CA",name:"French (Canada)",script:"latin",dir:"ltr"},{code:"fr-FR",name:"French",script:"latin",dir:"ltr"},{code:"gu-IN",name:"Gujarati",script:"indic",dir:"ltr"},{code:"he",name:"Hebrew",script:"rtl",dir:"rtl"},{code:"hi",name:"Hindi",script:"indic",dir:"ltr"},{code:"hr",name:"Croatian",script:"latin",dir:"ltr"},{code:"hu",name:"Hungarian",script:"latin",dir:"ltr"},{code:"id",name:"Indonesian",script:"latin",dir:"ltr"},{code:"it",name:"Italian",script:"latin",dir:"ltr"},{code:"ja",name:"Japanese",script:"cjk",dir:"ltr"},{code:"kn-IN",name:"Kannada",script:"indic",dir:"ltr"},{code:"ko",name:"Korean",script:"cjk",dir:"ltr"},{code:"ml-IN",name:"Malayalam",script:"indic",dir:"ltr"},{code:"mr-IN",name:"Marathi",script:"indic",dir:"ltr"},{code:"ms",name:"Malay",script:"latin",dir:"ltr"},{code:"nl-NL",name:"Dutch",script:"latin",dir:"ltr"},{code:"no",name:"Norwegian",script:"latin",dir:"ltr"},{code:"or-IN",name:"Odia",script:"indic",dir:"ltr"},{code:"pa-IN",name:"Punjabi",script:"indic",dir:"ltr"},{code:"pl",name:"Polish",script:"latin",dir:"ltr"},{code:"pt-BR",name:"Portuguese (Brazil)",script:"latin",dir:"ltr"},{code:"pt-PT",name:"Portuguese (Portugal)",script:"latin",dir:"ltr"},{code:"ro",name:"Romanian",script:"latin",dir:"ltr"},{code:"ru",name:"Russian",script:"cyrillic",dir:"ltr"},{code:"sk",name:"Slovak",script:"latin",dir:"ltr"},{code:"sl-SI",name:"Slovenian",script:"latin",dir:"ltr"},{code:"sv",name:"Swedish",script:"latin",dir:"ltr"},{code:"ta-IN",name:"Tamil",script:"indic",dir:"ltr"},{code:"te-IN",name:"Telugu",script:"indic",dir:"ltr"},{code:"th",name:"Thai",script:"thai",dir:"ltr"},{code:"tr",name:"Turkish",script:"latin",dir:"ltr"},{code:"uk",name:"Ukrainian",script:"cyrillic",dir:"ltr"},{code:"ur-PK",name:"Urdu",script:"rtl",dir:"rtl"},{code:"vi",name:"Vietnamese",script:"latin",dir:"ltr"},{code:"zh-Hans",name:"Chinese (Simplified)",script:"cjk",dir:"ltr"},{code:"zh-Hant",name:"Chinese (Traditional)",script:"cjk",dir:"ltr"}];Mt.map(e=>e.code);Object.fromEntries(Mt.map(e=>[e.code,e]));({...we});function nr(e,t){t.material==="clay"?(e.setClay(t.clayTone==="custom"?`#${t.clayCustom}`:t.clayTone),e.setClearcoat(0)):(e.clayOff(),t.colorway==="custom"?e.setCustomColor(`#${t.customColor}`):e.setColorway(t.colorway),e.setFinish(R("finish",t.finish)),e.setClearcoat(R("clearcoat",t.clearcoat)))}function rr(e,t,n){e.screen.setGlare(!n&&t?e.device.environmentMap():null)}async function or(e,t,n,o){const{stage:r,device:a,screen:i}=e,s=t.material==="clay";if(t.cameraPos?r.setCameraPosition(t.cameraPos):r.setAngle(t.angle),a.setRoll(R("roll",t.roll)),nr(a,t),i.restoreScreen(),r.setStudioLighting(t.lighting),a.setReflections(t.reflections),i.setFlatScreen(t.flatScreen),n){try{await i.applyWallpaper(n)}catch{}if(!o())return}rr(e,t.glare,s)}async function ar(e,t,n,o={}){const r=e.renderer,a=r.getPixelRatio(),i=r.getSize(new ln);r.setPixelRatio(1),r.setSize(t,n,!1);let s;o.cameraPosition?(s=new Qe(tt,t/n,.01,100),s.position.copy(o.cameraPosition),s.lookAt(0,0,0)):(s=e.camera.clone(),s.aspect=t/n,s.updateProjectionMatrix());try{const l=e.scene.background;e.scene.background=null;try{r.render(e.scene,s)}finally{e.scene.background=l}const d=document.createElement("canvas");let u=null;try{d.width=t,d.height=n,d.getContext("2d")?.drawImage(r.domElement,0,0);const p=ir(d),h=p.canvas;u=h;const f=h.width,c=h.height;return{bytes:await new Promise((m,b)=>{h.toBlob(y=>{if(!y){b(new Error("toBlob returned null"));return}y.arrayBuffer().then(S=>m(new Uint8Array(S)),b)},"image/png")}),width:f,height:c,frame:{width:t,height:n,cropX:p.x,cropY:p.y}}}finally{u&&u!==d&&(u.width=0,u.height=0),d.width=0,d.height=0}}finally{r.setPixelRatio(a),r.setSize(i.x,i.y,!1)}}function ir(e,t=2){const n=e.getContext("2d",{willReadFrequently:!0});if(!n)return{canvas:e,x:0,y:0};const{width:o,height:r}=e,a=n.getImageData(0,0,o,r).data;let i=o,s=r,l=-1,d=-1;for(let f=0;f<r;f++)for(let c=0;c<o;c++)a[(f*o+c)*4+3]!==0&&(c<i&&(i=c),c>l&&(l=c),f<s&&(s=f),f>d&&(d=f));if(l<i||d<s)return{canvas:e,x:0,y:0};i=Math.max(0,i-t),s=Math.max(0,s-t),l=Math.min(o-1,l+t),d=Math.min(r-1,d+t);const u=l-i+1,p=d-s+1,h=document.createElement("canvas");return h.width=u,h.height=p,h.getContext("2d")?.drawImage(e,i,s,u,p,0,0,u,p),{canvas:h,x:i,y:s}}const sr=1900,lr=2800;function cr(e,t,n){return{shots:e,panels:t,selectedShotId:n.selectedShotId,panelPresetId:n.panelPresetId,bgMode:n.bgMode,gradientStops:n.gradientStops,gradientAngle:n.gradientAngle,gradientFrom:n.gradientFrom,gradientTo:n.gradientTo,gradientDir:n.gradientDir,panelColors:n.panelColors,shadow:n.shadow,floorReflection:n.floorReflection,panelBackgrounds:n.panelBackgrounds,captionStyles:n.captionStyles,captionText:n.captionText,locales:n.locales,baseLocale:n.baseLocale,outputs:n.outputs}}async function dr(e,t,n,o,r,a){const i=U(n,o),s=i.filter(f=>f.bytes);if(s.length===0)return null;const l=G(r.panelPresetId),d=new Map(o.map((f,c)=>[f.id,c])),u=r.renderScale!=null&&r.renderScale>0&&r.renderScale<=1?r.renderScale:1,p=Math.max(1,Math.round(sr*u)),h=Math.max(1,Math.round(lr*u));{const f=[];for(let b=0;b<s.length;b++){const y=s[b];a(`Rendering ${b+1}/${s.length}${r.locale?` (${r.locale})`:""}…`),await or(t,y.look,y.bytes,()=>!0);const S=await ar(e,p,h,{cameraPosition:bn(y.look.angle,y.look.cameraPos)});f.push({shotId:y.id,bytes:S.bytes,width:S.width,height:S.height,heightFrac:R("phoneHeight",y.look.phoneHeight)/100,hOffset:R("hOffset",y.look.hOffset)/100,vOffset:R("vOffset",y.look.vOffset)/100,panelIndex:d.get(y.panelId)??0,...y.look.clipToFrame===!0?{clipToFrame:!0}:{}})}const c=qn(f,l.width,l.height),g=Yn(o,r);a("Saving project…");const m=Et(cr(i,o,r));return{panelWidth:l.width,panelHeight:l.height,count:o.length,background:g,phones:c,shadow:r.shadow,floorReflection:r.floorReflection??!1,project:JSON.stringify(m)}}}function ee(e){return/^[0-9a-fA-F]{3,8}$/.test(e)?`#${e}`:e}function be(e,t,n,o){const{x0:r,y0:a,x1:i,y1:s}=Rn(n,o,t.angle),l=e.createLinearGradient(r,a,i,s);for(const d of t.stops)l.addColorStop(Math.min(1,Math.max(0,d.at)),ee(d.color));return l}function He(e,t,n,o,r,a,i){const s=document.createElement("canvas");s.width=n,s.height=o;try{const l=s.getContext("2d");if(!l)return;l.fillStyle=be(l,t,n,o),l.fillRect(r,0,a,o),e.drawImage(s,r,0,a,o,i,0,a,o)}finally{s.width=0,s.height=0}}function Ge(e){return URL.createObjectURL(new Blob([e],{type:"image/png"}))}function Ue(e){return new Promise((t,n)=>{const o=new Image;o.onload=()=>t(o),o.onerror=n,o.src=e})}const ur=16*1024*1024;function $e(e){const t=(e.img.naturalWidth||0)*(e.img.naturalHeight||0),n=e.reflection?e.reflection.canvas.width*e.reflection.canvas.height:0;return t+n}function Lt(){let e=0;const t=Array.from(F.values());for(const r of t)e+=$e(r);const n=Array.from(F.keys());let o=0;for(;e>ur&&o<n.length-1;){const r=F.get(n[o]);r&&(e-=$e(r),r.reflection&&(r.reflection.canvas.width=0,r.reflection.canvas.height=0),F.delete(n[o])),o++}}const F=new Map,ie=new Map;async function fr(e,t){if(!t){const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}}const n=F.get(e);if(n)return F.delete(e),F.set(e,n),n;const o=ie.get(e);if(o)return o;const r=(async()=>{const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}})();ie.set(e,r);try{const a=await r;return F.set(e,a),Lt(),a}finally{ie.delete(e)}}async function hr(e,t,n){const o=t.panelWidth*t.count,r=t.panelHeight,a=n?.backgroundViewport,i=a?t.panelWidth*a.panelCount:o,s=a?t.panelWidth*a.panelIndex:0;e.width=o,e.height=r;const l=e.getContext("2d");if(!l)return;n?.transparentBg?l.clearRect(0,0,o,r):t.background.kind==="gradient"?a?He(l,t.background.gradient,i,r,s,o,0):(l.fillStyle=be(l,t.background.gradient,o,r),l.fillRect(0,0,o,r)):t.background.kind==="perPanel"?t.background.colors.forEach((h,f)=>{l.fillStyle=ee(h),l.fillRect(f*t.panelWidth,0,t.panelWidth,r)}):t.background.fills.forEach((h,f)=>{const c=f*t.panelWidth;h.kind==="solid"?(l.fillStyle=ee(h.color),l.fillRect(c,0,t.panelWidth,r)):a?He(l,h.gradient,i,r,s+c,t.panelWidth,c):(l.fillStyle=be(l,h.gradient,o,r),l.fillRect(c,0,t.panelWidth,r))});const d=n?.phoneRange?Math.max(0,n.phoneRange.start):0,u=n?.phoneRange?Math.min(t.phones.length,n.phoneRange.end):t.phones.length,p=n?.reuseDecodedPhones===!0;for(let h=d;h<u;h++){const f=t.phones[h],c=n?.clip==="panel"||n?.clip===void 0&&f.clipToFrame===!0;if(n?.skipShotId&&f.shotId===n.skipShotId)continue;const g=await fr(f.bytes,p),m=g.img;c&&(l.save(),l.beginPath(),l.rect(f.panelIndex*t.panelWidth,0,t.panelWidth,r),l.clip()),t.floorReflection&&pr(l,g,f,p),l.save(),t.shadow&&(l.shadowColor="rgba(0,0,0,0.35)",l.shadowBlur=t.panelWidth*.03,l.shadowOffsetY=t.panelHeight*.012),l.drawImage(m,f.x,f.y,f.width,f.height),l.restore(),c&&l.restore()}}function pr(e,t,n,o){const r=Math.round(n.height*.45);if(r<=0||n.width<=0)return;const a=o?t.reflection:void 0;if(a&&a.width===n.width&&a.height===n.height){e.drawImage(a.canvas,n.x,n.y+n.height,n.width,r);return}const i=t.img,s=document.createElement("canvas");s.width=Math.max(1,n.width),s.height=Math.max(1,r);const l=s.getContext("2d");if(!l)return;l.save(),l.translate(0,r),l.scale(1,-1),l.drawImage(i,0,r-n.height,n.width,n.height),l.restore();const d=l.createLinearGradient(0,0,0,r);d.addColorStop(0,"rgba(0,0,0,0.34)"),d.addColorStop(1,"rgba(0,0,0,0)"),l.globalCompositeOperation="destination-in",l.fillStyle=d,l.fillRect(0,0,n.width,r),e.drawImage(s,n.x,n.y+n.height,n.width,r),o&&(t.reflection={width:n.width,height:n.height,canvas:s},Lt())}const Ft=1.2,mr=.06,gr=.5;function yr(e,t,n){if(!e)return[t];const o=t.split(/\s+/).filter(Boolean),r=[];let a="";for(const i of o){const s=a?`${a} ${i}`:i;a&&e.measureText(s).width>n?(r.push(a),a=i):a=s}return a&&r.push(a),r}function br(e,t){if(!e)return 0;let n=0;for(const o of t)n=Math.max(n,e.measureText(o).width);return n}function Rt(e,t,n,o){const r=t.map((c,g)=>{const m=c.text.trim();if(!m)return null;const b=c.sizePt*(o/te),y=it(c.fontId).family;e&&(e.font=`${J} ${b}px "${y}"`);const S=yr(e,m,c.maxWidth*n),T=b*(c.lineHeight??Ft),P=S.length*T,w=Math.max(P,(c.reserveLines??S.length)*T);return{layer:c,index:g,lines:S,px:b,lineH:T,width:br(e,S),blockH:P,reservedH:w}}),a=r.filter(c=>c!==null&&B(c.layer.anchor)),i=a.filter(c=>c.layer.band==="bottom"),s=a.filter(c=>c.layer.band!=="bottom"),l=c=>c[0]?.layer.bandInset??mr,d=(1-l(i))*o-i.reduce((c,g)=>c+g.reservedH,0);let u=l(s)*o,p=d,h=0;const f=[];for(const c of r){if(!c)continue;if(!B(c.layer.anchor)){f.push({layer:c.layer,index:c.index,x:c.layer.anchor.x*n,yTop:c.layer.anchor.y*o,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width});continue}const g=c.layer.band==="bottom",b=(g?p:u)+(c.reservedH-c.blockH)/2,y=(c.layer.bandCenter??gr)*n;f.push({layer:c.layer,index:c.index,x:y,yTop:b,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width}),g?p+=c.reservedH:u+=c.reservedH,h=Math.max(h,b+c.blockH)}return{placed:f,bandBottom:h}}function Sr(e,t,n,o){e.save(),e.textBaseline="top";const{placed:r}=Rt(e,t,n,o);for(const a of r){e.fillStyle=ee(a.layer.color),e.textAlign=a.layer.align,e.font=`${J} ${a.px}px "${it(a.layer.fontId).family}"`;let i=a.yTop;for(const s of a.lines)e.fillText(s,a.x,i),i+=a.lineH}e.restore()}function vr(e,t){return e.sizePt*(t/te)*(e.lineHeight??Ft)}function wr(e,t,n,o){return Rt(e,t,n,o).placed.map(r=>{const a=r.layer.align==="center"?r.x-r.width/2:r.layer.align==="right"?r.x-r.width:r.x;return{index:r.index,anchorX:r.x/n,top:r.yTop/o,left:a/n,width:r.width/n,height:r.lines.length*r.lineH/o}})}function Ir(e,t,n,o,r){t.forEach((a,i)=>{const s=r[a];!s||s.length===0||(e.save(),e.translate(i*n,0),Sr(e,s,n,o),e.restore())})}const xr=""+new URL("Inter-SemiBold-B9yU462S.woff2",import.meta.url).href,Pr=""+new URL("Manrope-SemiBold-44nuYf5T.woff2",import.meta.url).href,Ar=""+new URL("Poppins-SemiBold-DJVo7c07.woff2",import.meta.url).href,Or=""+new URL("Fraunces-SemiBold-BtraKtER.woff2",import.meta.url).href,Tr=""+new URL("SpaceGrotesk-SemiBold-CyO2J5Y0.woff2",import.meta.url).href,kr={inter:xr,manrope:Pr,poppins:Ar,fraunces:Or,"space-grotesk":Tr},Cr=X.map(e=>({...e,url:kr[e.id]}));let K=null;function Er(){return K||(K=(async()=>{const e=new Set;document.fonts.forEach(t=>e.add(`${t.family}/${t.weight}`)),await Promise.all(Cr.filter(t=>!e.has(`${t.family}/${String(J)}`)).map(async t=>{const n=new FontFace(t.family,`url(${t.url})`,{weight:String(J)});await n.load(),document.fonts.add(n)})),await document.fonts.ready})().catch(e=>{throw K=null,e})),K}const Mr=22/te,Lr=1.1,We=4,Fr=.063,Rr=.167,Ve=Math.round(Mr*te),_r=.02,Nr=50;function j(e,t){const n=Math.max(e.x,t.x),o=Math.max(e.y,t.y),r=Math.min(e.x+e.width,t.x+t.width),a=Math.min(e.y+e.height,t.y+t.height);return r<=n||a<=o?null:{x:n,y:o,width:r-n,height:a-o}}function _t(e,t){return j(e,t)}function Ke(e,t){const n=Math.abs(e-t)%360;return n>180?360-n:n}function qe(e){return gn(e.cameraPos??et(e.angle))}function Nt(e,t,n,o,r,a,i){if(t.length===0)return[];const s=[];for(const l of wr(e,t,r,a)){const d=t[l.index];if(!d)continue;const u=_t({x:(o+l.left)*r,y:l.top*a,width:l.width*r,height:l.height*a},i);u&&s.push({ref:{panelId:n,panelIndex:o,layerIndex:l.index,band:B(d.anchor)},box:u,lineHeightPx:vr(d,a)})}return s}function Br(e,t,n,o){return n.height<=Se(e,t,o)}function Se(e,t,n){const o=e.y+e.height/2<=t.y+t.height/2;return n*(o?Rr:Fr)}function zr(e,t,n){return n.height<=Se(e.box,t.box,e.lineHeightPx)+Se(t.box,e.box,t.lineHeightPx)}function Dr(e){const{ctx:t,panelIds:n,panelWidth:o,panelHeight:r}=e,a={x:0,y:0,width:o*n.length,height:r};t?.save();try{return jr(e,a)}finally{t?.restore()}}function jr(e,t){const{ctx:n,panelIds:o,panelWidth:r,panelHeight:a,captionLayers:i}=e,s=o.flatMap((c,g)=>Nt(n,i[c]??[],c,g,r,a,t)),l=[];for(const c of e.phones){const m=e.clip==="panel"||e.clip===void 0&&c.clipToFrame===!0?{x:c.panelIndex*r,y:0,width:r,height:a}:t,b=_t({x:c.x,y:c.y,width:c.width,height:c.height},m);b&&l.push({shotId:c.shotId,box:b,layoutHeight:c.height})}const d=[];for(let c=0;c<s.length;c++)for(let g=c+1;g<s.length;g++){const m=j(s[c].box,s[g].box);m&&d.push({a:s[c].ref,b:s[g].ref,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:zr(s[c],s[g],m)})}const u=[];for(const c of s)for(const g of l){const m=j(c.box,g.box);m&&u.push({caption:c.ref,shotId:g.shotId,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Br(c.box,g.box,m,c.lineHeightPx)})}const p=[];for(let c=0;c<l.length;c++)for(let g=c+1;g<l.length;g++){const m=l[c],b=l[g],y=j(m.box,b.box);if(!y)continue;const S=[],T=Math.max(m.layoutHeight,b.layoutHeight),P=Math.min(m.layoutHeight,b.layoutHeight);P>0&&T/P>=Lr&&S.push("scale");const w=e.looks?.[m.shotId],v=e.looks?.[b.shotId],I=!!w&&!!v;w&&v&&(Ke(qe(w),qe(v))>=We&&S.push("angle"),Ke(R("roll",w.roll),R("roll",v.roll))>=We&&S.push("roll"));const A=Math.min(m.box.width*m.box.height,b.box.width*b.box.height);p.push({shotIdA:m.shotId,shotIdB:b.shotId,overlap:y,coverage:A>0?y.width*y.height/A:0,deliberate:S.length>0,signals:S,anglesKnown:I})}const h=new Set;for(const c of d)c.a.band&&h.add(c.a.panelId),c.b.band&&h.add(c.b.panelId);for(const c of u)c.caption.band&&h.add(c.caption.panelId);const f=[];return o.forEach((c,g)=>{if(!h.has(c))return;const m=Hr(e,c,g,t,s,l);m&&f.push(m)}),{captionsMeasurable:n!=null,captionOverlaps:d,captionDeviceOverlaps:u,deviceOverlaps:p,suggestions:f,hasWarning:d.length>0||u.length>0||p.some(c=>!c.deliberate)}}function Hr(e,t,n,o,r,a){const i=e.captionLayers[t]??[],s=i.flatMap((u,p)=>B(u.anchor)&&u.text.trim()?[p]:[]);if(s.length===0||!(Math.min(...s.map(u=>i[u].sizePt))>Ve))return null;const d=[...a.map(u=>u.box),...r.filter(u=>!(u.ref.panelId===t&&u.ref.band)).map(u=>u.box)];for(let u=1;u<=Nr;u++){const p=1-u*_r,h=new Map(s.map(m=>[m,Math.floor(i[m].sizePt*p)]));if(Math.min(...h.values())<Ve)return null;const f=i.map((m,b)=>{const y=h.get(b);return y===void 0?m:{...m,sizePt:y}});if(Nt(e.ctx,f,t,n,e.panelWidth,e.panelHeight,o).filter(m=>m.ref.band).every(m=>!d.some(b=>j(m.box,b))))return{panelId:t,scale:p,layers:s.map(m=>({layerIndex:m,fromSizePt:i[m].sizePt,toSizePt:h.get(m)}))}}return null}function Bt(e){return`build-shot-${e+1}`}function zt(e){const t=[];for(const n of e.shots)t.includes(n.panelId)||t.push(n.panelId);return t}const Gr=1900,Ur=2800;function $r(e){return new Promise((t,n)=>{e.toBlob(o=>{if(!o)return n(new Error("canvas toBlob returned null"));o.arrayBuffer().then(r=>t(new Uint8Array(r)),n)},"image/png")})}async function Dt(e,t,n,o={}){await Er();const r=zt(e),a=r.map(h=>({id:h})),i=e.shots.map((h,f)=>({id:Bt(f),panelId:h.panelId,frameNodeId:"",frameName:`screen-${f+1}`,bytes:t.screens[f]??null,look:h.look,thumb:null,thumbStale:!1})),l={panelPresetId:G(e.panelPresetId).id,bgMode:e.bgMode,gradientStops:e.gradientStops,gradientAngle:e.gradientAngle,gradientFrom:e.gradientFrom,gradientTo:e.gradientTo,gradientDir:e.gradientDir,panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection,panelBackgrounds:e.panelBackgrounds,selectedShotId:null,renderScale:o.renderScale,locale:n},d=o.onProgress??(()=>{});let u=o.refs,p=null;if(!u){const h=An(e.panelPresetId);p=nt(Gr,Ur);const f=await rt(p,h);u={stage:p,device:new ot(f,p.scene,p.renderer,h),screen:new at(f,h)}}try{const h=await dr(u.stage,u,i,a,l,d);if(!h)throw new Error("buildStrip: nothing renderable (no shots)");const f=document.createElement("canvas");try{const c={...o.clip?{clip:o.clip}:{},...o.backgroundViewport?{backgroundViewport:o.backgroundViewport}:{}};await hr(f,h,Object.keys(c).length>0?c:void 0);const g=h.panelWidth,m=h.panelHeight,b=f.getContext("2d");if(!b)throw new Error("buildStrip: 2D context unavailable");Ir(b,r,g,m,t.captionText??{});const y=Dr({phones:h.phones,panelIds:r,captionLayers:t.captionText??{},panelWidth:g,panelHeight:m,ctx:b,looks:Object.fromEntries(i.map(P=>[P.id,P.look])),clip:o.clip}),S=o.renderScale&&o.renderScale>0&&o.renderScale<1?o.renderScale:1,T=[];for(let P=0;P<h.count;P++){const w=document.createElement("canvas");w.width=Math.max(1,Math.round(g*S)),w.height=Math.max(1,Math.round(m*S));const v=w.width,I=w.height;try{const A=w.getContext("2d");if(!A)throw new Error("buildStrip: 2D context unavailable");A.drawImage(f,P*g,0,g,m,0,0,v,I),T.push({index:P,bytes:await $r(w),width:v,height:I})}finally{w.width=0,w.height=0}}return{panels:T,layout:y}}finally{f.width=0,f.height=0}}finally{p&&p.dispose()}}const Ye=.005,Wr=.02,Vr=12;function se(e){return Math.max(1,Math.round(e*100))}function le(e){return`${e.kind}:${e.frames.join(",")}:${e.anchored}`}function Kr(e,t){const n=new Map(e.suggestions.map(a=>[a.panelId,a.scale])),o=[];for(const a of e.captionOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const[i,s]=a.a.band===a.b.band?[a.a,a.b]:a.a.band?[a.a,a.b]:[a.b,a.a],l=ce([t.frameOfPanel(i.panelId),t.frameOfPanel(s.panelId)]);l.length!==0&&o.push({kind:"caption-caption",frames:l,depthPct:se(a.depthFrac),anchored:!a.a.band||!a.b.band,...Xe(n,i.band?i.panelId:null)})}for(const a of e.captionDeviceOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const i=ce([t.frameOfPanel(a.caption.panelId),t.frameOfShot(a.shotId)]);i.length!==0&&o.push({kind:"caption-device",frames:i,depthPct:se(a.depthFrac),anchored:!a.caption.band,...Xe(n,a.caption.band?a.caption.panelId:null)})}for(const a of e.deviceOverlaps){if(a.deliberate||a.coverage<Wr)continue;const i=ce([t.frameOfShot(a.shotIdA),t.frameOfShot(a.shotIdB)]);i.length!==0&&o.push({kind:"device-device",frames:i,depthPct:se(a.coverage),anchored:!1})}const r=new Map;for(const a of o){const i=r.get(le(a));i?a.depthPct>i.depthPct?r.set(le(a),{...a,clearAtScale:a.clearAtScale??i.clearAtScale}):i.clearAtScale==null&&a.clearAtScale!=null&&(i.clearAtScale=a.clearAtScale):r.set(le(a),a)}return[...r.values()].sort((a,i)=>i.depthPct-a.depthPct).slice(0,Vr).sort((a,i)=>a.frames[0]-i.frames[0]||a.kind.localeCompare(i.kind))}function ce(e){const[t,...n]=e;return t<0?[]:[t,...n.filter(o=>o>=0&&o!==t)]}function Xe(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtScale:Math.round(n*100)/100}}function qr(e,t){if(t.length===0)return e;const n=e.captionStyles;if(!n)return e;const o={};for(const[r,a]of Object.entries(n))o[r]=a.map(i=>({...i}));for(const r of t){const a=o[r.panelId];if(a)for(const i of r.layers){const s=a[i.layerIndex];s&&(s.sizePt=i.toSizePt)}}return{...e,captionStyles:o}}function de(e){return e.frames.map(t=>`frame ${t+1}`).join(" + ")}function Yr(e,t,n){if(!e.captionsMeasurable)return null;const o=Kr(e,t);if(o.length===0)return null;const r=o.filter(d=>d.kind==="device-device"),a=o.filter(d=>d.kind!=="device-device"&&d.clearAtScale==null);if(r.length>0||a.length>0||!n){const d=[];r.length>0&&d.push(`${r.map(de).join(", ")}: two devices overlap without reading as composed. There is no deterministic repair — give the pair a real difference in scale, camera angle or roll (that is what makes an overlap read as deliberate), or leave it if it already is.`);for(const u of a)d.push(u.anchored?`${de(u)}: the caption is anchored by hand, so it is reported and never rewritten — move it, or drop its \`anchor\` to let the band place it.`:`${de(u)}: no caption size above the legibility floor clears it — shorten the headline, or pick a layout with a bigger text region (\`style.layout: "bleed"\`).`);return d.length===0&&d.push("The rendered look was not available here, so no corrected look could be built."),{findings:o,note:`No corrected look is offered. ${d.join(" ")}`}}const i=new Set(o.filter(d=>d.kind!=="device-device").map(d=>d.frames[0])),s=e.suggestions.filter(d=>i.has(t.frameOfPanel(d.panelId))),l=s.flatMap(d=>{const u=t.frameOfPanel(d.panelId)+1;return d.layers.map(p=>`frame ${u} layer ${p.layerIndex+1}: sizePt ${p.fromSizePt} → ${p.toSizePt}`)}).join("; ");return{findings:o,correctedLook:qr(n,s),note:`\`layout.correctedLook\` is this same look with every caption shrunk to the largest size that was verified to clear the overlap (${l}). Nothing here is applied for you — re-render with it, or show the user the finding and let them decide. On render_strip pass it back as \`look\` together with the SAME \`style.captions\` you just sent: the words come from \`style.captions\` and every style field you leave out of them now comes from this look. The one thing that still beats it is a field you set EXPLICITLY on a caption entry, so if your \`style.captions\` pin \`sizePt\` at the original numbers, drop them or set them to the sizes above.`}}function Xr(e){return String(Math.max(-100,Math.min(100,e*35)))}const Jr=["roll","phoneHeight","hOffset","vOffset","finish","clearcoat"];function Je(e){if(!e||typeof e!="object")return{};const t={...e};for(const n of Jr)typeof t[n]=="number"&&(t[n]=String(t[n]));return t}function Zr(e){const{subtitle:t,sizePt:n,maxWidth:o,...r}=e;return{...r,...n!==void 0?{sizePt:Number(n)}:{},...o!==void 0?{maxWidth:Number(o)}:{}}}function Ze(e){const t={};for(const[n,o]of Object.entries(e))o!==void 0&&(t[n]=o);return t}function ue(e,t,n){const o={};return e&&t.forEach((r,a)=>{const i=n[a];i!==void 0&&e[r]!==void 0&&(o[i]=e[r])}),o}function Qr(e){const t=e.slotSizes.filter(v=>Number.isInteger(v)&&v>0),n=t.map((v,I)=>`panel-${I+1}`);let o={};const r=e.look&&typeof e.look=="object"?e.look:null,a=e.style??null,i=r&&Array.isArray(r.shots)?r.shots:[],s=i.length>0,l=a?Je(a.shotLook):null,d=[];let u=0;t.forEach((v,I)=>{for(let A=0;A<v;A++){const O=s?Je(i[u]?.look??i[0]?.look??{}):l??{},$=O.hOffset!==void 0&&O.hOffset!==null?String(O.hOffset):Xr(A);d.push({panelId:n[I],look:{...x,...O,hOffset:$}}),u++}});let p={},h={},f={},c="gradient",g=kn,m=Cn,b="vertical",y,S,T=!0,P=!1;if(r){c=r.bgMode==="perPanel"?"perPanel":"gradient";const v=Q({gradientStops:Array.isArray(r.gradientStops)?r.gradientStops:void 0,gradientAngle:typeof r.gradientAngle=="number"?r.gradientAngle:void 0,gradientFrom:typeof r.gradientFrom=="string"?r.gradientFrom:g,gradientTo:typeof r.gradientTo=="string"?r.gradientTo:m,gradientDir:r.gradientDir==="horizontal"?"horizontal":b});y=v.stops,S=v.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(v),r.shadow===!1&&(T=!1),r.floorReflection===!0&&(P=!0);const I=[];for(const A of i){const O=typeof A?.panelId=="string"?A.panelId:null;O&&!I.includes(O)&&I.push(O)}p=ue(r.panelColors,I,n),h=ue(xt(r.captionStyles),I,n),f=ue(vt(r.panelBackgrounds),I,n)}else if(a){const v=a.background??{};c=v.mode==="perPanel"?"perPanel":"gradient";const I=Q({gradientStops:v.stops,gradientAngle:v.angle,gradientFrom:g,gradientTo:m,gradientDir:b});y=I.stops,S=I.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(I),v.shadow===!1&&(T=!1),v.floorReflection===!0&&(P=!0),Array.isArray(v.panelColors)&&v.panelColors.forEach((A,O)=>{typeof A=="string"&&A&&n[O]!==void 0&&(p[n[O]]=A)})}if(a&&Array.isArray(a.captions)){const v=h,I=(L,oe)=>({...v[L]?.[oe]??{}}),A={},O={};a.captions.forEach((L,oe)=>{const _=n[oe];if(!(_===void 0||L===null||L===void 0)){if(Array.isArray(L)){const N=L.filter(E=>!!E&&typeof E=="object"&&typeof E.text=="string"&&E.text.trim()!=="").map((E,W)=>({...I(_,W),...Ze(Zr(E))}));N.length>0&&(O[_]=N)}else if(typeof L=="object"){const{text:N,subtitle:E,sizePt:W,maxWidth:Ae,...$t}=L;A[_]={...I(_,0),...Ze({...$t,...W!==void 0?{sizePt:Number(W)}:{},...Ae!==void 0?{maxWidth:Number(Ae)}:{}})},typeof N=="string"&&N.trim()&&(O[_]={headline:N,...typeof E=="string"&&E?{subtitle:E}:{}})}}});const $=re({[M]:O},It(A));h=At($),o=Pt($)}return{look:{panelPresetId:e.panelPresetId??(typeof r?.panelPresetId=="string"?r.panelPresetId:void 0)??"r69",bgMode:c,gradientStops:y,gradientAngle:S,gradientFrom:g,gradientTo:m,gradientDir:b,panelColors:p,shadow:T,floorReflection:P,panelBackgrounds:f,captionStyles:h,shots:d},panelIds:n,captionText:o}}function eo(e){const t=e?.trim();return t?/\.(png|jpe?g|webp)$/i.test(t)?t:`${t}.png`:null}function to(e,t){const n=new Map;return e.map((o,r)=>{const a=eo(t?.[r]);if(!a)return`screen-${r+1}.png`;const i=n.get(a)??0;if(n.set(a,i+1),i===0)return a;const s=a.lastIndexOf(".");return`${a.slice(0,s)}-${i+1}${a.slice(s)}`})}function no(e){const{look:t,panelIds:n,captionText:o,locale:r,shotNames:a,outputs:i}=e,s=n.map(u=>({id:u})),l=to(t.shots,a),d=t.shots.map((u,p)=>({id:`shot-${p+1}`,panelId:u.panelId,frameNodeId:l[p],frameName:l[p],bytes:null,look:u.look,thumb:null,thumbStale:!0}));return Et({shots:d,panels:s,selectedShotId:d[0]?.id??null,panelPresetId:t.panelPresetId,bgMode:t.bgMode,gradientStops:t.gradientStops,gradientAngle:t.gradientAngle,gradientFrom:t.gradientFrom,gradientTo:t.gradientTo,gradientDir:t.gradientDir,panelColors:t.panelColors,shadow:t.shadow,floorReflection:t.floorReflection,panelBackgrounds:t.panelBackgrounds,captionStyles:t.captionStyles,outputs:i,captionText:o&&Object.keys(o).length>0?{[r||M]:o}:{}})}function C(e){console.log("[render]",e)}const ro=1900,oo=2800;let fe=null;async function jt(){return fe||(fe=(async()=>{C("booting headless stage + loading model (once)…");const e=nt(ro,oo),t=await wn(e);return{stage:e,device:new ot(t,e.scene,e.renderer),screen:new at(t)}})()),fe}function Ht(e){const t=atob(e),n=new Uint8Array(t.length);for(let o=0;o<t.length;o++)n[o]=t.charCodeAt(o);return n}function Pe(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function ao(e){return new Promise((t,n)=>{e.toBlob(async o=>{if(!o)return n(new Error("toBlob null"));t(Pe(new Uint8Array(await o.arrayBuffer())))},"image/png")})}function Gt(e,t){const n=zt(t),o=new Map(t.shots.map((a,i)=>[Bt(i),n.indexOf(a.panelId)])),r=Yr(e.layout,{frameOfPanel:a=>n.indexOf(a),frameOfShot:a=>o.get(a)??-1},t);if(r)return{layout:{findings:r.findings,...r.correctedLook?{correctedLook:r.correctedLook}:{}},layoutNote:r.note}}async function Ut(e){const t=await Promise.all(e.map(a=>createImageBitmap(new Blob([new Uint8Array(a.bytes)],{type:"image/png"})))),n=document.createElement("canvas");n.width=t.reduce((a,i)=>a+i.width,0),n.height=t[0]?.height??1;const o=n.getContext("2d");let r=0;for(const a of t)o.drawImage(a,r,0),r+=a.width,a.close();return{base64:await ao(n),width:n.width,height:n.height}}window.renderStrip=async function(t){try{if(!t||!Array.isArray(t.screenshots)||t.screenshots.length===0)throw new Error("renderStrip: input.screenshots must be a non-empty nested array of slots");const n=await jt(),o=t.screenshots.map(S=>Array.isArray(S)?S:[S]),{look:r,panelIds:a,captionText:i}=Qr({slotSizes:o.map(S=>S.length),panelPresetId:t.panelPresetId,look:t.look??void 0,style:t.style??void 0}),s=o.flat().map(Ht),l=Array.isArray(t.captionLayers)&&a[0]?{[a[0]]:t.captionLayers}:i,d=t.preview?.25:1;C(`buildStrip (${o.length} panel${o.length===1?"":"s"}${t.locale?`, ${t.locale}`:""})…`);const u=performance.now(),p=await Dt(r,{screens:s,captionText:l},t.locale,{refs:n,renderScale:d,onProgress:S=>C(" "+S),...t.clip?{clip:t.clip}:{},...t.backgroundViewport?{backgroundViewport:t.backgroundViewport}:{}});C("encoding panels + stitching the strip preview…");const h=p.panels.map(S=>Pe(S.bytes)),f=await Ut(p.panels),c=G(r.panelPresetId),g=Math.round(performance.now()-u),m=Gt(p,r),b=Array.isArray(t.screenshotNames)?t.screenshotNames.flat():void 0,y=t.buildRecord?no({look:r,panelIds:a,captionText:i,locale:t.locale,shotNames:b,outputs:t.outputs}):void 0;return C(`done: ${p.panels.length} panels, strip ${f.width}x${f.height}, ${g}ms`),{ok:!0,ms:g,count:p.panels.length,panelPresetId:c.id,panelWidth:c.width,panelHeight:c.height,stripWidth:f.width,stripHeight:f.height,panels:h,stripBase64:f.base64,preview:d<1,...y?{projectRecord:y}:{},...m??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.renderProject=async function(t){try{if(!t||t.projectFile==null||typeof t.projectFile!="object")throw new Error("renderProject: input.projectFile (the saved ProjectFile) is required");const n=Qn(JSON.stringify(t.projectFile));if(!n)throw new Error("renderProject: the saved project could not be restored (wrong schema or malformed record)");const o=await jt(),r=t.locale||n.baseLocale||n.locales?.[0]||"en-US",a=U(n.shots,n.panels),i=me(t.variant||{},n.baseLocale),s=new Map((t.screenManifest||[]).map(w=>[`${w.shotId} ${Be(me(w.variant||{},n.baseLocale))}`,w])),l=(w,v)=>s.get(`${w} ${Be(v)}`),d=a.map(w=>Wn(l,w.id,i,Un,n.baseLocale)),u=a.map((w,v)=>({frameName:w.frameName,matched:d[v].value!==void 0,requestedVariant:i,resolvedAt:d[v].resolvedAt,inherited:d[v].inherited,...d[v].value?.source?{source:d[v].value.source}:{}})),p=d.map(({value:w})=>w?.bytes?Ht(w.bytes):null),h=Pt(n.captionText,r,n.baseLocale),f=tr({...n,selectedShotId:null,panelPresetId:t.panelPresetId??n.panelPresetId,captionText:{[r]:h}}),c=t.preview?.25:1;C(`renderProject (${a.length} shot${a.length===1?"":"s"}, ${r})…`);const g=performance.now(),m=await Dt(f,{screens:p,captionText:h},r,{refs:o,renderScale:c,onProgress:w=>C(" "+w)});C("encoding panels + stitching the strip preview…");const b=m.panels.map(w=>Pe(w.bytes)),y=await Ut(m.panels),S=G(f.panelPresetId),T=Math.round(performance.now()-g),P=Gt(m,f);return C(`done: ${m.panels.length} panels, strip ${y.width}x${y.height}, ${T}ms`),{ok:!0,ms:T,count:m.panels.length,panelPresetId:S.id,panelWidth:S.width,panelHeight:S.height,stripWidth:y.width,stripHeight:y.height,panels:b,stripBase64:y.base64,preview:c<1,shots:u,locale:r,...P??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.__RENDER_READY=!0;C("harness ready ✓");
|
package/harness-dist/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<title>shotops-mcp headless render harness</title>
|
|
6
|
-
<script type="module" crossorigin src="./assets/index-
|
|
6
|
+
<script type="module" crossorigin src="./assets/index-gGXe4d5n.js"></script>
|
|
7
7
|
<link rel="modulepreload" crossorigin href="./assets/three-webgl-DCw804Ic.js">
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/three-addons-CvX38LO6.js">
|
|
9
9
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shotops-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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.",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{S as Kt,G as qt,A as Yt,D as ae,M as Xt,R as Jt,a as Zt,C as Qt,b as en,c as tn}from"./three-addons-CvX38LO6.js";import{aa as Qe,P as et,b as nn,V as q,a4 as rn,C as k,ab as on,J as Oe,$ as V,M as an,a2 as sn,B as ln}from"./three-webgl-DCw804Ic.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))o(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&o(i)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerPolicy&&(a.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?a.credentials="include":r.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function o(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();const cn=""+new URL("iphone-optimized-BdcJzlHi.glb",import.meta.url).href,dn="data:model/gltf-binary;base64,Z2xURgIAAADUAAAAwAAAAEpTT057ImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJTaG90T3BzIHBsYWNlaG9sZGVyIOKAlCB0aGUgUGl4ZWwgbW9kZWwgaXMgZGVsaWJlcmF0ZWx5IG5vdCBidW5kbGVkIGluIHRoZSBucG0gcGFja2FnZSJ9LCJzY2VuZXMiOlt7Im5hbWUiOiJwaXhlbC1ub3QtYnVuZGxlZCIsIm5vZGVzIjpbXX1dLCJzY2VuZSI6MH0gICA=",un=[{id:"grey",label:"Grey",color:11579568},{id:"white",label:"White",color:15263458},{id:"charcoal",label:"Charcoal",color:3815996}],fn=[{id:"silver",label:"Silver",frame:16119285,back:16514043,accent:13816532},{id:"blue",label:"Deep Blue",frame:3290954,back:4409953,accent:2172211},{id:"orange",label:"Cosmic Orange",frame:16219693,back:16487504,accent:12738072}],hn=[{id:"silver",label:"Moonstone",frame:9345186,back:8621205,accent:7041658},{id:"blue",label:"Obsidian",frame:2763308,back:2565928,accent:2105377},{id:"orange",label:"Porcelain",frame:15920872,back:14670805,accent:11973550},{id:"green",label:"Jade",frame:9412240,back:8688005,accent:7042924}],Y={id:"iphone-17-pro",glb:cn,fitSize:3.6,baseEuler:{x:0,y:0,z:0},pruneNamePrefixes:[],screen:{material:"OLED",repeat:{x:-1,y:1},coverFill:1.001,coverCornerRatio:.18,coverRepeat:{x:-1,y:-1}},body:{frame:["Anodized aluminum"],back:["Frosted glass"],accent:["Plastic antena","PaletteMaterial001"]},colorways:fn},Te={id:"pixel-10-pro",glb:dn,fitSize:3.6,baseEuler:{x:0,y:Math.PI,z:0},pruneNamePrefixes:["Google Pixel 10 Pro Moonstone.1"],screen:{material:"GP10p_display",repeat:{x:1,y:1},coverFill:1.001,coverCornerRatio:.14,coverRepeat:{x:1,y:-1}},body:{frame:["GP10p_mid","GP10p_camera"],back:["GP10p_back","GP10p_plastic"],accent:["GP10p_logo"]},colorways:hn},pn={[Y.id]:Y,[Te.id]:Te};function we(e){return e&&pn[e]||Y}const mn=7,ke={front:{x:0,y:0,z:-1},left:{x:-2.6,y:.6,z:-5.2},right:{x:2.6,y:.6,z:-5.2}};function tt(e){const t=ke[e]??ke.front,n=Math.hypot(t.x,t.y,t.z),o=mn/n;return{x:t.x*o,y:t.y*o,z:t.z*o}}function gn(e){return Math.atan2(e.x,-e.z)*180/Math.PI}function he(e){const t=tt(e);return new q(t.x,t.y,t.z)}const yn=1.4;function bn(e,t){return(t?new q(t.x,t.y,t.z):he(e)).multiplyScalar(yn)}const nt=35;function Sn(e){e.traverse(t=>{const n=t;n.geometry?.dispose();const o=n.material;Array.isArray(o)?o.forEach(Ce):o&&Ce(o)}),e.clear()}function Ce(e){for(const t of Object.values(e))t&&typeof t=="object"&&t.isTexture&&t.dispose();e.dispose()}function rt(e,t){const n=document.createElement("canvas"),o=new Qe({canvas:n,antialias:!0,preserveDrawingBuffer:!0,alpha:!0});o.setSize(e,t,!1),o.shadowMap.enabled=!0;const r=new Kt;r.background=null;const{ambient:a,directionals:i}=wn(r),l=new et(nt,e/t,.01,100);l.position.copy(he("front")),l.lookAt(0,0,0);function s(f){l.position.copy(he(f)),l.lookAt(0,0,0)}function d(f){l.position.set(f.x,f.y,f.z),l.lookAt(0,0,0)}function u(f){i.forEach(c=>c.visible=f),a.intensity=f?.75:1.6}let p=!1;function h(){p||(p=!0,r.environment?.dispose(),r.environment=null,Sn(r),o.dispose(),o.forceContextLoss())}return{scene:r,camera:l,renderer:o,dispose:h,setAngle:s,setCameraPosition:d,getCameraPosition:()=>({x:l.position.x,y:l.position.y,z:l.position.z}),onUserPose:()=>{},previewOrbit:()=>{},commitOrbit:()=>{},setOrbitEnabled:()=>{},setBackground:()=>{},setStudioLighting:u,resize:()=>{}}}function wn(e){const t=new Yt(16777215,.75);e.add(t);const n=new ae(16777215,2.2);n.position.set(3,5,5);const o=new ae(12571903,.7);o.position.set(-4,1,2);const r=new ae(16777215,1);r.position.set(0,3,-5);const a=[n,o,r];return a.forEach(i=>e.add(i)),{ambient:t,directionals:a}}function ot(e,t){const n=we(t);return new Promise((o,r)=>{new qt().load(n.glb,a=>{const i=a.scene;if(n.pruneNamePrefixes.length){const h=[];i.traverse(f=>{n.pruneNamePrefixes.some(c=>f.name.startsWith(c))&&h.push(f)}),h.forEach(f=>f.removeFromParent())}i.rotation.set(n.baseEuler.x,n.baseEuler.y,n.baseEuler.z),i.updateMatrixWorld(!0);const l=new nn().setFromObject(i),s=l.getSize(new q),d=l.getCenter(new q),u=n.fitSize/Math.max(s.x,s.y,s.z);i.scale.multiplyScalar(u),i.position.copy(d.multiplyScalar(-u));const p=new rn;p.add(i),e.scene.add(p),e.setAngle("front"),o(p)},void 0,r)})}function vn(e){return ot(e)}Y.colorways;class at{frame=[];back=[];accent=[];allBody=[];model;scene;renderer;profile;envTexture=null;constructor(t,n,o,r){this.model=t,this.scene=n,this.renderer=o,this.profile=we(r);const{frame:a,back:i,accent:l}=this.profile.body;t.traverse(s=>{const d=s;if(!d.isMesh)return;(Array.isArray(d.material)?d.material:[d.material]).forEach(p=>{const h=p;!h||!h.name||(a.includes(h.name)?this.push(this.frame,h):i.includes(h.name)?this.push(this.back,h):l.includes(h.name)&&this.push(this.accent,h),this.push(this.allBody,h))})})}push(t,n){t.includes(n)||t.push(n)}get finishMaterials(){return[...this.frame,...this.back]}paint(t,n){const o=n instanceof k?n:new k(n);t.forEach(r=>{r.color=o.clone(),r.needsUpdate=!0})}setColorway(t){if(t==="custom")return;const n=this.profile.colorways.find(o=>o.id===t)??this.profile.colorways[0];n&&(this.paint(this.frame,n.frame),this.paint(this.back,n.back),this.paint(this.accent,n.accent))}setCustomColor(t){const n=new k(t),o=n.clone().lerp(new k(16777215),.2),r=n.clone().multiplyScalar(.6);this.paint(this.frame,n),this.paint(this.back,o),this.paint(this.accent,r)}setFinish(t){const n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0;this.finishMaterials.forEach(o=>{o.userData.baseRoughness===void 0&&(o.userData.baseRoughness=o.roughness);const r=o.userData.baseRoughness;o.roughness=r*(1-n),o.needsUpdate=!0})}clearcoatUpgraded=!1;setClearcoat(t){const n=Math.max(0,Math.min(1,t));n>0&&!this.clearcoatUpgraded&&this.upgradeFinishToPhysical(),this.clearcoatUpgraded&&this.finishMaterials.forEach(o=>{const r=o;r.clearcoat=n,r.clearcoatRoughness=.1,r.needsUpdate=!0})}upgradeFinishToPhysical(){this.clearcoatUpgraded=!0;const t=new Map;for(const o of Array.from(new Set(this.finishMaterials))){const r=new Xt;r.color.copy(o.color),r.metalness=o.metalness,r.roughness=o.roughness,r.map=o.map,r.normalMap=o.normalMap,r.normalScale.copy(o.normalScale),r.roughnessMap=o.roughnessMap,r.metalnessMap=o.metalnessMap,r.aoMap=o.aoMap,r.aoMapIntensity=o.aoMapIntensity,r.emissive.copy(o.emissive),r.emissiveMap=o.emissiveMap,r.emissiveIntensity=o.emissiveIntensity,r.envMapIntensity=o.envMapIntensity,r.transparent=o.transparent,r.opacity=o.opacity,r.alphaTest=o.alphaTest,r.side=o.side,r.flatShading=o.flatShading,r.vertexColors=o.vertexColors,r.name=o.name,r.userData={...o.userData},r.clearcoat=0,t.set(o,r)}this.model.traverse(o=>{const r=o;if(r.isMesh)if(Array.isArray(r.material))r.material=r.material.map(a=>t.get(a)??a);else{const a=t.get(r.material);a&&(r.material=a)}});const n=o=>o.map(r=>t.get(r)??r);this.frame=n(this.frame),this.back=n(this.back),this.allBody=n(this.allBody)}environmentMap(){if(!this.envTexture){const t=new on(this.renderer);this.envTexture=t.fromScene(new Jt,.04).texture,t.dispose()}return this.envTexture}setClay(t){const n=un.find(r=>r.id===t),o=n?new k(n.color):new k(t);this.allBody.forEach(r=>{r.userData.clayStashedMap===void 0&&(r.userData.clayStashedMap=r.map),r.color=o.clone(),r.metalness=0,r.roughness=.85,r.map=null,r.needsUpdate=!0})}clayOff(){this.allBody.forEach(t=>{t.userData.clayStashedMap!==void 0&&(t.map=t.userData.clayStashedMap,t.userData.clayStashedMap=void 0,t.needsUpdate=!0)})}setReflections(t){this.scene.environment=t?this.environmentMap():null}setRoll(t){this.model.rotation.z=t*Math.PI/180}}const Ee=1.35;class it{materials=[];glassOverlays=[];screenMesh=null;flatPlane=null;texture=null;appliedBytes=null;flatScreenOn=!1;profile;constructor(t,n){this.profile=we(n);const o=this.profile.screen.material;t.traverse(r=>{const a=r;if(!a.isMesh)return;(Array.isArray(a.material)?a.material:[a.material]).forEach(l=>{const s=l;s&&s.name===o&&!this.materials.includes(s)&&(this.materials.push(s),this.screenMesh=a)})}),this.dimGlassOverlay(t),this.prepareScreen(),this.setGlare(null)}get found(){return this.materials.length>0}setGlare(t){this.materials.forEach(n=>{n.envMap=t,n.envMapIntensity=t?.6:0,n.metalness=t?.5:0,n.roughness=t?.15:1,n.needsUpdate=!0}),this.glassOverlays.forEach(n=>{n.opacity=t?.08:0,n.needsUpdate=!0})}setFlatScreen(t){if(this.flatScreenOn=t,!this.screenMesh)return;if(this.flatPlane){this.screenMesh.remove(this.flatPlane);const y=this.flatPlane.material;y.map?.dispose(),y.dispose(),this.flatPlane.geometry.dispose(),this.flatPlane=null}if(!t||!this.texture)return;this.screenMesh.geometry.computeBoundingBox();const n=this.screenMesh.geometry.boundingBox;if(!n)return;const o=n.max.x-n.min.x,r=n.max.y-n.min.y,a=n.max.z-n.min.z,i=(n.max.x+n.min.x)/2,l=(n.max.y+n.min.y)/2,s=(n.max.z+n.min.z)/2,d=r<=o&&r<=a?"y":a<=o&&a<=r?"z":"x",u=this.texture.clone();u.needsUpdate=!0,u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V;const{coverFill:p,coverCornerRatio:h,coverRepeat:f}=this.profile.screen;u.repeat.set(f.x,f.y);const c=d==="x"?a:o,g=d==="z"||d==="x"?r:a,m=Math.min(c,g)*p*h,b=In(c*p,g*p,m),S=new an(b,new Zt({map:u,emissive:new k(16777215),emissiveMap:u,emissiveIntensity:Ee,color:new k(0),toneMapped:!1,side:sn,depthTest:!0,depthWrite:!0,polygonOffset:!0,polygonOffsetFactor:-4,polygonOffsetUnits:-4}));d==="y"?(S.rotation.x=-Math.PI/2,S.position.set(i,n.max.y,s)):d==="x"?(S.rotation.y=Math.PI/2,S.position.set(n.max.x,l,s)):S.position.set(i,l,n.max.z),S.renderOrder=999,this.screenMesh.add(S),this.flatPlane=S}restoreScreen(){this.prepareScreen()}prepareScreen(){this.materials.forEach(t=>{t.color=new k(0),t.emissive=new k(16777215),t.emissiveIntensity=1,t.roughness=1,t.metalness=0,t.needsUpdate=!0})}dimGlassOverlay(t){t.traverse(n=>{const o=n;if(!o.isMesh)return;(Array.isArray(o.material)?o.material:[o.material]).forEach(a=>{const i=a;i&&i.name!==this.profile.screen.material&&i.transparent&&i.opacity>.2&&this.glassOverlays.push(i)})})}applyWallpaper(t){if(this.appliedBytes===t&&this.texture)return this.lightScreen(this.texture),Promise.resolve();const n=new Blob([t],{type:"image/png"}),o=URL.createObjectURL(n),r=this.texture;return new Promise((a,i)=>{const l=new Image;l.onload=()=>{try{const s=document.createElement("canvas");s.width=l.naturalWidth,s.height=l.naturalHeight;const d=s.getContext("2d");if(!d)throw new Error("2D context unavailable for wallpaper blit");d.drawImage(l,0,0);const u=new Qt(s);u.colorSpace=Oe,u.flipY=!1,u.center.set(.5,.5),u.wrapS=V,u.wrapT=V,u.repeat.set(this.profile.screen.repeat.x,this.profile.screen.repeat.y),u.needsUpdate=!0,this.texture=u,this.appliedBytes=t,this.lightScreen(u),this.flatScreenOn&&this.setFlatScreen(!0),r?.dispose(),URL.revokeObjectURL(o),a()}catch(s){URL.revokeObjectURL(o),i(s)}},l.onerror=()=>{URL.revokeObjectURL(o),i(new Error("wallpaper image failed to decode"))},l.src=o})}lightScreen(t){this.materials.forEach(n=>{n.map=t,n.emissiveMap=t,n.emissive=new k(16777215),n.emissiveIntensity=Ee,n.color=new k(0),n.needsUpdate=!0})}}function In(e,t,n){const o=e/2,r=t/2,a=Math.min(n,o,r),i=new en;i.moveTo(-o+a,-r),i.lineTo(o-a,-r),i.quadraticCurveTo(o,-r,o,-r+a),i.lineTo(o,r-a),i.quadraticCurveTo(o,r,o-a,r),i.lineTo(-o+a,r),i.quadraticCurveTo(-o,r,-o,r-a),i.lineTo(-o,-r+a),i.quadraticCurveTo(-o,-r,-o+a,-r);const l=new tn(i,16),s=l.attributes.position,d=new Float32Array(s.count*2);for(let u=0;u<s.count;u++)d[u*2]=(s.getX(u)+o)/e,d[u*2+1]=(s.getY(u)+r)/t;return l.setAttribute("uv",new ln(d,2)),l}const Me={sizePt:{min:20,max:120},maxWidth:{min:.1,max:1},lineHeight:{min:.5,max:3},bandInset:{min:0,max:.5},bandCenter:{min:0,max:1},reserveLines:{min:1,max:8}},X=[{id:"inter",family:"Inter",label:"Inter"},{id:"manrope",family:"Manrope",label:"Manrope"},{id:"poppins",family:"Poppins",label:"Poppins"},{id:"fraunces",family:"Fraunces",label:"Fraunces"},{id:"space-grotesk",family:"Space Grotesk",label:"Space Grotesk"}],Le=X.map(e=>e.id);[Le[0],...Le.slice(1)];const J=600;function st(e){return X.find(t=>t.id===e)??X[0]}const lt=[{id:"standard",label:"Standard",blurb:"Headline on top, whole device below it, nothing cropped. The safe default.",character:"safe",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"18",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:2}},{id:"bleed",label:"Bleed",blurb:"Room for a longer headline, device running off the bottom edge. What the market looks like.",character:"conventional",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4}},{id:"top-bleed",label:"Top bleed",blurb:"Device running off the TOP edge, headline underneath it. Inverts the usual reading order.",character:"expressive",shotLook:{phoneHeight:"82",hOffset:"0",vOffset:"-38",angle:"front",cameraPos:null,roll:"0"},caption:{sizePt:32,maxWidth:.86,reserveLines:4,band:"bottom"}}],ct=lt,Fe=ct.map(e=>e.id);[Fe[0],...Fe.slice(1)];Array.from(new Set(lt.flatMap(e=>Object.keys(e.caption))));const dt=[{id:"noir",label:"Noir",blurb:"Near-black, high contrast, glossy silver device. The default premium look.",background:{gradientFrom:"#1C1C22",gradientTo:"#08080B",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"silver",finish:"0",clearcoat:"1",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"inter",color:"FFFFFF"}},{id:"daylight",label:"Daylight",blurb:"Warm off-white, soft matte white device, dark text. Calm and editorial.",background:{gradientFrom:"#F4F1EA",gradientTo:"#DFD8CA",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"white",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"front"},caption:{fontId:"manrope",color:"1A1A1F"}},{id:"pop",label:"Pop",blurb:"Saturated indigo-to-violet, orange device. Loud, consumer, scroll-stopping.",background:{gradientFrom:"#5B6CF0",gradientTo:"#9B4DF0",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"real",colorway:"orange",finish:"0",clearcoat:"0.4",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"poppins",color:"FFFFFF"}},{id:"clay",label:"Clay",blurb:"Warm terracotta, matte charcoal device, tilted. Reads like a product photo.",background:{gradientFrom:"#E9BBAC",gradientTo:"#C88C7C",gradientDir:"vertical",shadow:!0,floorReflection:!1},shotLook:{material:"clay",clayTone:"charcoal",finish:"0",clearcoat:"0",reflections:!1,lighting:!0,angle:"left",roll:"-6"},caption:{fontId:"fraunces",color:"2A2320"}},{id:"studio",label:"Studio",blurb:"Mid slate with a floor reflection, blue device. Technical and precise.",background:{gradientFrom:"#5A6678",gradientTo:"#2B3340",gradientDir:"vertical",shadow:!0,floorReflection:!0},shotLook:{material:"real",colorway:"blue",finish:"0",clearcoat:"0",reflections:!0,lighting:!0,angle:"front"},caption:{fontId:"space-grotesk",color:"FFFFFF"}}],Re=dt.map(e=>e.id);[Re[0],...Re.slice(1)];const x={angle:"front",cameraPos:null,roll:"0",phoneHeight:"82",hOffset:"0",vOffset:"18",material:"real",colorway:"silver",customColor:"F5F5F5",finish:"0",clearcoat:"0",clayTone:"grey",clayCustom:"B0B0B0",flatScreen:!1,glare:!1,lighting:!0,reflections:!1},_e={angle:{valueKind:"angle",required:!0,defaultSource:"default-look",defaultValue:x.angle,persistence:"always"},cameraPos:{valueKind:"camera-position",required:!0,defaultSource:"default-look",defaultValue:x.cameraPos,persistence:"always"},roll:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.roll,persistence:"always"},phoneHeight:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.phoneHeight,persistence:"always"},hOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.hOffset,persistence:"always"},vOffset:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.vOffset,persistence:"always"},material:{valueKind:"material",required:!0,defaultSource:"default-look",defaultValue:x.material,persistence:"always"},colorway:{valueKind:"colorway",required:!0,defaultSource:"default-look",defaultValue:x.colorway,persistence:"always"},customColor:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.customColor,persistence:"always"},finish:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.finish,persistence:"always"},clearcoat:{valueKind:"numeric-string",required:!0,defaultSource:"default-look",defaultValue:x.clearcoat,persistence:"always"},clayTone:{valueKind:"clay-tone",required:!0,defaultSource:"default-look",defaultValue:x.clayTone,persistence:"always"},clayCustom:{valueKind:"string",required:!0,defaultSource:"default-look",defaultValue:x.clayCustom,persistence:"always"},flatScreen:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.flatScreen,persistence:"always"},clipToFrame:{valueKind:"boolean",required:!1,defaultSource:"explicit",defaultValue:!1,persistence:"omit-default"},glare:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.glare,persistence:"always"},lighting:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.lighting,persistence:"always"},reflections:{valueKind:"boolean",required:!0,defaultSource:"default-look",defaultValue:x.reflections,persistence:"always"}},xn={roll:{min:-45,max:45},phoneHeight:{min:20,max:200},hOffset:{min:-100,max:100},vOffset:{min:-200,max:200},finish:{min:0,max:1},clearcoat:{min:0,max:1}},H={x:.5,y:.06},ve={fontId:"inter",sizePt:32,color:"FFFFFF",align:"center",anchor:H,maxWidth:.86},Ne=[{id:"r69",label:"6.9″ iPhone · 1290×2796",width:1290,height:2796},{id:"r65",label:"6.5″ iPhone · 1242×2688",width:1242,height:2688},{id:"r55",label:"5.5″ iPhone · 1242×2208",width:1242,height:2208},{id:"pixel69",label:"Pixel 10 Pro · 1290×2796",width:1290,height:2796},{id:"ipad13",label:"13″ iPad · 2064×2752",width:2064,height:2752},{id:"ipad129",label:"12.9″ iPad · 2048×2732",width:2048,height:2732}];function G(e){return Ne.find(t=>t.id===e)??Ne[0]}const te=932,pe="iphone-17-pro",Pn="pixel-10-pro",Ie=[{id:"iphone-6-9",label:"iPhone 17 Pro",panelPresetId:"r69",ascDeviceSet:"APP_IPHONE_67",modelId:pe,screenFamily:"iphone"},{id:"iphone-6-5",label:"iPhone 17",panelPresetId:"r65",ascDeviceSet:"APP_IPHONE_65",modelId:pe,screenFamily:"iphone"},{id:"pixel-10-pro",label:"Pixel 10 Pro",panelPresetId:"pixel69",ascDeviceSet:"ANDROID_PHONE",modelId:Pn,screenFamily:"android"},{id:"ipad-13",label:"iPad 13″",panelPresetId:"ipad13",ascDeviceSet:"APP_IPAD_PRO_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0},{id:"ipad-12-9",label:"iPad 12.9″",panelPresetId:"ipad129",ascDeviceSet:"APP_IPAD_PRO_3GEN_129",modelId:"ipad-pro",screenFamily:"ipad",comingSoon:!0}];function An(e){return Ie.find(t=>t.panelPresetId===e)?.modelId??pe}const ut="iphone-6-9";function ft(e){return Ie.find(t=>t.id===e)}function On(){return Ie.filter(e=>!e.comingSoon)}function ht(e,t){if(Array.isArray(e)){const n=new Set,o=[];for(const r of e){const a=ft(r);a&&!a.comingSoon&&!n.has(r)&&(n.add(r),o.push(r))}if(o.length>0)return o}if(t){const n=On().find(o=>o.panelPresetId===t);if(n)return[n.id]}return[ut]}const pt=ft(ut)?.screenFamily??"iphone";function mt(e){const t={...e},n=t,o=Object.keys(_e);for(const r of o){const a=_e[r];a.persistence==="omit-default"&&(n[r]===void 0||n[r]===a.defaultValue)&&delete n[r]}return t}function gt(e){return mt({...x,...e})}function yt(e){return mt(gt(e))}function R(e,t){const{min:n,max:o}=xn[e],r=typeof t=="number"?t:Number.parseFloat(t??""),a=Number.isFinite(r)?r:Number.parseFloat(x[e]);return Math.max(n,Math.min(o,a))}const Tn=90,bt=180,kn="#1b1b2e",Cn="#0a0a14";function En(e){return e==="horizontal"?Tn:bt}function Mn(e){const t=(e%180+180)%180;return t>=45&&t<135?"horizontal":"vertical"}function St(e,t,n){return{stops:[{color:e,at:0},{color:t,at:1}],angle:En(n)}}const wt=e=>e*Math.PI/180;function Ln(e){const t=wt(e);return{dx:Math.sin(t),dy:-Math.cos(t)}}function Fn(e,t,n){const o=wt(n);return Math.abs(e*Math.sin(o))+Math.abs(t*Math.cos(o))}function Rn(e,t,n){const{dx:o,dy:r}=Ln(n),a=Fn(e,t,n)/2,i=e/2,l=t/2;return{x0:i-a*o,y0:l-a*r,x1:i+a*o,y1:l+a*r}}function Z(e){const t=e.stops[0],n=e.stops[e.stops.length-1];return{gradientFrom:t?.color??"#000000",gradientTo:n?.color??t?.color??"#000000",gradientDir:Mn(e.angle)}}function ne(e,t){if(!Array.isArray(e)||e.length===0)return null;const n=[];for(const r of e){if(!r||typeof r!="object")return null;const a=r;if(typeof a.color!="string"||typeof a.at!="number"||!Number.isFinite(a.at))return null;n.push({color:a.color,at:Math.min(1,Math.max(0,a.at))})}const o=typeof t=="number"&&Number.isFinite(t)?_n(t):bt;return{stops:n,angle:o}}function Q(e){return ne(e.gradientStops,e.gradientAngle)??St(e.gradientFrom,e.gradientTo,e.gradientDir)}function _n(e){return(e%360+360)%360}function Nn(e){return ne(e.stops,e.angle)??St(e.from,e.to,e.dir)}function vt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(!o||typeof o!="object")continue;const r=o;if(r.kind==="solid"&&typeof r.color=="string")t[n]={kind:"solid",color:r.color};else if(r.kind==="gradient"&&typeof r.from=="string"&&typeof r.to=="string"&&(r.dir==="horizontal"||r.dir==="vertical")){const a=ne(r.stops,r.angle);t[n]=a?{kind:"gradient",stops:a.stops,angle:a.angle,from:r.from,to:r.to,dir:r.dir}:{kind:"gradient",from:r.from,to:r.to,dir:r.dir}}}return t}function B(e){return!e||e.x===H.x&&e.y===H.y}function Bn(e){const{text:t,anchor:n,...o}=e;return B(n)?{...o}:{...o,anchor:n}}const Dn=.55,M="en-US";function zn(e){const t=e.lastIndexOf(":");if(t<=0)return null;const n=Number(e.slice(t+1));return!Number.isSafeInteger(n)||n<0?null:{panelId:e.slice(0,t),index:n}}function It(e,t,n,o){if(!e||typeof e!="object"||Array.isArray(e))return{};const r={};for(const[a,i]of Object.entries(e)){if(a===n||!t.includes(a)||!Array.isArray(i))continue;const l=Array.from(new Set(i.filter(s=>typeof s=="string"))).filter(s=>{const d=zn(s);return!!d&&!!o[n]?.[d.panelId]?.[d.index]&&!!o[a]?.[d.panelId]?.[d.index]});l.length>0&&(r[a]=l)}return r}function xt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e))!o||typeof o!="object"||Array.isArray(o)||(t[n]=D(o));return t}function jn(e){if(!e||typeof e!="object")return;const t=e,n=typeof t.x=="number"?t.x:H.x,o=typeof t.y=="number"?t.y:H.y;return B({x:n,y:o})?void 0:{x:n,y:o}}function D(e){const t=e&&typeof e=="object"?e:{},n=jn(t.anchor),o={...ve,...t};n?o.anchor=n:delete o.anchor,typeof o.reserveLines!="number"||!Number.isFinite(o.reserveLines)||o.reserveLines<Me.reserveLines.min?delete o.reserveLines:o.reserveLines=Math.floor(o.reserveLines),o.band!=="bottom"&&delete o.band;for(const r of["bandInset","lineHeight","bandCenter"]){const{min:a,max:i}=Me[r],l=o[r];(typeof l!="number"||!Number.isFinite(l)||l<a||l>i)&&delete o[r]}return o}function Hn(e){const t=e&&typeof e=="object"?e:{};return{...D(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,o]of Object.entries(e))Array.isArray(o)?o.length>0&&(t[n]=o.map(D)):o&&typeof o=="object"&&(t[n]=[D(o)]);return t}function Gn(e,t){const n=[{...D(t),text:e.headline}];return typeof e.subtitle=="string"&&e.subtitle.trim()&&n.push({...D({...t,sizePt:t.sizePt*Dn}),text:e.subtitle}),n}function re(e,t,n=M){if(!e||typeof e!="object"||Array.isArray(e))return{};const o=xt(t),r=d=>o[d]??ve,a=d=>{if(!d||typeof d!="object"||Array.isArray(d))return{};const u={};for(const[p,h]of Object.entries(d))Array.isArray(h)?h.length>0&&(u[p]=h.map(Hn)):h&&typeof h=="object"&&typeof h.headline=="string"&&(u[p]=Gn(h,r(p)));return u},i=Object.entries(e);if(i.some(([,d])=>!!d&&typeof d=="object"&&typeof d.headline=="string"))return{[n]:a(e)};const s={};for(const[d,u]of i)s[d]=a(u);return s}function At(e,t,n=M){return e[t??n]??e[n]??{}}function Ot(e,t=M){const n={},o=Object.keys(e),r=o.includes(t)?[t,...o.filter(a=>a!==t)]:o;for(const a of r){const i=e[a];if(i)for(const[l,s]of Object.entries(i))n[l]===void 0&&Array.isArray(s)&&s.length>0&&(n[l]=s.map(Bn))}return n}function me(e,t=M,n=pt){const o={};return e.family!==void 0&&e.family!==n&&(o.family=e.family),e.locale!==void 0&&e.locale!==t&&(o.locale=e.locale),o}function Be(e){const t=[];return e.family!==void 0&&t.push(`family=${e.family}`),e.locale!==void 0&&t.push(`locale=${e.locale}`),t.join("&")}function ge(e){const t=[];return e.family!==void 0&&t.push("family"),e.locale!==void 0&&t.push("locale"),t}const Un={order:["family","locale"],reach:"strict"};function $n(e,t){const n=ge(e),o=t.order.filter(a=>n.includes(a));return Array.from({length:o.length+1},(a,i)=>o.slice(0,o.length-i)).map(a=>{const i={};return a.includes("family")&&(i.family=e.family),a.includes("locale")&&(i.locale=e.locale),i})}function Wn(e,t,n,o,r=M){const a=me(n,r);for(const i of $n(a,o)){const l=e(t,i);if(l!==void 0){const s=ge(i);return{value:l,resolvedAt:i,inherited:ge(a).filter(d=>!s.includes(d))}}}return{value:void 0,resolvedAt:null,inherited:[]}}function ye(e,t,n=M){const o=r=>{const a=new Set,i=[];for(const l of r)typeof l=="string"&&l&&!a.has(l)&&(a.add(l),i.push(l));return i};if(Array.isArray(t)){const r=o(t);if(r.length>0)return r.includes(n)?r:[...r,n]}return o([n,...Object.keys(e)])}function xe(e,t=M){return typeof e=="string"&&e.length>0?e:t}const z={"it-IT":"it"};function De(e,t){if(!(Object.keys(e).some(r=>r in z)||(t?.some(r=>r in z)??!1)))return{captionText:e,locales:t};const o={};for(const[r,a]of Object.entries(e))r in z||(o[r]=a);for(const[r,a]of Object.entries(e)){const i=z[r];i&&(o[i]={...a,...o[i]??{}})}return{captionText:o,locales:t?.map(r=>z[r]??r)}}function Tt(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[n,o]of Object.entries(e)){if(n===pt||!o||typeof o!="object"||Array.isArray(o))continue;const r={};for(const[a,i]of Object.entries(o)){if(!i||typeof i!="object"||Array.isArray(i))continue;const l={};for(const[s,d]of Object.entries(i))Array.isArray(d)&&d.every(u=>typeof u=="string")&&(l[s]=d);Object.keys(l).length>0&&(r[a]=l)}Object.keys(r).length>0&&(t[n]=r)}return t}function U(e,t){return t.flatMap(n=>e.filter(o=>o.panelId===n.id))}let ze=0,je=0;function Vn(e,t){const n=(o,r)=>o.reduce((a,i)=>{const l=i.startsWith(r)?Number.parseInt(i.slice(r.length),10):NaN;return Number.isFinite(l)&&l>a?l:a},0);ze=Math.max(ze,n(e,"shot-")),je=Math.max(je,n(t,"panel-"))}const Kn=.49;function qn(e,t,n){return e.map(o=>{const r=n*o.heightFrac,a=o.height>0?o.width*(r/o.height):r*Kn,l=o.panelIndex*t+t/2-a/2+o.hOffset*(t/2),d=(n-r)/2+o.vOffset*(n/2);return{shotId:o.shotId,bytes:o.bytes,x:Math.round(l),y:Math.round(d),width:Math.round(a),height:Math.round(r),panelIndex:o.panelIndex,...o.clipToFrame===!0?{clipToFrame:!0}:{}}})}function Yn(e,t){const n=t.panelBackgrounds??{},o=Q(t),r=o.stops[0]?.color??t.gradientFrom;if(!e.some(s=>n[s.id]))return t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"perPanel",colors:e.map(s=>t.panelColors[s.id]??r)};const i=s=>t.bgMode==="gradient"?{kind:"gradient",gradient:o}:{kind:"solid",color:t.panelColors[s]??r};return{kind:"perPanelFills",fills:e.map(s=>{const d=n[s.id];return d?d.kind==="solid"?{kind:"solid",color:d.color}:{kind:"gradient",gradient:Nn(d)}:i(s.id)})}}function kt(e,t,n=M){const o=Q(e);return{panelPresetId:e.panelPresetId,bgMode:e.bgMode,gradientStops:o.stops,gradientAngle:o.angle,...Z(o),panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection??!1,panelBackgrounds:e.panelBackgrounds??{},captionStyles:{...Pt(e.captionStyles),...Ot(t,n)}}}function Xn(e){const t=e.caption.reserveLines,n=typeof t=="number"?` Reserves ${t} headline lines (~${t*18} characters).`:"";return`${e.blurb}${n} Character: [${e.character}].`}function Jn(e,t){return Math.max(0,...Object.values(e).map(n=>n[t]?.length??0))}function Ct(e,t,n){const o=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=new Set,a={};let i=1;const l=()=>{for(;r.has(`caption-${i}`);)i+=1;const s=`caption-${i}`;return r.add(s),i+=1,s};for(const s of n){const d=Jn(t,s);if(d===0)continue;const u=Array.isArray(o[s])?o[s]:[];a[s]=Array.from({length:d},(p,h)=>{const f=u[h];return typeof f=="string"&&f.length>0&&!r.has(f)?(r.add(f),f):l()})}return a}const Et=2;function Zn(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e;return t.schema===Et&&Array.isArray(t.shots)&&Array.isArray(t.panels)&&t.panels.length>0}function Qn(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Zn(t))return null;const n=t,o=new Set(n.panels.map(c=>c.id)),r=n.shots.filter(c=>o.has(c.panelId)).map(c=>({id:c.id,panelId:c.panelId,frameNodeId:c.frameNodeId,frameName:c.frameName,bytes:null,look:gt(c.look),thumb:null,thumbStale:!0,inputAnchor:c.inputAnchor,outputAnchor:c.outputAnchor})),a=n.panels.map(c=>({id:c.id}));Vn(r.map(c=>c.id),a.map(c=>c.id));const i=n.selectedShotId&&r.some(c=>c.id===n.selectedShotId)?n.selectedShotId:r[0]?.id??null,l=xe(n.baseLocale),{captionText:s,locales:d}=De(re(n.captionText,n.captionStyles,l),n.locales),u=De({[l]:{}},[l]).locales?.[0]??l,p=ye(s,d,u),h=Ct(n.captionSlotIds,s,a.map(c=>c.id)),f=ne(n.gradientStops,n.gradientAngle);return{shots:U(r,a),panels:a,selectedShotId:i,panelPresetId:G(n.panelPresetId).id,bgMode:n.bgMode==="perPanel"?"perPanel":"gradient",...f?{gradientStops:f.stops,gradientAngle:f.angle,...Z(f)}:{gradientFrom:n.gradientFrom??"5B6CF0",gradientTo:n.gradientTo??"9B4DF0",gradientDir:n.gradientDir==="vertical"?"vertical":"horizontal"},panelColors:n.panelColors??{},shadow:n.shadow!==!1,floorReflection:n.floorReflection===!0,panelBackgrounds:vt(n.panelBackgrounds),captionText:s,locales:p,baseLocale:u,staleCaptionTranslations:It(n.staleCaptionTranslations,p,u,s),outputs:ht(n.outputs,n.panelPresetId),localeScreens:{},outputCaptions:Tt(n.outputCaptions),captionSlotIds:h,frameTemplates:n.frameTemplates&&typeof n.frameTemplates=="object"&&!Array.isArray(n.frameTemplates)?n.frameTemplates:{}}}function Mt(e){const n=U(e.shots,e.panels).map(a=>({id:a.id,panelId:a.panelId,frameNodeId:a.frameNodeId,frameName:a.frameName,look:yt(a.look),inputAnchor:a.inputAnchor,outputAnchor:a.outputAnchor})),o=xe(e.baseLocale),r=re(e.captionText,e.captionStyles,o);return{schema:Et,shots:n,panels:e.panels,selectedShotId:e.selectedShotId,captionText:r,locales:ye(r,e.locales,o),baseLocale:o,staleCaptionTranslations:It(e.staleCaptionTranslations,ye(r,e.locales,o),o,r),outputs:ht(e.outputs,e.panelPresetId),outputCaptions:Tt(e.outputCaptions),...(()=>{const a=Ct(e.captionSlotIds,r,e.panels.map(i=>i.id));return Object.keys(a).length>0?{captionSlotIds:a}:{}})(),...e.frameTemplates&&Object.keys(e.frameTemplates).length>0?{frameTemplates:e.frameTemplates}:{},...kt(e,r,o)}}const er=1;function tr(e){const t=U(e.shots,e.panels),n=re(e.captionText,e.captionStyles),o=xe(e.baseLocale);return{schema:er,shots:t.map(r=>({panelId:r.panelId,look:yt(r.look)})),...kt(e,n,o)}}dt.map(e=>({id:e.id,label:e.label,description:e.blurb}));ct.map(e=>({id:e.id,label:e.label,description:Xn(e),character:e.character}));const Lt=[{code:"ar-SA",name:"Arabic",script:"rtl",dir:"rtl"},{code:"bn-BD",name:"Bengali",script:"indic",dir:"ltr"},{code:"ca",name:"Catalan",script:"latin",dir:"ltr"},{code:"cs",name:"Czech",script:"latin",dir:"ltr"},{code:"da",name:"Danish",script:"latin",dir:"ltr"},{code:"de-DE",name:"German",script:"latin",dir:"ltr"},{code:"el",name:"Greek",script:"greek",dir:"ltr"},{code:"en-AU",name:"English (Australia)",script:"latin",dir:"ltr"},{code:"en-CA",name:"English (Canada)",script:"latin",dir:"ltr"},{code:"en-GB",name:"English (U.K.)",script:"latin",dir:"ltr"},{code:"en-US",name:"English (U.S.)",script:"latin",dir:"ltr"},{code:"es-ES",name:"Spanish (Spain)",script:"latin",dir:"ltr"},{code:"es-MX",name:"Spanish (Mexico)",script:"latin",dir:"ltr"},{code:"fi",name:"Finnish",script:"latin",dir:"ltr"},{code:"fr-CA",name:"French (Canada)",script:"latin",dir:"ltr"},{code:"fr-FR",name:"French",script:"latin",dir:"ltr"},{code:"gu-IN",name:"Gujarati",script:"indic",dir:"ltr"},{code:"he",name:"Hebrew",script:"rtl",dir:"rtl"},{code:"hi",name:"Hindi",script:"indic",dir:"ltr"},{code:"hr",name:"Croatian",script:"latin",dir:"ltr"},{code:"hu",name:"Hungarian",script:"latin",dir:"ltr"},{code:"id",name:"Indonesian",script:"latin",dir:"ltr"},{code:"it",name:"Italian",script:"latin",dir:"ltr"},{code:"ja",name:"Japanese",script:"cjk",dir:"ltr"},{code:"kn-IN",name:"Kannada",script:"indic",dir:"ltr"},{code:"ko",name:"Korean",script:"cjk",dir:"ltr"},{code:"ml-IN",name:"Malayalam",script:"indic",dir:"ltr"},{code:"mr-IN",name:"Marathi",script:"indic",dir:"ltr"},{code:"ms",name:"Malay",script:"latin",dir:"ltr"},{code:"nl-NL",name:"Dutch",script:"latin",dir:"ltr"},{code:"no",name:"Norwegian",script:"latin",dir:"ltr"},{code:"or-IN",name:"Odia",script:"indic",dir:"ltr"},{code:"pa-IN",name:"Punjabi",script:"indic",dir:"ltr"},{code:"pl",name:"Polish",script:"latin",dir:"ltr"},{code:"pt-BR",name:"Portuguese (Brazil)",script:"latin",dir:"ltr"},{code:"pt-PT",name:"Portuguese (Portugal)",script:"latin",dir:"ltr"},{code:"ro",name:"Romanian",script:"latin",dir:"ltr"},{code:"ru",name:"Russian",script:"cyrillic",dir:"ltr"},{code:"sk",name:"Slovak",script:"latin",dir:"ltr"},{code:"sl-SI",name:"Slovenian",script:"latin",dir:"ltr"},{code:"sv",name:"Swedish",script:"latin",dir:"ltr"},{code:"ta-IN",name:"Tamil",script:"indic",dir:"ltr"},{code:"te-IN",name:"Telugu",script:"indic",dir:"ltr"},{code:"th",name:"Thai",script:"thai",dir:"ltr"},{code:"tr",name:"Turkish",script:"latin",dir:"ltr"},{code:"uk",name:"Ukrainian",script:"cyrillic",dir:"ltr"},{code:"ur-PK",name:"Urdu",script:"rtl",dir:"rtl"},{code:"vi",name:"Vietnamese",script:"latin",dir:"ltr"},{code:"zh-Hans",name:"Chinese (Simplified)",script:"cjk",dir:"ltr"},{code:"zh-Hant",name:"Chinese (Traditional)",script:"cjk",dir:"ltr"}];Lt.map(e=>e.code);Object.fromEntries(Lt.map(e=>[e.code,e]));({...ve});function nr(e,t){t.material==="clay"?(e.setClay(t.clayTone==="custom"?`#${t.clayCustom}`:t.clayTone),e.setClearcoat(0)):(e.clayOff(),t.colorway==="custom"?e.setCustomColor(`#${t.customColor}`):e.setColorway(t.colorway),e.setFinish(R("finish",t.finish)),e.setClearcoat(R("clearcoat",t.clearcoat)))}function rr(e,t,n){e.screen.setGlare(!n&&t?e.device.environmentMap():null)}async function or(e,t,n,o){const{stage:r,device:a,screen:i}=e,l=t.material==="clay";if(t.cameraPos?r.setCameraPosition(t.cameraPos):r.setAngle(t.angle),a.setRoll(R("roll",t.roll)),nr(a,t),i.restoreScreen(),r.setStudioLighting(t.lighting),a.setReflections(t.reflections),i.setFlatScreen(t.flatScreen),n){try{await i.applyWallpaper(n)}catch{}if(!o())return}rr(e,t.glare,l)}function Ft(){const e=new Qe({antialias:!0,preserveDrawingBuffer:!0,alpha:!0});return e.setPixelRatio(1),e.shadowMap.enabled=!0,e}async function ar(e,t,n,o={}){const r=!o.renderer,a=o.renderer??Ft();a.setSize(t,n,!1);let i;o.cameraPosition?(i=new et(nt,t/n,.01,100),i.position.copy(o.cameraPosition),i.lookAt(0,0,0)):(i=e.camera.clone(),i.aspect=t/n,i.updateProjectionMatrix());try{const l=e.scene.background;e.scene.background=null;try{a.render(e.scene,i)}finally{e.scene.background=l}const s=document.createElement("canvas");let d=null;try{s.width=t,s.height=n,s.getContext("2d")?.drawImage(a.domElement,0,0);const u=ir(s);d=u;const p=u.width,h=u.height;return{bytes:await new Promise((c,g)=>{u.toBlob(m=>{if(!m){g(new Error("toBlob returned null"));return}m.arrayBuffer().then(b=>c(new Uint8Array(b)),g)},"image/png")}),width:p,height:h}}finally{d&&d!==s&&(d.width=0,d.height=0),s.width=0,s.height=0}}finally{r&&a.dispose()}}function ir(e,t=2){const n=e.getContext("2d",{willReadFrequently:!0});if(!n)return e;const{width:o,height:r}=e,a=n.getImageData(0,0,o,r).data;let i=o,l=r,s=-1,d=-1;for(let f=0;f<r;f++)for(let c=0;c<o;c++)a[(f*o+c)*4+3]!==0&&(c<i&&(i=c),c>s&&(s=c),f<l&&(l=f),f>d&&(d=f));if(s<i||d<l)return e;i=Math.max(0,i-t),l=Math.max(0,l-t),s=Math.min(o-1,s+t),d=Math.min(r-1,d+t);const u=s-i+1,p=d-l+1,h=document.createElement("canvas");return h.width=u,h.height=p,h.getContext("2d")?.drawImage(e,i,l,u,p,0,0,u,p),h}const sr=1900,lr=2800;function cr(e,t,n){return{shots:e,panels:t,selectedShotId:n.selectedShotId,panelPresetId:n.panelPresetId,bgMode:n.bgMode,gradientStops:n.gradientStops,gradientAngle:n.gradientAngle,gradientFrom:n.gradientFrom,gradientTo:n.gradientTo,gradientDir:n.gradientDir,panelColors:n.panelColors,shadow:n.shadow,floorReflection:n.floorReflection,panelBackgrounds:n.panelBackgrounds,captionStyles:n.captionStyles,captionText:n.captionText,locales:n.locales,baseLocale:n.baseLocale,outputs:n.outputs}}async function dr(e,t,n,o,r,a){const i=U(n,o),l=i.filter(c=>c.bytes);if(l.length===0)return null;const s=G(r.panelPresetId),d=new Map(o.map((c,g)=>[c.id,g])),u=r.renderScale!=null&&r.renderScale>0&&r.renderScale<=1?r.renderScale:1,p=Math.max(1,Math.round(sr*u)),h=Math.max(1,Math.round(lr*u)),f=Ft();try{const c=[];for(let S=0;S<l.length;S++){const y=l[S];a(`Rendering ${S+1}/${l.length}${r.locale?` (${r.locale})`:""}…`),await or(t,y.look,y.bytes,()=>!0);const P=await ar(e,p,h,{renderer:f,cameraPosition:bn(y.look.angle,y.look.cameraPos)});c.push({shotId:y.id,bytes:P.bytes,width:P.width,height:P.height,heightFrac:R("phoneHeight",y.look.phoneHeight)/100,hOffset:R("hOffset",y.look.hOffset)/100,vOffset:R("vOffset",y.look.vOffset)/100,panelIndex:d.get(y.panelId)??0,...y.look.clipToFrame===!0?{clipToFrame:!0}:{}})}const g=qn(c,s.width,s.height),m=Yn(o,r);a("Saving project…");const b=Mt(cr(i,o,r));return{panelWidth:s.width,panelHeight:s.height,count:o.length,background:m,phones:g,shadow:r.shadow,floorReflection:r.floorReflection??!1,project:JSON.stringify(b)}}finally{f.dispose(),f.forceContextLoss()}}function ee(e){return/^[0-9a-fA-F]{3,8}$/.test(e)?`#${e}`:e}function be(e,t,n,o){const{x0:r,y0:a,x1:i,y1:l}=Rn(n,o,t.angle),s=e.createLinearGradient(r,a,i,l);for(const d of t.stops)s.addColorStop(Math.min(1,Math.max(0,d.at)),ee(d.color));return s}function He(e,t,n,o,r,a,i){const l=document.createElement("canvas");l.width=n,l.height=o;try{const s=l.getContext("2d");if(!s)return;s.fillStyle=be(s,t,n,o),s.fillRect(r,0,a,o),e.drawImage(l,r,0,a,o,i,0,a,o)}finally{l.width=0,l.height=0}}function Ge(e){return URL.createObjectURL(new Blob([e],{type:"image/png"}))}function Ue(e){return new Promise((t,n)=>{const o=new Image;o.onload=()=>t(o),o.onerror=n,o.src=e})}const ur=16*1024*1024;function $e(e){const t=(e.img.naturalWidth||0)*(e.img.naturalHeight||0),n=e.reflection?e.reflection.canvas.width*e.reflection.canvas.height:0;return t+n}function Rt(){let e=0;const t=Array.from(F.values());for(const r of t)e+=$e(r);const n=Array.from(F.keys());let o=0;for(;e>ur&&o<n.length-1;){const r=F.get(n[o]);r&&(e-=$e(r),r.reflection&&(r.reflection.canvas.width=0,r.reflection.canvas.height=0),F.delete(n[o])),o++}}const F=new Map,ie=new Map;async function fr(e,t){if(!t){const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}}const n=F.get(e);if(n)return F.delete(e),F.set(e,n),n;const o=ie.get(e);if(o)return o;const r=(async()=>{const a=Ge(e);try{return{img:await Ue(a)}}finally{URL.revokeObjectURL(a)}})();ie.set(e,r);try{const a=await r;return F.set(e,a),Rt(),a}finally{ie.delete(e)}}async function hr(e,t,n){const o=t.panelWidth*t.count,r=t.panelHeight,a=n?.backgroundViewport,i=a?t.panelWidth*a.panelCount:o,l=a?t.panelWidth*a.panelIndex:0;e.width=o,e.height=r;const s=e.getContext("2d");if(!s)return;n?.transparentBg?s.clearRect(0,0,o,r):t.background.kind==="gradient"?a?He(s,t.background.gradient,i,r,l,o,0):(s.fillStyle=be(s,t.background.gradient,o,r),s.fillRect(0,0,o,r)):t.background.kind==="perPanel"?t.background.colors.forEach((h,f)=>{s.fillStyle=ee(h),s.fillRect(f*t.panelWidth,0,t.panelWidth,r)}):t.background.fills.forEach((h,f)=>{const c=f*t.panelWidth;h.kind==="solid"?(s.fillStyle=ee(h.color),s.fillRect(c,0,t.panelWidth,r)):a?He(s,h.gradient,i,r,l+c,t.panelWidth,c):(s.fillStyle=be(s,h.gradient,o,r),s.fillRect(c,0,t.panelWidth,r))});const d=n?.phoneRange?Math.max(0,n.phoneRange.start):0,u=n?.phoneRange?Math.min(t.phones.length,n.phoneRange.end):t.phones.length,p=n?.reuseDecodedPhones===!0;for(let h=d;h<u;h++){const f=t.phones[h],c=n?.clip==="panel"||n?.clip===void 0&&f.clipToFrame===!0;if(n?.skipShotId&&f.shotId===n.skipShotId)continue;const g=await fr(f.bytes,p),m=g.img;c&&(s.save(),s.beginPath(),s.rect(f.panelIndex*t.panelWidth,0,t.panelWidth,r),s.clip()),t.floorReflection&&pr(s,g,f,p),s.save(),t.shadow&&(s.shadowColor="rgba(0,0,0,0.35)",s.shadowBlur=t.panelWidth*.03,s.shadowOffsetY=t.panelHeight*.012),s.drawImage(m,f.x,f.y,f.width,f.height),s.restore(),c&&s.restore()}}function pr(e,t,n,o){const r=Math.round(n.height*.45);if(r<=0||n.width<=0)return;const a=o?t.reflection:void 0;if(a&&a.width===n.width&&a.height===n.height){e.drawImage(a.canvas,n.x,n.y+n.height,n.width,r);return}const i=t.img,l=document.createElement("canvas");l.width=Math.max(1,n.width),l.height=Math.max(1,r);const s=l.getContext("2d");if(!s)return;s.save(),s.translate(0,r),s.scale(1,-1),s.drawImage(i,0,r-n.height,n.width,n.height),s.restore();const d=s.createLinearGradient(0,0,0,r);d.addColorStop(0,"rgba(0,0,0,0.34)"),d.addColorStop(1,"rgba(0,0,0,0)"),s.globalCompositeOperation="destination-in",s.fillStyle=d,s.fillRect(0,0,n.width,r),e.drawImage(l,n.x,n.y+n.height,n.width,r),o&&(t.reflection={width:n.width,height:n.height,canvas:l},Rt())}const _t=1.2,mr=.06,gr=.5;function yr(e,t,n){if(!e)return[t];const o=t.split(/\s+/).filter(Boolean),r=[];let a="";for(const i of o){const l=a?`${a} ${i}`:i;a&&e.measureText(l).width>n?(r.push(a),a=i):a=l}return a&&r.push(a),r}function br(e,t){if(!e)return 0;let n=0;for(const o of t)n=Math.max(n,e.measureText(o).width);return n}function Nt(e,t,n,o){const r=t.map((c,g)=>{const m=c.text.trim();if(!m)return null;const b=c.sizePt*(o/te),S=st(c.fontId).family;e&&(e.font=`${J} ${b}px "${S}"`);const y=yr(e,m,c.maxWidth*n),P=b*(c.lineHeight??_t),A=y.length*P,v=Math.max(A,(c.reserveLines??y.length)*P);return{layer:c,index:g,lines:y,px:b,lineH:P,width:br(e,y),blockH:A,reservedH:v}}),a=r.filter(c=>c!==null&&B(c.layer.anchor)),i=a.filter(c=>c.layer.band==="bottom"),l=a.filter(c=>c.layer.band!=="bottom"),s=c=>c[0]?.layer.bandInset??mr,d=(1-s(i))*o-i.reduce((c,g)=>c+g.reservedH,0);let u=s(l)*o,p=d,h=0;const f=[];for(const c of r){if(!c)continue;if(!B(c.layer.anchor)){f.push({layer:c.layer,index:c.index,x:c.layer.anchor.x*n,yTop:c.layer.anchor.y*o,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width});continue}const g=c.layer.band==="bottom",b=(g?p:u)+(c.reservedH-c.blockH)/2,S=(c.layer.bandCenter??gr)*n;f.push({layer:c.layer,index:c.index,x:S,yTop:b,lines:c.lines,px:c.px,lineH:c.lineH,width:c.width}),g?p+=c.reservedH:u+=c.reservedH,h=Math.max(h,b+c.blockH)}return{placed:f,bandBottom:h}}function Sr(e,t,n,o){e.save(),e.textBaseline="top";const{placed:r}=Nt(e,t,n,o);for(const a of r){e.fillStyle=ee(a.layer.color),e.textAlign=a.layer.align,e.font=`${J} ${a.px}px "${st(a.layer.fontId).family}"`;let i=a.yTop;for(const l of a.lines)e.fillText(l,a.x,i),i+=a.lineH}e.restore()}function wr(e,t){return e.sizePt*(t/te)*(e.lineHeight??_t)}function vr(e,t,n,o){return Nt(e,t,n,o).placed.map(r=>{const a=r.layer.align==="center"?r.x-r.width/2:r.layer.align==="right"?r.x-r.width:r.x;return{index:r.index,anchorX:r.x/n,top:r.yTop/o,left:a/n,width:r.width/n,height:r.lines.length*r.lineH/o}})}function Ir(e,t,n,o,r){t.forEach((a,i)=>{const l=r[a];!l||l.length===0||(e.save(),e.translate(i*n,0),Sr(e,l,n,o),e.restore())})}const xr=""+new URL("Inter-SemiBold-B9yU462S.woff2",import.meta.url).href,Pr=""+new URL("Manrope-SemiBold-44nuYf5T.woff2",import.meta.url).href,Ar=""+new URL("Poppins-SemiBold-DJVo7c07.woff2",import.meta.url).href,Or=""+new URL("Fraunces-SemiBold-BtraKtER.woff2",import.meta.url).href,Tr=""+new URL("SpaceGrotesk-SemiBold-CyO2J5Y0.woff2",import.meta.url).href,kr={inter:xr,manrope:Pr,poppins:Ar,fraunces:Or,"space-grotesk":Tr},Cr=X.map(e=>({...e,url:kr[e.id]}));let K=null;function Er(){return K||(K=(async()=>{const e=new Set;document.fonts.forEach(t=>e.add(`${t.family}/${t.weight}`)),await Promise.all(Cr.filter(t=>!e.has(`${t.family}/${String(J)}`)).map(async t=>{const n=new FontFace(t.family,`url(${t.url})`,{weight:String(J)});await n.load(),document.fonts.add(n)})),await document.fonts.ready})().catch(e=>{throw K=null,e})),K}const Mr=22/te,Lr=1.1,We=4,Fr=.063,Rr=.167,Ve=Math.round(Mr*te),_r=.02,Nr=50;function j(e,t){const n=Math.max(e.x,t.x),o=Math.max(e.y,t.y),r=Math.min(e.x+e.width,t.x+t.width),a=Math.min(e.y+e.height,t.y+t.height);return r<=n||a<=o?null:{x:n,y:o,width:r-n,height:a-o}}function Bt(e,t){return j(e,t)}function Ke(e,t){const n=Math.abs(e-t)%360;return n>180?360-n:n}function qe(e){return gn(e.cameraPos??tt(e.angle))}function Dt(e,t,n,o,r,a,i){if(t.length===0)return[];const l=[];for(const s of vr(e,t,r,a)){const d=t[s.index];if(!d)continue;const u=Bt({x:(o+s.left)*r,y:s.top*a,width:s.width*r,height:s.height*a},i);u&&l.push({ref:{panelId:n,panelIndex:o,layerIndex:s.index,band:B(d.anchor)},box:u,lineHeightPx:wr(d,a)})}return l}function Br(e,t,n,o){return n.height<=Se(e,t,o)}function Se(e,t,n){const o=e.y+e.height/2<=t.y+t.height/2;return n*(o?Rr:Fr)}function Dr(e,t,n){return n.height<=Se(e.box,t.box,e.lineHeightPx)+Se(t.box,e.box,t.lineHeightPx)}function zr(e){const{ctx:t,panelIds:n,panelWidth:o,panelHeight:r}=e,a={x:0,y:0,width:o*n.length,height:r};t?.save();try{return jr(e,a)}finally{t?.restore()}}function jr(e,t){const{ctx:n,panelIds:o,panelWidth:r,panelHeight:a,captionLayers:i}=e,l=o.flatMap((c,g)=>Dt(n,i[c]??[],c,g,r,a,t)),s=[];for(const c of e.phones){const m=e.clip==="panel"||e.clip===void 0&&c.clipToFrame===!0?{x:c.panelIndex*r,y:0,width:r,height:a}:t,b=Bt({x:c.x,y:c.y,width:c.width,height:c.height},m);b&&s.push({shotId:c.shotId,box:b,layoutHeight:c.height})}const d=[];for(let c=0;c<l.length;c++)for(let g=c+1;g<l.length;g++){const m=j(l[c].box,l[g].box);m&&d.push({a:l[c].ref,b:l[g].ref,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Dr(l[c],l[g],m)})}const u=[];for(const c of l)for(const g of s){const m=j(c.box,g.box);m&&u.push({caption:c.ref,shotId:g.shotId,overlap:m,depthFrac:a>0?m.height/a:0,inkUncertain:Br(c.box,g.box,m,c.lineHeightPx)})}const p=[];for(let c=0;c<s.length;c++)for(let g=c+1;g<s.length;g++){const m=s[c],b=s[g],S=j(m.box,b.box);if(!S)continue;const y=[],P=Math.max(m.layoutHeight,b.layoutHeight),A=Math.min(m.layoutHeight,b.layoutHeight);A>0&&P/A>=Lr&&y.push("scale");const v=e.looks?.[m.shotId],w=e.looks?.[b.shotId],I=!!v&&!!w;v&&w&&(Ke(qe(v),qe(w))>=We&&y.push("angle"),Ke(R("roll",v.roll),R("roll",w.roll))>=We&&y.push("roll"));const O=Math.min(m.box.width*m.box.height,b.box.width*b.box.height);p.push({shotIdA:m.shotId,shotIdB:b.shotId,overlap:S,coverage:O>0?S.width*S.height/O:0,deliberate:y.length>0,signals:y,anglesKnown:I})}const h=new Set;for(const c of d)c.a.band&&h.add(c.a.panelId),c.b.band&&h.add(c.b.panelId);for(const c of u)c.caption.band&&h.add(c.caption.panelId);const f=[];return o.forEach((c,g)=>{if(!h.has(c))return;const m=Hr(e,c,g,t,l,s);m&&f.push(m)}),{captionsMeasurable:n!=null,captionOverlaps:d,captionDeviceOverlaps:u,deviceOverlaps:p,suggestions:f,hasWarning:d.length>0||u.length>0||p.some(c=>!c.deliberate)}}function Hr(e,t,n,o,r,a){const i=e.captionLayers[t]??[],l=i.flatMap((u,p)=>B(u.anchor)&&u.text.trim()?[p]:[]);if(l.length===0||!(Math.min(...l.map(u=>i[u].sizePt))>Ve))return null;const d=[...a.map(u=>u.box),...r.filter(u=>!(u.ref.panelId===t&&u.ref.band)).map(u=>u.box)];for(let u=1;u<=Nr;u++){const p=1-u*_r,h=new Map(l.map(m=>[m,Math.floor(i[m].sizePt*p)]));if(Math.min(...h.values())<Ve)return null;const f=i.map((m,b)=>{const S=h.get(b);return S===void 0?m:{...m,sizePt:S}});if(Dt(e.ctx,f,t,n,e.panelWidth,e.panelHeight,o).filter(m=>m.ref.band).every(m=>!d.some(b=>j(m.box,b))))return{panelId:t,scale:p,layers:l.map(m=>({layerIndex:m,fromSizePt:i[m].sizePt,toSizePt:h.get(m)}))}}return null}function zt(e){return`build-shot-${e+1}`}function jt(e){const t=[];for(const n of e.shots)t.includes(n.panelId)||t.push(n.panelId);return t}const Gr=1900,Ur=2800;function $r(e){return new Promise((t,n)=>{e.toBlob(o=>{if(!o)return n(new Error("canvas toBlob returned null"));o.arrayBuffer().then(r=>t(new Uint8Array(r)),n)},"image/png")})}async function Ht(e,t,n,o={}){await Er();const r=jt(e),a=r.map(h=>({id:h})),i=e.shots.map((h,f)=>({id:zt(f),panelId:h.panelId,frameNodeId:"",frameName:`screen-${f+1}`,bytes:t.screens[f]??null,look:h.look,thumb:null,thumbStale:!1})),s={panelPresetId:G(e.panelPresetId).id,bgMode:e.bgMode,gradientStops:e.gradientStops,gradientAngle:e.gradientAngle,gradientFrom:e.gradientFrom,gradientTo:e.gradientTo,gradientDir:e.gradientDir,panelColors:e.panelColors,shadow:e.shadow,floorReflection:e.floorReflection,panelBackgrounds:e.panelBackgrounds,selectedShotId:null,renderScale:o.renderScale,locale:n},d=o.onProgress??(()=>{});let u=o.refs,p=null;if(!u){const h=An(e.panelPresetId);p=rt(Gr,Ur);const f=await ot(p,h);u={stage:p,device:new at(f,p.scene,p.renderer,h),screen:new it(f,h)}}try{const h=await dr(u.stage,u,i,a,s,d);if(!h)throw new Error("buildStrip: nothing renderable (no shots)");const f=document.createElement("canvas");try{const c={...o.clip?{clip:o.clip}:{},...o.backgroundViewport?{backgroundViewport:o.backgroundViewport}:{}};await hr(f,h,Object.keys(c).length>0?c:void 0);const g=h.panelWidth,m=h.panelHeight,b=f.getContext("2d");if(!b)throw new Error("buildStrip: 2D context unavailable");Ir(b,r,g,m,t.captionText??{});const S=zr({phones:h.phones,panelIds:r,captionLayers:t.captionText??{},panelWidth:g,panelHeight:m,ctx:b,looks:Object.fromEntries(i.map(A=>[A.id,A.look])),clip:o.clip}),y=o.renderScale&&o.renderScale>0&&o.renderScale<1?o.renderScale:1,P=[];for(let A=0;A<h.count;A++){const v=document.createElement("canvas");v.width=Math.max(1,Math.round(g*y)),v.height=Math.max(1,Math.round(m*y));const w=v.width,I=v.height;try{const O=v.getContext("2d");if(!O)throw new Error("buildStrip: 2D context unavailable");O.drawImage(f,A*g,0,g,m,0,0,w,I),P.push({index:A,bytes:await $r(v),width:w,height:I})}finally{v.width=0,v.height=0}}return{panels:P,layout:S}}finally{f.width=0,f.height=0}}finally{p&&p.dispose()}}const Ye=.005,Wr=.02,Vr=12;function se(e){return Math.max(1,Math.round(e*100))}function le(e){return`${e.kind}:${e.frames.join(",")}:${e.anchored}`}function Kr(e,t){const n=new Map(e.suggestions.map(a=>[a.panelId,a.scale])),o=[];for(const a of e.captionOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const[i,l]=a.a.band===a.b.band?[a.a,a.b]:a.a.band?[a.a,a.b]:[a.b,a.a],s=ce([t.frameOfPanel(i.panelId),t.frameOfPanel(l.panelId)]);s.length!==0&&o.push({kind:"caption-caption",frames:s,depthPct:se(a.depthFrac),anchored:!a.a.band||!a.b.band,...Xe(n,i.band?i.panelId:null)})}for(const a of e.captionDeviceOverlaps){if(a.inkUncertain||a.depthFrac<Ye)continue;const i=ce([t.frameOfPanel(a.caption.panelId),t.frameOfShot(a.shotId)]);i.length!==0&&o.push({kind:"caption-device",frames:i,depthPct:se(a.depthFrac),anchored:!a.caption.band,...Xe(n,a.caption.band?a.caption.panelId:null)})}for(const a of e.deviceOverlaps){if(a.deliberate||a.coverage<Wr)continue;const i=ce([t.frameOfShot(a.shotIdA),t.frameOfShot(a.shotIdB)]);i.length!==0&&o.push({kind:"device-device",frames:i,depthPct:se(a.coverage),anchored:!1})}const r=new Map;for(const a of o){const i=r.get(le(a));i?a.depthPct>i.depthPct?r.set(le(a),{...a,clearAtScale:a.clearAtScale??i.clearAtScale}):i.clearAtScale==null&&a.clearAtScale!=null&&(i.clearAtScale=a.clearAtScale):r.set(le(a),a)}return[...r.values()].sort((a,i)=>i.depthPct-a.depthPct).slice(0,Vr).sort((a,i)=>a.frames[0]-i.frames[0]||a.kind.localeCompare(i.kind))}function ce(e){const[t,...n]=e;return t<0?[]:[t,...n.filter(o=>o>=0&&o!==t)]}function Xe(e,t){const n=t==null?void 0:e.get(t);return n==null?{}:{clearAtScale:Math.round(n*100)/100}}function qr(e,t){if(t.length===0)return e;const n=e.captionStyles;if(!n)return e;const o={};for(const[r,a]of Object.entries(n))o[r]=a.map(i=>({...i}));for(const r of t){const a=o[r.panelId];if(a)for(const i of r.layers){const l=a[i.layerIndex];l&&(l.sizePt=i.toSizePt)}}return{...e,captionStyles:o}}function de(e){return e.frames.map(t=>`frame ${t+1}`).join(" + ")}function Yr(e,t,n){if(!e.captionsMeasurable)return null;const o=Kr(e,t);if(o.length===0)return null;const r=o.filter(d=>d.kind==="device-device"),a=o.filter(d=>d.kind!=="device-device"&&d.clearAtScale==null);if(r.length>0||a.length>0||!n){const d=[];r.length>0&&d.push(`${r.map(de).join(", ")}: two devices overlap without reading as composed. There is no deterministic repair — give the pair a real difference in scale, camera angle or roll (that is what makes an overlap read as deliberate), or leave it if it already is.`);for(const u of a)d.push(u.anchored?`${de(u)}: the caption is anchored by hand, so it is reported and never rewritten — move it, or drop its \`anchor\` to let the band place it.`:`${de(u)}: no caption size above the legibility floor clears it — shorten the headline, or pick a layout with a bigger text region (\`style.layout: "bleed"\`).`);return d.length===0&&d.push("The rendered look was not available here, so no corrected look could be built."),{findings:o,note:`No corrected look is offered. ${d.join(" ")}`}}const i=new Set(o.filter(d=>d.kind!=="device-device").map(d=>d.frames[0])),l=e.suggestions.filter(d=>i.has(t.frameOfPanel(d.panelId))),s=l.flatMap(d=>{const u=t.frameOfPanel(d.panelId)+1;return d.layers.map(p=>`frame ${u} layer ${p.layerIndex+1}: sizePt ${p.fromSizePt} → ${p.toSizePt}`)}).join("; ");return{findings:o,correctedLook:qr(n,l),note:`\`layout.correctedLook\` is this same look with every caption shrunk to the largest size that was verified to clear the overlap (${s}). Nothing here is applied for you — re-render with it, or show the user the finding and let them decide. On render_strip pass it back as \`look\` together with the SAME \`style.captions\` you just sent: the words come from \`style.captions\` and every style field you leave out of them now comes from this look. The one thing that still beats it is a field you set EXPLICITLY on a caption entry, so if your \`style.captions\` pin \`sizePt\` at the original numbers, drop them or set them to the sizes above.`}}function Xr(e){return String(Math.max(-100,Math.min(100,e*35)))}const Jr=["roll","phoneHeight","hOffset","vOffset","finish","clearcoat"];function Je(e){if(!e||typeof e!="object")return{};const t={...e};for(const n of Jr)typeof t[n]=="number"&&(t[n]=String(t[n]));return t}function Zr(e){const{subtitle:t,sizePt:n,maxWidth:o,...r}=e;return{...r,...n!==void 0?{sizePt:Number(n)}:{},...o!==void 0?{maxWidth:Number(o)}:{}}}function Ze(e){const t={};for(const[n,o]of Object.entries(e))o!==void 0&&(t[n]=o);return t}function ue(e,t,n){const o={};return e&&t.forEach((r,a)=>{const i=n[a];i!==void 0&&e[r]!==void 0&&(o[i]=e[r])}),o}function Qr(e){const t=e.slotSizes.filter(w=>Number.isInteger(w)&&w>0),n=t.map((w,I)=>`panel-${I+1}`);let o={};const r=e.look&&typeof e.look=="object"?e.look:null,a=e.style??null,i=r&&Array.isArray(r.shots)?r.shots:[],l=i.length>0,s=a?Je(a.shotLook):null,d=[];let u=0;t.forEach((w,I)=>{for(let O=0;O<w;O++){const T=l?Je(i[u]?.look??i[0]?.look??{}):s??{},$=T.hOffset!==void 0&&T.hOffset!==null?String(T.hOffset):Xr(O);d.push({panelId:n[I],look:{...x,...T,hOffset:$}}),u++}});let p={},h={},f={},c="gradient",g=kn,m=Cn,b="vertical",S,y,P=!0,A=!1;if(r){c=r.bgMode==="perPanel"?"perPanel":"gradient";const w=Q({gradientStops:Array.isArray(r.gradientStops)?r.gradientStops:void 0,gradientAngle:typeof r.gradientAngle=="number"?r.gradientAngle:void 0,gradientFrom:typeof r.gradientFrom=="string"?r.gradientFrom:g,gradientTo:typeof r.gradientTo=="string"?r.gradientTo:m,gradientDir:r.gradientDir==="horizontal"?"horizontal":b});S=w.stops,y=w.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(w),r.shadow===!1&&(P=!1),r.floorReflection===!0&&(A=!0);const I=[];for(const O of i){const T=typeof O?.panelId=="string"?O.panelId:null;T&&!I.includes(T)&&I.push(T)}p=ue(r.panelColors,I,n),h=ue(Pt(r.captionStyles),I,n),f=ue(vt(r.panelBackgrounds),I,n)}else if(a){const w=a.background??{};c=w.mode==="perPanel"?"perPanel":"gradient";const I=Q({gradientStops:w.stops,gradientAngle:w.angle,gradientFrom:g,gradientTo:m,gradientDir:b});S=I.stops,y=I.angle,{gradientFrom:g,gradientTo:m,gradientDir:b}=Z(I),w.shadow===!1&&(P=!1),w.floorReflection===!0&&(A=!0),Array.isArray(w.panelColors)&&w.panelColors.forEach((O,T)=>{typeof O=="string"&&O&&n[T]!==void 0&&(p[n[T]]=O)})}if(a&&Array.isArray(a.captions)){const w=h,I=(L,oe)=>({...w[L]?.[oe]??{}}),O={},T={};a.captions.forEach((L,oe)=>{const _=n[oe];if(!(_===void 0||L===null||L===void 0)){if(Array.isArray(L)){const N=L.filter(E=>!!E&&typeof E=="object"&&typeof E.text=="string"&&E.text.trim()!=="").map((E,W)=>({...I(_,W),...Ze(Zr(E))}));N.length>0&&(T[_]=N)}else if(typeof L=="object"){const{text:N,subtitle:E,sizePt:W,maxWidth:Ae,...Vt}=L;O[_]={...I(_,0),...Ze({...Vt,...W!==void 0?{sizePt:Number(W)}:{},...Ae!==void 0?{maxWidth:Number(Ae)}:{}})},typeof N=="string"&&N.trim()&&(T[_]={headline:N,...typeof E=="string"&&E?{subtitle:E}:{}})}}});const $=re({[M]:T},xt(O));h=Ot($),o=At($)}return{look:{panelPresetId:e.panelPresetId??(typeof r?.panelPresetId=="string"?r.panelPresetId:void 0)??"r69",bgMode:c,gradientStops:S,gradientAngle:y,gradientFrom:g,gradientTo:m,gradientDir:b,panelColors:p,shadow:P,floorReflection:A,panelBackgrounds:f,captionStyles:h,shots:d},panelIds:n,captionText:o}}function eo(e){const t=e?.trim();return t?/\.(png|jpe?g|webp)$/i.test(t)?t:`${t}.png`:null}function to(e,t){const n=new Map;return e.map((o,r)=>{const a=eo(t?.[r]);if(!a)return`screen-${r+1}.png`;const i=n.get(a)??0;if(n.set(a,i+1),i===0)return a;const l=a.lastIndexOf(".");return`${a.slice(0,l)}-${i+1}${a.slice(l)}`})}function no(e){const{look:t,panelIds:n,captionText:o,locale:r,shotNames:a,outputs:i}=e,l=n.map(u=>({id:u})),s=to(t.shots,a),d=t.shots.map((u,p)=>({id:`shot-${p+1}`,panelId:u.panelId,frameNodeId:s[p],frameName:s[p],bytes:null,look:u.look,thumb:null,thumbStale:!0}));return Mt({shots:d,panels:l,selectedShotId:d[0]?.id??null,panelPresetId:t.panelPresetId,bgMode:t.bgMode,gradientStops:t.gradientStops,gradientAngle:t.gradientAngle,gradientFrom:t.gradientFrom,gradientTo:t.gradientTo,gradientDir:t.gradientDir,panelColors:t.panelColors,shadow:t.shadow,floorReflection:t.floorReflection,panelBackgrounds:t.panelBackgrounds,captionStyles:t.captionStyles,outputs:i,captionText:o&&Object.keys(o).length>0?{[r||M]:o}:{}})}function C(e){console.log("[render]",e)}const ro=1900,oo=2800;let fe=null;async function Gt(){return fe||(fe=(async()=>{C("booting headless stage + loading model (once)…");const e=rt(ro,oo),t=await vn(e);return{stage:e,device:new at(t,e.scene,e.renderer),screen:new it(t)}})()),fe}function Ut(e){const t=atob(e),n=new Uint8Array(t.length);for(let o=0;o<t.length;o++)n[o]=t.charCodeAt(o);return n}function Pe(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function ao(e){return new Promise((t,n)=>{e.toBlob(async o=>{if(!o)return n(new Error("toBlob null"));t(Pe(new Uint8Array(await o.arrayBuffer())))},"image/png")})}function $t(e,t){const n=jt(t),o=new Map(t.shots.map((a,i)=>[zt(i),n.indexOf(a.panelId)])),r=Yr(e.layout,{frameOfPanel:a=>n.indexOf(a),frameOfShot:a=>o.get(a)??-1},t);if(r)return{layout:{findings:r.findings,...r.correctedLook?{correctedLook:r.correctedLook}:{}},layoutNote:r.note}}async function Wt(e){const t=await Promise.all(e.map(a=>createImageBitmap(new Blob([new Uint8Array(a.bytes)],{type:"image/png"})))),n=document.createElement("canvas");n.width=t.reduce((a,i)=>a+i.width,0),n.height=t[0]?.height??1;const o=n.getContext("2d");let r=0;for(const a of t)o.drawImage(a,r,0),r+=a.width,a.close();return{base64:await ao(n),width:n.width,height:n.height}}window.renderStrip=async function(t){try{if(!t||!Array.isArray(t.screenshots)||t.screenshots.length===0)throw new Error("renderStrip: input.screenshots must be a non-empty nested array of slots");const n=await Gt(),o=t.screenshots.map(y=>Array.isArray(y)?y:[y]),{look:r,panelIds:a,captionText:i}=Qr({slotSizes:o.map(y=>y.length),panelPresetId:t.panelPresetId,look:t.look??void 0,style:t.style??void 0}),l=o.flat().map(Ut),s=Array.isArray(t.captionLayers)&&a[0]?{[a[0]]:t.captionLayers}:i,d=t.preview?.25:1;C(`buildStrip (${o.length} panel${o.length===1?"":"s"}${t.locale?`, ${t.locale}`:""})…`);const u=performance.now(),p=await Ht(r,{screens:l,captionText:s},t.locale,{refs:n,renderScale:d,onProgress:y=>C(" "+y),...t.clip?{clip:t.clip}:{},...t.backgroundViewport?{backgroundViewport:t.backgroundViewport}:{}});C("encoding panels + stitching the strip preview…");const h=p.panels.map(y=>Pe(y.bytes)),f=await Wt(p.panels),c=G(r.panelPresetId),g=Math.round(performance.now()-u),m=$t(p,r),b=Array.isArray(t.screenshotNames)?t.screenshotNames.flat():void 0,S=t.buildRecord?no({look:r,panelIds:a,captionText:i,locale:t.locale,shotNames:b,outputs:t.outputs}):void 0;return C(`done: ${p.panels.length} panels, strip ${f.width}x${f.height}, ${g}ms`),{ok:!0,ms:g,count:p.panels.length,panelPresetId:c.id,panelWidth:c.width,panelHeight:c.height,stripWidth:f.width,stripHeight:f.height,panels:h,stripBase64:f.base64,preview:d<1,...S?{projectRecord:S}:{},...m??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.renderProject=async function(t){try{if(!t||t.projectFile==null||typeof t.projectFile!="object")throw new Error("renderProject: input.projectFile (the saved ProjectFile) is required");const n=Qn(JSON.stringify(t.projectFile));if(!n)throw new Error("renderProject: the saved project could not be restored (wrong schema or malformed record)");const o=await Gt(),r=t.locale||n.baseLocale||n.locales?.[0]||"en-US",a=U(n.shots,n.panels),i=me(t.variant||{},n.baseLocale),l=new Map((t.screenManifest||[]).map(v=>[`${v.shotId} ${Be(me(v.variant||{},n.baseLocale))}`,v])),s=(v,w)=>l.get(`${v} ${Be(w)}`),d=a.map(v=>Wn(s,v.id,i,Un,n.baseLocale)),u=a.map((v,w)=>({frameName:v.frameName,matched:d[w].value!==void 0,requestedVariant:i,resolvedAt:d[w].resolvedAt,inherited:d[w].inherited,...d[w].value?.source?{source:d[w].value.source}:{}})),p=d.map(({value:v})=>v?.bytes?Ut(v.bytes):null),h=At(n.captionText,r,n.baseLocale),f=tr({...n,selectedShotId:null,panelPresetId:t.panelPresetId??n.panelPresetId,captionText:{[r]:h}}),c=t.preview?.25:1;C(`renderProject (${a.length} shot${a.length===1?"":"s"}, ${r})…`);const g=performance.now(),m=await Ht(f,{screens:p,captionText:h},r,{refs:o,renderScale:c,onProgress:v=>C(" "+v)});C("encoding panels + stitching the strip preview…");const b=m.panels.map(v=>Pe(v.bytes)),S=await Wt(m.panels),y=G(f.panelPresetId),P=Math.round(performance.now()-g),A=$t(m,f);return C(`done: ${m.panels.length} panels, strip ${S.width}x${S.height}, ${P}ms`),{ok:!0,ms:P,count:m.panels.length,panelPresetId:y.id,panelWidth:y.width,panelHeight:y.height,stripWidth:S.width,stripHeight:S.height,panels:b,stripBase64:S.base64,preview:c<1,shots:u,locale:r,...A??{}}}catch(n){const o=n&&n.stack?n.stack:String(n);return C("ERROR: "+o),{ok:!1,error:String(n&&n.message?n.message:n)}}};window.__RENDER_READY=!0;C("harness ready ✓");
|