@nexart/ui-renderer 0.2.1 → 0.3.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.
Files changed (38) hide show
  1. package/README.md +78 -4
  2. package/dist/compiler.d.ts +18 -2
  3. package/dist/compiler.d.ts.map +1 -1
  4. package/dist/compiler.js +25 -11
  5. package/dist/index.d.ts +19 -8
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +18 -7
  8. package/dist/presets/backgrounds.d.ts +14 -0
  9. package/dist/presets/backgrounds.d.ts.map +1 -0
  10. package/dist/presets/backgrounds.js +222 -0
  11. package/dist/presets/index.d.ts +7 -0
  12. package/dist/presets/index.d.ts.map +1 -0
  13. package/dist/presets/index.js +6 -0
  14. package/dist/presets/primitives.d.ts +16 -0
  15. package/dist/presets/primitives.d.ts.map +1 -0
  16. package/dist/presets/primitives.js +282 -0
  17. package/dist/presets/sketch-wrapper.d.ts +14 -0
  18. package/dist/presets/sketch-wrapper.d.ts.map +1 -0
  19. package/dist/presets/sketch-wrapper.js +70 -0
  20. package/dist/preview/code-renderer.d.ts +25 -0
  21. package/dist/preview/code-renderer.d.ts.map +1 -0
  22. package/dist/preview/code-renderer.js +651 -0
  23. package/dist/preview/primitives/sketch.d.ts +14 -0
  24. package/dist/preview/primitives/sketch.d.ts.map +1 -0
  25. package/dist/preview/primitives/sketch.js +407 -0
  26. package/dist/preview/renderer.d.ts +1 -1
  27. package/dist/preview/renderer.d.ts.map +1 -1
  28. package/dist/preview/renderer.js +23 -13
  29. package/dist/preview/unified-renderer.d.ts +16 -0
  30. package/dist/preview/unified-renderer.d.ts.map +1 -0
  31. package/dist/preview/unified-renderer.js +270 -0
  32. package/dist/system.d.ts +7 -3
  33. package/dist/system.d.ts.map +1 -1
  34. package/dist/system.js +187 -11
  35. package/dist/types.d.ts +125 -5
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js +12 -3
  38. package/package.json +2 -2
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Unified Renderer - Handles background, primitive, and sketch elements
3
+ * Dispatches rendering per element type in order: background → primitive → sketch
4
+ * All elements render to the same canvas/context with shared timing
5
+ */
6
+ import { compileBackgroundPreset, getPaletteColors } from '../presets/backgrounds';
7
+ import { compilePrimitive } from '../presets/primitives';
8
+ import { wrapSketch, validateSketchSafety } from '../presets/sketch-wrapper';
9
+ import { createP5Runtime } from './code-renderer';
10
+ function validateSketchElement(element) {
11
+ const { safe, warnings } = validateSketchSafety(element.code);
12
+ if (!safe) {
13
+ throw new Error(`Unsafe sketch code: ${warnings.join(', ')}`);
14
+ }
15
+ if (warnings.length > 0) {
16
+ console.warn('[SDK] Sketch warnings:', warnings);
17
+ }
18
+ }
19
+ function inferPalette(elements) {
20
+ for (const el of elements) {
21
+ if (el.type === 'background' && el.palette) {
22
+ return el.palette;
23
+ }
24
+ }
25
+ return 'offwhite-dark';
26
+ }
27
+ function extractFunctionBody(code, funcName) {
28
+ const pattern = new RegExp(`function\\s+${funcName}\\s*\\(\\s*\\)\\s*\\{`);
29
+ const match = code.match(pattern);
30
+ if (!match || match.index === undefined)
31
+ return '';
32
+ const startIndex = match.index + match[0].length;
33
+ let braceCount = 1;
34
+ let i = startIndex;
35
+ while (i < code.length && braceCount > 0) {
36
+ if (code[i] === '{')
37
+ braceCount++;
38
+ if (code[i] === '}')
39
+ braceCount--;
40
+ i++;
41
+ }
42
+ return code.slice(startIndex, i - 1).trim();
43
+ }
44
+ function extractDrawCode(code) {
45
+ const body = extractFunctionBody(code, 'draw');
46
+ return body || code.trim();
47
+ }
48
+ function extractSetupCode(code) {
49
+ return extractFunctionBody(code, 'setup');
50
+ }
51
+ function compileBackgroundElement(element, palette) {
52
+ const code = compileBackgroundPreset(element.preset, element.palette || palette, element.loop || { duration: 120 });
53
+ return {
54
+ setup: extractSetupCode(code),
55
+ draw: extractDrawCode(code),
56
+ };
57
+ }
58
+ function compilePrimitiveElement(element, foreground) {
59
+ const primitiveCode = compilePrimitive({
60
+ name: element.name,
61
+ count: element.count,
62
+ strokeWeight: element.strokeWeight,
63
+ motion: element.motion,
64
+ color: element.color,
65
+ opacity: element.opacity,
66
+ }, foreground);
67
+ return { setup: '', draw: primitiveCode };
68
+ }
69
+ function compileSketchElement(element) {
70
+ validateSketchElement(element);
71
+ const wrapped = wrapSketch({
72
+ code: element.code,
73
+ normalize: element.normalize !== false,
74
+ });
75
+ return {
76
+ setup: extractSetupCode(wrapped),
77
+ draw: extractDrawCode(wrapped),
78
+ };
79
+ }
80
+ export function renderUnifiedSystem(system, canvas, options = {}) {
81
+ const { showBadge = true, onPreview, onComplete, onError } = options;
82
+ canvas.width = system.width;
83
+ canvas.height = system.height;
84
+ const ctx = canvas.getContext('2d');
85
+ let animationId = null;
86
+ let isRunning = false;
87
+ let isDestroyed = false;
88
+ const palette = inferPalette(system.elements);
89
+ const colors = getPaletteColors(palette);
90
+ const totalFrames = system.loop?.duration ?? 120;
91
+ const sortedElements = [...system.elements].sort((a, b) => {
92
+ const order = { background: 0, primitive: 1, sketch: 2 };
93
+ return (order[a.type] ?? 3) - (order[b.type] ?? 3);
94
+ });
95
+ const backgroundElements = [];
96
+ const primitiveElements = [];
97
+ const sketchElements = [];
98
+ for (const element of sortedElements) {
99
+ switch (element.type) {
100
+ case 'background':
101
+ backgroundElements.push(compileBackgroundElement(element, palette));
102
+ break;
103
+ case 'primitive':
104
+ primitiveElements.push(compilePrimitiveElement(element, colors.foreground));
105
+ break;
106
+ case 'sketch':
107
+ sketchElements.push(compileSketchElement(element));
108
+ break;
109
+ }
110
+ }
111
+ const hasElements = system.elements.length > 0;
112
+ const hasRenderers = backgroundElements.length > 0 || primitiveElements.length > 0 || sketchElements.length > 0;
113
+ console.log('[UIRenderer] Compiled elements:', {
114
+ background: backgroundElements.length,
115
+ primitive: primitiveElements.length,
116
+ sketch: sketchElements.length,
117
+ backgroundDrawEmpty: backgroundElements.map(e => !e.draw.trim()),
118
+ primitiveDrawEmpty: primitiveElements.map(e => !e.draw.trim()),
119
+ sketchDrawEmpty: sketchElements.map(e => !e.draw.trim()),
120
+ });
121
+ if (hasElements && !hasRenderers) {
122
+ throw new Error('[UIRenderer] No renderers executed despite having elements');
123
+ }
124
+ const drawBadge = () => {
125
+ if (!showBadge)
126
+ return;
127
+ const text = '⚠️ Preview Renderer (Non-Canonical)';
128
+ ctx.font = '12px -apple-system, sans-serif';
129
+ const metrics = ctx.measureText(text);
130
+ const padding = 8;
131
+ const badgeWidth = metrics.width + padding * 2;
132
+ const badgeHeight = 24;
133
+ const x = system.width - badgeWidth - 10;
134
+ const y = 10;
135
+ ctx.fillStyle = 'rgba(255, 100, 100, 0.15)';
136
+ ctx.strokeStyle = 'rgba(255, 100, 100, 0.4)';
137
+ ctx.lineWidth = 1;
138
+ ctx.beginPath();
139
+ ctx.roundRect(x, y, badgeWidth, badgeHeight, 4);
140
+ ctx.fill();
141
+ ctx.stroke();
142
+ ctx.fillStyle = '#ff9999';
143
+ ctx.fillText(text, x + padding, y + 16);
144
+ };
145
+ const executeCode = (p, code, frame, t) => {
146
+ if (!code.trim())
147
+ return;
148
+ try {
149
+ const wrappedCode = new Function('p', 'frameCount', 't', 'time', 'tGlobal', `with(p) { ${code} }`);
150
+ wrappedCode(p, frame, t, t * (totalFrames / 30), t);
151
+ }
152
+ catch (err) {
153
+ console.error('[UIRenderer] Code execution error:', err);
154
+ }
155
+ };
156
+ const runSetup = (p) => {
157
+ let setupRan = false;
158
+ for (const el of backgroundElements) {
159
+ if (el.setup) {
160
+ executeCode(p, el.setup, 0, 0);
161
+ setupRan = true;
162
+ }
163
+ }
164
+ for (const el of primitiveElements) {
165
+ if (el.setup) {
166
+ executeCode(p, el.setup, 0, 0);
167
+ setupRan = true;
168
+ }
169
+ }
170
+ for (const el of sketchElements) {
171
+ if (el.setup) {
172
+ executeCode(p, el.setup, 0, 0);
173
+ setupRan = true;
174
+ }
175
+ }
176
+ if (!setupRan) {
177
+ p.strokeWeight(1.5);
178
+ }
179
+ };
180
+ const runDraw = (p, frame, t) => {
181
+ let elementsRendered = 0;
182
+ const hasBackground = backgroundElements.length > 0;
183
+ if (!hasBackground) {
184
+ p.background(colors.background);
185
+ }
186
+ for (const el of backgroundElements) {
187
+ if (el.draw) {
188
+ console.log('[UIRenderer] background rendered');
189
+ executeCode(p, el.draw, frame, t);
190
+ elementsRendered++;
191
+ }
192
+ }
193
+ for (const el of primitiveElements) {
194
+ if (el.draw) {
195
+ console.log('[UIRenderer] primitive rendered');
196
+ executeCode(p, el.draw, frame, t);
197
+ elementsRendered++;
198
+ }
199
+ }
200
+ for (const el of sketchElements) {
201
+ if (el.draw) {
202
+ console.log('[UIRenderer] sketch rendered');
203
+ executeCode(p, el.draw, frame, t);
204
+ elementsRendered++;
205
+ }
206
+ }
207
+ if (hasElements && elementsRendered === 0) {
208
+ throw new Error('[UIRenderer] Zero renderers executed in frame despite having elements');
209
+ }
210
+ };
211
+ const renderLoop = () => {
212
+ if (isDestroyed)
213
+ return;
214
+ try {
215
+ let frame = 0;
216
+ const p = createP5Runtime(canvas, system.width, system.height, system.seed);
217
+ runSetup(p);
218
+ const loop = () => {
219
+ if (!isRunning || isDestroyed)
220
+ return;
221
+ const t = frame / totalFrames;
222
+ p.randomSeed(system.seed);
223
+ p.noiseSeed(system.seed);
224
+ runDraw(p, frame, t);
225
+ drawBadge();
226
+ frame = (frame + 1) % totalFrames;
227
+ animationId = requestAnimationFrame(loop);
228
+ };
229
+ isRunning = true;
230
+ animationId = requestAnimationFrame(loop);
231
+ }
232
+ catch (error) {
233
+ const err = error instanceof Error ? error : new Error(String(error));
234
+ onError?.(err);
235
+ throw err;
236
+ }
237
+ };
238
+ const render = () => {
239
+ if (isDestroyed)
240
+ return;
241
+ renderLoop();
242
+ };
243
+ const start = () => {
244
+ if (isDestroyed)
245
+ return;
246
+ stop();
247
+ renderLoop();
248
+ };
249
+ const stop = () => {
250
+ isRunning = false;
251
+ if (animationId !== null) {
252
+ cancelAnimationFrame(animationId);
253
+ animationId = null;
254
+ }
255
+ };
256
+ const destroy = () => {
257
+ isDestroyed = true;
258
+ stop();
259
+ ctx.clearRect(0, 0, system.width, system.height);
260
+ };
261
+ const renderer = {
262
+ render,
263
+ start,
264
+ stop,
265
+ destroy,
266
+ isCanonical: false,
267
+ isArchival: false,
268
+ };
269
+ return renderer;
270
+ }
package/dist/system.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.2.0 - System Creation & Validation
2
+ * @nexart/ui-renderer v0.4.0 - System Creation & Validation
3
3
  *
