@viji-dev/core 0.19.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.
package/dist/index.d.ts CHANGED
@@ -1136,6 +1136,9 @@ declare interface ImageParameter {
1136
1136
  category: ParameterCategory;
1137
1137
  }
1138
1138
 
1139
+ /** Value type of one `initialParameters` entry: a parameter value, or a {@link ModelSeed} for model parameters. */
1140
+ export declare type InitialParameterValue = ParameterValue | ModelSeed;
1141
+
1139
1142
  /**
1140
1143
  * Runtime-enumerable identifiers for the **input / interaction** capabilities
1141
1144
  * tracked live per rendered frame. This is the live capability vocabulary — the
@@ -1144,7 +1147,10 @@ declare interface ImageParameter {
1144
1147
  *
1145
1148
  * Note: `parameters` is intentionally absent. It is a *declared-only* tag on
1146
1149
  * {@link SceneCapabilities} (the scene defines tunable parameters), not a live
1147
- * consumed signal, so it is not part of the per-frame capability set.
1150
+ * consumed signal, so it is not part of the per-frame capability set. The
1151
+ * nested `cv` and `sources` members of {@link SceneCapabilities} are likewise
1152
+ * outside this vocabulary (`cv` has its own, {@link CV_FEATURE_KEYS}; `sources`
1153
+ * is declared-only).
1148
1154
  */
1149
1155
  export declare const INPUT_CAPABILITY_KEYS: readonly ["audio", "video", "mouse", "keyboard", "pointer", "touch", "sensors", "devices"];
1150
1156
 
@@ -1225,6 +1231,158 @@ export declare interface MinAvgMax {
1225
1231
  max: number;
1226
1232
  }
1227
1233
 
