@nexart/ui-renderer 0.8.2 → 0.8.3

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.2
3
+ Version: 0.8.3
4
4
 
5
5
  **Lightweight Preview Runtime for NexArt Protocol**
6
6
 
@@ -19,6 +19,22 @@ Version: 0.8.2
19
19
 
20
20
  ---
21
21
 
22
+ ## v0.8.3 — Animation Loop Fix
23
+
24
+ Fixed critical bug where preview only rendered a single frame.
25
+
26
+ - **RAF unconditionally scheduled**: `requestAnimationFrame(loop)` is now called on every tick
27
+ - **Budget gates draw only**: Frame budget controls whether `draw()` runs, not whether the loop continues
28
+ - **Continuous animation**: Loop runs forever until explicitly stopped
29
+
30
+ **Animation Loop Invariant (locked for v0.x):**
31
+ ```
32
+ RAF schedules → budget checks → draw executes (or skips) → repeat
33
+ Never: budget check → stop loop
34
+ ```
35
+
36
+ ---
37
+
22
38
  ## v0.8.2 — Runtime Dimensions Fix
23
39
 
24
40
  Fixed critical bug where preview scaling affected `width`/`height` inside sketches.
@@ -225,7 +241,7 @@ import { getCapabilities } from '@nexart/ui-renderer';
225
241
 
226
242
  const caps = getCapabilities();
227
243
  // {
228
- // version: '0.8.2',
244
+ // version: '0.8.3',
229
245
  // isCanonical: false,
230
246
  // isArchival: false,
231
247
  // 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.2
3
+ * Version: 0.8.3
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.2";
49
+ export declare const SDK_VERSION = "0.8.3";
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.2
3
+ * Version: 0.8.3
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.2';
47
+ export const SDK_VERSION = '0.8.3';
48
48
  export const PROTOCOL_VERSION = '0.8';
49
49
  export const IS_CANONICAL = false;
50
50
  export const IS_ARCHIVAL = false;
@@ -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;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"}
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,CAkQd"}
@@ -173,13 +173,22 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
173
173
  }
174
174
  resetBudget(budget);
175
175
  isRunning = true;
176
+ // ╔═══════════════════════════════════════════════════════════════════════╗
177
+ // ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
178
+ // ║ ║
179
+ // ║ requestAnimationFrame(loop) MUST be called on EVERY tick. ║
180
+ // ║ Budget gates ONLY whether draw() executes, NOT whether loop runs. ║
181
+ // ║ Never return before scheduling RAF. Never conditionally schedule. ║
182
+ // ╚═══════════════════════════════════════════════════════════════════════╝
176
183
  const loop = () => {
184
+ // ALWAYS schedule next frame first — loop never terminates on its own
185
+ animationId = requestAnimationFrame(loop);
186
+ // Early exit checks (after RAF is scheduled)
177
187
  if (!isRunning || isDestroyed)
178
188
  return;
189
+ // Budget check — skip draw but keep loop alive
179
190
  if (!canRenderFrame(budget)) {
180
- console.log(`[UIRenderer] Budget exhausted: ${budget.exhaustionReason}`);
181
- isRunning = false;
182
- return;
191
+ return; // Loop continues, just don't draw
183
192
  }
184
193
  frameCount++;
185
194
  if (!shouldSkipFrame(frameCount)) {
@@ -200,7 +209,6 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
200
209
  console.warn('[UIRenderer] Draw error:', error);
201
210
  }
202
211
  }
203
- animationId = requestAnimationFrame(loop);
204
212
  };
205
213
  animationId = requestAnimationFrame(loop);
206
214
  }
@@ -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;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"}
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;AAqNzB;;;;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"}
@@ -132,15 +132,23 @@ class PreviewEngine {
132
132
  }
133
133
  this.scheduleNextFrame();
134
134
  }
135
+ // ╔═══════════════════════════════════════════════════════════════════════╗
136
+ // ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
137
+ // ║ ║
138
+ // ║ requestAnimationFrame MUST be called on EVERY tick. ║
139
+ // ║ Budget gates ONLY whether draw() executes, NOT whether loop runs. ║
140
+ // ║ Never return before scheduling RAF. Never conditionally schedule. ║
141
+ // ╚═══════════════════════════════════════════════════════════════════════╝
135
142
  scheduleNextFrame() {
136
- if (!this.running)
137
- return;
143
+ // ALWAYS schedule next frame first — loop never terminates on its own
138
144
  this.animationFrameId = requestAnimationFrame(() => {
145
+ this.scheduleNextFrame();
146
+ // Early exit checks (after RAF is scheduled)
139
147
  if (!this.running)
140
148
  return;
149
+ // Budget check — skip draw but keep loop alive
141
150
  if (!canRenderFrame(this.budget)) {
142
- this.running = false;
143
- return;
151
+ return; // Loop continues, just don't draw
144
152
  }
145
153
  this.internalFrameCount++;
146
154
  if (!shouldSkipFrame(this.internalFrameCount)) {
@@ -161,7 +169,6 @@ class PreviewEngine {
161
169
  console.warn('[PreviewEngine] Draw error:', error);
162
170
  }
163
171
  }
164
- this.scheduleNextFrame();
165
172
  });
166
173
  }
167
174
  stopLoop() {
@@ -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,CAqQjB"}
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,CA6QjB"}
@@ -237,13 +237,22 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
237
237
  runSetup(p);
238
238
  resetBudget(budget);
239
239
  isRunning = true;
240
+ // ╔═══════════════════════════════════════════════════════════════════════╗
241
+ // ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
242
+ // ║ ║
243
+ // ║ requestAnimationFrame(loop) MUST be called on EVERY tick. ║
244
+ // ║ Budget gates ONLY whether draw() executes, NOT whether loop runs. ║
245
+ // ║ Never return before scheduling RAF. Never conditionally schedule. ║
246
+ // ╚═══════════════════════════════════════════════════════════════════════╝
240
247
  const loop = () => {
248
+ // ALWAYS schedule next frame first — loop never terminates on its own
249
+ animationId = requestAnimationFrame(loop);
250
+ // Early exit checks (after RAF is scheduled)
241
251
  if (!isRunning || isDestroyed)
242
252
  return;
253
+ // Budget check — skip draw but keep loop alive
243
254
  if (!canRenderFrame(budget)) {
244
- console.log(`[UIRenderer] Budget exhausted: ${budget.exhaustionReason}`);
245
- isRunning = false;
246
- return;
255
+ return; // Loop continues, just don't draw
247
256
  }
248
257
  frameCount++;
249
258
  if (!shouldSkipFrame(frameCount)) {
@@ -259,7 +268,6 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
259
268
  console.warn('[UIRenderer] Draw error:', error);
260
269
  }
261
270
  }
262
- animationId = requestAnimationFrame(loop);
263
271
  };
264
272
  animationId = requestAnimationFrame(loop);
265
273
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.8.2 - Type Definitions
2
+ * @nexart/ui-renderer v0.8.3 - 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.2";
12
+ export declare const SDK_VERSION = "0.8.3";
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.2 - Type Definitions
2
+ * @nexart/ui-renderer v0.8.3 - 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.2';
12
+ export const SDK_VERSION = '0.8.3';
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.2",
3
+ "version": "0.8.3",
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",