@nexart/codemode-sdk 1.8.3 → 1.8.4

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 (141) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/browser.cjs +3077 -0
  4. package/dist/cjs/browser.js +3042 -0
  5. package/dist/cjs/core.cjs +1660 -0
  6. package/dist/cjs/core.js +1636 -0
  7. package/dist/cjs/node.cjs +3245 -0
  8. package/dist/cjs/node.js +3208 -0
  9. package/dist/esm/browser.cjs +3077 -0
  10. package/dist/esm/browser.js +3042 -0
  11. package/dist/esm/core.cjs +1660 -0
  12. package/dist/esm/core.js +1636 -0
  13. package/dist/esm/node.cjs +3245 -0
  14. package/dist/esm/node.js +3208 -0
  15. package/dist/types/sdk/codemode/builder-manifest.d.ts.map +1 -0
  16. package/dist/{sdk → types/sdk}/codemode/core-index.d.ts +1 -1
  17. package/dist/types/sdk/codemode/core-index.d.ts.map +1 -0
  18. package/dist/types/sdk/codemode/engine.d.ts.map +1 -0
  19. package/dist/{sdk → types/sdk}/codemode/entry/browser.d.ts +1 -1
  20. package/dist/types/sdk/codemode/entry/browser.d.ts.map +1 -0
  21. package/dist/types/sdk/codemode/entry/node.d.ts.map +1 -0
  22. package/dist/types/sdk/codemode/execute.d.ts.map +1 -0
  23. package/dist/types/sdk/codemode/execution-sandbox.d.ts.map +1 -0
  24. package/dist/types/sdk/codemode/loop-engine.d.ts.map +1 -0
  25. package/dist/types/sdk/codemode/p5-runtime.d.ts.map +1 -0
  26. package/dist/{sdk → types/sdk}/codemode/runtime.d.ts +1 -1
  27. package/dist/types/sdk/codemode/runtime.d.ts.map +1 -0
  28. package/dist/types/sdk/codemode/snapshot.d.ts +72 -0
  29. package/dist/types/sdk/codemode/snapshot.d.ts.map +1 -0
  30. package/dist/types/sdk/codemode/sound-bridge.d.ts.map +1 -0
  31. package/dist/types/sdk/codemode/soundart-engine.d.ts.map +1 -0
  32. package/dist/types/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +1 -0
  33. package/dist/types/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +1 -0
  34. package/dist/types/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +1 -0
  35. package/dist/types/sdk/codemode/soundart-sketches/index.d.ts.map +1 -0
  36. package/dist/types/sdk/codemode/soundart-sketches/isoflow.d.ts.map +1 -0
  37. package/dist/types/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +1 -0
  38. package/dist/types/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +1 -0
  39. package/dist/types/sdk/codemode/soundart-sketches/orb.d.ts.map +1 -0
  40. package/dist/types/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +1 -0
  41. package/dist/types/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +1 -0
  42. package/dist/types/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +1 -0
  43. package/dist/types/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +1 -0
  44. package/dist/types/sdk/codemode/soundart-sketches/rings.d.ts.map +1 -0
  45. package/dist/types/sdk/codemode/soundart-sketches/squares.d.ts.map +1 -0
  46. package/dist/types/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +1 -0
  47. package/dist/types/sdk/codemode/static-engine.d.ts.map +1 -0
  48. package/dist/types/sdk/codemode/types.d.ts.map +1 -0
  49. package/dist/{sdk → types/sdk}/codemode/version.d.ts +2 -2
  50. package/dist/types/sdk/codemode/version.d.ts.map +1 -0
  51. package/dist/types/shared/soundSnapshot.d.ts.map +1 -0
  52. package/package.json +22 -22
  53. package/dist/sdk/codemode/builder-manifest.d.ts.map +0 -1
  54. package/dist/sdk/codemode/builder-manifest.js +0 -97
  55. package/dist/sdk/codemode/core-index.d.ts.map +0 -1
  56. package/dist/sdk/codemode/core-index.js +0 -28
  57. package/dist/sdk/codemode/engine.d.ts.map +0 -1
  58. package/dist/sdk/codemode/engine.js +0 -67
  59. package/dist/sdk/codemode/entry/browser.d.ts.map +0 -1
  60. package/dist/sdk/codemode/entry/browser.js +0 -69
  61. package/dist/sdk/codemode/entry/node.d.ts.map +0 -1
  62. package/dist/sdk/codemode/entry/node.js +0 -35
  63. package/dist/sdk/codemode/execute.d.ts.map +0 -1
  64. package/dist/sdk/codemode/execute.js +0 -283
  65. package/dist/sdk/codemode/execution-sandbox.d.ts.map +0 -1
  66. package/dist/sdk/codemode/execution-sandbox.js +0 -207
  67. package/dist/sdk/codemode/loop-engine.d.ts.map +0 -1
  68. package/dist/sdk/codemode/loop-engine.js +0 -229
  69. package/dist/sdk/codemode/p5-runtime.d.ts.map +0 -1
  70. package/dist/sdk/codemode/p5-runtime.js +0 -1033
  71. package/dist/sdk/codemode/runtime.d.ts.map +0 -1
  72. package/dist/sdk/codemode/runtime.js +0 -220
  73. package/dist/sdk/codemode/sound-bridge.d.ts.map +0 -1
  74. package/dist/sdk/codemode/sound-bridge.js +0 -128
  75. package/dist/sdk/codemode/soundart-engine.d.ts.map +0 -1
  76. package/dist/sdk/codemode/soundart-engine.js +0 -173
  77. package/dist/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +0 -1
  78. package/dist/sdk/codemode/soundart-sketches/chladniBloom.js +0 -53
  79. package/dist/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +0 -1
  80. package/dist/sdk/codemode/soundart-sketches/dualVortex.js +0 -67
  81. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +0 -1
  82. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.js +0 -89
  83. package/dist/sdk/codemode/soundart-sketches/index.d.ts.map +0 -1
  84. package/dist/sdk/codemode/soundart-sketches/index.js +0 -72
  85. package/dist/sdk/codemode/soundart-sketches/isoflow.d.ts.map +0 -1
  86. package/dist/sdk/codemode/soundart-sketches/isoflow.js +0 -60
  87. package/dist/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +0 -1
  88. package/dist/sdk/codemode/soundart-sketches/loomWeave.js +0 -59
  89. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +0 -1
  90. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.js +0 -53
  91. package/dist/sdk/codemode/soundart-sketches/orb.d.ts.map +0 -1
  92. package/dist/sdk/codemode/soundart-sketches/orb.js +0 -50
  93. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +0 -1
  94. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.js +0 -72
  95. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +0 -1
  96. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.js +0 -51
  97. package/dist/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +0 -1
  98. package/dist/sdk/codemode/soundart-sketches/radialBurst.js +0 -60
  99. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +0 -1
  100. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.js +0 -89
  101. package/dist/sdk/codemode/soundart-sketches/rings.d.ts.map +0 -1
  102. package/dist/sdk/codemode/soundart-sketches/rings.js +0 -89
  103. package/dist/sdk/codemode/soundart-sketches/squares.d.ts.map +0 -1
  104. package/dist/sdk/codemode/soundart-sketches/squares.js +0 -52
  105. package/dist/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +0 -1
  106. package/dist/sdk/codemode/soundart-sketches/waveStripes.js +0 -44
  107. package/dist/sdk/codemode/static-engine.d.ts.map +0 -1
  108. package/dist/sdk/codemode/static-engine.js +0 -157
  109. package/dist/sdk/codemode/types.d.ts.map +0 -1
  110. package/dist/sdk/codemode/types.js +0 -34
  111. package/dist/sdk/codemode/version.d.ts.map +0 -1
  112. package/dist/sdk/codemode/version.js +0 -17
  113. package/dist/shared/soundSnapshot.d.ts.map +0 -1
  114. package/dist/shared/soundSnapshot.js +0 -128
  115. /package/dist/{sdk → types/sdk}/codemode/builder-manifest.d.ts +0 -0
  116. /package/dist/{sdk → types/sdk}/codemode/engine.d.ts +0 -0
  117. /package/dist/{sdk → types/sdk}/codemode/entry/node.d.ts +0 -0
  118. /package/dist/{sdk → types/sdk}/codemode/execute.d.ts +0 -0
  119. /package/dist/{sdk → types/sdk}/codemode/execution-sandbox.d.ts +0 -0
  120. /package/dist/{sdk → types/sdk}/codemode/loop-engine.d.ts +0 -0
  121. /package/dist/{sdk → types/sdk}/codemode/p5-runtime.d.ts +0 -0
  122. /package/dist/{sdk → types/sdk}/codemode/sound-bridge.d.ts +0 -0
  123. /package/dist/{sdk → types/sdk}/codemode/soundart-engine.d.ts +0 -0
  124. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/chladniBloom.d.ts +0 -0
  125. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/dualVortex.d.ts +0 -0
  126. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/geometryIllusion.d.ts +0 -0
  127. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/index.d.ts +0 -0
  128. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/isoflow.d.ts +0 -0
  129. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/loomWeave.d.ts +0 -0
  130. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/noiseTerraces.d.ts +0 -0
  131. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/orb.d.ts +0 -0
  132. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/pixelGlyphs.d.ts +0 -0
  133. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/prismFlowFields.d.ts +0 -0
  134. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/radialBurst.d.ts +0 -0
  135. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/resonantSoundBodies.d.ts +0 -0
  136. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/rings.d.ts +0 -0
  137. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/squares.d.ts +0 -0
  138. /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/waveStripes.d.ts +0 -0
  139. /package/dist/{sdk → types/sdk}/codemode/static-engine.d.ts +0 -0
  140. /package/dist/{sdk → types/sdk}/codemode/types.d.ts +0 -0
  141. /package/dist/{shared → types/shared}/soundSnapshot.d.ts +0 -0
