greenlight-motion 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,16 +17,16 @@ The agent writes the video as data: a `project.json` and its files. The engine t
17
17
  ## Quick start
18
18
 
19
19
  ```bash
20
- npx greenlight-motion@0.2.0 doctor # Node 18+, Chrome or Edge, WebGPU, H.264: what's missing and how to fix it
21
- npx greenlight-motion@0.2.0 init my-video # a working starter: index.html, project.json, assets/
20
+ npx greenlight-motion@0.2.1 doctor # Node 18+, Chrome or Edge, WebGPU, H.264: what's missing and how to fix it
21
+ npx greenlight-motion@0.2.1 init my-video # a working starter: index.html, project.json, assets/
22
22
  # edit my-video/project.json, put pictures / video / sound in my-video/assets/
23
23
  # or: write my-video/script.json (a scene script, skill/references/scene-script.md), then
24
- npx greenlight-motion@0.2.0 script my-video # script.json → project.json with the Video Editor's own presets
25
- npx greenlight-motion@0.2.0 check my-video # what to fix: missing files, template mistakes, blank frames, broken plugins
26
- npx greenlight-motion@0.2.0 stills my-video # frames + a labelled contact sheet, to look at
27
- npx greenlight-motion@0.2.0 render my-video --fps 60 --motion-blur
28
- npx greenlight-motion@0.2.0 pack my-video # everything in ONE html file
29
- npx greenlight-motion@0.2.0 pack my-video --glvideo # ONE .glvideo: double-clicked, it opens in GreenLight Dash
24
+ npx greenlight-motion@0.2.1 script my-video # script.json → project.json with the Video Editor's own presets
25
+ npx greenlight-motion@0.2.1 check my-video # what to fix: missing files, template mistakes, blank frames, broken plugins
26
+ npx greenlight-motion@0.2.1 stills my-video # frames + a labelled contact sheet, to look at
27
+ npx greenlight-motion@0.2.1 render my-video --fps 60 --motion-blur
28
+ npx greenlight-motion@0.2.1 pack my-video # everything in ONE html file
29
+ npx greenlight-motion@0.2.1 pack my-video --glvideo # ONE .glvideo: double-clicked, it opens in GreenLight Dash
30
30
  ```
31
31
 
32
32
  Also: `trace` (every animated layer's values frame by frame, plus the layer's moments), `set key=value`
@@ -72,7 +72,7 @@ The engine is one classic script, so it runs from a double-clicked file, a CDN o
72
72
 
73
73
  ```html
74
74
  <div id="app"></div>
75
- <script src="https://cdn.jsdelivr.net/npm/greenlight-motion@0.2.0/dist/greenlight-motion.js"></script>
75
+ <script src="https://cdn.jsdelivr.net/npm/greenlight-motion@0.2.1/dist/greenlight-motion.js"></script>
76
76
  <script>
77
77
  GreenLightMotion.mountStudio(document.getElementById('app'), {
78
78
  project: { /* project.json */ },
@@ -18870,6 +18870,27 @@ function cameraCatalog() {
18870
18870
  var FONT = "'Helvetica Neue', Helvetica, Arial, sans-serif";
18871
18871
  var TEXT = new Map([...PRESETS, ...CLASSIC_PRESETS].map((p) => [p.id, p]));
18872
18872
  var TEXT_RENAMED = { "glide-soft": "glide-continuous" };
18873
+ var rgbOf = (c) => {
18874
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(String(c || "").trim());
18875
+ if (!m) return null;
18876
+ const h = m[1].length === 3 ? m[1].split("").map((x) => x + x).join("") : m[1];
18877
+ return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
18878
+ };
18879
+ var vivid = (c) => {
18880
+ const v = rgbOf(c);
18881
+ return !!v && Math.max(...v) - Math.min(...v) >= 40;
18882
+ };
18883
+ var luminance = (c) => {
18884
+ const v = rgbOf(c);
18885
+ if (!v) return null;
18886
+ const [r, g, b2] = v.map((x) => x / 255 <= 0.03928 ? x / 255 / 12.92 : ((x / 255 + 0.055) / 1.055) ** 2.4);
18887
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b2;
18888
+ };
18889
+ var contrast = (a, b2) => {
18890
+ const la = luminance(a), lb = luminance(b2);
18891
+ return la == null || lb == null ? 21 : (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
18892
+ };
18893
+ var EDGE_FILLED = /* @__PURE__ */ new Set(["Advanced Zoom", "Advanced Movement"]);
18873
18894
  var KF_IN = new Map(kfpVariants(KEYFRAME_PRESETS, "in").map((p) => [p.id, p]));
18874
18895
  var KF_OUT = new Map(kfpVariants(KEYFRAME_PRESETS, "out").map((p) => [p.id, p]));
18875
18896
  var ROLES = { headline: [0.11, 800], subhead: [0.05, 500], body: [0.036, 400], label: [0.028, 600] };
@@ -19287,12 +19308,33 @@ function compileScript(script, caps, files = /* @__PURE__ */ new Map()) {
19287
19308
  else {
19288
19309
  const patch = presetPatch(item, p, { clipSec: end - start, fontPx: size, mode: "replace" });
19289
19310
  item.textAnimators = el?.textExit ? patch.textAnimators : patch.textAnimators.filter((a) => !(a.range?.drive === "time" && a.range?.animate === "out"));
19311
+ const boxed = [];
19290
19312
  item.textAnimators.forEach((a, ai) => (a.effects || []).forEach((fx, fi) => {
19291
- if (fx.type === "fill" && typeof fx.color === "string") {
19313
+ const at2 = `textAnimators.${ai}.effects.${fi}`;
19314
+ if (Array.isArray(fx.colors)) {
19315
+ fx.colors.forEach((c, ci) => {
19316
+ if (vivid(c)) {
19317
+ fx.colors[ci] = colors.accent;
19318
+ accentTargets.push({ itemId: id, path: `${at2}.colors.${ci}` });
19319
+ }
19320
+ });
19321
+ } else if (typeof fx.color === "string" && vivid(fx.color)) {
19292
19322
  fx.color = colors.accent;
19293
- accentTargets.push({ itemId: id, path: `textAnimators.${ai}.effects.${fi}.color` });
19323
+ accentTargets.push({ itemId: id, path: `${at2}.color` });
19324
+ if (fx.type === "box" && !boxed.includes(a)) boxed.push(a);
19294
19325
  }
19295
19326
  }));
19327
+ if (boxed.length) {
19328
+ const ink = contrast("#0b0d0c", colors.accent) >= contrast("#ffffff", colors.accent) ? "#0b0d0c" : "#ffffff";
19329
+ const fills = item.textAnimators.flatMap((a) => (a.effects || []).filter((fx) => fx.type === "fill"));
19330
+ if (fills.length) fills.forEach((fx) => {
19331
+ if (contrast(fx.color, colors.accent) < 4.5) fx.color = ink;
19332
+ });
19333
+ else if (contrast(color, colors.accent) < 4.5) {
19334
+ let pidN = Math.max(0, ...item.textAnimators.flatMap((a) => (a.effects || []).map((fx) => Number(String(fx.pid).replace(/^\D+/, "")) || 0)));
19335
+ for (const a of boxed) a.effects = [...a.effects || [], { pid: `p${++pidN}`, type: "fill", color: ink }];
19336
+ }
19337
+ }
19296
19338
  item.propertyKeyframes = { ...item.propertyKeyframes || {}, ...patch.propertyKeyframes };
19297
19339
  if (patch.textAnimatorPivotX != null) {
19298
19340
  item.textAnimatorPivotX = patch.textAnimatorPivotX;
@@ -19417,6 +19459,7 @@ function compileScript(script, caps, files = /* @__PURE__ */ new Map()) {
19417
19459
  else if (tr !== "None") {
19418
19460
  const prev = num7(scenes[si - 1]?.duration, 3);
19419
19461
  sceneItem.transition = tr;
19462
+ if (EDGE_FILLED.has(tr)) sceneItem.transitionOptions = { edgeMode: "none" };
19420
19463
  sceneItem.transitionDuration = r3(clamp5(num7(sc?.transitionDuration, 0.6), 0.2, Math.min(prev, D) / 2));
19421
19464
  total -= sceneItem.transitionDuration;
19422
19465
  if ((S.sound ?? "auto") === "auto") project.sounds.push({ sfx: soundOk("soft-swoosh", where) ? "soft-swoosh" : "standard-swoosh", on: project.channels[0].items.at(-1).id, at: "end" });
@@ -19495,6 +19538,19 @@ var refOf = (host, topic) => host === "chat" ? `get_motion_reference "${topic}"`
19495
19538
  function scriptFormatGuide(host = "chat") {
19496
19539
  return `## The scene script
19497
19540
 
