@nexart/ui-renderer 0.8.8 → 0.9.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,458 @@
1
+ # Changelog
2
+
3
+ All notable changes to @nexart/ui-renderer will be documented in this file.
4
+
5
+ ---
6
+
7
+ ## [0.9.0] — 2026-01-24
8
+
9
+ ### Added
10
+
11
+ - **Budget exceed callbacks**: `onBudgetExceeded` callback fires when frame or time limits are reached
12
+ - **Budget overlay**: Optional overlay displayed when preview stops due to budget (default: true for 'stop' behavior)
13
+ - **Budget behavior options**: `budgetBehavior: 'stop' | 'degrade'` — stop with overlay or continue with frame skipping
14
+ - **getPreviewStats()**: Full observability into runtime state (mode, scale, frames, stride, time, budgetExceeded)
15
+ - **previewScale**: Public readonly property exposing current canvas scale factor
16
+ - **createPreviewRuntime()**: New recommended entrypoint (alias for createPreviewEngine)
17
+ - **toCanonicalRequest()**: Helper for handoff to @nexart/codemode-sdk
18
+
19
+ ### Changed
20
+
21
+ - **No more silent stops**: Budget exceeded now triggers callbacks + console warning instead of silent termination
22
+ - **Default budget limits**: MAX_FRAMES: 1800 (~30 min at ~60 FPS), MAX_TOTAL_TIME_MS: 300000 (5 min)
23
+ - **Animation remains smooth**: Native RAF cadence (~60 FPS) preserved from v0.8.8 — no FPS throttle
24
+ - **README rewritten**: Agent-first documentation with 20-second quickstart, troubleshooting section
25
+ - **Version synced**: package.json, index.ts, README.md all show 0.9.0
26
+
27
+ ### Added Types
28
+
29
+ - `PreviewStats` — Runtime stats object
30
+ - `BudgetExceededInfo` — Callback info ({ reason, framesRendered, totalTimeMs, stride, scale })
31
+ - `BudgetExceedReason` — 'frame_limit' | 'time_limit'
32
+ - `BudgetBehavior` — 'stop' | 'degrade'
33
+ - `CanonicalRequest` — Handoff object for codemode-sdk
34
+
35
+ ### Examples
36
+
37
+ - `examples/basic-preview.ts` — Basic usage with stats logging
38
+ - `examples/budget-demo.ts` — Budget handling demonstration
39
+
40
+ ### Migration
41
+
42
+ No breaking changes. All v0.8.x exports are preserved.
43
+
44
+ New recommended pattern:
45
+ ```typescript
46
+ // Old (still works)
47
+ import { createPreviewEngine } from '@nexart/ui-renderer';
48
+
49
+ // New (recommended)
50
+ import { createPreviewRuntime } from '@nexart/ui-renderer';
51
+ ```
52
+
53
+ ---
54
+
55
+ ## [0.8.8] — 2026-01-04
56
+
57
+ ### Changed
58
+
59
+ - **REMOVED FPS THROTTLE**: Animation now renders at native browser refresh rate (~60 FPS)
60
+ - Matches smooth rendering experience of main NexArt app
61
+ - No more 8 FPS stuttering for loop animations
62
+
63
+ ### Why
64
+
65
+ The 8 FPS throttle (added in v0.8.6) caused poor UX with stuttery animations, while the main NexArt app renders smoothly using native `requestAnimationFrame`. Since the UI renderer's only job is to display sketches on canvas, it should render at the browser's natural frame rate.
66
+
67
+ ### Breaking Changes
68
+
69
+ None. API remains identical.
70
+
71
+ ### Technical Details
72
+
73
+ Removed:
74
+ - `shouldRenderFrame(throttle)` check in animation loop
75
+ - `recordFrame(throttle)` call after draw
76
+ - FPS throttle imports and initialization
77
+
78
+ Now uses native `requestAnimationFrame` without artificial frame rate limiting.
79
+
80
+ ---
81
+
82
+ ## [0.8.7] — 2026-01-03
83
+
84
+ ### Fixed
85
+
86
+ - **CRITICAL**: Time-varying properties (`frameCount`, `t`, `time`, `tGlobal`, `totalFrames`) were frozen in loop mode
87
+ - Animations now render correctly with live values updating every frame
88
+
89
+ ### Root Cause
90
+
91
+ The previous implementation used `new Function(...Object.keys(runtime), source)(...Object.values(runtime))`,
92
+ which captured primitive values BY VALUE at compile time. Updates to `runtime.frameCount` in the
93
+ animation loop were invisible to the sketch because it held frozen copies.
94
+
95
+ ### Solution
96
+
97
+ Replaced the parameter-spreading approach with a Proxy + `with()` pattern:
98
+ 1. Static properties (functions, constants) are cached once
99
+ 2. Time-varying properties read from the live runtime object via Proxy get trap
100
+ 3. `with(__scope)` enables bare-name access without changing sketch syntax
101
+ 4. `has()` trap returns true ONLY for known keys, allowing globals (Math, window) to fall through
102
+
103
+ **Animation Semantics:**
104
+ ```javascript
105
+ function draw() {
106
+ // These values update every frame:
107
+ // - frameCount: 1, 2, 3, ...
108
+ // - t: (frameCount % totalFrames) / totalFrames
109
+ // - time, tGlobal: aliases for t
110
+ circle(width/2 + sin(frameCount * 0.1) * 300, height/2, 120);
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## [0.8.6] — 2026-01-03 (SUPERSEDED)
117
+
118
+ ### Note: FPS throttle worked but time variables were still frozen. See v0.8.7.
119
+
120
+ ---
121
+
122
+ ## [0.8.5] — 2026-01-03 (SUPERSEDED)
123
+
124
+ ### Note: Budget approach was still problematic.
125
+
126
+ ---
127
+
128
+ ## [0.8.4] — 2026-01-03 (SUPERSEDED)
129
+
130
+ ### Note: This version had incorrect reset semantics.
131
+
132
+ ---
133
+
134
+ ## [0.8.3] — 2026-01-03
135
+
136
+ ### Fixed
137
+
138
+ - Fixed critical bug where preview only rendered a single frame
139
+ - Animation loop now runs continuously with invisible performance throttling
140
+ - Budget exhaustion no longer terminates the render loop
141
+
142
+ ### Details
143
+
144
+ The render loop conditionally scheduled `requestAnimationFrame`, causing the animation
145
+ to terminate after the first frame or after budget reset.
146
+
147
+ **Before (broken):**
148
+ ```typescript
149
+ const loop = () => {
150
+ if (!canRenderFrame(budget)) {
151
+ isRunning = false;
152
+ return; // Loop terminates — RAF never scheduled
153
+ }
154
+ draw();
155
+ animationId = requestAnimationFrame(loop); // Conditional — WRONG!
156
+ };
157
+ ```
158
+
159
+ **After (fixed):**
160
+ ```typescript
161
+ const loop = () => {
162
+ animationId = requestAnimationFrame(loop); // ALWAYS scheduled first
163
+
164
+ if (!canRenderFrame(budget)) {
165
+ return; // Skip draw, but loop continues
166
+ }
167
+ draw();
168
+ };
169
+ ```
170
+
171
+ **Animation Loop Invariant (locked for v0.x):**
172
+ - `requestAnimationFrame(loop)` MUST be called on every tick
173
+ - Budget gates ONLY whether `draw()` executes, NOT whether loop runs
174
+ - Never return before scheduling RAF. Never conditionally schedule.
175
+
176
+ ---
177
+
178
+ ## [0.8.2] — 2026-01-03
179
+
180
+ ### Fixed
181
+
182
+ - Fixed critical bug where preview scaling affected `width`/`height` inside sketches
183
+ - Runtime now uses original (protocol) dimensions for width/height semantics
184
+ - Loop animations and geometry math now work correctly
185
+
186
+ ### Details
187
+
188
+ The v0.8.0–v0.8.1 releases passed scaled dimensions to the preview runtime, which broke
189
+ sketch semantics. When a 1950×2400 canvas was downscaled to 900px for performance:
190
+
191
+ **Before (broken):**
192
+ ```typescript
193
+ // width = 675, height = 900 (scaled — WRONG!)
194
+ createPreviewRuntime(canvas, scaled.renderWidth, scaled.renderHeight, seed);
195
+ ```
196
+
197
+ **After (fixed):**
198
+ ```typescript
199
+ // width = 1950, height = 2400 (original — CORRECT!)
200
+ createPreviewRuntime(canvas, scaled.originalWidth, scaled.originalHeight, seed);
201
+ ```
202
+
203
+ **Key rule enforced:** Preview scaling is a rendering concern, not a semantic one.
204
+ Canvas buffer may be downscaled for performance, but `width`/`height` must always
205
+ match Code Mode to ensure loop animations and geometry math work correctly.
206
+
207
+ ---
208
+
209
+ ## [0.8.1] — 2026-01-03
210
+
211
+ ### Fixed
212
+
213
+ - Fixed canvas zoom/cropping caused by resolution downscaling
214
+ - Reapplied context scale after canvas resize
215
+ - Ensured clearRect ignores active transforms
216
+
217
+ ### Details
218
+
219
+ Canvas resizing resets the 2D context transform. The v0.8.0 resolution downscaling feature
220
+ introduced a visual bug where previews appeared zoomed or cropped. This patch ensures:
221
+
222
+ 1. **Scale Transform Reapplied**: After `applyScaledDimensions()`, the context scale is
223
+ immediately restored via new `reapplyContextScale()` function.
224
+
225
+ 2. **Transform-Safe Clear**: `clearRect()` now uses `clearCanvasIgnoringTransform()` which
226
+ saves/restores the context to clear the full canvas regardless of active transforms.
227
+
228
+ 3. **No API Changes**: All fixes are internal — no changes to the public API.
229
+
230
+ ---
231
+
232
+ ## [0.8.0] — 2026-01-03
233
+
234
+ ### Lightweight Preview Runtime — Major Refactor
235
+
236
+ This release refactors the UI renderer into a lightweight, performance-optimized preview runtime.
237
+
238
+ #### Philosophy Change
239
+
240
+ **The UI renderer is now explicitly preview-only:**
241
+ - NOT canonical — does not guarantee determinism
242
+ - NOT archival — not for minting or export
243
+ - NOT validation — does not enforce protocol compliance
244
+
245
+ For canonical output, minting, or validation: use `@nexart/codemode-sdk`.
246
+
247
+ #### New: Execution Budget (MANDATORY)
248
+
249
+ The preview runtime enforces hard limits to prevent browser freezes:
250
+
251
+ ```typescript
252
+ PREVIEW_BUDGET = {
253
+ MAX_FRAMES: 30, // Maximum frames before auto-stop
254
+ MAX_TOTAL_TIME_MS: 500, // Maximum execution time
255
+ TARGET_FRAME_TIME_MS: 16, // ~60fps target
256
+ FRAME_STRIDE: 3, // Render every 3rd frame
257
+ };
258
+ ```
259
+
260
+ If limits exceeded: rendering stops silently (no throw).
261
+
262
+ #### New: Canvas Scaling
263
+
264
+ Preview renderer no longer renders at full mint resolution:
265
+
266
+ ```typescript
267
+ CANVAS_LIMITS = {
268
+ MAX_DIMENSION: 900, // Max width/height
269
+ MIN_DIMENSION: 100,
270
+ };
271
+ ```
272
+
273
+ Aspect ratio preserved, CSS scaling for display.
274
+
275
+ #### New: Preview Engine API
276
+
277
+ ```typescript
278
+ import {
279
+ createPreviewEngine,
280
+ renderStaticPreview,
281
+ stopActivePreview,
282
+ PREVIEW_BUDGET,
283
+ CANVAS_LIMITS,
284
+ } from '@nexart/ui-renderer';
285
+
286
+ // Create preview renderer
287
+ const engine = createPreviewEngine({
288
+ canvas: canvasElement,
289
+ source: sketchCode,
290
+ mode: 'loop',
291
+ width: 1950, // Will be scaled to 900px
292
+ height: 2400,
293
+ seed: 12345,
294
+ vars: [50, 75, 25],
295
+ });
296
+
297
+ // Start/stop
298
+ engine.startLoop();
299
+ engine.stopLoop();
300
+ engine.destroy();
301
+ ```
302
+
303
+ #### Changed
304
+
305
+ - `renderCodeModeSystem()` now uses preview runtime with budget limits
306
+ - `renderUnifiedSystem()` updated to use new preview runtime
307
+ - Pattern warnings logged to console instead of throwing
308
+ - All renderers now include scaling and budget enforcement
309
+
310
+ #### Added Files
311
+
312
+ - `src/preview/preview-types.ts` — Type definitions and constants
313
+ - `src/preview/frame-budget.ts` — Execution budget manager
314
+ - `src/preview/canvas-scaler.ts` — Resolution scaling utilities
315
+ - `src/preview/preview-engine.ts` — Lightweight preview executor
316
+ - `src/preview/preview-runtime.ts` — Simplified p5-like runtime
317
+
318
+ ---
319
+
320
+ ## [0.7.0] — 2026-01-02
321
+
322
+ ### Protocol v1.2.0 Mirror Update
323
+
324
+ This release updates to mirror @nexart/codemode-sdk v1.4.0 (Protocol v1.2.0, Phase 3).
325
+
326
+ #### New Features Mirrored
327
+ - **Vertex Functions**: `curveVertex(x, y)`, `bezierVertex(cx1, cy1, cx2, cy2, x, y)`
328
+ - **Pixel System**: `loadPixels()`, `updatePixels()`, `pixels[]`, `get(x, y)`, `set(x, y, color)`
329
+ - **Graphics System**: `createGraphics(w, h)`, `image(pg, x, y, w, h)`
330
+ - **Time Variable**: `totalFrames` now available in Loop Mode
331
+
332
+ #### Changed
333
+ - Updated dependency to @nexart/codemode-sdk ^1.4.0
334
+ - Protocol version updated to v1.2.0 (Phase 3)
335
+ - Documentation updated to reflect new capabilities
336
+
337
+ ---
338
+
339
+ ## [0.6.0] — 2024-12-30
340
+
341
+ ### Minor Bump for npm Publishing
342
+
343
+ Version bump for npm release. Mirrors @nexart/codemode-sdk v1.1.0.
344
+
345
+ - Updated all version references from 0.5.0 to 0.6.0
346
+ - Updated codemode-sdk mirror reference from v1.0.2 to v1.1.0
347
+ - No breaking changes from 0.5.0
348
+
349
+ ---
350
+
351
+ ## [0.5.0] — 2024-12-30
352
+
353
+ ### VAR Parity Update (Mirrors @nexart/codemode-sdk v1.0.2)
354
+
355
+ **VAR input is now optional (0-10 elements):**
356
+ - Omit `vars` or pass `[]` for empty (defaults to all zeros)
357
+ - Input length must be 0-10 (throws if > 10)
358
+ - Values must be finite numbers in [0, 100] (throws if out of range, NO clamping)
359
+ - Runtime VAR is ALWAYS 10 elements (padded with zeros for consistency)
360
+
361
+ **Breaking Change Notice:**
362
+ - Previously: VAR required exactly 10 elements (error if not 10)
363
+ - Now: VAR accepts 0-10 elements (error if > 10, padded to 10)
364
+ - Existing code passing 10 elements works unchanged (backwards compatible)
365
+
366
+ **Enforcement Updated:**
367
+ - Out-of-range values now throw errors (previously warned)
368
+ - Non-finite numbers now throw errors
369
+ - Write attempts now throw detailed protocol errors
370
+
371
+ ### 30 Declarative Primitives
372
+
373
+ Expanded primitive library from 8 to 30 elements:
374
+
375
+ **Basic Shapes (6):** dots, lines, waves, stripes, circles, grid
376
+
377
+ **Geometric (5):** polygons, diamonds, hexgrid, stars, concentricSquares
378
+
379
+ **Radial (7):** spirals, rays, orbits, rings, arcs, radialLines, petals
380
+
381
+ **Flow & Motion (3):** flow, particles, bubbles
382
+
383
+ **Patterns (5):** crosshatch, chevrons, zigzag, weave, moire
384
+
385
+ **Organic (4):** curves, noise, mesh, branches
386
+
387
+ ### AI Capabilities API (Locked)
388
+
389
+ **Primitives are now marked as NON-CANONICAL helper generators.**
390
+
391
+ New exports for AI agents:
392
+ - `getCapabilities()` — Full SDK capabilities with primitive metadata
393
+ - `getPrimitiveTypes()` — All 30 primitive names as array
394
+ - `getPrimitivesByCategory()` — Primitives organized by category
395
+ - `getPrimitiveInfo(name)` — Detailed info for single primitive
396
+ - `isPrimitiveValid(name)` — Validate primitive name
397
+
398
+ Each primitive now includes:
399
+ - `name` — Unique identifier
400
+ - `category` — basic, geometric, radial, flow, patterns, organic
401
+ - `description` — Human-readable description
402
+ - `compilesTo` — Always 'codemode'
403
+ - `isCanonical` — Always false
404
+ - `parameters` — count, color, opacity, strokeWeight, motion with ranges
405
+
406
+ ### Changes
407
+
408
+ - `normalizeVars()` updated to match codemode-sdk v1.0.2 exactly
409
+ - `createProtectedVAR()` updated to use frozen 10-element array with Proxy
410
+ - `validateCodeSystem()` updated for 0-10 length validation
411
+ - Smoke tests updated for VAR parity verification
412
+ - Added 22 new primitive generators in `primitives.ts`
413
+ - Updated `PrimitiveName` type to include all 30 primitives
414
+ - Rewrote `capabilities.ts` with comprehensive primitive metadata
415
+ - Added `primitivesMeta` to capabilities for AI consumption
416
+ - Exported new helper functions: `getPrimitivesByCategory`, `getPrimitiveInfo`, `isPrimitiveValid`
417
+ - Updated SDK version to 0.5.0
418
+
419
+ ---
420
+
421
+ ## [0.4.0] — 2024-12-29
422
+
423
+ ### Protocol Lock (Mirror)
424
+
425
+ **This SDK now mirrors NexArt Code Mode Protocol v1.0.0.**
426
+
427
+ This is a MIRROR SDK, not an authority. All protocol semantics are defined by @nexart/codemode-sdk.
428
+
429
+ ### Aligned with v1.0.0
430
+
431
+ This SDK mirrors the frozen protocol surface:
432
+
433
+ - **Execution Model**: Static and Loop modes
434
+ - **VAR[0..9]**: Exactly 10 read-only protocol variables
435
+ - **Error Messages**: Identical format `[Code Mode Protocol Error]`
436
+ - **Forbidden Patterns**: 13 patterns rejected (same as SDK)
437
+ - **Validation**: Same structural checks (length, type, range)
438
+
439
+ ### Changes
440
+
441
+ - VAR validation now matches SDK exactly (throws for length/type, warns for range)
442
+ - Forbidden pattern errors use SDK format
443
+ - Documentation updated with Protocol Lock section
444
+ - Smoke tests added for cross-SDK verification
445
+
446
+ ### Authority Boundaries
447
+
448
+ | Aspect | This SDK | @nexart/codemode-sdk |
449
+ |--------|----------|---------------------|
450
+ | Authority | Mirror only | Canonical gate |
451
+ | Determinism | Mirrors behavior | Authoritative |
452
+ | Output | Preview-quality | Archival-quality |
453
+
454
+ ### Notes
455
+
456
+ - This SDK exists for preview and prototyping
457
+ - Production minting uses @nexart/codemode-sdk
458
+ - Any protocol change requires v2.0.0 in SDK first