@@ -0,0 +1,1660 @@
1
+ 'use strict';
2
+
3
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
4
+ // version.ts
5
+ var SDK_VERSION = "1.8.4";
6
+ var PROTOCOL_VERSION = "1.2.0";
7
+ var PROTOCOL_PHASE = 3;
8
+
9
+ // types.ts
10
+ var PROTOCOL_IDENTITY = {
11
+ protocol: "nexart",
12
+ engine: "codemode",
13
+ protocolVersion: PROTOCOL_VERSION,
14
+ phase: PROTOCOL_PHASE,
15
+ deterministic: true
16
+ };
17
+ var DEFAULT_VARS = {
18
+ VAR: Object.freeze([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
19
+ };
20
+ var DEFAULT_CONFIG = {
21
+ width: 1950,
22
+ height: 2400,
23
+ duration: 2,
24
+ fps: 30,
25
+ minDuration: 1,
26
+ maxDuration: 4
27
+ };
28
+
29
+ // p5-runtime.ts
30
+ var CODE_MODE_PROTOCOL_VERSION = PROTOCOL_VERSION;
31
+ var CODE_MODE_PROTOCOL_PHASE = PROTOCOL_PHASE;
32
+ var CODE_MODE_ENFORCEMENT = "HARD";
33
+ function createSeededRNG(seed = 123456) {
34
+ let a = seed >>> 0;
35
+ return () => {
36
+ a += 1831565813;
37
+ let t = Math.imul(a ^ a >>> 15, a | 1);
38
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
39
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
40
+ };
41
+ }
42
+ function createSeededNoise(seed = 0) {
43
+ const permutation = [];
44
+ const rng = createSeededRNG(seed);
45
+ for (let i = 0; i < 256; i++) {
46
+ permutation[i] = i;
47
+ }
48
+ for (let i = 255; i > 0; i--) {
49
+ const j = Math.floor(rng() * (i + 1));
50
+ [permutation[i], permutation[j]] = [permutation[j], permutation[i]];
51
+ }
52
+ for (let i = 0; i < 256; i++) {
53
+ permutation[256 + i] = permutation[i];
54
+ }
55
+ const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
56
+ const lerp = (a, b, t) => a + t * (b - a);
57
+ const grad = (hash, x, y, z) => {
58
+ const h = hash & 15;
59
+ const u = h < 8 ? x : y;
60
+ const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
61
+ return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
62
+ };
63
+ return (x, y = 0, z = 0) => {
64
+ const X = Math.floor(x) & 255;
65
+ const Y = Math.floor(y) & 255;
66
+ const Z = Math.floor(z) & 255;
67
+ x -= Math.floor(x);
68
+ y -= Math.floor(y);
69
+ z -= Math.floor(z);
70
+ const u = fade(x);
71
+ const v = fade(y);
72
+ const w = fade(z);
73
+ const A = permutation[X] + Y;
74
+ const AA = permutation[A] + Z;
75
+ const AB = permutation[A + 1] + Z;
76
+ const B = permutation[X + 1] + Y;
77
+ const BA = permutation[B] + Z;
78
+ const BB = permutation[B + 1] + Z;
79
+ return (lerp(
80
+ lerp(
81
+ lerp(grad(permutation[AA], x, y, z), grad(permutation[BA], x - 1, y, z), u),
82
+ lerp(grad(permutation[AB], x, y - 1, z), grad(permutation[BB], x - 1, y - 1, z), u),
83
+ v
84
+ ),
85
+ lerp(
86
+ lerp(grad(permutation[AA + 1], x, y, z - 1), grad(permutation[BA + 1], x - 1, y, z - 1), u),
87
+ lerp(grad(permutation[AB + 1], x, y - 1, z - 1), grad(permutation[BB + 1], x - 1, y - 1, z - 1), u),
88
+ v
89
+ ),
90
+ w
91
+ ) + 1) / 2;
92
+ };
93
+ }
94
+ function createP5Runtime(canvas, width, height, config) {
95
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
96
+ if (!ctx) throw new Error("Failed to get 2D context");
97
+ let currentFill = "rgba(255, 255, 255, 1)";
98
+ let currentStroke = "rgba(0, 0, 0, 1)";
99
+ let strokeEnabled = true;
100
+ let fillEnabled = true;
101
+ let currentStrokeWeight = 1;
102
+ let colorModeSettings = { mode: "RGB", maxR: 255, maxG: 255, maxB: 255, maxA: 255 };
103
+ let shapeVertices = [];
104
+ let pixelData = null;
105
+ let imageDataObj = null;
106
+ let currentTextSize = 12;
107
+ let currentTextFont = "sans-serif";
108
+ let currentTextAlignH = "left";
109
+ let currentTextAlignV = "alphabetic";
110
+ let randomSeedValue = config?.seed ?? Math.floor(Math.random() * 2147483647);
111
+ let rng = createSeededRNG(randomSeedValue);
112
+ let noiseSeedValue = config?.seed ?? 0;
113
+ let noiseFunc = createSeededNoise(noiseSeedValue);
114
+ let noiseOctaves = 4;
115
+ let noiseFalloff = 0.5;
116
+ const parseCssColor = (str) => {
117
+ const s = str.trim();
118
+ if (s.startsWith("#")) {
119
+ const hex = s.slice(1);
120
+ if (hex.length === 3) {
121
+ const r = parseInt(hex[0] + hex[0], 16);
122
+ const g = parseInt(hex[1] + hex[1], 16);
123
+ const b = parseInt(hex[2] + hex[2], 16);
124
+ return { r, g, b, a: 1 };
125
+ } else if (hex.length === 6) {
126
+ const r = parseInt(hex.slice(0, 2), 16);
127
+ const g = parseInt(hex.slice(2, 4), 16);
128
+ const b = parseInt(hex.slice(4, 6), 16);
129
+ return { r, g, b, a: 1 };
130
+ } else if (hex.length === 8) {
131
+ const r = parseInt(hex.slice(0, 2), 16);
132
+ const g = parseInt(hex.slice(2, 4), 16);
133
+ const b = parseInt(hex.slice(4, 6), 16);
134
+ const a = parseInt(hex.slice(6, 8), 16) / 255;
135
+ return { r, g, b, a };
136
+ }
137
+ }
138
+ const rgbMatch = s.match(/^rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)$/i);
139
+ if (rgbMatch) {
140
+ return {
141
+ r: parseInt(rgbMatch[1]),
142
+ g: parseInt(rgbMatch[2]),
143
+ b: parseInt(rgbMatch[3]),
144
+ a: 1
145
+ };
146
+ }
147
+ const rgbaMatch = s.match(/^rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\/\s]\s*([\d.]+)\s*\)$/i);
148
+ if (rgbaMatch) {
149
+ return {
150
+ r: parseInt(rgbaMatch[1]),
151
+ g: parseInt(rgbaMatch[2]),
152
+ b: parseInt(rgbaMatch[3]),
153
+ a: parseFloat(rgbaMatch[4])
154
+ };
155
+ }
156
+ const hslMatch = s.match(/^hsla?\s*\(\s*([\d.]+)\s*[,\s]\s*([\d.]+)%?\s*[,\s]\s*([\d.]+)%?\s*(?:[,\/\s]\s*([\d.]+))?\s*\)$/i);
157
+ if (hslMatch) {
158
+ const h = parseFloat(hslMatch[1]) / 360;
159
+ const sat = parseFloat(hslMatch[2]) / 100;
160
+ const l = parseFloat(hslMatch[3]) / 100;
161
+ const a = hslMatch[4] ? parseFloat(hslMatch[4]) : 1;
162
+ let r, g, b;
163
+ if (sat === 0) {
164
+ r = g = b = l;
165
+ } else {
166
+ const hue2rgb = (p3, q2, t) => {
167
+ if (t < 0) t += 1;
168
+ if (t > 1) t -= 1;
169
+ if (t < 1 / 6) return p3 + (q2 - p3) * 6 * t;
170
+ if (t < 1 / 2) return q2;
171
+ if (t < 2 / 3) return p3 + (q2 - p3) * (2 / 3 - t) * 6;
172
+ return p3;
173
+ };
174
+ const q = l < 0.5 ? l * (1 + sat) : l + sat - l * sat;
175
+ const p2 = 2 * l - q;
176
+ r = hue2rgb(p2, q, h + 1 / 3);
177
+ g = hue2rgb(p2, q, h);
178
+ b = hue2rgb(p2, q, h - 1 / 3);
179
+ }
180
+ return {
181
+ r: Math.round(r * 255),
182
+ g: Math.round(g * 255),
183
+ b: Math.round(b * 255),
184
+ a
185
+ };
186
+ }
187
+ return null;
188
+ };
189
+ const parseColor = (...args) => {
190
+ if (args.length === 0) return "rgba(0, 0, 0, 1)";
191
+ const { mode, maxR, maxG, maxB, maxA } = colorModeSettings;
192
+ if (args.length === 1) {
193
+ const val = args[0];
194
+ if (typeof val === "string") {
195
+ const parsed = parseCssColor(val);
196
+ if (parsed) {
197
+ return `rgba(${parsed.r}, ${parsed.g}, ${parsed.b}, ${parsed.a})`;
198
+ }
199
+ return val;
200
+ }
201
+ if (mode === "HSB") {
202
+ return `hsla(${val}, 100%, 50%, 1)`;
203
+ }
204
+ const gray = Math.round(val / maxR * 255);
205
+ return `rgba(${gray}, ${gray}, ${gray}, 1)`;
206
+ }
207
+ if (args.length === 2) {
208
+ const [gray, alpha] = args;
209
+ const g = Math.round(gray / maxR * 255);
210
+ const a = alpha / maxA;
211
+ return `rgba(${g}, ${g}, ${g}, ${a})`;
212
+ }
213
+ if (args.length === 3) {
214
+ const [r, g, b] = args;
215
+ if (mode === "HSB") {
216
+ return `hsla(${r / maxR * 360}, ${g / maxG * 100}%, ${b / maxB * 100}%, 1)`;
217
+ }
218
+ return `rgba(${Math.round(r / maxR * 255)}, ${Math.round(g / maxG * 255)}, ${Math.round(b / maxB * 255)}, 1)`;
219
+ }
220
+ if (args.length === 4) {
221
+ const [r, g, b, a] = args;
222
+ if (mode === "HSB") {
223
+ return `hsla(${r / maxR * 360}, ${g / maxG * 100}%, ${b / maxB * 100}%, ${a / maxA})`;
224
+ }
225
+ return `rgba(${Math.round(r / maxR * 255)}, ${Math.round(g / maxG * 255)}, ${Math.round(b / maxB * 255)}, ${a / maxA})`;
226
+ }
227
+ return "rgba(0, 0, 0, 1)";
228
+ };
229
+ const p = {
230
+ width,
231
+ height,
232
+ frameCount: 0,
233
+ // Constants
234
+ PI: Math.PI,
235
+ TWO_PI: Math.PI * 2,
236
+ TAU: Math.PI * 2,
237
+ HALF_PI: Math.PI / 2,
238
+ QUARTER_PI: Math.PI / 4,
239
+ // Shape mode constants
240
+ CORNER: "corner",
241
+ CENTER: "center",
242
+ CORNERS: "corners",
243
+ RADIUS: "radius",
244
+ ROUND: "round",
245
+ SQUARE: "butt",
246
+ PROJECT: "square",
247
+ MITER: "miter",
248
+ BEVEL: "bevel",
249
+ CLOSE: "close",
250
+ PIE: "pie",
251
+ CHORD: "chord",
252
+ OPEN: "open",
253
+ // Blend mode constants (v1.1)
254
+ NORMAL: "source-over",
255
+ ADD: "lighter",
256
+ MULTIPLY: "multiply",
257
+ SCREEN: "screen",
258
+ // Text alignment constants
259
+ LEFT: "left",
260
+ RIGHT: "right",
261
+ TOP: "top",
262
+ BOTTOM: "bottom",
263
+ BASELINE: "alphabetic",
264
+ // Canvas operations
265
+ background: (...args) => {
266
+ ctx.save();
267
+ ctx.fillStyle = parseColor(...args);
268
+ ctx.fillRect(0, 0, width, height);
269
+ ctx.restore();
270
+ },
271
+ clear: () => {
272
+ ctx.clearRect(0, 0, width, height);
273
+ },
274
+ blendMode: (mode) => {
275
+ const modeMap = {
276
+ "source-over": "source-over",
277
+ "NORMAL": "source-over",
278
+ "lighter": "lighter",
279
+ "ADD": "lighter",
280
+ "multiply": "multiply",
281
+ "MULTIPLY": "multiply",
282
+ "screen": "screen",
283
+ "SCREEN": "screen"
284
+ };
285
+ const compositeOp = modeMap[mode];
286
+ if (!compositeOp) {
287
+ throw new Error(`[Code Mode Protocol Error] Unsupported blend mode: ${mode}. Supported: NORMAL, ADD, MULTIPLY, SCREEN`);
288
+ }
289
+ ctx.globalCompositeOperation = compositeOp;
290
+ },
291
+ // Color functions
292
+ fill: (...args) => {
293
+ fillEnabled = true;
294
+ currentFill = parseColor(...args);
295
+ ctx.fillStyle = currentFill;
296
+ },
297
+ noFill: () => {
298
+ fillEnabled = false;
299
+ },
300
+ stroke: (...args) => {
301
+ strokeEnabled = true;
302
+ currentStroke = parseColor(...args);
303
+ ctx.strokeStyle = currentStroke;
304
+ },
305
+ noStroke: () => {
306
+ strokeEnabled = false;
307
+ },
308
+ strokeWeight: (weight) => {
309
+ currentStrokeWeight = weight;
310
+ ctx.lineWidth = weight;
311
+ },
312
+ strokeCap: (cap) => {
313
+ const capMap = {
314
+ "round": "round",
315
+ "ROUND": "round",
316
+ "square": "butt",
317
+ "SQUARE": "butt",
318
+ "project": "square",
319
+ "PROJECT": "square",
320
+ "butt": "butt"
321
+ };
322
+ ctx.lineCap = capMap[cap] || "round";
323
+ },
324
+ strokeJoin: (join) => {
325
+ const joinMap = {
326
+ "miter": "miter",
327
+ "MITER": "miter",
328
+ "bevel": "bevel",
329
+ "BEVEL": "bevel",
330
+ "round": "round",
331
+ "ROUND": "round"
332
+ };
333
+ ctx.lineJoin = joinMap[join] || "miter";
334
+ },
335
+ colorMode: (mode, max1, max2, max3, maxA) => {
336
+ colorModeSettings = {
337
+ mode: mode.toUpperCase(),
338
+ maxR: max1 ?? 255,
339
+ maxG: max2 ?? max1 ?? 255,
340
+ maxB: max3 ?? max1 ?? 255,
341
+ maxA: maxA ?? 255
342
+ };
343
+ },
344
+ color: (...args) => parseColor(...args),
345
+ lerpColor: (c1, c2, amt) => {
346
+ const color1 = parseCssColor(c1) || { r: 0, g: 0, b: 0, a: 1 };
347
+ const color2 = parseCssColor(c2) || { r: 255, g: 255, b: 255, a: 1 };
348
+ const r = Math.round(color1.r + (color2.r - color1.r) * amt);
349
+ const g = Math.round(color1.g + (color2.g - color1.g) * amt);
350
+ const b = Math.round(color1.b + (color2.b - color1.b) * amt);
351
+ const a = color1.a + (color2.a - color1.a) * amt;
352
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
353
+ },
354
+ red: (color) => {
355
+ const parsed = parseCssColor(color);
356
+ return parsed ? parsed.r : 0;
357
+ },
358
+ green: (color) => {
359
+ const parsed = parseCssColor(color);
360
+ return parsed ? parsed.g : 0;
361
+ },
362
+ blue: (color) => {
363
+ const parsed = parseCssColor(color);
364
+ return parsed ? parsed.b : 0;
365
+ },
366
+ alpha: (color) => {
367
+ const parsed = parseCssColor(color);
368
+ return parsed ? parsed.a * 255 : 255;
369
+ },
370
+ brightness: (color) => {
371
+ const parsed = parseCssColor(color);
372
+ if (!parsed) return 0;
373
+ return Math.max(parsed.r, parsed.g, parsed.b) / 255 * 100;
374
+ },
375
+ saturation: (color) => {
376
+ const parsed = parseCssColor(color);
377
+ if (!parsed) return 0;
378
+ const max = Math.max(parsed.r, parsed.g, parsed.b);
379
+ const min = Math.min(parsed.r, parsed.g, parsed.b);
380
+ if (max === 0) return 0;
381
+ return (max - min) / max * 100;
382
+ },
383
+ hue: (color) => {
384
+ const parsed = parseCssColor(color);
385
+ if (!parsed) return 0;
386
+ const { r, g, b } = parsed;
387
+ const max = Math.max(r, g, b);
388
+ const min = Math.min(r, g, b);
389
+ if (max === min) return 0;
390
+ let h = 0;
391
+ const d = max - min;
392
+ switch (max) {
393
+ case r:
394
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
395
+ break;
396
+ case g:
397
+ h = ((b - r) / d + 2) / 6;
398
+ break;
399
+ case b:
400
+ h = ((r - g) / d + 4) / 6;
401
+ break;
402
+ }
403
+ return h * 360;
404
+ },
405
+ // Shape functions
406
+ ellipse: (x, y, w, h) => {
407
+ const rw = w / 2;
408
+ const rh = (h ?? w) / 2;
409
+ ctx.beginPath();
410
+ ctx.ellipse(x, y, rw, rh, 0, 0, Math.PI * 2);
411
+ if (fillEnabled) ctx.fill();
412
+ if (strokeEnabled) ctx.stroke();
413
+ },
414
+ circle: (x, y, d) => {
415
+ p.ellipse(x, y, d, d);
416
+ },
417
+ rect: (x, y, w, h, r) => {
418
+ const height2 = h ?? w;
419
+ ctx.beginPath();
420
+ if (r && r > 0) {
421
+ ctx.roundRect(x, y, w, height2, r);
422
+ } else {
423
+ ctx.rect(x, y, w, height2);
424
+ }
425
+ if (fillEnabled) ctx.fill();
426
+ if (strokeEnabled) ctx.stroke();
427
+ },
428
+ square: (x, y, s, r) => {
429
+ p.rect(x, y, s, s, r);
430
+ },
431
+ line: (x1, y1, x2, y2) => {
432
+ ctx.beginPath();
433
+ ctx.moveTo(x1, y1);
434
+ ctx.lineTo(x2, y2);
435
+ if (strokeEnabled) ctx.stroke();
436
+ },
437
+ point: (x, y) => {
438
+ ctx.beginPath();
439
+ ctx.arc(x, y, currentStrokeWeight / 2, 0, Math.PI * 2);
440
+ ctx.fillStyle = currentStroke;
441
+ ctx.fill();
442
+ },
443
+ triangle: (x1, y1, x2, y2, x3, y3) => {
444
+ ctx.beginPath();
445
+ ctx.moveTo(x1, y1);
446
+ ctx.lineTo(x2, y2);
447
+ ctx.lineTo(x3, y3);
448
+ ctx.closePath();
449
+ if (fillEnabled) ctx.fill();
450
+ if (strokeEnabled) ctx.stroke();
451
+ },
452
+ quad: (x1, y1, x2, y2, x3, y3, x4, y4) => {
453
+ ctx.beginPath();
454
+ ctx.moveTo(x1, y1);
455
+ ctx.lineTo(x2, y2);
456
+ ctx.lineTo(x3, y3);
457
+ ctx.lineTo(x4, y4);
458
+ ctx.closePath();
459
+ if (fillEnabled) ctx.fill();
460
+ if (strokeEnabled) ctx.stroke();
461
+ },
462
+ arc: (x, y, w, h, start, stop, mode) => {
463
+ ctx.beginPath();
464
+ ctx.ellipse(x, y, w / 2, h / 2, 0, start, stop);
465
+ if (mode === "pie" || mode === "PIE") {
466
+ ctx.lineTo(x, y);
467
+ ctx.closePath();
468
+ } else if (mode === "chord" || mode === "CHORD") {
469
+ ctx.closePath();
470
+ }
471
+ if (fillEnabled) ctx.fill();
472
+ if (strokeEnabled) ctx.stroke();
473
+ },
474
+ // Bezier and curve functions
475
+ bezier: (x1, y1, cx1, cy1, cx2, cy2, x2, y2) => {
476
+ ctx.beginPath();
477
+ ctx.moveTo(x1, y1);
478
+ ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2);
479
+ if (strokeEnabled) ctx.stroke();
480
+ },
481
+ curve: (x1, y1, x2, y2, x3, y3, x4, y4) => {
482
+ const tension = 1 / 6;
483
+ const cp1x = x2 + (x3 - x1) * tension;
484
+ const cp1y = y2 + (y3 - y1) * tension;
485
+ const cp2x = x3 - (x4 - x2) * tension;
486
+ const cp2y = y3 - (y4 - y2) * tension;
487
+ ctx.beginPath();
488
+ ctx.moveTo(x2, y2);
489
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x3, y3);
490
+ if (strokeEnabled) ctx.stroke();
491
+ },
492
+ // Shape helpers (v1.1)
493
+ polygon: (cx, cy, radius, sides, rotation = 0) => {
494
+ ctx.beginPath();
495
+ for (let i = 0; i < sides; i++) {
496
+ const angle = rotation + i / sides * Math.PI * 2 - Math.PI / 2;
497
+ const x = cx + Math.cos(angle) * radius;
498
+ const y = cy + Math.sin(angle) * radius;
499
+ if (i === 0) {
500
+ ctx.moveTo(x, y);
501
+ } else {
502
+ ctx.lineTo(x, y);
503
+ }
504
+ }
505
+ ctx.closePath();
506
+ if (fillEnabled) ctx.fill();
507
+ if (strokeEnabled) ctx.stroke();
508
+ },
509
+ star: (cx, cy, innerRadius, outerRadius, points, rotation = 0) => {
510
+ ctx.beginPath();
511
+ const totalPoints = points * 2;
512
+ for (let i = 0; i < totalPoints; i++) {
513
+ const angle = rotation + i / totalPoints * Math.PI * 2 - Math.PI / 2;
514
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
515
+ const x = cx + Math.cos(angle) * radius;
516
+ const y = cy + Math.sin(angle) * radius;
517
+ if (i === 0) {
518
+ ctx.moveTo(x, y);
519
+ } else {
520
+ ctx.lineTo(x, y);
521
+ }
522
+ }
523
+ ctx.closePath();
524
+ if (fillEnabled) ctx.fill();
525
+ if (strokeEnabled) ctx.stroke();
526
+ },
527
+ // Vertex-based shapes
528
+ beginShape: () => {
529
+ shapeVertices = [];
530
+ },
531
+ vertex: (x, y) => {
532
+ shapeVertices.push({ x, y, type: "vertex" });
533
+ },
534
+ curveVertex: (x, y) => {
535
+ shapeVertices.push({ x, y, type: "curve" });
536
+ },
537
+ bezierVertex: (cx1, cy1, cx2, cy2, x, y) => {
538
+ shapeVertices.push({ x, y, type: "bezier", cx1, cy1, cx2, cy2 });
539
+ },
540
+ endShape: (mode) => {
541
+ if (shapeVertices.length === 0) return;
542
+ ctx.beginPath();
543
+ let started = false;
544
+ let curveBuffer = [];
545
+ const tension = 1 / 6;
546
+ const flushCurveBuffer = () => {
547
+ if (curveBuffer.length >= 4) {
548
+ if (!started) {
549
+ ctx.moveTo(curveBuffer[1].x, curveBuffer[1].y);
550
+ started = true;
551
+ } else {
552
+ ctx.lineTo(curveBuffer[1].x, curveBuffer[1].y);
553
+ }
554
+ for (let i = 1; i < curveBuffer.length - 2; i++) {
555
+ const p0 = curveBuffer[i - 1];
556
+ const p1 = curveBuffer[i];
557
+ const p2 = curveBuffer[i + 1];
558
+ const p3 = curveBuffer[i + 2];
559
+ const cp1x = p1.x + (p2.x - p0.x) * tension;
560
+ const cp1y = p1.y + (p2.y - p0.y) * tension;
561
+ const cp2x = p2.x - (p3.x - p1.x) * tension;
562
+ const cp2y = p2.y - (p3.y - p1.y) * tension;
563
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);
564
+ }
565
+ }
566
+ curveBuffer = [];
567
+ };
568
+ for (let i = 0; i < shapeVertices.length; i++) {
569
+ const v = shapeVertices[i];
570
+ if (v.type === "curve") {
571
+ curveBuffer.push({ x: v.x, y: v.y });
572
+ } else {
573
+ if (curveBuffer.length > 0) {
574
+ flushCurveBuffer();
575
+ }
576
+ if (v.type === "vertex") {
577
+ if (!started) {
578
+ ctx.moveTo(v.x, v.y);
579
+ started = true;
580
+ } else {
581
+ ctx.lineTo(v.x, v.y);
582
+ }
583
+ } else if (v.type === "bezier" && started) {
584
+ ctx.bezierCurveTo(v.cx1, v.cy1, v.cx2, v.cy2, v.x, v.y);
585
+ }
586
+ }
587
+ }
588
+ if (curveBuffer.length > 0) {
589
+ flushCurveBuffer();
590
+ }
591
+ if (mode === "close" || mode === "CLOSE") {
592
+ ctx.closePath();
593
+ }
594
+ if (fillEnabled) ctx.fill();
595
+ if (strokeEnabled) ctx.stroke();
596
+ shapeVertices = [];
597
+ },
598
+ // Transform functions
599
+ push: () => {
600
+ ctx.save();
601
+ },
602
+ pop: () => {
603
+ ctx.restore();
604
+ ctx.fillStyle = currentFill;
605
+ ctx.strokeStyle = currentStroke;
606
+ ctx.lineWidth = currentStrokeWeight;
607
+ },
608
+ translate: (x, y) => {
609
+ ctx.translate(x, y);
610
+ },
611
+ rotate: (angle) => {
612
+ ctx.rotate(angle);
613
+ },
614
+ scale: (sx, sy) => {
615
+ ctx.scale(sx, sy ?? sx);
616
+ },
617
+ resetMatrix: () => {
618
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
619
+ },
620
+ shearX: (angle) => {
621
+ ctx.transform(1, 0, Math.tan(angle), 1, 0, 0);
622
+ },
623
+ shearY: (angle) => {
624
+ ctx.transform(1, Math.tan(angle), 0, 1, 0, 0);
625
+ },
626
+ // Math functions - SEEDED for determinism
627
+ random: (min, max) => {
628
+ if (Array.isArray(min)) {
629
+ return min[Math.floor(rng() * min.length)];
630
+ }
631
+ if (min === void 0) return rng();
632
+ if (max === void 0) return rng() * min;
633
+ return min + rng() * (max - min);
634
+ },
635
+ randomSeed: (seed) => {
636
+ randomSeedValue = seed;
637
+ rng = createSeededRNG(seed);
638
+ },
639
+ randomGaussian: (mean = 0, sd = 1) => {
640
+ const u1 = rng();
641
+ const u2 = rng();
642
+ const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
643
+ return z0 * sd + mean;
644
+ },
645
+ noise: (x, y, z) => {
646
+ let total = 0;
647
+ let frequency = 1;
648
+ let amplitude = 1;
649
+ let maxValue = 0;
650
+ for (let i = 0; i < noiseOctaves; i++) {
651
+ total += noiseFunc(x * frequency, (y ?? 0) * frequency, (z ?? 0) * frequency) * amplitude;
652
+ maxValue += amplitude;
653
+ amplitude *= noiseFalloff;
654
+ frequency *= 2;
655
+ }
656
+ return total / maxValue;
657
+ },
658
+ noiseSeed: (seed) => {
659
+ noiseSeedValue = seed;
660
+ noiseFunc = createSeededNoise(seed);
661
+ },
662
+ noiseDetail: (lod, falloff) => {
663
+ noiseOctaves = Math.max(1, Math.min(8, lod));
664
+ if (falloff !== void 0) {
665
+ noiseFalloff = Math.max(0, Math.min(1, falloff));
666
+ }
667
+ },
668
+ // Noise extensions (v1.1) - use seeded noise internally
669
+ fbm: (x, y, octaves = 4, falloff = 0.5) => {
670
+ let total = 0;
671
+ let frequency = 1;
672
+ let amplitude = 1;
673
+ let maxValue = 0;
674
+ for (let i = 0; i < octaves; i++) {
675
+ total += noiseFunc(x * frequency, y * frequency) * amplitude;
676
+ maxValue += amplitude;
677
+ amplitude *= falloff;
678
+ frequency *= 2;
679
+ }
680
+ return total / maxValue;
681
+ },
682
+ ridgedNoise: (x, y) => {
683
+ let total = 0;
684
+ let frequency = 1;
685
+ let amplitude = 1;
686
+ let maxValue = 0;
687
+ for (let i = 0; i < 4; i++) {
688
+ const n = noiseFunc(x * frequency, y * frequency);
689
+ total += (1 - Math.abs(n * 2 - 1)) * amplitude;
690
+ maxValue += amplitude;
691
+ amplitude *= 0.5;
692
+ frequency *= 2;
693
+ }
694
+ return total / maxValue;
695
+ },
696
+ curlNoise: (x, y) => {
697
+ const eps = 1e-4;
698
+ const n1 = noiseFunc(x + eps, y);
699
+ const n2 = noiseFunc(x - eps, y);
700
+ const n3 = noiseFunc(x, y + eps);
701
+ const n4 = noiseFunc(x, y - eps);
702
+ const dx = (n1 - n2) / (2 * eps);
703
+ const dy = (n3 - n4) / (2 * eps);
704
+ return { x: -dy, y: dx };
705
+ },
706
+ map: (value, start1, stop1, start2, stop2) => {
707
+ return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
708
+ },
709
+ constrain: (n, low, high) => {
710
+ return Math.max(low, Math.min(high, n));
711
+ },
712
+ lerp: (start, stop, amt) => {
713
+ return start + (stop - start) * amt;
714
+ },
715
+ dist: (x1, y1, x2, y2) => {
716
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
717
+ },
718
+ mag: (x, y) => {
719
+ return Math.sqrt(x * x + y * y);
720
+ },
721
+ norm: (value, start, stop) => {
722
+ return (value - start) / (stop - start);
723
+ },
724
+ // Trig functions
725
+ sin: Math.sin,
726
+ cos: Math.cos,
727
+ tan: Math.tan,
728
+ asin: Math.asin,
729
+ acos: Math.acos,
730
+ atan: Math.atan,
731
+ atan2: Math.atan2,
732
+ radians: (degrees) => degrees * (Math.PI / 180),
733
+ degrees: (radians) => radians * (180 / Math.PI),
734
+ // Utility functions
735
+ abs: Math.abs,
736
+ ceil: Math.ceil,
737
+ floor: Math.floor,
738
+ round: Math.round,
739
+ sqrt: Math.sqrt,
740
+ pow: Math.pow,
741
+ exp: Math.exp,
742
+ log: Math.log,
743
+ min: Math.min,
744
+ max: Math.max,
745
+ int: (n) => Math.floor(n),
746
+ sq: (n) => n * n,
747
+ fract: (n) => n - Math.floor(n),
748
+ sign: (n) => n > 0 ? 1 : n < 0 ? -1 : 0,
749
+ // Vector helpers (v1.1) - plain objects, no mutation
750
+ vec: (x, y) => ({ x, y }),
751
+ vecAdd: (a, b) => ({
752
+ x: a.x + b.x,
753
+ y: a.y + b.y
754
+ }),
755
+ vecSub: (a, b) => ({
756
+ x: a.x - b.x,
757
+ y: a.y - b.y
758
+ }),
759
+ vecMult: (v, s) => ({
760
+ x: v.x * s,
761
+ y: v.y * s
762
+ }),
763
+ vecMag: (v) => Math.sqrt(v.x * v.x + v.y * v.y),
764
+ vecNorm: (v) => {
765
+ const m = Math.sqrt(v.x * v.x + v.y * v.y);
766
+ return m === 0 ? { x: 0, y: 0 } : { x: v.x / m, y: v.y / m };
767
+ },
768
+ vecDist: (a, b) => {
769
+ const dx = b.x - a.x;
770
+ const dy = b.y - a.y;
771
+ return Math.sqrt(dx * dx + dy * dy);
772
+ },
773
+ // Easing functions (v1.1) - pure functions, t ∈ [0,1] → [0,1]
774
+ easeIn: (t) => t * t,
775
+ easeOut: (t) => t * (2 - t),
776
+ easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
777
+ easeCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
778
+ easeExpo: (t) => {
779
+ if (t === 0) return 0;
780
+ if (t === 1) return 1;
781
+ if (t < 0.5) {
782
+ return Math.pow(2, 20 * t - 10) / 2;
783
+ }
784
+ return (2 - Math.pow(2, -20 * t + 10)) / 2;
785
+ },
786
+ // Text functions
787
+ text: (str, x, y) => {
788
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
789
+ ctx.textAlign = currentTextAlignH;
790
+ ctx.textBaseline = currentTextAlignV;
791
+ if (fillEnabled) {
792
+ ctx.fillStyle = currentFill;
793
+ ctx.fillText(String(str), x, y);
794
+ }
795
+ if (strokeEnabled) {
796
+ ctx.strokeStyle = currentStroke;
797
+ ctx.strokeText(String(str), x, y);
798
+ }
799
+ },
800
+ textSize: (size) => {
801
+ currentTextSize = size;
802
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
803
+ },
804
+ textFont: (font) => {
805
+ currentTextFont = font;
806
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
807
+ },
808
+ textAlign: (horizAlign, vertAlign) => {
809
+ const hMap = {
810
+ "left": "left",
811
+ "LEFT": "left",
812
+ "center": "center",
813
+ "CENTER": "center",
814
+ "right": "right",
815
+ "RIGHT": "right"
816
+ };
817
+ const vMap = {
818
+ "top": "top",
819
+ "TOP": "top",
820
+ "bottom": "bottom",
821
+ "BOTTOM": "bottom",
822
+ "center": "middle",
823
+ "CENTER": "middle",
824
+ "baseline": "alphabetic",
825
+ "BASELINE": "alphabetic"
826
+ };
827
+ currentTextAlignH = hMap[horizAlign] || "left";
828
+ if (vertAlign) {
829
+ currentTextAlignV = vMap[vertAlign] || "alphabetic";
830
+ }
831
+ },
832
+ textWidth: (str) => {
833
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
834
+ return ctx.measureText(String(str)).width;
835
+ },
836
+ // Pixel manipulation (v1.2)
837
+ loadPixels: () => {
838
+ imageDataObj = ctx.getImageData(0, 0, width, height);
839
+ pixelData = imageDataObj.data;
840
+ p.pixels = pixelData;
841
+ },
842
+ updatePixels: () => {
843
+ if (imageDataObj && pixelData) {
844
+ ctx.putImageData(imageDataObj, 0, 0);
845
+ }
846
+ },
847
+ pixels: null,
848
+ get: (x, y) => {
849
+ const imgData = ctx.getImageData(Math.floor(x), Math.floor(y), 1, 1);
850
+ return [imgData.data[0], imgData.data[1], imgData.data[2], imgData.data[3]];
851
+ },
852
+ set: (x, y, c) => {
853
+ const fx = Math.floor(x);
854
+ const fy = Math.floor(y);
855
+ if (Array.isArray(c)) {
856
+ const imgData = ctx.createImageData(1, 1);
857
+ imgData.data[0] = c[0];
858
+ imgData.data[1] = c[1];
859
+ imgData.data[2] = c[2];
860
+ imgData.data[3] = c[3] ?? 255;
861
+ ctx.putImageData(imgData, fx, fy);
862
+ } else {
863
+ const parsed = parseCssColor(c);
864
+ if (parsed) {
865
+ const imgData = ctx.createImageData(1, 1);
866
+ imgData.data[0] = parsed.r;
867
+ imgData.data[1] = parsed.g;
868
+ imgData.data[2] = parsed.b;
869
+ imgData.data[3] = Math.round(parsed.a * 255);
870
+ ctx.putImageData(imgData, fx, fy);
871
+ }
872
+ }
873
+ },
874
+ // Offscreen graphics (v1.2)
875
+ createGraphics: (w, h) => {
876
+ const offscreenCanvas = document.createElement("canvas");
877
+ offscreenCanvas.width = w;
878
+ offscreenCanvas.height = h;
879
+ const pg = createP5Runtime(offscreenCanvas, w, h, config);
880
+ pg._canvas = offscreenCanvas;
881
+ return pg;
882
+ },
883
+ // Draw image or graphics object to canvas
884
+ image: (src, x, y, w, h) => {
885
+ const srcCanvas = src._canvas || src;
886
+ if (srcCanvas instanceof HTMLCanvasElement) {
887
+ const dw = w ?? srcCanvas.width;
888
+ const dh = h ?? srcCanvas.height;
889
+ ctx.drawImage(srcCanvas, x, y, dw, dh);
890
+ }
891
+ },
892
+ // Loop control (no-ops for SDK)
893
+ noLoop: () => {
894
+ },
895
+ loop: () => {
896
+ },
897
+ redraw: () => {
898
+ },
899
+ frameRate: (fps) => {
900
+ },
901
+ // totalFrames placeholder (injected by engine)
902
+ totalFrames: 0
903
+ };
904
+ return p;
905
+ }
906
+ function injectTimeVariables(p, time) {
907
+ p.frameCount = time.frameCount;
908
+ p.t = time.t;
909
+ p.time = time.time;
910
+ p.tGlobal = time.tGlobal;
911
+ p.totalFrames = time.totalFrames;
912
+ }
913
+ var VAR_COUNT = 10;
914
+ var VAR_MIN = 0;
915
+ var VAR_MAX = 100;
916
+ function createProtocolVAR(vars) {
917
+ const normalizedVars = [];
918
+ for (let i = 0; i < VAR_COUNT; i++) {
919
+ normalizedVars[i] = vars?.[i] ?? 0;
920
+ }
921
+ const frozenVars = Object.freeze(normalizedVars);
922
+ return new Proxy(frozenVars, {
923
+ set(_target, prop, _value) {
924
+ const propName = typeof prop === "symbol" ? prop.toString() : prop;
925
+ throw new Error(
926
+ `[Code Mode Protocol Error] VAR is read-only. Cannot write to VAR[${propName}]. VAR[0..9] are protocol inputs, not sketch state.`
927
+ );
928
+ },
929
+ deleteProperty(_target, prop) {
930
+ const propName = typeof prop === "symbol" ? prop.toString() : prop;
931
+ throw new Error(
932
+ `[Code Mode Protocol Error] VAR is read-only. Cannot delete VAR[${propName}].`
933
+ );
934
+ },
935
+ defineProperty(_target, prop) {
936
+ const propName = typeof prop === "symbol" ? prop.toString() : prop;
937
+ throw new Error(
938
+ `[Code Mode Protocol Error] Cannot define new VAR properties. VAR is fixed at 10 elements (VAR[0..9]). Attempted: ${propName}`
939
+ );
940
+ }
941
+ });
942
+ }
943
+ function injectProtocolVariables(p, vars) {
944
+ p.VAR = createProtocolVAR(vars);
945
+ }
946
+
947
+ // execution-sandbox.ts
948
+ function createForbiddenStub(name) {
949
+ const stub = function() {
950
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
951
+ };
952
+ return new Proxy(stub, {
953
+ get(_target, prop) {
954
+ if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
955
+ return () => {
956
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
957
+ };
958
+ }
959
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}.${String(prop)}`);
960
+ },
961
+ apply() {
962
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}()`);
963
+ },
964
+ construct() {
965
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: new ${name}()`);
966
+ },
967
+ set() {
968
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name} (assignment blocked)`);
969
+ },
970
+ has() {
971
+ return true;
972
+ }
973
+ });
974
+ }
975
+ function createForbiddenObject(name) {
976
+ return new Proxy({}, {
977
+ get(_target, prop) {
978
+ if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
979
+ return () => {
980
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
981
+ };
982
+ }
983
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}.${String(prop)}`);
984
+ },
985
+ set() {
986
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name} (assignment blocked)`);
987
+ },
988
+ has() {
989
+ return true;
990
+ },
991
+ apply() {
992
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}()`);
993
+ },
994
+ construct() {
995
+ throw new Error(`[Code Mode Protocol Error] Forbidden API: new ${name}()`);
996
+ }
997
+ });
998
+ }
999
+ var FORBIDDEN_APIS = {
1000
+ Date: createForbiddenStub("Date"),
1001
+ performance: createForbiddenObject("performance"),
1002
+ process: createForbiddenObject("process"),
1003
+ navigator: createForbiddenObject("navigator"),
1004
+ globalThis: createForbiddenObject("globalThis"),
1005
+ crypto: createForbiddenObject("crypto"),
1006
+ setTimeout: createForbiddenStub("setTimeout"),
1007
+ setInterval: createForbiddenStub("setInterval"),
1008
+ clearTimeout: createForbiddenStub("clearTimeout"),
1009
+ clearInterval: createForbiddenStub("clearInterval"),
1010
+ requestAnimationFrame: createForbiddenStub("requestAnimationFrame"),
1011
+ cancelAnimationFrame: createForbiddenStub("cancelAnimationFrame"),
1012
+ fetch: createForbiddenStub("fetch"),
1013
+ XMLHttpRequest: createForbiddenStub("XMLHttpRequest"),
1014
+ WebSocket: createForbiddenStub("WebSocket"),
1015
+ document: createForbiddenObject("document"),
1016
+ window: createForbiddenObject("window"),
1017
+ self: createForbiddenObject("self"),
1018
+ top: createForbiddenObject("top"),
1019
+ parent: createForbiddenObject("parent"),
1020
+ frames: createForbiddenObject("frames"),
1021
+ location: createForbiddenObject("location"),
1022
+ history: createForbiddenObject("history"),
1023
+ localStorage: createForbiddenObject("localStorage"),
1024
+ sessionStorage: createForbiddenObject("sessionStorage"),
1025
+ indexedDB: createForbiddenObject("indexedDB"),
1026
+ caches: createForbiddenObject("caches"),
1027
+ Notification: createForbiddenStub("Notification"),
1028
+ Worker: createForbiddenStub("Worker"),
1029
+ SharedWorker: createForbiddenStub("SharedWorker"),
1030
+ ServiceWorker: createForbiddenObject("ServiceWorker"),
1031
+ Blob: createForbiddenStub("Blob"),
1032
+ File: createForbiddenStub("File"),
1033
+ FileReader: createForbiddenStub("FileReader"),
1034
+ URL: createForbiddenStub("URL"),
1035
+ URLSearchParams: createForbiddenStub("URLSearchParams"),
1036
+ Headers: createForbiddenStub("Headers"),
1037
+ Request: createForbiddenStub("Request"),
1038
+ Response: createForbiddenStub("Response"),
1039
+ EventSource: createForbiddenStub("EventSource"),
1040
+ Image: createForbiddenStub("Image"),
1041
+ Audio: createForbiddenStub("Audio"),
1042
+ Video: createForbiddenStub("Video"),
1043
+ eval: createForbiddenStub("eval"),
1044
+ Function: createForbiddenStub("Function")
1045
+ };
1046
+ function createSafeMath() {
1047
+ const safeMath = Object.create(Math);
1048
+ Object.defineProperty(safeMath, "random", {
1049
+ get() {
1050
+ throw new Error("[Code Mode Protocol Error] Forbidden API: Math.random() \u2014 use random() instead (seeded)");
1051
+ },
1052
+ configurable: false,
1053
+ enumerable: true
1054
+ });
1055
+ return Object.freeze(safeMath);
1056
+ }
1057
+
1058
+ // static-engine.ts
1059
+ var nodeCanvasModule = null;
1060
+ async function getNodeCanvas() {
1061
+ if (nodeCanvasModule) return nodeCanvasModule;
1062
+ if (typeof window === "undefined") {
1063
+ try {
1064
+ const { createRequire } = await import('module');
1065
+ const require2 = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('core.cjs', document.baseURI).href)));
1066
+ nodeCanvasModule = require2("canvas");
1067
+ return nodeCanvasModule;
1068
+ } catch {
1069
+ return null;
1070
+ }
1071
+ }
1072
+ return null;
1073
+ }
1074
+ async function createRuntimeCanvas(width, height) {
1075
+ if (typeof document !== "undefined" && typeof document.createElement === "function") {
1076
+ const canvas = document.createElement("canvas");
1077
+ canvas.width = width;
1078
+ canvas.height = height;
1079
+ return canvas;
1080
+ }
1081
+ const nodeCanvas = await getNodeCanvas();
1082
+ if (nodeCanvas && nodeCanvas.createCanvas) {
1083
+ return nodeCanvas.createCanvas(width, height);
1084
+ }
1085
+ throw new Error(
1086
+ "[Code Mode Protocol Error] Headless canvas unavailable. Install `canvas` for oracle execution."
1087
+ );
1088
+ }
1089
+ async function runStaticMode(config, options) {
1090
+ const { code, seed, vars, onPreview, onProgress, onComplete, onError, returnImageData } = options;
1091
+ const width = config.width ?? DEFAULT_CONFIG.width;
1092
+ const height = config.height ?? DEFAULT_CONFIG.height;
1093
+ try {
1094
+ onProgress?.({
1095
+ phase: "setup",
1096
+ percent: 0,
1097
+ message: "Initializing canvas..."
1098
+ });
1099
+ const canvas = await createRuntimeCanvas(width, height);
1100
+ const p = createP5Runtime(canvas, width, height, { seed });
1101
+ injectTimeVariables(p, {
1102
+ frameCount: 0,
1103
+ t: 0,
1104
+ time: 0,
1105
+ tGlobal: 0,
1106
+ totalFrames: 1
1107
+ // Static mode has 1 frame
1108
+ });
1109
+ injectProtocolVariables(p, vars);
1110
+ onProgress?.({
1111
+ phase: "setup",
1112
+ percent: 10,
1113
+ message: "Parsing code..."
1114
+ });
1115
+ const setupMatch = code.match(/function\s+setup\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
1116
+ const setupCode = setupMatch ? setupMatch[1].trim() : code;
1117
+ const forbiddenPatterns = ["setTimeout", "setInterval", "requestAnimationFrame"];
1118
+ for (const pattern of forbiddenPatterns) {
1119
+ if (code.includes(pattern)) {
1120
+ throw new Error(`Forbidden async timing function: ${pattern}`);
1121
+ }
1122
+ }
1123
+ onProgress?.({
1124
+ phase: "rendering",
1125
+ percent: 30,
1126
+ message: "Executing setup()..."
1127
+ });
1128
+ const safeMath = createSafeMath();
1129
+ const forbiddenKeys = Object.keys(FORBIDDEN_APIS);
1130
+ const wrappedSetup = new Function(
1131
+ "p",
1132
+ "frameCount",
1133
+ "t",
1134
+ "time",
1135
+ "tGlobal",
1136
+ "VAR",
1137
+ "Math",
1138
+ ...forbiddenKeys,
1139
+ `with(p) { ${setupCode} }`
1140
+ );
1141
+ const forbiddenValues = forbiddenKeys.map((k) => FORBIDDEN_APIS[k]);
1142
+ wrappedSetup(p, 0, 0, 0, 0, p.VAR, safeMath, ...forbiddenValues);
1143
+ onPreview?.(canvas);
1144
+ onProgress?.({
1145
+ phase: "encoding",
1146
+ percent: 70,
1147
+ message: returnImageData ? "Capturing ImageData..." : "Capturing PNG..."
1148
+ });
1149
+ const ctx = canvas.getContext("2d");
1150
+ if (!ctx) {
1151
+ throw new Error("Failed to acquire 2D context");
1152
+ }
1153
+ const imageData = ctx.getImageData(0, 0, width, height);
1154
+ if (returnImageData) {
1155
+ onProgress?.({
1156
+ phase: "complete",
1157
+ percent: 100,
1158
+ message: "Complete"
1159
+ });
1160
+ onComplete({
1161
+ type: "image",
1162
+ imageData
1163
+ });
1164
+ return;
1165
+ }
1166
+ const blob = await new Promise((resolve, reject) => {
1167
+ canvas.toBlob(
1168
+ (b) => b ? resolve(b) : reject(new Error("Failed to capture PNG")),
1169
+ "image/png"
1170
+ );
1171
+ });
1172
+ onProgress?.({
1173
+ phase: "complete",
1174
+ percent: 100,
1175
+ message: "Complete"
1176
+ });
1177
+ onComplete({
1178
+ type: "image",
1179
+ blob
1180
+ });
1181
+ } catch (error) {
1182
+ const err = error instanceof Error ? error : new Error(String(error));
1183
+ onError?.(err);
1184
+ }
1185
+ }
1186
+
1187
+ // loop-engine.ts
1188
+ var isCancelled = false;
1189
+ function cancelLoopMode() {
1190
+ isCancelled = true;
1191
+ }
1192
+ async function runLoopMode(config, options) {
1193
+ const { code, seed, vars, onPreview, onProgress, onComplete, onError } = options;
1194
+ const width = config.width ?? DEFAULT_CONFIG.width;
1195
+ const height = config.height ?? DEFAULT_CONFIG.height;
1196
+ const duration = Math.max(
1197
+ DEFAULT_CONFIG.minDuration,
1198
+ Math.min(DEFAULT_CONFIG.maxDuration, config.duration ?? DEFAULT_CONFIG.duration)
1199
+ );
1200
+ const fps = config.fps ?? DEFAULT_CONFIG.fps;
1201
+ const totalFrames = Math.floor(duration * fps);
1202
+ isCancelled = false;
1203
+ try {
1204
+ onProgress?.({
1205
+ phase: "setup",
1206
+ percent: 0,
1207
+ message: "Initializing canvas..."
1208
+ });
1209
+ const canvas = document.createElement("canvas");
1210
+ canvas.width = width;
1211
+ canvas.height = height;
1212
+ const p = createP5Runtime(canvas, width, height, { seed });
1213
+ injectProtocolVariables(p, vars);
1214
+ const hasDrawFunction = /function\s+draw\s*\(\s*\)/.test(code);
1215
+ if (!hasDrawFunction) {
1216
+ throw new Error("Loop Mode requires a draw() function.");
1217
+ }
1218
+ const forbiddenPatterns = [
1219
+ { pattern: /noLoop\s*\(\s*\)/, name: "noLoop()" },
1220
+ { pattern: /setTimeout\s*\(/, name: "setTimeout" },
1221
+ { pattern: /setInterval\s*\(/, name: "setInterval" },
1222
+ { pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" }
1223
+ ];
1224
+ for (const { pattern, name } of forbiddenPatterns) {
1225
+ if (pattern.test(code)) {
1226
+ throw new Error(`Forbidden function in Loop Mode: ${name}`);
1227
+ }
1228
+ }
1229
+ onProgress?.({
1230
+ phase: "setup",
1231
+ percent: 5,
1232
+ message: "Parsing code..."
1233
+ });
1234
+ const setupMatch = code.match(/function\s+setup\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
1235
+ const drawMatch = code.match(/function\s+draw\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
1236
+ const setupCode = setupMatch ? setupMatch[1].trim() : "";
1237
+ const drawCode = drawMatch ? drawMatch[1].trim() : "";
1238
+ if (!drawCode) {
1239
+ throw new Error("Loop Mode requires a draw() function with content.");
1240
+ }
1241
+ p.totalFrames = totalFrames;
1242
+ const safeMath = createSafeMath();
1243
+ const forbiddenKeys = Object.keys(FORBIDDEN_APIS);
1244
+ const wrappedSetup = new Function(
1245
+ "p",
1246
+ "frameCount",
1247
+ "t",
1248
+ "time",
1249
+ "tGlobal",
1250
+ "VAR",
1251
+ "totalFrames",
1252
+ "Math",
1253
+ ...forbiddenKeys,
1254
+ `with(p) { ${setupCode} }`
1255
+ );
1256
+ const wrappedDraw = new Function(
1257
+ "p",
1258
+ "frameCount",
1259
+ "t",
1260
+ "time",
1261
+ "tGlobal",
1262
+ "VAR",
1263
+ "totalFrames",
1264
+ "Math",
1265
+ ...forbiddenKeys,
1266
+ `with(p) { ${drawCode} }`
1267
+ );
1268
+ const forbiddenValues = forbiddenKeys.map((k) => FORBIDDEN_APIS[k]);
1269
+ onProgress?.({
1270
+ phase: "setup",
1271
+ percent: 10,
1272
+ message: "Executing setup()..."
1273
+ });
1274
+ wrappedSetup(p, 0, 0, 0, 0, p.VAR, totalFrames, safeMath, ...forbiddenValues);
1275
+ const frames = [];
1276
+ onProgress?.({
1277
+ phase: "rendering",
1278
+ frame: 0,
1279
+ totalFrames,
1280
+ percent: 10,
1281
+ message: `Rendering frames (0/${totalFrames})...`
1282
+ });
1283
+ for (let frame = 0; frame < totalFrames; frame++) {
1284
+ if (isCancelled) {
1285
+ throw new Error("Rendering cancelled");
1286
+ }
1287
+ const t = frame / totalFrames;
1288
+ const time = t * duration;
1289
+ p.frameCount = frame;
1290
+ p.clear();
1291
+ p.blendMode("NORMAL");
1292
+ wrappedDraw(p, frame, t, time, t, p.VAR, totalFrames, safeMath, ...forbiddenValues);
1293
+ const blob = await new Promise((resolve, reject) => {
1294
+ canvas.toBlob(
1295
+ (b) => b ? resolve(b) : reject(new Error(`Failed to capture frame ${frame}`)),
1296
+ "image/png"
1297
+ );
1298
+ });
1299
+ frames.push(blob);
1300
+ if (frame === 0) {
1301
+ onPreview?.(canvas);
1302
+ }
1303
+ const percent = 10 + Math.floor(frame / totalFrames * 60);
1304
+ onProgress?.({
1305
+ phase: "rendering",
1306
+ frame: frame + 1,
1307
+ totalFrames,
1308
+ percent,
1309
+ message: `Rendering frames (${frame + 1}/${totalFrames})...`
1310
+ });
1311
+ if (frame % 10 === 0) {
1312
+ await new Promise((resolve) => setTimeout(resolve, 0));
1313
+ }
1314
+ }
1315
+ onProgress?.({
1316
+ phase: "encoding",
1317
+ frame: totalFrames,
1318
+ totalFrames,
1319
+ percent: 70,
1320
+ message: "Encoding video..."
1321
+ });
1322
+ const videoBlob = await encodeFramesToMP4(frames, fps, width, height, (progress) => {
1323
+ const percent = 70 + Math.floor(progress * 30);
1324
+ onProgress?.({
1325
+ phase: "encoding",
1326
+ frame: totalFrames,
1327
+ totalFrames,
1328
+ percent,
1329
+ message: `Encoding video (${Math.floor(progress * 100)}%)...`
1330
+ });
1331
+ });
1332
+ onProgress?.({
1333
+ phase: "complete",
1334
+ frame: totalFrames,
1335
+ totalFrames,
1336
+ percent: 100,
1337
+ message: "Complete"
1338
+ });
1339
+ const result = {
1340
+ type: "video",
1341
+ blob: videoBlob,
1342
+ frames: totalFrames,
1343
+ duration
1344
+ };
1345
+ onComplete(result);
1346
+ } catch (error) {
1347
+ const err = error instanceof Error ? error : new Error(String(error));
1348
+ onError?.(err);
1349
+ }
1350
+ }
1351
+ async function encodeFramesToMP4(frames, fps, width, height, onProgress) {
1352
+ const frameDataUrls = [];
1353
+ for (let i = 0; i < frames.length; i++) {
1354
+ const reader = new FileReader();
1355
+ const dataUrl = await new Promise((resolve, reject) => {
1356
+ reader.onload = () => resolve(reader.result);
1357
+ reader.onerror = reject;
1358
+ reader.readAsDataURL(frames[i]);
1359
+ });
1360
+ frameDataUrls.push(dataUrl);
1361
+ onProgress?.(i / frames.length * 0.3);
1362
+ }
1363
+ const response = await fetch("/api/encode-loop", {
1364
+ method: "POST",
1365
+ headers: { "Content-Type": "application/json" },
1366
+ body: JSON.stringify({
1367
+ frames: frameDataUrls,
1368
+ fps,
1369
+ width,
1370
+ height
1371
+ })
1372
+ });
1373
+ if (!response.ok) {
1374
+ const errorText = await response.text();
1375
+ throw new Error(`Video encoding failed: ${errorText}`);
1376
+ }
1377
+ onProgress?.(0.8);
1378
+ const data = await response.json();
1379
+ if (!data.video) {
1380
+ throw new Error("No video data returned from encoder");
1381
+ }
1382
+ const binaryString = atob(data.video.split(",")[1] || data.video);
1383
+ const bytes = new Uint8Array(binaryString.length);
1384
+ for (let i = 0; i < binaryString.length; i++) {
1385
+ bytes[i] = binaryString.charCodeAt(i);
1386
+ }
1387
+ onProgress?.(1);
1388
+ return new Blob([bytes], { type: "video/mp4" });
1389
+ }
1390
+
1391
+ // execute.ts
1392
+ function normalizeVars(vars) {
1393
+ if (!vars || !Array.isArray(vars)) {
1394
+ console.log("[CodeMode] No vars provided, using defaults [0,0,0,0,0,0,0,0,0,0]");
1395
+ return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1396
+ }
1397
+ if (vars.length > 10) {
1398
+ throw new Error(`[Code Mode Protocol Error] VAR array must have at most 10 elements, got ${vars.length}`);
1399
+ }
1400
+ const result = [];
1401
+ for (let i = 0; i < vars.length; i++) {
1402
+ const v = vars[i];
1403
+ if (typeof v !== "number" || !Number.isFinite(v)) {
1404
+ throw new Error(`[Code Mode Protocol Error] VAR[${i}] must be a finite number, got ${typeof v === "number" ? v : typeof v}`);
1405
+ }
1406
+ if (v < 0 || v > 100) {
1407
+ throw new Error(`[Code Mode Protocol Error] VAR[${i}] = ${v} is out of range. Values must be 0-100.`);
1408
+ }
1409
+ result.push(v);
1410
+ }
1411
+ while (result.length < 10) {
1412
+ result.push(0);
1413
+ }
1414
+ return result;
1415
+ }
1416
+ function validateInput(input) {
1417
+ if (!input.source || typeof input.source !== "string") {
1418
+ throw new Error("[Code Mode Protocol Error] source is required and must be a string");
1419
+ }
1420
+ if (typeof input.width !== "number" || input.width <= 0) {
1421
+ throw new Error("[Code Mode Protocol Error] width must be a positive number");
1422
+ }
1423
+ if (typeof input.height !== "number" || input.height <= 0) {
1424
+ throw new Error("[Code Mode Protocol Error] height must be a positive number");
1425
+ }
1426
+ if (typeof input.seed !== "number") {
1427
+ throw new Error("[Code Mode Protocol Error] seed is required and must be a number");
1428
+ }
1429
+ if (input.mode !== "static" && input.mode !== "loop") {
1430
+ throw new Error('[Code Mode Protocol Error] mode must be "static" or "loop"');
1431
+ }
1432
+ if (input.mode === "loop") {
1433
+ if (typeof input.totalFrames !== "number" || input.totalFrames <= 0) {
1434
+ throw new Error("[Code Mode Protocol Error] totalFrames is required for loop mode and must be a positive number");
1435
+ }
1436
+ }
1437
+ const forbiddenPatterns = [
1438
+ // Async timing (breaks determinism)
1439
+ { pattern: /setTimeout\s*\(/, name: "setTimeout" },
1440
+ { pattern: /setInterval\s*\(/, name: "setInterval" },
1441
+ { pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" },
1442
+ // Time-based entropy (breaks determinism)
1443
+ { pattern: /Date\.now\s*\(/, name: "Date.now() \u2014 use time variable instead" },
1444
+ { pattern: /new\s+Date\s*\(/, name: "new Date() \u2014 use time variable instead" },
1445
+ // Unseeded random (use random() instead)
1446
+ { pattern: /Math\.random\s*\(/, name: "Math.random() \u2014 use random() instead (seeded)" },
1447
+ // External IO (breaks determinism)
1448
+ { pattern: /fetch\s*\(/, name: "fetch() \u2014 external IO forbidden" },
1449
+ { pattern: /XMLHttpRequest/, name: "XMLHttpRequest \u2014 external IO forbidden" },
1450
+ // Canvas is pre-initialized
1451
+ { pattern: /createCanvas\s*\(/, name: "createCanvas() \u2014 canvas is pre-initialized" },
1452
+ // DOM manipulation forbidden
1453
+ { pattern: /document\./, name: "DOM access \u2014 document.* forbidden" },
1454
+ { pattern: /window\./, name: "DOM access \u2014 window.* forbidden" },
1455
+ // External imports forbidden
1456
+ { pattern: /\bimport\s+/, name: "import \u2014 external imports forbidden" },
1457
+ { pattern: /\brequire\s*\(/, name: "require() \u2014 external imports forbidden" }
1458
+ ];
1459
+ for (const { pattern, name } of forbiddenPatterns) {
1460
+ if (pattern.test(input.source)) {
1461
+ throw new Error(`[Code Mode Protocol Error] Forbidden pattern: ${name}`);
1462
+ }
1463
+ }
1464
+ if (input.mode === "loop") {
1465
+ if (!/function\s+draw\s*\(\s*\)/.test(input.source)) {
1466
+ throw new Error("[Code Mode Protocol Error] Loop mode requires a draw() function");
1467
+ }
1468
+ if (/noLoop\s*\(\s*\)/.test(input.source)) {
1469
+ throw new Error("[Code Mode Protocol Error] noLoop() is forbidden in Loop mode");
1470
+ }
1471
+ }
1472
+ }
1473
+ function createMetadata(input, vars) {
1474
+ return {
1475
+ ...PROTOCOL_IDENTITY,
1476
+ seed: input.seed,
1477
+ vars,
1478
+ width: input.width,
1479
+ height: input.height,
1480
+ mode: input.mode,
1481
+ ...input.mode === "loop" && input.totalFrames ? { totalFrames: input.totalFrames } : {}
1482
+ };
1483
+ }
1484
+ async function executeStatic(input, vars) {
1485
+ console.log("[CodeMode] Rendered via @nexart/codemode-sdk (Protocol v1.2.0)");
1486
+ console.log("[CodeMode] Execution: Static mode \u2014 delegating to static-engine");
1487
+ return new Promise((resolve, reject) => {
1488
+ runStaticMode(
1489
+ {
1490
+ width: input.width,
1491
+ height: input.height
1492
+ },
1493
+ {
1494
+ code: input.source,
1495
+ seed: input.seed,
1496
+ vars,
1497
+ onComplete: (result) => {
1498
+ resolve({
1499
+ image: "blob" in result ? result.blob : void 0,
1500
+ frames: "imageData" in result ? [result.imageData] : void 0,
1501
+ metadata: createMetadata(input, vars)
1502
+ });
1503
+ },
1504
+ onError: (error) => {
1505
+ reject(error);
1506
+ }
1507
+ }
1508
+ );
1509
+ });
1510
+ }
1511
+ async function executeLoop(input, vars) {
1512
+ console.log("[CodeMode] Rendered via @nexart/codemode-sdk (Protocol v1.2.0)");
1513
+ console.log(`[CodeMode] Execution: Loop mode \u2014 delegating to loop-engine (${input.totalFrames} frames)`);
1514
+ const fps = DEFAULT_CONFIG.fps;
1515
+ const duration = (input.totalFrames || 60) / fps;
1516
+ return new Promise((resolve, reject) => {
1517
+ runLoopMode(
1518
+ {
1519
+ width: input.width,
1520
+ height: input.height,
1521
+ duration,
1522
+ fps
1523
+ },
1524
+ {
1525
+ code: input.source,
1526
+ seed: input.seed,
1527
+ vars,
1528
+ onComplete: (result) => {
1529
+ resolve({
1530
+ video: "blob" in result && result.type === "video" ? result.blob : void 0,
1531
+ metadata: createMetadata(input, vars)
1532
+ });
1533
+ },
1534
+ onError: (error) => {
1535
+ reject(error);
1536
+ }
1537
+ }
1538
+ );
1539
+ });
1540
+ }
1541
+ async function executeCodeMode(input) {
1542
+ validateInput(input);
1543
+ const vars = normalizeVars(input.vars);
1544
+ console.log("[CodeMode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
1545
+ console.log("[CodeMode] Protocol v1.2.0 \u2014 Phase 3 \u2014 HARD Enforcement");
1546
+ console.log(`[CodeMode] Mode: ${input.mode}`);
1547
+ console.log(`[CodeMode] Seed: ${input.seed}`);
1548
+ console.log(`[CodeMode] VAR: [${vars.join(", ")}]`);
1549
+ console.log("[CodeMode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
1550
+ if (input.mode === "static") {
1551
+ return executeStatic(input, vars);
1552
+ } else {
1553
+ return executeLoop(input, vars);
1554
+ }
1555
+ }
1556
+ function validateCodeModeSource(source, mode) {
1557
+ const errors = [];
1558
+ const forbiddenPatterns = [
1559
+ { pattern: /setTimeout\s*\(/, name: "setTimeout" },
1560
+ { pattern: /setInterval\s*\(/, name: "setInterval" },
1561
+ { pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" },
1562
+ { pattern: /Date\.now\s*\(/, name: "Date.now() \u2014 use time variable instead" },
1563
+ { pattern: /new\s+Date\s*\(/, name: "new Date() \u2014 use time variable instead" },
1564
+ { pattern: /Math\.random\s*\(/, name: "Math.random() \u2014 use random() instead (seeded)" },
1565
+ { pattern: /fetch\s*\(/, name: "fetch() \u2014 external IO forbidden" },
1566
+ { pattern: /XMLHttpRequest/, name: "XMLHttpRequest \u2014 external IO forbidden" },
1567
+ { pattern: /createCanvas\s*\(/, name: "createCanvas() \u2014 canvas is pre-initialized" },
1568
+ { pattern: /document\./, name: "DOM access \u2014 document.* forbidden" },
1569
+ { pattern: /window\./, name: "DOM access \u2014 window.* forbidden" },
1570
+ { pattern: /\bimport\s+/, name: "import \u2014 external imports forbidden" },
1571
+ { pattern: /\brequire\s*\(/, name: "require() \u2014 external imports forbidden" }
1572
+ ];
1573
+ for (const { pattern, name } of forbiddenPatterns) {
1574
+ if (pattern.test(source)) {
1575
+ errors.push(`Forbidden pattern: ${name}`);
1576
+ }
1577
+ }
1578
+ if (mode === "loop") {
1579
+ if (!/function\s+draw\s*\(\s*\)/.test(source)) {
1580
+ errors.push("Loop mode requires a draw() function");
1581
+ }
1582
+ if (/noLoop\s*\(\s*\)/.test(source)) {
1583
+ errors.push("noLoop() is forbidden in Loop mode");
1584
+ }
1585
+ }
1586
+ return {
1587
+ valid: errors.length === 0,
1588
+ errors
1589
+ };
1590
+ }
1591
+
1592
+ // engine.ts
1593
+ function createEngine(config) {
1594
+ const resolvedConfig = {
1595
+ mode: config.mode,
1596
+ width: config.width ?? DEFAULT_CONFIG.width,
1597
+ height: config.height ?? DEFAULT_CONFIG.height,
1598
+ duration: config.duration ?? DEFAULT_CONFIG.duration,
1599
+ fps: config.fps ?? DEFAULT_CONFIG.fps
1600
+ };
1601
+ let isRunning = false;
1602
+ const run = async (options) => {
1603
+ if (isRunning) {
1604
+ throw new Error("Engine is already running. Call stop() first.");
1605
+ }
1606
+ isRunning = true;
1607
+ try {
1608
+ if (resolvedConfig.mode === "static") {
1609
+ await runStaticMode(resolvedConfig, options);
1610
+ } else if (resolvedConfig.mode === "loop") {
1611
+ await runLoopMode(resolvedConfig, options);
1612
+ } else {
1613
+ throw new Error(`Unknown mode: ${resolvedConfig.mode}`);
1614
+ }
1615
+ } finally {
1616
+ isRunning = false;
1617
+ }
1618
+ };
1619
+ const stop = () => {
1620
+ if (resolvedConfig.mode === "loop") {
1621
+ cancelLoopMode();
1622
+ }
1623
+ isRunning = false;
1624
+ };
1625
+ const getConfig = () => {
1626
+ return { ...resolvedConfig };
1627
+ };
1628
+ return {
1629
+ run,
1630
+ stop,
1631
+ getConfig
1632
+ };
1633
+ }
1634
+
1635
+ // core-index.ts
1636
+ var SDK_VERSION2 = SDK_VERSION;
1637
+ var SDK_NAME = "@nexart/codemode-sdk";
1638
+
1639
+ exports.CODE_MODE_ENFORCEMENT = CODE_MODE_ENFORCEMENT;
1640
+ exports.CODE_MODE_PROTOCOL_PHASE = CODE_MODE_PROTOCOL_PHASE;
1641
+ exports.CODE_MODE_PROTOCOL_VERSION = CODE_MODE_PROTOCOL_VERSION;
1642
+ exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
1643
+ exports.DEFAULT_VARS = DEFAULT_VARS;
1644
+ exports.PROTOCOL_IDENTITY = PROTOCOL_IDENTITY;
1645
+ exports.PROTOCOL_PHASE = PROTOCOL_PHASE;
1646
+ exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
1647
+ exports.SDK_NAME = SDK_NAME;
1648
+ exports.SDK_VERSION = SDK_VERSION2;
1649
+ exports.VAR_COUNT = VAR_COUNT;
1650
+ exports.VAR_MAX = VAR_MAX;
1651
+ exports.VAR_MIN = VAR_MIN;
1652
+ exports.cancelLoopMode = cancelLoopMode;
1653
+ exports.createEngine = createEngine;
1654
+ exports.createP5Runtime = createP5Runtime;
1655
+ exports.createProtocolVAR = createProtocolVAR;
1656
+ exports.executeCodeMode = executeCodeMode;
1657
+ exports.injectTimeVariables = injectTimeVariables;
1658
+ exports.runLoopMode = runLoopMode;
1659
+ exports.runStaticMode = runStaticMode;
1660
+ exports.validateCodeModeSource = validateCodeModeSource;