19541
+ **Before the script, a direction.** Presets make a video move; they don't give it an idea. Decide in a few lines first,
19542
+ and keep them next to the script:
19543
+ - **The idea**: ONE picture from the subject's own world (its object, place or action: a coffee shop's steam and cup rim,
19544
+ a budgeting app's receipt printing line by line, a running app's track the scenes run along), carried through every scene.
19545
+ - **The palette**: background, text, one accent, plus only the colours that carry meaning (good / bad, before / after).
19546
+ Light or dark on purpose.
19547
+ - **The type**: a big headline against a small support line; aligned left or centred on purpose, not by default.
19548
+ - **Each scene**: what it SHOWS besides its words, built from shapes, lines, \`svg\` you write, keyframes and the camera.
19549
+ The same line centred on a gradient in every scene reads as a template. Judge the stills against the direction, not
19550
+ only for mistakes.${host === "cli" ? `
19551
+ What a script can't say (a custom GLSL shader on a shape or the background, words dipping under a surface with a track
19552
+ matte, masks, particles, 3D) goes on from \`project.json\`: SKILL.md, "Beyond the script".` : ""}
19553
+
19498
19554
  \`\`\`json
19499
19555
  {
19500
19556
  "title": "Bean There \u2014 opening day",
@@ -19539,6 +19595,9 @@ function scriptFormatGuide(host = "chat") {
19539
19595
  (the scene's camera doesn't move it: a logo, a caption). Keyframes also take a \`z\` track.
19540
19596
  - **Scene**: \`duration\` (s), \`transition\` into it (default Cross Fade, 0.6 s; \`transitionDuration\`), \`camera\` (below; default push-in),
19541
19597
  \`background\` (overrides style), \`sounds\`: [{ "sfx", "at" }]. Script-level \`sound\`: auto (default: a whoosh on each transition and on the first headline) | none.
19598
+ **A transition overlaps the scenes on both sides**, so the video lasts the scenes' durations added up MINUS every
19599
+ transition: five 3 s scenes with the default 0.6 s transitions run 15 \u2212 4 \xD7 0.6 = 12.6 s. To hit a length, add the
19600
+ transitions back to the scenes (${host === "chat" ? "build_video" : "`script`"} prints the total).
19542
19601
  - **camera** \u2014 a quick move of the whole scene: ${CAMERAS.join(" | ")}. Or a REAL camera that films the scene's elements in 3D (the
19543
19602
  Video Editor's own camera layer; the scene's backdrop stays put, so its edge never shows). One of:
19544
19603
  - \`{ "move": id }\` \u2014 a camera move from the catalogue (or just \`"camera": "cm-quiet-push"\`), spread over the scene;
@@ -18515,7 +18515,7 @@ ${Jh("Box · Text · Pin",Zh("R","T","N"))}
18515
18515
  ${Jh("Hide · Crop · Range",Zh("X","C","G"))}
18516
18516
  </div>
18517
18517
  </div>`}let eSe=!1;function tSe(t,{project:e,sources:n,strips:a,parts:s,config:r,assets:o=null,chat:i=null}={}){if(!t)throw new Error("GreenLightMotion.mountStudio needs an element");if(eSe)throw new Error("GreenLightMotion.mountStudio: one studio per page");if(e&&(window.GL_LAB_PROJECT=e),!window.GL_LAB_PROJECT)throw new Error("GreenLightMotion.mountStudio needs a project");if(n&&(window.RV_SOURCES=n),a&&(window.RV_STRIPS=a),s&&(window.RV_PARTS=s),r&&(window.RV_CONFIG=r),!window.RV_CONFIG&&!window.RV_SOURCES){const c=window.GL_LAB_PROJECT.template?.title||window.GL_LAB_PROJECT.name||"Video";window.RV_CONFIG={title:c,storeKey:"glm-studio-"+String(c).toLowerCase().replace(/[^a-z0-9]+/g,"-").slice(0,40),start:"edit",startAt:0,saveLabel:"Copy for agent",saveTitle:"Your notes, highlights and changes, as text for your agent",savedText:"Ready to copy",copyNote:"<b>Your review.</b> Copy it and give it to your agent with this video."}}if(!document.querySelector(".lab-side")){const c=document.createElement("aside");c.className="lab-side",c.setAttribute("aria-label","Sources"),c.hidden=!0,c.innerHTML=BMt((window.RV_SOURCES||[]).length||1),t.after(c)}if(!document.getElementById("lab-toast")){const c=document.createElement("div");c.id="lab-toast",c.setAttribute("role","status"),document.body.append(c)}if(!document.querySelector("style[data-greenlight-studio]")){const c=document.createElement("style");c.setAttribute("data-greenlight-studio","");const d=[U9t,G9t,j9t,V9t,H9t,W9t,$9t].join(`
18518
- `);c.textContent=i?Z8e(d):d,document.head.append(c)}if(i&&!document.querySelector("style[data-greenlight-chat]")){const c=document.querySelector("style[data-greenlight-motion]");c&&(c.textContent=Z8e(c.textContent));const d=document.createElement("style");d.setAttribute("data-greenlight-chat",""),d.textContent=Y9t,document.head.insertBefore(d,c||document.querySelector("style[data-greenlight-studio]"))}window.LabNoteEngine={...mMt,zipStore:jte};const l={el:t,assets:o};eSe=!0;for(const c of[gMt,xMt,yMt,vMt,bMt,_Mt,IMt])c(l);return i&&(l.chat=RMt(l,i)),l}const Kte="0.2.0",uz=()=>{};kxt(),z3t();function FMt({project:t,playerRef:e}){const n=t.videoSize||{w:1920,h:1080};return le.jsx("div",{style:{width:n.w,height:n.h,background:"#000"},children:le.jsx($Me,{ref:e,channels:t.channels||[],clips:t.clips||[],isPlaying:!1,setIsPlaying:uz,currentTime:0,playheadTime:0,setCurrentTime:uz,setPlayheadTime:uz,soundEnabled:!1,forceMuteAudio:!1,videoWidth:n.w,videoHeight:n.h,outputSize:null,previewQuality:"full",motionBlurEnabled:t.motionBlurEnabled===!0,background:t.background||null,markers:t.markers||[],textVariables:t.textVariables||[],timecodeFps:t.fps||30})})}const LMt=async(t,e,n)=>{const a=performance.now()+e;for(;!t();){if(performance.now()>a)throw new Error(`GreenLightMotion: timed out waiting for ${n}`);await new Promise(s=>setTimeout(s,30))}};async function nSe(t,{renderer:e="webgpu",assets:n=null}={}){if(!t)throw new Error("GreenLightMotion.create needs a container element");if(e==="webgpu"&&!navigator.gpu)throw new Error("This browser has no WebGPU");EY(e),HY(e);const a=o0e.createRoot(t),s={current:null};let r=!1,o=fte(n);OL(o);let i=null;const l=d=>pI({channels:i?.channels||[],clips:i?.clips||[],getPlayer:()=>s.current,range:d}),c=()=>{if(r)throw new Error("GreenLightMotion: disposed");if(!s.current)throw new Error("GreenLightMotion: load a project first");return s.current};return{version:Kte,async load(d,{assets:h}={}){if(!d||typeof d!="object")throw new Error("GreenLightMotion.load needs a project");h&&(o.dispose(),o=fte(h),OL(o)),U3t();const u=await vI(d),f=await t6e(u,o);return i=f,a.render(le.jsx(FMt,{project:f,playerRef:s})),await LMt(()=>s.current,1e4,"the player"),{missing:gI()}},missingAssets:gI,async renderFrame(d,h={}){const u=Math.max(0,Number(d)||0);return c(),await l({start:u,end:u+.001}),c().captureFrameAtTime(u,h)},isExportSupported(){try{return!!c().isWebCodecsSupported?.()}catch{return!1}},async export({format:d="mp4",fps:h=30,onProgress:u=uz,signal:f}={}){const p=c();if(!p.isWebCodecsSupported?.())throw new Error("This browser cannot encode video (WebCodecs)");return await l(null),p.exportVideo(d,u,f,h)},dispose(){r=!0,a.unmount(),o.dispose(),OL(null)}}}function aSe(t,{project:e,assets:n=null,theme:a="dark"}={}){if(!t)throw new Error("GreenLightMotion.mountPage needs an element");if(!e)throw new Error("GreenLightMotion.mountPage needs a project");EY("webgpu"),HY("webgpu");const s=o0e.createRoot(t);return s.render(le.jsx(z9t,{project:e,assets:n,theme:a})),{unmount:()=>s.unmount()}}window.GreenLightMotion={create:nSe,mountPage:aSe,mountStudio:tSe,checkTemplate:qMe,checkSoundCues:T6e,sounds:N4,missingAssets:gI,pluginStatus:e6e,normalize:vI,trace:async(t,e)=>G8e(await vI(t),e),version:Kte};const zMt=["alphaMap","alphaTest","anisotropy","anisotropyMap","anisotropyRotation","aoMap","aoMapIntensity","attenuationColor","attenuationDistance","bumpMap","clearcoat","clearcoatMap","clearcoatNormalMap","clearcoatNormalScale","clearcoatRoughness","color","dispersion","displacementMap","emissive","emissiveIntensity","emissiveMap","envMap","envMapIntensity","gradientMap","ior","iridescence","iridescenceIOR","iridescenceMap","iridescenceThicknessMap","lightMap","lightMapIntensity","map","matcap","metalness","metalnessMap","normalMap","normalScale","opacity","roughness","roughnessMap","sheen","sheenColor","sheenColorMap","sheenRoughnessMap","shininess","specular","specularColor","specularColorMap","specularIntensity","specularIntensityMap","specularMap","thickness","transmission","transmissionMap"],Jte=new WeakMap;class sSe{constructor(e){this.renderObjects=new WeakMap,this.hasNode=this.containsNode(e),this.hasAnimation=e.object.isSkinnedMesh===!0,this.refreshUniforms=zMt,this.renderId=0}firstInitialization(e){return this.renderObjects.has(e)===!1?(this.getRenderObjectData(e),!0):!1}needsVelocity(e){const n=e.getMRT();return n!==null&&n.has("velocity")}getRenderObjectData(e){let n=this.renderObjects.get(e);if(n===void 0){const{geometry:a,material:s,object:r}=e;if(n={material:this.getMaterialData(s),geometry:{id:a.id,attributes:this.getAttributesData(a.attributes),indexId:a.index?a.index.id:null,indexVersion:a.index?a.index.version:null,drawRange:{start:a.drawRange.start,count:a.drawRange.count}},worldMatrix:r.matrixWorld.clone()},r.center&&(n.center=r.center.clone()),r.morphTargetInfluences&&(n.morphTargetInfluences=r.morphTargetInfluences.slice()),e.bundle!==null&&(n.version=e.bundle.version),n.material.transmission>0){const{width:o,height:i}=e.context;n.bufferWidth=o,n.bufferHeight=i}n.lights=this.getLightsData(e.lightsNode.getLights()),this.renderObjects.set(e,n)}return n}getAttributesData(e){const n={};for(const a in e){const s=e[a];n[a]={id:s.id,version:s.version}}return n}containsNode(e){const n=e.material;for(const a in n)if(n[a]&&n[a].isNode)return!0;return!!(e.context.modelViewMatrix||e.context.modelNormalViewMatrix||e.context.getAO||e.context.getShadow)}getMaterialData(e){const n={};for(const a of this.refreshUniforms){const s=e[a];s!=null&&(typeof s=="object"&&s.clone!==void 0?s.isTexture===!0?n[a]={id:s.id,version:s.version}:n[a]=s.clone():n[a]=s)}return n}equals(e,n){const{object:a,material:s,geometry:r}=e,o=this.getRenderObjectData(e);if(o.worldMatrix.equals(a.matrixWorld)!==!0)return o.worldMatrix.copy(a.matrixWorld),!1;const i=o.material;for(const v in i){const b=i[v],M=s[v];if(b.equals!==void 0){if(b.equals(M)===!1)return b.copy(M),!1}else if(M.isTexture===!0){if(b.id!==M.id||b.version!==M.version)return b.id=M.id,b.version=M.version,!1}else if(b!==M)return i[v]=M,!1}if(i.transmission>0){const{width:v,height:b}=e.context;if(o.bufferWidth!==v||o.bufferHeight!==b)return o.bufferWidth=v,o.bufferHeight=b,!1}const l=o.geometry,c=r.attributes,d=l.attributes,h=Object.keys(d),u=Object.keys(c);if(l.id!==r.id)return l.id=r.id,!1;if(h.length!==u.length)return o.geometry.attributes=this.getAttributesData(c),!1;for(const v of h){const b=d[v],M=c[v];if(M===void 0)return delete d[v],!1;if(b.id!==M.id||b.version!==M.version)return b.id=M.id,b.version=M.version,!1}const f=r.index,p=l.indexId,m=l.indexVersion,g=f?f.id:null,x=f?f.version:null;if(p!==g||m!==x)return l.indexId=g,l.indexVersion=x,!1;if(l.drawRange.start!==r.drawRange.start||l.drawRange.count!==r.drawRange.count)return l.drawRange.start=r.drawRange.start,l.drawRange.count=r.drawRange.count,!1;if(o.morphTargetInfluences){let v=!1;for(let b=0;b<o.morphTargetInfluences.length;b++)o.morphTargetInfluences[b]!==a.morphTargetInfluences[b]&&(o.morphTargetInfluences[b]=a.morphTargetInfluences[b],v=!0);if(v)return!1}if(o.lights){for(let v=0;v<n.length;v++)if(o.lights[v].map!==n[v].map)return!1}return o.center&&o.center.equals(a.center)===!1?(o.center.copy(a.center),!0):(e.bundle!==null&&(o.version=e.bundle.version),!0)}getLightsData(e){const n=[];for(const a of e)a.isSpotLight===!0&&a.map!==null&&n.push({map:a.map.version});return n}getLights(e,n){if(Jte.has(e)){const s=Jte.get(e);if(s.renderId===n)return s.lightsData}const a=this.getLightsData(e.getLights());return Jte.set(e,{renderId:n,lightsData:a}),a}needsRefresh(e,n){if(this.hasNode||this.hasAnimation||this.firstInitialization(e)||this.needsVelocity(n.renderer))return!0;const{renderId:a}=n;if(this.renderId!==a)return this.renderId=a,!0;const s=e.object.static===!0,r=e.bundle!==null&&e.bundle.static===!0&&this.getRenderObjectData(e).version===e.bundle.version;if(s||r)return!1;const o=this.getLights(e.lightsNode,a);return this.equals(e,o)!==!0}}const UMt=[/^StackTrace\.js$/,/^TSLCore\.js$/,/^.*Node\.js$/,/^three\.webgpu.*\.js$/];function GMt(t){const e=/(?:at\s+(.+?)\s+\()?(?:(.+?)@)?([^@\s()]+):(\d+):(\d+)/;return t.split(`
18518
+ `);c.textContent=i?Z8e(d):d,document.head.append(c)}if(i&&!document.querySelector("style[data-greenlight-chat]")){const c=document.querySelector("style[data-greenlight-motion]");c&&(c.textContent=Z8e(c.textContent));const d=document.createElement("style");d.setAttribute("data-greenlight-chat",""),d.textContent=Y9t,document.head.insertBefore(d,c||document.querySelector("style[data-greenlight-studio]"))}window.LabNoteEngine={...mMt,zipStore:jte};const l={el:t,assets:o};eSe=!0;for(const c of[gMt,xMt,yMt,vMt,bMt,_Mt,IMt])c(l);return i&&(l.chat=RMt(l,i)),l}const Kte="0.2.1",uz=()=>{};kxt(),z3t();function FMt({project:t,playerRef:e}){const n=t.videoSize||{w:1920,h:1080};return le.jsx("div",{style:{width:n.w,height:n.h,background:"#000"},children:le.jsx($Me,{ref:e,channels:t.channels||[],clips:t.clips||[],isPlaying:!1,setIsPlaying:uz,currentTime:0,playheadTime:0,setCurrentTime:uz,setPlayheadTime:uz,soundEnabled:!1,forceMuteAudio:!1,videoWidth:n.w,videoHeight:n.h,outputSize:null,previewQuality:"full",motionBlurEnabled:t.motionBlurEnabled===!0,background:t.background||null,markers:t.markers||[],textVariables:t.textVariables||[],timecodeFps:t.fps||30})})}const LMt=async(t,e,n)=>{const a=performance.now()+e;for(;!t();){if(performance.now()>a)throw new Error(`GreenLightMotion: timed out waiting for ${n}`);await new Promise(s=>setTimeout(s,30))}};async function nSe(t,{renderer:e="webgpu",assets:n=null}={}){if(!t)throw new Error("GreenLightMotion.create needs a container element");if(e==="webgpu"&&!navigator.gpu)throw new Error("This browser has no WebGPU");EY(e),HY(e);const a=o0e.createRoot(t),s={current:null};let r=!1,o=fte(n);OL(o);let i=null;const l=d=>pI({channels:i?.channels||[],clips:i?.clips||[],getPlayer:()=>s.current,range:d}),c=()=>{if(r)throw new Error("GreenLightMotion: disposed");if(!s.current)throw new Error("GreenLightMotion: load a project first");return s.current};return{version:Kte,async load(d,{assets:h}={}){if(!d||typeof d!="object")throw new Error("GreenLightMotion.load needs a project");h&&(o.dispose(),o=fte(h),OL(o)),U3t();const u=await vI(d),f=await t6e(u,o);return i=f,a.render(le.jsx(FMt,{project:f,playerRef:s})),await LMt(()=>s.current,1e4,"the player"),{missing:gI()}},missingAssets:gI,async renderFrame(d,h={}){const u=Math.max(0,Number(d)||0);return c(),await l({start:u,end:u+.001}),c().captureFrameAtTime(u,h)},isExportSupported(){try{return!!c().isWebCodecsSupported?.()}catch{return!1}},async export({format:d="mp4",fps:h=30,onProgress:u=uz,signal:f}={}){const p=c();if(!p.isWebCodecsSupported?.())throw new Error("This browser cannot encode video (WebCodecs)");return await l(null),p.exportVideo(d,u,f,h)},dispose(){r=!0,a.unmount(),o.dispose(),OL(null)}}}function aSe(t,{project:e,assets:n=null,theme:a="dark"}={}){if(!t)throw new Error("GreenLightMotion.mountPage needs an element");if(!e)throw new Error("GreenLightMotion.mountPage needs a project");EY("webgpu"),HY("webgpu");const s=o0e.createRoot(t);return s.render(le.jsx(z9t,{project:e,assets:n,theme:a})),{unmount:()=>s.unmount()}}window.GreenLightMotion={create:nSe,mountPage:aSe,mountStudio:tSe,checkTemplate:qMe,checkSoundCues:T6e,sounds:N4,missingAssets:gI,pluginStatus:e6e,normalize:vI,trace:async(t,e)=>G8e(await vI(t),e),version:Kte};const zMt=["alphaMap","alphaTest","anisotropy","anisotropyMap","anisotropyRotation","aoMap","aoMapIntensity","attenuationColor","attenuationDistance","bumpMap","clearcoat","clearcoatMap","clearcoatNormalMap","clearcoatNormalScale","clearcoatRoughness","color","dispersion","displacementMap","emissive","emissiveIntensity","emissiveMap","envMap","envMapIntensity","gradientMap","ior","iridescence","iridescenceIOR","iridescenceMap","iridescenceThicknessMap","lightMap","lightMapIntensity","map","matcap","metalness","metalnessMap","normalMap","normalScale","opacity","roughness","roughnessMap","sheen","sheenColor","sheenColorMap","sheenRoughnessMap","shininess","specular","specularColor","specularColorMap","specularIntensity","specularIntensityMap","specularMap","thickness","transmission","transmissionMap"],Jte=new WeakMap;class sSe{constructor(e){this.renderObjects=new WeakMap,this.hasNode=this.containsNode(e),this.hasAnimation=e.object.isSkinnedMesh===!0,this.refreshUniforms=zMt,this.renderId=0}firstInitialization(e){return this.renderObjects.has(e)===!1?(this.getRenderObjectData(e),!0):!1}needsVelocity(e){const n=e.getMRT();return n!==null&&n.has("velocity")}getRenderObjectData(e){let n=this.renderObjects.get(e);if(n===void 0){const{geometry:a,material:s,object:r}=e;if(n={material:this.getMaterialData(s),geometry:{id:a.id,attributes:this.getAttributesData(a.attributes),indexId:a.index?a.index.id:null,indexVersion:a.index?a.index.version:null,drawRange:{start:a.drawRange.start,count:a.drawRange.count}},worldMatrix:r.matrixWorld.clone()},r.center&&(n.center=r.center.clone()),r.morphTargetInfluences&&(n.morphTargetInfluences=r.morphTargetInfluences.slice()),e.bundle!==null&&(n.version=e.bundle.version),n.material.transmission>0){const{width:o,height:i}=e.context;n.bufferWidth=o,n.bufferHeight=i}n.lights=this.getLightsData(e.lightsNode.getLights()),this.renderObjects.set(e,n)}return n}getAttributesData(e){const n={};for(const a in e){const s=e[a];n[a]={id:s.id,version:s.version}}return n}containsNode(e){const n=e.material;for(const a in n)if(n[a]&&n[a].isNode)return!0;return!!(e.context.modelViewMatrix||e.context.modelNormalViewMatrix||e.context.getAO||e.context.getShadow)}getMaterialData(e){const n={};for(const a of this.refreshUniforms){const s=e[a];s!=null&&(typeof s=="object"&&s.clone!==void 0?s.isTexture===!0?n[a]={id:s.id,version:s.version}:n[a]=s.clone():n[a]=s)}return n}equals(e,n){const{object:a,material:s,geometry:r}=e,o=this.getRenderObjectData(e);if(o.worldMatrix.equals(a.matrixWorld)!==!0)return o.worldMatrix.copy(a.matrixWorld),!1;const i=o.material;for(const v in i){const b=i[v],M=s[v];if(b.equals!==void 0){if(b.equals(M)===!1)return b.copy(M),!1}else if(M.isTexture===!0){if(b.id!==M.id||b.version!==M.version)return b.id=M.id,b.version=M.version,!1}else if(b!==M)return i[v]=M,!1}if(i.transmission>0){const{width:v,height:b}=e.context;if(o.bufferWidth!==v||o.bufferHeight!==b)return o.bufferWidth=v,o.bufferHeight=b,!1}const l=o.geometry,c=r.attributes,d=l.attributes,h=Object.keys(d),u=Object.keys(c);if(l.id!==r.id)return l.id=r.id,!1;if(h.length!==u.length)return o.geometry.attributes=this.getAttributesData(c),!1;for(const v of h){const b=d[v],M=c[v];if(M===void 0)return delete d[v],!1;if(b.id!==M.id||b.version!==M.version)return b.id=M.id,b.version=M.version,!1}const f=r.index,p=l.indexId,m=l.indexVersion,g=f?f.id:null,x=f?f.version:null;if(p!==g||m!==x)return l.indexId=g,l.indexVersion=x,!1;if(l.drawRange.start!==r.drawRange.start||l.drawRange.count!==r.drawRange.count)return l.drawRange.start=r.drawRange.start,l.drawRange.count=r.drawRange.count,!1;if(o.morphTargetInfluences){let v=!1;for(let b=0;b<o.morphTargetInfluences.length;b++)o.morphTargetInfluences[b]!==a.morphTargetInfluences[b]&&(o.morphTargetInfluences[b]=a.morphTargetInfluences[b],v=!0);if(v)return!1}if(o.lights){for(let v=0;v<n.length;v++)if(o.lights[v].map!==n[v].map)return!1}return o.center&&o.center.equals(a.center)===!1?(o.center.copy(a.center),!0):(e.bundle!==null&&(o.version=e.bundle.version),!0)}getLightsData(e){const n=[];for(const a of e)a.isSpotLight===!0&&a.map!==null&&n.push({map:a.map.version});return n}getLights(e,n){if(Jte.has(e)){const s=Jte.get(e);if(s.renderId===n)return s.lightsData}const a=this.getLightsData(e.getLights());return Jte.set(e,{renderId:n,lightsData:a}),a}needsRefresh(e,n){if(this.hasNode||this.hasAnimation||this.firstInitialization(e)||this.needsVelocity(n.renderer))return!0;const{renderId:a}=n;if(this.renderId!==a)return this.renderId=a,!0;const s=e.object.static===!0,r=e.bundle!==null&&e.bundle.static===!0&&this.getRenderObjectData(e).version===e.bundle.version;if(s||r)return!1;const o=this.getLights(e.lightsNode,a);return this.equals(e,o)!==!0}}const UMt=[/^StackTrace\.js$/,/^TSLCore\.js$/,/^.*Node\.js$/,/^three\.webgpu.*\.js$/];function GMt(t){const e=/(?:at\s+(.+?)\s+\()?(?:(.+?)@)?([^@\s()]+):(\d+):(\d+)/;return t.split(`
18519
18519
  `).map(n=>{const a=n.match(e);if(!a)return null;const s=a[1]||a[2]||"",r=a[3].split("?")[0],o=parseInt(a[4],10),i=parseInt(a[5],10),l=r.split("/").pop();return{fn:s,file:l,line:o,column:i}}).filter(n=>n&&!UMt.some(a=>a.test(n.file)))}class Kl{constructor(e=null){this.isStackTrace=!0,this.stack=GMt(e||new Error().stack)}getLocation(){if(this.stack.length===0)return"[Unknown location]";const e=this.stack[0],n=e.fn;return`${n?`"${n}()" at `:""}"${e.file}:${e.line}"`}getError(e){if(this.stack.length===0)return e;const n=this.stack.map(a=>{const s=`${a.file}:${a.line}:${a.column}`;return a.fn?` at ${a.fn} (${s})`:` at ${s}`}).join(`
18520
18520
  `);return`${e}
18521
18521
  ${n}`}}function Zte(t,e=0){let n=3735928559^e,a=1103547991^e;if(t instanceof Array)for(let s=0,r;s<t.length;s++)r=t[s],n=Math.imul(n^r,2654435761),a=Math.imul(a^r,1597334677);else for(let s=0,r;s<t.length;s++)r=t.charCodeAt(s),n=Math.imul(n^r,2654435761),a=Math.imul(a^r,1597334677);return n=Math.imul(n^n>>>16,2246822507),n^=Math.imul(a^a>>>13,3266489909),a=Math.imul(a^a>>>16,2246822507),a^=Math.imul(n^n>>>13,3266489909),4294967296*(2097151&a)+(n>>>0)}const F4=t=>Zte(t),T8=t=>Zte(t),C8=(...t)=>Zte(t),jMt=new Map([[1,"float"],[2,"vec2"],[3,"vec3"],[4,"vec4"],[9,"mat3"],[16,"mat4"]]),rSe=new WeakMap;function ene(t){return jMt.get(t)}function II(t){if(/[iu]?vec\d/.test(t))return t.startsWith("ivec")?Int32Array:t.startsWith("uvec")?Uint32Array:Float32Array;if(/mat\d/.test(t)||/float/.test(t))return Float32Array;if(/uint/.test(t))return Uint32Array;if(/int/.test(t))return Int32Array;throw new Error(`THREE.NodeUtils: Unsupported type: ${t}`)}function tne(t){if(/float|int|uint/.test(t))return 1;if(/vec2/.test(t))return 2;if(/vec3/.test(t))return 3;if(/vec4/.test(t)||/mat2/.test(t))return 4;if(/mat3/.test(t))return 9;if(/mat4/.test(t))return 16;On(`TSL: Unsupported type: ${t}`,new Kl)}function oSe(t){if(/float|int|uint/.test(t))return 1;if(/vec2/.test(t))return 2;if(/vec3/.test(t))return 3;if(/vec4/.test(t)||/mat2/.test(t))return 4;if(/mat3/.test(t))return 12;if(/mat4/.test(t))return 16;On(`TSL: Unsupported type: ${t}`,new Kl)}function iSe(t){if(/float|int|uint/.test(t))return 4;if(/vec2/.test(t))return 8;if(/vec3/.test(t)||/vec4/.test(t))return 16;if(/mat2/.test(t))return 8;if(/mat3/.test(t)||/mat4/.test(t))return 16;On(`TSL: Unsupported type: ${t}`,new Kl)}function cx(t){if(t==null)return null;const e=typeof t;return t.isNode===!0?"node":e==="number"?"float":e==="boolean"?"bool":e==="string"?"string":e==="function"?"shader":t.isVector2===!0?"vec2":t.isVector3===!0?"vec3":t.isVector4===!0?"vec4":t.isMatrix2===!0?"mat2":t.isMatrix3===!0?"mat3":t.isMatrix4===!0?"mat4":t.isColor===!0?"color":t instanceof ArrayBuffer?"ArrayBuffer":null}function dz(t,...e){const n=t?t.slice(-4):void 0;return e.length===1&&(n==="vec2"?e=[e[0],e[0]]:n==="vec3"?e=[e[0],e[0],e[0]]:n==="vec4"&&(e=[e[0],e[0],e[0],e[0]])),t==="color"?new Ht(...e):n==="vec2"?new Te(...e):n==="vec3"?new Ie(...e):n==="vec4"?new Vn(...e):n==="mat2"?new eM(...e):n==="mat3"?new Pa(...e):n==="mat4"?new Xn(...e):t==="bool"?e[0]||!1:t==="float"||t==="int"||t==="uint"?e[0]||0:t==="string"?e[0]||"":t==="ArrayBuffer"?cSe(e[0]):null}function nne(t){let e=rSe.get(t);return e===void 0&&(e={},rSe.set(t,e)),e}function lSe(t){let e="";const n=new Uint8Array(t);for(let a=0;a<n.length;a++)e+=String.fromCharCode(n[a]);return btoa(e)}function cSe(t){return Uint8Array.from(atob(t),e=>e.charCodeAt(0)).buffer}var VMt=Object.freeze({__proto__:null,arrayBufferToBase64:lSe,base64ToArrayBuffer:cSe,getAlignmentFromType:iSe,getDataFromObject:nne,getLengthFromType:tne,getMemoryLengthFromType:oSe,getTypeFromLength:ene,getTypedArrayFromType:II,getValueFromType:dz,getValueType:cx,hash:C8,hashArray:T8,hashString:F4});const _8={VERTEX:"vertex",FRAGMENT:"fragment"},Hs={NONE:"none",FRAME:"frame",RENDER:"render",OBJECT:"object"},uSe={BOOLEAN:"bool",INTEGER:"int",FLOAT:"float",VECTOR2:"vec2",VECTOR3:"vec3",VECTOR4:"vec4",MATRIX2:"mat2",MATRIX3:"mat3",MATRIX4:"mat4"},jc={READ_ONLY:"readOnly",WRITE_ONLY:"writeOnly",READ_WRITE:"readWrite"},ane=["fragment","vertex"],fz=["setup","analyze","generate"],hz=[...ane,"compute"],eb=["x","y","z","w"],HMt={analyze:"setup",generate:"analyze"};let WMt=0;class Ka extends Qd{static get type(){return"Node"}constructor(e=null){super(),this.nodeType=e,this.updateType=Hs.NONE,this.updateBeforeType=Hs.NONE,this.updateAfterType=Hs.NONE,this.uuid=eu.generateUUID(),this.version=0,this.name="",this.global=!1,this.parents=!1,this.isNode=!0,this._beforeNodes=null,this._cacheKey=null,this._cacheKeyVersion=0,Object.defineProperty(this,"id",{value:WMt++}),this.stackTrace=null,Ka.captureStackTrace===!0&&(this.stackTrace=new Kl)}set needsUpdate(e){e===!0&&this.version++}get type(){return this.constructor.type}onUpdate(e,n){return this.updateType=n,this.update=e.bind(this),this}onFrameUpdate(e){return this.onUpdate(e,Hs.FRAME)}onRenderUpdate(e){return this.onUpdate(e,Hs.RENDER)}onObjectUpdate(e){return this.onUpdate(e,Hs.OBJECT)}onReference(e){return this.updateReference=e.bind(this),this}updateReference(){return this}isGlobal(){return this.global}*getChildren(){for(const{childNode:e}of this._getChildren())yield e}dispose(){this.dispatchEvent({type:"dispose"})}traverse(e){e(this);for(const n of this.getChildren())n.traverse(e)}_getChildren(e=new Set){const n=[];e.add(this);for(const a of Object.getOwnPropertyNames(this)){const s=this[a];if(!(a.startsWith("_")===!0||e.has(s))){if(Array.isArray(s)===!0)for(let r=0;r<s.length;r++){const o=s[r];o&&o.isNode===!0&&n.push({property:a,index:r,childNode:o})}else if(s&&s.isNode===!0)n.push({property:a,childNode:s});else if(s&&Object.getPrototypeOf(s)===Object.prototype)for(const r in s){if(r.startsWith("_")===!0)continue;const o=s[r];o&&o.isNode===!0&&n.push({property:a,index:r,childNode:o})}}}return n}getCacheKey(e=!1,n=null){if(e=e||this.version!==this._cacheKeyVersion,e===!0||this._cacheKey===null){n===null&&(n=new Set);const a=[];for(const{property:s,childNode:r}of this._getChildren(n))a.push(F4(s.slice(0,-4)),r.getCacheKey(e,n));this._cacheKey=C8(T8(a),this.customCacheKey()),this._cacheKeyVersion=this.version}return this._cacheKey}customCacheKey(){return this.id}getScope(){return this}getHash(){return this.uuid}getUpdateType(){return this.updateType}getUpdateBeforeType(){return this.updateBeforeType}getUpdateAfterType(){return this.updateAfterType}getElementType(e){const n=this.getNodeType(e);return e.getElementType(n)}getMemberType(){return"void"}getNodeType(e){const n=e.getNodeProperties(this);return n.outputNode?n.outputNode.getNodeType(e):this.nodeType}getShared(e){const n=this.getHash(e);return e.getNodeFromHash(n)||this}getArrayCount(){return null}setup(e){const n=e.getNodeProperties(this);let a=0;for(const s of this.getChildren())n["node"+a++]=s;return n.outputNode||null}analyze(e,n=null){const a=e.increaseUsage(this);if(this.parents===!0){const s=e.getDataFromNode(this,"any");s.stages=s.stages||{},s.stages[e.shaderStage]=s.stages[e.shaderStage]||[],s.stages[e.shaderStage].push(n)}if(a===1){const s=e.getNodeProperties(this);for(const r of Object.values(s))r&&r.isNode===!0&&r.build(e,this)}}generate(e,n){const{outputNode:a}=e.getNodeProperties(this);if(a&&a.isNode===!0)return a.build(e,n)}updateBefore(){Nn("Abstract function.")}updateAfter(){Nn("Abstract function.")}update(){Nn("Abstract function.")}before(e){return this._beforeNodes===null&&(this._beforeNodes=[]),this._beforeNodes.push(e),this}build(e,n=null){const a=this.getShared(e);if(this!==a)return a.build(e,n);if(this._beforeNodes!==null){const l=this._beforeNodes;this._beforeNodes=null;for(const c of l)c.build(e,n);this._beforeNodes=l}const s=e.getDataFromNode(this);s.buildStages=s.buildStages||{},s.buildStages[e.buildStage]=!0;const r=HMt[e.buildStage];if(r&&s.buildStages[r]!==!0){const l=e.getBuildStage();e.setBuildStage(r),this.build(e),e.setBuildStage(l)}e.addChain(this);let o=null;const i=e.getBuildStage();if(i==="setup"){e.addNode(this),this.updateReference(e);const l=e.getNodeProperties(this);if(l.initialized!==!0){l.initialized=!0,l.outputNode=this.setup(e)||l.outputNode||null;for(const c of Object.values(l))if(c&&c.isNode===!0){if(c.parents===!0){const d=e.getNodeProperties(c);d.parents=d.parents||[],d.parents.push(this)}c.build(e)}e.addSequentialNode(this)}o=l.outputNode}else if(i==="analyze")this.analyze(e,n);else if(i==="generate"){if(this.generate.length<2){const c=this.getNodeType(e),d=e.getDataFromNode(this);o=d.snippet,o===void 0?d.generated===void 0?(d.generated=!0,o=this.generate(e)||"",d.snippet=o):(Nn("Node: Recursion detected.",this),o="/* Recursion detected. */"):d.flowCodes!==void 0&&e.context.nodeBlock!==void 0&&e.addFlowCodeHierarchy(this,e.context.nodeBlock),o=e.format(o,c,n)}else o=this.generate(e,n)||"";o===""&&n!==null&&n!=="void"&&n!=="OutputType"&&(On(`TSL: Invalid generated code, expected a "${n}".`),o=e.generateConst(n))}return e.removeChain(this),o}getSerializeChildren(){return this._getChildren()}serialize(e){const n=this.getSerializeChildren(),a={};for(const{property:s,index:r,childNode:o}of n)r!==void 0?(a[s]===void 0&&(a[s]=Number.isInteger(r)?[]:{}),a[s][r]=o.toJSON(e.meta).uuid):a[s]=o.toJSON(e.meta).uuid;Object.keys(a).length>0&&(e.inputNodes=a)}deserialize(e){if(e.inputNodes!==void 0){const n=e.meta.nodes;for(const a in e.inputNodes)if(Array.isArray(e.inputNodes[a])){const s=[];for(const r of e.inputNodes[a])s.push(n[r]);this[a]=s}else if(typeof e.inputNodes[a]=="object"){const s={};for(const r in e.inputNodes[a]){const o=e.inputNodes[a][r];s[r]=n[o]}this[a]=s}else{const s=e.inputNodes[a];this[a]=n[s]}}}toJSON(e){const{uuid:n,type:a}=this,s=e===void 0||typeof e=="string";s&&(e={textures:{},images:{},nodes:{}});let r=e.nodes[n];r===void 0&&(r={uuid:n,type:a,meta:e,metadata:{version:4.7,type:"Node",generator:"Node.toJSON"}},s!==!0&&(e.nodes[r.uuid]=r),this.serialize(r),delete r.meta);function o(i){const l=[];for(const c in i){const d=i[c];delete d.metadata,l.push(d)}return l}if(s){const i=o(e.textures),l=o(e.images),c=o(e.nodes);i.length>0&&(r.textures=i),l.length>0&&(r.images=l),c.length>0&&(r.nodes=c)}return r}}Ka.captureStackTrace=!1;class tb extends Ka{static get type(){return"ArrayElementNode"}constructor(e,n){super(),this.node=e,this.indexNode=n,this.isArrayElementNode=!0}getNodeType(e){return this.node.getElementType(e)}getMemberType(e,n){return this.node.getMemberType(e,n)}generate(e){const n=this.indexNode.getNodeType(e),a=this.node.build(e),s=this.indexNode.build(e,!e.isVector(n)&&e.isInteger(n)?n:"uint");return`${a}[ ${s} ]`}}class sne extends Ka{static get type(){return"ConvertNode"}constructor(e,n){super(),this.node=e,this.convertTo=n}getNodeType(e){const n=this.node.getNodeType(e);let a=null;for(const s of this.convertTo.split("|"))(a===null||e.getTypeLength(n)===e.getTypeLength(s))&&(a=s);return a}serialize(e){super.serialize(e),e.convertTo=this.convertTo}deserialize(e){super.deserialize(e),this.convertTo=e.convertTo}generate(e,n){const a=this.node,s=this.getNodeType(e),r=a.build(e,s);return e.format(r,s,n)}}class Pl extends Ka{static get type(){return"TempNode"}constructor(e=null){super(e),this.isTempNode=!0}hasDependencies(e){return e.getDataFromNode(this).usageCount>1}build(e,n){if(e.getBuildStage()==="generate"){const s=e.getVectorType(this.getNodeType(e,n)),r=e.getDataFromNode(this);if(r.propertyName!==void 0)return e.format(r.propertyName,s,n);if(s!=="void"&&n!=="void"&&this.hasDependencies(e)){const o=super.build(e,s),i=e.getVarFromNode(this,null,s),l=e.getPropertyName(i);return e.addLineFlowCode(`${l} = ${o}`,this),r.snippet=o,r.propertyName=l,e.format(r.propertyName,s,n)}}return super.build(e,n)}}class dSe extends Pl{static get type(){return"JoinNode"}constructor(e=[],n=null){super(n),this.nodes=e}getNodeType(e){return this.nodeType!==null?e.getVectorType(this.nodeType):e.getTypeFromLength(this.nodes.reduce((n,a)=>n+e.getTypeLength(a.getNodeType(e)),0))}generate(e,n){const a=this.getNodeType(e),s=e.getTypeLength(a),r=this.nodes,o=e.getComponentType(a),i=[];let l=0;for(const d of r){if(l>=s){On(`TSL: Length of parameters exceeds maximum length of function '${a}()' type.`,this.stackTrace);break}let h=d.getNodeType(e),u=e.getTypeLength(h),f;if(l+u>s&&(On(`TSL: Length of '${a}()' data exceeds maximum length of output type.`,this.stackTrace),u=s-l,h=e.getTypeFromLength(u)),l+=u,f=d.build(e,h),e.getComponentType(h)!==o){const m=e.getTypeFromLength(u,o);f=e.format(f,h,m)}i.push(f)}const c=`${e.getType(a)}( ${i.join(", ")} )`;return e.format(c,a,n)}}const $Mt=eb.join("");class rne extends Ka{static get type(){return"SplitNode"}constructor(e,n="x"){super(),this.node=e,this.components=n,this.isSplitNode=!0}getVectorLength(){let e=this.components.length;for(const n of this.components)e=Math.max(eb.indexOf(n)+1,e);return e}getComponentType(e){return e.getComponentType(this.node.getNodeType(e))}getNodeType(e){return e.getTypeFromLength(this.components.length,this.getComponentType(e))}getScope(){return this.node.getScope()}generate(e,n){const a=this.node,s=e.getTypeLength(a.getNodeType(e));let r=null;if(s>1){let o=null;this.getVectorLength()>=s&&(o=e.getTypeFromLength(this.getVectorLength(),this.getComponentType(e)));const l=a.build(e,o);this.components.length===s&&this.components===$Mt.slice(0,this.components.length)?r=e.format(l,o,n):r=e.format(`${l}.${this.components}`,this.getNodeType(e),n)}else r=a.build(e,n);return r}serialize(e){super.serialize(e),e.components=this.components}deserialize(e){super.deserialize(e),this.components=e.components}}class fSe extends Pl{static get type(){return"SetNode"}constructor(e,n,a){super(),this.sourceNode=e,this.components=n,this.targetNode=a}getNodeType(e){return this.sourceNode.getNodeType(e)}generate(e){const{sourceNode:n,components:a,targetNode:s}=this,r=this.getNodeType(e),o=e.getComponentType(s.getNodeType(e)),i=e.getTypeFromLength(a.length,o),l=s.build(e,i),c=n.build(e,r),d=e.getTypeLength(r),h=[];for(let u=0;u<d;u++){const f=eb[u];f===a[0]?(h.push(l),u+=a.length-1):h.push(c+"."+f)}return`${e.getType(r)}( ${h.join(", ")} )`}}class hSe extends Pl{static get type(){return"FlipNode"}constructor(e,n){super(),this.sourceNode=e,this.components=n}getNodeType(e){return this.sourceNode.getNodeType(e)}generate(e){const{components:n,sourceNode:a}=this,s=this.getNodeType(e),r=a.build(e),o=e.getVarFromNode(this),i=e.getPropertyName(o);e.addLineFlowCode(i+" = "+r,this);const l=e.getTypeLength(s),c=[];let d=0;for(let h=0;h<l;h++){const u=eb[h];u===n[d]?(c.push("1.0 - "+(i+"."+u)),d++):c.push(i+"."+u)}return`${e.getType(s)}( ${c.join(", ")} )`}}class pz extends Ka{static get type(){return"InputNode"}constructor(e,n=null){super(n),this.isInputNode=!0,this.value=e,this.precision=null}getNodeType(){return this.nodeType===null?cx(this.value):this.nodeType}getInputType(e){return this.getNodeType(e)}setPrecision(e){return this.precision=e,this}serialize(e){super.serialize(e),e.value=this.value,this.value&&this.value.toArray&&(e.value=this.value.toArray()),e.valueType=cx(this.value),e.nodeType=this.nodeType,e.valueType==="ArrayBuffer"&&(e.value=lSe(e.value)),e.precision=this.precision}deserialize(e){super.deserialize(e),this.nodeType=e.nodeType,this.value=Array.isArray(e.value)?dz(e.valueType,...e.value):e.value,this.precision=e.precision||null,this.value&&this.value.fromArray&&(this.value=this.value.fromArray(e.value))}generate(){Nn("Abstract function.")}}const pSe=/float|u?int/;class fh extends pz{static get type(){return"ConstNode"}constructor(e,n=null){super(e,n),this.isConstNode=!0}generateConst(e){return e.generateConst(this.getNodeType(e),this.value)}generate(e,n){const a=this.getNodeType(e);return pSe.test(a)&&pSe.test(n)?e.generateConst(n,this.value):e.format(this.generateConst(e),a,n)}}class mSe extends Ka{static get type(){return"MemberNode"}constructor(e,n){super(),this.structNode=e,this.property=n,this.isMemberNode=!0}hasMember(e){return this.structNode.isMemberNode&&this.structNode.hasMember(e)===!1?!1:this.structNode.getMemberType(e,this.property)!=="void"}getNodeType(e){return this.hasMember(e)===!1?"float":this.structNode.getMemberType(e,this.property)}getMemberType(e,n){if(this.hasMember(e)===!1)return"float";const a=this.getNodeType(e);return e.getStructTypeNode(a).getMemberType(e,n)}generate(e){if(this.hasMember(e)===!1){Nn(`TSL: Member "${this.property}" does not exist in struct.`,this.stackTrace);const a=this.getNodeType(e);return e.generateConst(a)}return this.structNode.build(e)+"."+this.property}}let nb=null;const one=new Map;function Hn(t,e){if(one.has(t)){Nn(`TSL: Redefinition of method chaining '${t}'.`);return}if(typeof e!="function")throw new Error(`THREE.TSL: Node element ${t} is not a function`);one.set(t,e),t!=="assign"&&(Ka.prototype[t]=function(...n){return this.isStackNode?this.addToStack(e(...n)):e(this,...n)},Ka.prototype[t+"Assign"]=function(...n){return this.isStackNode?this.assign(n[0],e(...n)):this.assign(e(this,...n))})}const YMt=t=>t.replace(/r|s/g,"x").replace(/g|t/g,"y").replace(/b|p/g,"z").replace(/a|q/g,"w"),gSe=t=>YMt(t).split("").sort().join("");Ka.prototype.assign=function(...t){if(this.isStackNode!==!0)return nb!==null?nb.assign(this,...t):On("TSL: No stack defined for assign operation. Make sure the assign is inside a Fn().",new Kl),this;{const e=one.get("assign");return this.addToStack(e(...t))}},Ka.prototype.toVarIntent=function(){return this},Ka.prototype.get=function(t){return new mSe(this,t)};const EI={};function mz(t,e,n){EI[t]=EI[e]=EI[n]={get(){this._cache=this._cache||{};let o=this._cache[t];return o===void 0&&(o=new rne(this,t),this._cache[t]=o),o},set(o){this[t].assign(Ja(o))}};const a=t.toUpperCase(),s=e.toUpperCase(),r=n.toUpperCase();Ka.prototype["set"+a]=Ka.prototype["set"+s]=Ka.prototype["set"+r]=function(o){const i=gSe(t);return new fSe(this,i,Ja(o))},Ka.prototype["flip"+a]=Ka.prototype["flip"+s]=Ka.prototype["flip"+r]=function(){const o=gSe(t);return new hSe(this,o)}}const K1=["x","y","z","w"],J1=["r","g","b","a"],Z1=["s","t","p","q"];for(let t=0;t<4;t++){let e=K1[t],n=J1[t],a=Z1[t];mz(e,n,a);for(let s=0;s<4;s++){e=K1[t]+K1[s],n=J1[t]+J1[s],a=Z1[t]+Z1[s],mz(e,n,a);for(let r=0;r<4;r++){e=K1[t]+K1[s]+K1[r],n=J1[t]+J1[s]+J1[r],a=Z1[t]+Z1[s]+Z1[r],mz(e,n,a);for(let o=0;o<4;o++)e=K1[t]+K1[s]+K1[r]+K1[o],n=J1[t]+J1[s]+J1[r]+J1[o],a=Z1[t]+Z1[s]+Z1[r]+Z1[o],mz(e,n,a)}}}for(let t=0;t<32;t++)EI[t]={get(){this._cache=this._cache||{};let e=this._cache[t];return e===void 0&&(e=new tb(this,new fh(t,"uint")),this._cache[t]=e),e},set(e){this[t].assign(Ja(e))}};Object.defineProperties(Ka.prototype,EI);const xSe=new WeakMap,qMt=function(t,e=null){const n=cx(t);return n==="node"?t:e===null&&(n==="float"||n==="boolean")||n&&n!=="shader"&&n!=="string"?Ja(une(t,e)):n==="shader"?t.isFn?t:Kt(t):t},XMt=function(t,e=null){for(const n in t)t[n]=Ja(t[n],e);return t},QMt=function(t,e=null){const n=t.length;for(let a=0;a<n;a++)t[a]=Ja(t[a],e);return t},ySe=function(t,e=null,n=null,a=null){function s(d){return a!==null?(d=Ja(Object.assign(d,a)),a.intent===!0&&(d=d.toVarIntent())):d=Ja(d),d}let r,o=e,i,l;function c(d){let h;return o?h=/[a-z]/i.test(o)?o+"()":o:h=t.type,i!==void 0&&d.length<i?(On(`TSL: "${h}" parameter length is less than minimum required.`,new Kl),d.concat(new Array(i-d.length).fill(0))):l!==void 0&&d.length>l?(On(`TSL: "${h}" parameter length exceeds limit.`,new Kl),d.slice(0,l)):d}return e===null?r=(...d)=>s(new t(...L4(c(d)))):n!==null?(n=Ja(n),r=(...d)=>s(new t(e,...L4(c(d)),n))):r=(...d)=>s(new t(e,...L4(c(d)))),r.setParameterLength=(...d)=>(d.length===1?i=l=d[0]:d.length===2&&([i,l]=d),r),r.setName=d=>(o=d,r),r},KMt=function(t,...e){return new t(...L4(e))};class JMt extends Ka{constructor(e,n){super(),this.shaderNode=e,this.rawInputs=n,this.isShaderCallNodeInternal=!0}getNodeType(e){return this.shaderNode.nodeType||this.getOutputNode(e).getNodeType(e)}getElementType(e){return this.getOutputNode(e).getElementType(e)}getMemberType(e,n){return this.getOutputNode(e).getMemberType(e,n)}call(e){const{shaderNode:n,rawInputs:a}=this,s=e.getNodeProperties(n),r=e.getClosestSubBuild(n.subBuilds)||"",o=r||"default";if(s[o])return s[o];const i=e.subBuildFn,l=e.fnCall;e.subBuildFn=r,e.fnCall=this;let c=null;if(n.layout){let d=xSe.get(e.constructor);d===void 0&&(d=new WeakMap,xSe.set(e.constructor,d));let h=d.get(n);h===void 0&&(h=Ja(e.buildFunctionNode(n)),d.set(n,h)),e.addInclude(h);const u=a?ZMt(a):null;c=Ja(h.call(u))}else{const d=new Proxy(e,{get:(m,g,x)=>{let v;return Symbol.iterator===g?v=function*(){yield void 0}:v=Reflect.get(m,g,x),v}}),h=a?e6t(a):null,u=Array.isArray(a)?a.length>0:a!==null,f=n.jsFunc,p=u||f.length>1?f(h,d):f(d);c=Ja(p)}return e.subBuildFn=i,e.fnCall=l,n.once&&(s[o]=c),c}setupOutput(e){return e.addStack(),e.stack.outputNode=this.call(e),e.removeStack()}getOutputNode(e){const n=e.getNodeProperties(this),a=e.getSubBuildOutput(this);return n[a]=n[a]||this.setupOutput(e),n[a].subBuild=e.getClosestSubBuild(this),n[a]}build(e,n=null){let a=null;const s=e.getBuildStage(),r=e.getNodeProperties(this),o=e.getSubBuildOutput(this),i=this.getOutputNode(e),l=e.fnCall;if(e.fnCall=this,s==="setup"){const c=e.getSubBuildProperty("initialized",this);if(r[c]!==!0&&(r[c]=!0,r[o]=this.getOutputNode(e),r[o].build(e),this.shaderNode.subBuilds))for(const d of e.chaining){const h=e.getDataFromNode(d,"any");h.subBuilds=h.subBuilds||new Set;for(const u of this.shaderNode.subBuilds)h.subBuilds.add(u)}a=r[o]}else s==="analyze"?i.build(e,n):s==="generate"&&(a=i.build(e,n)||"");return e.fnCall=l,a}}function ZMt(t){let e;return yz(t),t[0]&&(t[0].isNode||Object.getPrototypeOf(t[0])!==Object.prototype)?e=[...t]:e=t[0],e}function e6t(t){let e=0;return yz(t),new Proxy(t,{get:(n,a,s)=>{let r;if(a==="length")return r=t.length,r;if(Symbol.iterator===a)r=function*(){for(const o of t)yield Ja(o)};else{if(t.length>0)if(Object.getPrototypeOf(t[0])===Object.prototype){const o=t[0];o[a]===void 0?r=o[e++]:r=Reflect.get(o,a,s)}else t[0]instanceof Ka&&(t[a]===void 0?r=t[e++]:r=Reflect.get(t,a,s));else r=Reflect.get(n,a,s);r=Ja(r)}return r}})}class t6t extends Ka{constructor(e,n){super(n),this.jsFunc=e,this.layout=null,this.global=!0,this.once=!1}setLayout(e){return this.layout=e,this}getLayout(){return this.layout}call(e=null){return new JMt(this,e)}setup(){return this.call()}}const n6t=[!1,!0],a6t=[0,1,2,3],s6t=[-1,-2],vSe=[.5,1.5,1/3,1e-6,1e6,Math.PI,Math.PI*2,1/Math.PI,2/Math.PI,1/(Math.PI*2),Math.PI/2],ine=new Map;for(const t of n6t)ine.set(t,new fh(t));const lne=new Map;for(const t of a6t)lne.set(t,new fh(t,"uint"));const cne=new Map([...lne].map(t=>new fh(t.value,"int")));for(const t of s6t)cne.set(t,new fh(t,"int"));const gz=new Map([...cne].map(t=>new fh(t.value)));for(const t of vSe)gz.set(t,new fh(t));for(const t of vSe)gz.set(-t,new fh(-t));const xz={bool:ine,uint:lne,ints:cne,float:gz},bSe=new Map([...ine,...gz]),une=(t,e)=>bSe.has(t)?bSe.get(t):t.isNode===!0?t:new fh(t,e),X0=function(t,e=null){return(...n)=>{for(const s of n)if(s===void 0)return On(`TSL: Invalid parameter for the type "${t}".`,new Kl),new fh(0,t);if((n.length===0||!["bool","float","int","uint"].includes(t)&&n.every(s=>{const r=typeof s;return r!=="object"&&r!=="function"}))&&(n=[dz(t,...n)]),n.length===1&&e!==null&&e.has(n[0]))return DI(e.get(n[0]));if(n.length===1){const s=une(n[0],t);return s.nodeType===t?DI(s):DI(new sne(s,t))}const a=n.map(s=>une(s));return DI(new dSe(a,t))}},kI=t=>typeof t=="object"&&t!==null?t.value:t,wSe=t=>t!=null?t.nodeType||t.convertTo||(typeof t=="string"?t:null):null;function I8(t,e){return new t6t(t,e)}const Ja=(t,e=null)=>qMt(t,e),DI=(t,e=null)=>Ja(t,e).toVarIntent(),yz=(t,e=null)=>new XMt(t,e),L4=(t,e=null)=>new QMt(t,e),hr=(t,e=null,n=null,a=null)=>new ySe(t,e,n,a),_a=(t,...e)=>new KMt(t,...e),ba=(t,e=null,n=null,a={})=>new ySe(t,e,n,{...a,intent:!0});let r6t=0;class o6t extends Ka{constructor(e,n=null){super();let a=null;n!==null&&(typeof n=="object"?a=n.return:(typeof n=="string"?a=n:On("TSL: Invalid layout type.",new Kl),n=null)),this.shaderNode=new I8(e,a),n!==null&&this.setLayout(n),this.isFn=!0}setLayout(e){const n=this.shaderNode.nodeType;if(typeof e.inputs!="object"){const a={name:"fn"+r6t++,type:n,inputs:[]};for(const s in e)s!=="return"&&a.inputs.push({name:s,type:e[s]});e=a}return this.shaderNode.setLayout(e),this}getNodeType(e){return this.shaderNode.getNodeType(e)||"float"}call(...e){const n=this.shaderNode.call(e);return this.shaderNode.nodeType==="void"&&n.toStack(),n.toVarIntent()}once(e=null){return this.shaderNode.once=!0,this.shaderNode.subBuilds=e,this}generate(e){const n=this.getNodeType(e);return On('TSL: "Fn()" was declared but not invoked. Try calling it like "Fn()( ...params )".',this.stackTrace),e.generateConst(n)}}function Kt(t,e=null){const n=new o6t(t,e);return new Proxy(()=>{},{apply(a,s,r){return n.call(...r)},get(a,s,r){return Reflect.get(n,s,r)},set(a,s,r,o){return Reflect.set(n,s,r,o)}})}const NI=t=>{nb=t},dne=()=>nb,ns=(...t)=>nb.If(...t),i6t=(...t)=>nb.Switch(...t);function vz(t){return nb&&nb.addToStack(t),t}Hn("toStack",vz);const MSe=new X0("color"),bt=new X0("float",xz.float),Gt=new X0("int",xz.ints),ia=new X0("uint",xz.uint),ep=new X0("bool",xz.bool),ha=new X0("vec2"),Q0=new X0("ivec2"),fne=new X0("uvec2"),SSe=new X0("bvec2"),$t=new X0("vec3"),hne=new X0("ivec3"),z4=new X0("uvec3"),pne=new X0("bvec3"),ja=new X0("vec4"),mne=new X0("ivec4"),gne=new X0("uvec4"),ASe=new X0("bvec4"),bz=new X0("mat2"),Vc=new X0("mat3"),ab=new X0("mat4"),l6t=(t="")=>new fh(t,"string"),c6t=t=>new fh(t,"ArrayBuffer");Hn("toColor",MSe),Hn("toFloat",bt),Hn("toInt",Gt),Hn("toUint",ia),Hn("toBool",ep),Hn("toVec2",ha),Hn("toIVec2",Q0),Hn("toUVec2",fne),Hn("toBVec2",SSe),Hn("toVec3",$t),Hn("toIVec3",hne),Hn("toUVec3",z4),Hn("toBVec3",pne),Hn("toVec4",ja),Hn("toIVec4",mne),Hn("toUVec4",gne),Hn("toBVec4",ASe),Hn("toMat2",bz),Hn("toMat3",Vc),Hn("toMat4",ab);const TSe=hr(tb).setParameterLength(2),CSe=(t,e)=>new sne(Ja(t),e),u6t=(t,e)=>new rne(Ja(t),e);Hn("element",TSe),Hn("convert",CSe);const d6t=t=>(Nn("TSL: append() has been renamed to Stack().",new Kl),vz(t));Hn("append",t=>(Nn("TSL: .append() has been renamed to .toStack().",new Kl),vz(t)));class Ho extends Ka{static get type(){return"PropertyNode"}constructor(e,n=null,a=!1){super(e),this.name=n,this.varying=a,this.isPropertyNode=!0,this.global=!0}customCacheKey(){return F4(this.type+":"+(this.name||"")+":"+(this.varying?"1":"0"))}getHash(e){return this.name||super.getHash(e)}generate(e){let n;return this.varying===!0?(n=e.getVaryingFromNode(this,this.name),n.needsInterpolation=!0):n=e.getVarFromNode(this,this.name),e.getPropertyName(n)}}const bm=(t,e)=>new Ho(t,e),hh=(t,e)=>new Ho(t,e,!0),di=_a(Ho,"vec4","DiffuseColor"),U4=_a(Ho,"vec3","DiffuseContribution"),xne=_a(Ho,"vec3","EmissiveColor"),eg=_a(Ho,"float","Roughness"),ux=_a(Ho,"float","Metalness"),wz=_a(Ho,"float","Clearcoat"),E8=_a(Ho,"float","ClearcoatRoughness"),ph=_a(Ho,"vec3","Sheen"),sb=_a(Ho,"float","SheenRoughness"),Mz=_a(Ho,"float","Iridescence"),Sz=_a(Ho,"float","IridescenceIOR"),Az=_a(Ho,"float","IridescenceThickness"),Tz=_a(Ho,"float","AlphaT"),rb=_a(Ho,"float","Anisotropy"),RI=_a(Ho,"vec3","AnisotropyT"),G4=_a(Ho,"vec3","AnisotropyB"),ob=_a(Ho,"color","SpecularColor"),j4=_a(Ho,"color","SpecularColorBlended"),V4=_a(Ho,"float","SpecularF90"),Cz=_a(Ho,"float","Shininess"),k8=_a(Ho,"vec4","Output"),H4=_a(Ho,"float","dashSize"),PI=_a(Ho,"float","gapSize"),f6t=_a(Ho,"float","pointWidth"),OI=_a(Ho,"float","IOR"),_z=_a(Ho,"float","Transmission"),yne=_a(Ho,"float","Thickness"),vne=_a(Ho,"float","AttenuationDistance"),bne=_a(Ho,"color","AttenuationColor"),wne=_a(Ho,"float","Dispersion");class Mne extends Ka{static get type(){return"UniformGroupNode"}constructor(e,n=!1,a=1){super("string"),this.name=e,this.shared=n,this.order=a,this.isUniformGroup=!0}serialize(e){super.serialize(e),e.name=this.name,e.version=this.version,e.shared=this.shared}deserialize(e){super.deserialize(e),this.name=e.name,this.version=e.version,this.shared=e.shared}}const _Se=t=>new Mne(t),Iz=(t,e=0)=>new Mne(t,!0,e),ISe=Iz("frame"),xs=Iz("render"),Sne=_Se("object");class D8 extends pz{static get type(){return"UniformNode"}constructor(e,n=null){super(e,n),this.isUniformNode=!0,this.name="",this.groupNode=Sne}setName(e){return this.name=e,this}label(e){return Nn('TSL: "label()" has been deprecated. Use "setName()" instead.',new Kl),this.setName(e)}setGroup(e){return this.groupNode=e,this}getGroup(){return this.groupNode}getUniformHash(e){return this.getHash(e)}onUpdate(e,n){return e=e.bind(this),super.onUpdate(a=>{const s=e(a,this);s!==void 0&&(this.value=s)},n)}getInputType(e){let n=super.getInputType(e);return n==="bool"&&(n="uint"),n}generate(e,n){const a=this.getNodeType(e),s=this.getUniformHash(e);let r=e.getNodeFromHash(s);r===void 0&&(e.setHashNode(this,s),r=this);const o=r.getInputType(e),i=e.getUniformFromNode(r,o,e.shaderStage,this.name||e.context.nodeName),l=e.getPropertyName(i);e.context.nodeName!==void 0&&delete e.context.nodeName;let c=l;if(a==="bool"){const d=e.getDataFromNode(this);let h=d.propertyName;if(h===void 0){const u=e.getVarFromNode(this,null,"bool");h=e.getPropertyName(u),d.propertyName=h,c=e.format(l,o,a),e.addLineFlowCode(`${h} = ${c}`,this)}c=h}return e.format(c,a,n)}}const us=(t,e)=>{const n=wSe(e||t);if(n===t&&(t=dz(n)),t&&t.isNode===!0){let a=t.value;t.traverse(s=>{s.isConstNode===!0&&(a=s.value)}),t=a}return new D8(t,n)};class Ane extends Pl{static get type(){return"ArrayNode"}constructor(e,n,a=null){super(e),this.count=n,this.values=a,this.isArrayNode=!0}getArrayCount(){return this.count}getNodeType(e){return this.nodeType===null?this.values[0].getNodeType(e):this.nodeType}getElementType(e){return this.getNodeType(e)}getMemberType(e,n){return this.nodeType===null?this.values[0].getMemberType(e,n):super.getMemberType(e,n)}generate(e){const n=this.getNodeType(e);return e.generateArray(n,this.count,this.values)}}const ESe=(...t)=>{let e;if(t.length===1){const n=t[0];e=new Ane(null,n.length,n)}else{const n=t[0],a=t[1];e=new Ane(n,a)}return Ja(e)};Hn("toArray",(t,e)=>ESe(Array(e).fill(t)));class kSe extends Pl{static get type(){return"AssignNode"}constructor(e,n){super(),this.targetNode=e,this.sourceNode=n,this.isAssignNode=!0}hasDependencies(){return!1}getNodeType(e,n){return n!=="void"?this.targetNode.getNodeType(e):"void"}needsSplitAssign(e){const{targetNode:n}=this;if(e.isAvailable("swizzleAssign")===!1&&n.isSplitNode&&n.components.length>1){const a=e.getTypeLength(n.node.getNodeType(e));return eb.join("").slice(0,a)!==n.components}return!1}setup(e){const{targetNode:n,sourceNode:a}=this,s=n.getScope(),r=e.getDataFromNode(s);r.assign=!0;const o=e.getNodeProperties(this);o.sourceNode=a,o.targetNode=n.context({assign:!0})}generate(e,n){const{targetNode:a,sourceNode:s}=e.getNodeProperties(this),r=this.needsSplitAssign(e),o=a.build(e),i=a.getNodeType(e),l=s.build(e,i),c=s.getNodeType(e),d=e.getDataFromNode(this);let h;if(d.initialized===!0)n!=="void"&&(h=o);else if(r){const u=e.getVarFromNode(this,null,i),f=e.getPropertyName(u);e.addLineFlowCode(`${f} = ${l}`,this);const p=a.node,g=p.node.context({assign:!0}).build(e);for(let x=0;x<p.components.length;x++){const v=p.components[x];e.addLineFlowCode(`${g}.${v} = ${f}[ ${x} ]`,this)}n!=="void"&&(h=o)}else h=`${o} = ${l}`,(n==="void"||c==="void")&&(e.addLineFlowCode(h,this),n!=="void"&&(h=o));return d.initialized=!0,e.format(h,i,n)}}const DSe=hr(kSe).setParameterLength(2);Hn("assign",DSe);class NSe extends Pl{static get type(){return"FunctionCallNode"}constructor(e=null,n={}){super(),this.functionNode=e,this.parameters=n}setParameters(e){return this.parameters=e,this}getParameters(){return this.parameters}getNodeType(e){return this.functionNode.getNodeType(e)}getMemberType(e,n){return this.functionNode.getMemberType(e,n)}generate(e){const n=[],a=this.functionNode,s=a.getInputs(e),r=this.parameters,o=(l,c)=>{const d=c.type,h=d==="pointer";let u;return h?u="&"+l.build(e):u=l.build(e,d),u};if(Array.isArray(r)){if(r.length>s.length)On("TSL: The number of provided parameters exceeds the expected number of inputs in 'Fn()'."),r.length=s.length;else if(r.length<s.length)for(On("TSL: The number of provided parameters is less than the expected number of inputs in 'Fn()'.");r.length<s.length;)r.push(bt(0));for(let l=0;l<r.length;l++)n.push(o(r[l],s[l]))}else for(const l of s){const c=r[l.name];c!==void 0?n.push(o(c,l)):(On(`TSL: Input '${l.name}' not found in 'Fn()'.`),n.push(o(bt(0),l)))}return`${a.build(e,"property")}( ${n.join(", ")} )`}}const RSe=(t,...e)=>(e=e.length>1||e[0]&&e[0].isNode===!0?L4(e):yz(e[0]),new NSe(Ja(t),e));Hn("call",RSe);const h6t={"==":"equal","!=":"notEqual","<":"lessThan",">":"greaterThan","<=":"lessThanEqual",">=":"greaterThanEqual","%":"mod"};class ml extends Pl{static get type(){return"OperatorNode"}constructor(e,n,a,...s){if(super(),s.length>0){let r=new ml(e,n,a);for(let o=0;o<s.length-1;o++)r=new ml(e,r,s[o]);n=r,a=s[s.length-1]}this.op=e,this.aNode=n,this.bNode=a,this.isOperatorNode=!0}getOperatorMethod(e,n){return e.getMethod(h6t[this.op],n)}getNodeType(e,n=null){const a=this.op,s=this.aNode,r=this.bNode,o=s.getNodeType(e),i=r?r.getNodeType(e):null;if(o==="void"||i==="void")return n||"void";if(a==="%")return o;if(a==="~"||a==="&"||a==="|"||a==="^"||a===">>"||a==="<<")return e.getIntegerType(o);if(a==="!"||a==="&&"||a==="||"||a==="^^")return"bool";if(a==="=="||a==="!="||a==="<"||a===">"||a==="<="||a===">="){const l=Math.max(e.getTypeLength(o),e.getTypeLength(i));return l>1?`bvec${l}`:"bool"}else{if(e.isMatrix(o)){if(i==="float")return o;if(e.isVector(i))return e.getVectorFromMatrix(o);if(e.isMatrix(i))return o}else if(e.isMatrix(i)){if(o==="float")return i;if(e.isVector(o))return e.getVectorFromMatrix(i)}return e.getTypeLength(i)>e.getTypeLength(o)?i:o}}generate(e,n){const a=this.op,{aNode:s,bNode:r}=this,o=this.getNodeType(e,n);let i=null,l=null;o!=="void"?(i=s.getNodeType(e),l=r?r.getNodeType(e):null,a==="<"||a===">"||a==="<="||a===">="||a==="=="||a==="!="?e.isVector(i)?l=i:e.isVector(l)?i=l:i!==l&&(i=l="float"):a===">>"||a==="<<"?(i=o,l=e.changeComponentType(l,"uint")):a==="%"?(i=o,l=e.isInteger(i)&&e.isInteger(l)?l:i):e.isMatrix(i)?l==="float"?l="float":e.isVector(l)?l=e.getVectorFromMatrix(i):e.isMatrix(l)||(i=l=o):e.isMatrix(l)?i==="float"?i="float":e.isVector(i)?i=e.getVectorFromMatrix(l):i=l=o:i=l=o):i=l=o;const c=s.build(e,i),d=r?r.build(e,l):null,h=e.getFunctionOperator(a);if(n!=="void"){const u=e.renderer.coordinateSystem===Tc;if(a==="=="||a==="!="||a==="<"||a===">"||a==="<="||a===">=")return u?e.isVector(i)?e.format(`${this.getOperatorMethod(e,n)}( ${c}, ${d} )`,o,n):e.format(`( ${c} ${a} ${d} )`,o,n):e.format(`( ${c} ${a} ${d} )`,o,n);if(a==="%")return e.isInteger(l)?e.format(`( ${c} % ${d} )`,o,n):e.format(`${this.getOperatorMethod(e,o)}( ${c}, ${d} )`,o,n);if(a==="!"||a==="~")return e.format(`(${a}${c})`,i,n);if(h)return e.format(`${h}( ${c}, ${d} )`,o,n);if(e.isMatrix(i)&&l==="float")return e.format(`( ${d} ${a} ${c} )`,o,n);if(i==="float"&&e.isMatrix(l))return e.format(`${c} ${a} ${d}`,o,n);{let f=`( ${c} ${a} ${d} )`;return!u&&o==="bool"&&e.isVector(i)&&e.isVector(l)&&(f=`all${f}`),e.format(f,o,n)}}else if(i!=="void")return h?e.format(`${h}( ${c}, ${d} )`,o,n):e.isMatrix(i)&&l==="float"?e.format(`${d} ${a} ${c}`,o,n):e.format(`${c} ${a} ${d}`,o,n)}serialize(e){super.serialize(e),e.op=this.op}deserialize(e){super.deserialize(e),this.op=e.op}}const R0=ba(ml,"+").setParameterLength(2,1/0).setName("add"),ho=ba(ml,"-").setParameterLength(2,1/0).setName("sub"),Xs=ba(ml,"*").setParameterLength(2,1/0).setName("mul"),mh=ba(ml,"/").setParameterLength(2,1/0).setName("div"),BI=ba(ml,"%").setParameterLength(2).setName("mod"),PSe=ba(ml,"==").setParameterLength(2).setName("equal"),OSe=ba(ml,"!=").setParameterLength(2).setName("notEqual"),BSe=ba(ml,"<").setParameterLength(2).setName("lessThan"),Tne=ba(ml,">").setParameterLength(2).setName("greaterThan"),FSe=ba(ml,"<=").setParameterLength(2).setName("lessThanEqual"),LSe=ba(ml,">=").setParameterLength(2).setName("greaterThanEqual"),zSe=ba(ml,"&&").setParameterLength(2,1/0).setName("and"),USe=ba(ml,"||").setParameterLength(2,1/0).setName("or"),GSe=ba(ml,"!").setParameterLength(1).setName("not"),jSe=ba(ml,"^^").setParameterLength(2).setName("xor"),VSe=ba(ml,"&").setParameterLength(2).setName("bitAnd"),HSe=ba(ml,"~").setParameterLength(1).setName("bitNot"),WSe=ba(ml,"|").setParameterLength(2).setName("bitOr"),$Se=ba(ml,"^").setParameterLength(2).setName("bitXor"),YSe=ba(ml,"<<").setParameterLength(2).setName("shiftLeft"),qSe=ba(ml,">>").setParameterLength(2).setName("shiftRight"),XSe=Kt(([t])=>(t.addAssign(1),t)),QSe=Kt(([t])=>(t.subAssign(1),t)),KSe=Kt(([t])=>{const e=Gt(t).toConst();return t.addAssign(1),e}),JSe=Kt(([t])=>{const e=Gt(t).toConst();return t.subAssign(1),e});Hn("add",R0),Hn("sub",ho),Hn("mul",Xs),Hn("div",mh),Hn("mod",BI),Hn("equal",PSe),Hn("notEqual",OSe),Hn("lessThan",BSe),Hn("greaterThan",Tne),Hn("lessThanEqual",FSe),Hn("greaterThanEqual",LSe),Hn("and",zSe),Hn("or",USe),Hn("not",GSe),Hn("xor",jSe),Hn("bitAnd",VSe),Hn("bitNot",HSe),Hn("bitOr",WSe),Hn("bitXor",$Se),Hn("shiftLeft",YSe),Hn("shiftRight",qSe),Hn("incrementBefore",XSe),Hn("decrementBefore",QSe),Hn("increment",KSe),Hn("decrement",JSe);const ZSe=(t,e)=>(Nn('TSL: "modInt()" is deprecated. Use "mod( int( ... ) )" instead.',new Kl),BI(Gt(t),Gt(e)));Hn("modInt",ZSe);class xn extends Pl{static get type(){return"MathNode"}constructor(e,n,a=null,s=null){if(super(),(e===xn.MAX||e===xn.MIN)&&arguments.length>3){let r=new xn(e,n,a);for(let o=2;o<arguments.length-1;o++)r=new xn(e,r,arguments[o]);n=r,a=arguments[arguments.length-1],s=null}this.method=e,this.aNode=n,this.bNode=a,this.cNode=s,this.isMathNode=!0}getInputType(e){const n=this.aNode.getNodeType(e),a=this.bNode?this.bNode.getNodeType(e):null,s=this.cNode?this.cNode.getNodeType(e):null,r=e.isMatrix(n)?0:e.getTypeLength(n),o=e.isMatrix(a)?0:e.getTypeLength(a),i=e.isMatrix(s)?0:e.getTypeLength(s);return r>o&&r>i?n:o>i?a:i>r?s:n}getNodeType(e){const n=this.method;return n===xn.LENGTH||n===xn.DISTANCE||n===xn.DOT?"float":n===xn.CROSS?"vec3":n===xn.ALL||n===xn.ANY?"bool":n===xn.EQUALS?e.changeComponentType(this.aNode.getNodeType(e),"bool"):this.getInputType(e)}setup(e){const{aNode:n,bNode:a,method:s}=this;let r=null;if(s===xn.ONE_MINUS)r=ho(1,n);else if(s===xn.RECIPROCAL)r=mh(1,n);else if(s===xn.DIFFERENCE)r=Oi(ho(n,a));else if(s===xn.TRANSFORM_DIRECTION){let o=n,i=a;e.isMatrix(o.getNodeType(e))?i=ja($t(i),0):o=ja($t(o),0);const l=Xs(o,i).xyz;r=cf(l)}return r!==null?r:super.setup(e)}generate(e,n){if(e.getNodeProperties(this).outputNode)return super.generate(e,n);let s=this.method;const r=this.getNodeType(e),o=this.getInputType(e),i=this.aNode,l=this.bNode,c=this.cNode,d=e.renderer.coordinateSystem;if(s===xn.NEGATE)return e.format("( - "+i.build(e,o)+" )",r,n);{const h=[];return s===xn.CROSS?h.push(i.build(e,r),l.build(e,r)):d===Tc&&s===xn.STEP?h.push(i.build(e,e.getTypeLength(i.getNodeType(e))===1?"float":o),l.build(e,o)):d===Tc&&(s===xn.MIN||s===xn.MAX)?h.push(i.build(e,o),l.build(e,e.getTypeLength(l.getNodeType(e))===1?"float":o)):s===xn.REFRACT?h.push(i.build(e,o),l.build(e,o),c.build(e,"float")):s===xn.MIX?h.push(i.build(e,o),l.build(e,o),c.build(e,e.getTypeLength(c.getNodeType(e))===1?"float":o)):(d===Cc&&s===xn.ATAN&&l!==null&&(s="atan2"),e.shaderStage!=="fragment"&&(s===xn.DFDX||s===xn.DFDY)&&(Nn(`TSL: '${s}' is not supported in the ${e.shaderStage} stage.`,this.stackTrace),s="/*"+s+"*/"),h.push(i.build(e,o)),l!==null&&h.push(l.build(e,o)),c!==null&&h.push(c.build(e,o))),e.format(`${e.getMethod(s,r)}( ${h.join(", ")} )`,r,n)}}serialize(e){super.serialize(e),e.method=this.method}deserialize(e){super.deserialize(e),this.method=e.method}}xn.ALL="all",xn.ANY="any",xn.RADIANS="radians",xn.DEGREES="degrees",xn.EXP="exp",xn.EXP2="exp2",xn.LOG="log",xn.LOG2="log2",xn.SQRT="sqrt",xn.INVERSE_SQRT="inversesqrt",xn.FLOOR="floor",xn.CEIL="ceil",xn.NORMALIZE="normalize",xn.FRACT="fract",xn.SIN="sin",xn.COS="cos",xn.TAN="tan",xn.ASIN="asin",xn.ACOS="acos",xn.ATAN="atan",xn.ABS="abs",xn.SIGN="sign",xn.LENGTH="length",xn.NEGATE="negate",xn.ONE_MINUS="oneMinus",xn.DFDX="dFdx",xn.DFDY="dFdy",xn.ROUND="round",xn.RECIPROCAL="reciprocal",xn.TRUNC="trunc",xn.FWIDTH="fwidth",xn.TRANSPOSE="transpose",xn.DETERMINANT="determinant",xn.INVERSE="inverse",xn.EQUALS="equals",xn.MIN="min",xn.MAX="max",xn.STEP="step",xn.REFLECT="reflect",xn.DISTANCE="distance",xn.DIFFERENCE="difference",xn.DOT="dot",xn.CROSS="cross",xn.POW="pow",xn.TRANSFORM_DIRECTION="transformDirection",xn.MIX="mix",xn.CLAMP="clamp",xn.REFRACT="refract",xn.SMOOTHSTEP="smoothstep",xn.FACEFORWARD="faceforward";const Cne=bt(1e-6),p6t=bt(1e6),Ez=bt(Math.PI),m6t=bt(Math.PI*2),g6t=bt(Math.PI*2),x6t=bt(Math.PI*.5),eAe=ba(xn,xn.ALL).setParameterLength(1),tAe=ba(xn,xn.ANY).setParameterLength(1),nAe=ba(xn,xn.RADIANS).setParameterLength(1),aAe=ba(xn,xn.DEGREES).setParameterLength(1),_ne=ba(xn,xn.EXP).setParameterLength(1),N8=ba(xn,xn.EXP2).setParameterLength(1),kz=ba(xn,xn.LOG).setParameterLength(1),tg=ba(xn,xn.LOG2).setParameterLength(1),Ad=ba(xn,xn.SQRT).setParameterLength(1),Ine=ba(xn,xn.INVERSE_SQRT).setParameterLength(1),ng=ba(xn,xn.FLOOR).setParameterLength(1),Dz=ba(xn,xn.CEIL).setParameterLength(1),cf=ba(xn,xn.NORMALIZE).setParameterLength(1),wm=ba(xn,xn.FRACT).setParameterLength(1),Td=ba(xn,xn.SIN).setParameterLength(1),Mm=ba(xn,xn.COS).setParameterLength(1),sAe=ba(xn,xn.TAN).setParameterLength(1),rAe=ba(xn,xn.ASIN).setParameterLength(1),Ene=ba(xn,xn.ACOS).setParameterLength(1),kne=ba(xn,xn.ATAN).setParameterLength(1,2),Oi=ba(xn,xn.ABS).setParameterLength(1),Dne=ba(xn,xn.SIGN).setParameterLength(1),ag=ba(xn,xn.LENGTH).setParameterLength(1),Nne=ba(xn,xn.NEGATE).setParameterLength(1),oAe=ba(xn,xn.ONE_MINUS).setParameterLength(1),Rne=ba(xn,xn.DFDX).setParameterLength(1),Pne=ba(xn,xn.DFDY).setParameterLength(1),iAe=ba(xn,xn.ROUND).setParameterLength(1),lAe=ba(xn,xn.RECIPROCAL).setParameterLength(1),One=ba(xn,xn.TRUNC).setParameterLength(1),Bne=ba(xn,xn.FWIDTH).setParameterLength(1),cAe=ba(xn,xn.TRANSPOSE).setParameterLength(1),uAe=ba(xn,xn.DETERMINANT).setParameterLength(1),dAe=ba(xn,xn.INVERSE).setParameterLength(1),uf=ba(xn,xn.MIN).setParameterLength(2,1/0),Ol=ba(xn,xn.MAX).setParameterLength(2,1/0),W4=ba(xn,xn.STEP).setParameterLength(2),fAe=ba(xn,xn.REFLECT).setParameterLength(2),hAe=ba(xn,xn.DISTANCE).setParameterLength(2),pAe=ba(xn,xn.DIFFERENCE).setParameterLength(2),tp=ba(xn,xn.DOT).setParameterLength(2),dx=ba(xn,xn.CROSS).setParameterLength(2),np=ba(xn,xn.POW).setParameterLength(2),Fne=t=>Xs(t,t),mAe=t=>Xs(t,t,t),Lne=t=>Xs(t,t,t,t),gAe=ba(xn,xn.TRANSFORM_DIRECTION).setParameterLength(2),xAe=t=>Xs(Dne(t),np(Oi(t),1/3)),zne=t=>tp(t,t),Wr=ba(xn,xn.MIX).setParameterLength(3),Sm=(t,e=0,n=1)=>new xn(xn.CLAMP,Ja(t),Ja(e),Ja(n)),Nz=t=>Sm(t),Une=ba(xn,xn.REFRACT).setParameterLength(3),gh=ba(xn,xn.SMOOTHSTEP).setParameterLength(3),Gne=ba(xn,xn.FACEFORWARD).setParameterLength(3),yAe=Kt(([t])=>{const a=43758.5453,s=tp(t.xy,ha(12.9898,78.233)),r=BI(s,Ez);return wm(Td(r).mul(a))}),vAe=(t,e,n)=>Wr(e,n,t),bAe=(t,e,n)=>gh(e,n,t),wAe=(t,e)=>W4(e,t),y6t=Gne,v6t=Ine;Hn("all",eAe),Hn("any",tAe),Hn("radians",nAe),Hn("degrees",aAe),Hn("exp",_ne),Hn("exp2",N8),Hn("log",kz),Hn("log2",tg),Hn("sqrt",Ad),Hn("inverseSqrt",Ine),Hn("floor",ng),Hn("ceil",Dz),Hn("normalize",cf),Hn("fract",wm),Hn("sin",Td),Hn("cos",Mm),Hn("tan",sAe),Hn("asin",rAe),Hn("acos",Ene),Hn("atan",kne),Hn("abs",Oi),Hn("sign",Dne),Hn("length",ag),Hn("lengthSq",zne),Hn("negate",Nne),Hn("oneMinus",oAe),Hn("dFdx",Rne),Hn("dFdy",Pne),Hn("round",iAe),Hn("reciprocal",lAe),Hn("trunc",One),Hn("fwidth",Bne),Hn("min",uf),Hn("max",Ol),Hn("step",wAe),Hn("reflect",fAe),Hn("distance",hAe),Hn("dot",tp),Hn("cross",dx),Hn("pow",np),Hn("pow2",Fne),Hn("pow3",mAe),Hn("pow4",Lne),Hn("transformDirection",gAe),Hn("mix",vAe),Hn("clamp",Sm),Hn("refract",Une),Hn("smoothstep",bAe),Hn("faceForward",Gne),Hn("difference",pAe),Hn("saturate",Nz),Hn("cbrt",xAe),Hn("transpose",cAe),Hn("determinant",uAe),Hn("inverse",dAe),Hn("rand",yAe);class MAe extends Ka{static get type(){return"ConditionalNode"}constructor(e,n,a=null){super(),this.condNode=e,this.ifNode=n,this.elseNode=a}getNodeType(e){const{ifNode:n,elseNode:a}=e.getNodeProperties(this);if(n===void 0)return e.flowBuildStage(this,"setup"),this.getNodeType(e);const s=n.getNodeType(e);if(a!==null){const r=a.getNodeType(e);if(e.getTypeLength(r)>e.getTypeLength(s))return r}return s}setup(e){const n=this.condNode,a=this.ifNode.isolate(),s=this.elseNode?this.elseNode.isolate():null,r=e.context.nodeBlock;e.getDataFromNode(a).parentNodeBlock=r,s!==null&&(e.getDataFromNode(s).parentNodeBlock=r);const o=e.context.uniformFlow,i=e.getNodeProperties(this);i.condNode=n,i.ifNode=o?a:a.context({nodeBlock:a}),i.elseNode=s?o?s:s.context({nodeBlock:s}):null}generate(e,n){const a=this.getNodeType(e),s=e.getDataFromNode(this);if(s.nodeProperty!==void 0)return s.nodeProperty;const{condNode:r,ifNode:o,elseNode:i}=e.getNodeProperties(this),l=e.currentFunctionNode,c=n!=="void",d=c?bm(a).build(e):"";s.nodeProperty=d;const h=r.build(e,"bool");if(e.context.uniformFlow&&i!==null){const p=o.build(e,a),m=i.build(e,a),g=e.getTernary(h,p,m);return e.format(g,a,n)}e.addFlowCode(`
@@ -79142,7 +79142,7 @@ function n7t(t, { project: e, sources: a, strips: r, parts: n, config: i, assets
79142
79142
  for (const u of [z8t, F8t, L8t, U8t, j8t, X8t, Y8t]) u(l);
79143
79143
  return s && (l.chat = e7t(l, s)), l;
79144
79144
  }
79145
- const _Ce = "0.2.0", Lz = () => {
79145
+ const _Ce = "0.2.1", Lz = () => {
79146
79146
  };
79147
79147
  Pbt(), Y4t();
79148
79148
  function i7t({ project: t, playerRef: e }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greenlight-motion",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "GreenLight Motion Engine: AI agents make motion-design videos (3D, camera, effects, transitions, text animation, particles, motion blur) with the GreenLight Dash Video Editor's own engine, in the user's browser.",
5
5
  "keywords": [
6
6
  "video",
package/skill/SKILL.md CHANGED
@@ -18,12 +18,19 @@ You never write UI or animation code. If something is not in the format, it is n
18
18
  ## Setup (once per machine)
19
19
 
20
20
  ```
21
- npx greenlight-motion@0.2.0 doctor
21
+ npx greenlight-motion@0.2.1 doctor
22
22
  ```
23
23
 
24
24
  It checks Node 18+, Chrome or Edge, WebGPU and H.264 encoding, and prints a fix for anything missing. Do not install
25
25
  anything globally without asking the user. Always pin the version in every command you run.
26
26
 
27
+ ## First, a direction
28
+
29
+ Before any JSON, decide in a few lines what the video LOOKS like: one visual idea from the subject's own world carried
30
+ through every scene, the palette, the type, and what each scene shows besides its words. Presets make a video move;
31
+ the direction makes it look designed. `skill/references/scene-script.md` opens with how. Then build to it, and judge
32
+ every contact sheet against it.
33
+
27
34
  ## The fast way: a scene script
28
35
 
29
36
  For a new video, start with a **scene script** instead of a whole project: a short JSON file — scenes, their texts and
@@ -32,7 +39,7 @@ animation presets (the same converter the GreenLight Motion connector uses in Cl
32
39
  keyframes rarely look as good. The format, the design rules and the catalogues: `skill/references/scene-script.md`.
33
40
 
34
41
  ```
35
- npx greenlight-motion@0.2.0 script <folder> --from scenes.json # or write <folder>/script.json, then: script <folder>
42
+ npx greenlight-motion@0.2.1 script <folder> --from scenes.json # or write <folder>/script.json, then: script <folder>
36
43
  ```
37
44
 
38
45
  Then `check` and `stills` (below), fix the SCRIPT and run `script` again — it rewrites `project.json`. A scene's
@@ -40,16 +47,39 @@ Then `check` and `stills` (below), fix the SCRIPT and run `script` again — it
40
47
  `z`). For what a script can't say (3D models, particles, motion paths, masks, text animators by hand), go on from `project.json` with the
41
48
  workflow below and stop running `script` (it refuses to overwrite a project edited since, unless `--force`).
42
49
 
50
+ ## Beyond the script: the signature visual
51
+
52
+ The script gives structure; the picture that makes the video THIS product's usually needs the engine's full format.
53
+ Build it with `apply` on the project `script` wrote (the item ids are the script's element ids):
54
+
55
+ - **A material of its own**: a custom GLSL shader on a shape or the whole background (water, light through glass,
56
+ paper, heat): `shapeBackground` / `background` `{ "type": "shader", "shaderId": "custom", "shaderCode": "void
57
+ mainImage(out vec4 o, in vec2 fc) { … }" }`, Shadertoy-style, `iTime` from the timeline
58
+ (`references/video-backgrounds.md`, Custom GLSL shaders).
59
+ - **Layers that interact**: a track matte, so words dip under a surface or show only through a shape
60
+ (`"trackMatte": { "sourceId": "<item>", "mode": "alpha" | "alphaInverted" | "luma" | "lumaInverted" }` on the layer
61
+ that is cut; the source is hidden from the output; `references/video-shapes.md`). Clipping masks and Layer Mask
62
+ (`references/video-effects.md`) for reveals.
63
+ - **Your own drawing and motion**: SVG you write (`{ "type": "image", "svg": "…" }` in the script), shapes with
64
+ keyframes, motion paths (`references/video-paths.md`), particles (`references/video-particles.md`), 3D
65
+ (`references/video-3d.md`), Text Animators by hand (`references/video-text.md`).
66
+ - **Sound when nothing may be downloaded**: write it in code. A short Node script can write a 48 kHz WAV (a soft bed, a
67
+ click, a noise whoosh) into `assets/`, then use it as the script's `music` or as an audio clip. Keep the bed quiet
68
+ under the hits.
69
+
70
+ Keep the build repeatable: put your edits in a file you `apply` again after each `script` run (`script --force`, then
71
+ `apply`), so the script and the hand-made layers never drift apart.
72
+
43
73
  ## Workflow
44
74
 
45
- 1. `npx greenlight-motion@0.2.0 init <folder>` — starts from a working example (title, subtitle, accent line, four
75
+ 1. `npx greenlight-motion@0.2.1 init <folder>` — starts from a working example (title, subtitle, accent line, four
46
76
  options). `--from file.json` starts from a project you have (for example one exported from GreenLight Dash).
47
77
  2. Edit `<folder>/project.json` — for changes to an EXISTING project prefer `apply` (below): it checks every edit and
48
78
  applies all or nothing. Put pictures, video, sound and fonts in `<folder>/assets/` and refer to them as
49
79
  `"asset:<file>"` (e.g. `"url": "asset:product.png"`).
50
- 3. `npx greenlight-motion@0.2.0 check <folder>` — fix everything it lists (missing files, template mistakes, blank
80
+ 3. `npx greenlight-motion@0.2.1 check <folder>` — fix everything it lists (missing files, template mistakes, blank
51
81
  frames, page errors). `--json` for machine output.
52
- 4. `npx greenlight-motion@0.2.0 stills <folder>` — renders frames and **a labelled contact sheet**. Open and look at
82
+ 4. `npx greenlight-motion@0.2.1 stills <folder>` — renders frames and **a labelled contact sheet**. Open and look at
53
83
  it before you go on: layout, readability, timing. Fix and repeat. `--at 0.5,1.2,3` for exact times.
54
84
  5. Hand it over: tell the user to open `<folder>/index.html` (Chrome or Edge), or `pack` it into one file, or
55
85
  `render` the MP4 yourself (`--fps 60 --motion-blur` for fast motion).
@@ -114,7 +144,7 @@ editor's own normalizer), so write only what you mean.
114
144
  Full references (generated from the editor's own docs; ignore the `/api/` routes they mention):
115
145
  `skill/references/` — video-design, scene-script, video-projects, video-text, video-text-kinetic, video-text-presets, video-shapes, video-keyframes,
116
146
  video-camera, video-3d, video-effects, video-transitions, video-particles, video-paths, video-backgrounds, video-grid.
117
- Machine-readable lists: `npx greenlight-motion@0.2.0 schema effects | transitions | easings | paths | expressions`.
147
+ Machine-readable lists: `npx greenlight-motion@0.2.1 schema effects | transitions | easings | paths | expressions`.
118
148
 
119
149
  ## Template
120
150
 
@@ -161,7 +191,7 @@ an absolute moment; `align` `sync` (default) / `start` / `end`; `offset` seconds
161
191
 
162
192
  The library is 77 hand-picked sounds in categories (whooshes and swooshes, transitions, clicks, pop-ups and
163
193
  notifications, data blips, typing and writing, foley, bubbles, impacts and bass drops, camera, gears, voice clips).
164
- `npx greenlight-motion@0.2.0 schema sounds` lists every one with its `category`, `use` (what it is for), `duration`
194
+ `npx greenlight-motion@0.2.1 schema sounds` lists every one with its `category`, `use` (what it is for), `duration`
165
195
  and `sync`. Pick by `category` and `use`, then by name.
166
196
 
167
197
  - **Where the files come from:** the GreenLight sound CDN. `build` downloads each sound a cue uses into
@@ -177,7 +207,7 @@ The page has **Notes**: the user marks what should change right on the frame (a
177
207
  and writes a note for each mark. Every note carries the moment (seconds and frame), the mark in composition pixels
178
208
  and the layers under it (item ids in project.json), so you change exactly that and nothing else.
179
209
 
180
- - **They review on this computer:** run `npx greenlight-motion@0.2.0 review <folder>` and give them the URL it prints
210
+ - **They review on this computer:** run `npx greenlight-motion@0.2.1 review <folder>` and give them the URL it prints
181
211
  (it keeps running; `--open` opens their browser, only when they ask). Their notes land in `<folder>/review/`: read
182
212
  `review/NOTES.md`, look at the `at-<t>s.png` pictures (the marks are numbered), make the changes (`apply`), `build`
183
213
  again and tell them to reload the page.
@@ -206,11 +236,11 @@ that fails (`plugin`); look at `stills` for the look. A package that carries its
206
236
 
207
237
  The same project opens in GreenLight Dash (the desktop editor this engine comes from) for full editing: the page's
208
238
  edit note offers **Open in GreenLight Dash** (it imports the folder as a new project on the open board; a page with
209
- changes saves a `.glvideo`). `npx greenlight-motion@0.2.0 pack <folder> --glvideo` writes that file yourself: ONE
239
+ changes saves a `.glvideo`). `npx greenlight-motion@0.2.1 pack <folder> --glvideo` writes that file yourself: ONE
210
240
  `.glvideo` (the project + its files) that opens in GreenLight Dash on a double-click, or dropped on its board. Hand it
211
241
  over when the user wants to keep editing in the app. A
212
242
  GreenLight Dash project comes the other way through the app's **Export for agents** — a ready folder like the one
213
- `init` makes, with the options the author ticked. `npx greenlight-motion@0.2.0 init <folder> --from <its project.json>`
243
+ `init` makes, with the options the author ticked. `npx greenlight-motion@0.2.1 init <folder> --from <its project.json>`
214
244
  starts from one too. Never tell the user they must install it: the page plays, changes options and renders on its own.
215
245
 
216
246
  ## Files
@@ -13,6 +13,19 @@ works too (it has to stay reachable). SVG you write: `{ "type": "image", "svg":
13
13
 
14
14
  ## The scene script
15
15
 
16
+ **Before the script, a direction.** Presets make a video move; they don't give it an idea. Decide in a few lines first,
17
+ and keep them next to the script:
18
+ - **The idea**: ONE picture from the subject's own world (its object, place or action: a coffee shop's steam and cup rim,
19
+ a budgeting app's receipt printing line by line, a running app's track the scenes run along), carried through every scene.
20
+ - **The palette**: background, text, one accent, plus only the colours that carry meaning (good / bad, before / after).
21
+ Light or dark on purpose.
22
+ - **The type**: a big headline against a small support line; aligned left or centred on purpose, not by default.
23
+ - **Each scene**: what it SHOWS besides its words, built from shapes, lines, `svg` you write, keyframes and the camera.
24
+ The same line centred on a gradient in every scene reads as a template. Judge the stills against the direction, not
25
+ only for mistakes.
26
+ What a script can't say (a custom GLSL shader on a shape or the background, words dipping under a surface with a track
27
+ matte, masks, particles, 3D) goes on from `project.json`: SKILL.md, "Beyond the script".
28
+
16
29
  ```json
17
30
  {
18
31
  "title": "Bean There — opening day",
@@ -57,6 +70,9 @@ works too (it has to stay reachable). SVG you write: `{ "type": "image", "svg":
57
70
  (the scene's camera doesn't move it: a logo, a caption). Keyframes also take a `z` track.
58
71
  - **Scene**: `duration` (s), `transition` into it (default Cross Fade, 0.6 s; `transitionDuration`), `camera` (below; default push-in),
59
72
  `background` (overrides style), `sounds`: [{ "sfx", "at" }]. Script-level `sound`: auto (default: a whoosh on each transition and on the first headline) | none.
73
+ **A transition overlaps the scenes on both sides**, so the video lasts the scenes' durations added up MINUS every
74
+ transition: five 3 s scenes with the default 0.6 s transitions run 15 − 4 × 0.6 = 12.6 s. To hit a length, add the
75
+ transitions back to the scenes (`script` prints the total).
60
76
  - **camera** — a quick move of the whole scene: none | push-in | pull-out | drift-left | drift-right | drift-up. Or a REAL camera that films the scene's elements in 3D (the
61
77
  Video Editor's own camera layer; the scene's backdrop stays put, so its edge never shows). One of:
62
78
  - `{ "move": id }` — a camera move from the catalogue (or just `"camera": "cm-quiet-push"`), spread over the scene;