@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.
Files changed (229) hide show
  1. package/AGENTS.md +25 -5
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +382 -0
  4. package/README.md +1 -2
  5. package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
  6. package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
  7. package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-C-xYG-Wn.js} +120 -120
  8. package/dist/assets/CodePanel-CtXd50m6.js +2 -0
  9. package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
  10. package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
  11. package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
  12. package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
  13. package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
  14. package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
  15. package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
  16. package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
  17. package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-CBX3Wa87.js} +1 -1
  18. package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
  19. package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
  20. package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
  21. package/dist/assets/assert-BO2_erPD.js +1 -0
  22. package/dist/assets/assert-hvykGCnP.js +1 -0
  23. package/dist/assets/builder-BmBKcDix.js +1 -0
  24. package/dist/assets/camera-DFF7YA3F.js +1 -0
  25. package/dist/assets/checks-DAvyO9Gf.js +1 -0
  26. package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
  27. package/dist/assets/{core-BICrEt0y.js → core-DB-MkGjj.js} +3 -4
  28. package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
  29. package/dist/assets/{cssMode-D7TXw57I.js → cssMode-DPVckSOc.js} +1 -1
  30. package/dist/assets/definitions-B227e7f_.js +1 -0
  31. package/dist/assets/definitions-D0WOUJd7.js +1 -0
  32. package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-Dkutg_Z1.js} +1 -1
  33. package/dist/assets/expressions-X-Z-GrgW.js +5 -0
  34. package/dist/assets/file-DAL6IfjV.js +1 -0
  35. package/dist/assets/filters-D3k1LRMa.js +1 -0
  36. package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-CLiLKk3B.js} +1 -1
  37. package/dist/assets/generators-BhDxRAFC.js +1 -0
  38. package/dist/assets/geo-BsMImJIJ.js +1 -0
  39. package/dist/assets/geo-C4YngDob.js +2 -0
  40. package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-CcD3aMeL.js} +1 -1
  41. package/dist/assets/{html-C7O4TyhX.js → html-DvZWGfqb.js} +1 -1
  42. package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUD3VaoN.js} +1 -1
  43. package/dist/assets/image-0SfH-snh.js +1 -0
  44. package/dist/assets/image-Ck-RdGU4.js +2 -0
  45. package/dist/assets/index-BWnrmIB9.js +137 -0
  46. package/dist/assets/index-DIaxEONI.css +1 -0
  47. package/dist/assets/io-BGmt2g0D.js +1 -0
  48. package/dist/assets/{javascript-CpIEK7tz.js → javascript-Cap2ahgh.js} +1 -1
  49. package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-B0uLjp9C.js} +1 -1
  50. package/dist/assets/{liquid-CN6FFHZE.js → liquid-CnxwhfLn.js} +1 -1
  51. package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
  52. package/dist/assets/math-GeULS-sk.js +1 -0
  53. package/dist/assets/{mdx-DE89pafV.js → mdx-BV3jbrVa.js} +1 -1
  54. package/dist/assets/pop-KMF_qloi.js +1 -0
  55. package/dist/assets/preload-helper-Czpn1I53.js +1 -0
  56. package/dist/assets/{python-ByTllTB9.js → python-CGyFk4XK.js} +1 -1
  57. package/dist/assets/{razor-USYlRRCE.js → razor-CQfzX3-N.js} +1 -1
  58. package/dist/assets/remap-5SjXQU-q.js +1 -0
  59. package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
  60. package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
  61. package/dist/assets/text-ZpX7qNkx.js +2 -0
  62. package/dist/assets/time-P_OiBhcf.js +1 -0
  63. package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BOeXzO9H.js} +1 -1
  64. package/dist/assets/{typescript-DInBjGQQ.js → typescript-J1SnE_Tu.js} +1 -1
  65. package/dist/assets/{workers-C7Ye5B7D.js → workers-CYwwTiVF.js} +1 -1
  66. package/dist/assets/{xml-U0Uocqyo.js → xml-DnPRNzK9.js} +1 -1
  67. package/dist/assets/{yaml-CBw_PVYf.js → yaml-BmmYBNa9.js} +1 -1
  68. package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
  69. package/dist/cli/Graph-OB6EWSGL.js +2975 -0
  70. package/dist/cli/assert-GROGQNCQ.js +25 -0
  71. package/dist/cli/checks-3WB44XBL.js +189 -0
  72. package/dist/cli/chunk-2JEU3MGB.js +1125 -0
  73. package/dist/cli/chunk-2KXB47FP.js +115 -0
  74. package/dist/cli/chunk-3RXL5GHB.js +816 -0
  75. package/dist/cli/chunk-53LGFPWX.js +138 -0
  76. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  77. package/dist/cli/chunk-73S4JZXK.js +336 -0
  78. package/dist/cli/chunk-7QE2ZNSN.js +121 -0
  79. package/dist/cli/chunk-BP7UKF36.js +376 -0
  80. package/dist/cli/chunk-BPODRH52.js +201 -0
  81. package/dist/cli/chunk-BZ63XQ3I.js +1301 -0
  82. package/dist/cli/chunk-CKS2FQWR.js +89 -0
  83. package/dist/cli/chunk-CO22XHNG.js +593 -0
  84. package/dist/cli/chunk-CTPLRSWF.js +2197 -0
  85. package/dist/cli/chunk-DEQSEFFH.js +153 -0
  86. package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
  87. package/dist/cli/chunk-EZRWJ4G2.js +104 -0
  88. package/dist/cli/chunk-FWMSGKEH.js +71 -0
  89. package/dist/cli/chunk-G23A2BZZ.js +705 -0
  90. package/dist/cli/chunk-GRIN2DDP.js +1465 -0
  91. package/dist/cli/chunk-HFHWYL55.js +139 -0
  92. package/dist/cli/chunk-HSNOBIVT.js +263 -0
  93. package/dist/cli/chunk-ICH7N5PN.js +26 -0
  94. package/dist/cli/chunk-J7VK7MNF.js +418 -0
  95. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  96. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  97. package/dist/cli/chunk-KH4MUBKF.js +1551 -0
  98. package/dist/cli/chunk-L6B6FUQP.js +330 -0
  99. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  100. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  101. package/dist/cli/chunk-MP653A5O.js +3766 -0
  102. package/dist/cli/chunk-NJFMDXHR.js +320 -0
  103. package/dist/cli/chunk-OM6BEME6.js +267 -0
  104. package/dist/cli/chunk-OWWBYLHC.js +136 -0
  105. package/dist/cli/chunk-QQPA2ZHH.js +273 -0
  106. package/dist/cli/chunk-RUU3YWXU.js +99 -0
  107. package/dist/cli/chunk-RXDIMXN5.js +98 -0
  108. package/dist/cli/chunk-ST6LROUG.js +267 -0
  109. package/dist/cli/chunk-TJW67QSI.js +225 -0
  110. package/dist/cli/chunk-WD62JIP6.js +118 -0
  111. package/dist/cli/chunk-WGKPJZDY.js +364 -0
  112. package/dist/cli/chunk-X4WKAOQP.js +634 -0
  113. package/dist/cli/chunk-X566FWBV.js +156 -0
  114. package/dist/cli/chunk-YH637PDL.js +317 -0
  115. package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
  116. package/dist/cli/chunk-ZSCUZE6I.js +112 -0
  117. package/dist/cli/cliCommands-JJN5Z3ZC.js +3470 -0
  118. package/dist/cli/commands-PHYFYA3F.js +1374 -0
  119. package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
  120. package/dist/cli/definitions-BPOPW52L.js +32 -0
  121. package/dist/cli/doctorCommand-ORWQRNYP.js +106 -0
  122. package/dist/cli/edit-S5KEQ5RA.js +28 -0
  123. package/dist/cli/editorCommands-NFR32JOY.js +41 -0
  124. package/dist/cli/encode-ICPEPMH5.js +17 -0
  125. package/dist/cli/file-3HTQHPYQ.js +41 -0
  126. package/dist/cli/filters-6ODFWYK4.js +379 -0
  127. package/dist/cli/frames-LSAUBZ7P.js +16 -0
  128. package/dist/cli/generators-YU7GB4MD.js +210 -0
  129. package/dist/cli/geo-6ZPGKZRU.js +43 -0
  130. package/dist/cli/graphCommands-EOUOBOIJ.js +184 -0
  131. package/dist/cli/graphFile-YTGJB5F5.js +292 -0
  132. package/dist/cli/headlessCanvas-ENRHC7GS.js +16 -0
  133. package/dist/cli/helpCommand-PULH6ZYB.js +157 -0
  134. package/dist/cli/image-TXELZZK5.js +55 -0
  135. package/dist/cli/index.js +100 -30756
  136. package/dist/cli/math-RSYRQ3C6.js +16 -0
  137. package/dist/cli/mcp-JVZFQOEX.js +164 -0
  138. package/dist/cli/parity-X6DEH2DV.js +334 -0
  139. package/dist/cli/pop-B7SIIYKN.js +39 -0
  140. package/dist/cli/project-VYGIA7JX.js +12 -0
  141. package/dist/cli/projectDefinition-F3D7UIHJ.js +27 -0
  142. package/dist/cli/projectRuntime-2Q4JKEH3.js +55 -0
  143. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  144. package/dist/cli/render-PT4MWI6U.js +438 -0
  145. package/dist/cli/runCommand-HNPQVZMS.js +198 -0
  146. package/dist/cli/runner-5XPXVW7J.js +43 -0
  147. package/dist/cli/runtime/image.ts +302 -0
  148. package/dist/cli/runtime/io.ts +78 -0
  149. package/dist/cli/runtime/stage.ts +4 -1
  150. package/dist/cli/share-MHF6TNRK.js +166 -0
  151. package/dist/cli/stageCache-TY2BFZJR.js +15 -0
  152. package/dist/cli/stageRunner-KY2EJP7X.js +10 -0
  153. package/dist/cli/text-MB3EKR4H.js +126 -0
  154. package/dist/cli/verbs-PI2JH5FI.js +19 -0
  155. package/dist/cli/webBuild-WPPMGGW7.js +44 -0
  156. package/dist/contracts/capabilities.d.ts +18 -1
  157. package/dist/contracts/color.d.ts +8 -0
  158. package/dist/contracts/color.js +23 -0
  159. package/dist/contracts/definition.d.ts +10 -0
  160. package/dist/contracts/documentFormat.d.ts +18 -0
  161. package/dist/contracts/documentFormat.js +195 -0
  162. package/dist/contracts/editor.d.ts +17 -0
  163. package/dist/contracts/index.d.ts +1 -0
  164. package/dist/contracts/index.js +1 -0
  165. package/dist/contracts/schema.d.ts +1 -1
  166. package/dist/contracts/schema.js +1 -0
  167. package/dist/contracts/validate.js +11 -1
  168. package/dist/contracts/values.d.ts +24 -0
  169. package/dist/image.d.ts +156 -0
  170. package/dist/image.js +161 -0
  171. package/dist/index.html +8 -2
  172. package/dist/io.d.ts +29 -0
  173. package/dist/io.js +40 -0
  174. package/dist/player/app.js +3 -2
  175. package/dist/player/editor.js +35 -9
  176. package/dist/player/editorEngine.js +3 -2
  177. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  178. package/dist/runtime/builtins/assert/checks.js +207 -0
  179. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  180. package/dist/runtime/builtins/assert/definitions.js +54 -0
  181. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  182. package/dist/runtime/builtins/assert/index.js +36 -0
  183. package/dist/runtime/builtins/image/contactSheet.d.ts +64 -0
  184. package/dist/runtime/builtins/image/contactSheet.js +150 -0
  185. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  186. package/dist/runtime/builtins/image/definitions.js +343 -0
  187. package/dist/runtime/builtins/image/file.d.ts +8 -0
  188. package/dist/runtime/builtins/image/file.js +37 -0
  189. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  190. package/dist/runtime/builtins/image/filters.js +394 -0
  191. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  192. package/dist/runtime/builtins/image/generators.js +144 -0
  193. package/dist/runtime/builtins/image/index.d.ts +952 -0
  194. package/dist/runtime/builtins/image/index.js +44 -0
  195. package/dist/runtime/builtins/image/io.d.ts +100 -0
  196. package/dist/runtime/builtins/image/io.js +142 -0
  197. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  198. package/dist/runtime/builtins/image/planar.js +274 -0
  199. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  200. package/dist/runtime/builtins/image/simplex.js +92 -0
  201. package/dist/runtime/builtins/image/text.d.ts +4 -0
  202. package/dist/runtime/builtins/image/text.js +141 -0
  203. package/dist/runtime/builtins/index.d.ts +3 -0
  204. package/dist/runtime/builtins/index.js +7 -0
  205. package/dist/runtime/gpu.d.ts +25 -0
  206. package/dist/runtime/gpu.js +40 -0
  207. package/dist/runtime/runtime.d.ts +2 -0
  208. package/dist/runtime/runtime.js +268 -44
  209. package/dist/runtime/types.d.ts +30 -0
  210. package/dist/stage.js +4 -1
  211. package/doc/CLI_REFERENCE.md +91 -2
  212. package/doc/EDITOR_EXPORT.md +37 -2
  213. package/doc/MCP.md +18 -1
  214. package/doc/NODE_REFERENCE.md +424 -10
  215. package/doc/PROJECT_AUTHORING.md +222 -7
  216. package/package.json +19 -4
  217. package/dist/assets/CodePanel-sY6sZa7B.js +0 -2
  218. package/dist/assets/CustomNodeDialog-BRKnqZhp.js +0 -1
  219. package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
  220. package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
  221. package/dist/assets/OpenGraphDialog-ZsgL-iwr.js +0 -1
  222. package/dist/assets/ProjectSettings-Cw17nNe3.js +0 -2
  223. package/dist/assets/definitions-DUww2Rt-.js +0 -1
  224. package/dist/assets/geo-C2RHOVHC.js +0 -1
  225. package/dist/assets/image-DR4oeyJB.js +0 -2062
  226. package/dist/assets/index-7vae6YFC.css +0 -1
  227. package/dist/assets/index-DyvDC6pS.js +0 -219
  228. package/dist/assets/math-DaDEgWKt.js +0 -1
  229. package/dist/assets/pop-pLH7L0PW.js +0 -1
