@nexart/ui-renderer 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @nexart/ui-renderer
2
2
 
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
 
5
5
  **Lightweight Preview Runtime for NexArt Protocol**
6
6
 
@@ -19,6 +19,28 @@ Version: 0.8.0
19
19
 
20
20
  ---
21
21
 
22
+ ## v0.8.2 — Runtime Dimensions Fix
23
+
24
+ Fixed critical bug where preview scaling affected `width`/`height` inside sketches.
25
+
26
+ - **Runtime uses original dimensions**: `width` and `height` now match Code Mode exactly
27
+ - **Canvas buffer still scaled for performance**: Rendering is fast, semantics are correct
28
+ - **Loop animations work correctly**: Geometry math and timing no longer break
29
+
30
+ **Key rule enforced:** Preview scaling is a rendering concern, not a semantic one.
31
+
32
+ ---
33
+
34
+ ## v0.8.1 — Canvas Scaling Fix
35
+
36
+ Fixed canvas zoom/cropping bug caused by resolution downscaling in v0.8.0.
37
+
38
+ - **Scale Transform Reapplied**: Context scale is now properly restored after canvas resize
39
+ - **Transform-Safe Clear**: `clearRect` now ignores active transforms for correct full-canvas clearing
40
+ - **No API Changes**: All fixes are internal
41
+
42
+ ---
43
+
22
44
  ## v0.8.0 — Lightweight Preview Runtime
23
45
 
24
46
  This release refactors the UI renderer into a performance-optimized preview runtime.
@@ -203,7 +225,7 @@ import { getCapabilities } from '@nexart/ui-renderer';
203
225
 
204
226
  const caps = getCapabilities();
205
227
  // {
206
- // version: '0.8.0',
228
+ // version: '0.8.2',
207
229
  // isCanonical: false,
208
230
  // isArchival: false,
209
231
  // previewBudget: { MAX_FRAMES: 30, MAX_TOTAL_TIME_MS: 500, FRAME_STRIDE: 3 },
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @nexart/ui-renderer
3
- * Version: 0.8.0
3
+ * Version: 0.8.2
4
4
  *
5
5
  * Lightweight Preview Runtime for NexArt Protocol
6
6
  *