4
- * Declarative system authoring for NexArt Protocol.
4
+ * Opinionated generative design system with aesthetic guardrails.
5
+ * Supports background, primitive, and sketch element types.
5
6
  */
6
- import type { NexArtSystemInput, NexArtSystem, ValidationResult } from './types';
7
+ import type { NexArtSystemInput, NexArtSystem, DeclarativeSystem, NexArtCodeSystem, UnifiedSystem, ValidationResult } from './types';
7
8
  export declare function validateSystem(input: NexArtSystemInput): ValidationResult;
8
9
  export declare function createSystem(input: NexArtSystemInput): NexArtSystem;
10
+ export declare function isCodeModeSystem(system: NexArtSystem): system is NexArtCodeSystem;
11
+ export declare function isUnifiedModeSystem(system: NexArtSystem): system is UnifiedSystem;
12
+ export declare function isDeclarativeSystem(system: NexArtSystem): system is DeclarativeSystem;
9
13
  //# sourceMappingURL=system.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EAEZ,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAoIjB,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,CA6BzE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CA0BnE"}
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EAEZ,iBAAiB,EAEjB,gBAAgB,EAEhB,aAAa,EAGb,gBAAgB,EAKjB,MAAM,SAAS,CAAC;AAiTjB,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,CAiBzE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CA6DnE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,IAAI,gBAAgB,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,IAAI,aAAa,CAEjF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,IAAI,iBAAiB,CAErF"}
package/dist/system.js CHANGED
@@ -1,12 +1,97 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.2.0 - System Creation & Validation
2
+ * @nexart/ui-renderer v0.4.0 - System Creation & Validation
3
3
  *