@@ -1,6 +1,6 @@
1
1
  # CLI reference: editing graphs
2
2
 
3
- Everything an agent needs to build or change a `.cascade` graph without hand-editing JSON. Each command is one graph concept (node, wire, parameter, subnet), checks the edit with the same code as `cascade check`, and writes the file atomically. For running, rendering and exporting, see `cascade --help`, `doc/EDITOR_EXPORT.md` and `doc/WEB_PLAYER.md`.
3
+ Everything an agent needs to build or change a `.cascade` graph without hand-editing JSON. Each command is one graph concept (node, wire, parameter, subnet), checks the edit with the same code as `cascade check`, and writes the file atomically. Running, rendering, exporting and locks are listed under [The other commands](#the-other-commands); see also `cascade --help`, `doc/EDITOR_EXPORT.md` and `doc/WEB_PLAYER.md`.
4
4
 
5
5
  ## The guarantees
6
6
 
@@ -15,7 +15,7 @@ Everything an agent needs to build or change a `.cascade` graph without hand-edi
15
15
  | --- | --- |
16
16
  | `cascade graph create <graph> [--name <title>]` | A new empty graph. Never overwrites. |
17
17
  | `cascade graph read <graph> [--in <subnet>]` | Nodes with stored parameters, subnets with their ports, wires as `a.out -> b.in`. |
18
- | `cascade graph list [dir]` | Every `.cascade` under a directory, with node and wire counts. |
18
+ | `cascade graph list [dir]` | Every `.cascade` under a directory, with node and wire counts. Skips `renders/`, `out/`, `dist/` and `node_modules/` below it. |
19
19
  | `cascade node add <graph> <type> [--name <id>] [--in <subnet>] [--set k=v]... [--at x,y] [--comment <text>]` | Add a node. `<type>` is a module id or a short name: `Circle` is `cascade.geo.Circle`, `InkVocabulary` is `project.InkVocabulary`. The id defaults to the type in kebab case (`copy-to-points`, then `copy-to-points-2`). |
20
20
  | `cascade node rm <graph> <node> [--recursive]` | Remove a node and its wires. A subnet with children needs `--recursive`. |
21
21
  | `cascade connect <graph> <node.output> <node.input> [--no-layout]` | Wire two ports. A port can be left off when the node has one on that side. An input that was fed is rewired, and the old wire is reported. |
@@ -31,6 +31,41 @@ Everything an agent needs to build or change a `.cascade` graph without hand-edi
31
31
 
32
32
  Every edit takes `--dry-run` (check, write nothing) and `--json`.
33
33
 
34
+ ## The other commands
35
+
36
+ Running, sharing, locks and Studio. `cascade --help` has their options; `cascade render --help` and `cascade lock --help` have their own.
37
+
38
+ | Command | What it does |
39
+ | --- | --- |
40
+ | `cascade [project-directory \| graph-file]` | Open Studio on a project or graph. |
41
+ | `cascade new <name>` | Create a Cascade project. |
42
+ | `cascade node <Name> [project-directory]` | Create a deterministic custom node. |
43
+ | `cascade projects [directory]` | Show or set the default projects directory. |
44
+ | `cascade serve start\|stop\|restart\|status\|logs\|install` | Run Studio as a managed background service. |
45
+ | `cascade run <graph-file> [options]` | Cook a graph headlessly; `--frames` renders a sequence. |
46
+ | `cascade render <graph-file>` | Render an animation across worker processes, then a preview video. |
47
+ | `cascade validate <graph-file>` | Validate a graph file without executing. |
48
+ | `cascade check <graph-file>` | Statically check node definitions, types and graph structure. |
49
+ | `cascade inspect <graph-file>` | Every node with its classification and full definition. |
50
+ | `cascade parity <graph-file>` | Cook headless twice and through Studio's path; report per-node differences. |
51
+ | `cascade build <graph-file> --out <new-directory>` | A standalone browser player ([WEB_PLAYER.md](WEB_PLAYER.md)). |
52
+ | `cascade export <graph-file> (--plan \| --out <dir>)` | The graph's published editor as a static site ([EDITOR_EXPORT.md](EDITOR_EXPORT.md)). |
53
+ | `cascade share <graph-file \| site-directory> [--name <slug>] [--scope <team>] [--protected]` | One link for the team: export the editor (or take a static directory with an `index.html`), deploy it to the Vercel project `cascade-<slug>` (team `field-io`), print the production URL (`data.url` with `--json`) and record it in the project's `shares.json`. Public unless `--protected` ([EDITOR_EXPORT.md](EDITOR_EXPORT.md#share-as-a-link)). |
54
+ | `cascade publish <graph-file> <node>.<prop>` | Publish a node prop as an editor parameter. |
55
+ | `cascade freeze <graph-file> <node> --fields <a,b>` | Snapshot chosen fields of a live data source for export. |
56
+ | `cascade scene <graph-file> <scene.json> --out <new-graph-file>` | Apply a scene file to a copy of the graph. |
57
+ | `cascade mcp [project-directory]` | Serve these commands as MCP tools over stdio ([MCP.md](MCP.md)). |
58
+ | `cascade lock <graph-file> <node> --name <name>` | Record a render (graph, params, seeds, inputs, code) in `locks/<id>.json`. |
59
+ | `cascade lock verify [<lock-id>...]` | Replay locks and report which still match. |
60
+ | `cascade replay <lock-id>` | Re-render a lock from its stored code and compare fingerprints. |
61
+ | `cascade locks` | List the project's locks. |
62
+ | `cascade ladder <graph-file> <node> --name <name> --vary <node.prop>=<a,b,c> \| --seeds <a,b,c> [--cairn <vault dir> [--vault <dir>]]` | Cook 3 to 5 variants (values zipped across params, or graph seeds) from the stored code; write `ladders/<id>/<n>.png`, a labelled contact sheet `ladders/<id>/sheet.png` and the record `ladders/<id>.json` (per variant: graph, params, seed, inputs, fingerprint; the code and git commit once). `--cairn` copies each variant to `<dir>/<id>-<nn>.png` and prints a `cairn:choice` card (`--json`: `data.cairnChoice`) to post in the Cairn chat; see [Picking on the phone](#picking-a-ladder-variant-in-cairn). |
63
+ | `cascade pick <ladder-id> <n> [--name <lock name>]` | At approval, write `locks/<id>.json` for variant `n` from the ladder's record, without cooking again; `cascade replay` reproduces that image pixel for pixel. |
64
+ | `cascade help node [<type>] [--project <dir>]` | A node type's inputs, props and outputs (type, default, range, description), where it runs and its capabilities, read from the installed package or the project's `nodes/`; without a type, every node type. No repository checkout needed. |
65
+ | `cascade doctor [project-directory] [--fix]` | What a headless run here will find: the declared Python environment, the stage interpreter, ffmpeg, the canvas. `--fix` builds the environment. See [Python for stages](#python-for-stages-cascadejson-python). |
66
+
67
+ Every verb is one row in `src/cli/verbs.ts`: its usage and summary for `--help`, the handler that runs it, how it treats `--json`, and the MCP tools that call it. A new verb is a row there, its handler, and its line here (`tests/cli-verbs.test.ts` checks both this page and [MCP.md](MCP.md)).
68
+
34
69
  ## `--json`, on every command
35
70
 
36
71
  Every command, not only the editing ones, prints one envelope on stdout and nothing else:
@@ -98,3 +133,57 @@ cascade check index.cascade && cascade run index.cascade --frames 1 --json
98
133
  ```
99
134
 
100
135
  Opened in Studio, the top level reads `motif → transform → render`, with the camera beside `transform`, and `motif` holds the circle and rectangle side by side above `copy-to-points` and its output.
136
+
137
+ ## Outputs as files (`run --out`)
138
+
139
+ `cascade run <graph> --out <dir>` copies each image output to `<dir>/<node>.<ext>`, or `<dir>/<node>.<port>.<ext>` for a node with more than one image output. With `--frames` the same names carry the frame: `<node>[.<port>].<frame>.<ext>`. Which outputs: every image output nothing downstream consumes (a `cascade.core.Null` passing an image counts), or those of `--node`; `--out-node <id>` (repeatable) adds a mid-graph node's. `--out` may be project-relative or absolute; `files` in the result is spelled the same way. `run --json` lists every image output as `data.images: [{ node, port, source, file? }]`, with or without `--out`. The MCP `cook` and `render` tools take `out` and `outNodes`.
140
+
141
+ ## Picking a ladder variant in Cairn
142
+
143
+ Marcus picks on his phone, in the Cairn web chat, by tapping a picture; the tap locks it. The loop for an agent:
144
+
145
+ 1. `cascade ladder <graph> <node> --name <name> --seeds 1,2,3 --cairn <vault>/<space>/_attachments/ladders/<id> --json` (`<id>` is the name lower-cased with dashes). Each variant is copied to `<dir>/<id>-<nn>.png`, and `data.cairnChoice` is the card. At most 8 variants.
146
+ 2. Post `data.cairnChoice` in the reply exactly as returned:
147
+
148
+ ````text
149
+ ```cairn:choice
150
+ {"prompt":"Dial: which one? (ladder dial, index.cascade render)","options":[{"label":"seed 1","value":"pick:dial:1","image":"Work/Projects/Cascade/_attachments/ladders/dial/dial-01.png"}, …]}
151
+ ```
152
+ ````
153
+
154
+ `label` says what the variant changed (`seed 1`, `hue=0.3`); `image` is relative to the vault, the nearest `.obsidian` (then `.git`) above `--cairn`, or `--vault`.
155
+ 3. A tap comes back as `Picked: seed 1 (pick:dial:1)`. Run `cascade pick dial 1 --project <project>` (MCP: `pick { "ladder": "dial", "variant": 1 }`).
156
+ 4. Reply with the locked image, `![[<that option's image>]]`, and the lock id.
157
+
158
+ ## Checks that fail a run (`cascade.assert.*`)
159
+
160
+ Put the check in the graph instead of a script beside it:
161
+
162
+ ```sh
163
+ cascade node add index.cascade NotBlank --name poster-not-blank --set 'message="the poster rendered empty"'
164
+ cascade connect index.cascade render.image poster-not-blank.image
165
+ cascade node add index.cascade cascade.assert.Range --name density-ok --set min=0.2 --set max=0.8
166
+ cascade connect index.cascade stats.density density-ok.value
167
+ cascade run index.cascade --json # exit 1: { "ok": false, "error": { "message": "Assertion failed in \"density-ok\" (cascade.assert.Range): value 0.93 is outside [0.2, 0.8]." } }
168
+ ```
169
+
170
+ `Range` (a finite number in `[min, max]`), `Equals` (JSON-equal to `expected`, numbers within `tolerance`), `True`, and `NotBlank` (a PNG or PPM that is not one flat colour within `tolerance`, nor fully transparent). Each passes its input through and runs when the graph runs whole, or when the `--node` you name is downstream of it. An image wired only into assertions is still saved by `--out`. Headless only for now; see `doc/NODE_REFERENCE.md`.
171
+
172
+ ## Debug output and environment (`.cascade/runs/`, `--env`)
173
+
174
+ Every run's trace in `.cascade/runs/<time>-<graph>.json` holds, per node, what it printed while it cooked: `console.log/info/warn/error/debug` from the node, and its Python stages' stderr, as `logs: [{ level, text }]` (500 lines per node, the rest counted in `logsDropped`). `run --json` gives the same lines as `data.logs: [{ node, level, text }]`. A failed run writes its trace too, and the error ends with `Run trace: <path>`. Each run (one per frame with `--frames`) carries `ms`, the cook; `frameMs`, the whole frame to its outputs on disk; and `saveMs`, the difference (collecting and writing the outputs), beside each node's own `ms`.
175
+
176
+ `--env NAME=value` (repeatable) sets a variable for the run: Python stages, the warm stage worker and `cascade render`'s worker processes see it, so `CLOUD_ANALYST_ONLINE=1` no longer needs a shell prefix. The trace records the names under `env`, never the values. The MCP `cook` and `render` tools take `env: { NAME: "value" }`.
177
+
178
+ ## Python for stages (`cascade.json` `python`)
179
+
180
+ A project whose stages import numpy or Pillow declares them, and Cascade keeps a virtual environment for it in `.cascade-cache/python/venv` (inside the project, never system-wide):
181
+
182
+ ```json
183
+ {
184
+ "exec": { "stages": { "entrypoint": "runtime/node_cli.py" } },
185
+ "python": { "requirements": ["numpy", "Pillow>=10"] }
186
+ }
187
+ ```
188
+
189
+ `requirements` is a list of pip specifiers or a project-relative requirements file; `interpreter` (default `python3`) is what creates the venv. `cascade doctor` reports it as `current`, `missing` or `stale` (cascade.json changed since it was built); `cascade doctor --fix` creates or rebuilds it, moving an old one to `.cascade-cache/python/_archive/`. With none declared, doctor fails when a stage source imports numpy and the stage interpreter cannot (`stage imports`), which is how a project on the system Python renders nothing. While one is declared, stages run under its interpreter instead of a bare `python`/`python3` in `exec.stages.python` (a project-relative executable there still wins), and a stage call with the environment missing or stale fails with the command that fixes it, rather than later on an `ImportError`. MCP: `doctor { "fix": true }`.
@@ -92,7 +92,34 @@ prints the same manifest without building anything.
92
92
  directory, so no `.env.local` can land in the site. It deploys to
93
93
  production, then checks that an anonymous request to
94
94
  `https://<project>.vercel.app` is refused. If it is not, it says to turn on
95
- Vercel Authentication.
95
+ Vercel Authentication. Add `--public` when the editor is meant to be open to
96
+ anyone: a readable site is then reported as intended instead of failing.
97
+
98
+ ## Share as a link
99
+
100
+ `cascade share` is the one step from a graph to a URL for the team:
101
+
102
+ ```sh
103
+ cascade share plotter-social.cascade # https://cascade-plotter-social.vercel.app
104
+ cascade share renders/shelf --name commons-shelf # any static directory with an index.html
105
+ ```
106
+
107
+ It exports the editor into a scratch directory with the same allowlist and
108
+ leak scan as `cascade export` (a directory is copied without dotfiles or
109
+ `licensed/` and scanned the same way), deploys it to production in the Vercel
110
+ project `cascade-<slug>` (team `field-io`, `--scope` for another), and prints
111
+ the project's production URL. The slug is `--name`, else the editor id or the
112
+ directory name. `--json` gives `data.url`, `data.deployment` and `data.shares`.
113
+
114
+ Each share is recorded in the project's `shares.json` (the directory holding
115
+ the graph, or the nearest `cascade.json` above a shared directory), one entry
116
+ per source with its `name`, `project`, `url` and `sharedAt`. Sharing again
117
+ replaces the entry and redeploys to the same URL.
118
+
119
+ A share is public: that is what a link someone can open is. The export
120
+ boundary above is the gate, so review `cascade export --plan` first.
121
+ `--protected` instead requires Vercel Authentication on the project, as
122
+ `cascade export --deploy vercel` does without `--public`.
96
123
 
97
124
  ## The `editor` section
98
125
 
@@ -121,12 +148,18 @@ Studio keeps it verbatim when it saves. The types are in
121
148
  { "id": "soft", "label": "Soft", "values": { "strength": 1 }, "keep": ["colour", "font"] }
122
149
  ],
123
150
  "assets": ["assets/fonts/InterDisplay-Medium.otf"],
151
+ "fonts": [{ "family": "DM Mono", "file": "assets/fonts/DMMono-Regular.woff2", "license": "OFL-1.1", "weight": 400 }],
124
152
  "freeze": [{ "node": "moment", "output": "moment", "snapshot": "frozen/moment.json" }],
125
153
  "panel": { "module": "editor/panel.ts", "placement": "extend" },
126
154
  "inspect": true
127
155
  }
128
156
  ```
129
157
 
158
+ `fonts` ships open-licensed fonts with the site and loads them in the page
159
+ (`@font-face`) and in the engine worker (`FontFace`), so a panel or a canvas
160
+ that names the family draws with it. Each entry must say `"license": "OFL-1.1"`;
161
+ anything else, or a file under `licensed/`, is refused.
162
+
130
163
  - **Parameter `id`** is the key a scene uses. It survives renaming the node
131
164
  or the prop, so keep it stable.
132
165
  - **`control`** is one of `number`, `slider`, `int`, `bool`, `select`
@@ -216,7 +249,9 @@ The same scene gives byte-identical output in the editor and under `cascade run`
216
249
 
217
250
  - Only browser-runnable definition-v1 nodes ship. Python, shell and dynamic
218
251
  nodes are listed together as "not exportable". Port them to TypeScript, or
219
- freeze their output.
252
+ freeze their output. A node that draws ships when it uses `cascade/image`
253
+ (PROJECT_AUTHORING.md, "Draw an image in any host"); fonts and images it
254
+ reads must be shipped files (`editor.assets`, `editor.fonts`, frozen files).
220
255
  - The editor cooks in a module worker. Unchanged stages are reused
221
256
  (`run({ reuse: true })`), so only the stages after an edit re-cook, and the
222
257
  page stays responsive while they do. Values cross to the page by structured
package/doc/MCP.md CHANGED
@@ -36,9 +36,15 @@ With a global install (`npm i -g @field/cascade`), use `"command": "cascade", "a
36
36
  | `cook` | `cascade run <graph> [--node <id>]` |
37
37
  | `render` | `cascade run <graph> --frames … [--fps] [--out] [--node]… [--scene]` |
38
38
  | `export` | `cascade export <graph> --plan \| --out <dir>` |
39
+ | `share` | `cascade share <graph \| directory> [--name] [--scope] [--protected]` (deploys; `data.url` is the link) |
39
40
  | `lock`, `replay`, `locks` | `cascade lock <graph> <node> --name`, `cascade replay <id> [--live]`, `cascade locks` |
41
+ | `ladder`, `pick` | `cascade ladder <graph> <node> --name --vary node.prop=a,b,c \| --seeds a,b,c` (`vary` is `{ "node.prop": [a, b, c] }`; `cairn`, an absolute vault directory, returns `data.cairnChoice`, a tap-to-pick card for the Cairn chat: see [Picking in Cairn](CLI_REFERENCE.md#picking-a-ladder-variant-in-cairn)), `cascade pick <ladder> <n>` (on "Picked: … (pick:<id>:<n>)", `ladder` <id>, `variant` <n>) |
42
+ | `node_help` | `cascade help node [<type>]` (a node type's declaration; every type without one) |
43
+ | `doctor` | `cascade doctor [--fix]` (Python environment, stage interpreter, ffmpeg, canvas; `fix` builds the environment) |
40
44
 
41
- The editing tools take `dryRun`. Reads and edits run inside the server through the same functions the CLI commands call. Cooking, rendering, checking and exporting run `cascade <command> --json` as a child process, so a long render leaves nothing behind in the server and returns exactly what the CLI would print. Calls run one at a time, so two edits to one file never interleave.
45
+ The editing tools take `dryRun`. Reads and edits run inside the server through the same functions the CLI commands call. Cooking, rendering, checking, exporting and the lock tools run `cascade <command> --json` as one child process each, so a long render leaves nothing behind in the server and returns exactly what the CLI would print. Calls run one at a time, so two edits to one file never interleave.
46
+
47
+ Each tool is an `mcp` entry on its command's row in `src/cli/verbs.ts`, with its description and input schema, so a tool cannot name a command this build does not have (`tests/cli-verbs.test.ts` checks every tool against the table and this page).
42
48
 
43
49
  ## A session
44
50
 
@@ -51,3 +57,14 @@ render { "graph": "index.cascade", "frames": "1-48", "out": "renders/check
51
57
  ```
52
58
 
53
59
  See `doc/CLI_REFERENCE.md` for what each operation guarantees about diffs, placement and subnet boundaries.
60
+
61
+ ## Another project, per call
62
+
63
+ Every tool takes an optional `project`: the project directory for that call, absolute or relative to the directory the server was started in. Without it nothing changes. With it, the call's paths (`graph`, `out`, `scene`, `directory`) resolve against that project and may not leave it, and `cook`, `render`, `check` and the others run their CLI child there. So one server started in `commons` can edit and render a sketch next to it:
64
+
65
+ ```text
66
+ graph_read { "project": "/Users/me/Documents/Cascade/cloud-volumes", "graph": "index.cascade" }
67
+ cook { "project": "../cloud-volumes", "graph": "index.cascade", "out": "renders/check" }
68
+ ```
69
+
70
+ A `project` that is not a directory is refused.
@@ -589,6 +589,321 @@ Runs on `portable`.
589
589
  | --- | --- | --- | --- |
590
590
  | `geometry` | `geometry` | | |
591
591
 
592
+ ### `cascade.image.Blur`
593
+
594
+ Blur — Box (three passes, fast) or true Gaussian blur.
595
+
596
+ Runs on `portable`.
597
+
598
+ **Inputs**
599
+
600
+ | name | type | default | range |
601
+ | --- | --- | --- | --- |
602
+ | `image` | `image` | | |
603
+
604
+ **Props**
605
+
606
+ | name | type | default | range |
607
+ | --- | --- | --- | --- |
608
+ | `method` | `string` | `"box"` | |
609
+ | `radius` | `float` | `5` | 0…100 |
610
+ | `wrapEdges` | `bool` | `false` | |
611
+
612
+ **Outputs**
613
+
614
+ | name | type | default | range |
615
+ | --- | --- | --- | --- |
616
+ | `image` | `image` | | |
617
+
618
+ ### `cascade.image.Checkers`
619
+
620
+ Checkers — A checkerboard.
621
+
622
+ Runs on `portable`.
623
+
624
+ **Props**
625
+
626
+ | name | type | default | range |
627
+ | --- | --- | --- | --- |
628
+ | `color1` | `color` | `[1,1,1,1]` | |
629
+ | `color2` | `color` | `[0,0,0,1]` | |
630
+ | `mode` | `string` | `"size"` | |
631
+ | `size` | `float` | `32` | 1…512 |
632
+ | `divisions` | `int` | `16` | 1…512 |
633
+ | `centered` | `bool` | `false` | |
634
+ | `resolution` | `vec2i` | `[512,512]` | 1…4096 |
635
+
636
+ **Outputs**
637
+
638
+ | name | type | default | range |
639
+ | --- | --- | --- | --- |
640
+ | `image` | `image` | | |
641
+
642
+ ### `cascade.image.Color`
643
+
644
+ Color — A solid colour.
645
+
646
+ Runs on `portable`.
647
+
648
+ **Props**
649
+
650
+ | name | type | default | range |
651
+ | --- | --- | --- | --- |
652
+ | `color` | `color` | `[1,1,1,1]` | |
653
+ | `resolution` | `vec2i` | `[512,512]` | 1…4096 |
654
+
655
+ **Outputs**
656
+
657
+ | name | type | default | range |
658
+ | --- | --- | --- | --- |
659
+ | `image` | `image` | | |
660
+
661
+ ### `cascade.image.Composite`
662
+
663
+ Composite — Blend image2 over image1.
664
+
665
+ Runs on `portable`.
666
+
667
+ **Inputs**
668
+
669
+ | name | type | default | range |
670
+ | --- | --- | --- | --- |
671
+ | `image1` | `image` | | |
672
+ | `image2` | `image` | | |
673
+
674
+ **Props**
675
+
676
+ | name | type | default | range |
677
+ | --- | --- | --- | --- |
678
+ | `blendMode` | `string` | `"multiply"` | |
679
+ | `opacity` | `float` | `1` | 0…1 |
680
+ | `outputResolution` | `string` | `"input1"` | |
681
+ | `customResolution` | `vec2i` | `[512,512]` | 1…4096 |
682
+ | `fitMode` | `string` | `"fill"` | |
683
+
684
+ **Outputs**
685
+
686
+ | name | type | default | range |
687
+ | --- | --- | --- | --- |
688
+ | `image` | `image` | | |
689
+
690
+ ### `cascade.image.ContactSheet`
691
+
692
+ Contact Sheet — Lay images out as one labelled grid.
693
+
694
+ Runs on `portable`.
695
+
696
+ **Inputs**
697
+
698
+ | name | type | default | range |
699
+ | --- | --- | --- | --- |
700
+ | `images` | `image` | | |
701
+ | `list` | `array` | | |
702
+
703
+ **Props**
704
+
705
+ | name | type | default | range |
706
+ | --- | --- | --- | --- |
707
+ | `columns` | `int` | `0` | 0…64 |
708
+ | `gap` | `int` | `16` | 0…512 |
709
+ | `background` | `color` | `[1,1,1,1]` | |
710
+ | `cellWidth` | `int` | `0` | 0…8192 |
711
+ | `labels` | `string` | `""` | |
712
+ | `labelSize` | `int` | `14` | 6…128 |
713
+ | `labelColor` | `color` | `[0.1,0.1,0.1,1]` | |
714
+ | `filename` | `string` | `"contact-sheet.png"` | |
715
+
716
+ **Outputs**
717
+
718
+ | name | type | default | range |
719
+ | --- | --- | --- | --- |
720
+ | `image` | `image` | | |
721
+ | `asset` | `asset` | | |
722
+
723
+ ### `cascade.image.Image`
724
+
725
+ File — An image file from the project, or an image input, optionally resized.
726
+
727
+ Runs on `portable`.
728
+
729
+ **Inputs**
730
+
731
+ | name | type | default | range |
732
+ | --- | --- | --- | --- |
733
+ | `image` | `image` | | |
734
+
735
+ **Props**
736
+
737
+ | name | type | default | range |
738
+ | --- | --- | --- | --- |
739
+ | `file` | `string` | `""` | |
740
+ | `resolutionMode` | `string` | `"original"` | |
741
+ | `maxResolution` | `vec2i` | `[2048,2048]` | 1…4096 |
742
+ | `fixedResolution` | `vec2i` | `[512,512]` | 1…4096 |
743
+
744
+ **Outputs**
745
+
746
+ | name | type | default | range |
747
+ | --- | --- | --- | --- |
748
+ | `image` | `image` | | |
749
+
750
+ ### `cascade.image.Noise`
751
+
752
+ Noise — Fractal simplex noise, grey.
753
+
754
+ Runs on `portable`.
755
+
756
+ **Props**
757
+
758
+ | name | type | default | range |
759
+ | --- | --- | --- | --- |
760
+ | `seed` | `int` | `0` | 0…10000 |
761
+ | `scale` | `float` | `0.01` | 0.001…1 |
762
+ | `iterations` | `int` | `4` | 1…8 |
763
+ | `resolution` | `vec2i` | `[512,512]` | 1…4096 |
764
+
765
+ **Outputs**
766
+
767
+ | name | type | default | range |
768
+ | --- | --- | --- | --- |
769
+ | `image` | `image` | | |
770
+
771
+ ### `cascade.image.NormalMap`
772
+
773
+ Normal Map — A normal map from the red (or grey) channel as height.
774
+
775
+ Runs on `portable`.
776
+
777
+ **Inputs**
778
+
779
+ | name | type | default | range |
780
+ | --- | --- | --- | --- |
781
+ | `image` | `image` | | |
782
+
783
+ **Props**
784
+
785
+ | name | type | default | range |
786
+ | --- | --- | --- | --- |
787
+ | `scale` | `float` | `1` | 0…10 |
788
+ | `flipX` | `bool` | `false` | |
789
+ | `flipY` | `bool` | `false` | |
790
+
791
+ **Outputs**
792
+
793
+ | name | type | default | range |
794
+ | --- | --- | --- | --- |
795
+ | `image` | `image` | | |
796
+
797
+ ### `cascade.image.Ramp`
798
+
799
+ Ramp — A colour ramp: horizontal, vertical, radial or concentric.
800
+
801
+ Runs on `portable`.
802
+
803
+ **Props**
804
+
805
+ | name | type | default | range |
806
+ | --- | --- | --- | --- |
807
+ | `type` | `string` | `"horizontal"` | |
808
+ | `points` | `array` | `[{"position":0,"color":{"r":0,"g":0,"b":0},"interpolation":"linear"},{"position":1,"color":{"r":1,"g":1,"b":1},"interpolation":"linear"}]` | |
809
+ | `resolution` | `vec2i` | `[512,512]` | 1…4096 |
810
+
811
+ **Outputs**
812
+
813
+ | name | type | default | range |
814
+ | --- | --- | --- | --- |
815
+ | `image` | `image` | | |
816
+
817
+ ### `cascade.image.Resize`
818
+
819
+ Resize — Scale an image, or resize it to a fixed size, bilinear.
820
+
821
+ Runs on `portable`.
822
+
823
+ **Inputs**
824
+
825
+ | name | type | default | range |
826
+ | --- | --- | --- | --- |
827
+ | `image` | `image` | | |
828
+
829
+ **Props**
830
+
831
+ | name | type | default | range |
832
+ | --- | --- | --- | --- |
833
+ | `mode` | `string` | `"scale"` | |
834
+ | `scale` | `float` | `1` | 0.1…10 |
835
+ | `width` | `int` | `512` | 1…4096 |
836
+ | `height` | `int` | `512` | 1…4096 |
837
+
838
+ **Outputs**
839
+
840
+ | name | type | default | range |
841
+ | --- | --- | --- | --- |
842
+ | `image` | `image` | | |
843
+
844
+ ### `cascade.image.Text`
845
+
846
+ Text — Text drawn with a CSS font family, or a font file from the project.
847
+
848
+ Runs on `portable`.
849
+
850
+ **Props**
851
+
852
+ | name | type | default | range |
853
+ | --- | --- | --- | --- |
854
+ | `text` | `string` | `"Hello, World!"` | |
855
+ | `font` | `string` | `"Inter, system-ui, sans-serif"` | |
856
+ | `fontSize` | `float` | `48` | 1…500 |
857
+ | `fontWeight` | `string` | `"normal"` | |
858
+ | `fontStyle` | `string` | `"normal"` | |
859
+ | `alignH` | `string` | `"center"` | |
860
+ | `alignV` | `string` | `"middle"` | |
861
+ | `color` | `color` | `[1,1,1,1]` | |
862
+ | `backgroundColor` | `color` | `[0,0,0,0]` | |
863
+ | `resolution` | `vec2i` | `[512,512]` | 1…4096 |
864
+ | `offset` | `vec2` | `[0,0]` | -2048…2048 |
865
+ | `rotation` | `float` | `0` | -180…180 |
866
+ | `letterSpacing` | `float` | `0` | -50…100 |
867
+ | `lineHeight` | `float` | `1.2` | 0.5…3 |
868
+ | `opacity` | `float` | `1` | 0…1 |
869
+
870
+ **Outputs**
871
+
872
+ | name | type | default | range |
873
+ | --- | --- | --- | --- |
874
+ | `image` | `image` | | |
875
+
876
+ ### `cascade.image.Transform`
877
+
878
+ Transform — Rotate, translate and scale an image about a pivot.
879
+
880
+ Runs on `portable`.
881
+
882
+ **Inputs**
883
+
884
+ | name | type | default | range |
885
+ | --- | --- | --- | --- |
886
+ | `image` | `image` | | |
887
+
888
+ **Props**
889
+
890
+ | name | type | default | range |
891
+ | --- | --- | --- | --- |
892
+ | `translate` | `vec2` | `[0,0]` | -2048…2048 |
893
+ | `rotation` | `float` | `0` | -180…180 |
894
+ | `scale` | `vec2` | `[1,1]` | 0.01…10 |
895
+ | `uniformScale` | `bool` | `true` | |
896
+ | `pivot` | `string` | `"center"` | |
897
+ | `customPivot` | `vec2` | `[0.5,0.5]` | 0…1 |
898
+ | `filter` | `string` | `"bilinear"` | |
899
+ | `wrap` | `string` | `"clamp"` | |
900
+
901
+ **Outputs**
902
+
903
+ | name | type | default | range |
904
+ | --- | --- | --- | --- |
905
+ | `image` | `image` | | |
906
+
592
907
  ### `cascade.math.Binary`
593
908
 
594
909
  Binary Math — Two values, one operation. Component-wise, and a scalar broadcasts against a vector.
@@ -973,6 +1288,115 @@ Runs on `portable`.
973
1288
  | --- | --- | --- | --- |
974
1289
  | `scene` | `scene` | | |
975
1290
 
1291
+ ## Assertions
1292
+
1293
+ `cascade.assert.*` fail a `cascade run` (non-zero exit, the message in
1294
+ `run --json`'s `error.message`) when their check does not hold, and pass
1295
+ their input through. In Studio a failed assertion is the node's error. An image
1296
+ wired only into assertions still counts as the graph's final image for
1297
+ `run --out`.
1298
+
1299
+ ### `cascade.assert.Equals`
1300
+
1301
+ Assert Equals — Fails the run unless value equals expected (JSON-equal; numbers within tolerance); passes value through
1302
+
1303
+ Runs on `portable`.
1304
+
1305
+ **Inputs**
1306
+
1307
+ | name | type | default | range |
1308
+ | --- | --- | --- | --- |
1309
+ | `value` | `any` | `null` | |
1310
+
1311
+ **Props**
1312
+
1313
+ | name | type | default | range |
1314
+ | --- | --- | --- | --- |
1315
+ | `expected` | `any` | `null` | |
1316
+ | `tolerance` | `float` | `0` | |
1317
+ | `message` | `string` | `""` | |
1318
+
1319
+ **Outputs**
1320
+
1321
+ | name | type | default | range |
1322
+ | --- | --- | --- | --- |
1323
+ | `value` | `any` | | |
1324
+
1325
+ ### `cascade.assert.NotBlank`
1326
+
1327
+ Assert Not Blank — Fails the run when an image (PNG or PPM) is one flat colour or fully transparent; passes the image through
1328
+
1329
+ Runs on `portable`.
1330
+
1331
+ **Inputs**
1332
+
1333
+ | name | type | default | range |
1334
+ | --- | --- | --- | --- |
1335
+ | `image` | `image` | | |
1336
+
1337
+ **Props**
1338
+
1339
+ | name | type | default | range |
1340
+ | --- | --- | --- | --- |
1341
+ | `tolerance` | `int` | `0` | 0…255 |
1342
+ | `message` | `string` | `""` | |
1343
+
1344
+ **Outputs**
1345
+
1346
+ | name | type | default | range |
1347
+ | --- | --- | --- | --- |
1348
+ | `image` | `image` | | |
1349
+
1350
+ ### `cascade.assert.Range`
1351
+
1352
+ Assert Range — Fails the run unless value is a finite number in [min, max]; passes value through
1353
+
1354
+ Runs on `portable`.
1355
+
1356
+ **Inputs**
1357
+
1358
+ | name | type | default | range |
1359
+ | --- | --- | --- | --- |
1360
+ | `value` | `float` | `0` | |
1361
+
1362
+ **Props**
1363
+
1364
+ | name | type | default | range |
1365
+ | --- | --- | --- | --- |
1366
+ | `min` | `float` | `0` | |
1367
+ | `max` | `float` | `1` | |
1368
+ | `message` | `string` | `""` | |
1369
+
1370
+ **Outputs**
1371
+
1372
+ | name | type | default | range |
1373
+ | --- | --- | --- | --- |
1374
+ | `value` | `float` | | |
1375
+
1376
+ ### `cascade.assert.True`
1377
+
1378
+ Assert True — Fails the run unless value is true (wire a condition, or give the prop an expression); passes value through
1379
+
1380
+ Runs on `portable`.
1381
+
1382
+ **Inputs**
1383
+
1384
+ | name | type | default | range |
1385
+ | --- | --- | --- | --- |
1386
+ | `value` | `bool` | `false` | |
1387
+
1388
+ **Props**
1389
+
1390
+ | name | type | default | range |
1391
+ | --- | --- | --- | --- |
1392
+ | `message` | `string` | `""` | |
1393
+
1394
+ **Outputs**
1395
+
1396
+ | name | type | default | range |
1397
+ | --- | --- | --- | --- |
1398
+ | `value` | `bool` | | |
1399
+
976
1400
  ## Studio node libraries
977
1401
 
978
1402
  Class-based nodes, available in Studio. Their parameters are declared in the
@@ -991,16 +1415,6 @@ under `src/nodes/<library>/nodes/`.
991
1415
  ### Geometry — `cascade.geo.*`
992
1416
 
993
1417
  **Geometry.** `cascade.geo.Rectangle` (A closed four-point polygon, counter-clockwise from bottom-left.), `cascade.geo.Circle` (A closed circle, as one cubic Bezier chain or as a polygon of divisions segments.), `cascade.geo.Transform` (Translate, rotate and scale geometry about a pivot.), `cascade.geo.Merge` (Concatenate geometries, taking the union of their attributes.), `cascade.geo.CopyToPoints` (Instance one geometry onto every point of another.), `cascade.geo.SvgExport` (Write geometry as an SVG document, one group element per group.), `cascade.geo.Render` (Draw a scene through a camera, as a raster.), `cascade.geo.FromRects` (Convert rects into geometry, one closed polygon each, keeping the tag.)
994
-
995
- ### Image — `cascade.image.*`
996
-
997
- **Create.** `Image` (Load images from files), `Color` (Create a solid color canvas), `Checkers` (Generate a checkerboard pattern), `Noise` (Generate procedural noise patterns), `Ramp` (Generate color ramps), `Text` (Render text with customizable font and style)
998
-
999
- **Filter.** `Blur` (Gaussian blur), `NormalMap` (Compute normal map from height map)
1000
-
1001
- **Transform.** `Transform` (2D rotation, translation, and scaling), `Resize` (Scale or resize an image)
1002
-
1003
- **Composite.** `Composite` (Blend two images together)
1004
1418
  ## A project's own nodes
1005
1419
 
1006
1420
  Not listed here, because they belong to the project rather than to Cascade. Ask