@@ -46,7 +46,7 @@ export { calculateScaledDimensions, applyScaledDimensions, type ScaledDimensions
46
46
  export type { NexArtSystemInput, NexArtSystem, DeclarativeSystemInput, DeclarativeSystem, CodeSystem, NexArtCodeSystem, UnifiedSystemInput, UnifiedSystem, UnifiedElement, BackgroundElement, PrimitiveElement, SketchElement, BackgroundPreset, PrimitiveName, ColorPalette, MotionSpeed, StrokeWeightAuto, LoopConfig, DeclarativeElement, SystemElement, DotsElement, LinesElement, WavesElement, GridElement, FlowFieldElement, OrbitsElement, BackgroundConfig, MotionConfig, PreviewOptions, ValidationResult, } from './types';
47
47
  export { AESTHETIC_DEFAULTS, SDK_VERSION as TYPE_SDK_VERSION } from './types';
48
48
  export type { Capabilities, PrimitiveCapability, ParameterSpec, } from './capabilities';
49
- export declare const SDK_VERSION = "0.8.0";
49
+ export declare const SDK_VERSION = "0.8.2";
50
50
  export declare const PROTOCOL_VERSION = "0.8";
51
51
  export declare const IS_CANONICAL = false;
52
52
  export declare const IS_ARCHIVAL = false;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @nexart/ui-renderer
3
- * Version: 0.8.0
3
+ * Version: 0.8.2
4
4
  *
5
5
  * Lightweight Preview Runtime for NexArt Protocol
6
6
  *
@@ -44,7 +44,7 @@ export { PREVIEW_BUDGET, CANVAS_LIMITS, } from './preview/preview-types';
44
44
  export { createFrameBudget, canRenderFrame, recordFrame, resetBudget, shouldSkipFrame, } from './preview/frame-budget';
45
45
  export { calculateScaledDimensions, applyScaledDimensions, } from './preview/canvas-scaler';
46
46
  export { AESTHETIC_DEFAULTS, SDK_VERSION as TYPE_SDK_VERSION } from './types';
47
- export const SDK_VERSION = '0.8.0';
47
+ export const SDK_VERSION = '0.8.2';
48
48
  export const PROTOCOL_VERSION = '0.8';
49
49
  export const IS_CANONICAL = false;
50
50
  export const IS_ARCHIVAL = false;
@@ -10,6 +10,16 @@
10
10
  * ║ Max dimension: 900px ║
11
11
  * ║ Preserves aspect ratio ║
12
12
  * ║ Uses CSS scaling for display ║
13
+ * ╠══════════════════════════════════════════════════════════════════════════╣
14
+ * ║ ARCHITECTURAL INVARIANT (v0.8.2+): ║
15
+ * ║ ║
16
+ * ║ Scaling is a RENDERING concern, NOT a SEMANTIC one. ║
17
+ * ║ ║
18
+ * ║ - Canvas buffer: scaled (renderWidth × renderHeight) ║
19
+ * ║ - Runtime width/height: ALWAYS original protocol dimensions ║
20
+ * ║ - Context transform: ctx.scale() maps original → render space ║
21
+ * ║ ║
22
+ * ║ This ensures sketch math works identically in preview and Code Mode. ║
13
23
  * ╚══════════════════════════════════════════════════════════════════════════╝
14
24
  */
15
25
  export interface ScaledDimensions {
@@ -46,4 +56,25 @@ export declare function scaleCoordinate(value: number, scaleFactor: number): num
46
56
  * This allows sketches to use original coordinates while rendering at scaled resolution.
47
57
  */
48
58
  export declare function applyScaleTransform(ctx: CanvasRenderingContext2D, scaleFactor: number): void;
59
+ /**
60
+ * Reapply context scale after canvas resize.
61
+ *
62
+ * NOTE:
63
+ * Canvas resizing resets the 2D context transform.
64
+ * The UI renderer downsamples the canvas for performance,
65
+ * so we must reapply the internal scale factor once after resize.
66
+ * This runtime is preview-only and intentionally non-deterministic.
67
+ *
68
+ * @param canvas - The canvas element
69
+ * @param dimensions - The scaled dimensions object
70
+ */
71
+ export declare function reapplyContextScale(canvas: HTMLCanvasElement, dimensions: ScaledDimensions): void;
72
+ /**
73
+ * Clear the canvas ignoring any active transforms.
74
+ * Ensures full canvas is cleared even when scaled.
75
+ *
76
+ * @param ctx - The 2D rendering context
77
+ * @param canvas - The canvas element (for dimensions)
78
+ */
79
+ export declare function clearCanvasIgnoringTransform(ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void;
49
80
  //# sourceMappingURL=canvas-scaler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-scaler.d.ts","sourceRoot":"","sources":["../../src/preview/canvas-scaler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,gBAAgB,CA0BlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,gBAAgB,GAC3B,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,wBAAwB,EAC7B,WAAW,EAAE,MAAM,GAClB,IAAI,CAIN"}
1
+ {"version":3,"file":"canvas-scaler.d.ts","sourceRoot":"","sources":["../../src/preview/canvas-scaler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,gBAAgB,CA0BlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,gBAAgB,GAC3B,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,wBAAwB,EAC7B,WAAW,EAAE,MAAM,GAClB,IAAI,CAIN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,gBAAgB,GAC3B,IAAI,CAaN;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,wBAAwB,EAC7B,MAAM,EAAE,iBAAiB,GACxB,IAAI,CAKN"}
@@ -10,6 +10,16 @@
10
10
  * ║ Max dimension: 900px ║
11
11
  * ║ Preserves aspect ratio ║
12
12
  * ║ Uses CSS scaling for display ║
13
+ * ╠══════════════════════════════════════════════════════════════════════════╣
14
+ * ║ ARCHITECTURAL INVARIANT (v0.8.2+): ║
15
+ * ║ ║
16
+ * ║ Scaling is a RENDERING concern, NOT a SEMANTIC one. ║
17
+ * ║ ║
18
+ * ║ - Canvas buffer: scaled (renderWidth × renderHeight) ║
19
+ * ║ - Runtime width/height: ALWAYS original protocol dimensions ║
20
+ * ║ - Context transform: ctx.scale() maps original → render space ║
21
+ * ║ ║
22
+ * ║ This ensures sketch math works identically in preview and Code Mode. ║
13
23
  * ╚══════════════════════════════════════════════════════════════════════════╝
14
24
  */
15
25
  import { CANVAS_LIMITS } from './preview-types';
@@ -72,3 +82,41 @@ export function applyScaleTransform(ctx, scaleFactor) {
72
82
  ctx.scale(scaleFactor, scaleFactor);
73
83
  }
74
84
  }
85
+ /**
86
+ * Reapply context scale after canvas resize.
87
+ *
88
+ * NOTE:
89
+ * Canvas resizing resets the 2D context transform.
90
+ * The UI renderer downsamples the canvas for performance,
91
+ * so we must reapply the internal scale factor once after resize.
92
+ * This runtime is preview-only and intentionally non-deterministic.
93
+ *
94
+ * @param canvas - The canvas element
95
+ * @param dimensions - The scaled dimensions object
96
+ */
97
+ export function reapplyContextScale(canvas, dimensions) {
98
+ if (!dimensions.wasScaled)
99
+ return;
100
+ const ctx = canvas.getContext('2d');
101
+ if (!ctx)
102
+ return;
103
+ // Canvas resize resets transform — must restore it
104
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
105
+ // Reapply internal renderer scale (NOT clientWidth-based)
106
+ // This is calculated from render dimensions, not DOM size
107
+ const scaleFactor = dimensions.renderWidth / dimensions.originalWidth;
108
+ ctx.scale(scaleFactor, scaleFactor);
109
+ }
110
+ /**
111
+ * Clear the canvas ignoring any active transforms.
112
+ * Ensures full canvas is cleared even when scaled.
113
+ *
114
+ * @param ctx - The 2D rendering context
115
+ * @param canvas - The canvas element (for dimensions)
116
+ */
117
+ export function clearCanvasIgnoringTransform(ctx, canvas) {
118
+ ctx.save();
119
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
120
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
121
+ ctx.restore();
122
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAwBjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AAwBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,YAAY,CA0Od"}
1
+ {"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA0BjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AAwBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,YAAY,CA0Pd"}
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { PREVIEW_BUDGET, } from './preview-types';
20
20
  import { createFrameBudget, canRenderFrame, recordFrame, resetBudget, shouldSkipFrame, } from './frame-budget';
21
- import { calculateScaledDimensions, applyScaledDimensions, } from './canvas-scaler';
21
+ import { calculateScaledDimensions, applyScaledDimensions, reapplyContextScale, clearCanvasIgnoringTransform, } from './canvas-scaler';
22
22
  import { createPreviewRuntime } from './preview-runtime';
23
23
  const PROTOCOL_VERSION = '1.2.0';
24
24
  let activeRendererInstance = null;
@@ -57,6 +57,9 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
57
57
  console.log(`[UIRenderer] Canvas scaled: ${system.width}x${system.height} → ${scaled.renderWidth}x${scaled.renderHeight}`);
58
58
  }
59
59
  applyScaledDimensions(canvas, scaled);
60
+ // NOTE: Canvas resizing resets the 2D context transform.
61
+ // Reapply scale factor once after resize for correct rendering.
62
+ reapplyContextScale(canvas, scaled);
60
63
  const ctx = canvas.getContext('2d');
61
64
  let animationId = null;
62
65
  let isRunning = false;
@@ -67,7 +70,21 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
67
70
  let setupFn = null;
68
71
  let drawFn = null;
69
72
  const compileSource = () => {
70
- runtime = createPreviewRuntime(canvas, scaled.renderWidth, scaled.renderHeight, system.seed ?? 12345, normalizedVars);
73
+ // ╔═══════════════════════════════════════════════════════════════════════╗
74
+ // ║ ARCHITECTURAL INVARIANT — DO NOT CHANGE ║
75
+ // ║ ║
76
+ // ║ Runtime width/height MUST always equal protocol dimensions. ║
77
+ // ║ DO NOT pass scaled values (renderWidth/renderHeight) here. ║
78
+ // ║ ║
79
+ // ║ Scaling is a RENDERING concern handled by ctx.scale(). ║
80
+ // ║ width/height are SEMANTIC values used by sketch math. ║
81
+ // ║ ║
82
+ // ║ Passing scaled dimensions breaks loop animations and geometry. ║
83
+ // ║ This invariant is locked for v0.x — see CHANGELOG v0.8.2. ║
84
+ // ╚═══════════════════════════════════════════════════════════════════════╝
85
+ runtime = createPreviewRuntime(canvas, scaled.originalWidth, // ← Protocol dimension (e.g. 1950)
86
+ scaled.originalHeight, // ← Protocol dimension (e.g. 2400)
87
+ system.seed ?? 12345, normalizedVars);
71
88
  const totalFrames = system.totalFrames ?? 120;
72
89
  runtime.totalFrames = totalFrames;
73
90
  try {
@@ -173,7 +190,7 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
173
190
  runtime.tGlobal = runtime.t;
174
191
  }
175
192
  try {
176
- ctx.clearRect(0, 0, scaled.renderWidth, scaled.renderHeight);
193
+ clearCanvasIgnoringTransform(ctx, canvas);
177
194
  if (drawFn)
178
195
  drawFn();
179
196
  drawBadge();
@@ -225,7 +242,7 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
225
242
  const destroy = () => {
226
243
  isDestroyed = true;
227
244
  stop();
228
- ctx.clearRect(0, 0, scaled.renderWidth, scaled.renderHeight);
245
+ clearCanvasIgnoringTransform(ctx, canvas);
229
246
  runtime = null;
230
247
  setupFn = null;
231
248
  drawFn = null;
@@ -1 +1 @@
1
- {"version":3,"file":"preview-engine.d.ts","sourceRoot":"","sources":["../../src/preview/preview-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAC;AAmMzB;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CAEhF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAKpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC"}
1
+ {"version":3,"file":"preview-engine.d.ts","sourceRoot":"","sources":["../../src/preview/preview-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAC;AA8MzB;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CAEhF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAKpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC"}
@@ -25,7 +25,7 @@
25
25
  * ╚══════════════════════════════════════════════════════════════════════════╝
26
26
  */
27
27
  import { createFrameBudget, canRenderFrame, recordFrame, getElapsedMs, resetBudget, shouldSkipFrame, } from './frame-budget';
28
- import { calculateScaledDimensions, applyScaledDimensions, } from './canvas-scaler';
28
+ import { calculateScaledDimensions, applyScaledDimensions, reapplyContextScale, } from './canvas-scaler';
29
29
  import { createPreviewRuntime } from './preview-runtime';
30
30
  let activePreviewRenderer = null;
31
31
  class PreviewEngine {
@@ -46,7 +46,18 @@ class PreviewEngine {
46
46
  initialize() {
47
47
  const scaled = calculateScaledDimensions(this.config.width, this.config.height);
48
48
  applyScaledDimensions(this.canvas, scaled);
49
- this.runtime = createPreviewRuntime(this.canvas, scaled.renderWidth, scaled.renderHeight, this.config.seed ?? 12345, this.config.vars ?? []);
49
+ // NOTE: Canvas resizing resets the 2D context transform.
50
+ // Reapply scale factor once after resize for correct rendering.
51
+ reapplyContextScale(this.canvas, scaled);
52
+ // ╔═══════════════════════════════════════════════════════════════════════╗
53
+ // ║ ARCHITECTURAL INVARIANT — DO NOT CHANGE ║
54
+ // ║ Runtime width/height MUST equal protocol dimensions. ║
55
+ // ║ DO NOT pass renderWidth/renderHeight — breaks loop animations. ║
56
+ // ║ Scaling is handled by ctx.scale(), not by changing width/height. ║
57
+ // ╚═══════════════════════════════════════════════════════════════════════╝
58
+ this.runtime = createPreviewRuntime(this.canvas, scaled.originalWidth, // ← Protocol dimension
59
+ scaled.originalHeight, // ← Protocol dimension
60
+ this.config.seed ?? 12345, this.config.vars ?? []);
50
61
  const totalFrames = this.config.totalFrames ?? 120;
51
62
  this.runtime.totalFrames = totalFrames;
52
63
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"unified-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/unified-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,cAAc,EAAoE,MAAM,UAAU,CAAC;AAmBhJ,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AA6ED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CAsPjB"}
1
+ {"version":3,"file":"unified-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/unified-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,cAAc,EAAoE,MAAM,UAAU,CAAC;AAmBhJ,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AA6ED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CAqQjB"}
@@ -7,7 +7,7 @@ import { compileBackgroundPreset, getPaletteColors } from '../presets/background
7
7
  import { compilePrimitive } from '../presets/primitives';
8
8
  import { wrapSketch, validateSketchSafety } from '../presets/sketch-wrapper';
9
9
  import { createPreviewRuntime } from './preview-runtime';
10
- import { calculateScaledDimensions, applyScaledDimensions } from './canvas-scaler';
10
+ import { calculateScaledDimensions, applyScaledDimensions, reapplyContextScale, clearCanvasIgnoringTransform } from './canvas-scaler';
11
11
  import { createFrameBudget, canRenderFrame, recordFrame, resetBudget, shouldSkipFrame } from './frame-budget';
12
12
  import { PREVIEW_BUDGET } from './preview-types';
13
13
  function validateSketchElement(element) {
@@ -89,6 +89,9 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
89
89
  console.log(`[UIRenderer] Canvas scaled: ${system.width}x${system.height} → ${scaled.renderWidth}x${scaled.renderHeight}`);
90
90
  }
91
91
  applyScaledDimensions(canvas, scaled);
92
+ // NOTE: Canvas resizing resets the 2D context transform.
93
+ // Reapply scale factor once after resize for correct rendering.
94
+ reapplyContextScale(canvas, scaled);
92
95
  const ctx = canvas.getContext('2d');
93
96
  let animationId = null;
94
97
  let isRunning = false;
@@ -222,7 +225,15 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
222
225
  return;
223
226
  try {
224
227
  let frameCount = 0;
225
- const p = createPreviewRuntime(canvas, scaled.renderWidth, scaled.renderHeight, system.seed);
228
+ // ╔═══════════════════════════════════════════════════════════════════════╗
229
+ // ║ ARCHITECTURAL INVARIANT — DO NOT CHANGE ║
230
+ // ║ Runtime width/height MUST equal protocol dimensions. ║
231
+ // ║ DO NOT pass renderWidth/renderHeight — breaks loop animations. ║
232
+ // ║ Scaling is handled by ctx.scale(), not by changing width/height. ║
233
+ // ╚═══════════════════════════════════════════════════════════════════════╝
234
+ const p = createPreviewRuntime(canvas, scaled.originalWidth, // ← Protocol dimension
235
+ scaled.originalHeight, // ← Protocol dimension
236
+ system.seed);
226
237
  runSetup(p);
227
238
  resetBudget(budget);
228
239
  isRunning = true;
@@ -279,7 +290,7 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
279
290
  const destroy = () => {
280
291
  isDestroyed = true;
281
292
  stop();
282
- ctx.clearRect(0, 0, scaled.renderWidth, scaled.renderHeight);
293
+ clearCanvasIgnoringTransform(ctx, canvas);
283
294
  };
284
295
  const renderer = {
285
296
  render,
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.8.0 - Type Definitions
2
+ * @nexart/ui-renderer v0.8.2 - Type Definitions
3
3
  *
4
4
  * Lightweight Preview Runtime for NexArt Protocol.
5
5
  * This SDK is non-canonical and for preview only.
@@ -9,7 +9,7 @@
9
9
  * - Max total time: 500ms
10
10
  * - Max canvas dimension: 900px
11
11
  */
12
- export declare const SDK_VERSION = "0.8.0";
12
+ export declare const SDK_VERSION = "0.8.2";
13
13
  export declare const AESTHETIC_DEFAULTS: {
14
14
  readonly background: {
15
15
  readonly r: 246;
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.8.0 - Type Definitions
2
+ * @nexart/ui-renderer v0.8.2 - Type Definitions
3
3
  *
4
4
  * Lightweight Preview Runtime for NexArt Protocol.
5
5
  * This SDK is non-canonical and for preview only.
@@ -9,7 +9,7 @@
9
9
  * - Max total time: 500ms
10
10
  * - Max canvas dimension: 900px
11
11
  */
12
- export const SDK_VERSION = '0.8.0';
12
+ export const SDK_VERSION = '0.8.2';
13
13
  export const AESTHETIC_DEFAULTS = {
14
14
  background: { r: 246, g: 245, b: 242 },
15
15
  foreground: { r: 45, g: 45, b: 45 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexart/ui-renderer",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Lightweight Preview Runtime for NexArt Protocol. Non-canonical, performance-optimized with budget limits (max 30 frames, 500ms, 900px canvas).",
5
5
  "license": "MIT",
6
6
  "type": "module",