1234
+ /**
1235
+ * The 3D model formats a `viji.model()` parameter accepts, with the file
1236
+ * extension (dot included) and the canonical MIME type the Viji backend
1237
+ * stores and serves for each. Cross-repo contract: the client builds its
1238
+ * picker `accept` list and MIME canonicalization from this table, the
1239
+ * backend keeps an equal literal and asserts equality in its tests.
1240
+ *
1241
+ * Multi-file formats (`.gltf` with external buffers and textures) are
1242
+ * deliberately absent: one asset is one file.
1243
+ */
1244
+ export declare const MODEL_FORMATS: ReadonlyArray<ModelFormatInfo>;
1245
+
1246
+ /**
1247
+ * Configuration object passed to `viji.model()`. The host renders a file
1248
+ * picker for 3D model files; the user-supplied file becomes available as a
1249
+ * {@link ModelValue} (bytes plus a blob URL) that your own loader parses.
1250
+ */
1251
+ declare interface ModelConfig {
1252
+ /**
1253
+ * Advanced: explicit parameter identity. Normally you do NOT set this —
1254
+ * the parameter's name is inferred from the variable or object key you
1255
+ * assign it to (`const statue = viji.model(...)` → `statue`). Set `name`
1256
+ * only for parameters not assigned to a variable or key (created in an
1257
+ * array, loop, or factory). Must be a valid identifier and scene-globally
1258
+ * unique. When set, it overrides the inferred name.
1259
+ */
1260
+ name?: string;
1261
+ /** Display name shown next to the file picker in the parameter UI. Optional — defaults to the parameter's name. */
1262
+ label?: string;
1263
+ /** Optional tooltip / help text shown next to the control. */
1264
+ description?: string;
1265
+ /** Group name for organizing related parameters under a shared heading. Default: `'general'`. */
1266
+ group?: string;
1267
+ /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1268
+ category?: ParameterCategory;
1269
+ /**
1270
+ * Required: the formats your scene can parse, for example `['glb']` for a
1271
+ * three.js `GLTFLoader` scene or `['obj', 'stl']` for p5 `loadModel()`.
1272
+ * The host filters its picker by this list and core rejects files of other
1273
+ * formats before the scene sees them. Must not be empty; list only what
1274
+ * your loader handles.
1275
+ */
1276
+ formats: ModelFormat[];
1277
+ }
1278
+
1279
+ /**
1280
+ * Host-side value of a 3D model parameter: what parameter listeners,
1281
+ * `getParameter()` and `getParameterValues()` deliver once a model is set.
1282
+ * A frozen plain object carrying the format tag only; core keeps no copy of
1283
+ * the bytes on the host (they are transferred to the scene). Hosts that need
1284
+ * to tell it apart from other object values check the parameter's declared
1285
+ * type (`'model'`) or the presence of a string `format`.
1286
+ */
1287
+ export declare interface ModelDescriptor {
1288
+ /** Format tag of the model the scene currently holds. */
1289
+ readonly format: ModelFormat;
1290
+ }
1291
+
1292
+ /**
1293
+ * `VijiCoreError.code` values a model parameter can raise:
1294
+ * `MODEL_FORMAT_UNKNOWN` (the file is not a supported model),
1295
+ * `MODEL_FORMAT_NOT_ALLOWED` (the parameter's `formats` exclude it),
1296
+ * `MODEL_LOAD_ERROR` (reading or fetching the input failed).
1297
+ */
1298
+ export declare type ModelErrorCode = 'MODEL_FORMAT_UNKNOWN' | 'MODEL_FORMAT_NOT_ALLOWED' | 'MODEL_LOAD_ERROR';
1299
+
1300
+ /**
1301
+ * Format tag of a 3D model file. One tag per supported file type; the
1302
+ * extension and canonical MIME type of each live in `MODEL_FORMATS`.
1303
+ */
1304
+ export declare type ModelFormat = 'glb' | 'obj' | 'stl' | 'ply' | 'las' | 'laz' | 'splat';
1305
+
1306
+ /** One row of `MODEL_FORMATS`: a format tag with its file extension and canonical MIME type. */
1307
+ export declare interface ModelFormatInfo {
1308
+ /** Format tag used by `viji.model()` and the `format` field of a model value. */
1309
+ readonly format: ModelFormat;
1310
+ /** File extension including the dot, lower case (for picker `accept` lists). */
1311
+ readonly extension: string;
1312
+ /** Canonical MIME type the Viji backend stores and serves for this format. */
1313
+ readonly mime: string;
1314
+ }
1315
+
1316
+ /**
1317
+ * What `setParameter` accepts for a model parameter: a `File` or `Blob`
1318
+ * (read on the host), a bare `ArrayBuffer` or a {@link ModelSeed} (the
1319
+ * buffer is TRANSFERRED to the scene and becomes unusable on the host), a
1320
+ * URL string (fetched without credentials), or `null` to clear.
1321
+ */
1322
+ export declare type ModelInput = File | Blob | ArrayBuffer | ModelSeed | string | null;
1323
+
1324
+ /**
1325
+ * Reactive object returned by `viji.model()`. `value` is `null` until the user
1326
+ * provides a 3D model file.
1327
+ */
1328
+ declare interface ModelParameter {
1329
+ /** The current model as a {@link ModelValue}, or `null` until the user provides one. */
1330
+ value: ModelValue | null;
1331
+ /** Formats this parameter accepts (the `formats` you passed). */
1332
+ readonly formats: ReadonlyArray<ModelFormat>;
1333
+ /** Display name shown next to the upload field in the parameter UI. */
1334
+ label?: string;
1335
+ /** Tooltip / help text for the control (empty string when not configured). */
1336
+ description?: string;
1337
+ /** Group name under which the control appears in the UI. */
1338
+ group: string;
1339
+ /** Visibility category that controls when the parameter is shown. */
1340
+ category: ParameterCategory;
1341
+ }
1342
+
1343
+ /**
1344
+ * What `initialParameters` accepts for a model parameter. Structured-cloned
1345
+ * to the scene (your buffer stays usable). `format` is preferred (a host
1346
+ * that stores assets knows it from the MIME type); when omitted, the format
1347
+ * is sniffed from the bytes with `name` as the extension hint.
1348
+ */
1349
+ export declare interface ModelSeed {
1350
+ /** The whole model file. Must be an `ArrayBuffer` (not a view, not shared) with at least one byte. */
1351
+ bytes: ArrayBuffer;
1352
+ /** Format tag. Sniffed from `bytes` when omitted. */
1353
+ format?: ModelFormat | undefined;
1354
+ /** File name or URL, used only as an extension hint for the sniff; never stored. */
1355
+ name?: string | undefined;
1356
+ }
1357
+
1358
+ /**
1359
+ * The model a `viji.model()` parameter currently holds: the file as bytes plus
1360
+ * a lazily created blob URL, tagged with its format. Core never parses the
1361
+ * file; pass `bytes` to a loader's `parse()` or `url` to its `load()`.
1362
+ *
1363
+ * Read `value` where you parse it. When the host replaces or clears the
1364
+ * model, core drops the bytes and revokes `url`; geometry you already built
1365
+ * from them stays valid, but a URL taken from the replaced value rejects
1366
+ * when loaded afterwards.
1367
+ */
1368
+ declare interface ModelValue {
1369
+ /** Format tag of the file, resolved by the host before transfer. */
1370
+ readonly format: ModelFormat;
1371
+ /**
1372
+ * The whole file, transferred from the host without a copy. `byteLength`
1373
+ * is the file size. Some loaders transfer the buffer to their own worker
1374
+ * and detach it; pass `bytes.slice(0)` to those, or disable their worker.
1375
+ */
1376
+ readonly bytes: ArrayBuffer;
1377
+ /**
1378
+ * Blob URL for loaders that take a URL (three.js `.load()`, p5 `loadModel()`).
1379
+ * Created on first read (a Blob copies the bytes, so it is never built
1380
+ * eagerly), cached, and revoked when the host replaces or clears the model.
1381
+ * Reading it on a value the host already replaced throws.
1382
+ */
1383
+ readonly url: string;
1384
+ }
1385
+
1228
1386
  /**
1229
1387
  * Mouse input state for the current frame: position, individual button states,
1230
1388
  * per-frame movement, scroll wheel, and frame-based press / release events.
@@ -1392,6 +1550,12 @@ export declare interface ParameterConfig {
1392
1550
  */
1393
1551
  step?: number;
1394
1552
  options?: string[] | number[];
