@viji-dev/core 0.3.39 → 0.4.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.
@@ -590,7 +590,7 @@ class IFrameManager {
590
590
  }
591
591
  function WorkerWrapper(options) {
592
592
  return new Worker(
593
- "" + new URL("assets/viji.worker-BoI8e3NI.js", import.meta.url).href,
593
+ "" + new URL("assets/viji.worker-4gGFik2A.js", import.meta.url).href,
594
594
  {
595
595
  type: "module",
596
596
  name: options?.name
@@ -1752,7 +1752,7 @@ class EssentiaOnsetDetection {
1752
1752
  this.initPromise = (async () => {
1753
1753
  try {
1754
1754
  const essentiaModule = await import("./essentia.js-core.es-DnrJE0uR.js");
1755
- const wasmModule = await import("./essentia-wasm.web-CdUmKTbm.js").then((n) => n.e);
1755
+ const wasmModule = await import("./essentia-wasm.web-CCpzl5zi.js").then((n) => n.e);
1756
1756
  const EssentiaClass = essentiaModule.Essentia || essentiaModule.default?.Essentia || essentiaModule.default;
1757
1757
  let WASMModule = wasmModule.default || wasmModule.EssentiaWASM || wasmModule.default?.EssentiaWASM;
1758
1758
  if (!WASMModule) {
@@ -3143,7 +3143,7 @@ class TempoInduction {
3143
3143
  candidates.push({ bpm: detectedBPM / 1.5, type: "other" });
3144
3144
  }
3145
3145
  for (const candidate of candidates) {
3146
- let existing = this.hypotheses.find(
3146
+ const existing = this.hypotheses.find(
3147
3147
  (h) => Math.abs(h.bpm - candidate.bpm) / candidate.bpm < 0.05
3148
3148
  );
3149
3149
  if (existing) {
@@ -9721,27 +9721,30 @@ class SpectralClustering {
9721
9721
  const totalEnergy = onset.bands.reduce((a, b) => a + b, 0);
9722
9722
  if (totalEnergy < 0.01) return false;
9723
9723
  switch (label) {
9724
- case "kick":
9724
+ case "kick": {
9725
9725
  if (low < 0.2 && lowMid < 0.25) {
9726
9726
  return false;
9727
9727
  }
9728
9728
  const bassEnergy = low + lowMid * 0.5;
9729
9729
  const bassRatio = bassEnergy / totalEnergy;
9730
9730
  return bassRatio > 0.3;
9731
- case "snare":
9731
+ }
9732
+ case "snare": {
9732
9733
  if (mid < 0.2) {
9733
9734
  return false;
9734
9735
  }
9735
9736
  const midEnergy = lowMid * 0.5 + mid + highMid * 0.3;
9736
9737
  const midRatio = midEnergy / totalEnergy;
9737
9738
  return midRatio > 0.25;
9738
- case "hat":
9739
+ }
9740
+ case "hat": {
9739
9741
  if (high < 0.2 && highMid < 0.25) {
9740
9742
  return false;
9741
9743
  }
9742
9744
  const trebleEnergy = highMid * 0.7 + high;
9743
9745
  const trebleRatio = trebleEnergy / totalEnergy;
9744
9746
  return trebleRatio > 0.35;
9747
+ }
9745
9748
  default:
9746
9749
  return true;
9747
9750
  }
@@ -10905,7 +10908,7 @@ class BeatStateManager {
10905
10908
  const lowFloor = this.getAdaptiveFloor("low");
10906
10909
  const midFloor = this.getAdaptiveFloor("mid");
10907
10910
  const highFloor = this.getAdaptiveFloor("high");
10908
- let hasKickEnergy = lowEnergy > Math.max(3e-4, lowFloor * 1.02);
10911
+ const hasKickEnergy = lowEnergy > Math.max(3e-4, lowFloor * 1.02);
10909
10912
  let hasSnareEnergy = midEnergy > Math.max(5e-5, midFloor * 1.15);
10910
10913
  let hasHatEnergy = highEnergy > Math.max(5e-6, highFloor * 0.6);
10911
10914
  const snareConfidence = Math.min(1, Math.max(0, (bands.mid.strength - 0.05) / 0.6));
@@ -11082,9 +11085,9 @@ class BeatStateManager {
11082
11085
  }
11083
11086
  }
11084
11087
  }
11085
- let kickFinal = kick;
11086
- let snareFinal = snare;
11087
- let hatFinal = hat;
11088
+ const kickFinal = kick;
11089
+ const snareFinal = snare;
11090
+ const hatFinal = hat;
11088
11091
  const hasIOIHistory = kick || snare || hat;
11089
11092
  if ((kick || snare || hat) && hasIOIHistory) {
11090
11093
  if (kick) this.updateIOITracker("kick", now);
@@ -13462,7 +13465,7 @@ class AudioSystem {
13462
13465
  try {
13463
13466
  ch.workletNode.port.onmessage = null;
13464
13467
  ch.workletNode.disconnect();
13465
- } catch (_) {
13468
+ } catch {
13466
13469
  }
13467
13470
  ch.workletNode = null;
13468
13471
  }
@@ -14591,6 +14594,256 @@ class SceneAnalyzer {
14591
14594
  return "2d";
14592
14595
  }
14593
14596
  }
14597
+ function parseColorInput(input) {
14598
+ if (typeof input === "string") {
14599
+ return parseColorString(input);
14600
+ }
14601
+ if (input && typeof input === "object") {
14602
+ const obj = input;
14603
+ const hasRgbKeys = "r" in obj && "g" in obj && "b" in obj;
14604
+ const hasHsbKeys = "h" in obj && "s" in obj && "b" in obj;
14605
+ if (hasHsbKeys && !("r" in obj) && !("g" in obj)) {
14606
+ return hsbObjectToHex(obj);
14607
+ }
14608
+ if (hasRgbKeys) {
14609
+ return rgbObjectToHex(obj);
14610
+ }
14611
+ throw new Error(
14612
+ `Invalid color object: expected {r,g,b} or {h,s,b} keys, got ${JSON.stringify(input)}`
14613
+ );
14614
+ }
14615
+ throw new Error(`Invalid color input: expected string or object, got ${typeof input}`);
14616
+ }
14617
+ function rgbToHex(r, g, b) {
14618
+ const rByte = clampByte(r, "r");
14619
+ const gByte = clampByte(g, "g");
14620
+ const bByte = clampByte(b, "b");
14621
+ return "#" + toHexByte(rByte) + toHexByte(gByte) + toHexByte(bByte);
14622
+ }
14623
+ function hsbToHex(h, s, b) {
14624
+ if (typeof h !== "number" || typeof s !== "number" || typeof b !== "number" || !isFinite(h) || !isFinite(s) || !isFinite(b)) {
14625
+ throw new Error(`Invalid HSB: h=${h}, s=${s}, b=${b}`);
14626
+ }
14627
+ if (s < 0 || s > 100) {
14628
+ throw new Error(`HSB saturation must be 0..100, got: ${s}`);
14629
+ }
14630
+ if (b < 0 || b > 100) {
14631
+ throw new Error(`HSB brightness must be 0..100, got: ${b}`);
14632
+ }
14633
+ const hue = (h % 360 + 360) % 360;
14634
+ const sN = s / 100;
14635
+ const bN = b / 100;
14636
+ const c = bN * sN;
14637
+ const hPrime = hue / 60;
14638
+ const x = c * (1 - Math.abs(hPrime % 2 - 1));
14639
+ let r1 = 0, g1 = 0, b1 = 0;
14640
+ if (hPrime < 1) {
14641
+ r1 = c;
14642
+ g1 = x;
14643
+ b1 = 0;
14644
+ } else if (hPrime < 2) {
14645
+ r1 = x;
14646
+ g1 = c;
14647
+ b1 = 0;
14648
+ } else if (hPrime < 3) {
14649
+ r1 = 0;
14650
+ g1 = c;
14651
+ b1 = x;
14652
+ } else if (hPrime < 4) {
14653
+ r1 = 0;
14654
+ g1 = x;
14655
+ b1 = c;
14656
+ } else if (hPrime < 5) {
14657
+ r1 = x;
14658
+ g1 = 0;
14659
+ b1 = c;
14660
+ } else {
14661
+ r1 = c;
14662
+ g1 = 0;
14663
+ b1 = x;
14664
+ }
14665
+ const m = bN - c;
14666
+ return rgbToHex(
14667
+ Math.round((r1 + m) * 255),
14668
+ Math.round((g1 + m) * 255),
14669
+ Math.round((b1 + m) * 255)
14670
+ );
14671
+ }
14672
+ function parseColorString(input) {
14673
+ const trimmed = input.trim();
14674
+ if (trimmed.startsWith("#")) {
14675
+ return parseHexString(trimmed);
14676
+ }
14677
+ const fnMatch = trimmed.match(/^(rgb|hsl|hsb|vec3)\s*\(([^)]*)\)$/i);
14678
+ if (fnMatch) {
14679
+ const fnName = fnMatch[1].toLowerCase();
14680
+ const args = fnMatch[2];
14681
+ switch (fnName) {
14682
+ case "rgb":
14683
+ return parseRgbFunction(args);
14684
+ case "hsl":
14685
+ return parseHslFunction(args);
14686
+ case "hsb":
14687
+ return parseHsbFunction(args);
14688
+ case "vec3":
14689
+ return parseVec3Function(args);
14690
+ }
14691
+ }
14692
+ throw new Error(
14693
+ `Invalid color string: '${input}'. Expected #rrggbb, #rgb, rgb(...), hsl(...), hsb(...), or vec3(...)`
14694
+ );
14695
+ }
14696
+ function parseHexString(hex) {
14697
+ if (/^#[0-9a-fA-F]{6}$/.test(hex)) {
14698
+ return hex.toLowerCase();
14699
+ }
14700
+ if (/^#[0-9a-fA-F]{3}$/.test(hex)) {
14701
+ const r = hex[1];
14702
+ const g = hex[2];
14703
+ const b = hex[3];
14704
+ return ("#" + r + r + g + g + b + b).toLowerCase();
14705
+ }
14706
+ throw new Error(`Invalid hex color: '${hex}'. Expected #rrggbb or #rgb`);
14707
+ }
14708
+ function parseRgbFunction(args) {
14709
+ const parts = splitArgs(args);
14710
+ if (parts.length !== 3) {
14711
+ throw new Error(`rgb() requires 3 arguments, got ${parts.length}: rgb(${args})`);
14712
+ }
14713
+ const r = parseNumber(parts[0], "r");
14714
+ const g = parseNumber(parts[1], "g");
14715
+ const b = parseNumber(parts[2], "b");
14716
+ return rgbToHex(r, g, b);
14717
+ }
14718
+ function parseHslFunction(args) {
14719
+ const parts = splitArgs(args);
14720
+ if (parts.length !== 3) {
14721
+ throw new Error(`hsl() requires 3 arguments, got ${parts.length}: hsl(${args})`);
14722
+ }
14723
+ const h = parseNumber(parts[0], "h");
14724
+ const s = parsePercent(parts[1], "s");
14725
+ const l = parsePercent(parts[2], "l");
14726
+ return hslToHex(h, s, l);
14727
+ }
14728
+ function parseHsbFunction(args) {
14729
+ const parts = splitArgs(args);
14730
+ if (parts.length !== 3) {
14731
+ throw new Error(`hsb() requires 3 arguments, got ${parts.length}: hsb(${args})`);
14732
+ }
14733
+ const h = parseNumber(parts[0], "h");
14734
+ const s = parseNumber(parts[1], "s");
14735
+ const b = parseNumber(parts[2], "b");
14736
+ return hsbToHex(h, s, b);
14737
+ }
14738
+ function parseVec3Function(args) {
14739
+ const parts = splitArgs(args);
14740
+ if (parts.length !== 3) {
14741
+ throw new Error(`vec3() requires 3 arguments, got ${parts.length}: vec3(${args})`);
14742
+ }
14743
+ const r = parseNumber(parts[0], "r");
14744
+ const g = parseNumber(parts[1], "g");
14745
+ const b = parseNumber(parts[2], "b");
14746
+ if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1) {
14747
+ throw new Error(`vec3() components must be in 0..1, got: vec3(${r}, ${g}, ${b})`);
14748
+ }
14749
+ return rgbToHex(
14750
+ Math.round(r * 255),
14751
+ Math.round(g * 255),
14752
+ Math.round(b * 255)
14753
+ );
14754
+ }
14755
+ function rgbObjectToHex(obj) {
14756
+ const r = numericField(obj.r, "r");
14757
+ const g = numericField(obj.g, "g");
14758
+ const b = numericField(obj.b, "b");
14759
+ return rgbToHex(r, g, b);
14760
+ }
14761
+ function hsbObjectToHex(obj) {
14762
+ const h = numericField(obj.h, "h");
14763
+ const s = numericField(obj.s, "s");
14764
+ const b = numericField(obj.b, "b");
14765
+ return hsbToHex(h, s, b);
14766
+ }
14767
+ function hslToHex(h, s, l) {
14768
+ if (s < 0 || s > 100) {
14769
+ throw new Error(`HSL saturation must be 0..100%, got: ${s}%`);
14770
+ }
14771
+ if (l < 0 || l > 100) {
14772
+ throw new Error(`HSL lightness must be 0..100%, got: ${l}%`);
14773
+ }
14774
+ const hue = (h % 360 + 360) % 360;
14775
+ const sN = s / 100;
14776
+ const lN = l / 100;
14777
+ const c = (1 - Math.abs(2 * lN - 1)) * sN;
14778
+ const hPrime = hue / 60;
14779
+ const x = c * (1 - Math.abs(hPrime % 2 - 1));
14780
+ let r1 = 0, g1 = 0, b1 = 0;
14781
+ if (hPrime < 1) {
14782
+ r1 = c;
14783
+ g1 = x;
14784
+ b1 = 0;
14785
+ } else if (hPrime < 2) {
14786
+ r1 = x;
14787
+ g1 = c;
14788
+ b1 = 0;
14789
+ } else if (hPrime < 3) {
14790
+ r1 = 0;
14791
+ g1 = c;
14792
+ b1 = x;
14793
+ } else if (hPrime < 4) {
14794
+ r1 = 0;
14795
+ g1 = x;
14796
+ b1 = c;
14797
+ } else if (hPrime < 5) {
14798
+ r1 = x;
14799
+ g1 = 0;
14800
+ b1 = c;
14801
+ } else {
14802
+ r1 = c;
14803
+ g1 = 0;
14804
+ b1 = x;
14805
+ }
14806
+ const m = lN - c / 2;
14807
+ return rgbToHex(
14808
+ Math.round((r1 + m) * 255),
14809
+ Math.round((g1 + m) * 255),
14810
+ Math.round((b1 + m) * 255)
14811
+ );
14812
+ }
14813
+ function splitArgs(args) {
14814
+ return args.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
14815
+ }
14816
+ function parseNumber(token, fieldName) {
14817
+ const n = parseFloat(token);
14818
+ if (!isFinite(n)) {
14819
+ throw new Error(`Invalid number for '${fieldName}': '${token}'`);
14820
+ }
14821
+ return n;
14822
+ }
14823
+ function parsePercent(token, fieldName) {
14824
+ if (!token.endsWith("%")) {
14825
+ throw new Error(`Field '${fieldName}' must use percent notation (e.g., 50%), got: '${token}'`);
14826
+ }
14827
+ return parseNumber(token.slice(0, -1), fieldName);
14828
+ }
14829
+ function numericField(value, fieldName) {
14830
+ if (typeof value !== "number" || !isFinite(value)) {
14831
+ throw new Error(`Invalid color field '${fieldName}': expected finite number, got ${value}`);
14832
+ }
14833
+ return value;
14834
+ }
14835
+ function clampByte(value, fieldName) {
14836
+ if (typeof value !== "number" || !isFinite(value)) {
14837
+ throw new Error(`RGB component '${fieldName}' must be a finite number, got: ${value}`);
14838
+ }
14839
+ if (value < 0 || value > 255) {
14840
+ throw new Error(`RGB component '${fieldName}' must be 0..255, got: ${value}`);
14841
+ }
14842
+ return Math.round(value);
14843
+ }
14844
+ function toHexByte(byte) {
14845
+ return byte.toString(16).padStart(2, "0");
14846
+ }
14594
14847
  const simdLoaderJs = new URL("assets/wasm/vision_wasm_internal.js", import.meta.url).href;
14595
14848
  const simdBinary = new URL("assets/wasm/vision_wasm_internal.wasm", import.meta.url).href;
14596
14849
  const nosimdLoaderJs = new URL("assets/wasm/vision_wasm_nosimd_internal.js", import.meta.url).href;
@@ -15160,6 +15413,29 @@ class VijiCore {
15160
15413
  }
15161
15414
  return void 0;
15162
15415
  }
15416
+ /**
15417
+ * For color parameters, accept rich input forms (hex, RGB/HSB objects, CSS / GLSL strings)
15418
+ * and normalize them to canonical lowercase hex `#rrggbb` before they go on the wire.
15419
+ * This keeps the host<->worker contract simple (always a hex string) while letting
15420
+ * external callers use whichever form is most convenient.
15421
+ *
15422
+ * Non-color values are returned unchanged.
15423
+ */
15424
+ normalizeColorInputIfNeeded(name, value) {
15425
+ const def = this.getParameterDefinition(name);
15426
+ if (def?.type !== "color") {
15427
+ return value;
15428
+ }
15429
+ try {
15430
+ return parseColorInput(value);
15431
+ } catch (err) {
15432
+ throw new VijiCoreError(
15433
+ `Invalid color value for parameter '${name}': ${err.message}`,
15434
+ "INVALID_COLOR_VALUE",
15435
+ { name, value }
15436
+ );
15437
+ }
15438
+ }
15163
15439
  /**
15164
15440
  * Set a single parameter value
15165
15441
  * Handles all parameter types including images intelligently
@@ -15177,17 +15453,18 @@ class VijiCore {
15177
15453
  await this.handleImageParameter(name, value);
15178
15454
  return;
15179
15455
  }
15456
+ const normalizedValue = this.normalizeColorInputIfNeeded(name, value);
15180
15457
  const oldValue = this.parameterValues.get(name);
15181
- this.parameterValues.set(name, value);
15458
+ this.parameterValues.set(name, normalizedValue);
15182
15459
  if (this.workerManager) {
15183
15460
  this.workerManager.postMessage("parameter-update", {
15184
15461
  name,
15185
- value
15462
+ value: normalizedValue
15186
15463
  });
15187
15464
  }
15188
- const isEqual = paramDef?.type === "coordinate" ? isCoordinateValueEqual(oldValue, value) : oldValue === value;
15465
+ const isEqual = paramDef?.type === "coordinate" ? isCoordinateValueEqual(oldValue, normalizedValue) : oldValue === normalizedValue;
15189
15466
  if (!isEqual) {
15190
- this.notifyParameterListeners(name, value);
15467
+ this.notifyParameterListeners(name, normalizedValue);
15191
15468
  }
15192
15469
  }
15193
15470
  /**
@@ -15249,7 +15526,10 @@ class VijiCore {
15249
15526
  }
15250
15527
  }
15251
15528
  /**
15252
- * Set multiple parameter values efficiently
15529
+ * Set multiple parameter values efficiently.
15530
+ *
15531
+ * For color parameters, accepts the same rich input forms as `setParameter`
15532
+ * (hex, RGB/HSB objects, CSS / GLSL strings); they are normalized to canonical hex.
15253
15533
  */
15254
15534
  async setParameters(values) {
15255
15535
  this.validateReady();
@@ -15263,16 +15543,17 @@ class VijiCore {
15263
15543
  console.warn(`Unknown parameter: ${name}`);
15264
15544
  continue;
15265
15545
  }
15546
+ const normalizedValue = this.normalizeColorInputIfNeeded(name, value);
15266
15547
  const oldValue = this.parameterValues.get(name);
15267
- this.parameterValues.set(name, value);
15548
+ this.parameterValues.set(name, normalizedValue);
15268
15549
  updates.push({
15269
15550
  name,
15270
- value
15551
+ value: normalizedValue
15271
15552
  });
15272
15553
  const paramDef = this.getParameterDefinition(name);
15273
- const isEqual = paramDef?.type === "coordinate" ? isCoordinateValueEqual(oldValue, value) : oldValue === value;
15554
+ const isEqual = paramDef?.type === "coordinate" ? isCoordinateValueEqual(oldValue, normalizedValue) : oldValue === normalizedValue;
15274
15555
  if (!isEqual) {
15275
- changedParams.push({ name, value });
15556
+ changedParams.push({ name, value: normalizedValue });
15276
15557
  }
15277
15558
  }
15278
15559
  if (updates.length > 0 && this.workerManager) {
@@ -16192,7 +16473,7 @@ class VijiCore {
16192
16473
  async detectScreenRefreshRate() {
16193
16474
  return new Promise((resolve) => {
16194
16475
  let frameCount = 0;
16195
- let startTime = performance.now();
16476
+ const startTime = performance.now();
16196
16477
  const measureFrames = () => {
16197
16478
  frameCount++;
16198
16479
  if (frameCount === 60) {
@@ -16467,4 +16748,4 @@ export {
16467
16748
  VijiCoreError as b,
16468
16749
  getDefaultExportFromCjs as g
16469
16750
  };
16470
- //# sourceMappingURL=index-DZzYWg7c.js.map
16751
+ //# sourceMappingURL=index-G2N9Tgtd.js.map