@nexart/codemode-sdk 1.1.1 → 1.5.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 (91) hide show
  1. package/README.md +131 -238
  2. package/dist/engine.d.ts +39 -17
  3. package/dist/engine.d.ts.map +1 -1
  4. package/dist/engine.js +253 -52
  5. package/dist/index.d.ts +17 -24
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +16 -30
  8. package/dist/loop-engine.d.ts +2 -1
  9. package/dist/loop-engine.d.ts.map +1 -1
  10. package/dist/loop-engine.js +16 -10
  11. package/dist/p5-runtime.d.ts +3 -3
  12. package/dist/p5-runtime.d.ts.map +1 -1
  13. package/dist/p5-runtime.js +398 -13
  14. package/dist/static-engine.d.ts +1 -1
  15. package/dist/static-engine.d.ts.map +1 -1
  16. package/dist/static-engine.js +3 -2
  17. package/dist/types.d.ts +6 -5
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/types.js +3 -3
  20. package/package.json +17 -23
  21. package/CHANGELOG.md +0 -109
  22. package/CODE_MODE_PROTOCOL.md +0 -312
  23. package/dist/core-index.d.ts +0 -21
  24. package/dist/core-index.d.ts.map +0 -1
  25. package/dist/core-index.js +0 -26
  26. package/dist/execute.d.ts +0 -46
  27. package/dist/execute.d.ts.map +0 -1
  28. package/dist/execute.js +0 -268
  29. package/dist/noise-bridge.d.ts +0 -44
  30. package/dist/noise-bridge.d.ts.map +0 -1
  31. package/dist/noise-bridge.js +0 -68
  32. package/dist/noise-engine.d.ts +0 -74
  33. package/dist/noise-engine.d.ts.map +0 -1
  34. package/dist/noise-engine.js +0 -132
  35. package/dist/noise-sketches/fractalNoise.d.ts +0 -11
  36. package/dist/noise-sketches/fractalNoise.d.ts.map +0 -1
  37. package/dist/noise-sketches/fractalNoise.js +0 -121
  38. package/dist/noise-sketches/index.d.ts +0 -21
  39. package/dist/noise-sketches/index.d.ts.map +0 -1
  40. package/dist/noise-sketches/index.js +0 -28
  41. package/dist/sound-bridge.d.ts +0 -89
  42. package/dist/sound-bridge.d.ts.map +0 -1
  43. package/dist/sound-bridge.js +0 -128
  44. package/dist/soundart-engine.d.ts +0 -87
  45. package/dist/soundart-engine.d.ts.map +0 -1
  46. package/dist/soundart-engine.js +0 -173
  47. package/dist/soundart-sketches/chladniBloom.d.ts +0 -3
  48. package/dist/soundart-sketches/chladniBloom.d.ts.map +0 -1
  49. package/dist/soundart-sketches/chladniBloom.js +0 -53
  50. package/dist/soundart-sketches/dualVortex.d.ts +0 -3
  51. package/dist/soundart-sketches/dualVortex.d.ts.map +0 -1
  52. package/dist/soundart-sketches/dualVortex.js +0 -67
  53. package/dist/soundart-sketches/geometryIllusion.d.ts +0 -3
  54. package/dist/soundart-sketches/geometryIllusion.d.ts.map +0 -1
  55. package/dist/soundart-sketches/geometryIllusion.js +0 -89
  56. package/dist/soundart-sketches/index.d.ts +0 -39
  57. package/dist/soundart-sketches/index.d.ts.map +0 -1
  58. package/dist/soundart-sketches/index.js +0 -72
  59. package/dist/soundart-sketches/isoflow.d.ts +0 -3
  60. package/dist/soundart-sketches/isoflow.d.ts.map +0 -1
  61. package/dist/soundart-sketches/isoflow.js +0 -60
  62. package/dist/soundart-sketches/loomWeave.d.ts +0 -3
  63. package/dist/soundart-sketches/loomWeave.d.ts.map +0 -1
  64. package/dist/soundart-sketches/loomWeave.js +0 -59
  65. package/dist/soundart-sketches/noiseTerraces.d.ts +0 -3
  66. package/dist/soundart-sketches/noiseTerraces.d.ts.map +0 -1
  67. package/dist/soundart-sketches/noiseTerraces.js +0 -53
  68. package/dist/soundart-sketches/orb.d.ts +0 -3
  69. package/dist/soundart-sketches/orb.d.ts.map +0 -1
  70. package/dist/soundart-sketches/orb.js +0 -50
  71. package/dist/soundart-sketches/pixelGlyphs.d.ts +0 -3
  72. package/dist/soundart-sketches/pixelGlyphs.d.ts.map +0 -1
  73. package/dist/soundart-sketches/pixelGlyphs.js +0 -72
  74. package/dist/soundart-sketches/prismFlowFields.d.ts +0 -3
  75. package/dist/soundart-sketches/prismFlowFields.d.ts.map +0 -1
  76. package/dist/soundart-sketches/prismFlowFields.js +0 -51
  77. package/dist/soundart-sketches/radialBurst.d.ts +0 -3
  78. package/dist/soundart-sketches/radialBurst.d.ts.map +0 -1
  79. package/dist/soundart-sketches/radialBurst.js +0 -60
  80. package/dist/soundart-sketches/resonantSoundBodies.d.ts +0 -3
  81. package/dist/soundart-sketches/resonantSoundBodies.d.ts.map +0 -1
  82. package/dist/soundart-sketches/resonantSoundBodies.js +0 -89
  83. package/dist/soundart-sketches/rings.d.ts +0 -11
  84. package/dist/soundart-sketches/rings.d.ts.map +0 -1
  85. package/dist/soundart-sketches/rings.js +0 -89
  86. package/dist/soundart-sketches/squares.d.ts +0 -3
  87. package/dist/soundart-sketches/squares.d.ts.map +0 -1
  88. package/dist/soundart-sketches/squares.js +0 -52
  89. package/dist/soundart-sketches/waveStripes.d.ts +0 -3
  90. package/dist/soundart-sketches/waveStripes.d.ts.map +0 -1
  91. package/dist/soundart-sketches/waveStripes.js +0 -44
