greenlight-motion 0.1.4 → 0.1.5
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 +9 -9
- package/dist/greenlight-motion-script.mjs +1 -1
- package/dist/greenlight-motion.js +1 -1
- package/dist/greenlight-motion.mjs +1 -1
- package/package.json +2 -2
- package/skill/SKILL.md +10 -10
- package/skill/references/video-design.md +5 -0
- package/skill/references/video-text.md +2 -2
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.1.
|
|
21
|
-
npx greenlight-motion@0.1.
|
|
20
|
+
npx greenlight-motion@0.1.5 doctor # Node 18+, Chrome or Edge, WebGPU, H.264: what's missing and how to fix it
|
|
21
|
+
npx greenlight-motion@0.1.5 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.1.
|
|
25
|
-
npx greenlight-motion@0.1.
|
|
26
|
-
npx greenlight-motion@0.1.
|
|
27
|
-
npx greenlight-motion@0.1.
|
|
28
|
-
npx greenlight-motion@0.1.
|
|
29
|
-
npx greenlight-motion@0.1.
|
|
24
|
+
npx greenlight-motion@0.1.5 script my-video # script.json → project.json with the Video Editor's own presets
|
|
25
|
+
npx greenlight-motion@0.1.5 check my-video # what to fix: missing files, template mistakes, blank frames, broken plugins
|
|
26
|
+
npx greenlight-motion@0.1.5 stills my-video # frames + a labelled contact sheet, to look at
|
|
27
|
+
npx greenlight-motion@0.1.5 render my-video --fps 60 --motion-blur
|
|
28
|
+
npx greenlight-motion@0.1.5 pack my-video # everything in ONE html file
|
|
29
|
+
npx greenlight-motion@0.1.5 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.1.
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/greenlight-motion@0.1.5/dist/greenlight-motion.js"></script>
|
|
76
76
|
<script>
|
|
77
77
|
GreenLightMotion.mountStudio(document.getElementById('app'), {
|
|
78
78
|
project: { /* project.json */ },
|
|
@@ -16593,7 +16593,7 @@ function compileScript(script, caps, files = /* @__PURE__ */ new Map()) {
|
|
|
16593
16593
|
init_define_import_meta_env();
|
|
16594
16594
|
|
|
16595
16595
|
// ../skills/greenlight-dash/references/video-design.md
|
|
16596
|
-
var video_design_default = "# Motion design that looks made, not generated\n\nWhat makes a video read as designed rather than generated. The rules hold however the video is built \u2014 a project\nthrough the app's API, `project.json` with the engine's CLI, or a scene script \u2014 so read them before the first version\nand check every version against them.\n\n## Story and words\n\n- **One idea per scene**: 3\u20136 scenes of 2\u20133 s for a short video. A scene that says two things says neither.\n- **At most ~8 words on screen at once, held at least 1 s.** People read about 3 words a second; text that leaves\n before it is read is noise.\n- **9:16**: bigger text, stacked vertically, everything in the middle 80 %.\n\n## Hierarchy\n\n- One headline per scene; supporting text much smaller. Hierarchy comes from size and weight, not colour alone.\n- One accent colour and strong contrast.\n- Keep text inside the middle 80 % of the frame (in a project: |panX| and |panY| \u2264 40): players and feeds crop the\n edges.\n\n## Timing\n\n- Stagger related elements by 0.06\u20130.12 s.\n- **One focal move at a time.** If everything moves, nothing reads: supporting elements move less and later.\n- Shapes and pictures enter in 0.4\u20130.8 s; exits are faster than entrances.\n- A text animation takes at least 0.8\u20131 s over the whole line, first unit in to last unit settled, unless the video\n needs snap (a beat hit, a glitch flick): faster text flashes past before it is read. The arithmetic per kind of\n animation: video-projects.md, Text animation pace.\n\n## Easing by intent\n\n- Entrances: `AE Out` or `Expo Out` (fast start, soft landing). Camera and big moves: `AE Smooth`.\n- A layer that passes through (in, glide, out): `Smooth Glide` with the `glide` expression \u2014 the Glide keyframe\n presets build it (video-keyframes.md).\n- Playful arrivals: `Back Out` or `Overshoot`. Exits: `Ease In`.\n- `Linear` only for constant motion: tickers, rotation, progress along a path.\n\n## Text animation\n\n- Animate words or letters, never a whole line at once \u2014 the text presets do this.\n- One family per video. Calm presets for lines and captions: Glide Text (`glide-text`), Classic Fade Up\n (`classic-fade-up`), Classic Rise (Mask) (`classic-rise-mask`); Focus Type (`focus-type`) for a title.\n- Punchy presets that tilt or overshoot \u2014 Swing Pop (`swing-pop`), Whip (`whip`), Letter Vortex (`letter-vortex`) \u2014\n only on 1\u20133 words when the video wants energy. On a longer caption they read cheap.\n- Glide Continuous (`glide-continuous`) and Glide Soft Stop (`glide-soft-stop`) never let the text rest: use them\n only when the user asks for drifting text.\n- On a coloured card, pick a preset without a colour band (Classic Rise (Mask), Classic Fade Up): a band's colour can\n clash with the card.\n\n## Transitions and camera\n\n- Vary two or three kinds of transition across a video: one kind everywhere looks like a template, a new one at\n every cut looks random.\n- Camera moves are slow and small unless they are the point. Fast motion looks best at 60 fps with motion blur.\n\n## Sound\n\n- A soft whoosh on each transition and on the first headline is a good base. `data-1` or `selection-data-click` for\n UI-like pops, `cinematic-transition-13` for one big reveal.\n\n## What to check in every version\n\nLook at real frames across the video (a contact sheet) before the user sees it \u2014 the JSON doesn't show these:\nblank or empty frames, overlapping or cut-off text, text too small or gone too fast, nothing moving or everything\nmoving at once, weak hierarchy, clashing colours. Fix them and look again; two rounds are usually enough.\n";
|
|
16596
|
+
var video_design_default = "# Motion design that looks made, not generated\n\nWhat makes a video read as designed rather than generated. The rules hold however the video is built \u2014 a project\nthrough the app's API, `project.json` with the engine's CLI, or a scene script \u2014 so read them before the first version\nand check every version against them.\n\n## Story and words\n\n- **One idea per scene**: 3\u20136 scenes of 2\u20133 s for a short video. A scene that says two things says neither.\n- **At most ~8 words on screen at once, held at least 1 s.** People read about 3 words a second; text that leaves\n before it is read is noise.\n- **9:16**: bigger text, stacked vertically, everything in the middle 80 %.\n\n## Hierarchy\n\n- One headline per scene; supporting text much smaller. Hierarchy comes from size and weight, not colour alone.\n- One accent colour and strong contrast.\n- **No text shadows unless the user asks for one.** A shadow under type reads as a stock default and softens the\n letter edges. When text has to read over footage or a busy picture, get the contrast from the design instead: a\n calmer or darker part of the frame, a caption bar (`textBgEnabled`), or a shape behind the text. So no\n `textShadowX` / `textShadowY` / `textShadowBlur` / `textShadowSpread` on a text layer, no `shadow` property in a\n Text Animator and no `longShadow` effect on text. A shadow already in the user's project stays.\n- Keep text inside the middle 80 % of the frame (in a project: |panX| and |panY| \u2264 40): players and feeds crop the\n edges.\n\n## Timing\n\n- Stagger related elements by 0.06\u20130.12 s.\n- **One focal move at a time.** If everything moves, nothing reads: supporting elements move less and later.\n- Shapes and pictures enter in 0.4\u20130.8 s; exits are faster than entrances.\n- A text animation takes at least 0.8\u20131 s over the whole line, first unit in to last unit settled, unless the video\n needs snap (a beat hit, a glitch flick): faster text flashes past before it is read. The arithmetic per kind of\n animation: video-projects.md, Text animation pace.\n\n## Easing by intent\n\n- Entrances: `AE Out` or `Expo Out` (fast start, soft landing). Camera and big moves: `AE Smooth`.\n- A layer that passes through (in, glide, out): `Smooth Glide` with the `glide` expression \u2014 the Glide keyframe\n presets build it (video-keyframes.md).\n- Playful arrivals: `Back Out` or `Overshoot`. Exits: `Ease In`.\n- `Linear` only for constant motion: tickers, rotation, progress along a path.\n\n## Text animation\n\n- Animate words or letters, never a whole line at once \u2014 the text presets do this.\n- One family per video. Calm presets for lines and captions: Glide Text (`glide-text`), Classic Fade Up\n (`classic-fade-up`), Classic Rise (Mask) (`classic-rise-mask`); Focus Type (`focus-type`) for a title.\n- Punchy presets that tilt or overshoot \u2014 Swing Pop (`swing-pop`), Whip (`whip`), Letter Vortex (`letter-vortex`) \u2014\n only on 1\u20133 words when the video wants energy. On a longer caption they read cheap.\n- Glide Continuous (`glide-continuous`) and Glide Soft Stop (`glide-soft-stop`) never let the text rest: use them\n only when the user asks for drifting text.\n- On a coloured card, pick a preset without a colour band (Classic Rise (Mask), Classic Fade Up): a band's colour can\n clash with the card.\n\n## Transitions and camera\n\n- Vary two or three kinds of transition across a video: one kind everywhere looks like a template, a new one at\n every cut looks random.\n- Camera moves are slow and small unless they are the point. Fast motion looks best at 60 fps with motion blur.\n\n## Sound\n\n- A soft whoosh on each transition and on the first headline is a good base. `data-1` or `selection-data-click` for\n UI-like pops, `cinematic-transition-13` for one big reveal.\n\n## What to check in every version\n\nLook at real frames across the video (a contact sheet) before the user sees it \u2014 the JSON doesn't show these:\nblank or empty frames, overlapping or cut-off text, text too small or gone too fast, nothing moving or everything\nmoving at once, weak hierarchy, clashing colours. Fix them and look again; two rounds are usually enough.\n";
|
|
16597
16597
|
|
|
16598
16598
|
// src/motionEngine/sceneScriptGuide.js
|
|
16599
16599
|
var PREFERRED = {
|
|
@@ -18473,7 +18473,7 @@ ${Gh("Box · Text · Pin",jh("R","T","N"))}
|
|
|
18473
18473
|
${Gh("Hide · Crop · Range",jh("X","C","G"))}
|
|
18474
18474
|
</div>
|
|
18475
18475
|
</div>`}let c6e=!1;function u6e(t,{project:e,sources:n,strips:a,parts:s,config:r,assets:i=null,chat:o=null}={}){if(!t)throw new Error("GreenLightMotion.mountStudio needs an element");if(c6e)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=Y5t((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=[s5t,r5t,i5t,o5t,l5t,c5t,u5t].join(`
|
|
18476
|
-
`);c.textContent=o?l6e(d):d,document.head.append(c)}if(o&&!document.querySelector("style[data-greenlight-chat]")){const c=document.querySelector("style[data-greenlight-motion]");c&&(c.textContent=l6e(c.textContent));const d=document.createElement("style");d.setAttribute("data-greenlight-chat",""),d.textContent=d5t,document.head.insertBefore(d,c||document.querySelector("style[data-greenlight-studio]"))}window.LabNoteEngine={...R5t,zipStore:Ree};const l={el:t,assets:i};c6e=!0;for(const c of[P5t,O5t,B5t,L5t,F5t,z5t,U5t])c(l);return o&&(l.chat=H5t(l,o)),l}const Uee="0.1.
|
|
18476
|
+
`);c.textContent=o?l6e(d):d,document.head.append(c)}if(o&&!document.querySelector("style[data-greenlight-chat]")){const c=document.querySelector("style[data-greenlight-motion]");c&&(c.textContent=l6e(c.textContent));const d=document.createElement("style");d.setAttribute("data-greenlight-chat",""),d.textContent=d5t,document.head.insertBefore(d,c||document.querySelector("style[data-greenlight-studio]"))}window.LabNoteEngine={...R5t,zipStore:Ree};const l={el:t,assets:i};c6e=!0;for(const c of[P5t,O5t,B5t,L5t,F5t,z5t,U5t])c(l);return o&&(l.chat=H5t(l,o)),l}const Uee="0.1.5",wF=()=>{};_1t(),lyt();function q5t({project:t,playerRef:e}){const n=t.videoSize||{w:1920,h:1080};return de.jsx("div",{style:{width:n.w,height:n.h,background:"#000"},children:de.jsx(a9e,{ref:e,channels:t.channels||[],clips:t.clips||[],isPlaying:!1,setIsPlaying:wF,currentTime:0,playheadTime:0,setCurrentTime:wF,setPlayheadTime:wF,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 X5t=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 d6e(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");N$(e),Y$(e);const a=Joe.createRoot(t),s={current:null};let r=!1,i=aee(n);WL(i);let o=null;const l=d=>P_({channels:o?.channels||[],clips:o?.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:Uee,async load(d,{assets:h}={}){if(!d||typeof d!="object")throw new Error("GreenLightMotion.load needs a project");h&&(i.dispose(),i=aee(h),WL(i)),cyt();const u=await z_(d),f=await f9e(u,i);return o=f,a.render(de.jsx(q5t,{project:f,playerRef:s})),await X5t(()=>s.current,1e4,"the player"),{missing:B_()}},missingAssets:B_,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=wF,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(),i.dispose(),WL(null)}}}function f6e(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");N$("webgpu"),Y$("webgpu");const s=Joe.createRoot(t);return s.render(de.jsx(a5t,{project:e,assets:n,theme:a})),{unmount:()=>s.unmount()}}window.GreenLightMotion={create:d6e,mountPage:f6e,mountStudio:u6e,checkTemplate:r9e,checkSoundCues:O9e,sounds:o4,missingAssets:B_,pluginStatus:d9e,normalize:z_,trace:async(t,e)=>KMe(await z_(t),e),version:Uee};const Q5t=["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"],Gee=new WeakMap;class h6e{constructor(e){this.renderObjects=new WeakMap,this.hasNode=this.containsNode(e),this.hasAnimation=e.object.isSkinnedMesh===!0,this.refreshUniforms=Q5t,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:i,height:o}=e.context;n.bufferWidth=i,n.bufferHeight=o}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,i=this.getRenderObjectData(e);if(i.worldMatrix.equals(a.matrixWorld)!==!0)return i.worldMatrix.copy(a.matrixWorld),!1;const o=i.material;for(const v in o){const b=o[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 o[v]=M,!1}if(o.transmission>0){const{width:v,height:b}=e.context;if(i.bufferWidth!==v||i.bufferHeight!==b)return i.bufferWidth=v,i.bufferHeight=b,!1}const l=i.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 i.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(i.morphTargetInfluences){let v=!1;for(let b=0;b<i.morphTargetInfluences.length;b++)i.morphTargetInfluences[b]!==a.morphTargetInfluences[b]&&(i.morphTargetInfluences[b]=a.morphTargetInfluences[b],v=!0);if(v)return!1}if(i.lights){for(let v=0;v<n.length;v++)if(i.lights[v].map!==n[v].map)return!1}return i.center&&i.center.equals(a.center)===!1?(i.center.copy(a.center),!0):(e.bundle!==null&&(i.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(Gee.has(e)){const s=Gee.get(e);if(s.renderId===n)return s.lightsData}const a=this.getLightsData(e.getLights());return Gee.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 i=this.getLights(e.lightsNode,a);return this.equals(e,i)!==!0}}const K5t=[/^StackTrace\.js$/,/^TSLCore\.js$/,/^.*Node\.js$/,/^three\.webgpu.*\.js$/];function J5t(t){const e=/(?:at\s+(.+?)\s+\()?(?:(.+?)@)?([^@\s()]+):(\d+):(\d+)/;return t.split(`
|
|
18477
18477
|
`).map(n=>{const a=n.match(e);if(!a)return null;const s=a[1]||a[2]||"",r=a[3].split("?")[0],i=parseInt(a[4],10),o=parseInt(a[5],10),l=r.split("/").pop();return{fn:s,file:l,line:i,column:o}}).filter(n=>n&&!K5t.some(a=>a.test(n.file)))}class Ul{constructor(e=null){this.isStackTrace=!0,this.stack=J5t(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(`
|
|
18478
18478
|
`);return`${e}
|
|
18479
18479
|
${n}`}}function jee(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=>jee(t),X6=t=>jee(t),Q6=(...t)=>jee(t),Z5t=new Map([[1,"float"],[2,"vec2"],[3,"vec3"],[4,"vec4"],[9,"mat3"],[16,"mat4"]]),p6e=new WeakMap;function Vee(t){return Z5t.get(t)}function Y_(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 Hee(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;Pn(`TSL: Unsupported type: ${t}`,new Ul)}function m6e(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;Pn(`TSL: Unsupported type: ${t}`,new Ul)}function g6e(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;Pn(`TSL: Unsupported type: ${t}`,new Ul)}function V2(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 MF(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 Ut(...e):n==="vec2"?new Ce(...e):n==="vec3"?new ke(...e):n==="vec4"?new Un(...e):n==="mat2"?new k9(...e):n==="mat3"?new _a(...e):n==="mat4"?new Hn(...e):t==="bool"?e[0]||!1:t==="float"||t==="int"||t==="uint"?e[0]||0:t==="string"?e[0]||"":t==="ArrayBuffer"?y6e(e[0]):null}function Wee(t){let e=p6e.get(t);return e===void 0&&(e={},p6e.set(t,e)),e}function x6e(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 y6e(t){return Uint8Array.from(atob(t),e=>e.charCodeAt(0)).buffer}var e4t=Object.freeze({__proto__:null,arrayBufferToBase64:x6e,base64ToArrayBuffer:y6e,getAlignmentFromType:g6e,getDataFromObject:Wee,getLengthFromType:Hee,getMemoryLengthFromType:m6e,getTypeFromLength:Vee,getTypedArrayFromType:Y_,getValueFromType:MF,getValueType:V2,hash:Q6,hashArray:X6,hashString:f4});const K6={VERTEX:"vertex",FRAGMENT:"fragment"},zs={NONE:"none",FRAME:"frame",RENDER:"render",OBJECT:"object"},v6e={BOOLEAN:"bool",INTEGER:"int",FLOAT:"float",VECTOR2:"vec2",VECTOR3:"vec3",VECTOR4:"vec4",MATRIX2:"mat2",MATRIX3:"mat3",MATRIX4:"mat4"},Rc={READ_ONLY:"readOnly",WRITE_ONLY:"writeOnly",READ_WRITE:"readWrite"},$ee=["fragment","vertex"],SF=["setup","analyze","generate"],AF=[...$ee,"compute"],kv=["x","y","z","w"],t4t={analyze:"setup",generate:"analyze"};let n4t=0;class Fa extends Gd{static get type(){return"Node"}constructor(e=null){super(),this.nodeType=e,this.updateType=zs.NONE,this.updateBeforeType=zs.NONE,this.updateAfterType=zs.NONE,this.uuid=Wc.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:n4t++}),this.stackTrace=null,Fa.captureStackTrace===!0&&(this.stackTrace=new Ul)}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,zs.FRAME)}onRenderUpdate(e){return this.onUpdate(e,zs.RENDER)}onObjectUpdate(e){return this.onUpdate(e,zs.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 i=s[r];i&&i.isNode===!0&&n.push({property:a,index:r,childNode:i})}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 i=s[r];i&&i.isNode===!0&&n.push({property:a,index:r,childNode:i})}}}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=Q6(X6(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=t4t[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 i=null;const o=e.getBuildStage();if(o==="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)}i=l.outputNode}else if(o==="analyze")this.analyze(e,n);else if(o==="generate"){if(this.generate.length<2){const c=this.getNodeType(e),d=e.getDataFromNode(this);i=d.snippet,i===void 0?d.generated===void 0?(d.generated=!0,i=this.generate(e)||"",d.snippet=i):(Nn("Node: Recursion detected.",this),i="/* Recursion detected. */"):d.flowCodes!==void 0&&e.context.nodeBlock!==void 0&&e.addFlowCodeHierarchy(this,e.context.nodeBlock),i=e.format(i,c,n)}else i=this.generate(e,n)||"";i===""&&n!==null&&n!=="void"&&n!=="OutputType"&&(Pn(`TSL: Invalid generated code, expected a "${n}".`),i=e.generateConst(n))}return e.removeChain(this),i}getSerializeChildren(){return this._getChildren()}serialize(e){const n=this.getSerializeChildren(),a={};for(const{property:s,index:r,childNode:i}of n)r!==void 0?(a[s]===void 0&&(a[s]=Number.isInteger(r)?[]:{}),a[s][r]=i.toJSON(e.meta).uuid):a[s]=i.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 i=e.inputNodes[a][r];s[r]=n[i]}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 i(o){const l=[];for(const c in o){const d=o[c];delete d.metadata,l.push(d)}return l}if(s){const o=i(e.textures),l=i(e.images),c=i(e.nodes);o.length>0&&(r.textures=o),l.length>0&&(r.images=l),c.length>0&&(r.nodes=c)}return r}}Fa.captureStackTrace=!1;class Dv extends Fa{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 Yee extends Fa{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 Sl extends Fa{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 i=super.build(e,s),o=e.getVarFromNode(this,null,s),l=e.getPropertyName(o);return e.addLineFlowCode(`${l} = ${i}`,this),r.snippet=i,r.propertyName=l,e.format(r.propertyName,s,n)}}return super.build(e,n)}}class b6e extends Sl{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,i=e.getComponentType(a),o=[];let l=0;for(const d of r){if(l>=s){Pn(`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&&(Pn(`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)!==i){const m=e.getTypeFromLength(u,i);f=e.format(f,h,m)}o.push(f)}const c=`${e.getType(a)}( ${o.join(", ")} )`;return e.format(c,a,n)}}const a4t=kv.join("");class qee extends Fa{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(kv.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 i=null;this.getVectorLength()>=s&&(i=e.getTypeFromLength(this.getVectorLength(),this.getComponentType(e)));const l=a.build(e,i);this.components.length===s&&this.components===a4t.slice(0,this.components.length)?r=e.format(l,i,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 w6e extends Sl{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),i=e.getComponentType(s.getNodeType(e)),o=e.getTypeFromLength(a.length,i),l=s.build(e,o),c=n.build(e,r),d=e.getTypeLength(r),h=[];for(let u=0;u<d;u++){const f=kv[u];f===a[0]?(h.push(l),u+=a.length-1):h.push(c+"."+f)}return`${e.getType(r)}( ${h.join(", ")} )`}}class M6e extends Sl{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),i=e.getVarFromNode(this),o=e.getPropertyName(i);e.addLineFlowCode(o+" = "+r,this);const l=e.getTypeLength(s),c=[];let d=0;for(let h=0;h<l;h++){const u=kv[h];u===n[d]?(c.push("1.0 - "+(o+"."+u)),d++):c.push(o+"."+u)}return`${e.getType(s)}( ${c.join(", ")} )`}}class TF extends Fa{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?V2(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=V2(this.value),e.nodeType=this.nodeType,e.valueType==="ArrayBuffer"&&(e.value=x6e(e.value)),e.precision=this.precision}deserialize(e){super.deserialize(e),this.nodeType=e.nodeType,this.value=Array.isArray(e.value)?MF(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 S6e=/float|u?int/;class ah extends TF{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 S6e.test(a)&&S6e.test(n)?e.generateConst(n,this.value):e.format(this.generateConst(e),a,n)}}class A6e extends Fa{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 Nv=null;const Xee=new Map;function Gn(t,e){if(Xee.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`);Xee.set(t,e),t!=="assign"&&(Fa.prototype[t]=function(...n){return this.isStackNode?this.addToStack(e(...n)):e(this,...n)},Fa.prototype[t+"Assign"]=function(...n){return this.isStackNode?this.assign(n[0],e(...n)):this.assign(e(this,...n))})}const s4t=t=>t.replace(/r|s/g,"x").replace(/g|t/g,"y").replace(/b|p/g,"z").replace(/a|q/g,"w"),T6e=t=>s4t(t).split("").sort().join("");Fa.prototype.assign=function(...t){if(this.isStackNode!==!0)return Nv!==null?Nv.assign(this,...t):Pn("TSL: No stack defined for assign operation. Make sure the assign is inside a Fn().",new Ul),this;{const e=Xee.get("assign");return this.addToStack(e(...t))}},Fa.prototype.toVarIntent=function(){return this},Fa.prototype.get=function(t){return new A6e(this,t)};const q_={};function CF(t,e,n){q_[t]=q_[e]=q_[n]={get(){this._cache=this._cache||{};let i=this._cache[t];return i===void 0&&(i=new qee(this,t),this._cache[t]=i),i},set(i){this[t].assign(za(i))}};const a=t.toUpperCase(),s=e.toUpperCase(),r=n.toUpperCase();Fa.prototype["set"+a]=Fa.prototype["set"+s]=Fa.prototype["set"+r]=function(i){const o=T6e(t);return new w6e(this,o,za(i))},Fa.prototype["flip"+a]=Fa.prototype["flip"+s]=Fa.prototype["flip"+r]=function(){const i=T6e(t);return new M6e(this,i)}}const L1=["x","y","z","w"],F1=["r","g","b","a"],z1=["s","t","p","q"];for(let t=0;t<4;t++){let e=L1[t],n=F1[t],a=z1[t];CF(e,n,a);for(let s=0;s<4;s++){e=L1[t]+L1[s],n=F1[t]+F1[s],a=z1[t]+z1[s],CF(e,n,a);for(let r=0;r<4;r++){e=L1[t]+L1[s]+L1[r],n=F1[t]+F1[s]+F1[r],a=z1[t]+z1[s]+z1[r],CF(e,n,a);for(let i=0;i<4;i++)e=L1[t]+L1[s]+L1[r]+L1[i],n=F1[t]+F1[s]+F1[r]+F1[i],a=z1[t]+z1[s]+z1[r]+z1[i],CF(e,n,a)}}}for(let t=0;t<32;t++)q_[t]={get(){this._cache=this._cache||{};let e=this._cache[t];return e===void 0&&(e=new Dv(this,new ah(t,"uint")),this._cache[t]=e),e},set(e){this[t].assign(za(e))}};Object.defineProperties(Fa.prototype,q_);const C6e=new WeakMap,r4t=function(t,e=null){const n=V2(t);return n==="node"?t:e===null&&(n==="float"||n==="boolean")||n&&n!=="shader"&&n!=="string"?za(Zee(t,e)):n==="shader"?t.isFn?t:Kt(t):t},i4t=function(t,e=null){for(const n in t)t[n]=za(t[n],e);return t},o4t=function(t,e=null){const n=t.length;for(let a=0;a<n;a++)t[a]=za(t[a],e);return t},_6e=function(t,e=null,n=null,a=null){function s(d){return a!==null?(d=za(Object.assign(d,a)),a.intent===!0&&(d=d.toVarIntent())):d=za(d),d}let r,i=e,o,l;function c(d){let h;return i?h=/[a-z]/i.test(i)?i+"()":i:h=t.type,o!==void 0&&d.length<o?(Pn(`TSL: "${h}" parameter length is less than minimum required.`,new Ul),d.concat(new Array(o-d.length).fill(0))):l!==void 0&&d.length>l?(Pn(`TSL: "${h}" parameter length exceeds limit.`,new Ul),d.slice(0,l)):d}return e===null?r=(...d)=>s(new t(...h4(c(d)))):n!==null?(n=za(n),r=(...d)=>s(new t(e,...h4(c(d)),n))):r=(...d)=>s(new t(e,...h4(c(d)))),r.setParameterLength=(...d)=>(d.length===1?o=l=d[0]:d.length===2&&([o,l]=d),r),r.setName=d=>(i=d,r),r},l4t=function(t,...e){return new t(...h4(e))};class c4t extends Fa{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)||"",i=r||"default";if(s[i])return s[i];const o=e.subBuildFn,l=e.fnCall;e.subBuildFn=r,e.fnCall=this;let c=null;if(n.layout){let d=C6e.get(e.constructor);d===void 0&&(d=new WeakMap,C6e.set(e.constructor,d));let h=d.get(n);h===void 0&&(h=za(e.buildFunctionNode(n)),d.set(n,h)),e.addInclude(h);const u=a?u4t(a):null;c=za(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?d4t(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=za(p)}return e.subBuildFn=o,e.fnCall=l,n.once&&(s[i]=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),i=e.getSubBuildOutput(this),o=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[i]=this.getOutputNode(e),r[i].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[i]}else s==="analyze"?o.build(e,n):s==="generate"&&(a=o.build(e,n)||"");return e.fnCall=l,a}}function u4t(t){let e;return EF(t),t[0]&&(t[0].isNode||Object.getPrototypeOf(t[0])!==Object.prototype)?e=[...t]:e=t[0],e}function d4t(t){let e=0;return EF(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 i of t)yield za(i)};else{if(t.length>0)if(Object.getPrototypeOf(t[0])===Object.prototype){const i=t[0];i[a]===void 0?r=i[e++]:r=Reflect.get(i,a,s)}else t[0]instanceof Fa&&(t[a]===void 0?r=t[e++]:r=Reflect.get(t,a,s));else r=Reflect.get(n,a,s);r=za(r)}return r}})}class f4t extends Fa{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 c4t(this,e)}setup(){return this.call()}}const h4t=[!1,!0],p4t=[0,1,2,3],m4t=[-1,-2],I6e=[.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],Qee=new Map;for(const t of h4t)Qee.set(t,new ah(t));const Kee=new Map;for(const t of p4t)Kee.set(t,new ah(t,"uint"));const Jee=new Map([...Kee].map(t=>new ah(t.value,"int")));for(const t of m4t)Jee.set(t,new ah(t,"int"));const _F=new Map([...Jee].map(t=>new ah(t.value)));for(const t of I6e)_F.set(t,new ah(t));for(const t of I6e)_F.set(-t,new ah(-t));const IF={bool:Qee,uint:Kee,ints:Jee,float:_F},E6e=new Map([...Qee,..._F]),Zee=(t,e)=>E6e.has(t)?E6e.get(t):t.isNode===!0?t:new ah(t,e),G0=function(t,e=null){return(...n)=>{for(const s of n)if(s===void 0)return Pn(`TSL: Invalid parameter for the type "${t}".`,new Ul),new ah(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=[MF(t,...n)]),n.length===1&&e!==null&&e.has(n[0]))return Q_(e.get(n[0]));if(n.length===1){const s=Zee(n[0],t);return s.nodeType===t?Q_(s):Q_(new Yee(s,t))}const a=n.map(s=>Zee(s));return Q_(new b6e(a,t))}},X_=t=>typeof t=="object"&&t!==null?t.value:t,k6e=t=>t!=null?t.nodeType||t.convertTo||(typeof t=="string"?t:null):null;function J6(t,e){return new f4t(t,e)}const za=(t,e=null)=>r4t(t,e),Q_=(t,e=null)=>za(t,e).toVarIntent(),EF=(t,e=null)=>new i4t(t,e),h4=(t,e=null)=>new o4t(t,e),sr=(t,e=null,n=null,a=null)=>new _6e(t,e,n,a),Sa=(t,...e)=>new l4t(t,...e),ha=(t,e=null,n=null,a={})=>new _6e(t,e,n,{...a,intent:!0});let g4t=0;class x4t extends Fa{constructor(e,n=null){super();let a=null;n!==null&&(typeof n=="object"?a=n.return:(typeof n=="string"?a=n:Pn("TSL: Invalid layout type.",new Ul),n=null)),this.shaderNode=new J6(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"+g4t++,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 Pn('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 x4t(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,i){return Reflect.set(n,s,r,i)}})}const K_=t=>{Nv=t},ete=()=>Nv,ja=(...t)=>Nv.If(...t),y4t=(...t)=>Nv.Switch(...t);function kF(t){return Nv&&Nv.addToStack(t),t}Gn("toStack",kF);const D6e=new G0("color"),vt=new G0("float",IF.float),Ft=new G0("int",IF.ints),ea=new G0("uint",IF.uint),Vh=new G0("bool",IF.bool),ia=new G0("vec2"),j0=new G0("ivec2"),tte=new G0("uvec2"),N6e=new G0("bvec2"),Gt=new G0("vec3"),nte=new G0("ivec3"),p4=new G0("uvec3"),ate=new G0("bvec3"),Da=new G0("vec4"),ste=new G0("ivec4"),rte=new G0("uvec4"),R6e=new G0("bvec4"),DF=new G0("mat2"),Pc=new G0("mat3"),Rv=new G0("mat4"),v4t=(t="")=>new ah(t,"string"),b4t=t=>new ah(t,"ArrayBuffer");Gn("toColor",D6e),Gn("toFloat",vt),Gn("toInt",Ft),Gn("toUint",ea),Gn("toBool",Vh),Gn("toVec2",ia),Gn("toIVec2",j0),Gn("toUVec2",tte),Gn("toBVec2",N6e),Gn("toVec3",Gt),Gn("toIVec3",nte),Gn("toUVec3",p4),Gn("toBVec3",ate),Gn("toVec4",Da),Gn("toIVec4",ste),Gn("toUVec4",rte),Gn("toBVec4",R6e),Gn("toMat2",DF),Gn("toMat3",Pc),Gn("toMat4",Rv);const P6e=sr(Dv).setParameterLength(2),O6e=(t,e)=>new Yee(za(t),e),w4t=(t,e)=>new qee(za(t),e);Gn("element",P6e),Gn("convert",O6e);const M4t=t=>(Nn("TSL: append() has been renamed to Stack().",new Ul),kF(t));Gn("append",t=>(Nn("TSL: .append() has been renamed to .toStack().",new Ul),kF(t)));class Pi extends Fa{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 cm=(t,e)=>new Pi(t,e),sh=(t,e)=>new Pi(t,e,!0),ao=Sa(Pi,"vec4","DiffuseColor"),m4=Sa(Pi,"vec3","DiffuseContribution"),ite=Sa(Pi,"vec3","EmissiveColor"),U1=Sa(Pi,"float","Roughness"),H2=Sa(Pi,"float","Metalness"),NF=Sa(Pi,"float","Clearcoat"),Z6=Sa(Pi,"float","ClearcoatRoughness"),rh=Sa(Pi,"vec3","Sheen"),Pv=Sa(Pi,"float","SheenRoughness"),RF=Sa(Pi,"float","Iridescence"),PF=Sa(Pi,"float","IridescenceIOR"),OF=Sa(Pi,"float","IridescenceThickness"),BF=Sa(Pi,"float","AlphaT"),Ov=Sa(Pi,"float","Anisotropy"),J_=Sa(Pi,"vec3","AnisotropyT"),g4=Sa(Pi,"vec3","AnisotropyB"),Bv=Sa(Pi,"color","SpecularColor"),x4=Sa(Pi,"color","SpecularColorBlended"),y4=Sa(Pi,"float","SpecularF90"),LF=Sa(Pi,"float","Shininess"),e8=Sa(Pi,"vec4","Output"),v4=Sa(Pi,"float","dashSize"),Z_=Sa(Pi,"float","gapSize"),S4t=Sa(Pi,"float","pointWidth"),eI=Sa(Pi,"float","IOR"),FF=Sa(Pi,"float","Transmission"),ote=Sa(Pi,"float","Thickness"),lte=Sa(Pi,"float","AttenuationDistance"),cte=Sa(Pi,"color","AttenuationColor"),ute=Sa(Pi,"float","Dispersion");class dte extends Fa{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 B6e=t=>new dte(t),zF=(t,e=0)=>new dte(t,!0,e),L6e=zF("frame"),rs=zF("render"),fte=B6e("object");class t8 extends TF{static get type(){return"UniformNode"}constructor(e,n=null){super(e,n),this.isUniformNode=!0,this.name="",this.groupNode=fte}setName(e){return this.name=e,this}label(e){return Nn('TSL: "label()" has been deprecated. Use "setName()" instead.',new Ul),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 i=r.getInputType(e),o=e.getUniformFromNode(r,i,e.shaderStage,this.name||e.context.nodeName),l=e.getPropertyName(o);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,i,a),e.addLineFlowCode(`${h} = ${c}`,this)}c=h}return e.format(c,a,n)}}const Qa=(t,e)=>{const n=k6e(e||t);if(n===t&&(t=MF(n)),t&&t.isNode===!0){let a=t.value;t.traverse(s=>{s.isConstNode===!0&&(a=s.value)}),t=a}return new t8(t,n)};class hte extends Sl{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 F6e=(...t)=>{let e;if(t.length===1){const n=t[0];e=new hte(null,n.length,n)}else{const n=t[0],a=t[1];e=new hte(n,a)}return za(e)};Gn("toArray",(t,e)=>F6e(Array(e).fill(t)));class z6e extends Sl{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 kv.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 i=e.getNodeProperties(this);i.sourceNode=a,i.targetNode=n.context({assign:!0})}generate(e,n){const{targetNode:a,sourceNode:s}=e.getNodeProperties(this),r=this.needsSplitAssign(e),i=a.build(e),o=a.getNodeType(e),l=s.build(e,o),c=s.getNodeType(e),d=e.getDataFromNode(this);let h;if(d.initialized===!0)n!=="void"&&(h=i);else if(r){const u=e.getVarFromNode(this,null,o),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=i)}else h=`${i} = ${l}`,(n==="void"||c==="void")&&(e.addLineFlowCode(h,this),n!=="void"&&(h=i));return d.initialized=!0,e.format(h,o,n)}}const U6e=sr(z6e).setParameterLength(2);Gn("assign",U6e);class G6e extends Sl{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,i=(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)Pn("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(Pn("TSL: The number of provided parameters is less than the expected number of inputs in 'Fn()'.");r.length<s.length;)r.push(vt(0));for(let l=0;l<r.length;l++)n.push(i(r[l],s[l]))}else for(const l of s){const c=r[l.name];c!==void 0?n.push(i(c,l)):(Pn(`TSL: Input '${l.name}' not found in 'Fn()'.`),n.push(i(vt(0),l)))}return`${a.build(e,"property")}( ${n.join(", ")} )`}}const j6e=(t,...e)=>(e=e.length>1||e[0]&&e[0].isNode===!0?h4(e):EF(e[0]),new G6e(za(t),e));Gn("call",j6e);const A4t={"==":"equal","!=":"notEqual","<":"lessThan",">":"greaterThan","<=":"lessThanEqual",">=":"greaterThanEqual","%":"mod"};class al extends Sl{static get type(){return"OperatorNode"}constructor(e,n,a,...s){if(super(),s.length>0){let r=new al(e,n,a);for(let i=0;i<s.length-1;i++)r=new al(e,r,s[i]);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(A4t[this.op],n)}getNodeType(e,n=null){const a=this.op,s=this.aNode,r=this.bNode,i=s.getNodeType(e),o=r?r.getNodeType(e):null;if(i==="void"||o==="void")return n||"void";if(a==="%")return i;if(a==="~"||a==="&"||a==="|"||a==="^"||a===">>"||a==="<<")return e.getIntegerType(i);if(a==="!"||a==="&&"||a==="||"||a==="^^")return"bool";if(a==="=="||a==="!="||a==="<"||a===">"||a==="<="||a===">="){const l=Math.max(e.getTypeLength(i),e.getTypeLength(o));return l>1?`bvec${l}`:"bool"}else{if(e.isMatrix(i)){if(o==="float")return i;if(e.isVector(o))return e.getVectorFromMatrix(i);if(e.isMatrix(o))return i}else if(e.isMatrix(o)){if(i==="float")return o;if(e.isVector(i))return e.getVectorFromMatrix(o)}return e.getTypeLength(o)>e.getTypeLength(i)?o:i}}generate(e,n){const a=this.op,{aNode:s,bNode:r}=this,i=this.getNodeType(e,n);let o=null,l=null;i!=="void"?(o=s.getNodeType(e),l=r?r.getNodeType(e):null,a==="<"||a===">"||a==="<="||a===">="||a==="=="||a==="!="?e.isVector(o)?l=o:e.isVector(l)?o=l:o!==l&&(o=l="float"):a===">>"||a==="<<"?(o=i,l=e.changeComponentType(l,"uint")):a==="%"?(o=i,l=e.isInteger(o)&&e.isInteger(l)?l:o):e.isMatrix(o)?l==="float"?l="float":e.isVector(l)?l=e.getVectorFromMatrix(o):e.isMatrix(l)||(o=l=i):e.isMatrix(l)?o==="float"?o="float":e.isVector(o)?o=e.getVectorFromMatrix(l):o=l=i:o=l=i):o=l=i;const c=s.build(e,o),d=r?r.build(e,l):null,h=e.getFunctionOperator(a);if(n!=="void"){const u=e.renderer.coordinateSystem===yc;if(a==="=="||a==="!="||a==="<"||a===">"||a==="<="||a===">=")return u?e.isVector(o)?e.format(`${this.getOperatorMethod(e,n)}( ${c}, ${d} )`,i,n):e.format(`( ${c} ${a} ${d} )`,i,n):e.format(`( ${c} ${a} ${d} )`,i,n);if(a==="%")return e.isInteger(l)?e.format(`( ${c} % ${d} )`,i,n):e.format(`${this.getOperatorMethod(e,i)}( ${c}, ${d} )`,i,n);if(a==="!"||a==="~")return e.format(`(${a}${c})`,o,n);if(h)return e.format(`${h}( ${c}, ${d} )`,i,n);if(e.isMatrix(o)&&l==="float")return e.format(`( ${d} ${a} ${c} )`,i,n);if(o==="float"&&e.isMatrix(l))return e.format(`${c} ${a} ${d}`,i,n);{let f=`( ${c} ${a} ${d} )`;return!u&&i==="bool"&&e.isVector(o)&&e.isVector(l)&&(f=`all${f}`),e.format(f,i,n)}}else if(o!=="void")return h?e.format(`${h}( ${c}, ${d} )`,i,n):e.isMatrix(o)&&l==="float"?e.format(`${d} ${a} ${c}`,i,n):e.format(`${c} ${a} ${d}`,i,n)}serialize(e){super.serialize(e),e.op=this.op}deserialize(e){super.deserialize(e),this.op=e.op}}const _0=ha(al,"+").setParameterLength(2,1/0).setName("add"),si=ha(al,"-").setParameterLength(2,1/0).setName("sub"),Vs=ha(al,"*").setParameterLength(2,1/0).setName("mul"),ih=ha(al,"/").setParameterLength(2,1/0).setName("div"),tI=ha(al,"%").setParameterLength(2).setName("mod"),V6e=ha(al,"==").setParameterLength(2).setName("equal"),H6e=ha(al,"!=").setParameterLength(2).setName("notEqual"),W6e=ha(al,"<").setParameterLength(2).setName("lessThan"),pte=ha(al,">").setParameterLength(2).setName("greaterThan"),$6e=ha(al,"<=").setParameterLength(2).setName("lessThanEqual"),Y6e=ha(al,">=").setParameterLength(2).setName("greaterThanEqual"),q6e=ha(al,"&&").setParameterLength(2,1/0).setName("and"),X6e=ha(al,"||").setParameterLength(2,1/0).setName("or"),Q6e=ha(al,"!").setParameterLength(1).setName("not"),K6e=ha(al,"^^").setParameterLength(2).setName("xor"),J6e=ha(al,"&").setParameterLength(2).setName("bitAnd"),Z6e=ha(al,"~").setParameterLength(1).setName("bitNot"),e8e=ha(al,"|").setParameterLength(2).setName("bitOr"),t8e=ha(al,"^").setParameterLength(2).setName("bitXor"),n8e=ha(al,"<<").setParameterLength(2).setName("shiftLeft"),a8e=ha(al,">>").setParameterLength(2).setName("shiftRight"),s8e=Kt(([t])=>(t.addAssign(1),t)),r8e=Kt(([t])=>(t.subAssign(1),t)),i8e=Kt(([t])=>{const e=Ft(t).toConst();return t.addAssign(1),e}),o8e=Kt(([t])=>{const e=Ft(t).toConst();return t.subAssign(1),e});Gn("add",_0),Gn("sub",si),Gn("mul",Vs),Gn("div",ih),Gn("mod",tI),Gn("equal",V6e),Gn("notEqual",H6e),Gn("lessThan",W6e),Gn("greaterThan",pte),Gn("lessThanEqual",$6e),Gn("greaterThanEqual",Y6e),Gn("and",q6e),Gn("or",X6e),Gn("not",Q6e),Gn("xor",K6e),Gn("bitAnd",J6e),Gn("bitNot",Z6e),Gn("bitOr",e8e),Gn("bitXor",t8e),Gn("shiftLeft",n8e),Gn("shiftRight",a8e),Gn("incrementBefore",s8e),Gn("decrementBefore",r8e),Gn("increment",i8e),Gn("decrement",o8e);const l8e=(t,e)=>(Nn('TSL: "modInt()" is deprecated. Use "mod( int( ... ) )" instead.',new Ul),tI(Ft(t),Ft(e)));Gn("modInt",l8e);class gn extends Sl{static get type(){return"MathNode"}constructor(e,n,a=null,s=null){if(super(),(e===gn.MAX||e===gn.MIN)&&arguments.length>3){let r=new gn(e,n,a);for(let i=2;i<arguments.length-1;i++)r=new gn(e,r,arguments[i]);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),i=e.isMatrix(a)?0:e.getTypeLength(a),o=e.isMatrix(s)?0:e.getTypeLength(s);return r>i&&r>o?n:i>o?a:o>r?s:n}getNodeType(e){const n=this.method;return n===gn.LENGTH||n===gn.DISTANCE||n===gn.DOT?"float":n===gn.CROSS?"vec3":n===gn.ALL||n===gn.ANY?"bool":n===gn.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===gn.ONE_MINUS)r=si(1,n);else if(s===gn.RECIPROCAL)r=ih(1,n);else if(s===gn.DIFFERENCE)r=Ao(si(n,a));else if(s===gn.TRANSFORM_DIRECTION){let i=n,o=a;e.isMatrix(i.getNodeType(e))?o=Da(Gt(o),0):i=Da(Gt(i),0);const l=Vs(i,o).xyz;r=Jd(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),i=this.getInputType(e),o=this.aNode,l=this.bNode,c=this.cNode,d=e.renderer.coordinateSystem;if(s===gn.NEGATE)return e.format("( - "+o.build(e,i)+" )",r,n);{const h=[];return s===gn.CROSS?h.push(o.build(e,r),l.build(e,r)):d===yc&&s===gn.STEP?h.push(o.build(e,e.getTypeLength(o.getNodeType(e))===1?"float":i),l.build(e,i)):d===yc&&(s===gn.MIN||s===gn.MAX)?h.push(o.build(e,i),l.build(e,e.getTypeLength(l.getNodeType(e))===1?"float":i)):s===gn.REFRACT?h.push(o.build(e,i),l.build(e,i),c.build(e,"float")):s===gn.MIX?h.push(o.build(e,i),l.build(e,i),c.build(e,e.getTypeLength(c.getNodeType(e))===1?"float":i)):(d===vc&&s===gn.ATAN&&l!==null&&(s="atan2"),e.shaderStage!=="fragment"&&(s===gn.DFDX||s===gn.DFDY)&&(Nn(`TSL: '${s}' is not supported in the ${e.shaderStage} stage.`,this.stackTrace),s="/*"+s+"*/"),h.push(o.build(e,i)),l!==null&&h.push(l.build(e,i)),c!==null&&h.push(c.build(e,i))),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}}gn.ALL="all",gn.ANY="any",gn.RADIANS="radians",gn.DEGREES="degrees",gn.EXP="exp",gn.EXP2="exp2",gn.LOG="log",gn.LOG2="log2",gn.SQRT="sqrt",gn.INVERSE_SQRT="inversesqrt",gn.FLOOR="floor",gn.CEIL="ceil",gn.NORMALIZE="normalize",gn.FRACT="fract",gn.SIN="sin",gn.COS="cos",gn.TAN="tan",gn.ASIN="asin",gn.ACOS="acos",gn.ATAN="atan",gn.ABS="abs",gn.SIGN="sign",gn.LENGTH="length",gn.NEGATE="negate",gn.ONE_MINUS="oneMinus",gn.DFDX="dFdx",gn.DFDY="dFdy",gn.ROUND="round",gn.RECIPROCAL="reciprocal",gn.TRUNC="trunc",gn.FWIDTH="fwidth",gn.TRANSPOSE="transpose",gn.DETERMINANT="determinant",gn.INVERSE="inverse",gn.EQUALS="equals",gn.MIN="min",gn.MAX="max",gn.STEP="step",gn.REFLECT="reflect",gn.DISTANCE="distance",gn.DIFFERENCE="difference",gn.DOT="dot",gn.CROSS="cross",gn.POW="pow",gn.TRANSFORM_DIRECTION="transformDirection",gn.MIX="mix",gn.CLAMP="clamp",gn.REFRACT="refract",gn.SMOOTHSTEP="smoothstep",gn.FACEFORWARD="faceforward";const mte=vt(1e-6),T4t=vt(1e6),UF=vt(Math.PI),C4t=vt(Math.PI*2),_4t=vt(Math.PI*2),I4t=vt(Math.PI*.5),c8e=ha(gn,gn.ALL).setParameterLength(1),u8e=ha(gn,gn.ANY).setParameterLength(1),d8e=ha(gn,gn.RADIANS).setParameterLength(1),f8e=ha(gn,gn.DEGREES).setParameterLength(1),gte=ha(gn,gn.EXP).setParameterLength(1),n8=ha(gn,gn.EXP2).setParameterLength(1),GF=ha(gn,gn.LOG).setParameterLength(1),G1=ha(gn,gn.LOG2).setParameterLength(1),gd=ha(gn,gn.SQRT).setParameterLength(1),xte=ha(gn,gn.INVERSE_SQRT).setParameterLength(1),j1=ha(gn,gn.FLOOR).setParameterLength(1),jF=ha(gn,gn.CEIL).setParameterLength(1),Jd=ha(gn,gn.NORMALIZE).setParameterLength(1),um=ha(gn,gn.FRACT).setParameterLength(1),xd=ha(gn,gn.SIN).setParameterLength(1),dm=ha(gn,gn.COS).setParameterLength(1),h8e=ha(gn,gn.TAN).setParameterLength(1),p8e=ha(gn,gn.ASIN).setParameterLength(1),yte=ha(gn,gn.ACOS).setParameterLength(1),vte=ha(gn,gn.ATAN).setParameterLength(1,2),Ao=ha(gn,gn.ABS).setParameterLength(1),bte=ha(gn,gn.SIGN).setParameterLength(1),V1=ha(gn,gn.LENGTH).setParameterLength(1),wte=ha(gn,gn.NEGATE).setParameterLength(1),m8e=ha(gn,gn.ONE_MINUS).setParameterLength(1),Mte=ha(gn,gn.DFDX).setParameterLength(1),Ste=ha(gn,gn.DFDY).setParameterLength(1),g8e=ha(gn,gn.ROUND).setParameterLength(1),x8e=ha(gn,gn.RECIPROCAL).setParameterLength(1),Ate=ha(gn,gn.TRUNC).setParameterLength(1),Tte=ha(gn,gn.FWIDTH).setParameterLength(1),y8e=ha(gn,gn.TRANSPOSE).setParameterLength(1),v8e=ha(gn,gn.DETERMINANT).setParameterLength(1),b8e=ha(gn,gn.INVERSE).setParameterLength(1),Zd=ha(gn,gn.MIN).setParameterLength(2,1/0),Al=ha(gn,gn.MAX).setParameterLength(2,1/0),b4=ha(gn,gn.STEP).setParameterLength(2),w8e=ha(gn,gn.REFLECT).setParameterLength(2),M8e=ha(gn,gn.DISTANCE).setParameterLength(2),S8e=ha(gn,gn.DIFFERENCE).setParameterLength(2),Hh=ha(gn,gn.DOT).setParameterLength(2),W2=ha(gn,gn.CROSS).setParameterLength(2),Wh=ha(gn,gn.POW).setParameterLength(2),Cte=t=>Vs(t,t),A8e=t=>Vs(t,t,t),_te=t=>Vs(t,t,t,t),T8e=ha(gn,gn.TRANSFORM_DIRECTION).setParameterLength(2),C8e=t=>Vs(bte(t),Wh(Ao(t),1/3)),Ite=t=>Hh(t,t),Lr=ha(gn,gn.MIX).setParameterLength(3),fm=(t,e=0,n=1)=>new gn(gn.CLAMP,za(t),za(e),za(n)),VF=t=>fm(t),Ete=ha(gn,gn.REFRACT).setParameterLength(3),oh=ha(gn,gn.SMOOTHSTEP).setParameterLength(3),kte=ha(gn,gn.FACEFORWARD).setParameterLength(3),_8e=Kt(([t])=>{const a=43758.5453,s=Hh(t.xy,ia(12.9898,78.233)),r=tI(s,UF);return um(xd(r).mul(a))}),I8e=(t,e,n)=>Lr(e,n,t),E8e=(t,e,n)=>oh(e,n,t),k8e=(t,e)=>b4(e,t),E4t=kte,k4t=xte;Gn("all",c8e),Gn("any",u8e),Gn("radians",d8e),Gn("degrees",f8e),Gn("exp",gte),Gn("exp2",n8),Gn("log",GF),Gn("log2",G1),Gn("sqrt",gd),Gn("inverseSqrt",xte),Gn("floor",j1),Gn("ceil",jF),Gn("normalize",Jd),Gn("fract",um),Gn("sin",xd),Gn("cos",dm),Gn("tan",h8e),Gn("asin",p8e),Gn("acos",yte),Gn("atan",vte),Gn("abs",Ao),Gn("sign",bte),Gn("length",V1),Gn("lengthSq",Ite),Gn("negate",wte),Gn("oneMinus",m8e),Gn("dFdx",Mte),Gn("dFdy",Ste),Gn("round",g8e),Gn("reciprocal",x8e),Gn("trunc",Ate),Gn("fwidth",Tte),Gn("min",Zd),Gn("max",Al),Gn("step",k8e),Gn("reflect",w8e),Gn("distance",M8e),Gn("dot",Hh),Gn("cross",W2),Gn("pow",Wh),Gn("pow2",Cte),Gn("pow3",A8e),Gn("pow4",_te),Gn("transformDirection",T8e),Gn("mix",I8e),Gn("clamp",fm),Gn("refract",Ete),Gn("smoothstep",E8e),Gn("faceForward",kte),Gn("difference",S8e),Gn("saturate",VF),Gn("cbrt",C8e),Gn("transpose",y8e),Gn("determinant",v8e),Gn("inverse",b8e),Gn("rand",_8e);class D8e extends Fa{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 i=e.context.uniformFlow,o=e.getNodeProperties(this);o.condNode=n,o.ifNode=i?a:a.context({nodeBlock:a}),o.elseNode=s?i?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:i,elseNode:o}=e.getNodeProperties(this),l=e.currentFunctionNode,c=n!=="void",d=c?cm(a).build(e):"";s.nodeProperty=d;const h=r.build(e,"bool");if(e.context.uniformFlow&&o!==null){const p=i.build(e,a),m=o.build(e,a),g=e.getTernary(h,p,m);return e.format(g,a,n)}e.addFlowCode(`
|
|
@@ -77778,7 +77778,7 @@ function fwt(t, { project: e, sources: a, strips: r, parts: n, config: i, assets
|
|
|
77778
77778
|
for (const u of [ewt, twt, awt, rwt, nwt, iwt, owt]) u(l);
|
|
77779
77779
|
return s && (l.chat = cwt(l, s)), l;
|
|
77780
77780
|
}
|
|
77781
|
-
const NTe = "0.1.
|
|
77781
|
+
const NTe = "0.1.5", ez = () => {
|
|
77782
77782
|
};
|
|
77783
77783
|
N2t(), xyt();
|
|
77784
77784
|
function mwt({ project: t, playerRef: e }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "greenlight-motion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"greenlight-motion": "cli/index.mjs"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"prepack": "npm --prefix ../frontend run build
|
|
34
|
+
"prepack": "npm --prefix ../frontend run build"
|
|
35
35
|
},
|
|
36
36
|
"main": "dist/greenlight-motion.mjs",
|
|
37
37
|
"exports": {
|
package/skill/SKILL.md
CHANGED
|
@@ -18,7 +18,7 @@ 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.1.
|
|
21
|
+
npx greenlight-motion@0.1.5 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
|
|
@@ -32,7 +32,7 @@ animation presets (the same converter the GreenLight Motion connector uses in Cl
|
|
|
32
32
|
keyframes rarely look as good. The format, the design rules and the catalogues: `skill/references/scene-script.md`.
|
|
33
33
|
|
|
34
34
|
```
|
|
35
|
-
npx greenlight-motion@0.1.
|
|
35
|
+
npx greenlight-motion@0.1.5 script <folder> --from scenes.json # or write <folder>/script.json, then: script <folder>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Then `check` and `stills` (below), fix the SCRIPT and run `script` again — it rewrites `project.json`. For what a
|
|
@@ -41,14 +41,14 @@ workflow below and stop running `script` (it refuses to overwrite a project edit
|
|
|
41
41
|
|
|
42
42
|
## Workflow
|
|
43
43
|
|
|
44
|
-
1. `npx greenlight-motion@0.1.
|
|
44
|
+
1. `npx greenlight-motion@0.1.5 init <folder>` — starts from a working example (title, subtitle, accent line, four
|
|
45
45
|
options). `--from file.json` starts from a project you have (for example one exported from GreenLight Dash).
|
|
46
46
|
2. Edit `<folder>/project.json` — for changes to an EXISTING project prefer `apply` (below): it checks every edit and
|
|
47
47
|
applies all or nothing. Put pictures, video, sound and fonts in `<folder>/assets/` and refer to them as
|
|
48
48
|
`"asset:<file>"` (e.g. `"url": "asset:product.png"`).
|
|
49
|
-
3. `npx greenlight-motion@0.1.
|
|
49
|
+
3. `npx greenlight-motion@0.1.5 check <folder>` — fix everything it lists (missing files, template mistakes, blank
|
|
50
50
|
frames, page errors). `--json` for machine output.
|
|
51
|
-
4. `npx greenlight-motion@0.1.
|
|
51
|
+
4. `npx greenlight-motion@0.1.5 stills <folder>` — renders frames and **a labelled contact sheet**. Open and look at
|
|
52
52
|
it before you go on: layout, readability, timing. Fix and repeat. `--at 0.5,1.2,3` for exact times.
|
|
53
53
|
5. Hand it over: tell the user to open `<folder>/index.html` (Chrome or Edge), or `pack` it into one file, or
|
|
54
54
|
`render` the MP4 yourself (`--fps 60 --motion-blur` for fast motion).
|
|
@@ -113,7 +113,7 @@ editor's own normalizer), so write only what you mean.
|
|
|
113
113
|
Full references (generated from the editor's own docs; ignore the `/api/` routes they mention):
|
|
114
114
|
`skill/references/` — video-design, scene-script, video-projects, video-text, video-text-kinetic, video-text-presets, video-shapes, video-keyframes,
|
|
115
115
|
video-camera, video-3d, video-effects, video-transitions, video-particles, video-paths, video-backgrounds, video-grid.
|
|
116
|
-
Machine-readable lists: `npx greenlight-motion@0.1.
|
|
116
|
+
Machine-readable lists: `npx greenlight-motion@0.1.5 schema effects | transitions | easings | paths | expressions`.
|
|
117
117
|
|
|
118
118
|
## Template
|
|
119
119
|
|
|
@@ -160,7 +160,7 @@ an absolute moment; `align` `sync` (default) / `start` / `end`; `offset` seconds
|
|
|
160
160
|
|
|
161
161
|
The library is 77 hand-picked sounds in categories (whooshes and swooshes, transitions, clicks, pop-ups and
|
|
162
162
|
notifications, data blips, typing and writing, foley, bubbles, impacts and bass drops, camera, gears, voice clips).
|
|
163
|
-
`npx greenlight-motion@0.1.
|
|
163
|
+
`npx greenlight-motion@0.1.5 schema sounds` lists every one with its `category`, `use` (what it is for), `duration`
|
|
164
164
|
and `sync`. Pick by `category` and `use`, then by name.
|
|
165
165
|
|
|
166
166
|
- **Where the files come from:** the GreenLight sound CDN. `build` downloads each sound a cue uses into
|
|
@@ -176,7 +176,7 @@ The page has **Notes**: the user marks what should change right on the frame (a
|
|
|
176
176
|
and writes a note for each mark. Every note carries the moment (seconds and frame), the mark in composition pixels
|
|
177
177
|
and the layers under it (item ids in project.json), so you change exactly that and nothing else.
|
|
178
178
|
|
|
179
|
-
- **They review on this computer:** run `npx greenlight-motion@0.1.
|
|
179
|
+
- **They review on this computer:** run `npx greenlight-motion@0.1.5 review <folder>` and give them the URL it prints
|
|
180
180
|
(it keeps running; `--open` opens their browser, only when they ask). Their notes land in `<folder>/review/`: read
|
|
181
181
|
`review/NOTES.md`, look at the `at-<t>s.png` pictures (the marks are numbered), make the changes (`apply`), `build`
|
|
182
182
|
again and tell them to reload the page.
|
|
@@ -205,11 +205,11 @@ that fails (`plugin`); look at `stills` for the look. A package that carries its
|
|
|
205
205
|
|
|
206
206
|
The same project opens in GreenLight Dash (the desktop editor this engine comes from) for full editing: the page's
|
|
207
207
|
edit note offers **Open in GreenLight Dash** (it imports the folder as a new project on the open board; a page with
|
|
208
|
-
changes saves a `.glvideo`). `npx greenlight-motion@0.1.
|
|
208
|
+
changes saves a `.glvideo`). `npx greenlight-motion@0.1.5 pack <folder> --glvideo` writes that file yourself: ONE
|
|
209
209
|
`.glvideo` (the project + its files) that opens in GreenLight Dash on a double-click, or dropped on its board. Hand it
|
|
210
210
|
over when the user wants to keep editing in the app. A
|
|
211
211
|
GreenLight Dash project comes the other way through the app's **Export for agents** — a ready folder like the one
|
|
212
|
-
`init` makes, with the options the author ticked. `npx greenlight-motion@0.1.
|
|
212
|
+
`init` makes, with the options the author ticked. `npx greenlight-motion@0.1.5 init <folder> --from <its project.json>`
|
|
213
213
|
starts from one too. Never tell the user they must install it: the page plays, changes options and renders on its own.
|
|
214
214
|
|
|
215
215
|
## Files
|
|
@@ -15,6 +15,11 @@ and check every version against them.
|
|
|
15
15
|
|
|
16
16
|
- One headline per scene; supporting text much smaller. Hierarchy comes from size and weight, not colour alone.
|
|
17
17
|
- One accent colour and strong contrast.
|
|
18
|
+
- **No text shadows unless the user asks for one.** A shadow under type reads as a stock default and softens the
|
|
19
|
+
letter edges. When text has to read over footage or a busy picture, get the contrast from the design instead: a
|
|
20
|
+
calmer or darker part of the frame, a caption bar (`textBgEnabled`), or a shape behind the text. So no
|
|
21
|
+
`textShadowX` / `textShadowY` / `textShadowBlur` / `textShadowSpread` on a text layer, no `shadow` property in a
|
|
22
|
+
Text Animator and no `longShadow` effect on text. A shadow already in the user's project stays.
|
|
18
23
|
- Keep text inside the middle 80 % of the frame (in a project: |panX| and |panY| ≤ 40): players and feeds crop the
|
|
19
24
|
edges.
|
|
20
25
|
|
|
@@ -60,7 +60,7 @@ Any field you omit is filled with the default below, so a caption item only need
|
|
|
60
60
|
| `panX` | `0` | **Horizontal position**, % of the frame WIDTH from center (±50 = the frame edge; no limit, beyond that is off-frame). + = right. |
|
|
61
61
|
| `panY` | `0` | **Vertical position**, % of the frame HEIGHT from center (±50 = the frame edge; no limit). **+ = down**, − = up. Bottom caption ≈ **`panY: 35`** (≈ `38` sits lower). |
|
|
62
62
|
|
|
63
|
-
Text extras: `textBgEnabled` + `textBgColor` + `textBgPadding` draw a caption-style background bar behind each text line (padding in px — the "spacing" around the text). `textBgPaddingX` / `textBgPaddingY` (px, 0–200) set the horizontal and vertical spacing separately — the panel's **Spacing H / Spacing V** — and each falls back to `textBgPadding` when omitted or `null`, so setting only the legacy field still moves both axes. The same bar can carry a border: `textBorderEnabled: true` + `textBorderColor` (default `"#ffffff"`, rgba allowed) + `textBorderWidth` (px, 0.5–40, default 2) + `textBorderStyle` (`"solid"` default, `"dashed"`, `"dotted"`). The border is its own toggle — a bar can be outline-only (`textBorderEnabled` without `textBgEnabled`) — and is stroked INSIDE the bar's bounds, so width never grows the bar. `textBgRadius` (px, 0–200) sets the bar's corner radius for both fill and border; omit it (or `null`) for the automatic radius (the padding, capped at 40% of the font size and 40px). The bar can animate instead of appearing instantly: `textBgInType` / `textBgOutType` pick the reveal — `"none"` (default), `"fade"`, `"mask-right"`, `"mask-left"`, `"mask-down"`, `"mask-up"`, `"center-h"`, `"center-v"`. Mask names give the direction the motion travels for In and Out alike (In `"mask-right"` sweeps the bar on left→right, Out `"mask-right"` sweeps it away left→right, so the same value both ways reads as one continuous pass); `center-*` grow/shrink each bar about its middle. `textBgInStyle` / `textBgOutStyle` are `"wipe"` (default — the finished bar is clipped by a rectangle, so the leading edge is flat), `"slide"` (the fill slides behind a mask fixed at the bar's final shape; `mask-*` only) or `"grow"` (**Grow Shape** — the bar's own rounded geometry scales from the anchored edge, so the corner radius is preserved throughout; pick this for pill-shaped bars). `center-*` take `wipe` or `grow`. Grow Shape extends a **seed** sitting at the anchored edge: the seed is `textBgInGrowStart` / `textBgOutGrowStart` (px, 0–600, default 0) when set, otherwise the pill's diameter when the bar has a radius (a circle), otherwise a hairline caret — so the fill really starts at ~0 width. Grow Shape can also give the seed its own appearance: `textBgInGrowAppear` / `textBgOutGrowAppear` — `"none"` (default: the seed is there from the first frame and extends), `"fade"` (alphas in while extending), `"scale-in"` (the seed zooms from a dot **at the start point** — dot → caret line, or dot → circle with a radius — and only then extends along the direction; the two run one after the other, so the total In takes appear-duration + duration) or `"scale-down"` (settles from 150% about the start point while fading in) — with `textBgInGrowAppearDuration` / `textBgOutGrowAppearDuration` (s, 0.05–4, default 0.4) and `textBgInGrowAppearEasing` / `textBgOutGrowAppearEasing` (easing display values, defaults `"Ease Out"` / `"Ease In"`). Nothing scales about the moving box centre, so the shape never drifts. Out mirrors everything (retract to the seed, then the seed zooms away). Ignored unless the slot's style is `"grow"`. `textBgInDuration` / `textBgOutDuration` are seconds (0.05–4, defaults 0.5 / 0.4); In plays over the item's first seconds, Out over its last. `textBgInDelay` (s, 0–4, default 0) holds the bar off-screen for that long before the In reveal starts — use it when the bar should land after the words have begun animating. The reveal (and the delay) applies to the border-only bar too. `textBgInEasing` / `textBgOutEasing` take an easing **display value** such as `"Ease Out"`, `"Expo Out"`, `"Back Out"` — the same values as `transitionInEasing` (defaults `"Ease Out"` in, `"Ease In"` out). Also: `textShadowX`/`textShadowY`/`textShadowBlur`/`textShadowColor` add a text shadow (defaults 0 / `rgba(0,0,0,0.75)`; off while offset and blur are all 0), with `textShadowSpread` (px, 0–50, default 0) thickening it before the blur. `textLetterCase` re-cases the rendered glyphs without touching `textContent` — `"none"` (default), `"uppercase"`, `"lowercase"` or `"capitalize"`, so a caption can shout without the stored text shouting. `textDecoration` is `"none"` (default), `"underline"`, `"line-through"` or `"overline"`. `textClipMask: true` turns the layer into a clipping mask so footage below plays inside the letters — see **Clipping masks** in the shapes section.
|
|
63
|
+
Text extras: `textBgEnabled` + `textBgColor` + `textBgPadding` draw a caption-style background bar behind each text line (padding in px — the "spacing" around the text). `textBgPaddingX` / `textBgPaddingY` (px, 0–200) set the horizontal and vertical spacing separately — the panel's **Spacing H / Spacing V** — and each falls back to `textBgPadding` when omitted or `null`, so setting only the legacy field still moves both axes. The same bar can carry a border: `textBorderEnabled: true` + `textBorderColor` (default `"#ffffff"`, rgba allowed) + `textBorderWidth` (px, 0.5–40, default 2) + `textBorderStyle` (`"solid"` default, `"dashed"`, `"dotted"`). The border is its own toggle — a bar can be outline-only (`textBorderEnabled` without `textBgEnabled`) — and is stroked INSIDE the bar's bounds, so width never grows the bar. `textBgRadius` (px, 0–200) sets the bar's corner radius for both fill and border; omit it (or `null`) for the automatic radius (the padding, capped at 40% of the font size and 40px). The bar can animate instead of appearing instantly: `textBgInType` / `textBgOutType` pick the reveal — `"none"` (default), `"fade"`, `"mask-right"`, `"mask-left"`, `"mask-down"`, `"mask-up"`, `"center-h"`, `"center-v"`. Mask names give the direction the motion travels for In and Out alike (In `"mask-right"` sweeps the bar on left→right, Out `"mask-right"` sweeps it away left→right, so the same value both ways reads as one continuous pass); `center-*` grow/shrink each bar about its middle. `textBgInStyle` / `textBgOutStyle` are `"wipe"` (default — the finished bar is clipped by a rectangle, so the leading edge is flat), `"slide"` (the fill slides behind a mask fixed at the bar's final shape; `mask-*` only) or `"grow"` (**Grow Shape** — the bar's own rounded geometry scales from the anchored edge, so the corner radius is preserved throughout; pick this for pill-shaped bars). `center-*` take `wipe` or `grow`. Grow Shape extends a **seed** sitting at the anchored edge: the seed is `textBgInGrowStart` / `textBgOutGrowStart` (px, 0–600, default 0) when set, otherwise the pill's diameter when the bar has a radius (a circle), otherwise a hairline caret — so the fill really starts at ~0 width. Grow Shape can also give the seed its own appearance: `textBgInGrowAppear` / `textBgOutGrowAppear` — `"none"` (default: the seed is there from the first frame and extends), `"fade"` (alphas in while extending), `"scale-in"` (the seed zooms from a dot **at the start point** — dot → caret line, or dot → circle with a radius — and only then extends along the direction; the two run one after the other, so the total In takes appear-duration + duration) or `"scale-down"` (settles from 150% about the start point while fading in) — with `textBgInGrowAppearDuration` / `textBgOutGrowAppearDuration` (s, 0.05–4, default 0.4) and `textBgInGrowAppearEasing` / `textBgOutGrowAppearEasing` (easing display values, defaults `"Ease Out"` / `"Ease In"`). Nothing scales about the moving box centre, so the shape never drifts. Out mirrors everything (retract to the seed, then the seed zooms away). Ignored unless the slot's style is `"grow"`. `textBgInDuration` / `textBgOutDuration` are seconds (0.05–4, defaults 0.5 / 0.4); In plays over the item's first seconds, Out over its last. `textBgInDelay` (s, 0–4, default 0) holds the bar off-screen for that long before the In reveal starts — use it when the bar should land after the words have begun animating. The reveal (and the delay) applies to the border-only bar too. `textBgInEasing` / `textBgOutEasing` take an easing **display value** such as `"Ease Out"`, `"Expo Out"`, `"Back Out"` — the same values as `transitionInEasing` (defaults `"Ease Out"` in, `"Ease In"` out). Also: `textShadowX`/`textShadowY`/`textShadowBlur`/`textShadowColor` add a text shadow (defaults 0 / `rgba(0,0,0,0.75)`; off while offset and blur are all 0), with `textShadowSpread` (px, 0–50, default 0) thickening it before the blur. Set them only when the user asks for a text shadow ([video-design.md](video-design.md#hierarchy)). `textLetterCase` re-cases the rendered glyphs without touching `textContent` — `"none"` (default), `"uppercase"`, `"lowercase"` or `"capitalize"`, so a caption can shout without the stored text shouting. `textDecoration` is `"none"` (default), `"underline"`, `"line-through"` or `"overline"`. `textClipMask: true` turns the layer into a clipping mask so footage below plays inside the letters — see **Clipping masks** in the shapes section.
|
|
64
64
|
|
|
65
65
|
**Fill style / outline type.** `textFillMode` is `"fill"` (default, solid glyphs), `"outline"` (hollow glyphs — the glyph boundary only) or `"both"`. `textOutlineWidth` is the stroke width in px on the text canvas (0–60, default 2; outline modes never go below a 0.5px hairline). `textOutlineColor` is optional — **omit it and the outline uses `textColor`**, so switching mode never silently changes the layer's colour; set it for a contrasting rim in `"both"` mode. Gradients work on the outline exactly as they do on the fill.
|
|
66
66
|
|
|
@@ -211,7 +211,7 @@ Rules the sanitizer enforces (anything else is dropped or clamped on load): `aid
|
|
|
211
211
|
| `hue` | `hue` 180°, `saturation` 0, `brightness` 0 % | rotates whatever colour the unit has at that point |
|
|
212
212
|
| `stroke` | `color` `#ffffff`, `width` 4 px, `outline` false | outline around the glyphs; `outline: true` fades the fill out |
|
|
213
213
|
| `glow` | `color` `#5ef2ff`, `size` 30 px, `strength` 120 % | soft light around the glyphs |
|
|
214
|
-
| `shadow` | `color` `#000000`, `x` 0, `y` 18, `blur` 20 px, `opacity` 70 % | offset shadow |
|
|
214
|
+
| `shadow` | `color` `#000000`, `x` 0, `y` 18, `blur` 20 px, `opacity` 70 % | offset shadow; only when the user asks for a text shadow |
|
|
215
215
|
| `blur` | `amount` 18 px | defocus |
|
|
216
216
|
| `box` | `color` `#33efab`, `behind` `selected`\|`revealed`, `shape` `line`\|`unit`\|`block`, `padX` 12, `padY` 4, `radius` 10 px, `opacity` 100 %, `grow` true, `withText` false | `line`: one bar behind each run of selected units in a row; `unit`: a bar behind each selected unit of `basedOn` (a whole word even when drawn by letters), as bright as its amount; `block`: one shape around all of it. `behind: revealed` = behind what this animator lets through (a typing animator grows its box). `grow` makes a moving span sweep it like a marker. `withText` (`unit` only): each bar is as visible as its unit — every animator's opacity included — so a word that has not appeared yet has no bar |
|
|
217
217
|
| `caret` | `style` `bar`\|`underscore`\|`block`, `ownColor` false, `color`, `width` 0 (auto) px, `blink` 1.1/s, `hide` false, `hideAfter` 0 s | a text cursor at this animator's reveal edge (what an Opacity 0 on the same range lets through): on an `in` animator it types with the text; solid while typing, blinking otherwise, gone when an `out` starts or `hideAfter` s after the typing ends |
|