@field/cascade 0.11.0 → 0.13.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 (220) hide show
  1. package/AGENTS.md +8 -5
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +245 -0
  4. package/README.md +1 -1
  5. package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
  6. package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
  7. package/dist/assets/{CodeEditor-4O7FQ__R.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-Bz8JxISH.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-D5Z0nJ5-.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-Yqi0Ni61.js → core-DB-MkGjj.js} +3 -4
  28. package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
  29. package/dist/assets/{cssMode-CxUzc4vp.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-BNFb25CX.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-0ZDnWxeM.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-Cy18MoaD.js → handlebars-CcD3aMeL.js} +1 -1
  41. package/dist/assets/{html-B-2Dx7nq.js → html-DvZWGfqb.js} +1 -1
  42. package/dist/assets/{htmlMode-DUAc9B_d.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-DeQsDqP5.js → javascript-Cap2ahgh.js} +1 -1
  49. package/dist/assets/{jsonMode-BiFwJVSk.js → jsonMode-B0uLjp9C.js} +1 -1
  50. package/dist/assets/{liquid-BLoHH68a.js → liquid-CnxwhfLn.js} +1 -1
  51. package/dist/assets/{lspLanguageFeatures-CNFgm1Kv.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
  52. package/dist/assets/math-GeULS-sk.js +1 -0
  53. package/dist/assets/{mdx-CFXQVNfs.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-Cf7S7p2E.js → python-CGyFk4XK.js} +1 -1
  57. package/dist/assets/{razor-CMHBcG-g.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-BEih0GDj.js → tsMode-BOeXzO9H.js} +1 -1
  64. package/dist/assets/{typescript-COEKYD_a.js → typescript-J1SnE_Tu.js} +1 -1
  65. package/dist/assets/{workers-BNhAhMNn.js → workers-CYwwTiVF.js} +1 -1
  66. package/dist/assets/{xml-CPiMpnPl.js → xml-DnPRNzK9.js} +1 -1
  67. package/dist/assets/{yaml-Bgp1ITLR.js → yaml-BmmYBNa9.js} +1 -1
  68. package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
  69. package/dist/cli/{Graph-ZJJTRQTM.js → Graph-OB6EWSGL.js} +12 -12
  70. package/dist/cli/assert-GROGQNCQ.js +25 -0
  71. package/dist/cli/checks-3WB44XBL.js +189 -0
  72. package/dist/cli/{chunk-3JTJHF7V.js → chunk-2L34ERS2.js} +4 -4
  73. package/dist/cli/chunk-53LGFPWX.js +138 -0
  74. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  75. package/dist/cli/{chunk-YAT5Y25U.js → chunk-7QE2ZNSN.js} +3 -3
  76. package/dist/cli/{chunk-SGJAET2L.js → chunk-BPODRH52.js} +8 -4
  77. package/dist/cli/{chunk-XWDSKG7N.js → chunk-BZ63XQ3I.js} +2 -2
  78. package/dist/cli/{chunk-FCDXJ6EK.js → chunk-CTPLRSWF.js} +2 -13
  79. package/dist/cli/{chunk-WIH4TEIS.js → chunk-EZRWJ4G2.js} +1 -1
  80. package/dist/cli/{chunk-BIJO5OFS.js → chunk-FWMSGKEH.js} +1 -1
  81. package/dist/cli/{chunk-POMMNKZZ.js → chunk-G23A2BZZ.js} +38 -5
  82. package/dist/cli/{chunk-35D343DC.js → chunk-GRIN2DDP.js} +19 -1
  83. package/dist/cli/{chunk-WKURDVYP.js → chunk-JEW5MPGI.js} +28 -331
  84. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  85. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  86. package/dist/cli/{chunk-GFS6W3C5.js → chunk-L6B6FUQP.js} +54 -32
  87. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  88. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  89. package/dist/cli/{chunk-A4OAMOZH.js → chunk-NJFMDXHR.js} +13 -3
  90. package/dist/cli/{chunk-DUIUFVU2.js → chunk-OBZAP3GG.js} +49 -23
  91. package/dist/cli/chunk-QAGATJGE.js +336 -0
  92. package/dist/cli/{chunk-EUIAC3U3.js → chunk-QKL7CCRS.js} +966 -132
  93. package/dist/cli/{chunk-RLCAJBVR.js → chunk-TJW67QSI.js} +8 -7
  94. package/dist/cli/{chunk-RQZ4SX5R.js → chunk-W5PJ5RMP.js} +156 -84
  95. package/dist/cli/chunk-W7OUAT7J.js +122 -0
  96. package/dist/cli/chunk-WGKPJZDY.js +364 -0
  97. package/dist/cli/{chunk-D6BUOHUG.js → chunk-X4WKAOQP.js} +4 -4
  98. package/dist/cli/{chunk-VVGSLVSF.js → chunk-X566FWBV.js} +6 -1
  99. package/dist/cli/chunk-YH637PDL.js +317 -0
  100. package/dist/cli/chunk-ZSCUZE6I.js +112 -0
  101. package/dist/cli/{cliCommands-UQSFGEHF.js → cliCommands-XVFRD7CF.js} +23 -17
  102. package/dist/cli/{commands-Q23M26QM.js → commands-M2XLTPTZ.js} +129 -47
  103. package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
  104. package/dist/cli/{definitions-HF2FTHYU.js → definitions-BPOPW52L.js} +7 -7
  105. package/dist/cli/{doctorCommand-B7BPQMN6.js → doctorCommand-RAEI7GVY.js} +36 -6
  106. package/dist/cli/{edit-JHCPTQFT.js → edit-S5KEQ5RA.js} +3 -3
  107. package/dist/cli/editorCommands-5BBG4S6V.js +41 -0
  108. package/dist/cli/{encode-HAUZTYPT.js → encode-ICPEPMH5.js} +2 -2
  109. package/dist/cli/file-3HTQHPYQ.js +41 -0
  110. package/dist/cli/filters-6ODFWYK4.js +379 -0
  111. package/dist/cli/generators-YU7GB4MD.js +210 -0
  112. package/dist/cli/{geo-4RNK3QVN.js → geo-6ZPGKZRU.js} +7 -7
  113. package/dist/cli/{graphCommands-6BMAKGQE.js → graphCommands-MULQ7VD2.js} +4 -4
  114. package/dist/cli/{graphFile-VPSQ6OF7.js → graphFile-CITF36ZM.js} +25 -19
  115. package/dist/cli/{headlessCanvas-5XLDZT7M.js → headlessCanvas-ENRHC7GS.js} +3 -2
  116. package/dist/cli/{helpCommand-3VRE3KVO.js → helpCommand-X2QDANM3.js} +9 -7
  117. package/dist/cli/image-TXELZZK5.js +55 -0
  118. package/dist/cli/index.js +14 -14
  119. package/dist/cli/{math-ZRMLKU2D.js → math-RSYRQ3C6.js} +5 -5
  120. package/dist/cli/{mcp-ZLEEMYGF.js → mcp-YRIGRLYQ.js} +4 -4
  121. package/dist/cli/{parity-KINARCP2.js → parity-3N3HTKWX.js} +42 -36
  122. package/dist/cli/{pop-USDFXB7H.js → pop-B7SIIYKN.js} +5 -5
  123. package/dist/cli/project-SJF5ZF3F.js +12 -0
  124. package/dist/cli/{projectDefinition-DMTZN2NE.js → projectDefinition-F3D7UIHJ.js} +8 -8
  125. package/dist/cli/{projectRuntime-Z5PJOH47.js → projectRuntime-NY4XMDFH.js} +19 -12
  126. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  127. package/dist/cli/{render-YURAPPUI.js → render-Y23W6MY6.js} +29 -22
  128. package/dist/cli/{runCommand-PI764LEG.js → runCommand-XOG7AVC5.js} +3 -3
  129. package/dist/cli/runner-DNJO5WO2.js +43 -0
  130. package/dist/cli/runtime/image.ts +27 -2
  131. package/dist/cli/runtime/io.ts +78 -0
  132. package/dist/cli/share-B7HTCOJX.js +166 -0
  133. package/dist/cli/stageCache-TY2BFZJR.js +15 -0
  134. package/dist/cli/{stageRunner-KUNIMQKJ.js → stageRunner-KY2EJP7X.js} +2 -2
  135. package/dist/cli/text-MB3EKR4H.js +126 -0
  136. package/dist/cli/{verbs-RCE3K4VQ.js → verbs-PI2JH5FI.js} +2 -2
  137. package/dist/cli/webBuild-2K4COCGY.js +44 -0
  138. package/dist/contracts/capabilities.d.ts +39 -1
  139. package/dist/contracts/color.d.ts +8 -0
  140. package/dist/contracts/color.js +23 -0
  141. package/dist/contracts/definition.d.ts +10 -0
  142. package/dist/contracts/schema.d.ts +1 -1
  143. package/dist/contracts/schema.js +1 -0
  144. package/dist/contracts/validate.js +11 -1
  145. package/dist/contracts/values.d.ts +24 -0
  146. package/dist/image.d.ts +15 -0
  147. package/dist/image.js +36 -2
  148. package/dist/index.html +8 -2
  149. package/dist/io.d.ts +29 -0
  150. package/dist/io.js +40 -0
  151. package/dist/player/app.js +11 -7
  152. package/dist/player/editor.js +3 -2
  153. package/dist/player/editorEngine.js +3 -2
  154. package/dist/player/embed.js +2 -0
  155. package/dist/player/types.d.ts +6 -0
  156. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  157. package/dist/runtime/builtins/assert/checks.js +207 -0
  158. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  159. package/dist/runtime/builtins/assert/definitions.js +54 -0
  160. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  161. package/dist/runtime/builtins/assert/index.js +36 -0
  162. package/dist/runtime/builtins/image/contactSheet.d.ts +3 -165
  163. package/dist/runtime/builtins/image/contactSheet.js +0 -49
  164. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  165. package/dist/runtime/builtins/image/definitions.js +343 -0
  166. package/dist/runtime/builtins/image/file.d.ts +8 -0
  167. package/dist/runtime/builtins/image/file.js +37 -0
  168. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  169. package/dist/runtime/builtins/image/filters.js +394 -0
  170. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  171. package/dist/runtime/builtins/image/generators.js +144 -0
  172. package/dist/runtime/builtins/image/index.d.ts +947 -4
  173. package/dist/runtime/builtins/image/index.js +41 -6
  174. package/dist/runtime/builtins/image/io.d.ts +100 -0
  175. package/dist/runtime/builtins/image/io.js +142 -0
  176. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  177. package/dist/runtime/builtins/image/planar.js +274 -0
  178. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  179. package/dist/runtime/builtins/image/simplex.js +92 -0
  180. package/dist/runtime/builtins/image/text.d.ts +4 -0
  181. package/dist/runtime/builtins/image/text.js +141 -0
  182. package/dist/runtime/builtins/index.d.ts +3 -0
  183. package/dist/runtime/builtins/index.js +7 -0
  184. package/dist/runtime/definition/architecture.js +89 -0
  185. package/dist/runtime/gpu.d.ts +25 -0
  186. package/dist/runtime/gpu.js +40 -0
  187. package/dist/runtime/runtime.d.ts +2 -0
  188. package/dist/runtime/runtime.js +268 -44
  189. package/dist/runtime/types.d.ts +30 -0
  190. package/doc/CLI_REFERENCE.md +21 -3
  191. package/doc/EDITOR_EXPORT.md +26 -0
  192. package/doc/MCP.md +2 -1
  193. package/doc/NODE_REFERENCE.md +286 -14
  194. package/doc/PROJECT_AUTHORING.md +158 -4
  195. package/doc/WEB_PLAYER.md +9 -1
  196. package/package.json +4 -1
  197. package/dist/assets/CodePanel-BU-autmq.js +0 -2
  198. package/dist/assets/CustomNodeDialog-BRpDfOV5.js +0 -1
  199. package/dist/assets/OpenGraphDialog-BwbRlC0X.js +0 -1
  200. package/dist/assets/ProjectSettings-DLOXLxcA.js +0 -2
  201. package/dist/assets/definitions-GreEMXLV.js +0 -1
  202. package/dist/assets/geo-u0n_jOsD.js +0 -1
  203. package/dist/assets/image-cAa-Nu9O.js +0 -2062
  204. package/dist/assets/index-5ZtDAuy2.js +0 -220
  205. package/dist/assets/index-7vae6YFC.css +0 -1
  206. package/dist/assets/math-DioIQOPJ.js +0 -1
  207. package/dist/assets/pop-DC_TQG9t.js +0 -1
  208. package/dist/cli/chunk-36P5K3SD.js +0 -631
  209. package/dist/cli/chunk-4UDFYF5G.js +0 -132
  210. package/dist/cli/chunk-53NMBJAO.js +0 -35
  211. package/dist/cli/chunk-MIIEFUGG.js +0 -623
  212. package/dist/cli/chunk-QBBKRNGY.js +0 -90
  213. package/dist/cli/chunk-WPUDGEFY.js +0 -20
  214. package/dist/cli/editorCommands-UIF5YLUZ.js +0 -33
  215. package/dist/cli/project-K3QIPGBB.js +0 -12
  216. package/dist/cli/registerStandardNodes-S2VLMF6J.js +0 -3081
  217. package/dist/cli/runner-RA6UKEPD.js +0 -37
  218. package/dist/cli/webBuild-GT4PZT5D.js +0 -37
  219. package/dist/runtime/builtins/headless.d.ts +0 -19
  220. package/dist/runtime/builtins/headless.js +0 -25
@@ -1,48 +1,50 @@
1
1
  import {
2
2
  runGraph
3
- } from "./chunk-DUIUFVU2.js";
4
- import "./chunk-4UDFYF5G.js";
5
- import "./chunk-QBBKRNGY.js";
3
+ } from "./chunk-OBZAP3GG.js";
6
4
  import "./chunk-2KXB47FP.js";