1553
+ /**
1554
+ * For model type: the formats the scene can parse (required by
1555
+ * `viji.model()`, so always present on model definitions). Hosts filter
1556
+ * their pickers by it; core rejects files of other formats at set time.
1557
+ */
1558
+ formats?: ModelFormat[] | undefined;
1395
1559
  maxLength?: number;
1396
1560
  /**
1397
1561
  * For color and gradient types: the value carries a transparency channel
@@ -1444,9 +1608,9 @@ export declare interface ParameterGroup {
1444
1608
  * The kind of UI control a parameter renders as. Used for the per-type
1445
1609
  * breakdown in {@link SceneData.parameterCounts}.
1446
1610
  */
1447
- export declare type ParameterType = 'slider' | 'color' | 'toggle' | 'select' | 'text' | 'number' | 'image' | 'button' | 'coordinate' | 'gradient';
1611
+ export declare type ParameterType = 'slider' | 'color' | 'toggle' | 'select' | 'text' | 'number' | 'image' | 'button' | 'coordinate' | 'gradient' | 'model';
1448
1612
 
1449
- export declare type ParameterValue = string | number | boolean | ImageBitmap | CoordinateValue | GradientValue | null;
1613
+ export declare type ParameterValue = string | number | boolean | ImageBitmap | CoordinateValue | GradientValue | ModelDescriptor | null;
1450
1614
 
