@viji-dev/core 0.20.0 → 0.21.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.
@@ -988,6 +988,92 @@ declare global {
988
988
  textureData: Uint8Array;
989
989
  }
990
990
 
991
+ /**
992
+ * Configuration object passed to `viji.model()`. The host renders a file
993
+ * picker for 3D model files; the user-supplied file becomes available as a
994
+ * {@link ModelValue} (bytes plus a blob URL) that your own loader parses.
995
+ */
996
+ interface ModelConfig {
997
+ /**
998
+ * Advanced: explicit parameter identity. Normally you do NOT set this —
999
+ * the parameter's name is inferred from the variable or object key you
1000
+ * assign it to (`const statue = viji.model(...)` → `statue`). Set `name`
1001
+ * only for parameters not assigned to a variable or key (created in an
1002
+ * array, loop, or factory). Must be a valid identifier and scene-globally
1003
+ * unique. When set, it overrides the inferred name.
1004
+ */
1005
+ name?: string;
1006
+ /** Display name shown next to the file picker in the parameter UI. Optional — defaults to the parameter's name. */
1007
+ label?: string;
1008
+ /** Optional tooltip / help text shown next to the control. */
1009
+ description?: string;
1010
+ /** Group name for organizing related parameters under a shared heading. Default: `'general'`. */
1011
+ group?: string;
1012
+ /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1013
+ category?: ParameterCategory;
1014
+ /**
1015
+ * Required: the formats your scene can parse, for example `['glb']` for a
1016
+ * three.js `GLTFLoader` scene or `['obj', 'stl']` for p5 `loadModel()`.
1017
+ * The host filters its picker by this list and core rejects files of other
1018
+ * formats before the scene sees them. Must not be empty; list only what
1019
+ * your loader handles.
1020
+ */
1021
+ formats: ModelFormat[];
1022
+ }
1023
+
1024
+ /**
1025
+ * Format tag of a 3D model file. One tag per supported file type; the
1026
+ * extension and canonical MIME type of each live in `MODEL_FORMATS`.
1027
+ */
1028
+ type ModelFormat = 'glb' | 'obj' | 'stl' | 'ply' | 'las' | 'laz' | 'splat';
1029
+
1030
+ /**
1031
+ * Reactive object returned by `viji.model()`. `value` is `null` until the user
1032
+ * provides a 3D model file.
1033
+ */
1034
+ interface ModelParameter {
1035
+ /** The current model as a {@link ModelValue}, or `null` until the user provides one. */
1036
+ value: ModelValue | null;
1037
+ /** Formats this parameter accepts (the `formats` you passed). */
1038
+ readonly formats: ReadonlyArray<ModelFormat>;
1039
+ /** Display name shown next to the upload field in the parameter UI. */
1040
+ label?: string;
1041
+ /** Tooltip / help text for the control (empty string when not configured). */
1042
+ description?: string;
1043
+ /** Group name under which the control appears in the UI. */
1044
+ group: string;
1045
+ /** Visibility category that controls when the parameter is shown. */
1046
+ category: ParameterCategory;
1047
+ }
1048
+
1049
+ /**
1050
+ * The model a `viji.model()` parameter currently holds: the file as bytes plus
1051
+ * a lazily created blob URL, tagged with its format. Core never parses the
1052
+ * file; pass `bytes` to a loader's `parse()` or `url` to its `load()`.
1053
+ *
1054
+ * Read `value` where you parse it. When the host replaces or clears the
1055
+ * model, core drops the bytes and revokes `url`; geometry you already built
1056
+ * from them stays valid, but a URL taken from the replaced value rejects
1057
+ * when loaded afterwards.
1058
+ */
1059
+ interface ModelValue {
1060
+ /** Format tag of the file, resolved by the host before transfer. */
1061
+ readonly format: ModelFormat;
1062
+ /**
1063
+ * The whole file, transferred from the host without a copy. `byteLength`
1064
+ * is the file size. Some loaders transfer the buffer to their own worker
1065
+ * and detach it; pass `bytes.slice(0)` to those, or disable their worker.
1066
+ */
1067
+ readonly bytes: ArrayBuffer;
1068
+ /**
1069
+ * Blob URL for loaders that take a URL (three.js `.load()`, p5 `loadModel()`).
1070
+ * Created on first read (a Blob copies the bytes, so it is never built
1071
+ * eagerly), cached, and revoked when the host replaces or clears the model.
1072
+ * Reading it on a value the host already replaced throws.
1073
+ */
1074
+ readonly url: string;
1075
+ }
1076
+
991
1077
  /**
992
1078
  * Mouse input state for the current frame: position, individual button states,
993
1079
  * per-frame movement, scroll wheel, and frame-based press / release events.
@@ -1470,7 +1556,7 @@ declare global {
1470
1556
  isEnding: boolean;
1471
1557
  }
1472
1558
 
1473
- const VERSION = "0.20.0";
1559
+ const VERSION = "0.21.0";
1474
1560
 
1475
1561
  /**
1476
1562
  * Real-time video API: drawable frame, dimensions, and the source-side
@@ -1771,6 +1857,20 @@ declare global {
1771
1857
  * if (tex.value) ctx.drawImage(tex.value, 0, 0, viji.width, viji.height);
1772
1858
  */