@@ -2,7 +2,7 @@
2
2
  * NexArt Code Mode Runtime SDK - p5-like Runtime
3
3
  *
4
4
  * ╔══════════════════════════════════════════════════════════════════════════╗
5
- * ║ CODE MODE PROTOCOL v1.0.0 (Phase 1) — LOCKED
5
+ * ║ CODE MODE PROTOCOL v1.2.0 (Phase 3) — STABLE
6
6
  * ║ ║
7
7
  * ║ Status: HARD PROTOCOL ENFORCEMENT ║
8
8
  * ║ This is the stable, canonical execution surface. ║
@@ -32,8 +32,8 @@
32
32
  * This constant defines the locked protocol version.
33
33
  * Changes to the execution surface require a version bump.
34
34
  */
35
- export const CODE_MODE_PROTOCOL_VERSION = '1.0.0';
36
- export const CODE_MODE_PROTOCOL_PHASE = 1;
35
+ export const CODE_MODE_PROTOCOL_VERSION = '1.2.0';
36
+ export const CODE_MODE_PROTOCOL_PHASE = 3;
37
37
  export const CODE_MODE_ENFORCEMENT = 'HARD';
38
38
  /**
39
39
  * Create a seeded random number generator (Mulberry32)
@@ -100,6 +100,15 @@ export function createP5Runtime(canvas, width, height, config) {
100
100
  let currentStrokeWeight = 1;
101
101
  let colorModeSettings = { mode: 'RGB', maxR: 255, maxG: 255, maxB: 255, maxA: 255 };
102
102
  let shapeStarted = false;
103
+ let shapeVertices = [];
104
+ // Pixel array for pixel manipulation
105
+ let pixelData = null;
106
+ let imageDataObj = null;
107
+ // Text state
108
+ let currentTextSize = 12;
109
+ let currentTextFont = 'sans-serif';
110
+ let currentTextAlignH = 'left';
111
+ let currentTextAlignV = 'alphabetic';
103
112
  // Seeded random state
104
113
  let randomSeedValue = config?.seed ?? Math.floor(Math.random() * 2147483647);
105
114
  let rng = createSeededRNG(randomSeedValue);
@@ -248,6 +257,7 @@ export function createP5Runtime(canvas, width, height, config) {
248
257
  // Constants
249
258
  PI: Math.PI,
250
259
  TWO_PI: Math.PI * 2,
260
+ TAU: Math.PI * 2,
251
261
  HALF_PI: Math.PI / 2,
252
262
  QUARTER_PI: Math.PI / 4,
253
263
  // Shape mode constants
@@ -256,11 +266,25 @@ export function createP5Runtime(canvas, width, height, config) {
256
266
  CORNERS: 'corners',
257
267
  RADIUS: 'radius',
258
268
  ROUND: 'round',
259
- SQUARE: 'square',
260
- PROJECT: 'project',
269
+ SQUARE: 'butt',
270
+ PROJECT: 'square',
261
271
  MITER: 'miter',
262
272
  BEVEL: 'bevel',
263
273
  CLOSE: 'close',
274
+ PIE: 'pie',
275
+ CHORD: 'chord',
276
+ OPEN: 'open',
277
+ // Blend mode constants (v1.1)
278
+ NORMAL: 'source-over',
279
+ ADD: 'lighter',
280
+ MULTIPLY: 'multiply',
281
+ SCREEN: 'screen',
282
+ // Text alignment constants
283
+ LEFT: 'left',
284
+ RIGHT: 'right',
285
+ TOP: 'top',
286
+ BOTTOM: 'bottom',
287
+ BASELINE: 'alphabetic',
264
288
  // Canvas operations
265
289
  background: (...args) => {
266
290
  ctx.save();
@@ -271,6 +295,23 @@ export function createP5Runtime(canvas, width, height, config) {
271
295
  clear: () => {
272
296
  ctx.clearRect(0, 0, width, height);
273
297
  },
298
+ blendMode: (mode) => {
299
+ const modeMap = {
300
+ 'source-over': 'source-over',
301
+ 'NORMAL': 'source-over',
302
+ 'lighter': 'lighter',
303
+ 'ADD': 'lighter',
304
+ 'multiply': 'multiply',
305
+ 'MULTIPLY': 'multiply',
306
+ 'screen': 'screen',
307
+ 'SCREEN': 'screen',
308
+ };
309
+ const compositeOp = modeMap[mode];
310
+ if (!compositeOp) {
311
+ throw new Error(`[Code Mode Protocol Error] Unsupported blend mode: ${mode}. Supported: NORMAL, ADD, MULTIPLY, SCREEN`);
312
+ }
313
+ ctx.globalCompositeOperation = compositeOp;
314
+ },
274
315
  // Color functions
275
316
  fill: (...args) => {
276
317
  fillEnabled = true;
@@ -292,6 +333,29 @@ export function createP5Runtime(canvas, width, height, config) {
292
333
  currentStrokeWeight = weight;
293
334
  ctx.lineWidth = weight;
294
335
  },
336
+ strokeCap: (cap) => {
337
+ const capMap = {
338
+ 'round': 'round',
339
+ 'ROUND': 'round',
340
+ 'square': 'butt',
341
+ 'SQUARE': 'butt',
342
+ 'project': 'square',
343
+ 'PROJECT': 'square',
344
+ 'butt': 'butt',
345
+ };
346
+ ctx.lineCap = capMap[cap] || 'round';
347
+ },
348
+ strokeJoin: (join) => {
349
+ const joinMap = {
350
+ 'miter': 'miter',
351
+ 'MITER': 'miter',
352
+ 'bevel': 'bevel',
353
+ 'BEVEL': 'bevel',
354
+ 'round': 'round',
355
+ 'ROUND': 'round',
356
+ };
357
+ ctx.lineJoin = joinMap[join] || 'miter';
358
+ },
295
359
  colorMode: (mode, max1, max2, max3, maxA) => {
296
360
  colorModeSettings = {
297
361
  mode: mode.toUpperCase(),
@@ -451,21 +515,144 @@ export function createP5Runtime(canvas, width, height, config) {
451
515
  if (strokeEnabled)
452
516
  ctx.stroke();
453
517
  },
518
+ // Bezier and curve functions
519
+ bezier: (x1, y1, cx1, cy1, cx2, cy2, x2, y2) => {
520
+ ctx.beginPath();
521
+ ctx.moveTo(x1, y1);
522
+ ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2);
523
+ if (strokeEnabled)
524
+ ctx.stroke();
525
+ },
526
+ curve: (x1, y1, x2, y2, x3, y3, x4, y4) => {
527
+ // Catmull-Rom spline conversion to cubic bezier
528
+ // The curve is drawn from (x2,y2) to (x3,y3) using (x1,y1) and (x4,y4) as control points
529
+ const tension = 1 / 6;
530
+ const cp1x = x2 + (x3 - x1) * tension;
531
+ const cp1y = y2 + (y3 - y1) * tension;
532
+ const cp2x = x3 - (x4 - x2) * tension;
533
+ const cp2y = y3 - (y4 - y2) * tension;
534
+ ctx.beginPath();
535
+ ctx.moveTo(x2, y2);
536
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x3, y3);
537
+ if (strokeEnabled)
538
+ ctx.stroke();
539
+ },
540
+ // Shape helpers (v1.1)
541
+ polygon: (cx, cy, radius, sides, rotation = 0) => {
542
+ ctx.beginPath();
543
+ for (let i = 0; i < sides; i++) {
544
+ const angle = rotation + (i / sides) * Math.PI * 2 - Math.PI / 2;
545
+ const x = cx + Math.cos(angle) * radius;
546
+ const y = cy + Math.sin(angle) * radius;
547
+ if (i === 0) {
548
+ ctx.moveTo(x, y);
549
+ }
550
+ else {
551
+ ctx.lineTo(x, y);
552
+ }
553
+ }
554
+ ctx.closePath();
555
+ if (fillEnabled)
556
+ ctx.fill();
557
+ if (strokeEnabled)
558
+ ctx.stroke();
559
+ },
560
+ star: (cx, cy, innerRadius, outerRadius, points, rotation = 0) => {
561
+ ctx.beginPath();
562
+ const totalPoints = points * 2;
563
+ for (let i = 0; i < totalPoints; i++) {
564
+ const angle = rotation + (i / totalPoints) * Math.PI * 2 - Math.PI / 2;
565
+ const radius = i % 2 === 0 ? outerRadius : innerRadius;
566
+ const x = cx + Math.cos(angle) * radius;
567
+ const y = cy + Math.sin(angle) * radius;
568
+ if (i === 0) {
569
+ ctx.moveTo(x, y);
570
+ }
571
+ else {
572
+ ctx.lineTo(x, y);
573
+ }
574
+ }
575
+ ctx.closePath();
576
+ if (fillEnabled)
577
+ ctx.fill();
578
+ if (strokeEnabled)
579
+ ctx.stroke();
580
+ },
454
581
  // Vertex-based shapes
455
582
  beginShape: () => {
456
- ctx.beginPath();
583
+ shapeVertices = [];
457
584
  shapeStarted = false;
458
585
  },
459
586
  vertex: (x, y) => {
460
- if (!shapeStarted) {
461
- ctx.moveTo(x, y);
462
- shapeStarted = true;
463
- }
464
- else {
465
- ctx.lineTo(x, y);
466
- }
587
+ shapeVertices.push({ x, y, type: 'vertex' });
588
+ },
589
+ curveVertex: (x, y) => {
590
+ shapeVertices.push({ x, y, type: 'curve' });
591
+ },
592
+ bezierVertex: (cx1, cy1, cx2, cy2, x, y) => {
593
+ shapeVertices.push({ x, y, type: 'bezier', cx1, cy1, cx2, cy2 });
467
594
  },
468
595
  endShape: (mode) => {
596
+ if (shapeVertices.length === 0)
597
+ return;
598
+ ctx.beginPath();
599
+ // Process vertices in sequence, handling mixed types correctly
600
+ let started = false;
601
+ let curveBuffer = [];
602
+ const tension = 1 / 6;
603
+ const flushCurveBuffer = () => {
604
+ if (curveBuffer.length >= 4) {
605
+ // Draw Catmull-Rom spline from buffered curve vertices
606
+ // First and last points are control points only
607
+ if (!started) {
608
+ ctx.moveTo(curveBuffer[1].x, curveBuffer[1].y);
609
+ started = true;
610
+ }
611
+ else {
612
+ ctx.lineTo(curveBuffer[1].x, curveBuffer[1].y);
613
+ }
614
+ for (let i = 1; i < curveBuffer.length - 2; i++) {
615
+ const p0 = curveBuffer[i - 1];
616
+ const p1 = curveBuffer[i];
617
+ const p2 = curveBuffer[i + 1];
618
+ const p3 = curveBuffer[i + 2];
619
+ const cp1x = p1.x + (p2.x - p0.x) * tension;
620
+ const cp1y = p1.y + (p2.y - p0.y) * tension;
621
+ const cp2x = p2.x - (p3.x - p1.x) * tension;
622
+ const cp2y = p2.y - (p3.y - p1.y) * tension;
623
+ ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);
624
+ }
625
+ }
626
+ curveBuffer = [];
627
+ };
628
+ for (let i = 0; i < shapeVertices.length; i++) {
629
+ const v = shapeVertices[i];
630
+ if (v.type === 'curve') {
631
+ curveBuffer.push({ x: v.x, y: v.y });
632
+ }
633
+ else {
634
+ // Flush any pending curve vertices before processing non-curve vertex
635
+ if (curveBuffer.length > 0) {
636
+ flushCurveBuffer();
637
+ }
638
+ if (v.type === 'vertex') {
639
+ if (!started) {
640
+ ctx.moveTo(v.x, v.y);
641
+ started = true;
642
+ }
643
+ else {
644
+ ctx.lineTo(v.x, v.y);
645
+ }
646
+ }
647
+ else if (v.type === 'bezier' && started) {
648
+ ctx.bezierCurveTo(v.cx1, v.cy1, v.cx2, v.cy2, v.x, v.y);
649
+ }
650
+ }
651
+ }
652
+ // Flush any remaining curve vertices at the end
653
+ if (curveBuffer.length > 0) {
654
+ flushCurveBuffer();
655
+ }
469
656
  if (mode === 'close' || mode === 'CLOSE') {
470
657
  ctx.closePath();
471
658
  }
@@ -473,6 +660,7 @@ export function createP5Runtime(canvas, width, height, config) {
473
660
  ctx.fill();
474
661
  if (strokeEnabled)
475
662
  ctx.stroke();
663
+ shapeVertices = [];
476
664
  shapeStarted = false;
477
665
  },
478
666
  // Transform functions
@@ -497,6 +685,12 @@ export function createP5Runtime(canvas, width, height, config) {
497
685
  resetMatrix: () => {
498
686
  ctx.setTransform(1, 0, 0, 1, 0, 0);
499
687
  },
688
+ shearX: (angle) => {
689
+ ctx.transform(1, 0, Math.tan(angle), 1, 0, 0);
690
+ },
691
+ shearY: (angle) => {
692
+ ctx.transform(1, Math.tan(angle), 0, 1, 0, 0);
693
+ },
500
694
  // Math functions - SEEDED for determinism
501
695
  random: (min, max) => {
502
696
  // Support random() with arrays
@@ -544,6 +738,47 @@ export function createP5Runtime(canvas, width, height, config) {
544
738
  noiseFalloff = Math.max(0, Math.min(1, falloff));
545
739
  }
546
740
  },
741
+ // Noise extensions (v1.1) - use seeded noise internally
742
+ fbm: (x, y, octaves = 4, falloff = 0.5) => {
743
+ let total = 0;
744
+ let frequency = 1;
745
+ let amplitude = 1;
746
+ let maxValue = 0;
747
+ for (let i = 0; i < octaves; i++) {
748
+ total += noiseFunc(x * frequency, y * frequency) * amplitude;
749
+ maxValue += amplitude;
750
+ amplitude *= falloff;
751
+ frequency *= 2;
752
+ }
753
+ return total / maxValue;
754
+ },
755
+ ridgedNoise: (x, y) => {
756
+ // Ridged noise: absolute value creates ridge-like patterns
757
+ let total = 0;
758
+ let frequency = 1;
759
+ let amplitude = 1;
760
+ let maxValue = 0;
761
+ for (let i = 0; i < 4; i++) {
762
+ const n = noiseFunc(x * frequency, y * frequency);
763
+ total += (1 - Math.abs(n * 2 - 1)) * amplitude;
764
+ maxValue += amplitude;
765
+ amplitude *= 0.5;
766
+ frequency *= 2;
767
+ }
768
+ return total / maxValue;
769
+ },
770
+ curlNoise: (x, y) => {
771
+ // Curl noise: compute gradient and rotate 90 degrees
772
+ const eps = 0.0001;
773
+ const n1 = noiseFunc(x + eps, y);
774
+ const n2 = noiseFunc(x - eps, y);
775
+ const n3 = noiseFunc(x, y + eps);
776
+ const n4 = noiseFunc(x, y - eps);
777
+ const dx = (n1 - n2) / (2 * eps);
778
+ const dy = (n3 - n4) / (2 * eps);
779
+ // Rotate 90 degrees: (dx, dy) -> (-dy, dx)
780
+ return { x: -dy, y: dx };
781
+ },
547
782
  map: (value, start1, stop1, start2, stop2) => {
548
783
  return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
549
784
  },
@@ -583,16 +818,166 @@ export function createP5Runtime(canvas, width, height, config) {
583
818
  log: Math.log,
584
819
  min: Math.min,
585
820
  max: Math.max,
821
+ int: (n) => Math.floor(n),
822
+ sq: (n) => n * n,
823
+ fract: (n) => n - Math.floor(n),
824
+ sign: (n) => n > 0 ? 1 : n < 0 ? -1 : 0,
825
+ // Vector helpers (v1.1) - plain objects, no mutation
826
+ vec: (x, y) => ({ x, y }),
827
+ vecAdd: (a, b) => ({
828
+ x: a.x + b.x,
829
+ y: a.y + b.y
830
+ }),
831
+ vecSub: (a, b) => ({
832
+ x: a.x - b.x,
833
+ y: a.y - b.y
834
+ }),
835
+ vecMult: (v, s) => ({
836
+ x: v.x * s,
837
+ y: v.y * s
838
+ }),
839
+ vecMag: (v) => Math.sqrt(v.x * v.x + v.y * v.y),
840
+ vecNorm: (v) => {
841
+ const m = Math.sqrt(v.x * v.x + v.y * v.y);
842
+ return m === 0 ? { x: 0, y: 0 } : { x: v.x / m, y: v.y / m };
843
+ },
844
+ vecDist: (a, b) => {
845
+ const dx = b.x - a.x;
846
+ const dy = b.y - a.y;
847
+ return Math.sqrt(dx * dx + dy * dy);
848
+ },
849
+ // Easing functions (v1.1) - pure functions, t ∈ [0,1] → [0,1]
850
+ easeIn: (t) => t * t,
851
+ easeOut: (t) => t * (2 - t),
852
+ easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
853
+ easeCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
854
+ easeExpo: (t) => {
855
+ if (t === 0)
856
+ return 0;
857
+ if (t === 1)
858
+ return 1;
859
+ if (t < 0.5) {
860
+ return Math.pow(2, 20 * t - 10) / 2;
861
+ }
862
+ return (2 - Math.pow(2, -20 * t + 10)) / 2;
863
+ },
864
+ // Text functions
865
+ text: (str, x, y) => {
866
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
867
+ ctx.textAlign = currentTextAlignH;
868
+ ctx.textBaseline = currentTextAlignV;
869
+ if (fillEnabled) {
870
+ ctx.fillStyle = currentFill;
871
+ ctx.fillText(String(str), x, y);
872
+ }
873
+ if (strokeEnabled) {
874
+ ctx.strokeStyle = currentStroke;
875
+ ctx.strokeText(String(str), x, y);
876
+ }
877
+ },
878
+ textSize: (size) => {
879
+ currentTextSize = size;
880
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
881
+ },
882
+ textFont: (font) => {
883
+ currentTextFont = font;
884
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
885
+ },
886
+ textAlign: (horizAlign, vertAlign) => {
887
+ const hMap = {
888
+ 'left': 'left', 'LEFT': 'left',
889
+ 'center': 'center', 'CENTER': 'center',
890
+ 'right': 'right', 'RIGHT': 'right',
891
+ };
892
+ const vMap = {
893
+ 'top': 'top', 'TOP': 'top',
894
+ 'bottom': 'bottom', 'BOTTOM': 'bottom',
895
+ 'center': 'middle', 'CENTER': 'middle',
896
+ 'baseline': 'alphabetic', 'BASELINE': 'alphabetic',
897
+ };
898
+ currentTextAlignH = hMap[horizAlign] || 'left';
899
+ if (vertAlign) {
900
+ currentTextAlignV = vMap[vertAlign] || 'alphabetic';
901
+ }
902
+ },
903
+ textWidth: (str) => {
904
+ ctx.font = `${currentTextSize}px ${currentTextFont}`;
905
+ return ctx.measureText(String(str)).width;
906
+ },
907
+ // Pixel manipulation (v1.2)
908
+ loadPixels: () => {
909
+ imageDataObj = ctx.getImageData(0, 0, width, height);
910
+ pixelData = imageDataObj.data;
911
+ p.pixels = pixelData;
912
+ },
913
+ updatePixels: () => {
914
+ if (imageDataObj && pixelData) {
915
+ ctx.putImageData(imageDataObj, 0, 0);
916
+ }
917
+ },
918
+ pixels: null,
919
+ get: (x, y) => {
920
+ const imgData = ctx.getImageData(Math.floor(x), Math.floor(y), 1, 1);
921
+ return [imgData.data[0], imgData.data[1], imgData.data[2], imgData.data[3]];
922
+ },
923
+ set: (x, y, c) => {
924
+ const fx = Math.floor(x);
925
+ const fy = Math.floor(y);
926
+ if (Array.isArray(c)) {
927
+ const imgData = ctx.createImageData(1, 1);
928
+ imgData.data[0] = c[0];
929
+ imgData.data[1] = c[1];
930
+ imgData.data[2] = c[2];
931
+ imgData.data[3] = c[3] ?? 255;
932
+ ctx.putImageData(imgData, fx, fy);
933
+ }
934
+ else {
935
+ const parsed = parseCssColor(c);
936
+ if (parsed) {
937
+ const imgData = ctx.createImageData(1, 1);
938
+ imgData.data[0] = parsed.r;
939
+ imgData.data[1] = parsed.g;
940
+ imgData.data[2] = parsed.b;
941
+ imgData.data[3] = Math.round(parsed.a * 255);
942
+ ctx.putImageData(imgData, fx, fy);
943
+ }
944
+ }
945
+ },
946
+ // Offscreen graphics (v1.2)
947
+ createGraphics: (w, h) => {
948
+ const offscreenCanvas = document.createElement('canvas');
949
+ offscreenCanvas.width = w;
950
+ offscreenCanvas.height = h;
951
+ const pg = createP5Runtime(offscreenCanvas, w, h, config);
952
+ // Add reference to canvas for image() drawing
953
+ pg._canvas = offscreenCanvas;
954
+ return pg;
955
+ },
956
+ // Draw image or graphics object to canvas
957
+ image: (src, x, y, w, h) => {
958
+ const srcCanvas = src._canvas || src;
959
+ if (srcCanvas instanceof HTMLCanvasElement) {
960
+ const dw = w ?? srcCanvas.width;
961
+ const dh = h ?? srcCanvas.height;
962
+ ctx.drawImage(srcCanvas, x, y, dw, dh);
963
+ }
964
+ },
586
965
  // Loop control (no-ops for SDK)
587
966
  noLoop: () => { },
588
967
  loop: () => { },
589
968
  redraw: () => { },
590
969
  frameRate: (fps) => { },
970
+ // totalFrames placeholder (injected by engine)
971
+ totalFrames: 0,
591
972
  };
592
973
  return p;
593
974
  }
594
975
  export function injectTimeVariables(p, time) {
595
976
  p.frameCount = time.frameCount;
977
+ p.t = time.t;
978
+ p.time = time.time;
979
+ p.tGlobal = time.tGlobal;
980
+ p.totalFrames = time.totalFrames;
596
981
  }
597
982
  /**
598
983
  * VAR Protocol Constants (Phase 1 — Protocol v1.0.0)
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Static Engine
3
- * Protocol: v1.0.0 (Phase 1) — HARD ENFORCEMENT
3
+ * Protocol: v1.2.0 (Phase 3) — HARD ENFORCEMENT
4
4
  *
5
5
  * Static mode renderer: executes setup() only, captures single PNG.
6
6
  * Does NOT execute draw() - per NexArt Execution Specification v1.
@@ -1 +1 @@
1
- {"version":3,"file":"static-engine.d.ts","sourceRoot":"","sources":["../static-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAItE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAkGf"}
1
+ {"version":3,"file":"static-engine.d.ts","sourceRoot":"","sources":["../static-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAItE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAmGf"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Static Engine
3
- * Protocol: v1.0.0 (Phase 1) — HARD ENFORCEMENT
3
+ * Protocol: v1.2.0 (Phase 3) — HARD ENFORCEMENT
4
4
  *
5
5
  * Static mode renderer: executes setup() only, captures single PNG.
6
6
  * Does NOT execute draw() - per NexArt Execution Specification v1.
@@ -26,12 +26,13 @@ export async function runStaticMode(config, options) {
26
26
  canvas.height = height;
27
27
  // Create p5 runtime with optional seed for determinism
28
28
  const p = createP5Runtime(canvas, width, height, { seed });
29
- // Inject time variables (static = frame 0, t = 0)
29
+ // Inject time variables (static = frame 0, t = 0, totalFrames = 1)
30
30
  injectTimeVariables(p, {
31
31
  frameCount: 0,
32
32
  t: 0,
33
33
  time: 0,
34
34
  tGlobal: 0,
35
+ totalFrames: 1, // Static mode has 1 frame
35
36
  });
36
37
  // Inject protocol variables (VAR[0..9])
37
38
  injectProtocolVariables(p, vars);
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Types
3
- * Version: 1.0.2 (Protocol v1.0.0)
3
+ * Version: 1.5.0 (Protocol v1.2.0)
4
4
  *
5
5
  * Type definitions for the Code Mode runtime engine.
6
6
  * This is the canonical type surface for @nexart/codemode-sdk.
@@ -12,8 +12,8 @@
12
12
  export declare const PROTOCOL_IDENTITY: {
13
13
  readonly protocol: "nexart";
14
14
  readonly engine: "codemode";
15
- readonly protocolVersion: "1.0.0";
16
- readonly phase: 1;
15
+ readonly protocolVersion: "1.2.0";
16
+ readonly phase: 3;
17
17
  readonly deterministic: true;
18
18
  };
19
19
  export type RenderMode = 'static' | 'loop';
@@ -56,6 +56,7 @@ export interface TimeVariables {
56
56
  t: number;
57
57
  time: number;
58
58
  tGlobal: number;
59
+ totalFrames: number;
59
60
  }
60
61
  /**
61
62
  * Protocol Variables (VAR[0..9])
@@ -92,8 +93,8 @@ export declare const DEFAULT_CONFIG: {
92
93
  export interface ProtocolMetadata {
93
94
  protocol: 'nexart';
94
95
  engine: 'codemode';
95
- protocolVersion: '1.0.0';
96
- phase: 1;
96
+ protocolVersion: '1.2.0';
97
+ phase: 3;
97
98
  deterministic: true;
98
99
  seed: number;
99
100
  vars: number[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Types
3
- * Version: 1.0.2 (Protocol v1.0.0)
3
+ * Version: 1.5.0 (Protocol v1.2.0)
4
4
  *
5
5
  * Type definitions for the Code Mode runtime engine.
6
6
  * This is the canonical type surface for @nexart/codemode-sdk.
@@ -12,8 +12,8 @@
12
12
  export const PROTOCOL_IDENTITY = {
13
13
  protocol: 'nexart',
14
14
  engine: 'codemode',
15
- protocolVersion: '1.0.0',
16
- phase: 1,
15
+ protocolVersion: '1.2.0',
16
+ phase: 3,
17
17
  deterministic: true,
18
18
  };
19
19
  /**
package/package.json CHANGED
@@ -1,42 +1,36 @@
1
1
  {
2
2
  "name": "@nexart/codemode-sdk",
3
- "version": "1.1.1",
4
- "description": "NexArt Code Mode SDK - Canonical execution engine for deterministic generative art",
3
+ "version": "1.5.0",
4
+ "description": "NexArt Code Mode Runtime SDK - Deterministic generative art rendering engine (Protocol v1.2.0)",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
5
8
  "type": "module",
6
- "main": "./dist/core-index.js",
7
- "types": "./dist/core-index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/core-index.d.ts",
11
- "import": "./dist/core-index.js"
12
- }
13
- },
14
9
  "files": [
15
- "dist",
16
- "README.md",
17
- "CHANGELOG.md",
18
- "CODE_MODE_PROTOCOL.md"
10
+ "dist"
19
11
  ],
20
12
  "scripts": {
21
13
  "build": "tsc",
22
- "test": "npx tsx smoke-test.ts",
23
- "prepublishOnly": "npm run build && npm run test"
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.0.0"
24
18
  },
25
19
  "keywords": [
26
- "nexart",
27
20
  "generative-art",
28
- "nft",
29
21
  "p5js",
22
+ "canvas",
23
+ "rendering",
30
24
  "deterministic",
31
- "codemode"
25
+ "nft",
26
+ "creative-coding"
32
27
  ],
33
- "author": "NexArt",
34
- "license": "MIT",
35
28
  "repository": {
36
29
  "type": "git",
37
30
  "url": "https://github.com/artnames/nexart-codemode-sdk.git"
38
31
  },
39
- "devDependencies": {
40
- "typescript": "^5.0.0"
32
+ "author": "NexArt",
33
+ "publishConfig": {
34
+ "access": "public"
41
35
  }
42
36
  }