5
+ import "./chunk-EZRWJ4G2.js";
7
6
  import {
8
7
  parseFrameSpec
9
8
  } from "./chunk-BP7UKF36.js";
9
+ import "./chunk-RUU3YWXU.js";
10
10
  import {
11
11
  encodeSequence,
12
12
  frameSequences,
13
13
  hasFfmpeg,
14
14
  timelineFrameSpec
15
- } from "./chunk-YAT5Y25U.js";
15
+ } from "./chunk-7QE2ZNSN.js";
16
16
  import {
17
17
  parseEnvArgs
18
18
  } from "./chunk-ZL4JTG5Y.js";
19
- import "./chunk-RUU3YWXU.js";
20
- import "./chunk-MIIEFUGG.js";
21
- import "./chunk-WPUDGEFY.js";
22
- import "./chunk-VVGSLVSF.js";
23
- import "./chunk-WD62JIP6.js";
24
- import "./chunk-EUIAC3U3.js";
25
- import "./chunk-36P5K3SD.js";
26
- import "./chunk-D6BUOHUG.js";
19
+ import "./chunk-QKL7CCRS.js";
20
+ import "./chunk-MI5JLQYA.js";
21
+ import "./chunk-GRIN2DDP.js";
22
+ import "./chunk-X4WKAOQP.js";
23
+ import "./chunk-DEQSEFFH.js";
27
24
  import "./chunk-RXDIMXN5.js";