1773
1859
  image: (defaultValue: null, config: ImageConfig) => ImageParameter;
1860
+ /**
1861
+ * Declares a 3D model parameter. `formats` is required: list only the formats
1862
+ * your loader parses, so the host picker offers nothing the scene cannot show.
1863
+ * The user-supplied file becomes available as `value` (a `ModelValue` with
1864
+ * `bytes`, `url` and `format`); parse it with your own loader (three.js loaders,
1865
+ * p5 `loadModel()`, loaders.gl). Core never parses the file. Must be called at
1866
+ * the top level of the scene, never inside `render()`.
1867
+ *
1868
+ * @example
1869
+ * const statue = viji.model(null, { label: 'Statue', formats: ['glb'] });
1870
+ * // later, when statue.value changes:
1871
+ * loader.parse(statue.value.bytes, '', (gltf) => scene.add(gltf.scene));
1872
+ */
1873
+ model: (defaultValue: null, config: ModelConfig) => ModelParameter;
1774
1874
  /**
1775
1875
  * Declares a momentary button parameter. `value` is `true` for one frame after
1776
1876
  * the user clicks, then auto-resets. Perfect for triggering discrete events.
@@ -977,6 +977,92 @@ declare global {
977
977
  textureData: Uint8Array;
978
978
  }
979
979
 
980
+ /**
981
+ * Configuration object passed to `viji.model()`. The host renders a file
982
+ * picker for 3D model files; the user-supplied file becomes available as a
983
+ * {@link ModelValue} (bytes plus a blob URL) that your own loader parses.
984
+ */
985
+ interface ModelConfig {
986
+ /**
987
+ * Advanced: explicit parameter identity. Normally you do NOT set this —
988
+ * the parameter's name is inferred from the variable or object key you
989
+ * assign it to (`const statue = viji.model(...)` → `statue`). Set `name`
990
+ * only for parameters not assigned to a variable or key (created in an
991
+ * array, loop, or factory). Must be a valid identifier and scene-globally
992
+ * unique. When set, it overrides the inferred name.
993
+ */
994
+ name?: string;
995
+ /** Display name shown next to the file picker in the parameter UI. Optional — defaults to the parameter's name. */
996
+ label?: string;
997
+ /** Optional tooltip / help text shown next to the control. */
998
+ description?: string;
999
+ /** Group name for organizing related parameters under a shared heading. Default: `'general'`. */
1000
+ group?: string;
1001
+ /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1002
+ category?: ParameterCategory;
1003
+ /**
1004
+ * Required: the formats your scene can parse, for example `['glb']` for a
1005
+ * three.js `GLTFLoader` scene or `['obj', 'stl']` for p5 `loadModel()`.
1006
+ * The host filters its picker by this list and core rejects files of other
1007
+ * formats before the scene sees them. Must not be empty; list only what
1008
+ * your loader handles.
1009
+ */
1010
+ formats: ModelFormat[];
1011
+ }
1012
+
1013
+ /**
1014
+ * Format tag of a 3D model file. One tag per supported file type; the
1015
+ * extension and canonical MIME type of each live in `MODEL_FORMATS`.
1016
+ */
1017
+ type ModelFormat = 'glb' | 'obj' | 'stl' | 'ply' | 'las' | 'laz' | 'splat';
1018
+
1019
+ /**
1020
+ * Reactive object returned by `viji.model()`. `value` is `null` until the user
1021
+ * provides a 3D model file.
1022
+ */
1023
+ interface ModelParameter {
1024
+ /** The current model as a {@link ModelValue}, or `null` until the user provides one. */
1025
+ value: ModelValue | null;
1026
+ /** Formats this parameter accepts (the `formats` you passed). */
1027
+ readonly formats: ReadonlyArray<ModelFormat>;
1028
+ /** Display name shown next to the upload field in the parameter UI. */
1029
+ label?: string;
1030
+ /** Tooltip / help text for the control (empty string when not configured). */
1031
+ description?: string;
1032
+ /** Group name under which the control appears in the UI. */
1033
+ group: string;
1034
+ /** Visibility category that controls when the parameter is shown. */
1035
+ category: ParameterCategory;
1036
+ }
1037
+
1038
+ /**
1039
+ * The model a `viji.model()` parameter currently holds: the file as bytes plus
1040
+ * a lazily created blob URL, tagged with its format. Core never parses the
1041
+ * file; pass `bytes` to a loader's `parse()` or `url` to its `load()`.
1042
+ *
1043
+ * Read `value` where you parse it. When the host replaces or clears the
1044
+ * model, core drops the bytes and revokes `url`; geometry you already built
1045
+ * from them stays valid, but a URL taken from the replaced value rejects
1046
+ * when loaded afterwards.
1047
+ */
1048
+ interface ModelValue {
1049
+ /** Format tag of the file, resolved by the host before transfer. */
1050
+ readonly format: ModelFormat;
1051
+ /**
1052
+ * The whole file, transferred from the host without a copy. `byteLength`
1053
+ * is the file size. Some loaders transfer the buffer to their own worker
1054
+ * and detach it; pass `bytes.slice(0)` to those, or disable their worker.
1055
+ */
1056
+ readonly bytes: ArrayBuffer;
1057
+ /**
1058
+ * Blob URL for loaders that take a URL (three.js `.load()`, p5 `loadModel()`).
1059
+ * Created on first read (a Blob copies the bytes, so it is never built
1060
+ * eagerly), cached, and revoked when the host replaces or clears the model.
1061
+ * Reading it on a value the host already replaced throws.
1062
+ */
1063
+ readonly url: string;
1064
+ }
1065
+
980
1066
  /**
981
1067
  * Mouse input state for the current frame: position, individual button states,
982
1068
  * per-frame movement, scroll wheel, and frame-based press / release events.
@@ -1459,7 +1545,7 @@ declare global {
1459
1545
  isEnding: boolean;
1460
1546
  }
1461
1547
 
1462
- const VERSION = "0.20.0";
1548
+ const VERSION = "0.21.0";
1463
1549
 
1464
1550
  /**
1465
1551
  * Real-time video API: drawable frame, dimensions, and the source-side
@@ -1760,6 +1846,20 @@ declare global {
1760
1846
  * if (tex.value) ctx.drawImage(tex.value, 0, 0, viji.width, viji.height);
1761
1847
  */
