@nexart/ui-renderer 0.8.8 → 0.9.1

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