@nexart/ui-renderer 0.8.6 → 0.8.7

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.6
3
+ Version: 0.8.7
4
4
 
5
5
  **Lightweight Preview Runtime for NexArt Protocol**
6
6
 
@@ -19,31 +19,35 @@ Version: 0.8.6
19
19
 
20
20
  ---
21
21
 
22
- ## v0.8.6Continuous FPS-Capped Animation
22
+ ## v0.8.7Live Runtime Binding Fix
23
23
 
24
- Replaced time/frame budget with simple FPS throttle for continuous animation.
24
+ Fixed critical bug where time-varying properties were frozen in loop mode.
25
25
 
26
- - **No execution budget**: Removed 30-frame/500ms limits
27
- - **FPS cap**: Renders at 8 FPS (125ms per frame) for smooth, efficient animation
28
- - **Continuous animation**: Loops indefinitely until `stop()` is called
29
- - **No flashing**: Canvas only updates when draw() runs
26
+ - **Live binding**: `frameCount`, `t`, `time`, `tGlobal`, `totalFrames` now update every frame
27
+ - **Proxy + with pattern**: Uses a Proxy scope with `with()` for live property access
28
+ - **Animations work**: Sketches see correct values on every frame
30
29
 
31
- **Animation Pattern:**
30
+ **Animation Semantics:**
31
+ In loop mode, all time variables update every frame:
32
+ - `frameCount`: Increments each frame
33
+ - `t`: Normalized time `(frameCount % totalFrames) / totalFrames` (range [0,1))
34
+ - `time`: Alias for `t`
35
+ - `tGlobal`: Alias for `t`
36
+ - `totalFrames`: Total frames in the loop (default 120)
37
+
38
+ **Test Sketch:**
32
39
  ```javascript
33
- function loop() {
34
- requestAnimationFrame(loop);
35
-
36
- if (!shouldRenderFrame(throttle)) return; // FPS cap
37
-
38
- runtime.draw();
39
- recordFrame(throttle);
40
+ function draw() {
41
+ background(0);
42
+ fill(255);
43
+ circle(
44
+ width / 2 + sin(frameCount * 0.1) * 300,
45
+ height / 2,
46
+ 120
47
+ );
40
48
  }
41
49
  ```
42
-
43
- **Behavior:**
44
- - Renders continuously at 8 FPS
45
- - Modulo-based looping: `t = (frameCount % totalFrames) / totalFrames`
46
- - Stops only when `stop()` is called
50
+ Expected: Circle moves horizontally, motion is continuous.
47
51
 
48
52
  ---
49
53
 
@@ -269,7 +273,7 @@ import { getCapabilities } from '@nexart/ui-renderer';
269
273
 
270
274
  const caps = getCapabilities();
271
275
  // {
272
- // version: '0.8.6',
276
+ // version: '0.8.7',
273
277
  // isCanonical: false,
274
278
  // isArchival: false,
275
279
  // previewBudget: { MAX_FRAMES: 30, MAX_TOTAL_TIME_MS: 500, FRAME_STRIDE: 3 },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.8.6 - Code Mode Renderer
2
+ * @nexart/ui-renderer v0.8.7 - Code Mode Renderer
3
3
  *
4
4
  * ╔══════════════════════════════════════════════════════════════════════════╗
5
5
  * ║ PREVIEW RENDERER — LIGHTWEIGHT, NON-AUTHORITATIVE ║
