@nexart/ui-renderer 0.8.6 → 0.8.8
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.
|
|
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.
|
|
22
|
+
## v0.8.7 — Live Runtime Binding Fix
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Fixed critical bug where time-varying properties were frozen in loop mode.
|
|
25
25
|
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
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
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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.
|
|
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.
|
|
2
|
+
* @nexart/ui-renderer v0.8.8 - Code Mode Renderer
|
|
3
3
|
*
|
|
4
4
|
* ╔══════════════════════════════════════════════════════════════════════════╗
|
|
5
5
|
* ║ PREVIEW RENDERER — LIGHTWEIGHT, NON-AUTHORITATIVE ║
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* ║ This renderer is a preview-only runtime. ║
|
|
8
8
|
* ║ It does not guarantee determinism or protocol compliance. ║
|
|
9
9
|
* ║ ║
|
|
10
|
-
* ║ Performance:
|
|
10
|
+
* ║ Performance: Native requestAnimationFrame (~60 FPS), canvas max 900px ║
|
|
11
11
|
* ║ Animation: Runs continuously until stop() is called ║
|
|
12
12
|
* ║ ║
|
|
13
13
|
* ║ For minting, export, or validation: use @nexart/codemode-sdk ║
|
|
@@ -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;
|
|
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;AAiBjE,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,CA4Sd"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @nexart/ui-renderer v0.8.
|
|
2
|
+
* @nexart/ui-renderer v0.8.8 - Code Mode Renderer
|
|
3
3
|
*
|
|
4
4
|
* ╔══════════════════════════════════════════════════════════════════════════╗
|
|
5
5
|
* ║ PREVIEW RENDERER — LIGHTWEIGHT, NON-AUTHORITATIVE ║
|
|
@@ -7,14 +7,13 @@
|
|
|
7
7
|
* ║ This renderer is a preview-only runtime. ║
|
|
8
8
|
* ║ It does not guarantee determinism or protocol compliance. ║
|
|
9
9
|
* ║ ║
|
|
10
|
-
* ║ Performance:
|
|
10
|
+
* ║ Performance: Native requestAnimationFrame (~60 FPS), canvas max 900px ║
|
|
11
11
|
* ║ Animation: Runs continuously until stop() is called ║
|
|
12
12
|
* ║ ║
|
|
13
13
|
* ║ For minting, export, or validation: use @nexart/codemode-sdk ║
|
|
14
14
|
* ╚══════════════════════════════════════════════════════════════════════════╝
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
import { createFpsThrottle, shouldRenderFrame, recordFrame, resetThrottle, } from './frame-budget';
|
|
16
|
+
import { CANVAS_LIMITS, } from './preview-types';
|
|
18
17
|
import { calculateScaledDimensions, applyScaledDimensions, reapplyContextScale, clearCanvasIgnoringTransform, } from './canvas-scaler';
|
|
19
18
|
import { createPreviewRuntime } from './preview-runtime';
|
|
20
19
|
const PROTOCOL_VERSION = '1.2.0';
|
|
@@ -42,8 +41,8 @@ function normalizeVars(vars) {
|
|
|
42
41
|
return result;
|
|
43
42
|
}
|
|
44
43
|
export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
45
|
-
console.log('[UIRenderer] Preview mode →
|
|
46
|
-
console.log(`[UIRenderer]
|
|
44
|
+
console.log('[UIRenderer] Preview mode → native requestAnimationFrame (~60 FPS)');
|
|
45
|
+
console.log(`[UIRenderer] Canvas max ${CANVAS_LIMITS.MAX_DIMENSION}px`);
|
|
47
46
|
if (activeRendererInstance) {
|
|
48
47
|
activeRendererInstance.destroy();
|
|
49
48
|
activeRendererInstance = null;
|
|
@@ -61,7 +60,6 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
61
60
|
let animationId = null;
|
|
62
61
|
let isRunning = false;
|
|
63
62
|
let isDestroyed = false;
|
|
64
|
-
const throttle = createFpsThrottle();
|
|
65
63
|
const normalizedVars = normalizeVars(system.vars);
|
|
66
64
|
let runtime = null;
|
|
67
65
|
let setupFn = null;
|
|
@@ -85,19 +83,68 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
85
83
|
const totalFrames = system.totalFrames ?? 120;
|
|
86
84
|
runtime.totalFrames = totalFrames;
|
|
87
85
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
// ╔═══════════════════════════════════════════════════════════════════════╗
|
|
87
|
+
// ║ LIVE RUNTIME BINDING — v0.8.7 FIX ║
|
|
88
|
+
// ║ ║
|
|
89
|
+
// ║ Time-varying properties (frameCount, t, time, tGlobal, totalFrames) ║
|
|
90
|
+
// ║ must be accessed via getters that read from the live runtime object. ║
|
|
91
|
+
// ║ ║
|
|
92
|
+
// ║ We use a Proxy + with() pattern to enable bare-name access to live ║
|
|
93
|
+
// ║ runtime properties. The proxy's get trap reads from runtime for ║
|
|
94
|
+
// ║ time-varying props, and from a static cache for everything else. ║
|
|
95
|
+
// ╚═══════════════════════════════════════════════════════════════════════╝
|
|
96
|
+
// Time-varying properties that need live access
|
|
97
|
+
const liveProps = new Set(['frameCount', 't', 'time', 'tGlobal', 'totalFrames']);
|
|
98
|
+
// Cache static properties (functions, constants)
|
|
99
|
+
const staticCache = {};
|
|
100
|
+
for (const key of Object.keys(runtime)) {
|
|
101
|
+
if (!liveProps.has(key)) {
|
|
102
|
+
staticCache[key] = runtime[key];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
95
105
|
const registerSetup = (fn) => { setupFn = fn; };
|
|
96
106
|
const registerDraw = (fn) => { drawFn = fn; };
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
// Build the set of all known keys (for has trap)
|
|
108
|
+
const knownKeys = new Set([
|
|
109
|
+
...liveProps,
|
|
110
|
+
...Object.keys(staticCache),
|
|
111
|
+
'__registerSetup',
|
|
112
|
+
'__registerDraw'
|
|
113
|
+
]);
|
|
114
|
+
// Create a proxy that provides live access to time-varying properties
|
|
115
|
+
// IMPORTANT: has() must return true ONLY for known keys
|
|
116
|
+
// Otherwise globals (Math, window, etc.) are masked and become undefined
|
|
117
|
+
const runtimeRef = runtime;
|
|
118
|
+
const scope = new Proxy({}, {
|
|
119
|
+
has: (_, prop) => knownKeys.has(prop),
|
|
120
|
+
get: (_, prop) => {
|
|
121
|
+
// Time-varying properties - read live from runtime
|
|
122
|
+
if (liveProps.has(prop)) {
|
|
123
|
+
return runtimeRef[prop];
|
|
124
|
+
}
|
|
125
|
+
// Special functions
|
|
126
|
+
if (prop === '__registerSetup')
|
|
127
|
+
return registerSetup;
|
|
128
|
+
if (prop === '__registerDraw')
|
|
129
|
+
return registerDraw;
|
|
130
|
+
// Static properties from cache
|
|
131
|
+
if (prop in staticCache)
|
|
132
|
+
return staticCache[prop];
|
|
133
|
+
// Fall through to undefined (should not hit this if has() is correct)
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// Use with() to make the proxy scope available to bare variable names
|
|
138
|
+
// Note: with() is safe here since we control the scope completely
|
|
139
|
+
const wrappedSource = `
|
|
140
|
+
with (__scope) {
|
|
141
|
+
${system.source}
|
|
142
|
+
if (typeof setup === 'function') __registerSetup(setup);
|
|
143
|
+
if (typeof draw === 'function') __registerDraw(draw);
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
const fn = new Function('__scope', wrappedSource);
|
|
147
|
+
fn(scope);
|
|
101
148
|
}
|
|
102
149
|
catch (error) {
|
|
103
150
|
console.warn('[UIRenderer] Compile error:', error);
|
|
@@ -168,15 +215,14 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
168
215
|
if (setupFn) {
|
|
169
216
|
setupFn();
|
|
170
217
|
}
|
|
171
|
-
resetThrottle(throttle);
|
|
172
218
|
isRunning = true;
|
|
173
219
|
// ╔═══════════════════════════════════════════════════════════════════════╗
|
|
174
|
-
// ║ ANIMATION LOOP —
|
|
220
|
+
// ║ ANIMATION LOOP — NATIVE requestAnimationFrame (~60 FPS) ║
|
|
175
221
|
// ║ ║
|
|
176
|
-
// ║
|
|
177
|
-
// ║
|
|
222
|
+
// ║ v0.8.8: Removed FPS throttle for smooth rendering matching NexArt. ║
|
|
223
|
+
// ║ Browser handles frame pacing naturally via requestAnimationFrame. ║
|
|
178
224
|
// ║ Looping uses modulo math: t = (frame % total) / total ║
|
|
179
|
-
// ║ Canvas cleared before each draw
|
|
225
|
+
// ║ Canvas cleared before each draw() call. ║
|
|
180
226
|
// ╚═══════════════════════════════════════════════════════════════════════╝
|
|
181
227
|
const loop = () => {
|
|
182
228
|
// Schedule next frame first — loop runs until stop()
|
|
@@ -184,10 +230,6 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
184
230
|
// Exit if stopped or destroyed
|
|
185
231
|
if (!isRunning || isDestroyed)
|
|
186
232
|
return;
|
|
187
|
-
// FPS throttle — skip if not enough time has passed
|
|
188
|
-
if (!shouldRenderFrame(throttle)) {
|
|
189
|
-
return; // Preserve current canvas
|
|
190
|
-
}
|
|
191
233
|
frameCount++;
|
|
192
234
|
// Update runtime timing using modulo for natural looping
|
|
193
235
|
if (runtime) {
|
|
@@ -202,7 +244,6 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
202
244
|
if (drawFn)
|
|
203
245
|
drawFn();
|
|
204
246
|
drawBadge();
|
|
205
|
-
recordFrame(throttle);
|
|
206
247
|
}
|
|
207
248
|
catch (error) {
|
|
208
249
|
console.warn('[UIRenderer] Draw error:', error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexart/ui-renderer",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "Lightweight Preview Runtime for NexArt Protocol. Non-canonical,
|
|
3
|
+
"version": "0.8.8",
|
|
4
|
+
"description": "Lightweight Preview Runtime for NexArt Protocol. Non-canonical, native requestAnimationFrame (~60 FPS, max 900px canvas).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|