28
25
  import "./chunk-QQPA2ZHH.js";
29
26
  import "./chunk-KH4MUBKF.js";
30
- import "./chunk-35D343DC.js";
31
- import "./chunk-DEQSEFFH.js";
32
27
  import "./chunk-HSNOBIVT.js";
33
- import "./chunk-ICH7N5PN.js";
34
- import "./chunk-GFS6W3C5.js";
28
+ import "./chunk-L6B6FUQP.js";
35
29
  import "./chunk-HFHWYL55.js";
30
+ import "./chunk-KBGUO27Q.js";
31
+ import "./chunk-ICH7N5PN.js";
36
32
  import {
37
33
  DEFAULT_FPS,
38
34
  frameRange
39
35
  } from "./chunk-CO22XHNG.js";
40
- import "./chunk-RQZ4SX5R.js";
41
- import "./chunk-A4OAMOZH.js";
36
+ import "./chunk-LTXWBXOM.js";
37
+ import "./chunk-BZ63XQ3I.js";
38
+ import "./chunk-W7OUAT7J.js";
39
+ import "./chunk-YH637PDL.js";
40
+ import "./chunk-X566FWBV.js";
41
+ import "./chunk-WD62JIP6.js";
42
+ import "./chunk-5ORUQ6ES.js";
43
+ import "./chunk-W5PJ5RMP.js";
44
+ import "./chunk-NJFMDXHR.js";
42
45
  import "./chunk-J7VK7MNF.js";
43
- import "./chunk-XWDSKG7N.js";
44
- import "./chunk-BIJO5OFS.js";
45
- import "./chunk-WIH4TEIS.js";
46
+ import "./chunk-FWMSGKEH.js";
47
+ import "./chunk-JVOCK5W4.js";
46
48
 
47
49
  // src/cli/render.ts
48
50
  import { fork, spawn } from "node:child_process";