1451
1615
  export declare interface PerformanceStats {
1452
1616
  resolution: Resolution;
@@ -1629,7 +1793,8 @@ export declare interface SceneAnalysisData {
1629
1793
  *
1630
1794
  * The input keys here are pinned to {@link INPUT_CAPABILITY_KEYS} and the `cv`
1631
1795
  * keys to {@link CV_FEATURE_KEYS} by a compile-time guard (below), so this shape
1632
- * cannot drift from the runtime vocabulary.
1796
+ * cannot drift from the runtime vocabulary. `parameters`, `cv`, and `sources`
1797
+ * are the declared-only members outside the per-frame input vocabulary.
1633
1798
  */
1634
1799
  export declare interface SceneCapabilities {
1635
1800
  /** Scene reads audio analysis (volume, bands, beat, FFT/waveform, or audio streams). */
@@ -1665,6 +1830,67 @@ export declare interface SceneCapabilities {
1665
1830
  /** Scene uses body segmentation. */
1666
1831
  segmentation: boolean;
1667
1832
  };
1833
+ /**
1834
+ * Which input **source** each capability reads, for host routing UIs (e.g. a
1835
+ * controller deciding whether a phone camera belongs in the main slot or a
1836
+ * device slot). Declared-only (static analysis, like the flags above) and
1837
+ * inclusive-biased; not part of the per-frame {@link INPUT_CAPABILITY_KEYS}
1838
+ * vocabulary (same standing as `parameters` and `cv`).
1839
+ *
1840
+ * - `main`: the host's primary stream, index 0 (`viji.video` / `viji.audio`,
1841
+ * `u_video*` / `u_audio*`).
1842
+ * - `streams`: host-side additional compositor inputs, indices 1-99
1843
+ * (`viji.videoStreams` / `viji.audioStreams`, `u_videoStream*` /
1844
+ * `u_audioStream*`). Not routable from a controller; they set neither
1845
+ * `main` nor `device`.
1846
+ * - `device`: an external device slot, indices 100+ (`viji.devices[i].video`
1847
+ * / `.audio` / `.motion` / `.orientation`, `u_device{i}*`, `u_deviceCount`,
1848
+ * `u_externalDeviceCount`).
1849
+ *
1850
+ * Native/p5 scenes reach device members through a `.video` / `.audio` /
1851
+ * `.motion` / `.orientation` token after `viji.devices`. When a scene
1852
+ * references `viji.devices` but no such token is visible (destructuring,
1853
+ * `viji.devices.length`), all three `device` booleans report `true`: the
1854
+ * analyzer never hides a source a scene may use.
1855
+ */
1856
+ sources: {
1857
+ /** Video image source. */
1858
+ video: {
1859
+ /**
1860
+ * Main camera. Shader scenes also report `true` when any `cv.*` flag is
1861
+ * set (CV runs on the main stream only; no device-CV uniforms exist).
1862
+ * Native/p5 CV `enable*` calls do NOT imply this: device cameras expose
1863
+ * the same CV surface (`viji.devices[i].video.cv`).
1864
+ */
1865
+ main: boolean;
1866
+ /** Additional compositor video streams (`viji.videoStreams`, `u_videoStream*`). */
1867
+ streams: boolean;
1868
+ /** External device cameras (`viji.devices[i].video`, `u_device{i}`, `u_deviceCount`). */
1869
+ device: boolean;
1870
+ };
1871
+ /** Audio analysis source. */
1872
+ audio: {
1873
+ /** Main microphone / audio input (`viji.audio`, `u_audio*`). */
1874
+ main: boolean;
1875
+ /** Additional compositor audio streams (`viji.audioStreams`, `u_audioStream*`). */
1876
+ streams: boolean;
1877
+ /**
1878
+ * External device audio (`viji.devices[i].audio`). Always `false` for
1879
+ * shader scenes: no device-audio uniform family exists.
1880
+ */
1881
+ device: boolean;
1882
+ };
1883
+ /** Motion / orientation sensor source. */
1884
+ sensors: {
1885
+ /** The host machine's own sensors (`viji.device`, `u_deviceAcceleration` and family). */
1886
+ internal: boolean;
1887
+ /**
1888
+ * External device sensors (`viji.devices[i].motion` / `.orientation`,
1889
+ * `u_device{i}Acceleration` and family, `u_externalDeviceCount`).
1890
+ */
1891
+ device: boolean;
1892
+ };
1893
+ };
1668
1894
  }
1669
1895
 
1670
1896
  /**
@@ -2458,7 +2684,7 @@ declare interface TouchPoint {
2458
2684
  /** Returned by `on*` listener registration calls; invoke to unsubscribe. */
2459
2685
  export declare type Unsubscribe = () => void;
2460
2686
 
2461
- export declare const VERSION = "0.19.0";
2687
+ export declare const VERSION = "0.21.0";
2462
2688
 
2463
2689
  /**
2464
2690
  * Real-time video API: drawable frame, dimensions, and the source-side
@@ -2759,6 +2985,20 @@ export declare interface VijiAPI {
2759
2985
  * if (tex.value) ctx.drawImage(tex.value, 0, 0, viji.width, viji.height);
2760
2986
  */
2761
2987
  image: (defaultValue: null, config: ImageConfig) => ImageParameter;
2988
+ /**
2989
+ * Declares a 3D model parameter. `formats` is required: list only the formats
2990
+ * your loader parses, so the host picker offers nothing the scene cannot show.
2991
+ * The user-supplied file becomes available as `value` (a `ModelValue` with
2992
+ * `bytes`, `url` and `format`); parse it with your own loader (three.js loaders,
2993
+ * p5 `loadModel()`, loaders.gl). Core never parses the file. Must be called at
2994
+ * the top level of the scene, never inside `render()`.
2995
+ *
2996
+ * @example
2997
+ * const statue = viji.model(null, { label: 'Statue', formats: ['glb'] });
2998
+ * // later, when statue.value changes:
2999
+ * loader.parse(statue.value.bytes, '', (gltf) => scene.add(gltf.scene));
3000
+ */
3001
+ model: (defaultValue: null, config: ModelConfig) => ModelParameter;
2762
3002
  /**
2763
3003
  * Declares a momentary button parameter. `value` is `true` for one frame after
2764
3004
  * the user clicks, then auto-resets. Perfect for triggering discrete events.
@@ -2874,9 +3114,12 @@ export declare class VijiCore {
2874
3114
  private config;
2875
3115
  /**
2876
3116
  * `config.initialParameters` after `sanitizeInitialParameters` (set in
2877
- * `initialize()`, cleared in `destroy()`). Overlaid onto `parameterValues`
3117
+ * `initialize()`, released at the end of `handleParametersDefined`, its
3118
+ * only consumer, and in `destroy()`). Overlaid onto `parameterValues`
2878
3119
  * when the worker reports its definitions, so the host table matches what
2879
3120
  * the worker seeded before frame 1. `null` when no seed was supplied.
3121
+ * Released after define so model seed buffers (up to tens of MB) do not
3122
+ * stay reachable from the host for the core's lifetime.
2880
3123
  */
2881
3124
  private initialSeed;
2882
3125
  private isHeadless;
@@ -3086,10 +3329,20 @@ export declare class VijiCore {
3086
3329
  */
3087
3330
  private normalizeParameterInput;
3088
3331
  /**
3089
- * Set a single parameter value
3090
- * Handles all parameter types including images intelligently
3091
- */
3092
- setParameter(name: string, value: ParameterValue | ColorInput | GradientInput | File | Blob): Promise<void>;
3332
+ * Set a single parameter value.
3333
+ * Handles all parameter types including images and 3D models.
3334
+ *
3335
+ * Model parameters accept a {@link ModelInput}: a `File` or `Blob` (read
3336
+ * here), a URL string (fetched without credentials), a bare `ArrayBuffer`
3337
+ * or a `ModelSeed`, or `null` to clear. A buffer you pass is TRANSFERRED to
3338
+ * the scene and is unusable afterwards; when one model feeds several cores
3339
+ * (VJ decks), pass a `File` / `Blob` or re-read the bytes per core. Core
3340
+ * keeps no copy of the bytes: listeners and `getParameter()` receive a
3341
+ * `ModelDescriptor` (`{ format }`). Rejects with `MODEL_FORMAT_UNKNOWN` when the file
3342
+ * is not a supported model, `MODEL_FORMAT_NOT_ALLOWED` when the parameter's
3343
+ * `formats` exclude it, and `MODEL_LOAD_ERROR` when reading or fetching fails.
3344
+ */
3345
+ setParameter(name: string, value: ParameterValue | ColorInput | GradientInput | File | Blob | ModelInput): Promise<void>;
3093
3346
  /**
3094
3347
  * Internal method to handle image parameter loading and transfer
3095
3348
  * Loads the image on the host side and creates TWO ImageBitmaps:
@@ -3097,13 +3350,26 @@ export declare class VijiCore {
3097
3350
  * - One to transfer to the worker
3098
3351
  */
3099
3352
  private handleImageParameter;
3353
+ /**
3354
+ * Internal: resolve, validate and transfer a 3D model parameter value.
3355
+ *
3356
+ * Order (security review S12): resolve the bytes, validate format and
3357
+ * allowlist, then post inside try/catch, and only after a successful post
3358
+ * commit the host table and notify listeners. Format errors are thrown
3359
+ * BEFORE the I/O try so they keep their own codes; only read / fetch
3360
+ * failures become `MODEL_LOAD_ERROR`. The host keeps the descriptor only.
3361
+ */
3362
+ private handleModelParameter;
3100
3363
  /**
3101
3364
  * Set multiple parameter values efficiently.
3102
3365
  *
3103
3366
  * For color parameters, accepts the same rich input forms as `setParameter`
3104
3367
  * (hex, RGB/HSB objects, CSS / GLSL strings); they are normalized to canonical hex.
3368
+ * Model parameters accept the same {@link ModelInput} forms as `setParameter`;
3369
+ * each model entry is resolved and transferred individually (in order) before
3370
+ * the remaining values are sent as one batch.
3105
3371
  */
3106
- setParameters(values: Record<string, ParameterValue | ColorInput | GradientInput>): Promise<void>;
3372
+ setParameters(values: Record<string, ParameterValue | ColorInput | GradientInput | ModelInput>): Promise<void>;
3107
3373
  /**
3108
3374
  * Get current parameter value
3109
3375
  */
@@ -4066,8 +4332,18 @@ export declare interface VijiCoreConfig {
4066
4332
  * closed is dropped from the seed with a console warning. URL strings are
4067
4333
  * not resolved and `File` / `Blob` values are not seedable; use
4068
4334
  * {@link VijiCore.setParameter} after `onParametersDefined` for those.
4069
- */
4070
- initialParameters?: Record<string, ParameterValue>;
4335
+ *
4336
+ * Model parameters accept a {@link ModelSeed} (`{ bytes, format? }`). The
4337
+ * buffer is structured-cloned to the scene (one copy; your buffer stays
4338
+ * usable and may be shared between keys), core keeps none of it after the
4339
+ * scene reports its parameters, and the host table shows a
4340
+ * {@link ModelDescriptor}. Seeds whose bytes are empty, detached, or
4341
+ * not an `ArrayBuffer`, or whose format cannot be resolved, are dropped
4342
+ * with a warning. The whole seed rides one message with a 5 second budget:
4343
+ * keep model seeds small (total under 64 MB) and set large models with
4344
+ * `setParameter` after `onParametersDefined` instead.
4345
+ */
4346
+ initialParameters?: Record<string, InitialParameterValue>;
4071
4347
  /** Frame rate mode - render every frame (`'full'`), every 2nd (`'half'`), every 4th (`'quarter'`), or every 8th (`'eighth'`). */
4072
4348
  frameRateMode?: FrameRateMode;
4073
4349
  /** Resolution: scale (number) or explicit dimensions (object). Required for headless, must be object form */
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
- import { C as E, I as o, V as s, a as C, b as I } from "./index-PgPvRJ2p.js";
2
- export {
3
- E as CV_FEATURE_KEYS,
4
- o as INPUT_CAPABILITY_KEYS,
5
- s as VERSION,
6
- C as VijiCore,
7
- I as VijiCoreError
8
- };
1
+ import { C as E, I as s, M as o, V as C, a as I, b as V } from "./index-C9OHLFGB.js";
2
+ export {
3
+ E as CV_FEATURE_KEYS,
4
+ s as INPUT_CAPABILITY_KEYS,
5
+ o as MODEL_FORMATS,
6
+ C as VERSION,
7
+ I as VijiCore,
8
+ V as VijiCoreError
9
+ };
@@ -3100,6 +3100,7 @@ const ai = /* @__PURE__ */ new Set([
3100
3100
  "number",
3101
3101
  "text",
3102
3102
  "image",
3103
+ "model",
3103
3104
  "button",
3104
3105
  "coordinate",
3105
3106
  "gradient"
@@ -1,4 +1,4 @@
1
- class a {
1
+ class u {
2
2
  /**
3
3
  * Detects the renderer type from scene code comments
4
4
  *
@@ -39,6 +39,15 @@ class a {
39
39
  * - Shader scenes: matched against built-in `u_*` uniform usage (exact).
40
40
  * - Native / p5 scenes: matched against `viji.*` usage tokens.
41
41
  *
42
+ * The top-level flags are the per-frame cohort vocabulary
43
+ * (`INPUT_CAPABILITY_KEYS`) and are persisted downstream; their rules do
44
+ * not change. `sources` (0.20.0) is a declared-only refinement on top:
45
+ * per input kind, whether the scene reads the main stream, the additional
46
+ * compositor streams, or an external device slot. Same inclusive bias; a
47
+ * native/p5 scene that references `viji.devices` without a visible member
48
+ * token reports every `device` source as used. Shader scenes never report
49
+ * `audio.device` (no device-audio uniform family exists).
50
+ *
42
51
  * @param sceneCode - The artist's scene code to analyze
43
52
  * @param renderer - The renderer type (from {@link detectRendererType})
44
53
  * @returns The set of capabilities the scene declares it uses
@@ -61,9 +70,14 @@ class a {
61
70
  handTracking: !1,
62
71
  poseDetection: !1,
63
72
  segmentation: !1
73
+ },
74
+ sources: {
75
+ video: { main: !1, streams: !1, device: !1 },
76
+ audio: { main: !1, streams: !1, device: !1 },
77
+ sensors: { internal: !1, device: !1 }
64
78
  }
65
79
  };
66
- return t === "shader" ? this.detectShaderCapabilities(i, s) : this.detectScriptCapabilities(i, s), s;
80
+ return t === "shader" ? (this.detectShaderCapabilities(i, s), this.detectShaderSources(i, s)) : (this.detectScriptCapabilities(i, s), this.detectScriptSources(i, s)), s;
67
81
  }
68
82
  /**
69
83
  * Replaces comment spans and string/template literals with whitespace via a
@@ -77,25 +91,25 @@ class a {
77
91
  const i = e.length;
78
92
  let s = 0;
79
93
  for (; s < i; ) {
80
- const r = e[s], o = s + 1 < i ? e[s + 1] : "";
81
- if (r === "/" && o === "/") {
94
+ const r = e[s], a = s + 1 < i ? e[s + 1] : "";
95
+ if (r === "/" && a === "/") {
82
96
  for (s += 2; s < i && e[s] !== `
83
97
  `; ) s++;
84
98
  continue;
85
99
  }
86
- if (r === "/" && o === "*") {
100
+ if (r === "/" && a === "*") {
87
101
  for (s += 2; s < i && !(e[s] === "*" && e[s + 1] === "/"); ) s++;
88
102
  s += 2, t += " ";
89
103
  continue;
90
104
  }
91
105
  if (r === '"' || r === "'" || r === "`") {
92
- const u = r;
106
+ const o = r;
93
107
  for (s++; s < i; ) {
94
108
  if (e[s] === "\\") {
95
109
  s += 2;
96
110
  continue;
97
111
  }
98
- if (e[s] === u) {
112
+ if (e[s] === o) {
99
113
  s++;
100
114
  break;
101
115
  }
@@ -115,6 +129,9 @@ class a {
115
129
  * (`u_device0`, `u_device0Resolution/Connected`) map to video+devices,
116
130
  * sensor uniforms (`u_device0Acceleration`, …) map to sensors+devices, and
117
131
  * the internal-device sensors (`u_deviceAcceleration`, …) map to sensors.
132
+ *
133
+ * These flag rules are the persisted cohort vocabulary and must not change;
134
+ * per-source refinement lives in {@link detectShaderSources}, run afterwards.
118
135
  */
119
136
  static detectShaderCapabilities(e, t) {
120
137
  /\bu_audio\w*/.test(e) && (t.audio = !0), /\bu_video\w*/.test(e) && (t.video = !0), /\bu_mouse\w*/.test(e) && (t.mouse = !0), /\bu_key\w*/.test(e) && (t.keyboard = !0), /\bu_touch\w*/.test(e) && (t.touch = !0), /\bu_pointer\w*/.test(e) && (t.pointer = !0), /\bu_device(Acceleration|AccelerationGravity|RotationRate|OrientationAbsolute|Orientation)\b/.test(e) && (t.sensors = !0), /\bu_device[0-7](AccelerationGravity|Acceleration|RotationRate|Orientation)\b/.test(e) && (t.sensors = !0, t.devices = !0), /\bu_externalDeviceCount\b/.test(e) && (t.devices = !0, t.sensors = !0), (/\bu_deviceCount\b/.test(e) || /\bu_device[0-7](Resolution|Connected)?\b/.test(e)) && (t.video = !0, t.devices = !0), /\bu_face\w*/.test(e) && (t.cv.faceDetection = !0), /\bu_face0(Neutral|Happy|Sad|Angry|Surprised|Disgusted|Fearful)\b/.test(e) && (t.cv.emotion = !0), (/\bu_face0HeadPose\b/.test(e) || /\bu_videoAnalysed\w*/.test(e) || /\bu_face0(Brow|Cheek|Eye|Jaw|Mouth|Nose|Tongue)\w*/.test(e)) && (t.cv.faceMesh = !0), (/\bu_handCount\b/.test(e) || /\bu_(left|right)Hand\w*/.test(e)) && (t.cv.handTracking = !0), (/\bu_pose\w*/.test(e) || /\bu_(nose|leftShoulder|rightShoulder|leftElbow|rightElbow|leftWrist|rightWrist|leftHip|rightHip|leftKnee|rightKnee|leftAnkle|rightAnkle)Position\b/.test(e)) && (t.cv.poseDetection = !0), /\bu_segmentation\w*/.test(e) && (t.cv.segmentation = !0);
@@ -123,21 +140,54 @@ class a {
123
140
  * Native / p5 capability detection from `viji.*` usage tokens. CV features
124
141
  * are keyed on their canonical `enable*` method names (the canonical
125
142
  * signal — a scene must enable a feature to receive its data).
143
+ *
144
+ * These flag rules are the persisted cohort vocabulary and must not change;
145
+ * per-source refinement lives in {@link detectScriptSources}, run afterwards.
126
146
  */
127
147
  static detectScriptCapabilities(e, t) {
128
- /\bviji\.audio/.test(e) && (t.audio = !0), /\bviji\.video/.test(e) && (t.video = !0), /\bviji\.mouse/.test(e) && (t.mouse = !0), /\bviji\.keyboard/.test(e) && (t.keyboard = !0), /\bviji\.touches/.test(e) && (t.touch = !0), /\bviji\.pointer/.test(e) && (t.pointer = !0), /\bviji\.device\b/.test(e) && (t.sensors = !0), /\bviji\.devices/.test(e) && (t.devices = !0), /\bviji\.(slider|color|toggle|select|text|number|image|button|coordinate|gradient)\s*\(/.test(e) && (t.parameters = !0), /\benableFaceDetection\b/.test(e) && (t.cv.faceDetection = !0), /\benableFaceMesh\b/.test(e) && (t.cv.faceMesh = !0), /\benableEmotionDetection\b/.test(e) && (t.cv.emotion = !0), /\benableHandTracking\b/.test(e) && (t.cv.handTracking = !0), /\benablePoseDetection\b/.test(e) && (t.cv.poseDetection = !0), /\benableBodySegmentation\b/.test(e) && (t.cv.segmentation = !0);
148
+ /\bviji\.audio/.test(e) && (t.audio = !0), /\bviji\.video/.test(e) && (t.video = !0), /\bviji\.mouse/.test(e) && (t.mouse = !0), /\bviji\.keyboard/.test(e) && (t.keyboard = !0), /\bviji\.touches/.test(e) && (t.touch = !0), /\bviji\.pointer/.test(e) && (t.pointer = !0), /\bviji\.device\b/.test(e) && (t.sensors = !0), /\bviji\.devices/.test(e) && (t.devices = !0), /\bviji\.(slider|color|toggle|select|text|number|image|model|button|coordinate|gradient)\s*\(/.test(e) && (t.parameters = !0), /\benableFaceDetection\b/.test(e) && (t.cv.faceDetection = !0), /\benableFaceMesh\b/.test(e) && (t.cv.faceMesh = !0), /\benableEmotionDetection\b/.test(e) && (t.cv.emotion = !0), /\benableHandTracking\b/.test(e) && (t.cv.handTracking = !0), /\benablePoseDetection\b/.test(e) && (t.cv.poseDetection = !0), /\benableBodySegmentation\b/.test(e) && (t.cv.segmentation = !0);
129
149
  }
130
150
  /**
131
- * Detects which specific interaction inputs a scene reads (positions and
132
- * mouse buttons), for the worker's consumed-meaningfully evaluation. Renderer-
133
- * aware: shader scenes are read from built-in uniform usage, native/p5 from
134
- * `mouse.*` / `pointer.*` / `touches.*` member access. Comments/strings are
135
- * stripped first. Inclusive-biased like {@link detectCapabilities}.
136
- *
137
- * @param sceneCode - The artist's scene code to analyze
138
- * @param renderer - The renderer type (from {@link detectRendererType})
139
- * @returns The interaction-input usage mask
151
+ * Shader per-source refinement (`caps.sources`) from built-in uniform usage.
152
+ * Runs after {@link detectShaderCapabilities} because `video.main` also
153
+ * follows from any `cv.*` flag: CV runs on the main stream only and no
154
+ * device-CV uniforms exist. The `u_*Stream*` families are host compositor
155
+ * inputs and report `streams`, never `main` or `device`. No device-audio
156
+ * uniform family exists, so `audio.device` stays `false`. The device-sensor
157
+ * regexes repeat the flag rules above on purpose: the two surfaces must not
158
+ * disagree.
159
+ */
160
+ static detectShaderSources(e, t) {
161
+ const i = t.sources, s = Object.values(t.cv).some(Boolean);
162
+ i.video.main = s || /\bu_video(?!Stream)\w*/.test(e), i.video.streams = /\bu_videoStream\w*/.test(e), i.video.device = /\bu_deviceCount\b/.test(e) || /\bu_device[0-7](Resolution|Connected)?\b/.test(e), i.audio.main = /\bu_audio(?!Stream)\w*/.test(e), i.audio.streams = /\bu_audioStream\w*/.test(e), i.sensors.internal = /\bu_device(Acceleration|AccelerationGravity|RotationRate|OrientationAbsolute|Orientation)\b/.test(e), i.sensors.device = /\bu_device[0-7](AccelerationGravity|Acceleration|RotationRate|Orientation)\b/.test(e) || /\bu_externalDeviceCount\b/.test(e);
163
+ }
164
+ /**
165
+ * Native / p5 per-source refinement (`caps.sources`) from `viji.*` tokens.
166
+ * `main` / `streams` / `internal` are exact-token matches (`\b` after the
167
+ * name keeps `viji.videoStreams` out of `video.main`). CV `enable*` calls do
168
+ * not imply `video.main`: device cameras expose the same CV surface, so the
169
+ * receiver decides. Device sources come from {@link detectScriptDeviceSources}.
140
170
  */
171
+ static detectScriptSources(e, t) {
172
+ const i = t.sources;
173
+ i.video.main = /\bviji\.video\b/.test(e), i.video.streams = /\bviji\.videoStreams\b/.test(e), i.audio.main = /\bviji\.audio\b/.test(e), i.audio.streams = /\bviji\.audioStreams\b/.test(e), i.sensors.internal = /\bviji\.device\b/.test(e), /\bviji\.devices\b/.test(e) && this.detectScriptDeviceSources(e, t);
174
+ }
175
+ /**
176
+ * Which members a native / p5 scene reads off `viji.devices[i]`. Device
177
+ * elements are reached through aliases (`for (const d of viji.devices)`,
178
+ * `devices.filter(d => d.video)`), so the signal is the member token itself:
179
+ * `.video`, `.audio`, `.motion` / `.orientation`. The main-stream and
180
+ * internal-sensor member chains (`viji.video.cv…`, `viji.device.motion…`)
181
+ * are removed first, whole chain not just the head token, so their own
182
+ * members cannot leak into the device result. If `viji.devices` is
183
+ * referenced and no member token survives (destructuring,
184
+ * `viji.devices.length`), every device source reports `true`: inclusive
185
+ * bias, the scene may read any of them.
186
+ */
187
+ static detectScriptDeviceSources(e, t) {
188
+ const i = e.replace(/\bviji\.(video|audio|device)\b(\s*\??\.\s*[A-Za-z_$][\w$]*)*/g, " "), s = /\.video\b/.test(i), r = /\.audio\b/.test(i), a = /\.(motion|orientation)\b/.test(i), o = !s && !r && !a, b = t.sources;
189
+ b.video.device = s || o, b.audio.device = r || o, b.sensors.device = a || o;
190
+ }
141
191
  /**
142
192
  * Whether the scene statically consumes the CV analysed frame. Gates
143
193
  * per-sink `analysedFrame` bitmap delivery from the shared CV engine to
@@ -162,6 +212,17 @@ class a {
162
212
  const t = this.stripCommentsAndStrings(e);
163
213
  return /\b(analysedFrame|getAnalysedFrameData)\b/.test(t) || /\bu_videoAnalysed\w*/.test(t);
164
214
  }
215
+ /**
216
+ * Detects which specific interaction inputs a scene reads (positions and
217
+ * mouse buttons), for the worker's consumed-meaningfully evaluation. Renderer-
218
+ * aware: shader scenes are read from built-in uniform usage, native/p5 from
219
+ * `mouse.*` / `pointer.*` / `touches.*` member access. Comments/strings are
220
+ * stripped first. Inclusive-biased like {@link detectCapabilities}.
221
+ *
222
+ * @param sceneCode - The artist's scene code to analyze
223
+ * @param renderer - The renderer type (from {@link detectRendererType})
224
+ * @returns The interaction-input usage mask
225
+ */
165
226
  static detectUsage(e, t) {
166
227
  const i = this.stripCommentsAndStrings(e), s = {
167
228
  mouse: { position: !1, left: !1, right: !1, middle: !1 },
@@ -171,16 +232,16 @@ class a {
171
232
  return t === "shader" ? (s.mouse.position = /\bu_mouse(\b|InCanvas|Delta|Wheel)/.test(i), s.mouse.left = /\bu_mouse(Left|Pressed|WasPressed|WasReleased)\b/.test(i), s.mouse.right = /\bu_mouseRight\b/.test(i), s.mouse.middle = /\bu_mouseMiddle\b/.test(i), s.pointer.position = /\bu_pointer(\b|Delta|InCanvas)/.test(i), s.pointer.down = /\bu_pointer(Down|WasPressed|WasReleased)\b/.test(i), s.touch = /\bu_touch/.test(i)) : (s.mouse.position = /\bmouse\.(x|y|deltaX|deltaY|wheelDelta|wheelX|wheelY|wasMoved)\b/.test(i), s.mouse.left = /\bmouse\.(leftButton|isPressed|wasPressed|wasReleased)\b/.test(i), s.mouse.right = /\bmouse\.rightButton\b/.test(i), s.mouse.middle = /\bmouse\.middleButton\b/.test(i), s.pointer.position = /\bpointer\.(x|y|deltaX|deltaY)\b/.test(i), s.pointer.down = /\bpointer\.(isDown|wasPressed|wasReleased)\b/.test(i), s.touch = /\btouches\./.test(i)), s;
172
233
  }
173
234
  }
174
- function b(n) {
175
- const e = a.detectRendererType(n), t = e === "p5" ? a.detectP5CanvasMode(n) : void 0;
235
+ function l(n) {
236
+ const e = u.detectRendererType(n), t = e === "p5" ? u.detectP5CanvasMode(n) : void 0;
176
237
  return {
177
238
  renderer: e,
178
239
  ...t !== void 0 && { p5CanvasMode: t },
179
- capabilities: a.detectCapabilities(n, e),
180
- consumesAnalysedFrame: a.detectAnalysedFrameUse(n)
240
+ capabilities: u.detectCapabilities(n, e),
241
+ consumesAnalysedFrame: u.detectAnalysedFrameUse(n)
181
242
  };
182
243
  }
183
244
  export {
184
- a as S,
185
- b as a
245
+ u as S,
246
+ l as a
186
247
  };