1762
1848
  image: (defaultValue: null, config: ImageConfig) => ImageParameter;
1849
+ /**
1850
+ * Declares a 3D model parameter. `formats` is required: list only the formats
1851
+ * your loader parses, so the host picker offers nothing the scene cannot show.
1852
+ * The user-supplied file becomes available as `value` (a `ModelValue` with
1853
+ * `bytes`, `url` and `format`); parse it with your own loader (three.js loaders,
1854
+ * p5 `loadModel()`, loaders.gl). Core never parses the file. Must be called at
1855
+ * the top level of the scene, never inside `render()`.
1856
+ *
1857
+ * @example
1858
+ * const statue = viji.model(null, { label: 'Statue', formats: ['glb'] });
1859
+ * // later, when statue.value changes:
1860
+ * loader.parse(statue.value.bytes, '', (gltf) => scene.add(gltf.scene));
1861
+ */
1862
+ model: (defaultValue: null, config: ModelConfig) => ModelParameter;
1763
1863
  /**
1764
1864
  * Declares a momentary button parameter. `value` is `true` for one frame after
1765
1865
  * the user clicks, then auto-resets. Perfect for triggering discrete events.
@@ -82,6 +82,17 @@
82
82
  * @property {ParameterCategory} [category] - Visibility category. The control hides when its capability is unavailable. Default: `'general'`.
83
83
  */
