@field/cascade 0.10.0 → 0.12.0
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 +25 -5
- package/ARCHITECTURE.md +5 -4
- package/CHANGELOG.md +382 -0
- package/README.md +1 -2
- package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
- package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
- package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-C-xYG-Wn.js} +120 -120
- package/dist/assets/CodePanel-CtXd50m6.js +2 -0
- package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
- package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
- package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
- package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
- package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
- package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
- package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
- package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
- package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-CBX3Wa87.js} +1 -1
- package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
- package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
- package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
- package/dist/assets/assert-BO2_erPD.js +1 -0
- package/dist/assets/assert-hvykGCnP.js +1 -0
- package/dist/assets/builder-BmBKcDix.js +1 -0
- package/dist/assets/camera-DFF7YA3F.js +1 -0
- package/dist/assets/checks-DAvyO9Gf.js +1 -0
- package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
- package/dist/assets/{core-BICrEt0y.js → core-DB-MkGjj.js} +3 -4
- package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
- package/dist/assets/{cssMode-D7TXw57I.js → cssMode-DPVckSOc.js} +1 -1
- package/dist/assets/definitions-B227e7f_.js +1 -0
- package/dist/assets/definitions-D0WOUJd7.js +1 -0
- package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-Dkutg_Z1.js} +1 -1
- package/dist/assets/expressions-X-Z-GrgW.js +5 -0
- package/dist/assets/file-DAL6IfjV.js +1 -0
- package/dist/assets/filters-D3k1LRMa.js +1 -0
- package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-CLiLKk3B.js} +1 -1
- package/dist/assets/generators-BhDxRAFC.js +1 -0
- package/dist/assets/geo-BsMImJIJ.js +1 -0
- package/dist/assets/geo-C4YngDob.js +2 -0
- package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-CcD3aMeL.js} +1 -1
- package/dist/assets/{html-C7O4TyhX.js → html-DvZWGfqb.js} +1 -1
- package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUD3VaoN.js} +1 -1
- package/dist/assets/image-0SfH-snh.js +1 -0
- package/dist/assets/image-Ck-RdGU4.js +2 -0
- package/dist/assets/index-BWnrmIB9.js +137 -0
- package/dist/assets/index-DIaxEONI.css +1 -0
- package/dist/assets/io-BGmt2g0D.js +1 -0
- package/dist/assets/{javascript-CpIEK7tz.js → javascript-Cap2ahgh.js} +1 -1
- package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-B0uLjp9C.js} +1 -1
- package/dist/assets/{liquid-CN6FFHZE.js → liquid-CnxwhfLn.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
- package/dist/assets/math-GeULS-sk.js +1 -0
- package/dist/assets/{mdx-DE89pafV.js → mdx-BV3jbrVa.js} +1 -1
- package/dist/assets/pop-KMF_qloi.js +1 -0
- package/dist/assets/preload-helper-Czpn1I53.js +1 -0
- package/dist/assets/{python-ByTllTB9.js → python-CGyFk4XK.js} +1 -1
- package/dist/assets/{razor-USYlRRCE.js → razor-CQfzX3-N.js} +1 -1
- package/dist/assets/remap-5SjXQU-q.js +1 -0
- package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
- package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
- package/dist/assets/text-ZpX7qNkx.js +2 -0
- package/dist/assets/time-P_OiBhcf.js +1 -0
- package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BOeXzO9H.js} +1 -1
- package/dist/assets/{typescript-DInBjGQQ.js → typescript-J1SnE_Tu.js} +1 -1
- package/dist/assets/{workers-C7Ye5B7D.js → workers-CYwwTiVF.js} +1 -1
- package/dist/assets/{xml-U0Uocqyo.js → xml-DnPRNzK9.js} +1 -1
- package/dist/assets/{yaml-CBw_PVYf.js → yaml-BmmYBNa9.js} +1 -1
- package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
- package/dist/cli/Graph-OB6EWSGL.js +2975 -0
- package/dist/cli/assert-GROGQNCQ.js +25 -0
- package/dist/cli/checks-3WB44XBL.js +189 -0
- package/dist/cli/chunk-2JEU3MGB.js +1125 -0
- package/dist/cli/chunk-2KXB47FP.js +115 -0
- package/dist/cli/chunk-3RXL5GHB.js +816 -0
- package/dist/cli/chunk-53LGFPWX.js +138 -0
- package/dist/cli/chunk-5ORUQ6ES.js +267 -0
- package/dist/cli/chunk-73S4JZXK.js +336 -0
- package/dist/cli/chunk-7QE2ZNSN.js +121 -0
- package/dist/cli/chunk-BP7UKF36.js +376 -0
- package/dist/cli/chunk-BPODRH52.js +201 -0
- package/dist/cli/chunk-BZ63XQ3I.js +1301 -0
- package/dist/cli/chunk-CKS2FQWR.js +89 -0
- package/dist/cli/chunk-CO22XHNG.js +593 -0
- package/dist/cli/chunk-CTPLRSWF.js +2197 -0
- package/dist/cli/chunk-DEQSEFFH.js +153 -0
- package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
- package/dist/cli/chunk-EZRWJ4G2.js +104 -0
- package/dist/cli/chunk-FWMSGKEH.js +71 -0
- package/dist/cli/chunk-G23A2BZZ.js +705 -0
- package/dist/cli/chunk-GRIN2DDP.js +1465 -0
- package/dist/cli/chunk-HFHWYL55.js +139 -0
- package/dist/cli/chunk-HSNOBIVT.js +263 -0
- package/dist/cli/chunk-ICH7N5PN.js +26 -0
- package/dist/cli/chunk-J7VK7MNF.js +418 -0
- package/dist/cli/chunk-JVOCK5W4.js +83 -0
- package/dist/cli/chunk-KBGUO27Q.js +15 -0
- package/dist/cli/chunk-KH4MUBKF.js +1551 -0
- package/dist/cli/chunk-L6B6FUQP.js +330 -0
- package/dist/cli/chunk-LTXWBXOM.js +438 -0
- package/dist/cli/chunk-MI5JLQYA.js +220 -0
- package/dist/cli/chunk-MP653A5O.js +3766 -0
- package/dist/cli/chunk-NJFMDXHR.js +320 -0
- package/dist/cli/chunk-OM6BEME6.js +267 -0
- package/dist/cli/chunk-OWWBYLHC.js +136 -0
- package/dist/cli/chunk-QQPA2ZHH.js +273 -0
- package/dist/cli/chunk-RUU3YWXU.js +99 -0
- package/dist/cli/chunk-RXDIMXN5.js +98 -0
- package/dist/cli/chunk-ST6LROUG.js +267 -0
- package/dist/cli/chunk-TJW67QSI.js +225 -0
- package/dist/cli/chunk-WD62JIP6.js +118 -0
- package/dist/cli/chunk-WGKPJZDY.js +364 -0
- package/dist/cli/chunk-X4WKAOQP.js +634 -0
- package/dist/cli/chunk-X566FWBV.js +156 -0
- package/dist/cli/chunk-YH637PDL.js +317 -0
- package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
- package/dist/cli/chunk-ZSCUZE6I.js +112 -0
- package/dist/cli/cliCommands-JJN5Z3ZC.js +3470 -0
- package/dist/cli/commands-PHYFYA3F.js +1374 -0
- package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
- package/dist/cli/definitions-BPOPW52L.js +32 -0
- package/dist/cli/doctorCommand-ORWQRNYP.js +106 -0
- package/dist/cli/edit-S5KEQ5RA.js +28 -0
- package/dist/cli/editorCommands-NFR32JOY.js +41 -0
- package/dist/cli/encode-ICPEPMH5.js +17 -0
- package/dist/cli/file-3HTQHPYQ.js +41 -0
- package/dist/cli/filters-6ODFWYK4.js +379 -0
- package/dist/cli/frames-LSAUBZ7P.js +16 -0
- package/dist/cli/generators-YU7GB4MD.js +210 -0
- package/dist/cli/geo-6ZPGKZRU.js +43 -0
- package/dist/cli/graphCommands-EOUOBOIJ.js +184 -0
- package/dist/cli/graphFile-YTGJB5F5.js +292 -0
- package/dist/cli/headlessCanvas-ENRHC7GS.js +16 -0
- package/dist/cli/helpCommand-PULH6ZYB.js +157 -0
- package/dist/cli/image-TXELZZK5.js +55 -0
- package/dist/cli/index.js +100 -30756
- package/dist/cli/math-RSYRQ3C6.js +16 -0
- package/dist/cli/mcp-JVZFQOEX.js +164 -0
- package/dist/cli/parity-X6DEH2DV.js +334 -0
- package/dist/cli/pop-B7SIIYKN.js +39 -0
- package/dist/cli/project-VYGIA7JX.js +12 -0
- package/dist/cli/projectDefinition-F3D7UIHJ.js +27 -0
- package/dist/cli/projectRuntime-2Q4JKEH3.js +55 -0
- package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
- package/dist/cli/render-PT4MWI6U.js +438 -0
- package/dist/cli/runCommand-HNPQVZMS.js +198 -0
- package/dist/cli/runner-5XPXVW7J.js +43 -0
- package/dist/cli/runtime/image.ts +302 -0
- package/dist/cli/runtime/io.ts +78 -0
- package/dist/cli/runtime/stage.ts +4 -1
- package/dist/cli/share-MHF6TNRK.js +166 -0
- package/dist/cli/stageCache-TY2BFZJR.js +15 -0
- package/dist/cli/stageRunner-KY2EJP7X.js +10 -0
- package/dist/cli/text-MB3EKR4H.js +126 -0
- package/dist/cli/verbs-PI2JH5FI.js +19 -0
- package/dist/cli/webBuild-WPPMGGW7.js +44 -0
- package/dist/contracts/capabilities.d.ts +18 -1
- package/dist/contracts/color.d.ts +8 -0
- package/dist/contracts/color.js +23 -0
- package/dist/contracts/definition.d.ts +10 -0
- package/dist/contracts/documentFormat.d.ts +18 -0
- package/dist/contracts/documentFormat.js +195 -0
- package/dist/contracts/editor.d.ts +17 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/schema.d.ts +1 -1
- package/dist/contracts/schema.js +1 -0
- package/dist/contracts/validate.js +11 -1
- package/dist/contracts/values.d.ts +24 -0
- package/dist/image.d.ts +156 -0
- package/dist/image.js +161 -0
- package/dist/index.html +8 -2
- package/dist/io.d.ts +29 -0
- package/dist/io.js +40 -0
- package/dist/player/app.js +3 -2
- package/dist/player/editor.js +35 -9
- package/dist/player/editorEngine.js +3 -2
- package/dist/runtime/builtins/assert/checks.d.ts +18 -0
- package/dist/runtime/builtins/assert/checks.js +207 -0
- package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
- package/dist/runtime/builtins/assert/definitions.js +54 -0
- package/dist/runtime/builtins/assert/index.d.ts +152 -0
- package/dist/runtime/builtins/assert/index.js +36 -0
- package/dist/runtime/builtins/image/contactSheet.d.ts +64 -0
- package/dist/runtime/builtins/image/contactSheet.js +150 -0
- package/dist/runtime/builtins/image/definitions.d.ts +950 -0
- package/dist/runtime/builtins/image/definitions.js +343 -0
- package/dist/runtime/builtins/image/file.d.ts +8 -0
- package/dist/runtime/builtins/image/file.js +37 -0
- package/dist/runtime/builtins/image/filters.d.ts +8 -0
- package/dist/runtime/builtins/image/filters.js +394 -0
- package/dist/runtime/builtins/image/generators.d.ts +7 -0
- package/dist/runtime/builtins/image/generators.js +144 -0
- package/dist/runtime/builtins/image/index.d.ts +952 -0
- package/dist/runtime/builtins/image/index.js +44 -0
- package/dist/runtime/builtins/image/io.d.ts +100 -0
- package/dist/runtime/builtins/image/io.js +142 -0
- package/dist/runtime/builtins/image/planar.d.ts +55 -0
- package/dist/runtime/builtins/image/planar.js +274 -0
- package/dist/runtime/builtins/image/simplex.d.ts +22 -0
- package/dist/runtime/builtins/image/simplex.js +92 -0
- package/dist/runtime/builtins/image/text.d.ts +4 -0
- package/dist/runtime/builtins/image/text.js +141 -0
- package/dist/runtime/builtins/index.d.ts +3 -0
- package/dist/runtime/builtins/index.js +7 -0
- package/dist/runtime/gpu.d.ts +25 -0
- package/dist/runtime/gpu.js +40 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +268 -44
- package/dist/runtime/types.d.ts +30 -0
- package/dist/stage.js +4 -1
- package/doc/CLI_REFERENCE.md +91 -2
- package/doc/EDITOR_EXPORT.md +37 -2
- package/doc/MCP.md +18 -1
- package/doc/NODE_REFERENCE.md +424 -10
- package/doc/PROJECT_AUTHORING.md +222 -7
- package/package.json +19 -4
- package/dist/assets/CodePanel-sY6sZa7B.js +0 -2
- package/dist/assets/CustomNodeDialog-BRKnqZhp.js +0 -1
- package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
- package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
- package/dist/assets/OpenGraphDialog-ZsgL-iwr.js +0 -1
- package/dist/assets/ProjectSettings-Cw17nNe3.js +0 -2
- package/dist/assets/definitions-DUww2Rt-.js +0 -1
- package/dist/assets/geo-C2RHOVHC.js +0 -1
- package/dist/assets/image-DR4oeyJB.js +0 -2062
- package/dist/assets/index-7vae6YFC.css +0 -1
- package/dist/assets/index-DyvDC6pS.js +0 -219
- package/dist/assets/math-DaDEgWKt.js +0 -1
- package/dist/assets/pop-pLH7L0PW.js +0 -1
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import {
|
|
2
|
+
expressionEngine
|
|
3
|
+
} from "./chunk-RUU3YWXU.js";
|
|
4
|
+
import {
|
|
5
|
+
imagePath,
|
|
6
|
+
sequenceFileName,
|
|
7
|
+
sequenceWidth
|
|
8
|
+
} from "./chunk-ICH7N5PN.js";
|
|
9
|
+
import {
|
|
10
|
+
TimeState,
|
|
11
|
+
runFrameRange
|
|
12
|
+
} from "./chunk-CO22XHNG.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/frames.ts
|
|
15
|
+
import fs from "node:fs/promises";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
// src/engine/cascade.ts
|
|
19
|
+
var CascadeContext = class {
|
|
20
|
+
constructor() {
|
|
21
|
+
this._graph = null;
|
|
22
|
+
this._pwd = null;
|
|
23
|
+
/**
|
|
24
|
+
* Time state. Frames are stored as real numbers: `frame()` reports the
|
|
25
|
+
* integer frame ($F) and `fframe()` the fractional one ($FF), so sub-frame
|
|
26
|
+
* time exists rather than being floored away at the door.
|
|
27
|
+
*/
|
|
28
|
+
this._time = new TimeState();
|
|
29
|
+
this._selectedNodes = /* @__PURE__ */ new Set();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize with a graph reference
|
|
33
|
+
*/
|
|
34
|
+
setGraph(graph) {
|
|
35
|
+
this._graph = graph;
|
|
36
|
+
expressionEngine.setGraph(graph);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the current graph
|
|
40
|
+
*/
|
|
41
|
+
getGraph() {
|
|
42
|
+
return this._graph;
|
|
43
|
+
}
|
|
44
|
+
// ============ Node Access ============
|
|
45
|
+
/**
|
|
46
|
+
* Find a node by path (absolute or relative to pwd)
|
|
47
|
+
*/
|
|
48
|
+
node(path2) {
|
|
49
|
+
if (!this._graph) return null;
|
|
50
|
+
if (path2.startsWith("/")) {
|
|
51
|
+
return this.resolveAbsolutePath(path2);
|
|
52
|
+
}
|
|
53
|
+
if (this._pwd) {
|
|
54
|
+
return this._pwd.node(path2);
|
|
55
|
+
}
|
|
56
|
+
return this.resolveAbsolutePath("/" + path2);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Find a parameter by path (e.g., "/node1/radius")
|
|
60
|
+
*/
|
|
61
|
+
parm(path2) {
|
|
62
|
+
const lastSlash = path2.lastIndexOf("/");
|
|
63
|
+
if (lastSlash === -1) return null;
|
|
64
|
+
const nodePath = path2.substring(0, lastSlash) || "/";
|
|
65
|
+
const parmName = path2.substring(lastSlash + 1);
|
|
66
|
+
const node = this.node(nodePath);
|
|
67
|
+
return node?.parm(parmName) ?? null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the root node (virtual - returns null, represents "/")
|
|
71
|
+
* In Cascade, root is not a real node but the graph itself
|
|
72
|
+
*/
|
|
73
|
+
root() {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get current working directory/network
|
|
78
|
+
*/
|
|
79
|
+
pwd() {
|
|
80
|
+
return this._pwd;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Set current working directory/network
|
|
84
|
+
*/
|
|
85
|
+
setPwd(node) {
|
|
86
|
+
this._pwd = node;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Navigate to a path (like cd command)
|
|
90
|
+
*/
|
|
91
|
+
cd(path2) {
|
|
92
|
+
if (path2 === "/") {
|
|
93
|
+
this._pwd = null;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const target = this.node(path2);
|
|
97
|
+
if (target && target.isNetwork()) {
|
|
98
|
+
this._pwd = target;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ============ Selection ============
|
|
102
|
+
/**
|
|
103
|
+
* Get all selected nodes
|
|
104
|
+
*/
|
|
105
|
+
selectedNodes() {
|
|
106
|
+
if (!this._graph) return [];
|
|
107
|
+
return Array.from(this._selectedNodes).map((id) => this._graph.getNode(id)).filter((n) => n !== null);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get selected node IDs
|
|
111
|
+
*/
|
|
112
|
+
selectedNodeIds() {
|
|
113
|
+
return Array.from(this._selectedNodes);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Set selection (internal use by editor)
|
|
117
|
+
*/
|
|
118
|
+
setSelection(nodeIds) {
|
|
119
|
+
this._selectedNodes = new Set(nodeIds);
|
|
120
|
+
this.onSelectionChange?.(nodeIds);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Clear all selection
|
|
124
|
+
*/
|
|
125
|
+
clearAllSelected() {
|
|
126
|
+
this._selectedNodes.clear();
|
|
127
|
+
this.onSelectionChange?.([]);
|
|
128
|
+
}
|
|
129
|
+
// ============ Time/Playback ============
|
|
130
|
+
/**
|
|
131
|
+
* Get current frame number (integer) — $F
|
|
132
|
+
*/
|
|
133
|
+
frame() {
|
|
134
|
+
return this._time.frame;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get current frame as a fractional value — $FF
|
|
138
|
+
*/
|
|
139
|
+
fframe() {
|
|
140
|
+
return this._time.frameFraction;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get current time in seconds — $T
|
|
144
|
+
*/
|
|
145
|
+
time() {
|
|
146
|
+
return this._time.time;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get frames per second — $FPS
|
|
150
|
+
*/
|
|
151
|
+
fps() {
|
|
152
|
+
return this._time.fps;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Set current frame. Fractional frames are preserved; `frame()` still
|
|
156
|
+
* reports the floored integer frame.
|
|
157
|
+
*/
|
|
158
|
+
setFrame(frame) {
|
|
159
|
+
this._time.setFrame(frame);
|
|
160
|
+
expressionEngine.setFrame(this._time.frameFraction);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Set current time in seconds, keeping the sub-frame remainder
|
|
164
|
+
*/
|
|
165
|
+
setTime(time) {
|
|
166
|
+
this._time.setTime(time);
|
|
167
|
+
expressionEngine.setFrame(this._time.frameFraction);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Set FPS
|
|
171
|
+
*/
|
|
172
|
+
setFps(fps) {
|
|
173
|
+
this._time.setFps(fps);
|
|
174
|
+
expressionEngine.setFps(this._time.fps);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The clock a frame-range loop drives. Nothing here reads a wall clock —
|
|
178
|
+
* the caller owns time, which is what keeps a cook deterministic.
|
|
179
|
+
*/
|
|
180
|
+
clock() {
|
|
181
|
+
return {
|
|
182
|
+
setFrame: (frame) => this.setFrame(frame),
|
|
183
|
+
markTimeDependentDirty: () => this.markTimeDependentDirty(),
|
|
184
|
+
getFrame: () => this.frame(),
|
|
185
|
+
getFrameFraction: () => this.fframe(),
|
|
186
|
+
getTime: () => this.time(),
|
|
187
|
+
getFps: () => this.fps()
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// ============ Channel Shortcuts ============
|
|
191
|
+
/**
|
|
192
|
+
* Get parameter value as number (shorthand)
|
|
193
|
+
*/
|
|
194
|
+
ch(path2) {
|
|
195
|
+
const p = this.parm(path2);
|
|
196
|
+
return p?.evalAsFloat() ?? 0;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get parameter value as string (shorthand)
|
|
200
|
+
*/
|
|
201
|
+
chs(path2) {
|
|
202
|
+
const p = this.parm(path2);
|
|
203
|
+
return p?.evalAsString() ?? "";
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get parameter value as vector (shorthand)
|
|
207
|
+
*/
|
|
208
|
+
chv(path2) {
|
|
209
|
+
const p = this.parm(path2);
|
|
210
|
+
const val = p?.eval();
|
|
211
|
+
if (Array.isArray(val)) return val;
|
|
212
|
+
if (typeof val === "number") return [val];
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
// ============ Time-Dependent Updates ============
|
|
216
|
+
/**
|
|
217
|
+
* Mark all time-dependent nodes as dirty
|
|
218
|
+
* Call this at the start of each frame in the animation loop
|
|
219
|
+
*/
|
|
220
|
+
markTimeDependentDirty() {
|
|
221
|
+
if (!this._graph) return;
|
|
222
|
+
for (const node of this._graph.nodes) {
|
|
223
|
+
if (node.isTimeDependent) {
|
|
224
|
+
node.markDirty();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get all time-dependent nodes in the graph
|
|
230
|
+
*/
|
|
231
|
+
getTimeDependentNodes() {
|
|
232
|
+
if (!this._graph) return [];
|
|
233
|
+
return this._graph.nodes.filter((n) => n.isTimeDependent);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Advance the frame and mark time-dependent nodes dirty.
|
|
237
|
+
* Convenience method for animation loops; `step` may be fractional.
|
|
238
|
+
* Returns the new fractional frame.
|
|
239
|
+
*/
|
|
240
|
+
nextFrame(step = 1) {
|
|
241
|
+
this.setFrame(this.fframe() + step);
|
|
242
|
+
this.markTimeDependentDirty();
|
|
243
|
+
return this.fframe();
|
|
244
|
+
}
|
|
245
|
+
// ============ Internal Helpers ============
|
|
246
|
+
resolveAbsolutePath(path2) {
|
|
247
|
+
if (!this._graph) return null;
|
|
248
|
+
const segments = path2.replace(/^\//, "").split("/").filter((s) => s.length > 0);
|
|
249
|
+
if (segments.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
let current = null;
|
|
253
|
+
const rootNodes = this._graph.nodes.filter((n) => !n.parent);
|
|
254
|
+
for (let i = 0; i < segments.length; i++) {
|
|
255
|
+
const segment = segments[i];
|
|
256
|
+
const searchIn = current ? current.children() : rootNodes;
|
|
257
|
+
const found = searchIn.find((n) => n.id === segment);
|
|
258
|
+
if (!found) return null;
|
|
259
|
+
current = found;
|
|
260
|
+
}
|
|
261
|
+
return current;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var cascade = new CascadeContext();
|
|
265
|
+
|
|
266
|
+
// src/engine/animation/frameRange.ts
|
|
267
|
+
async function evaluateFrameRange(options) {
|
|
268
|
+
const { fps, context = cascade, ...rest } = options;
|
|
269
|
+
const previousFps = context.fps();
|
|
270
|
+
if (fps !== void 0) context.setFps(fps);
|
|
271
|
+
try {
|
|
272
|
+
return await runFrameRange(context.clock(), rest);
|
|
273
|
+
} finally {
|
|
274
|
+
if (fps !== void 0) context.setFps(previousFps);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/cli/frames.ts
|
|
279
|
+
function parseFrameSpec(spec) {
|
|
280
|
+
const text = String(spec ?? "").trim();
|
|
281
|
+
const match = /^(-?\d+(?:\.\d+)?)(?:\s*[-:]\s*(-?\d+(?:\.\d+)?))?(?:\s*x\s*(\d+(?:\.\d+)?))?$/i.exec(text);
|
|
282
|
+
if (!match) {
|
|
283
|
+
throw new Error(`--frames expects a range like 1-100, 1-100x2, or a single frame like 42 (got "${spec}")`);
|
|
284
|
+
}
|
|
285
|
+
const start = Number(match[1]);
|
|
286
|
+
const end = match[2] === void 0 ? start : Number(match[2]);
|
|
287
|
+
const step = match[3] === void 0 ? void 0 : Number(match[3]);
|
|
288
|
+
if (end < start) throw new Error(`--frames: end (${end}) must not be before start (${start})`);
|
|
289
|
+
if (step !== void 0 && !(step > 0)) throw new Error(`--frames: step must be greater than 0 (got ${step})`);
|
|
290
|
+
return step === void 0 ? { start, end } : { start, end, step };
|
|
291
|
+
}
|
|
292
|
+
function outputPorts(graph, entryNode) {
|
|
293
|
+
const nodes = entryNode ? [entryNode] : graph.nodes;
|
|
294
|
+
const found = [];
|
|
295
|
+
for (const node of nodes) {
|
|
296
|
+
for (const port of node.outputs) {
|
|
297
|
+
if (port.dataType !== "image") continue;
|
|
298
|
+
if (!entryNode && port.connections.length > 0) continue;
|
|
299
|
+
found.push({ node, port });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return found;
|
|
303
|
+
}
|
|
304
|
+
function outputSnapshot(graph) {
|
|
305
|
+
return JSON.stringify(graph.nodes.map((node) => [node.id, node.outputs.map((port) => port.value ?? null)]));
|
|
306
|
+
}
|
|
307
|
+
async function cookUntilSettled(graph, entryNode, options = {}) {
|
|
308
|
+
const { fixpoint = false, passes = 8 } = options;
|
|
309
|
+
const dirtyAll = () => {
|
|
310
|
+
for (const node of graph.nodes) node.markDirty?.();
|
|
311
|
+
};
|
|
312
|
+
let previous = fixpoint ? outputSnapshot(graph) : null;
|
|
313
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
314
|
+
await graph.execute(entryNode);
|
|
315
|
+
await graph.scheduler?.whenIdle?.();
|
|
316
|
+
if (graph.nodes.some((node) => node.error !== null)) return;
|
|
317
|
+
const stale = graph.nodes.some((node) => node.cookState === "stale" || node.cookState === "queued");
|
|
318
|
+
if (!fixpoint) {
|
|
319
|
+
if (!stale) return;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const snapshot = outputSnapshot(graph);
|
|
323
|
+
const settled = !stale && snapshot === previous;
|
|
324
|
+
previous = snapshot;
|
|
325
|
+
if (settled) return;
|
|
326
|
+
dirtyAll();
|
|
327
|
+
}
|
|
328
|
+
throw new Error(`graph did not settle after ${passes} cook passes`);
|
|
329
|
+
}
|
|
330
|
+
async function renderFrameRange(options) {
|
|
331
|
+
const { graph, project, out, start, end, step, fps, entryNode, verbose, signal } = options;
|
|
332
|
+
cascade.setGraph(graph);
|
|
333
|
+
const outDirectory = project.resolve(out);
|
|
334
|
+
await fs.mkdir(outDirectory, { recursive: true });
|
|
335
|
+
const files = [];
|
|
336
|
+
const width = sequenceWidth(end);
|
|
337
|
+
const result = await evaluateFrameRange({
|
|
338
|
+
start,
|
|
339
|
+
end,
|
|
340
|
+
step,
|
|
341
|
+
fps,
|
|
342
|
+
signal,
|
|
343
|
+
onFrame: async ({ frame, index, total }) => {
|
|
344
|
+
if (index === 0) for (const node of graph.nodes) node.markDirty();
|
|
345
|
+
await cookUntilSettled(graph, entryNode, { fixpoint: index === 0 });
|
|
346
|
+
const failed = graph.nodes.filter((node) => node.error !== null);
|
|
347
|
+
if (failed.length) {
|
|
348
|
+
const detail = failed.map((node) => `${node.id}: ${node.error?.message ?? "unknown cook error"}`).join("; ");
|
|
349
|
+
throw new Error(`frame ${frame} failed \u2014 ${detail}`);
|
|
350
|
+
}
|
|
351
|
+
const ports = outputPorts(graph, entryNode);
|
|
352
|
+
if (!ports.length) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
"No image output to save: the graph has no unconsumed image port. Name the node to render with --entry-node."
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
for (const { node, port } of ports) {
|
|
358
|
+
const source = imagePath(port.value);
|
|
359
|
+
if (!source) continue;
|
|
360
|
+
const name = sequenceFileName(node.id, frame, source, width);
|
|
361
|
+
await fs.copyFile(project.resolveMedia(source), path.join(outDirectory, name));
|
|
362
|
+
files.push(path.posix.join(out, name));
|
|
363
|
+
}
|
|
364
|
+
if (verbose) console.log(` frame ${frame} (${index + 1}/${total})`);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
return { frames: result.frames, files, aborted: result.aborted };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export {
|
|
371
|
+
parseFrameSpec,
|
|
372
|
+
outputPorts,
|
|
373
|
+
cookUntilSettled,
|
|
374
|
+
renderFrameRange
|
|
375
|
+
};
|
|
376
|
+
//# sourceMappingURL=chunk-BP7UKF36.js.map
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Node,
|
|
3
|
+
packagePathToType,
|
|
4
|
+
registerNodeClasses,
|
|
5
|
+
registerNodeMetadata
|
|
6
|
+
} from "./chunk-CTPLRSWF.js";
|
|
7
|
+
import {
|
|
8
|
+
bundleValueProblem,
|
|
9
|
+
gpuLimitShortfall,
|
|
10
|
+
graphSeed,
|
|
11
|
+
isGpuHost,
|
|
12
|
+
nodeSeed,
|
|
13
|
+
seededRandom,
|
|
14
|
+
selectBundleFields
|
|
15
|
+
} from "./chunk-L6B6FUQP.js";
|
|
16
|
+
|
|
17
|
+
// src/nodes/definition/DefinitionNode.ts
|
|
18
|
+
var silentProgress = { report: () => {
|
|
19
|
+
} };
|
|
20
|
+
function parameterOptions(definition) {
|
|
21
|
+
return {
|
|
22
|
+
...definition.label ? { label: definition.label } : {},
|
|
23
|
+
...definition.expression ? { defaultExpression: definition.expression } : {},
|
|
24
|
+
...definition.action ? { action: definition.action } : {},
|
|
25
|
+
...definition.min === void 0 ? {} : { min: definition.min },
|
|
26
|
+
...definition.max === void 0 ? {} : { max: definition.max },
|
|
27
|
+
...definition.step === void 0 ? {} : { step: definition.step },
|
|
28
|
+
...definition.options ? {
|
|
29
|
+
// A declared label wins; a bare value is its own label, which is what
|
|
30
|
+
// every definition written before the labelled form meant. Stringifying
|
|
31
|
+
// unconditionally is what turned "Magnific — costs money" into
|
|
32
|
+
// "magnific" and threw away the only notice that it bills per call.
|
|
33
|
+
choices: definition.options.map((option) => option !== null && typeof option === "object" && "value" in option ? {
|
|
34
|
+
value: option.value,
|
|
35
|
+
label: String(option.label ?? option.value),
|
|
36
|
+
...option.disabled ? { disabled: true } : {}
|
|
37
|
+
} : { value: option, label: String(option) })
|
|
38
|
+
} : {},
|
|
39
|
+
promotable: false
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function bundlePortOptions(port) {
|
|
43
|
+
if (port.type !== "bundle") return {};
|
|
44
|
+
return {
|
|
45
|
+
...port.schema ? { schema: port.schema } : {},
|
|
46
|
+
...Array.isArray(port.fields) ? { fieldSelection: port.fields } : port.fields ? { bundleFields: port.fields } : {}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function attachDefinition(node, registration, capabilities) {
|
|
50
|
+
const definition = registration.definition;
|
|
51
|
+
const pendingTriggers = /* @__PURE__ */ new Map();
|
|
52
|
+
let triggerSequence = 0;
|
|
53
|
+
const container = definition.container;
|
|
54
|
+
if (container === "subnet" || container === "array") {
|
|
55
|
+
node.declaredContainer = container;
|
|
56
|
+
}
|
|
57
|
+
for (const [name, input] of Object.entries(definition.inputs ?? {})) {
|
|
58
|
+
if (input.kind === "trigger") {
|
|
59
|
+
const port = node.in(name, void 0, {
|
|
60
|
+
type: "any",
|
|
61
|
+
...input.description ? { description: input.description } : {}
|
|
62
|
+
});
|
|
63
|
+
port.portType = "trigger";
|
|
64
|
+
port.onTrigger = async (payload) => {
|
|
65
|
+
const sequence = triggerSequence++;
|
|
66
|
+
pendingTriggers.set(name, {
|
|
67
|
+
id: `${node.id}:${sequence}`,
|
|
68
|
+
runId: `studio:${node.id}:${sequence}`,
|
|
69
|
+
sequence,
|
|
70
|
+
...payload === void 0 ? {} : { payload },
|
|
71
|
+
source: { kind: "external" }
|
|
72
|
+
});
|
|
73
|
+
node.markDirty();
|
|
74
|
+
await node.execute();
|
|
75
|
+
};
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (input.variadic) {
|
|
79
|
+
node.setVariadic(void 0, { type: input.type });
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
node.in(name, input.default, {
|
|
83
|
+
type: input.type,
|
|
84
|
+
...input.min === void 0 ? {} : { min: input.min },
|
|
85
|
+
...input.max === void 0 ? {} : { max: input.max },
|
|
86
|
+
...input.step === void 0 ? {} : { step: input.step },
|
|
87
|
+
...input.options ? {
|
|
88
|
+
choices: input.options.map((value) => ({ value, label: String(value) }))
|
|
89
|
+
} : {},
|
|
90
|
+
...input.description ? { description: input.description } : {},
|
|
91
|
+
...bundlePortOptions(input)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
for (const [name, output] of Object.entries(definition.outputs ?? {})) {
|
|
95
|
+
node.out(
|
|
96
|
+
name,
|
|
97
|
+
output.kind === "trigger" ? "trigger" : "param",
|
|
98
|
+
output.kind === "data" ? { type: output.type, description: output.description, ...bundlePortOptions(output) } : { description: output.description }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
for (const [name, prop] of Object.entries(definition.props ?? {})) {
|
|
102
|
+
const parameter = node.param(name, prop.default, {
|
|
103
|
+
type: prop.type,
|
|
104
|
+
...parameterOptions(prop)
|
|
105
|
+
});
|
|
106
|
+
parameter.documentField = "props";
|
|
107
|
+
}
|
|
108
|
+
const hasTriggerInputs = Object.values(definition.inputs ?? {}).some((input) => input.kind === "trigger");
|
|
109
|
+
let executePromise;
|
|
110
|
+
return async () => {
|
|
111
|
+
if (hasTriggerInputs && pendingTriggers.size === 0) return;
|
|
112
|
+
for (const name of definition.capabilities ?? []) {
|
|
113
|
+
if (!capabilities[name]) {
|
|
114
|
+
throw new Error(`${registration.moduleId} requires the Studio ${name} capability`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const execute = await (executePromise ??= registration.loadExecute("browser"));
|
|
118
|
+
const inputValues = {};
|
|
119
|
+
for (const [name, input] of Object.entries(definition.inputs ?? {})) {
|
|
120
|
+
inputValues[name] = input.kind === "trigger" ? pendingTriggers.get(name) : input.variadic ? node.getVariadicInputs().map((port) => port.value ?? void 0) : input.type === "bundle" && input.fields ? selectBundleFields(node.inputs.find((port) => port.name === name)?.value, input.fields) : node.inputs.find((port) => port.name === name)?.value;
|
|
121
|
+
}
|
|
122
|
+
pendingTriggers.clear();
|
|
123
|
+
const outputs = Object.fromEntries(Object.entries(definition.outputs ?? {}).map(([name, output]) => {
|
|
124
|
+
const port = node.outputs.find((candidate) => candidate.name === name);
|
|
125
|
+
if (!port) throw new Error(`Missing output ${registration.moduleId}.${name}`);
|
|
126
|
+
return [name, output.kind === "trigger" ? { trigger: (payload) => port.trigger(payload) } : {
|
|
127
|
+
set: (value) => {
|
|
128
|
+
if (output.type === "bundle") {
|
|
129
|
+
const problem = bundleValueProblem(value, output.fields);
|
|
130
|
+
if (problem) throw new Error(`${node.id}.${name} ${problem}`);
|
|
131
|
+
}
|
|
132
|
+
port.setValue(value);
|
|
133
|
+
}
|
|
134
|
+
}];
|
|
135
|
+
}));
|
|
136
|
+
let nodeCapabilities = capabilities;
|
|
137
|
+
const gpuHost = (definition.capabilities ?? []).includes("gpu") && isGpuHost(capabilities.gpu) ? capabilities.gpu : null;
|
|
138
|
+
if (gpuHost) {
|
|
139
|
+
await gpuHost.ensure();
|
|
140
|
+
const gpu = gpuHost.forNode(node.id);
|
|
141
|
+
const shortfall = gpuLimitShortfall(node.id, definition.gpuLimits, gpu);
|
|
142
|
+
if (shortfall) throw new Error(shortfall);
|
|
143
|
+
nodeCapabilities = { ...capabilities, gpu };
|
|
144
|
+
}
|
|
145
|
+
const seed = nodeSeed(graphSeed({ seed: node.documentSeed }), node.id);
|
|
146
|
+
const context = {
|
|
147
|
+
nodeId: node.id,
|
|
148
|
+
inputs: inputValues,
|
|
149
|
+
outputs,
|
|
150
|
+
props: Object.fromEntries(node.parameters.map((parameter) => [parameter.name, parameter.value])),
|
|
151
|
+
capabilities: nodeCapabilities,
|
|
152
|
+
// The node's own cook signal, not a fresh one that nobody aborts. See
|
|
153
|
+
// `Node.cookSignal` for what that cost.
|
|
154
|
+
signal: node.cookSignal ?? new AbortController().signal,
|
|
155
|
+
progress: silentProgress,
|
|
156
|
+
// The same derivation as the headless runtime, so a seeded node draws
|
|
157
|
+
// the same thing in both hosts.
|
|
158
|
+
seed,
|
|
159
|
+
random: seededRandom(seed)
|
|
160
|
+
};
|
|
161
|
+
await execute(context);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function configureNode(node, registration, capabilities) {
|
|
165
|
+
node.setFunction(attachDefinition(node, registration, capabilities));
|
|
166
|
+
}
|
|
167
|
+
function registerDefinitionNodes(registrations, capabilities = {}) {
|
|
168
|
+
const libraries = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const registration of registrations) {
|
|
170
|
+
const parts = registration.moduleId.split(".");
|
|
171
|
+
if (parts.length !== 3 || parts[0] !== "cascade") {
|
|
172
|
+
throw new Error(`Studio built-ins require cascade.<library>.<node>: ${registration.moduleId}`);
|
|
173
|
+
}
|
|
174
|
+
const libraryId = parts[1];
|
|
175
|
+
const nodeType = packagePathToType(registration.moduleId);
|
|
176
|
+
const classes = libraries.get(libraryId) ?? {};
|
|
177
|
+
classes[nodeType] = class extends Node {
|
|
178
|
+
constructor(id, graph) {
|
|
179
|
+
super(id, nodeType, graph);
|
|
180
|
+
}
|
|
181
|
+
setup() {
|
|
182
|
+
configureNode(this, registration, capabilities);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
libraries.set(libraryId, classes);
|
|
186
|
+
registerNodeMetadata({
|
|
187
|
+
type: registration.moduleId,
|
|
188
|
+
name: registration.definition.label ?? nodeType,
|
|
189
|
+
icon: registration.definition.icon,
|
|
190
|
+
description: registration.definition.description,
|
|
191
|
+
category: libraryId
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
for (const [libraryId, classes] of libraries) registerNodeClasses(libraryId, classes);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export {
|
|
198
|
+
attachDefinition,
|
|
199
|
+
registerDefinitionNodes
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=chunk-BPODRH52.js.map
|