@field/cascade 0.2.22
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/AGENTS.md +168 -0
- package/ARCHITECTURE.md +137 -0
- package/DESIGN.md +161 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/assets/CodeEditor-DR7u0q7p.css +1 -0
- package/dist/assets/CodeEditor-DoaNEUaM.js +641 -0
- package/dist/assets/CodePanel-B0_2XJ-t.js +2 -0
- package/dist/assets/CodePanel-IT1secyI.css +1 -0
- package/dist/assets/CustomNodeDialog-026lzecD.js +1 -0
- package/dist/assets/CustomNodeDialog-JGYnWY6m.css +1 -0
- package/dist/assets/ExportDialog-DOq0flMV.css +1 -0
- package/dist/assets/ExportDialog-DWN4nhXI.js +358 -0
- package/dist/assets/ProjectSettings-BzT164Yx.js +2 -0
- package/dist/assets/ProjectSettings-DdO9xoQz.css +1 -0
- package/dist/assets/SettingsDialog-CeK7ytpY.css +1 -0
- package/dist/assets/SettingsDialog-DmHb1V0r.js +1 -0
- package/dist/assets/VersionHistoryDialog-CWNmHQ-P.css +1 -0
- package/dist/assets/VersionHistoryDialog-hH6VsXLX.js +3 -0
- package/dist/assets/abap-08VXUWAP.js +1 -0
- package/dist/assets/apex-BWPQTe0t.js +1 -0
- package/dist/assets/azcli-Bc_sGQ0U.js +1 -0
- package/dist/assets/bat-i0X4ZdIN.js +1 -0
- package/dist/assets/bicep-B5-_aFwp.js +2 -0
- package/dist/assets/cameligo-DMUM7wLl.js +1 -0
- package/dist/assets/cascade.jpg +0 -0
- package/dist/assets/clojure-Cm7r79vr.js +1 -0
- package/dist/assets/codicon-Brq4_Ui5.ttf +0 -0
- package/dist/assets/coffee-Ba7i2nA0.js +1 -0
- package/dist/assets/core-Bji4aZUU.js +965 -0
- package/dist/assets/cpp-C7h46wYY.js +1 -0
- package/dist/assets/csharp-BKxtCVv1.js +1 -0
- package/dist/assets/csp-bTuwJoIa.js +1 -0
- package/dist/assets/css-DIMkf-bt.js +3 -0
- package/dist/assets/css.worker-B3ciXF_0.js +93 -0
- package/dist/assets/cssMode-B6Z_Q1Kh.js +1 -0
- package/dist/assets/cypher-CVaqCwHa.js +1 -0
- package/dist/assets/dart-onAF5SnQ.js +1 -0
- package/dist/assets/dockerfile-DZFCIeNp.js +1 -0
- package/dist/assets/ecl-D05T4iGw.js +1 -0
- package/dist/assets/editor-1FQrmyyI.css +1 -0
- package/dist/assets/editor.api-jMoW_43X.js +847 -0
- package/dist/assets/editor.worker-q-txB4vs.js +30 -0
- package/dist/assets/elixir-6RTg0lbw.js +1 -0
- package/dist/assets/flow9-C5_-GSwl.js +1 -0
- package/dist/assets/freemarker2-DvvPV6m5.js +3 -0
- package/dist/assets/fsharp-C8Ef5oNN.js +1 -0
- package/dist/assets/geo-CXmCtzMM.js +2 -0
- package/dist/assets/go-C-y9NEjX.js +1 -0
- package/dist/assets/graphql-fmXr3nnJ.js +1 -0
- package/dist/assets/handlebars-B8rfuSIa.js +1 -0
- package/dist/assets/hcl-CpzslTdj.js +1 -0
- package/dist/assets/html-DyHm6JCp.js +1 -0
- package/dist/assets/html.worker-C93Ht9o9.js +506 -0
- package/dist/assets/htmlMode-E9XLWE5u.js +1 -0
- package/dist/assets/image-BUcQC7PG.js +2076 -0
- package/dist/assets/index-BXYXSdgh.js +224 -0
- package/dist/assets/index-CrNke7SW.css +1 -0
- package/dist/assets/index-client-DN_FvjN8.js +1 -0
- package/dist/assets/ini-sBoK_t0W.js +1 -0
- package/dist/assets/java-BEtHBSE6.js +1 -0
- package/dist/assets/javascript-C_bM5PCN.js +1 -0
- package/dist/assets/json.worker-B2V3pomh.js +62 -0
- package/dist/assets/jsonMode-BdyVS7G4.js +7 -0
- package/dist/assets/julia-Bri6UV-V.js +1 -0
- package/dist/assets/kotlin-BOotOW0E.js +1 -0
- package/dist/assets/legacy-DIu3XRwl.js +2 -0
- package/dist/assets/less-B9JPFI3C.js +2 -0
- package/dist/assets/lexon-CfSJPG6W.js +1 -0
- package/dist/assets/liquid-CDkn3spA.js +1 -0
- package/dist/assets/lspLanguageFeatures-AB5DMAsr.js +4 -0
- package/dist/assets/lua-CsQS60Ue.js +1 -0
- package/dist/assets/m3-D-oSqn_W.js +1 -0
- package/dist/assets/markdown-Cimd5fb3.js +1 -0
- package/dist/assets/mdx-BvH_NjDN.js +1 -0
- package/dist/assets/mips-CIPQ_RoX.js +1 -0
- package/dist/assets/msdax-DauUninz.js +1 -0
- package/dist/assets/mysql-SOo6toE5.js +1 -0
- package/dist/assets/nodeTypeUtils-CweA3NHd.js +1 -0
- package/dist/assets/objective-c-FvmIjYaQ.js +1 -0
- package/dist/assets/pascal-DrH0SRf2.js +1 -0
- package/dist/assets/pascaligo-D-ptJ9y-.js +1 -0
- package/dist/assets/perl-oz_6vUea.js +1 -0
- package/dist/assets/pgsql-DTj74zXo.js +1 -0
- package/dist/assets/php-nr791fC2.js +1 -0
- package/dist/assets/pla-CopQ2nXW.js +1 -0
- package/dist/assets/postiats-43DmfD33.js +1 -0
- package/dist/assets/powerquery-D3hlyOfw.js +1 -0
- package/dist/assets/powershell-DmHpPYUd.js +1 -0
- package/dist/assets/projectSettingsApi-C1WvIwfP.js +1 -0
- package/dist/assets/protobuf-C531GsRP.js +2 -0
- package/dist/assets/pug-Z5eAx3Zn.js +1 -0
- package/dist/assets/python-B7uii4fi.js +1 -0
- package/dist/assets/qsharp-DkqhCAOL.js +1 -0
- package/dist/assets/quill-DjZ69wL_.js +121 -0
- package/dist/assets/r-BwWrilGY.js +1 -0
- package/dist/assets/razor-Z48fWdJc.js +1 -0
- package/dist/assets/redis-ClamHrr6.js +1 -0
- package/dist/assets/redshift-DT7zqm-g.js +1 -0
- package/dist/assets/restructuredtext-BYgofb2h.js +1 -0
- package/dist/assets/ruby-DezsRK8O.js +1 -0
- package/dist/assets/rust-DdL9SqIa.js +1 -0
- package/dist/assets/sb-CcwsVR0C.js +1 -0
- package/dist/assets/scala-DHpiXF5c.js +1 -0
- package/dist/assets/scheme-BeGwcela.js +1 -0
- package/dist/assets/scss-gp-XZpBa.js +3 -0
- package/dist/assets/shell-CC2rA5mh.js +1 -0
- package/dist/assets/solidity-BEEn4gHE.js +1 -0
- package/dist/assets/sophia-CRfGWb83.js +1 -0
- package/dist/assets/sparql-D_Lu-MrJ.js +1 -0
- package/dist/assets/sql-NEE52Syq.js +1 -0
- package/dist/assets/st-DbInun42.js +1 -0
- package/dist/assets/swift-Bxkupp3x.js +1 -0
- package/dist/assets/systemverilog-Bz4Y3fRF.js +1 -0
- package/dist/assets/tcl-DISqw1ZD.js +1 -0
- package/dist/assets/ts.worker-D7T1-Ig5.js +67738 -0
- package/dist/assets/tsMode-BDgacVG4.js +11 -0
- package/dist/assets/twig-De2hgUGE.js +1 -0
- package/dist/assets/typescript-Derb6Oqa.js +1 -0
- package/dist/assets/typespec-B8J7ngcE.js +1 -0
- package/dist/assets/vb-DV3o63ZY.js +1 -0
- package/dist/assets/wgsl-DpFanUEy.js +298 -0
- package/dist/assets/workers-BtoMX_pm.js +1 -0
- package/dist/assets/xml-B1CaFSrG.js +1 -0
- package/dist/assets/yaml-HmIeA0KH.js +1 -0
- package/dist/cli/index.js +11377 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/cli/runtime/io.ts +445 -0
- package/dist/cli/runtime/net.ts +19 -0
- package/dist/cli/runtime/shell.ts +46 -0
- package/dist/cli/runtime/stage.ts +132 -0
- package/dist/contracts/capabilities.d.ts +109 -0
- package/dist/contracts/capabilities.d.ts.map +1 -0
- package/dist/contracts/capabilities.js +11 -0
- package/dist/contracts/capabilities.js.map +1 -0
- package/dist/contracts/color.d.ts +67 -0
- package/dist/contracts/color.d.ts.map +1 -0
- package/dist/contracts/color.js +83 -0
- package/dist/contracts/color.js.map +1 -0
- package/dist/contracts/definition.d.ts +118 -0
- package/dist/contracts/definition.d.ts.map +1 -0
- package/dist/contracts/definition.js +2 -0
- package/dist/contracts/definition.js.map +1 -0
- package/dist/contracts/document.d.ts +50 -0
- package/dist/contracts/document.d.ts.map +1 -0
- package/dist/contracts/document.js +2 -0
- package/dist/contracts/document.js.map +1 -0
- package/dist/contracts/geometry.d.ts +301 -0
- package/dist/contracts/geometry.d.ts.map +1 -0
- package/dist/contracts/geometry.js +604 -0
- package/dist/contracts/geometry.js.map +1 -0
- package/dist/contracts/index.d.ts +8 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +8 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/schema.d.ts +6 -0
- package/dist/contracts/schema.d.ts.map +1 -0
- package/dist/contracts/schema.js +85 -0
- package/dist/contracts/schema.js.map +1 -0
- package/dist/contracts/validate.d.ts +13 -0
- package/dist/contracts/validate.d.ts.map +1 -0
- package/dist/contracts/validate.js +202 -0
- package/dist/contracts/validate.js.map +1 -0
- package/dist/contracts/values.d.ts +100 -0
- package/dist/contracts/values.d.ts.map +1 -0
- package/dist/contracts/values.js +59 -0
- package/dist/contracts/values.js.map +1 -0
- package/dist/graphs/default-orig.cascade +128 -0
- package/dist/graphs/default.cascade +21 -0
- package/dist/index.html +19 -0
- package/dist/runtime/browser.d.ts +3 -0
- package/dist/runtime/browser.d.ts.map +1 -0
- package/dist/runtime/browser.js +12 -0
- package/dist/runtime/browser.js.map +1 -0
- package/dist/runtime/builtins/core/index.d.ts +9 -0
- package/dist/runtime/builtins/core/index.d.ts.map +1 -0
- package/dist/runtime/builtins/core/index.js +21 -0
- package/dist/runtime/builtins/core/index.js.map +1 -0
- package/dist/runtime/builtins/core/random.d.ts +66 -0
- package/dist/runtime/builtins/core/random.d.ts.map +1 -0
- package/dist/runtime/builtins/core/random.js +46 -0
- package/dist/runtime/builtins/core/random.js.map +1 -0
- package/dist/runtime/builtins/core/remap.d.ts +105 -0
- package/dist/runtime/builtins/core/remap.d.ts.map +1 -0
- package/dist/runtime/builtins/core/remap.js +43 -0
- package/dist/runtime/builtins/core/remap.js.map +1 -0
- package/dist/runtime/builtins/core/routing.d.ts +117 -0
- package/dist/runtime/builtins/core/routing.d.ts.map +1 -0
- package/dist/runtime/builtins/core/routing.js +114 -0
- package/dist/runtime/builtins/core/routing.js.map +1 -0
- package/dist/runtime/builtins/core/structural.d.ts +86 -0
- package/dist/runtime/builtins/core/structural.d.ts.map +1 -0
- package/dist/runtime/builtins/core/structural.js +63 -0
- package/dist/runtime/builtins/core/structural.js.map +1 -0
- package/dist/runtime/builtins/geo/copyToPoints.d.ts +58 -0
- package/dist/runtime/builtins/geo/copyToPoints.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/copyToPoints.js +35 -0
- package/dist/runtime/builtins/geo/copyToPoints.js.map +1 -0
- package/dist/runtime/builtins/geo/definitions.d.ts +526 -0
- package/dist/runtime/builtins/geo/definitions.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/definitions.js +182 -0
- package/dist/runtime/builtins/geo/definitions.js.map +1 -0
- package/dist/runtime/builtins/geo/generators.d.ts +94 -0
- package/dist/runtime/builtins/geo/generators.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/generators.js +41 -0
- package/dist/runtime/builtins/geo/generators.js.map +1 -0
- package/dist/runtime/builtins/geo/index.d.ts +23 -0
- package/dist/runtime/builtins/geo/index.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/index.js +35 -0
- package/dist/runtime/builtins/geo/index.js.map +1 -0
- package/dist/runtime/builtins/geo/merge.d.ts +40 -0
- package/dist/runtime/builtins/geo/merge.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/merge.js +24 -0
- package/dist/runtime/builtins/geo/merge.js.map +1 -0
- package/dist/runtime/builtins/geo/namespace.d.ts +15 -0
- package/dist/runtime/builtins/geo/namespace.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/namespace.js +17 -0
- package/dist/runtime/builtins/geo/namespace.js.map +1 -0
- package/dist/runtime/builtins/geo/svgExport.d.ts +130 -0
- package/dist/runtime/builtins/geo/svgExport.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/svgExport.js +50 -0
- package/dist/runtime/builtins/geo/svgExport.js.map +1 -0
- package/dist/runtime/builtins/geo/transform.d.ts +60 -0
- package/dist/runtime/builtins/geo/transform.d.ts.map +1 -0
- package/dist/runtime/builtins/geo/transform.js +32 -0
- package/dist/runtime/builtins/geo/transform.js.map +1 -0
- package/dist/runtime/builtins/index.d.ts +9 -0
- package/dist/runtime/builtins/index.d.ts.map +1 -0
- package/dist/runtime/builtins/index.js +16 -0
- package/dist/runtime/builtins/index.js.map +1 -0
- package/dist/runtime/definition/architecture.d.ts +8 -0
- package/dist/runtime/definition/architecture.d.ts.map +1 -0
- package/dist/runtime/definition/architecture.js +112 -0
- package/dist/runtime/definition/architecture.js.map +1 -0
- package/dist/runtime/definition/extract.d.ts +12 -0
- package/dist/runtime/definition/extract.d.ts.map +1 -0
- package/dist/runtime/definition/extract.js +151 -0
- package/dist/runtime/definition/extract.js.map +1 -0
- package/dist/runtime/error.d.ts +8 -0
- package/dist/runtime/error.d.ts.map +1 -0
- package/dist/runtime/error.js +11 -0
- package/dist/runtime/error.js.map +1 -0
- package/dist/runtime/geometry/attributes.d.ts +65 -0
- package/dist/runtime/geometry/attributes.d.ts.map +1 -0
- package/dist/runtime/geometry/attributes.js +201 -0
- package/dist/runtime/geometry/attributes.js.map +1 -0
- package/dist/runtime/geometry/builder.d.ts +57 -0
- package/dist/runtime/geometry/builder.d.ts.map +1 -0
- package/dist/runtime/geometry/builder.js +153 -0
- package/dist/runtime/geometry/builder.js.map +1 -0
- package/dist/runtime/geometry/copy.d.ts +36 -0
- package/dist/runtime/geometry/copy.d.ts.map +1 -0
- package/dist/runtime/geometry/copy.js +68 -0
- package/dist/runtime/geometry/copy.js.map +1 -0
- package/dist/runtime/geometry/generate.d.ts +43 -0
- package/dist/runtime/geometry/generate.d.ts.map +1 -0
- package/dist/runtime/geometry/generate.js +76 -0
- package/dist/runtime/geometry/generate.js.map +1 -0
- package/dist/runtime/geometry/groups.d.ts +18 -0
- package/dist/runtime/geometry/groups.d.ts.map +1 -0
- package/dist/runtime/geometry/groups.js +69 -0
- package/dist/runtime/geometry/groups.js.map +1 -0
- package/dist/runtime/geometry/index.d.ts +23 -0
- package/dist/runtime/geometry/index.d.ts.map +1 -0
- package/dist/runtime/geometry/index.js +23 -0
- package/dist/runtime/geometry/index.js.map +1 -0
- package/dist/runtime/geometry/matrix.d.ts +26 -0
- package/dist/runtime/geometry/matrix.d.ts.map +1 -0
- package/dist/runtime/geometry/matrix.js +27 -0
- package/dist/runtime/geometry/matrix.js.map +1 -0
- package/dist/runtime/geometry/merge.d.ts +33 -0
- package/dist/runtime/geometry/merge.d.ts.map +1 -0
- package/dist/runtime/geometry/merge.js +185 -0
- package/dist/runtime/geometry/merge.js.map +1 -0
- package/dist/runtime/geometry/primitives.d.ts +41 -0
- package/dist/runtime/geometry/primitives.d.ts.map +1 -0
- package/dist/runtime/geometry/primitives.js +131 -0
- package/dist/runtime/geometry/primitives.js.map +1 -0
- package/dist/runtime/geometry/promote.d.ts +44 -0
- package/dist/runtime/geometry/promote.d.ts.map +1 -0
- package/dist/runtime/geometry/promote.js +203 -0
- package/dist/runtime/geometry/promote.js.map +1 -0
- package/dist/runtime/geometry/resample.d.ts +29 -0
- package/dist/runtime/geometry/resample.d.ts.map +1 -0
- package/dist/runtime/geometry/resample.js +165 -0
- package/dist/runtime/geometry/resample.js.map +1 -0
- package/dist/runtime/geometry/svg.d.ts +91 -0
- package/dist/runtime/geometry/svg.d.ts.map +1 -0
- package/dist/runtime/geometry/svg.js +216 -0
- package/dist/runtime/geometry/svg.js.map +1 -0
- package/dist/runtime/geometry/transform.d.ts +18 -0
- package/dist/runtime/geometry/transform.d.ts.map +1 -0
- package/dist/runtime/geometry/transform.js +56 -0
- package/dist/runtime/geometry/transform.js.map +1 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/node.d.ts +4 -0
- package/dist/runtime/node.d.ts.map +1 -0
- package/dist/runtime/node.js +12 -0
- package/dist/runtime/node.js.map +1 -0
- package/dist/runtime/runtime.d.ts +3 -0
- package/dist/runtime/runtime.d.ts.map +1 -0
- package/dist/runtime/runtime.js +1198 -0
- package/dist/runtime/runtime.js.map +1 -0
- package/dist/runtime/types.d.ts +241 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +2 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/shell.d.ts +8 -0
- package/dist/shell.js +54 -0
- package/dist/shell.js.map +7 -0
- package/dist/stage.d.ts +7 -0
- package/dist/stage.js +84 -0
- package/dist/stage.js.map +7 -0
- package/dist/studio/panel.d.ts +48 -0
- package/doc/PROJECT_AUTHORING.md +360 -0
- package/package.json +126 -0
|
@@ -0,0 +1,965 @@
|
|
|
1
|
+
import{c as e,o as t}from"./nodeTypeUtils-CweA3NHd.js";import{C as n,S as r,b as i,h as a,v as o,w as s,y as c}from"./index-BXYXSdgh.js";var l={type:`Switch`,name:`Switch`,icon:`GitBranch`,description:`Switch between multiple inputs by index`,category:`routing`},u=class extends r{constructor(e,t){super(e,`Switch`,t)}get activeInputIndex(){let e=this.getVariadicInputs();return Math.min(this.props.index?.value??0,e.length-1)}isInputActive(e){let t=this.getVariadicInputs(),n=this.activeInputIndex;return t.findIndex(t=>t.id===e)===n}setup(){this.setVariadic(),this.output=this.out(`output`),this.addParm(`index`,{value:0,type:`int`,params:{min:0,max:0,step:1},displayName:`Index`}),this.watchProp(`index`,()=>this.update()),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}update(){let e=this.getVariadicInputs(),t=e.filter(e=>e.connections.length>0).length;this.updatePropParams(`index`,{max:Math.max(0,t-1)});let n=e[Math.min(this.props.index.value,e.length-1)];n?(this.output.setValue(n.value),this.preview=this.isPreviewValue(n.value)?n.value:null):(this.output.setValue(null),this.preview=null)}},d={type:`Merge`,name:`Merge`,icon:`GitMerge`,description:`Combine multiple inputs into an array`,category:`routing`},f=class extends r{constructor(e,t){super(e,`Merge`,t)}setup(){this.setVariadic(),this.output=this.out(`output`),this.addParm(`append`,{value:!1,type:`boolean`,displayName:`Append Mode`,hidden:()=>{let e=this.getVariadicInputs();return!Array.isArray(e[0]?.value)}}),this.watchProp(`append`,()=>this.update()),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}update(){let e=this.getVariadicInputs().filter(e=>e.value!==null).map(e=>e.value);if(this.props.append.value&&e.length>0&&Array.isArray(e[0])){let t=[...e[0],...e.slice(1)];this.output.setValue(t)}else this.output.setValue(e);this.preview=null;for(let t of e)if(t instanceof HTMLCanvasElement||t instanceof HTMLImageElement){this.preview=t;break}}},p={type:`Subnet`,name:`Subnet`,icon:`Folder`,description:`Network container for organizing nodes`,category:`network`},m=class extends r{constructor(e,t){super(e,`Subnet`,t)}setup(){this.onUpdate=()=>this.update(),this.onReady=()=>{this.syncPorts(),this.update()}}isNetwork(){return!0}addChild(e){e.parent=this,this._children.push(e),(e.type===`Input`||e.type===`Output`)&&this.syncPorts()}removeChild(e){let t=this._children.indexOf(e);if(t!==-1){let n=e.type===`Input`||e.type===`Output`;this._children.splice(t,1),e.parent=null,n&&this.syncPorts()}}syncPorts(){let e=this._children.filter(e=>e.type===`Input`),t=e.reduce((e,t)=>{let n=t.inputIndex??0;return Math.max(e,n)},-1),n=this._children.filter(e=>e.type===`Output`),r=n.reduce((e,t)=>{let n=t.outputIndex??0;return Math.max(e,n)},-1),i=t+1,a=this.inputs.length;for(let t=a;t<i;t++){let n=e.find(e=>e.inputIndex===t),r=this.in(n?.inputName||`input_${t}`,null,{type:n?.dataType||`any`});r.onChange=e=>{this._children.filter(e=>e.type===`Input`&&e.inputIndex===t).forEach(e=>e.markDirty())}}for(let e=i;e<a;e++)this.inputs[e]&&(this.inputs[e].options={...this.inputs[e].options,hidden:!0});for(let t=0;t<i;t++)if(this.inputs[t]){let n=e.find(e=>e.inputIndex===t);this.inputs[t].name=n?.inputName||`input_${t}`,this.inputs[t].dataType=n?.dataType||`any`,this.inputs[t].options={...this.inputs[t].options,hidden:!1}}let o=r+1,s=this.outputs.length;for(let e=s;e<o;e++){let t=n.find(t=>t.outputIndex===e);this.out(t?.outputName||`output_${e}`,`param`,{type:t?.dataType||`any`})}for(let e=o;e<s;e++)this.outputs[e]&&(this.outputs[e].options={...this.outputs[e].options,hidden:!0});for(let e=0;e<o;e++)if(this.outputs[e]){let t=n.find(t=>t.outputIndex===e);this.outputs[e].name=t?.outputName||`output_${e}`,this.outputs[e].dataType=t?.dataType||`any`,this.outputs[e].options={...this.outputs[e].options,hidden:!1}}this.inputs=[...this.inputs],this.outputs=[...this.outputs]}outputNode(){return this._children.find(e=>e.type===`Output`)||this._children.find(e=>e.cook)||(this._children.length>0?this._children[this._children.length-1]:null)}displayNode(){return this._children.find(e=>e.cook)??null}indirectInputs(){return this._children.filter(e=>e.type===`Input`)}update(){let e=this._children.filter(e=>e.type===`Output`),t=new Map;for(let n of e){let e=n.outputIndex??0;t.set(e,n)}for(let e=0;e<this.outputs.length;e++){let n=t.get(e);if(n&&n.inputs.length>0){let t=n.inputs[0].value;this.outputs[e].setValue(t),e===0&&(t instanceof HTMLCanvasElement||t instanceof HTMLImageElement)&&(this.preview=t)}else if(e===0){let t=this._children.find(e=>e.cook);t&&t.outputs.length>0?(this.outputs[e].setValue(t.outputs[0].value),t.preview&&(this.preview=t.preview)):this.outputs[e].setValue(null)}else this.outputs[e].setValue(null)}(this.outputs.length===0||!t.has(0))&&(this._children.find(e=>e.cook)?.preview||(this.preview=null))}},h=c.map(e=>({value:e,label:e})),g={type:`Input`,name:`Input`,icon:`LogIn`,description:`Represents an external input to a subnet`,category:`network`},_=class extends r{constructor(e,t){super(e,`Input`,t)}setup(){this.output=this.out(`output`),this.addParm(`inputIndex`,{value:0,type:`int`,params:{min:0,step:1},displayName:`Input Index`}),this.addParm(`inputName`,{value:``,type:`text`,displayName:`Input Name`}),this.addParm(`dataType`,{value:`any`,type:`select`,params:{options:h},displayName:`Data Type`});let e=()=>{this.parent&&this.parent.syncPorts&&this.parent.syncPorts(),this.update()};this.watchProp(`inputIndex`,e),this.watchProp(`inputName`,e),this.watchProp(`dataType`,()=>{this.output.dataType=this.dataType,e()}),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}get inputIndex(){return this.props.inputIndex?.value??0}get inputName(){return this.props.inputName?.value??``}get dataType(){let e=this.props.dataType?.value;return typeof e==`string`&&i(e)?e:`any`}update(){if(this.parent&&this.parent.inputs.length>0){let e=this.inputIndex,t=this.parent.inputs[e];if(t){this.output.setValue(t.value),this.preview=this.isPreviewValue(t.value)?t.value:null;return}}this.output.setValue(null),this.preview=null}},v=c.map(e=>({value:e,label:e})),y={type:`Output`,name:`Output`,icon:`LogOut`,description:`Defines the output of a subnet`,category:`network`},b=class extends r{constructor(e,t){super(e,`Output`,t)}setup(){this.in(`input`,null),this.addParm(`outputIndex`,{value:0,type:`int`,params:{min:0,step:1},displayName:`Output Index`}),this.addParm(`outputName`,{value:``,type:`text`,displayName:`Output Name`}),this.addParm(`dataType`,{value:`any`,type:`select`,params:{options:v},displayName:`Data Type`});let e=()=>{this.parent&&this.parent.syncPorts&&this.parent.syncPorts(),this.update()};this.watchProp(`outputIndex`,e),this.watchProp(`outputName`,e),this.watchProp(`dataType`,()=>{this.inputs[0]&&(this.inputs[0].dataType=this.dataType),e()}),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}get outputIndex(){return this.props.outputIndex?.value??0}get outputName(){return this.props.outputName?.value??``}get dataType(){let e=this.props.dataType?.value;return typeof e==`string`&&i(e)?e:`any`}update(){let e=this.inputs[0];e&&(this.preview=this.isPreviewValue(e.value)?e.value:null)}};function x(e,t=0){let n=((Math.trunc(e)^Math.imul(Math.trunc(t),2654435761))>>>0)+1831565813>>>0;return n=Math.imul(n^n>>>15,n|1),n^=n+Math.imul(n^n>>>7,n|61),((n^n>>>14)>>>0)/4294967296}var S={type:`Random`,name:`Random`,icon:`Dice5`,description:`Generate a deterministic value from an explicit seed`,category:`utility`},C=class extends r{constructor(e,t){super(e,`Random`,t)}setup(){let e=this.in(`seed`,0,{type:`int`}),t=this.in(`sample`,0,{type:`int`}),n=this.out(`value`),r=()=>n.setValue(x(e.value,t.value));e.onChange=r,t.onChange=r,this.onUpdate=r,this.onReady=r}};function w(e,t,n,r,i,a){if(t===n)return r;let o=(e-t)/(n-t);return r+(a?Math.min(1,Math.max(0,o)):o)*(i-r)}var T={type:`Remap`,name:`Remap`,icon:`ArrowRightLeft`,description:`Map a number from one range into another`,category:`utility`},E=class extends r{constructor(e,t){super(e,`Remap`,t)}setup(){let e=this.in(`value`,0,{type:`float`}),t=this.in(`inMin`,0,{type:`float`}),n=this.in(`inMax`,1,{type:`float`}),r=this.in(`outMin`,0,{type:`float`}),i=this.in(`outMax`,1,{type:`float`}),a=this.out(`result`);this.addParm(`clamp`,{value:!1,type:`boolean`,displayName:`Clamp`});let o=()=>a.setValue(w(e.value,t.value,n.value,r.value,i.value,this.props.clamp.value));for(let a of[e,t,n,r,i])a.onChange=o;this.watchProp(`clamp`,o),this.onUpdate=o,this.onReady=o}},D={type:`Select`,name:`Select`,icon:`ListFilter`,description:`Pick an item from an array by index`,category:`utility`},O=class extends r{constructor(e,t){super(e,`Select`,t)}setup(){this.arrayInput=this.in(`array`,[],{type:`array`}),this.itemOutput=this.out(`item`,`param`),this.countOutput=this.out(`count`,`param`),this.addParm(`index`,{value:0,type:`int`,params:{min:0,max:0,step:1},displayName:`Index`}),this.addParm(`wrap`,{value:!0,type:`boolean`,displayName:`Wrap Index`}),this.watchProp(`index`,()=>this.update()),this.watchProp(`wrap`,()=>this.update()),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}update(){let e=this.arrayInput.value;if(!Array.isArray(e)||e.length===0){this.itemOutput.setValue(null),this.countOutput.setValue(0),this.updatePropParams(`index`,{max:0});return}this.updatePropParams(`index`,{max:e.length-1});let t=this.props.index?.value??0;t=(this.props.wrap?.value??!0)&&e.length>0?(t%e.length+e.length)%e.length:Math.max(0,Math.min(t,e.length-1));let n=e[t];this.itemOutput.setValue(n),this.countOutput.setValue(e.length),this.preview=n instanceof HTMLCanvasElement||n instanceof HTMLImageElement?n:null}},k={type:`Null`,name:`Null`,icon:`Circle`,description:`Pass its input through unchanged`,category:`routing`},A=class extends r{constructor(e,t){super(e,`Null`,t)}setup(){let e=this.in(`input`,null,{type:`any`}),t=this.out(`output`),n=()=>t.setValue(e.value);e.onChange=n,this.onUpdate=n,this.onReady=n}},j={type:`Freeze`,name:`Freeze`,icon:`Snowflake`,description:`Capture and lock a value`,category:`utility`},M=class extends r{constructor(e,t){super(e,`Freeze`,t),this.frozenValue=null,this.hasCaptured=!1}setup(){this.input=this.in(`input`,null,{type:`any`}),this.output=this.out(`output`,`param`),this.addParm(`frozen`,{value:!1,type:`boolean`,displayName:`Frozen`}),this.addAction(`capture`,{label:`Capture`,icon:`Camera`,callback:()=>this.capture()}),this.addAction(`clear`,{label:`Clear`,icon:`Trash2`,callback:()=>this.clear()}),this.watchProp(`frozen`,()=>this.update()),this.onUpdate=()=>this.update(),this.onReady=()=>this.update()}capture(){this.frozenValue=this.deepClone(this.input.value),this.hasCaptured=!0,this.setParm(`frozen`,!0),this.update()}clear(){this.frozenValue=null,this.hasCaptured=!1,this.setParm(`frozen`,!1),this.update()}deepClone(e){if(e==null)return e;if(e instanceof HTMLCanvasElement){let t=document.createElement(`canvas`);return t.width=e.width,t.height=e.height,t.getContext(`2d`)?.drawImage(e,0,0),t}if(e instanceof HTMLImageElement){let t=document.createElement(`canvas`);return t.width=e.naturalWidth||e.width,t.height=e.naturalHeight||e.height,t.getContext(`2d`)?.drawImage(e,0,0),t}if(Array.isArray(e))return e.map(e=>this.deepClone(e));if(typeof e==`object`)try{return JSON.parse(JSON.stringify(e))}catch{return e}return e}update(){(this.props.frozen?.value??!1)&&this.hasCaptured?(this.output.setValue(this.frozenValue),(this.frozenValue instanceof HTMLCanvasElement||this.frozenValue instanceof HTMLImageElement)&&(this.preview=this.frozenValue)):(this.output.setValue(this.input.value),this.preview=this.input.value instanceof HTMLCanvasElement||this.input.value instanceof HTMLImageElement?this.input.value:null)}serialize(){return{hasCaptured:this.hasCaptured}}deserialize(e){this.hasCaptured=e.hasCaptured===!0}},N=`/**
|
|
2
|
+
* SwitchNode - routes one of multiple inputs to output based on index
|
|
3
|
+
* Connect multiple inputs and use the index parameter to select which one passes through
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Node } from '../../Node.js';
|
|
7
|
+
import type { Graph } from '../../Graph.js';
|
|
8
|
+
import type { OutputPort } from '@/types/node.types';
|
|
9
|
+
|
|
10
|
+
export const nodeMetadata = {
|
|
11
|
+
type: 'Switch',
|
|
12
|
+
name: 'Switch',
|
|
13
|
+
icon: 'GitBranch',
|
|
14
|
+
description: 'Switch between multiple inputs by index',
|
|
15
|
+
category: 'routing'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class SwitchNode extends Node {
|
|
19
|
+
private output!: OutputPort<any>;
|
|
20
|
+
|
|
21
|
+
constructor(id: string, graph: Graph) {
|
|
22
|
+
super(id, 'Switch', graph);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the index of the currently active input
|
|
27
|
+
* Used by connection rendering to grey out inactive wires
|
|
28
|
+
*/
|
|
29
|
+
get activeInputIndex(): number {
|
|
30
|
+
const inputs = this.getVariadicInputs();
|
|
31
|
+
return Math.min(this.props.index?.value ?? 0, inputs.length - 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if a specific input port is active (for connection coloring)
|
|
36
|
+
*/
|
|
37
|
+
isInputActive(portId: string): boolean {
|
|
38
|
+
const inputs = this.getVariadicInputs();
|
|
39
|
+
const activeIdx = this.activeInputIndex;
|
|
40
|
+
const portIndex = inputs.findIndex(p => p.id === portId);
|
|
41
|
+
return portIndex === activeIdx;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected setup(): void {
|
|
45
|
+
this.setVariadic();
|
|
46
|
+
this.output = this.out('output');
|
|
47
|
+
|
|
48
|
+
this.addParm('index', {
|
|
49
|
+
value: 0,
|
|
50
|
+
type: 'int',
|
|
51
|
+
params: { min: 0, max: 0, step: 1 },
|
|
52
|
+
displayName: 'Index'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.watchProp('index', () => this.update());
|
|
56
|
+
|
|
57
|
+
this.onUpdate = () => this.update();
|
|
58
|
+
this.onReady = () => this.update();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private update(): void {
|
|
62
|
+
const inputs = this.getVariadicInputs();
|
|
63
|
+
|
|
64
|
+
// Update index max based on connected inputs (triggers UI reactivity)
|
|
65
|
+
const connectedCount = inputs.filter(p => p.connections.length > 0).length;
|
|
66
|
+
this.updatePropParams('index', { max: Math.max(0, connectedCount - 1) });
|
|
67
|
+
|
|
68
|
+
const idx = Math.min(this.props.index.value, inputs.length - 1);
|
|
69
|
+
const selectedInput = inputs[idx];
|
|
70
|
+
|
|
71
|
+
if (selectedInput) {
|
|
72
|
+
this.output.setValue(selectedInput.value);
|
|
73
|
+
|
|
74
|
+
// Use input's preview if it's a canvas/image
|
|
75
|
+
if (this.isPreviewValue(selectedInput.value)) {
|
|
76
|
+
this.preview = selectedInput.value;
|
|
77
|
+
} else {
|
|
78
|
+
this.preview = null;
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
this.output.setValue(null);
|
|
82
|
+
this.preview = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`,P=`/**
|
|
87
|
+
* MergeNode - combines multiple inputs into an array
|
|
88
|
+
* Connect multiple inputs to create an array of values
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
import { Node } from '../../Node.js';
|
|
92
|
+
import type { Graph } from '../../Graph.js';
|
|
93
|
+
import type { OutputPort } from '@/types/node.types';
|
|
94
|
+
|
|
95
|
+
export const nodeMetadata = {
|
|
96
|
+
type: 'Merge',
|
|
97
|
+
name: 'Merge',
|
|
98
|
+
icon: 'GitMerge',
|
|
99
|
+
description: 'Combine multiple inputs into an array',
|
|
100
|
+
category: 'routing'
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export class MergeNode extends Node {
|
|
104
|
+
private output!: OutputPort<any[]>;
|
|
105
|
+
|
|
106
|
+
constructor(id: string, graph: Graph) {
|
|
107
|
+
super(id, 'Merge', graph);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected setup(): void {
|
|
111
|
+
this.setVariadic();
|
|
112
|
+
this.output = this.out('output');
|
|
113
|
+
|
|
114
|
+
// Append mode - when first input is an array, append others to it
|
|
115
|
+
this.addParm('append', {
|
|
116
|
+
value: false,
|
|
117
|
+
type: 'boolean',
|
|
118
|
+
displayName: 'Append Mode',
|
|
119
|
+
hidden: () => {
|
|
120
|
+
const inputs = this.getVariadicInputs();
|
|
121
|
+
return !Array.isArray(inputs[0]?.value);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.watchProp('append', () => this.update());
|
|
126
|
+
|
|
127
|
+
this.onUpdate = () => this.update();
|
|
128
|
+
this.onReady = () => this.update();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private update(): void {
|
|
132
|
+
const inputs = this.getVariadicInputs();
|
|
133
|
+
|
|
134
|
+
// Collect non-null values
|
|
135
|
+
const values = inputs
|
|
136
|
+
.filter(p => p.value !== null)
|
|
137
|
+
.map(p => p.value);
|
|
138
|
+
|
|
139
|
+
// Handle append mode
|
|
140
|
+
const appendMode = this.props.append.value;
|
|
141
|
+
if (appendMode && values.length > 0 && Array.isArray(values[0])) {
|
|
142
|
+
// Append subsequent values to the first array
|
|
143
|
+
const result = [...values[0], ...values.slice(1)];
|
|
144
|
+
this.output.setValue(result);
|
|
145
|
+
} else {
|
|
146
|
+
this.output.setValue(values);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Set preview to first visual element if available
|
|
150
|
+
this.preview = null;
|
|
151
|
+
for (const val of values) {
|
|
152
|
+
if (val instanceof HTMLCanvasElement || val instanceof HTMLImageElement) {
|
|
153
|
+
this.preview = val;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`,F=`/**
|
|
160
|
+
* SubnetNode - Network container for organizing nodes hierarchically
|
|
161
|
+
* A subnet is a node that can contain child nodes, creating a nested graph
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
import { Node } from '../../Node.js';
|
|
165
|
+
import type { Graph } from '../../Graph.js';
|
|
166
|
+
import type { InputPort, OutputPort } from '@/types/node.types';
|
|
167
|
+
|
|
168
|
+
export const nodeMetadata = {
|
|
169
|
+
type: 'Subnet',
|
|
170
|
+
name: 'Subnet',
|
|
171
|
+
icon: 'Folder',
|
|
172
|
+
description: 'Network container for organizing nodes',
|
|
173
|
+
category: 'network'
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export class SubnetNode extends Node {
|
|
177
|
+
constructor(id: string, graph: Graph) {
|
|
178
|
+
super(id, 'Subnet', graph);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected setup(): void {
|
|
182
|
+
// Ports are dynamically created based on child Input/Output nodes
|
|
183
|
+
// Start with no ports - they'll be synced when children are added
|
|
184
|
+
this.onUpdate = () => this.update();
|
|
185
|
+
this.onReady = () => {
|
|
186
|
+
this.syncPorts();
|
|
187
|
+
this.update();
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Override: Subnets are networks and can contain children
|
|
193
|
+
*/
|
|
194
|
+
isNetwork(): boolean {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Add a child node to this subnet
|
|
200
|
+
*/
|
|
201
|
+
addChild(node: Node): void {
|
|
202
|
+
node.parent = this;
|
|
203
|
+
this._children.push(node);
|
|
204
|
+
// Sync ports when Input/Output nodes are added
|
|
205
|
+
if (node.type === 'Input' || node.type === 'Output') {
|
|
206
|
+
this.syncPorts();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Remove a child node from this subnet
|
|
212
|
+
*/
|
|
213
|
+
removeChild(node: Node): void {
|
|
214
|
+
const index = this._children.indexOf(node);
|
|
215
|
+
if (index !== -1) {
|
|
216
|
+
const wasInputOutput = node.type === 'Input' || node.type === 'Output';
|
|
217
|
+
this._children.splice(index, 1);
|
|
218
|
+
node.parent = null;
|
|
219
|
+
// Sync ports when Input/Output nodes are removed
|
|
220
|
+
if (wasInputOutput) {
|
|
221
|
+
this.syncPorts();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Sync input/output ports based on child Input/Output nodes
|
|
228
|
+
*/
|
|
229
|
+
syncPorts(): void {
|
|
230
|
+
// Find all Input nodes and determine required input ports
|
|
231
|
+
const inputNodes = this._children.filter(n => n.type === 'Input');
|
|
232
|
+
const maxInputIndex = inputNodes.reduce((max, node) => {
|
|
233
|
+
const idx = (node as any).inputIndex ?? 0;
|
|
234
|
+
return Math.max(max, idx);
|
|
235
|
+
}, -1);
|
|
236
|
+
|
|
237
|
+
// Find all Output nodes and determine required output ports
|
|
238
|
+
const outputNodes = this._children.filter(n => n.type === 'Output');
|
|
239
|
+
const maxOutputIndex = outputNodes.reduce((max, node) => {
|
|
240
|
+
const idx = (node as any).outputIndex ?? 0;
|
|
241
|
+
return Math.max(max, idx);
|
|
242
|
+
}, -1);
|
|
243
|
+
|
|
244
|
+
// Sync input ports
|
|
245
|
+
const requiredInputs = maxInputIndex + 1;
|
|
246
|
+
const currentInputs = this.inputs.length;
|
|
247
|
+
|
|
248
|
+
// Add missing input ports
|
|
249
|
+
for (let i = currentInputs; i < requiredInputs; i++) {
|
|
250
|
+
const boundary = inputNodes.find(n => (n as any).inputIndex === i) as any;
|
|
251
|
+
const port = this.in(boundary?.inputName || \`input_\${i}\`, null, {
|
|
252
|
+
type: boundary?.dataType || 'any'
|
|
253
|
+
});
|
|
254
|
+
// When input value changes, update any Input nodes referencing this index
|
|
255
|
+
port.onChange = (value: any) => {
|
|
256
|
+
this._children
|
|
257
|
+
.filter(n => n.type === 'Input' && (n as any).inputIndex === i)
|
|
258
|
+
.forEach(n => n.markDirty());
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Hide excess input ports (don't remove to preserve connections)
|
|
263
|
+
for (let i = requiredInputs; i < currentInputs; i++) {
|
|
264
|
+
if (this.inputs[i]) {
|
|
265
|
+
this.inputs[i].options = { ...this.inputs[i].options, hidden: true };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Unhide needed ports
|
|
269
|
+
for (let i = 0; i < requiredInputs; i++) {
|
|
270
|
+
if (this.inputs[i]) {
|
|
271
|
+
const boundary = inputNodes.find(n => (n as any).inputIndex === i) as any;
|
|
272
|
+
this.inputs[i].name = boundary?.inputName || \`input_\${i}\`;
|
|
273
|
+
this.inputs[i].dataType = boundary?.dataType || 'any';
|
|
274
|
+
this.inputs[i].options = { ...this.inputs[i].options, hidden: false };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Sync output ports - only create if there are Output nodes
|
|
279
|
+
const requiredOutputs = maxOutputIndex + 1; // 0 if no Output nodes
|
|
280
|
+
const currentOutputs = this.outputs.length;
|
|
281
|
+
|
|
282
|
+
// Add missing output ports
|
|
283
|
+
for (let i = currentOutputs; i < requiredOutputs; i++) {
|
|
284
|
+
const boundary = outputNodes.find(n => (n as any).outputIndex === i) as any;
|
|
285
|
+
this.out(boundary?.outputName || \`output_\${i}\`, 'param', {
|
|
286
|
+
type: boundary?.dataType || 'any'
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Hide excess output ports
|
|
291
|
+
for (let i = requiredOutputs; i < currentOutputs; i++) {
|
|
292
|
+
if (this.outputs[i]) {
|
|
293
|
+
this.outputs[i].options = { ...this.outputs[i].options, hidden: true };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Unhide needed ports
|
|
297
|
+
for (let i = 0; i < requiredOutputs; i++) {
|
|
298
|
+
if (this.outputs[i]) {
|
|
299
|
+
const boundary = outputNodes.find(n => (n as any).outputIndex === i) as any;
|
|
300
|
+
this.outputs[i].name = boundary?.outputName || \`output_\${i}\`;
|
|
301
|
+
this.outputs[i].dataType = boundary?.dataType || 'any';
|
|
302
|
+
this.outputs[i].options = { ...this.outputs[i].options, hidden: false };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Trigger reactivity
|
|
307
|
+
this.inputs = [...this.inputs];
|
|
308
|
+
this.outputs = [...this.outputs];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get the node that defines this subnet's output
|
|
313
|
+
* Priority: Output node > cooking node > last node
|
|
314
|
+
*/
|
|
315
|
+
outputNode(): Node | null {
|
|
316
|
+
// 1. Check for explicit Output node
|
|
317
|
+
const outputNode = this._children.find(n => n.type === 'Output');
|
|
318
|
+
if (outputNode) return outputNode;
|
|
319
|
+
|
|
320
|
+
// 2. Use cooking node
|
|
321
|
+
const cookingNode = this._children.find(n => n.cook);
|
|
322
|
+
if (cookingNode) return cookingNode;
|
|
323
|
+
|
|
324
|
+
// 3. Fall back to last node
|
|
325
|
+
return this._children.length > 0 ? this._children[this._children.length - 1] : null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Get the cooking (display) node in this subnet
|
|
330
|
+
*/
|
|
331
|
+
displayNode(): Node | null {
|
|
332
|
+
return this._children.find(n => n.cook) ?? null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get all Input nodes inside this subnet
|
|
337
|
+
*/
|
|
338
|
+
indirectInputs(): Node[] {
|
|
339
|
+
return this._children.filter(n => n.type === 'Input');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private update(): void {
|
|
343
|
+
// Update each output based on corresponding Output nodes
|
|
344
|
+
const outputNodes = this._children.filter(n => n.type === 'Output');
|
|
345
|
+
|
|
346
|
+
// Group output nodes by their outputIndex
|
|
347
|
+
const outputByIndex = new Map<number, Node>();
|
|
348
|
+
for (const node of outputNodes) {
|
|
349
|
+
const idx = (node as any).outputIndex ?? 0;
|
|
350
|
+
outputByIndex.set(idx, node);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Update each output port
|
|
354
|
+
for (let i = 0; i < this.outputs.length; i++) {
|
|
355
|
+
const outNode = outputByIndex.get(i);
|
|
356
|
+
if (outNode && outNode.inputs.length > 0) {
|
|
357
|
+
const value = outNode.inputs[0].value;
|
|
358
|
+
this.outputs[i].setValue(value);
|
|
359
|
+
|
|
360
|
+
// Set preview from first output
|
|
361
|
+
if (i === 0 && (value instanceof HTMLCanvasElement || value instanceof HTMLImageElement)) {
|
|
362
|
+
this.preview = value;
|
|
363
|
+
}
|
|
364
|
+
} else if (i === 0) {
|
|
365
|
+
// Fallback for first output: use cooking node if no Output node
|
|
366
|
+
const cookingNode = this._children.find(n => n.cook);
|
|
367
|
+
if (cookingNode && cookingNode.outputs.length > 0) {
|
|
368
|
+
this.outputs[i].setValue(cookingNode.outputs[0].value);
|
|
369
|
+
if (cookingNode.preview) {
|
|
370
|
+
this.preview = cookingNode.preview;
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
this.outputs[i].setValue(null);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
this.outputs[i].setValue(null);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Clear preview if no outputs set it
|
|
381
|
+
if (this.outputs.length === 0 || !outputByIndex.has(0)) {
|
|
382
|
+
const cookingNode = this._children.find(n => n.cook);
|
|
383
|
+
if (!cookingNode?.preview) {
|
|
384
|
+
this.preview = null;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
`,I=`/**
|
|
390
|
+
* InputNode - Represents an external input to a subnet
|
|
391
|
+
* Auto-created when wiring into a subnet, or manually for documentation
|
|
392
|
+
*/
|
|
393
|
+
|
|
394
|
+
import { Node } from '../../Node.js';
|
|
395
|
+
import type { Graph } from '../../Graph.js';
|
|
396
|
+
import type { DataType, OutputPort } from '@/types/node.types';
|
|
397
|
+
import { CORE_TYPES, isCoreType } from '@/types/coreTypes';
|
|
398
|
+
|
|
399
|
+
const TYPE_CHOICES = CORE_TYPES.map(type => ({ value: type, label: type }));
|
|
400
|
+
|
|
401
|
+
export const nodeMetadata = {
|
|
402
|
+
type: 'Input',
|
|
403
|
+
name: 'Input',
|
|
404
|
+
icon: 'LogIn',
|
|
405
|
+
description: 'Represents an external input to a subnet',
|
|
406
|
+
category: 'network'
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
export class InputNode extends Node {
|
|
410
|
+
private output!: OutputPort<any>;
|
|
411
|
+
|
|
412
|
+
constructor(id: string, graph: Graph) {
|
|
413
|
+
super(id, 'Input', graph);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
protected setup(): void {
|
|
417
|
+
// Output passes through the external input value
|
|
418
|
+
this.output = this.out('output');
|
|
419
|
+
|
|
420
|
+
// Input index - which external input this represents
|
|
421
|
+
this.addParm('inputIndex', {
|
|
422
|
+
value: 0,
|
|
423
|
+
type: 'int',
|
|
424
|
+
params: { min: 0, step: 1 },
|
|
425
|
+
displayName: 'Input Index'
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Optional name for documentation
|
|
429
|
+
this.addParm('inputName', {
|
|
430
|
+
value: '',
|
|
431
|
+
type: 'text',
|
|
432
|
+
displayName: 'Input Name'
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
this.addParm('dataType', {
|
|
436
|
+
value: 'any',
|
|
437
|
+
type: 'select',
|
|
438
|
+
params: { options: TYPE_CHOICES },
|
|
439
|
+
displayName: 'Data Type'
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Watch for index changes to trigger parent port sync
|
|
443
|
+
const syncParent = () => {
|
|
444
|
+
if (this.parent && (this.parent as any).syncPorts) {
|
|
445
|
+
(this.parent as any).syncPorts();
|
|
446
|
+
}
|
|
447
|
+
this.update();
|
|
448
|
+
};
|
|
449
|
+
this.watchProp('inputIndex', syncParent);
|
|
450
|
+
this.watchProp('inputName', syncParent);
|
|
451
|
+
this.watchProp('dataType', () => {
|
|
452
|
+
this.output.dataType = this.dataType;
|
|
453
|
+
syncParent();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
this.onUpdate = () => this.update();
|
|
457
|
+
this.onReady = () => this.update();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Get the input index this node represents
|
|
462
|
+
*/
|
|
463
|
+
get inputIndex(): number {
|
|
464
|
+
return this.props.inputIndex?.value ?? 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Get the input name (for documentation)
|
|
469
|
+
*/
|
|
470
|
+
get inputName(): string {
|
|
471
|
+
return this.props.inputName?.value ?? '';
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
get dataType(): DataType {
|
|
475
|
+
const value = this.props.dataType?.value;
|
|
476
|
+
return typeof value === 'string' && isCoreType(value) ? value : 'any';
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
private update(): void {
|
|
480
|
+
// Get value from parent subnet's corresponding input
|
|
481
|
+
if (this.parent && this.parent.inputs.length > 0) {
|
|
482
|
+
const idx = this.inputIndex;
|
|
483
|
+
const parentInput = this.parent.inputs[idx];
|
|
484
|
+
if (parentInput) {
|
|
485
|
+
this.output.setValue(parentInput.value);
|
|
486
|
+
|
|
487
|
+
// Pass through preview
|
|
488
|
+
if (this.isPreviewValue(parentInput.value)) {
|
|
489
|
+
this.preview = parentInput.value;
|
|
490
|
+
} else {
|
|
491
|
+
this.preview = null;
|
|
492
|
+
}
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
this.output.setValue(null);
|
|
498
|
+
this.preview = null;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
`,L=`/**
|
|
502
|
+
* OutputNode - Explicitly defines a subnet's output
|
|
503
|
+
* Overrides the cooking node when present
|
|
504
|
+
*/
|
|
505
|
+
|
|
506
|
+
import { Node } from '../../Node.js';
|
|
507
|
+
import type { Graph } from '../../Graph.js';
|
|
508
|
+
import type { DataType } from '@/types/node.types';
|
|
509
|
+
import { CORE_TYPES, isCoreType } from '@/types/coreTypes';
|
|
510
|
+
|
|
511
|
+
const TYPE_CHOICES = CORE_TYPES.map(type => ({ value: type, label: type }));
|
|
512
|
+
|
|
513
|
+
export const nodeMetadata = {
|
|
514
|
+
type: 'Output',
|
|
515
|
+
name: 'Output',
|
|
516
|
+
icon: 'LogOut',
|
|
517
|
+
description: 'Defines the output of a subnet',
|
|
518
|
+
category: 'network'
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
export class OutputNode extends Node {
|
|
522
|
+
constructor(id: string, graph: Graph) {
|
|
523
|
+
super(id, 'Output', graph);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
protected setup(): void {
|
|
527
|
+
// Input receives the value to output from the subnet
|
|
528
|
+
this.in('input', null);
|
|
529
|
+
|
|
530
|
+
// Output index - for multiple outputs from a subnet
|
|
531
|
+
this.addParm('outputIndex', {
|
|
532
|
+
value: 0,
|
|
533
|
+
type: 'int',
|
|
534
|
+
params: { min: 0, step: 1 },
|
|
535
|
+
displayName: 'Output Index'
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
this.addParm('outputName', {
|
|
539
|
+
value: '',
|
|
540
|
+
type: 'text',
|
|
541
|
+
displayName: 'Output Name'
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
this.addParm('dataType', {
|
|
545
|
+
value: 'any',
|
|
546
|
+
type: 'select',
|
|
547
|
+
params: { options: TYPE_CHOICES },
|
|
548
|
+
displayName: 'Data Type'
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Watch for index changes to trigger parent port sync
|
|
552
|
+
const syncParent = () => {
|
|
553
|
+
if (this.parent && (this.parent as any).syncPorts) {
|
|
554
|
+
(this.parent as any).syncPorts();
|
|
555
|
+
}
|
|
556
|
+
this.update();
|
|
557
|
+
};
|
|
558
|
+
this.watchProp('outputIndex', syncParent);
|
|
559
|
+
this.watchProp('outputName', syncParent);
|
|
560
|
+
this.watchProp('dataType', () => {
|
|
561
|
+
if (this.inputs[0]) this.inputs[0].dataType = this.dataType;
|
|
562
|
+
syncParent();
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
this.onUpdate = () => this.update();
|
|
566
|
+
this.onReady = () => this.update();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Get the output index this node represents
|
|
571
|
+
*/
|
|
572
|
+
get outputIndex(): number {
|
|
573
|
+
return this.props.outputIndex?.value ?? 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
get outputName(): string {
|
|
577
|
+
return this.props.outputName?.value ?? '';
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
get dataType(): DataType {
|
|
581
|
+
const value = this.props.dataType?.value;
|
|
582
|
+
return typeof value === 'string' && isCoreType(value) ? value : 'any';
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private update(): void {
|
|
586
|
+
// Pass through the input value and preview
|
|
587
|
+
const input = this.inputs[0];
|
|
588
|
+
if (input) {
|
|
589
|
+
if (this.isPreviewValue(input.value)) {
|
|
590
|
+
this.preview = input.value;
|
|
591
|
+
} else {
|
|
592
|
+
this.preview = null;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
`,R=`import { randomFromSeed } from '../../../../packages/runtime/src/builtins/core/random.js';
|
|
598
|
+
import { Node } from '../../Node.js';
|
|
599
|
+
import type { Graph } from '../../Graph.js';
|
|
600
|
+
|
|
601
|
+
export const nodeMetadata = {
|
|
602
|
+
type: 'Random',
|
|
603
|
+
name: 'Random',
|
|
604
|
+
icon: 'Dice5',
|
|
605
|
+
description: 'Generate a deterministic value from an explicit seed',
|
|
606
|
+
category: 'utility'
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
export class RandomNode extends Node {
|
|
610
|
+
constructor(id: string, graph: Graph) {
|
|
611
|
+
super(id, 'Random', graph);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
protected setup(): void {
|
|
615
|
+
const seed = this.in('seed', 0, { type: 'int' });
|
|
616
|
+
const sample = this.in('sample', 0, { type: 'int' });
|
|
617
|
+
const value = this.out<number>('value');
|
|
618
|
+
const update = () => value.setValue(randomFromSeed(seed.value, sample.value));
|
|
619
|
+
seed.onChange = update;
|
|
620
|
+
sample.onChange = update;
|
|
621
|
+
this.onUpdate = update;
|
|
622
|
+
this.onReady = update;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
`,z=`import { remap } from '../../../../packages/runtime/src/builtins/core/remap.js';
|
|
626
|
+
import { Node } from '../../Node.js';
|
|
627
|
+
import type { Graph } from '../../Graph.js';
|
|
628
|
+
|
|
629
|
+
export const nodeMetadata = {
|
|
630
|
+
type: 'Remap',
|
|
631
|
+
name: 'Remap',
|
|
632
|
+
icon: 'ArrowRightLeft',
|
|
633
|
+
description: 'Map a number from one range into another',
|
|
634
|
+
category: 'utility'
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
export class RemapNode extends Node {
|
|
638
|
+
constructor(id: string, graph: Graph) {
|
|
639
|
+
super(id, 'Remap', graph);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
protected setup(): void {
|
|
643
|
+
const value = this.in('value', 0, { type: 'float' });
|
|
644
|
+
const inMin = this.in('inMin', 0, { type: 'float' });
|
|
645
|
+
const inMax = this.in('inMax', 1, { type: 'float' });
|
|
646
|
+
const outMin = this.in('outMin', 0, { type: 'float' });
|
|
647
|
+
const outMax = this.in('outMax', 1, { type: 'float' });
|
|
648
|
+
const result = this.out<number>('result');
|
|
649
|
+
this.addParm('clamp', { value: false, type: 'boolean', displayName: 'Clamp' });
|
|
650
|
+
|
|
651
|
+
const update = () => result.setValue(remap(
|
|
652
|
+
value.value,
|
|
653
|
+
inMin.value,
|
|
654
|
+
inMax.value,
|
|
655
|
+
outMin.value,
|
|
656
|
+
outMax.value,
|
|
657
|
+
this.props.clamp.value,
|
|
658
|
+
));
|
|
659
|
+
for (const input of [value, inMin, inMax, outMin, outMax]) input.onChange = update;
|
|
660
|
+
this.watchProp('clamp', update);
|
|
661
|
+
this.onUpdate = update;
|
|
662
|
+
this.onReady = update;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
`,B=`/**
|
|
666
|
+
* SelectNode - Pick an item from an array by index
|
|
667
|
+
*
|
|
668
|
+
* Takes an array input and outputs the item at the specified index.
|
|
669
|
+
* Supports wrapping so indices beyond array length wrap around.
|
|
670
|
+
*/
|
|
671
|
+
|
|
672
|
+
import { Node } from '../../Node.js';
|
|
673
|
+
import type { Graph } from '../../Graph.js';
|
|
674
|
+
import type { InputPort, OutputPort } from '@/types/node.types';
|
|
675
|
+
|
|
676
|
+
export const nodeMetadata = {
|
|
677
|
+
type: 'Select',
|
|
678
|
+
name: 'Select',
|
|
679
|
+
icon: 'ListFilter',
|
|
680
|
+
description: 'Pick an item from an array by index',
|
|
681
|
+
category: 'utility'
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
export class SelectNode extends Node {
|
|
685
|
+
private arrayInput!: InputPort<unknown[]>;
|
|
686
|
+
private itemOutput!: OutputPort<unknown>;
|
|
687
|
+
private countOutput!: OutputPort<number>;
|
|
688
|
+
|
|
689
|
+
constructor(id: string, graph: Graph) {
|
|
690
|
+
super(id, 'Select', graph);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
protected setup(): void {
|
|
694
|
+
// Inputs (default to empty array, type is 'array')
|
|
695
|
+
this.arrayInput = this.in<unknown[]>('array', [], { type: 'array' });
|
|
696
|
+
|
|
697
|
+
// Outputs (second param is PortType: 'param' | 'trigger')
|
|
698
|
+
this.itemOutput = this.out('item', 'param');
|
|
699
|
+
this.countOutput = this.out('count', 'param');
|
|
700
|
+
|
|
701
|
+
// Parameters
|
|
702
|
+
this.addParm('index', {
|
|
703
|
+
value: 0,
|
|
704
|
+
type: 'int',
|
|
705
|
+
params: { min: 0, max: 0, step: 1 },
|
|
706
|
+
displayName: 'Index'
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
this.addParm('wrap', {
|
|
710
|
+
value: true,
|
|
711
|
+
type: 'boolean',
|
|
712
|
+
displayName: 'Wrap Index'
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// Watch for changes
|
|
716
|
+
this.watchProp('index', () => this.update());
|
|
717
|
+
this.watchProp('wrap', () => this.update());
|
|
718
|
+
|
|
719
|
+
this.onUpdate = () => this.update();
|
|
720
|
+
this.onReady = () => this.update();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private update(): void {
|
|
724
|
+
const array = this.arrayInput.value;
|
|
725
|
+
|
|
726
|
+
// Handle non-array or empty input
|
|
727
|
+
if (!Array.isArray(array) || array.length === 0) {
|
|
728
|
+
this.itemOutput.setValue(null);
|
|
729
|
+
this.countOutput.setValue(0);
|
|
730
|
+
this.updatePropParams('index', { max: 0 });
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Update index max based on array length
|
|
735
|
+
this.updatePropParams('index', { max: array.length - 1 });
|
|
736
|
+
|
|
737
|
+
// Get the index, applying wrapping if enabled
|
|
738
|
+
let index = this.props.index?.value ?? 0;
|
|
739
|
+
const wrap = this.props.wrap?.value ?? true;
|
|
740
|
+
|
|
741
|
+
if (wrap && array.length > 0) {
|
|
742
|
+
index = ((index % array.length) + array.length) % array.length;
|
|
743
|
+
} else {
|
|
744
|
+
index = Math.max(0, Math.min(index, array.length - 1));
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const item = array[index];
|
|
748
|
+
|
|
749
|
+
this.itemOutput.setValue(item);
|
|
750
|
+
this.countOutput.setValue(array.length);
|
|
751
|
+
|
|
752
|
+
// If the item is a canvas or image, show as preview
|
|
753
|
+
if (
|
|
754
|
+
item instanceof HTMLCanvasElement ||
|
|
755
|
+
item instanceof HTMLImageElement
|
|
756
|
+
) {
|
|
757
|
+
this.preview = item;
|
|
758
|
+
} else {
|
|
759
|
+
this.preview = null;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
`,V=`import { Node } from '../../Node.js';
|
|
764
|
+
import type { Graph } from '../../Graph.js';
|
|
765
|
+
|
|
766
|
+
export const nodeMetadata = {
|
|
767
|
+
type: 'Null',
|
|
768
|
+
name: 'Null',
|
|
769
|
+
icon: 'Circle',
|
|
770
|
+
description: 'Pass its input through unchanged',
|
|
771
|
+
category: 'routing',
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
export class NullNode extends Node {
|
|
775
|
+
constructor(id: string, graph: Graph) {
|
|
776
|
+
super(id, 'Null', graph);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
protected setup(): void {
|
|
780
|
+
const input = this.in('input', null, { type: 'any' });
|
|
781
|
+
const output = this.out('output');
|
|
782
|
+
const update = () => output.setValue(input.value);
|
|
783
|
+
input.onChange = update;
|
|
784
|
+
this.onUpdate = update;
|
|
785
|
+
this.onReady = update;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
`,H=`/**
|
|
789
|
+
* FreezeNode - Capture and lock a value in place
|
|
790
|
+
*
|
|
791
|
+
* When frozen, the node outputs its captured value regardless of input changes.
|
|
792
|
+
* Useful for preserving a specific state in a workflow.
|
|
793
|
+
*/
|
|
794
|
+
|
|
795
|
+
import { Node } from '../../Node.js';
|
|
796
|
+
import type { Graph } from '../../Graph.js';
|
|
797
|
+
import type { InputPort, OutputPort } from '@/types/node.types';
|
|
798
|
+
|
|
799
|
+
export const nodeMetadata = {
|
|
800
|
+
type: 'Freeze',
|
|
801
|
+
name: 'Freeze',
|
|
802
|
+
icon: 'Snowflake',
|
|
803
|
+
description: 'Capture and lock a value',
|
|
804
|
+
category: 'utility'
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
export class FreezeNode extends Node {
|
|
808
|
+
private input!: InputPort<unknown>;
|
|
809
|
+
private output!: OutputPort<unknown>;
|
|
810
|
+
private frozenValue: unknown = null;
|
|
811
|
+
private hasCaptured: boolean = false;
|
|
812
|
+
|
|
813
|
+
constructor(id: string, graph: Graph) {
|
|
814
|
+
super(id, 'Freeze', graph);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
protected setup(): void {
|
|
818
|
+
// Input (default to null, type is 'any')
|
|
819
|
+
this.input = this.in<unknown>('input', null, { type: 'any' });
|
|
820
|
+
|
|
821
|
+
// Output (second param is PortType: 'param' | 'trigger')
|
|
822
|
+
this.output = this.out<unknown>('output', 'param');
|
|
823
|
+
|
|
824
|
+
// Parameters
|
|
825
|
+
this.addParm('frozen', {
|
|
826
|
+
value: false,
|
|
827
|
+
type: 'boolean',
|
|
828
|
+
displayName: 'Frozen'
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Actions
|
|
832
|
+
this.addAction('capture', {
|
|
833
|
+
label: 'Capture',
|
|
834
|
+
icon: 'Camera',
|
|
835
|
+
callback: () => this.capture()
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
this.addAction('clear', {
|
|
839
|
+
label: 'Clear',
|
|
840
|
+
icon: 'Trash2',
|
|
841
|
+
callback: () => this.clear()
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// Watch frozen state
|
|
845
|
+
this.watchProp('frozen', () => this.update());
|
|
846
|
+
|
|
847
|
+
this.onUpdate = () => this.update();
|
|
848
|
+
this.onReady = () => this.update();
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Capture the current input value
|
|
853
|
+
*/
|
|
854
|
+
capture(): void {
|
|
855
|
+
this.frozenValue = this.deepClone(this.input.value);
|
|
856
|
+
this.hasCaptured = true;
|
|
857
|
+
this.setParm('frozen', true);
|
|
858
|
+
this.update();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Clear the frozen value
|
|
863
|
+
*/
|
|
864
|
+
clear(): void {
|
|
865
|
+
this.frozenValue = null;
|
|
866
|
+
this.hasCaptured = false;
|
|
867
|
+
this.setParm('frozen', false);
|
|
868
|
+
this.update();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Deep clone a value if possible
|
|
873
|
+
*/
|
|
874
|
+
private deepClone(value: unknown): unknown {
|
|
875
|
+
if (value === null || value === undefined) {
|
|
876
|
+
return value;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Handle canvas - create a copy
|
|
880
|
+
if (value instanceof HTMLCanvasElement) {
|
|
881
|
+
const canvas = document.createElement('canvas');
|
|
882
|
+
canvas.width = value.width;
|
|
883
|
+
canvas.height = value.height;
|
|
884
|
+
const ctx = canvas.getContext('2d');
|
|
885
|
+
ctx?.drawImage(value, 0, 0);
|
|
886
|
+
return canvas;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Handle image elements
|
|
890
|
+
if (value instanceof HTMLImageElement) {
|
|
891
|
+
const canvas = document.createElement('canvas');
|
|
892
|
+
canvas.width = value.naturalWidth || value.width;
|
|
893
|
+
canvas.height = value.naturalHeight || value.height;
|
|
894
|
+
const ctx = canvas.getContext('2d');
|
|
895
|
+
ctx?.drawImage(value, 0, 0);
|
|
896
|
+
return canvas;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Handle arrays
|
|
900
|
+
if (Array.isArray(value)) {
|
|
901
|
+
return value.map((item) => this.deepClone(item));
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Handle plain objects
|
|
905
|
+
if (typeof value === 'object') {
|
|
906
|
+
try {
|
|
907
|
+
return JSON.parse(JSON.stringify(value));
|
|
908
|
+
} catch {
|
|
909
|
+
return value;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Primitives are already immutable
|
|
914
|
+
return value;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
private update(): void {
|
|
918
|
+
const isFrozen = this.props.frozen?.value ?? false;
|
|
919
|
+
|
|
920
|
+
if (isFrozen && this.hasCaptured) {
|
|
921
|
+
// Output the frozen value
|
|
922
|
+
this.output.setValue(this.frozenValue);
|
|
923
|
+
|
|
924
|
+
// Show preview if it's a visual type
|
|
925
|
+
if (
|
|
926
|
+
this.frozenValue instanceof HTMLCanvasElement ||
|
|
927
|
+
this.frozenValue instanceof HTMLImageElement
|
|
928
|
+
) {
|
|
929
|
+
this.preview = this.frozenValue as HTMLCanvasElement | HTMLImageElement;
|
|
930
|
+
}
|
|
931
|
+
} else {
|
|
932
|
+
// Pass through the input
|
|
933
|
+
this.output.setValue(this.input.value);
|
|
934
|
+
|
|
935
|
+
// Show input preview if visual
|
|
936
|
+
if (
|
|
937
|
+
this.input.value instanceof HTMLCanvasElement ||
|
|
938
|
+
this.input.value instanceof HTMLImageElement
|
|
939
|
+
) {
|
|
940
|
+
this.preview = this.input.value;
|
|
941
|
+
} else {
|
|
942
|
+
this.preview = null;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Serialize with frozen state
|
|
949
|
+
*/
|
|
950
|
+
serialize(): Record<string, unknown> {
|
|
951
|
+
// Can't serialize canvas/images directly - would need to convert to data URL
|
|
952
|
+
// For now, just save the frozen state, not the value
|
|
953
|
+
return {
|
|
954
|
+
hasCaptured: this.hasCaptured
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Deserialize and restore frozen state
|
|
960
|
+
*/
|
|
961
|
+
deserialize(data: Record<string, unknown>): void {
|
|
962
|
+
this.hasCaptured = data.hasCaptured === true;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
`,U=class{constructor(){this._graph=null,this._pwd=null,this._frame=1,this._fps=30,this._selectedNodes=new Set}setGraph(e){this._graph=e,s.setGraph(e)}getGraph(){return this._graph}node(e){return this._graph?e.startsWith(`/`)?this.resolveAbsolutePath(e):this._pwd?this._pwd.node(e):this.resolveAbsolutePath(`/`+e):null}parm(e){let t=e.lastIndexOf(`/`);if(t===-1)return null;let n=e.substring(0,t)||`/`,r=e.substring(t+1);return this.node(n)?.parm(r)??null}root(){return null}pwd(){return this._pwd}setPwd(e){this._pwd=e}cd(e){if(e===`/`){this._pwd=null;return}let t=this.node(e);t&&t.isNetwork()&&(this._pwd=t)}selectedNodes(){return this._graph?Array.from(this._selectedNodes).map(e=>this._graph.getNode(e)).filter(e=>e!==null):[]}selectedNodeIds(){return Array.from(this._selectedNodes)}setSelection(e){this._selectedNodes=new Set(e),this.onSelectionChange?.(e)}clearAllSelected(){this._selectedNodes.clear(),this.onSelectionChange?.([])}frame(){return this._frame}time(){return this._frame/this._fps}fps(){return this._fps}setFrame(e){this._frame=Math.max(1,Math.floor(e)),s.setFrame(this._frame)}setTime(e){this._frame=Math.max(1,Math.floor(e*this._fps)),s.setFrame(this._frame)}setFps(e){this._fps=Math.max(1,e),s.setFps(this._fps)}ch(e){return this.parm(e)?.evalAsFloat()??0}chs(e){return this.parm(e)?.evalAsString()??``}chv(e){let t=this.parm(e)?.eval();return Array.isArray(t)?t:typeof t==`number`?[t]:[]}markTimeDependentDirty(){if(this._graph)for(let e of this._graph.nodes)e.isTimeDependent&&e.markDirty()}getTimeDependentNodes(){return this._graph?this._graph.nodes.filter(e=>e.isTimeDependent):[]}nextFrame(){return this.setFrame(this._frame+1),this.markTimeDependentDirty(),this._frame}resolveAbsolutePath(e){if(!this._graph)return null;let t=e.replace(/^\//,``).split(`/`).filter(e=>e.length>0);if(t.length===0)return null;let n=null,r=this._graph.nodes.filter(e=>!e.parent);for(let e=0;e<t.length;e++){let i=t[e],a=(n?n.children():r).find(e=>e.id===i);if(!a)return null;n=a}return n}},W=new U,G=[l,d,p,g,y,S,T,D,k,j],K={Switch:u,Merge:f,Subnet:m,Input:_,Output:b,Random:C,Remap:E,Select:O,Null:A,Freeze:M};t(`core`,K),e(`Switch`,N),e(`Merge`,P),e(`Subnet`,F),e(`Input`,I),e(`Output`,L),e(`Random`,R),e(`Remap`,z),e(`Select`,B),e(`Null`,V),e(`Freeze`,H);export{U as CascadeContext,n as ExpressionEngine,M as FreezeNode,o as Graph,_ as InputNode,f as MergeNode,r as Node,A as NullNode,b as OutputNode,C as RandomNode,E as RemapNode,O as SelectNode,m as SubnetNode,u as SwitchNode,W as cascade,a as coreLibrary,K as coreNodeClasses,s as expressionEngine,G as nodeMetadataList};
|