@@ -1 +1 @@
1
- {"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;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,CAmQd"}
1
+ {"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;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,CAoTd"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.8.6 - Code Mode Renderer
2
+ * @nexart/ui-renderer v0.8.7 - Code Mode Renderer
3
3
  *
4
4
  * ╔══════════════════════════════════════════════════════════════════════════╗
5
5
  * ║ PREVIEW RENDERER — LIGHTWEIGHT, NON-AUTHORITATIVE ║
@@ -85,19 +85,68 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
85
85
  const totalFrames = system.totalFrames ?? 120;
86
86
  runtime.totalFrames = totalFrames;
87
87
  try {
88
- const globalVars = Object.keys(runtime);
89
- const globalValues = Object.values(runtime);
90
- const wrappedSource = `
91
- ${system.source}
92
- if (typeof setup === 'function') __registerSetup(setup);
93
- if (typeof draw === 'function') __registerDraw(draw);
94
- `;
88
+ // ╔═══════════════════════════════════════════════════════════════════════╗
89
+ // ║ LIVE RUNTIME BINDING — v0.8.7 FIX ║
90
+ // ║ ║
91
+ // ║ Time-varying properties (frameCount, t, time, tGlobal, totalFrames) ║
92
+ // ║ must be accessed via getters that read from the live runtime object. ║
93
+ // ║ ║
94
+ // ║ We use a Proxy + with() pattern to enable bare-name access to live ║
95
+ // ║ runtime properties. The proxy's get trap reads from runtime for ║
96
+ // ║ time-varying props, and from a static cache for everything else. ║
97
+ // ╚═══════════════════════════════════════════════════════════════════════╝
98
+ // Time-varying properties that need live access
99
+ const liveProps = new Set(['frameCount', 't', 'time', 'tGlobal', 'totalFrames']);
100
+ // Cache static properties (functions, constants)
101
+ const staticCache = {};
102
+ for (const key of Object.keys(runtime)) {
103
+ if (!liveProps.has(key)) {
104
+ staticCache[key] = runtime[key];
105
+ }
106
+ }
95
107
  const registerSetup = (fn) => { setupFn = fn; };
96
108
  const registerDraw = (fn) => { drawFn = fn; };
97
- globalVars.push('__registerSetup', '__registerDraw');
98
- globalValues.push(registerSetup, registerDraw);
99
- const fn = new Function(...globalVars, wrappedSource);
100
- fn(...globalValues);
109
+ // Build the set of all known keys (for has trap)
110
+ const knownKeys = new Set([
111
+ ...liveProps,
112
+ ...Object.keys(staticCache),
113
+ '__registerSetup',
114
+ '__registerDraw'
115
+ ]);
116
+ // Create a proxy that provides live access to time-varying properties
117
+ // IMPORTANT: has() must return true ONLY for known keys
118
+ // Otherwise globals (Math, window, etc.) are masked and become undefined
119
+ const runtimeRef = runtime;
120
+ const scope = new Proxy({}, {
121
+ has: (_, prop) => knownKeys.has(prop),
122
+ get: (_, prop) => {
123
+ // Time-varying properties - read live from runtime
124
+ if (liveProps.has(prop)) {
125
+ return runtimeRef[prop];
126
+ }
127
+ // Special functions
128
+ if (prop === '__registerSetup')
129
+ return registerSetup;
130
+ if (prop === '__registerDraw')
131
+ return registerDraw;
132
+ // Static properties from cache
133
+ if (prop in staticCache)
134
+ return staticCache[prop];
135
+ // Fall through to undefined (should not hit this if has() is correct)
136
+ return undefined;
137
+ }
138
+ });
139
+ // Use with() to make the proxy scope available to bare variable names
140
+ // Note: with() is safe here since we control the scope completely
141
+ const wrappedSource = `
142
+ with (__scope) {
143
+ ${system.source}
144
+ if (typeof setup === 'function') __registerSetup(setup);
145
+ if (typeof draw === 'function') __registerDraw(draw);
146
+ }
147
+ `;
148
+ const fn = new Function('__scope', wrappedSource);
149
+ fn(scope);
101
150
  }
102
151
  catch (error) {
103
152
  console.warn('[UIRenderer] Compile error:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexart/ui-renderer",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Lightweight Preview Runtime for NexArt Protocol. Non-canonical, FPS-capped continuous animation (8 FPS, max 900px canvas).",
5
5
  "license": "MIT",
6
6
  "type": "module",