4
- * Declarative system authoring for NexArt Protocol.
4
+ * Opinionated generative design system with aesthetic guardrails.
5
+ * Supports background, primitive, and sketch element types.
5
6
  */
6
- const CURRENT_VERSION = '0.2';
7
+ const CURRENT_VERSION = '0.4';
8
+ function isCodeSystem(input) {
9
+ return 'type' in input && input.type === 'code';
10
+ }
11
+ function isUnifiedSystem(input) {
12
+ if (!('elements' in input) || !Array.isArray(input.elements)) {
13
+ return false;
14
+ }
15
+ const elements = input.elements;
16
+ return elements.length > 0 && elements.some((el) => el.type === 'background' || el.type === 'primitive' || el.type === 'sketch');
17
+ }
18
+ function isDeclarativeInput(input) {
19
+ return 'elements' in input && 'background' in input && Array.isArray(input.elements);
20
+ }
7
21
  const VALID_ELEMENT_TYPES = ['dots', 'lines', 'waves', 'grid', 'flowField', 'orbits'];
8
22
  const VALID_MOTION_SOURCES = ['none', 'time', 'seed'];
9
23
  const VALID_TEXTURES = ['none', 'noise', 'grain'];
24
+ const VALID_BACKGROUND_PRESETS = ['layered-waves', 'soft-noise-field', 'orbital-lines', 'flowing-stripes', 'minimal-grid'];
25
+ const VALID_PRIMITIVE_NAMES = ['waves', 'dots', 'lines', 'grid', 'flow', 'orbits', 'circles', 'stripes'];
26
+ const VALID_PALETTES = ['offwhite-dark', 'midnight', 'warm-neutral', 'ocean', 'sunset', 'forest'];
27
+ function validateUnifiedElement(el, index) {
28
+ const errors = [];
29
+ const prefix = `Element[${index}]`;
30
+ if (!el.type) {
31
+ errors.push(`${prefix}: type is required`);
32
+ return errors;
33
+ }
34
+ switch (el.type) {
35
+ case 'background':
36
+ if (!el.preset || !VALID_BACKGROUND_PRESETS.includes(el.preset)) {
37
+ errors.push(`${prefix}: preset must be one of: ${VALID_BACKGROUND_PRESETS.join(', ')}`);
38
+ }
39
+ if (el.palette && !VALID_PALETTES.includes(el.palette)) {
40
+ errors.push(`${prefix}: palette must be one of: ${VALID_PALETTES.join(', ')}`);
41
+ }
42
+ if (el.loop) {
43
+ if (typeof el.loop.duration !== 'number' || el.loop.duration < 1 || el.loop.duration > 600) {
44
+ errors.push(`${prefix}: loop.duration must be between 1 and 600`);
45
+ }
46
+ }
47
+ break;
48
+ case 'primitive':
49
+ if (!el.name || !VALID_PRIMITIVE_NAMES.includes(el.name)) {
50
+ errors.push(`${prefix}: name must be one of: ${VALID_PRIMITIVE_NAMES.join(', ')}`);
51
+ }
52
+ if (el.count !== undefined && (el.count < 1 || el.count > 100)) {
53
+ errors.push(`${prefix}: count must be between 1 and 100`);
54
+ }
55
+ if (el.opacity !== undefined && (el.opacity < 0 || el.opacity > 1)) {
56
+ errors.push(`${prefix}: opacity must be between 0 and 1`);
57
+ }
58
+ break;
59
+ case 'sketch':
60
+ if (!el.code || typeof el.code !== 'string' || el.code.trim().length === 0) {
61
+ errors.push(`${prefix}: code is required and must be a non-empty string`);
62
+ }
63
+ if (el.totalFrames !== undefined && (el.totalFrames < 1 || el.totalFrames > 600)) {
64
+ errors.push(`${prefix}: totalFrames must be between 1 and 600`);
65
+ }
66
+ break;
67
+ default:
68
+ errors.push(`${prefix}: type must be "background", "primitive", or "sketch"`);
69
+ }
70
+ return errors;
71
+ }
72
+ function validateUnifiedSystem(input) {
73
+ const errors = [];
74
+ if (!Array.isArray(input.elements) || input.elements.length === 0) {
75
+ errors.push('elements array is required and must not be empty');
76
+ return errors;
77
+ }
78
+ input.elements.forEach((el, i) => {
79
+ errors.push(...validateUnifiedElement(el, i));
80
+ });
81
+ if (input.width !== undefined && (input.width < 1 || input.width > 4096)) {
82
+ errors.push('width must be between 1 and 4096');
83
+ }
84
+ if (input.height !== undefined && (input.height < 1 || input.height > 4096)) {
85
+ errors.push('height must be between 1 and 4096');
86
+ }
87
+ if (!input.loop || typeof input.loop.duration !== 'number') {
88
+ errors.push('loop.duration is required for unified systems');
89
+ }
90
+ else if (input.loop.duration < 1 || input.loop.duration > 600) {
91
+ errors.push('loop.duration must be between 1 and 600');
92
+ }
93
+ return errors;
94
+ }
10
95
  function validateBackground(bg) {
11
96
  const errors = [];
12
97
  if (!bg.color || typeof bg.color !== 'string') {
@@ -112,7 +197,40 @@ function validateMotion(motion) {
112
197
  }
113
198
  return errors;
114
199
  }
115
- export function validateSystem(input) {
200
+ function validateCodeSystem(input) {
201
+ const errors = [];
202
+ if (input.type !== 'code') {
203
+ errors.push('type must be "code"');
204
+ }
205
+ if (!input.source || typeof input.source !== 'string') {
206
+ errors.push('source is required and must be a string');
207
+ }
208
+ if (input.source && input.source.trim().length === 0) {
209
+ errors.push('source cannot be empty');
210
+ }
211
+ if (!['static', 'loop'].includes(input.mode)) {
212
+ errors.push('mode must be "static" or "loop"');
213
+ }
214
+ if (typeof input.width !== 'number' || input.width < 1 || input.width > 4096) {
215
+ errors.push('width is required and must be between 1 and 4096');
216
+ }
217
+ if (typeof input.height !== 'number' || input.height < 1 || input.height > 4096) {
218
+ errors.push('height is required and must be between 1 and 4096');
219
+ }
220
+ if (input.mode === 'loop') {
221
+ if (typeof input.totalFrames !== 'number' || input.totalFrames < 1 || input.totalFrames > 600) {
222
+ errors.push('totalFrames is required for loop mode and must be between 1 and 600');
223
+ }
224
+ if (input.seed === undefined) {
225
+ errors.push('seed is required for loop mode to ensure determinism');
226
+ }
227
+ }
228
+ if (input.seed !== undefined && typeof input.seed !== 'number') {
229
+ errors.push('seed must be a number');
230
+ }
231
+ return errors;
232
+ }
233
+ function validateDeclarativeSystem(input) {
116
234
  const errors = [];
117
235
  if (typeof input.seed !== 'number') {
118
236
  errors.push('seed is required and must be a number');
@@ -134,6 +252,22 @@ export function validateSystem(input) {
134
252
  if (input.motion) {
135
253
  errors.push(...validateMotion(input.motion));
136
254
  }
255
+ return errors;
256
+ }
257
+ export function validateSystem(input) {
258
+ const errors = [];
259
+ if (isCodeSystem(input)) {
260
+ errors.push(...validateCodeSystem(input));
261
+ }
262
+ else if (isUnifiedSystem(input)) {
263
+ errors.push(...validateUnifiedSystem(input));
264
+ }
265
+ else if (isDeclarativeInput(input)) {
266
+ errors.push(...validateDeclarativeSystem(input));
267
+ }
268
+ else {
269
+ errors.push('Invalid system input: must be a code system, unified system, or declarative system');
270
+ }
137
271
  return {
138
272
  valid: errors.length === 0,
139
273
  errors,
@@ -144,22 +278,64 @@ export function createSystem(input) {
144
278
  if (!validation.valid) {
145
279
  throw new Error(`Invalid system: ${validation.errors.join('; ')}`);
146
280
  }
281
+ if (isCodeSystem(input)) {
282
+ const codeSystem = {
283
+ protocol: 'nexart',
284
+ systemType: 'code',
285
+ systemVersion: CURRENT_VERSION,
286
+ source: input.source,
287
+ mode: input.mode,
288
+ width: input.width,
289
+ height: input.height,
290
+ seed: input.seed ?? Math.floor(Math.random() * 2147483647),
291
+ totalFrames: input.totalFrames,
292
+ deterministic: true,
293
+ createdAt: new Date().toISOString(),
294
+ };
295
+ return codeSystem;
296
+ }
297
+ if (isUnifiedSystem(input)) {
298
+ const unifiedSystem = {
299
+ protocol: 'nexart',
300
+ systemType: 'unified',
301
+ systemVersion: CURRENT_VERSION,
302
+ seed: input.seed ?? Math.floor(Math.random() * 2147483647),
303
+ width: input.width ?? 800,
304
+ height: input.height ?? 800,
305
+ elements: input.elements,
306
+ loop: input.loop ?? { duration: 120 },
307
+ deterministic: true,
308
+ createdAt: new Date().toISOString(),
309
+ };
310
+ return unifiedSystem;
311
+ }
312
+ const declarativeInput = input;
147
313
  const system = {
148
314
  protocol: 'nexart',
149
- systemVersion: input.version || CURRENT_VERSION,
150
- seed: input.seed,
315
+ systemType: 'declarative',
316
+ systemVersion: declarativeInput.version || CURRENT_VERSION,
317
+ seed: declarativeInput.seed,
151
318
  background: {
152
- color: input.background.color,
153
- texture: input.background.texture || 'none',
154
- ...(input.background.gradient && { gradient: input.background.gradient }),
319
+ color: declarativeInput.background.color,
320
+ texture: declarativeInput.background.texture || 'none',
321
+ ...(declarativeInput.background.gradient && { gradient: declarativeInput.background.gradient }),
155
322
  },
156
- elements: input.elements.map((el) => ({
323
+ elements: declarativeInput.elements.map((el) => ({
157
324
  ...el,
158
325
  opacity: el.opacity ?? 1,
159
326
  })),
160
- motion: input.motion || { source: 'none' },
327
+ motion: declarativeInput.motion || { source: 'none' },
161
328
  deterministic: true,
162
329
  createdAt: new Date().toISOString(),
163
330
  };
164
331
  return system;
165
332
  }
333
+ export function isCodeModeSystem(system) {
334
+ return 'systemType' in system && system.systemType === 'code';
335
+ }
336
+ export function isUnifiedModeSystem(system) {
337
+ return 'systemType' in system && system.systemType === 'unified';
338
+ }
339
+ export function isDeclarativeSystem(system) {
340
+ return !('systemType' in system) || system.systemType === 'declarative';
341
+ }
package/dist/types.d.ts CHANGED
@@ -1,10 +1,107 @@
1
1
  /**
2
- * @nexart/ui-renderer v0.2.0 - Type Definitions
2
+ * @nexart/ui-renderer v0.4.0 - Type Definitions
3
3
  *
4
- * Declarative system authoring types for NexArt Protocol.
4
+ * Opinionated generative design system with aesthetic guardrails.
5
5
  * This SDK is non-canonical and for authoring/preview only.
6
6
  */
7
+ export declare const SDK_VERSION = "0.4.0";
8
+ export declare const AESTHETIC_DEFAULTS: {
9
+ readonly background: {
10
+ readonly r: 246;
11
+ readonly g: 245;
12
+ readonly b: 242;
13
+ };
14
+ readonly foreground: {
15
+ readonly r: 45;
16
+ readonly g: 45;
17
+ readonly b: 45;
18
+ };
19
+ readonly strokeWeight: {
20
+ readonly min: 0.5;
21
+ readonly max: 4;
22
+ readonly default: 1.5;
23
+ };
24
+ readonly density: {
25
+ readonly min: 3;
26
+ readonly max: 50;
27
+ readonly default: 12;
28
+ };
29
+ readonly motion: {
30
+ readonly speed: 0.5;
31
+ readonly easing: "sinusoidal";
32
+ };
33
+ readonly margins: {
34
+ readonly ratio: 0.08;
35
+ };
36
+ readonly loop: {
37
+ readonly defaultFrames: 120;
38
+ };
39
+ };
40
+ export type MotionSpeed = 'slow' | 'medium' | 'fast';
41
+ export type StrokeWeightAuto = 'auto' | 'thin' | 'medium' | 'thick' | number;
42
+ export interface LoopConfig {
43
+ duration: number;
44
+ fps?: number;
45
+ }
46
+ export type BackgroundPreset = 'layered-waves' | 'soft-noise-field' | 'orbital-lines' | 'flowing-stripes' | 'minimal-grid';
47
+ export type ColorPalette = 'offwhite-dark' | 'midnight' | 'warm-neutral' | 'ocean' | 'sunset' | 'forest';
48
+ export interface BackgroundElement {
49
+ type: 'background';
50
+ preset: BackgroundPreset;
51
+ palette?: ColorPalette;
52
+ loop?: LoopConfig;
53
+ seed?: number;
54
+ }
55
+ export type PrimitiveName = 'waves' | 'dots' | 'lines' | 'grid' | 'flow' | 'orbits' | 'circles' | 'stripes';
56
+ export interface PrimitiveElement {
57
+ type: 'primitive';
58
+ name: PrimitiveName;
59
+ count?: number;
60
+ strokeWeight?: StrokeWeightAuto;
61
+ motion?: MotionSpeed;
62
+ color?: string;
63
+ opacity?: number;
64
+ loop?: LoopConfig;
65
+ seed?: number;
66
+ }
67
+ export interface SketchElement {
68
+ type: 'sketch';
69
+ code: string;
70
+ totalFrames?: number;
71
+ seed?: number;
72
+ normalize?: boolean;
73
+ }
74
+ export type UnifiedElement = BackgroundElement | PrimitiveElement | SketchElement;
75
+ export interface UnifiedSystemInput {
76
+ version?: string;
77
+ seed?: number;
78
+ width?: number;
79
+ height?: number;
80
+ elements: UnifiedElement[];
81
+ loop?: LoopConfig;
82
+ }
83
+ export interface UnifiedSystem {
84
+ protocol: 'nexart';
85
+ systemType: 'unified';
86
+ systemVersion: string;
87
+ seed: number;
88
+ width: number;
89
+ height: number;
90
+ elements: UnifiedElement[];
91
+ loop: LoopConfig;
92
+ deterministic: true;
93
+ createdAt: string;
94
+ }
7
95
  export type BackgroundTexture = 'none' | 'noise' | 'grain';
96
+ export interface CodeSystem {
97
+ type: 'code';
98
+ source: string;
99
+ mode: 'static' | 'loop';
100
+ width: number;
101
+ height: number;
102
+ seed?: number;
103
+ totalFrames?: number;
104
+ }
8
105
  export interface BackgroundConfig {
9
106
  color: string;
10
107
  gradient?: {
@@ -65,21 +162,23 @@ export interface OrbitsElement {
65
162
  color?: string;
66
163
  opacity?: number;
67
164
  }
68
- export type SystemElement = DotsElement | LinesElement | WavesElement | GridElement | FlowFieldElement | OrbitsElement;
165
+ export type DeclarativeElement = DotsElement | LinesElement | WavesElement | GridElement | FlowFieldElement | OrbitsElement;
166
+ export type SystemElement = DeclarativeElement;
69
167
  export type MotionSource = 'none' | 'time' | 'seed';
70
168
  export interface MotionConfig {
71
169
  source: MotionSource;
72
170
  speed?: number;
73
171
  }
74
- export interface NexArtSystemInput {
172
+ export interface DeclarativeSystemInput {
75
173
  version?: string;
76
174
  seed: number;
77
175
  background: BackgroundConfig;
78
176
  elements: SystemElement[];
79
177
  motion?: MotionConfig;
80
178
  }
81
- export interface NexArtSystem {
179
+ export interface DeclarativeSystem {
82
180
  protocol: 'nexart';
181
+ systemType: 'declarative';
83
182
  systemVersion: string;
84
183
  seed: number;
85
184
  background: BackgroundConfig;
@@ -88,9 +187,30 @@ export interface NexArtSystem {
88
187
  deterministic: boolean;
89
188
  createdAt: string;
90
189
  }
190
+ export interface NexArtCodeSystem {
191
+ protocol: 'nexart';
192
+ systemType: 'code';
193
+ systemVersion: string;
194
+ source: string;
195
+ mode: 'static' | 'loop';
196
+ width: number;
197
+ height: number;
198
+ seed: number;
199
+ totalFrames?: number;
200
+ deterministic: boolean;
201
+ createdAt: string;
202
+ }
203
+ export type NexArtSystemInput = DeclarativeSystemInput | CodeSystem | UnifiedSystemInput;
204
+ export type NexArtSystem = DeclarativeSystem | NexArtCodeSystem | UnifiedSystem;
91
205
  export interface PreviewOptions {
92
206
  mode?: 'static' | 'loop';
93
207
  showBadge?: boolean;
208
+ onPreview?: (canvas: HTMLCanvasElement) => void;
209
+ onComplete?: (result: {
210
+ type: 'image' | 'video';
211
+ blob: Blob;
212
+ }) => void;
213
+ onError?: (error: Error) => void;
94
214
  }
95
215
  export interface ValidationResult {
96
216
  valid: boolean;