84
84
 
85
+ /**
86
+ * Configuration object passed to `viji.model()`. The host renders a file picker for 3D model files; the user-supplied file becomes available as a {@link ModelValue} (bytes plus a blob URL) that your own loader parses.
87
+ * @typedef {Object} ModelConfig
88
+ * @property {string} [name] - Advanced: explicit parameter identity. Normally you do NOT set this — the parameter's name is inferred from the variable or object key you assign it to (`const statue = viji.model(...)` → `statue`). Set `name` only for parameters not assigned to a variable or key (created in an array, loop, or factory). Must be a valid identifier and scene-globally unique. When set, it overrides the inferred name.
89
+ * @property {string} [label] - Display name shown next to the file picker in the parameter UI. Optional — defaults to the parameter's name.
90
+ * @property {string} [description] - Optional tooltip / help text shown next to the control.
91
+ * @property {string} [group] - Group name for organizing related parameters under a shared heading. Default: `'general'`.
92
+ * @property {ParameterCategory} [category] - Visibility category. The control hides when its capability is unavailable. Default: `'general'`.
93
+ * @property {ModelFormat[]} formats - Required: the formats your scene can parse, for example `['glb']` for a three.js `GLTFLoader` scene or `['obj', 'stl']` for p5 `loadModel()`. The host filters its picker by this list and core rejects files of other formats before the scene sees them. Must not be empty; list only what your loader handles.
94
+ */
95
+
85
96
  /**
86
97
  * Configuration object passed to `viji.button()`. The host renders a clickable momentary button. The resulting `value` is `true` for one frame after press, then auto-resets to `false`.
87
98
  * @typedef {Object} ButtonConfig
@@ -203,6 +214,16 @@
203
214
  * @property {ParameterCategory} category - Visibility category that controls when the parameter is shown.
204
215
  */
205
216
 
217
+ /**
218
+ * Reactive object returned by `viji.model()`. `value` is `null` until the user provides a 3D model file.
219
+ * @typedef {Object} ModelParameter
220
+ * @property {ModelValue |null} value - The current model as a {@link ModelValue}, or `null` until the user provides one.
221
+ * @property {string} [label] - Formats this parameter accepts (the `formats` you passed). Display name shown next to the upload field in the parameter UI.
222
+ * @property {string} [description] - Tooltip / help text for the control (empty string when not configured).
223
+ * @property {string} group - Group name under which the control appears in the UI.
224
+ * @property {ParameterCategory} category - Visibility category that controls when the parameter is shown.
225
+ */
226
+
206
227
  /**
207
228
  * Reactive object returned by `viji.button()`. `value` is a momentary flag: `true` for exactly one frame after the user clicks, then auto-resets to `false`. Use it to trigger discrete events from `render()`.
208
229
  * @typedef {Object} ButtonParameter
@@ -249,6 +270,11 @@
249
270
  * @property {number} position - Position along the ramp in `0..1`.
250
271
  */
251
272
 