@@ -178,12 +180,17 @@ function parseRenderArgs(args) {
178
180
  function sharedOutputPaths(a, b) {
179
181
  const shared = [];
180
182
  for (const output of a.outputs) {
183
+ if (isContentNamed(output.source)) continue;
181
184
  if (b.outputs.some((other) => other.nodeId === output.nodeId && other.source === output.source)) {
182
185
  shared.push({ nodeId: output.nodeId, source: output.source });
183
186
  }
184
187
  }
185
188
  return shared;
186
189
  }
190
+ function isContentNamed(source) {
191
+ const clean = source.split("\\").join("/");
192
+ return /(?:^|\/)\.cascade-cache\//.test(clean) && /^[^.]*\.[0-9a-f]{12}(?:\.[^/]*)?$/.test(path.posix.basename(clean));
193
+ }
187
194
  function fixedPathMessage(shared, a, b) {
188
195
  const nodes = shared.map(({ nodeId, source }) => `"${nodeId}" (${source})`).join(", ");
189
196
  return `Cannot render in parallel: ${shared.length === 1 ? "node" : "nodes"} ${nodes} wrote frames ${a} and ${b} to the same file, so workers would overwrite each other's frames and the sequence would silently mix them up. Give the output a per-frame path: put the frame in the file name, or save through saveImage(canvas, cachePath(context.nodeId, ".png")) from cascade/io or the assets capability, which name files by their content. Or render with --workers 1.`;
@@ -433,4 +440,4 @@ export {
433
440
  runRenderWorker,
434
441
  sharedOutputPaths
435
442
  };
436
- //# sourceMappingURL=render-YURAPPUI.js.map
443
+ //# sourceMappingURL=render-Y23W6MY6.js.map
@@ -155,7 +155,7 @@ async function runCommand(args) {
155
155
  process.exitCode = await superviseProcess(process.execPath, [...process.execArgv, process.argv[1], ...childArgs], ms);
156
156
  return;
157
157
  }
158
- const { runGraph } = await import("./runner-RA6UKEPD.js");
158
+ const { runGraph } = await import("./runner-DNJO5WO2.js");
159
159
  try {
160
160
  if (options.json) console.log = console.error;
161
161
  await runGraph(options);
@@ -168,7 +168,7 @@ async function runCommand(args) {
168
168
  }
169
169
  }
170
170
  async function buildCommand(args) {
171
- const { buildPlayer } = await import("./webBuild-GT4PZT5D.js");
171
+ const { buildPlayer } = await import("./webBuild-2K4COCGY.js");
172
172
  if (!args[1] || args[1].startsWith("-")) throw new Error("cascade build requires a graph file");
173
173
  let out;
174
174
  const assets = [];
@@ -195,4 +195,4 @@ export {
195
195
  buildCommand,
196
196
  runCommand
197
197
  };
198
- //# sourceMappingURL=runCommand-PI764LEG.js.map
198
+ //# sourceMappingURL=runCommand-XOG7AVC5.js.map
@@ -0,0 +1,43 @@
1
+ import {
2
+ RENDER_SUGGESTION_FRAMES,
3
+ renderSuggestion,
4
+ runGraph
5
+ } from "./chunk-OBZAP3GG.js";
6
+ import "./chunk-2KXB47FP.js";
7
+ import "./chunk-EZRWJ4G2.js";
8
+ import "./chunk-BP7UKF36.js";
9
+ import "./chunk-RUU3YWXU.js";
10
+ import "./chunk-7QE2ZNSN.js";
11
+ import "./chunk-ZL4JTG5Y.js";
12
+ import "./chunk-QKL7CCRS.js";
13
+ import "./chunk-MI5JLQYA.js";
14
+ import "./chunk-GRIN2DDP.js";
15
+ import "./chunk-X4WKAOQP.js";
16
+ import "./chunk-DEQSEFFH.js";
17
+ import "./chunk-RXDIMXN5.js";
18
+ import "./chunk-QQPA2ZHH.js";
19
+ import "./chunk-KH4MUBKF.js";
20
+ import "./chunk-HSNOBIVT.js";
21
+ import "./chunk-L6B6FUQP.js";
22
+ import "./chunk-HFHWYL55.js";
23
+ import "./chunk-KBGUO27Q.js";
24
+ import "./chunk-ICH7N5PN.js";
25
+ import "./chunk-CO22XHNG.js";
26
+ import "./chunk-LTXWBXOM.js";
27
+ import "./chunk-BZ63XQ3I.js";
28
+ import "./chunk-W7OUAT7J.js";
29
+ import "./chunk-YH637PDL.js";
30
+ import "./chunk-X566FWBV.js";
31
+ import "./chunk-WD62JIP6.js";
32
+ import "./chunk-5ORUQ6ES.js";
33
+ import "./chunk-W5PJ5RMP.js";
34
+ import "./chunk-NJFMDXHR.js";
35
+ import "./chunk-J7VK7MNF.js";
36
+ import "./chunk-FWMSGKEH.js";
37
+ import "./chunk-JVOCK5W4.js";
38
+ export {
39
+ RENDER_SUGGESTION_FRAMES,
40
+ renderSuggestion,
41
+ runGraph
42
+ };
43
+ //# sourceMappingURL=runner-DNJO5WO2.js.map
@@ -44,6 +44,10 @@
44
44
  * copy are not always the same module instance.
45
45
  */
46
46
 
47
+ import { canvasPixels, pixelsBitmap, type ImagePixels } from './io.js';
48
+
49
+ export type { ImagePixels } from './io.js';
50
+
47
51
  /** An image port value. Mirrors Cascade's core `ImageRef`. */
48
52
  export interface ImageRef {
49
53
  path: string;
@@ -64,6 +68,14 @@ export interface ImageAssets {
64
68
  metadata: { mediaType?: string; suggestedName?: string },
65
69
  options: { signal: unknown },
66
70
  ): Promise<{ path: string; mediaType?: string }>;
71
+ /** Optional: store an intermediate from its pixels (see core `AssetCapability.keepImage`). */
72
+ keepImage?(
73
+ pixels: ImagePixels,
74
+ metadata: { suggestedName?: string },
75
+ options: { signal: unknown },
76
+ ): Promise<{ path: string; mediaType?: string }>;
77
+ /** Optional: the pixels behind a stored image while the host holds them in memory. */
78
+ imagePixels?(ref: { path: string }): ImagePixels | undefined;
67
79
  }
68
80
 
69
81
  /**
@@ -250,6 +262,9 @@ export async function readBytes(context: ImageNodeContext, ref: ImageSourceRef):
250
262
 
251
263
  /** Read and decode an image input, an asset, or a project file. */
252
264
  export async function readImage(context: ImageNodeContext, ref: ImageSourceRef): Promise<ImageBitmap> {
265
+ // An image the host still holds in memory skips the read and the decode.
266
+ const pixels = assetsOf(context).imagePixels?.(refOf(ref));
267
+ if (pixels) return pixelsBitmap(pixels);
253
268
  return decodeImage(await readBytes(context, ref));
254
269
  }
255
270
 
@@ -265,8 +280,18 @@ export async function writeImage(
265
280
  options: { type?: ImageType; quality?: number; space?: 'srgb' | 'linear' } = {},
266
281
  ): Promise<ImageRef> {
267
282
  const type = options.type ?? 'image/png';
268
- const bytes = await encodeImage(canvas, { type, ...(options.quality === undefined ? {} : { quality: options.quality }) });
269
- const stored = await assetsOf(context).write(bytes, { mediaType: type, suggestedName: name }, { signal: context.signal ?? NEVER });
283
+ const assets = assetsOf(context);
284
+ const signal = context.signal ?? NEVER;
285
+ // A PNG intermediate goes to the host as pixels when it can take them: a
286
+ // fast lossless write, or no encode at all until something needs the file.
287
+ const pixels = type === 'image/png' && assets.keepImage ? canvasPixels(canvas) : null;
288
+ const stored = pixels && assets.keepImage
289
+ ? await assets.keepImage(pixels, { suggestedName: name }, { signal })
290
+ : await assets.write(
291
+ await encodeImage(canvas, { type, ...(options.quality === undefined ? {} : { quality: options.quality }) }),
292
+ { mediaType: type, suggestedName: name },
293
+ { signal },
294
+ );
270
295
  return {
271
296
  path: stored.path,
272
297
  size: [canvas.width, canvas.height],
@@ -51,6 +51,25 @@ export interface IoBridge {
51
51
  write(path: string, data: Uint8Array): Promise<string>;
52
52
  /** Host-owned display URL, valid until the host releases its stored asset. */
53
53
  url?(path: string, options?: MediaOptions): string;
54
+ /**
55
+ * Store an intermediate image from its pixels and return the stored path,
56
+ * as `write` would. Optional. The headless host has it: it writes a fast
57
+ * lossless PNG, and in a run that keeps pixels it holds them in memory and
58
+ * writes behind (`pixels` below). Without it `saveImage` encodes and writes.
59
+ */
60
+ keep?(path: string, pixels: ImagePixels): Promise<string>;
61
+ /** The pixels behind a path `keep` returned, while the host holds them. */
62
+ pixels?(path: string): ImagePixels | undefined;
63
+ }
64
+
65
+ /**
66
+ * An image's pixels in memory. Mirrors Cascade's core `ImagePixels`: 8-bit
67
+ * RGBA, sRGB, unpremultiplied, top row first — `ImageData`'s layout.
68
+ */
69
+ export interface ImagePixels {
70
+ readonly width: number;
71
+ readonly height: number;
72
+ readonly data: Uint8ClampedArray;
54
73
  }
55
74
 
56
75
  const IO_BRIDGE_KEY = '__cascadeIoBridge';
@@ -96,6 +115,8 @@ export async function loadBytes(path: string, options: MediaOptions = { raw: tru
96
115
  * to hand it. Pass a width explicitly to work at a smaller size on purpose.
97
116
  */
98
117
  export async function loadImage(path: string, options: MediaOptions = { raw: true }): Promise<HTMLImageElement> {
118
+ const held = heldBitmap(path);
119
+ if (held) return held as unknown as HTMLImageElement;
99
120
  const image = new Image();
100
121
  const bridge = localIo();
101
122
  let temporaryUrl: string | undefined;
@@ -123,8 +144,41 @@ export async function loadImage(path: string, options: MediaOptions = { raw: tru
123
144
  }
124
145
  }
125
146
 
147
+ /**
148
+ * Pixels the host still holds in memory for `path` (a path `keep` returned),
149
+ * as something `drawImage` takes, or null to read the file. Bypasses the read
150
+ * and the decode; the image is the same either way (`ImagePixels` is what a
151
+ * PNG decodes to).
152
+ */
153
+ function heldBitmap(path: string): ImageBitmap | null {
154
+ if (/^(https?:|data:|blob:)/.test(path)) return null;
155
+ const pixels = localIo()?.pixels?.(path.replace(/^\.?\//, ''));
156
+ return pixels ? pixelsBitmap(pixels) : null;
157
+ }
158
+
159
+ /**
160
+ * `ImagePixels` as a drawable: a canvas holding them, with an `ImageBitmap`'s
161
+ * `width`, `height` and `close()` (and an image's `naturalWidth`/`naturalHeight`).
162
+ */
163
+ export function pixelsBitmap(pixels: ImagePixels): ImageBitmap {
164
+ const Canvas = (globalThis as { OffscreenCanvas?: new (w: number, h: number) => OffscreenCanvas }).OffscreenCanvas;
165
+ if (!Canvas) throw new Error('cascade/io: this host has no OffscreenCanvas to hold the image');
166
+ const canvas = new Canvas(pixels.width, pixels.height);
167
+ const context = canvas.getContext('2d') as OffscreenCanvasRenderingContext2D | null;
168
+ if (!context) throw new Error('cascade/io: the canvas has no 2D context');
169
+ const data = new ImageData(pixels.data as Uint8ClampedArray<ArrayBuffer>, pixels.width, pixels.height);
170
+ context.putImageData(data, 0, 0);
171
+ const drawable = canvas as unknown as Record<string, unknown>;
172
+ drawable.close = () => {};
173
+ drawable.naturalWidth = pixels.width;
174
+ drawable.naturalHeight = pixels.height;
175
+ return canvas as unknown as ImageBitmap;
176
+ }
177
+
126
178
  /** Same, as an ImageBitmap — the right input for `texImage2D`. */
127
179
  export async function loadBitmap(path: string, options: MediaOptions = { raw: true }): Promise<ImageBitmap> {
180
+ const held = heldBitmap(path);
181
+ if (held) return held;
128
182
  const bridge = localIo();
129
183
  if (bridge?.url) return createImageBitmap(await loadImage(path, options));
130
184
  if (bridge) return createImageBitmap(new Blob([await loadBytes(path, options)]));
@@ -177,6 +231,15 @@ export async function saveImage(
177
231
  const type = `image/${format}`;
178
232
  let blob: Blob;
179
233
 
234
+ // A host that keeps intermediates gets the pixels, not an encode: the
235
+ // headless host writes a fast PNG (or holds them in memory), where the
236
+ // canvas's own encoder would spend six times as long.
237
+ const bridge = localIo();
238
+ if (format === 'png' && bridge?.keep) {
239
+ const pixels = canvasPixels(source);
240
+ if (pixels) return bridge.keep(path.replace(/^\.?\//, ''), pixels);
241
+ }
242
+
180
243
  if (source instanceof Blob) {
181
244
  blob = source;
182
245
  } else if (typeof (source as OffscreenCanvas).convertToBlob === 'function') {
@@ -200,6 +263,21 @@ export async function saveImage(
200
263
  return saveBytes(blob, path);
201
264
  }
202
265
 
266
+ /**
267
+ * A canvas's pixels as `ImagePixels`, or null for anything that is not a 2D
268
+ * canvas (a Blob, an encoder-only source, a WebGL canvas, whose `getContext('2d')`
269
+ * is null).
270
+ */
271
+ export function canvasPixels(source: unknown): ImagePixels | null {
272
+ const canvas = source as { width?: unknown; height?: unknown; getContext?: (kind: '2d') => CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null };
273
+ if (typeof canvas?.getContext !== 'function' || typeof canvas.width !== 'number' || typeof canvas.height !== 'number') return null;
274
+ if (!canvas.width || !canvas.height) return null;
275
+ const context = canvas.getContext('2d');
276
+ if (!context || typeof context.getImageData !== 'function') return null;
277
+ const image = context.getImageData(0, 0, canvas.width, canvas.height);
278
+ return { width: image.width, height: image.height, data: image.data };
279
+ }
280
+
203
281
  /** Write any bytes into the project cache and return the path. */
204
282
  export async function saveBytes(data: Blob | ArrayBuffer | Uint8Array, path: string): Promise<string> {
205
283
  const clean = path.replace(/^\.?\//, '');
@@ -0,0 +1,166 @@
1
+ import {
2
+ deployVercel,
3
+ exportEditor,
4
+ scanForLeaks,
5
+ vercelConfig
6
+ } from "./chunk-JEW5MPGI.js";
7
+ import "./chunk-2L34ERS2.js";
8
+ import "./chunk-QKL7CCRS.js";
9
+ import "./chunk-MI5JLQYA.js";
10
+ import "./chunk-GRIN2DDP.js";
11
+ import "./chunk-X4WKAOQP.js";
12
+ import "./chunk-DEQSEFFH.js";
13
+ import "./chunk-RXDIMXN5.js";
14
+ import "./chunk-QQPA2ZHH.js";
15
+ import "./chunk-KH4MUBKF.js";
16
+ import "./chunk-HSNOBIVT.js";
17
+ import "./chunk-L6B6FUQP.js";
18
+ import "./chunk-HFHWYL55.js";
19
+ import "./chunk-KBGUO27Q.js";
20
+ import "./chunk-ICH7N5PN.js";
21
+ import "./chunk-CO22XHNG.js";
22
+ import "./chunk-LTXWBXOM.js";
23
+ import "./chunk-BZ63XQ3I.js";
24
+ import "./chunk-YH637PDL.js";
25
+ import "./chunk-X566FWBV.js";
26
+ import "./chunk-WD62JIP6.js";
27
+ import "./chunk-5ORUQ6ES.js";
28
+ import "./chunk-W5PJ5RMP.js";
29
+ import "./chunk-NJFMDXHR.js";
30
+ import "./chunk-J7VK7MNF.js";
31
+ import "./chunk-FWMSGKEH.js";
32
+ import "./chunk-JVOCK5W4.js";
33
+
34
+ // src/cli/share.ts
35
+ import fs from "node:fs/promises";
36
+ import os from "node:os";
37
+ import path from "node:path";
38
+ var SHARE_USAGE = "cascade share <graph-file | site-directory> [--name <slug>] [--scope <team>] [--protected] [--json]";
39
+ function shareSlug(text) {
40
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/^cascade-/, "").slice(0, 80).replace(/-+$/, "");
41
+ if (!slug) throw new Error(`"${text}" makes no usable name; pass --name <slug>`);
42
+ return slug;
43
+ }
44
+ async function share(target, options = {}) {
45
+ const log = options.log ?? ((line) => console.log(line));
46
+ const source = path.resolve(target);
47
+ const stat = await fs.stat(source).catch(() => void 0);
48
+ if (!stat) throw new Error(`${target} does not exist`);
49
+ const isGraph = stat.isFile() && source.endsWith(".cascade");
50
+ if (!isGraph && !stat.isDirectory()) throw new Error(`cascade share takes a .cascade graph or a directory with an index.html (got ${target})`);
51
+ const scratch = await fs.mkdtemp(path.join(os.tmpdir(), "cascade-share-"));
52
+ try {
53
+ const site = path.join(scratch, "site");
54
+ let name;
55
+ let projectRoot;
56
+ if (isGraph) {
57
+ const { plan } = await exportEditor(source, { out: site, log });
58
+ name = shareSlug(options.name ?? plan.editor.id);
59
+ projectRoot = path.dirname(source);
60
+ } else {
61
+ if (!(await fs.stat(path.join(source, "index.html")).catch(() => void 0))?.isFile())
62
+ throw new Error(`${target} has no index.html to share`);
63
+ await copySite(source, site);
64
+ if (!await fs.stat(path.join(site, "vercel.json")).catch(() => void 0))
65
+ await fs.writeFile(path.join(site, "vercel.json"), `${JSON.stringify(vercelConfig(), null, 2)}
66
+ `);
67
+ projectRoot = await findProjectRoot(source);
68
+ await scanForLeaks(site, projectRoot ?? source);
69
+ name = shareSlug(options.name ?? path.basename(source));
70
+ }
71
+ const project = `cascade-${name}`;
72
+ const deploy = options.deploy ?? deployVercel;
73
+ const deployed = await deploy(site, { project, scope: options.scope, public: options.protected !== true, log });
74
+ const record = {
75
+ source: projectRoot ? path.relative(projectRoot, source).split(path.sep).join("/") || "." : path.basename(source),
76
+ name,
77
+ project,
78
+ url: deployed.url,
79
+ deployment: deployed.deployment,
80
+ sharedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
81
+ };
82
+ const shares = projectRoot ? await recordShare(projectRoot, record) : void 0;
83
+ return { ...record, ...shares ? { shares } : {} };
84
+ } finally {
85
+ await fs.rm(scratch, { recursive: true, force: true });
86
+ }
87
+ }
88
+ async function recordShare(projectRoot, record) {
89
+ const file = path.join(projectRoot, "shares.json");
90
+ const existing = await readShares(projectRoot);
91
+ const shares = [...existing.shares.filter((item) => item.source !== record.source), record].sort((a, b) => a.source.localeCompare(b.source));
92
+ const next = { format: "cascade.shares", version: 1, shares };
93
+ await fs.writeFile(file, `${JSON.stringify(next, null, 2)}
94
+ `);
95
+ return file;
96
+ }
97
+ async function readShares(projectRoot) {
98
+ const text = await fs.readFile(path.join(projectRoot, "shares.json"), "utf8").catch(() => void 0);
99
+ if (!text) return { format: "cascade.shares", version: 1, shares: [] };
100
+ const parsed = JSON.parse(text);
101
+ if (parsed.format !== "cascade.shares" || !Array.isArray(parsed.shares)) throw new Error(`${path.join(projectRoot, "shares.json")} is not a cascade.shares file`);
102
+ return { format: "cascade.shares", version: 1, shares: parsed.shares };
103
+ }
104
+ async function findProjectRoot(start) {
105
+ for (let directory = start; ; directory = path.dirname(directory)) {
106
+ if (await fs.stat(path.join(directory, "cascade.json")).catch(() => void 0)) return directory;
107
+ if (path.dirname(directory) === directory) return void 0;
108
+ }
109
+ }
110
+ async function copySite(from, to) {
111
+ await fs.cp(from, to, {
112
+ recursive: true,
113
+ dereference: true,
114
+ filter: (file) => {
115
+ const relative = path.relative(from, file);
116
+ return !relative || relative.split(path.sep).every((part) => !part.startsWith(".") && part !== "licensed");
117
+ }
118
+ });
119
+ }
120
+ async function runShareCommand(args) {
121
+ const rest = args.slice(1);
122
+ const json = rest.includes("--json");
123
+ let target;
124
+ const values = {};
125
+ let isProtected = false;
126
+ for (let index = 0; index < rest.length; index++) {
127
+ const arg = rest[index];
128
+ if (arg === "--json") continue;
129
+ if (arg === "--protected") {
130
+ isProtected = true;
131
+ continue;
132
+ }
133
+ if (arg === "--name" || arg === "--scope") {
134
+ const value = rest[++index];
135
+ if (!value || value.startsWith("--")) throw new Error(`${arg} needs a value`);
136
+ values[arg.slice(2)] = value;
137
+ continue;
138
+ }
139
+ if (arg.startsWith("--")) throw new Error(`Unknown option ${arg}
140
+ Usage: ${SHARE_USAGE}`);
141
+ if (target) throw new Error(`cascade share takes one graph or directory
142
+ Usage: ${SHARE_USAGE}`);
143
+ target = arg;
144
+ }
145
+ if (!target) throw new Error(`Usage: ${SHARE_USAGE}`);
146
+ const result = await share(target, {
147
+ ...values.name ? { name: values.name } : {},
148
+ ...values.scope ? { scope: values.scope } : {},
149
+ protected: isProtected,
150
+ log: json ? () => {
151
+ } : (line) => console.log(line)
152
+ });
153
+ if (json) console.log(JSON.stringify(result));
154
+ else console.log(`
155
+ Shared: ${result.url}${result.shares ? `
156
+ Recorded in ${result.shares}` : ""}`);
157
+ }
158
+ export {
159
+ SHARE_USAGE,
160
+ readShares,
161
+ recordShare,
162
+ runShareCommand,
163
+ share,
164
+ shareSlug
165
+ };
166
+ //# sourceMappingURL=share-B7HTCOJX.js.map
@@ -0,0 +1,15 @@
1
+ import {
2
+ callStage,
3
+ cascadeVersion,
4
+ formatStageTrace,
5
+ stageCacheKey,
6
+ stageSources
7
+ } from "./chunk-5ORUQ6ES.js";
8
+ export {
9
+ callStage,
10
+ cascadeVersion,
11
+ formatStageTrace,
12
+ stageCacheKey,
13
+ stageSources
14
+ };
15
+ //# sourceMappingURL=stageCache-TY2BFZJR.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  readStageConfig,
3
3
  runProjectStage
4
- } from "./chunk-VVGSLVSF.js";
4
+ } from "./chunk-X566FWBV.js";
5
5
  import "./chunk-WD62JIP6.js";
6
6
  export {
7
7
  readStageConfig,
8
8
  runProjectStage
9
9
  };
10
- //# sourceMappingURL=stageRunner-KUNIMQKJ.js.map
10
+ //# sourceMappingURL=stageRunner-KY2EJP7X.js.map
@@ -0,0 +1,126 @@
1
+ import {
2
+ colorToCss,
3
+ normalizeColor,
4
+ readPixelsFromCanvas,
5
+ surface,
6
+ writePixels
7
+ } from "./chunk-WGKPJZDY.js";
8
+
9
+ // packages/runtime/src/builtins/image/text.ts
10
+ var FONT_FILE = /\.(otf|ttf|woff2?)$/i;
11
+ var HOST_KEY = "__cascadeImageHost";
12
+ var registered = /* @__PURE__ */ new Set();
13
+ function fingerprint(bytes) {
14
+ let hash = 2166136261;
15
+ for (let index = 0; index < bytes.length; index += 1) {
16
+ hash ^= bytes[index];
17
+ hash = Math.imul(hash, 16777619);
18
+ }
19
+ return `${bytes.length}-${(hash >>> 0).toString(16)}`;
20
+ }
21
+ async function fontFamily(context, font) {
22
+ const path = font.trim();
23
+ if (!FONT_FILE.test(path)) return font;
24
+ let bytes;
25
+ try {
26
+ const assets = context.capabilities.assets;
27
+ if (!assets) throw new Error("no assets");
28
+ bytes = await assets.read({ path }, { signal: context.signal });
29
+ } catch {
30
+ return "sans-serif";
31
+ }
32
+ const family = `cascade-font-${fingerprint(bytes)}`;
33
+ const globals = globalThis;
34
+ const host = globals[HOST_KEY];
35
+ const fonts = host ? null : globals.fonts ?? globals.document?.fonts;
36
+ const key = `${host ? `headless:${host.renderer}` : "browser"}:${family}`;
37
+ if (registered.has(key)) return family;
38
+ if (host && typeof host.registerFont === "function") {
39
+ await host.registerFont(family, bytes, {});
40
+ } else if (typeof globals.FontFace === "function" && fonts) {
41
+ const face = new globals.FontFace(family, bytes.slice().buffer);
42
+ fonts.add(await face.load());
43
+ } else {
44
+ return "sans-serif";
45
+ }
46
+ registered.add(key);
47
+ return family;
48
+ }
49
+ async function executeText(context) {
50
+ const { props } = context;
51
+ const [width, height] = [props.resolution[0], props.resolution[1]];
52
+ const color = normalizeColor(props.color);
53
+ const background = normalizeColor(props.backgroundColor);
54
+ const [offsetX, offsetY] = props.offset;
55
+ const { fontSize, alignH, alignV, rotation, letterSpacing } = props;
56
+ const family = await fontFamily(context, props.font);
57
+ const { canvas, context: ctx } = surface(width, height);
58
+ if (background.a > 0) {
59
+ ctx.fillStyle = colorToCss(background);
60
+ ctx.fillRect(0, 0, width, height);
61
+ } else {
62
+ ctx.clearRect(0, 0, width, height);
63
+ }
64
+ ctx.save();
65
+ ctx.globalAlpha = props.opacity;
66
+ ctx.font = `${props.fontStyle} ${props.fontWeight} ${fontSize}px ${family}`;
67
+ ctx.fillStyle = colorToCss(color);
68
+ const lines = props.text.split("\n");
69
+ const lineHeightPx = fontSize * props.lineHeight;
70
+ const totalTextHeight = lines.length * lineHeightPx;
71
+ let anchorX;
72
+ let anchorY;
73
+ switch (alignH) {
74
+ case "left":
75
+ ctx.textAlign = "left";
76
+ anchorX = 0;
77
+ break;
78
+ case "right":
79
+ ctx.textAlign = "right";
80
+ anchorX = width;
81
+ break;
82
+ default:
83
+ ctx.textAlign = "center";
84
+ anchorX = width / 2;
85
+ }
86
+ switch (alignV) {
87
+ case "top":
88
+ anchorY = fontSize;
89
+ break;
90
+ case "bottom":
91
+ anchorY = height - totalTextHeight + fontSize;
92
+ break;
93
+ default:
94
+ anchorY = (height - totalTextHeight) / 2 + fontSize;
95
+ }
96
+ anchorX += offsetX;
97
+ anchorY += offsetY;
98
+ if (rotation !== 0) {
99
+ const pivotY = anchorY - fontSize / 2 + totalTextHeight / 2;
100
+ ctx.translate(anchorX, pivotY);
101
+ ctx.rotate(rotation * Math.PI / 180);
102
+ ctx.translate(-anchorX, -pivotY);
103
+ }
104
+ if (letterSpacing !== 0) {
105
+ ctx.textAlign = "left";
106
+ lines.forEach((line, index) => {
107
+ const y = anchorY + index * lineHeightPx;
108
+ let lineWidth = 0;
109
+ for (const char of line) lineWidth += ctx.measureText(char).width + letterSpacing;
110
+ lineWidth -= letterSpacing;
111
+ let x = alignH === "left" ? anchorX : alignH === "right" ? anchorX - lineWidth : anchorX - lineWidth / 2;
112
+ for (const char of line) {
113
+ ctx.fillText(char, x, y);
114
+ x += ctx.measureText(char).width + letterSpacing;
115
+ }
116
+ });
117
+ } else {
118
+ lines.forEach((line, index) => ctx.fillText(line, anchorX, anchorY + index * lineHeightPx));
119
+ }
120
+ ctx.restore();
121
+ context.outputs.image.set(await writePixels(context, readPixelsFromCanvas(canvas)));
122
+ }
123
+ export {
124
+ executeText
125
+ };
126
+ //# sourceMappingURL=text-MB3EKR4H.js.map
@@ -6,7 +6,7 @@ import {
6
6
  mainHelp,
7
7
  mcpTools,
8
8
  verbFor
9
- } from "./chunk-POMMNKZZ.js";
9
+ } from "./chunk-G23A2BZZ.js";
10
10
  export {
11
11
  VERBS,
12
12
  graphCommandsHelp,
@@ -16,4 +16,4 @@ export {
16
16
  mcpTools,
17
17
  verbFor
18
18
  };
19
- //# sourceMappingURL=verbs-RCE3K4VQ.js.map
19
+ //# sourceMappingURL=verbs-PI2JH5FI.js.map