273
+ /**
274
+ * The model a `viji.model()` parameter currently holds: the file as bytes plus a lazily created blob URL, tagged with its format. Core never parses the file; pass `bytes` to a loader's `parse()` or `url` to its `load()`. Read `value` where you parse it. When the host replaces or clears the model, core drops the bytes and revokes `url`; geometry you already built from them stays valid, but a URL taken from the replaced value rejects when loaded afterwards.
275
+ * @typedef {Object} ModelValue
276
+ */
277
+
252
278
  /**
253
279
  * Real-time audio analysis for the main audio stream (`viji.audio`). Provides volume, frequency bands, beat detection (with triggers, events, BPM), spectral features, and access to raw FFT / waveform data. All numeric outputs are zeroed when `isConnected` is `false`.
254
280
  * @typedef {Object} AudioAPI
@@ -404,6 +430,7 @@
404
430
  * @property {(defaultValue: string, config: TextConfig) => TextParameter} text - Declares a text input parameter. The host UI renders a single-line text field. Must be called at the top level of the scene, never inside `render()`. @example const caption = viji.text('Hello', { label: 'Caption', maxLength: 64 });
405
431
  * @property {(defaultValue: number, config: NumberConfig) => NumberParameter} number - Declares a precise numeric input parameter. Like `slider()` but the host UI shows a number field instead of a draggable handle. Must be called at the top level of the scene, never inside `render()`. @example const count = viji.number(12, { min: 1, max: 64, step: 1, label: 'Count' });
406
432
  * @property {(defaultValue: null, config: ImageConfig) => ImageParameter} image - Declares an image upload parameter. The user-supplied image becomes available as `value` (an `ImageBitmap`); in P5 scenes a `.p5` accessor is also added at runtime. Must be called at the top level of the scene, never inside `render()`. @example const tex = viji.image(null, { label: 'Texture' }); if (tex.value) ctx.drawImage(tex.value, 0, 0, viji.width, viji.height);
433
+ * @property {(defaultValue: null, config: ModelConfig) => ModelParameter} model - Declares a 3D model parameter. `formats` is required: list only the formats your loader parses, so the host picker offers nothing the scene cannot show. The user-supplied file becomes available as `value` (a `ModelValue` with `bytes`, `url` and `format`); parse it with your own loader (three.js loaders, p5 `loadModel()`, loaders.gl). Core never parses the file. Must be called at the top level of the scene, never inside `render()`. @example const statue = viji.model(null, { label: 'Statue', formats: ['glb'] }); // later, when statue.value changes: loader.parse(statue.value.bytes, '', (gltf) => scene.add(gltf.scene));
407
434
  * @property {(config: ButtonConfig) => ButtonParameter} button - Declares a momentary button parameter. `value` is `true` for one frame after the user clicks, then auto-resets. Perfect for triggering discrete events. Must be called at the top level of the scene, never inside `render()`. @example const reset = viji.button({ label: 'Reset' }); function render(viji) { if (reset.value) state = initialState; }
408
435
  * @property {(defaultValue: CoordinateValue, config: CoordinateConfig) => CoordinateParameter} coordinate - Declares a 2D coordinate parameter. Both `value.x` and `value.y` are in `-1..1`. Must be called at the top level of the scene, never inside `render()`. @example const origin = viji.coordinate({ x: 0, y: 0 }, { label: 'Origin' }); const cx = viji.width / 2 + origin.value.x * viji.width * 0.4;
409
436
  * @property {(defaultValue: GradientInput, config: GradientConfig) => GradientParameter} gradient - Declares a gradient parameter. The host renders a ramp editor; `value` is the live, sorted stop list and `at(t)` samples the ramp (CSS `linear-gradient` semantics). Stop colors accept every color form; pass `alpha: true` for per-stop transparency. `default` is required (no evenly-spaced shorthand). Must be called at the top level of the scene, never inside `render()`. @example const ramp = viji.gradient( [{ color: '#ff6b6b', position: 0 }, { color: '#4ecdc4', position: 1 }], { label: 'Ramp' } ); function render(viji) { const fill = viji.ctx.createLinearGradient(0, 0, viji.width, 0); for (const stop of ramp.value) fill.addColorStop(stop.position, stop.color); viji.ctx.fillStyle = fill; viji.ctx.fillRect(0, 0, viji.width, viji.height); viji.ctx.fillStyle = ramp.at(0.5); // per-element color, same math as the fill }