voodoojs 0.11.1 → 0.12.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 (41) hide show
  1. package/README.md +4 -3
  2. package/dist/{chunk-32J7GDLD.js → chunk-4M4ZXHNY.js} +94 -21
  3. package/dist/{chunk-TXWSOPF2.js → chunk-72PMUUMT.js} +4 -4
  4. package/dist/{chunk-OHSGDVPA.js → chunk-AFMCDNYS.js} +5 -5
  5. package/dist/{chunk-KE6WWJOB.js → chunk-D7675IJG.js} +6 -6
  6. package/dist/{chunk-BEQEBFQ2.js → chunk-GXPNWCGE.js} +3 -3
  7. package/dist/{chunk-FCJ2APGN.js → chunk-HNM3CTBD.js} +5 -5
  8. package/dist/{chunk-OJDW7KXT.js → chunk-KN7NAKBL.js} +4 -4
  9. package/dist/{chunk-FITEA237.js → chunk-LLNDLLKV.js} +4 -4
  10. package/dist/{chunk-J6RTM5P4.js → chunk-NP66JM2P.js} +4 -4
  11. package/dist/{chunk-XG2UDP4O.js → chunk-RDHYPGH6.js} +187 -18
  12. package/dist/{chunk-GC3BKNPT.js → chunk-T5ION5NG.js} +3 -3
  13. package/dist/essential.cjs +273 -27
  14. package/dist/essential.d.cts +1 -1
  15. package/dist/essential.d.ts +1 -1
  16. package/dist/essential.js +10 -10
  17. package/dist/gpu.cjs +1 -1
  18. package/dist/gpu.js +10 -10
  19. package/dist/http.cjs +1 -1
  20. package/dist/http.js +5 -5
  21. package/dist/index.cjs +283 -29
  22. package/dist/index.d.cts +88 -4
  23. package/dist/index.d.ts +88 -4
  24. package/dist/index.js +20 -20
  25. package/dist/{query-p04Fl8VW.d.ts → query-DzHfm8Kg.d.ts} +12 -1
  26. package/dist/{query-CbHihkvO.d.cts → query-XooOh4vB.d.cts} +12 -1
  27. package/dist/reactivity.cjs +1 -1
  28. package/dist/reactivity.js +2 -2
  29. package/dist/socket.cjs +11 -3
  30. package/dist/socket.js +9 -9
  31. package/dist/style-GR4X6O7I.js +5 -0
  32. package/dist/utils.cjs +1 -1
  33. package/dist/utils.js +2 -2
  34. package/dist/voodoo.core.js +252 -26
  35. package/dist/voodoo.core.min.js +17 -17
  36. package/dist/voodoo.full.js +276 -29
  37. package/dist/voodoo.full.min.js +57 -53
  38. package/dist/voodoo.js +274 -27
  39. package/dist/voodoo.min.js +29 -25
  40. package/package.json +1 -1
  41. package/dist/style-MX6ZATL4.js +0 -5
package/dist/index.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  /**
6
- * Voodoo.js v0.11.1
6
+ * Voodoo.js v0.12.1
7
7
  * JavaScript feels like magic.
8
8
  * (c) 2026 Voodoo.js contributors. MIT License.
9
9
  */
@@ -1626,9 +1626,17 @@ var Parser = class {
1626
1626
  if (t2.type === "punct") {
1627
1627
  if (t2.value === "(") {
1628
1628
  this.next();
1629
- const expr = this.parseExpression();
1629
+ const body = [this.parseExpression()];
1630
+ while (this.isPunct(",")) {
1631
+ this.next();
1632
+ if (this.isPunct(")")) {
1633
+ const stray = this.peek();
1634
+ throw new VoodooSyntaxError("Trailing comma in a group", this.source, stray.start);
1635
+ }
1636
+ body.push(this.parseExpression());
1637
+ }
1630
1638
  this.expect(")");
1631
- return expr;
1639
+ return body.length === 1 ? body[0] : { t: "seq", body };
1632
1640
  }
1633
1641
  if (t2.value === "[") return this.parseArrayLiteral();
1634
1642
  if (t2.value === "{") return this.parseObjectLiteral();
@@ -2229,6 +2237,10 @@ var magics = /* @__PURE__ */ new Map();
2229
2237
  function magic(name, getter) {
2230
2238
  magics.set(name.startsWith("$") ? name : `$${name}`, getter);
2231
2239
  }
2240
+ var hooks = /* @__PURE__ */ new Map();
2241
+ function hook(name, getter) {
2242
+ hooks.set(name, getter);
2243
+ }
2232
2244
  var Scope = class _Scope {
2233
2245
  // Assignment order matches the order the fields were declared in before, so
2234
2246
  // the properties are created in the same sequence they always were.
@@ -2284,7 +2296,10 @@ var Scope = class _Scope {
2284
2296
  s = s.parent;
2285
2297
  }
2286
2298
  if (name.charCodeAt(0) === 36 && magics.has(name)) {
2287
- return this.magicContainer(name);
2299
+ return this.magicContainer(name, magics);
2300
+ }
2301
+ if (hooks.has(name)) {
2302
+ return this.magicContainer(name, hooks);
2288
2303
  }
2289
2304
  return void 0;
2290
2305
  }
@@ -2313,11 +2328,11 @@ var Scope = class _Scope {
2313
2328
  reactiveChild(vars, el = null) {
2314
2329
  return new _Scope(reactive(vars), this, el ?? this.el);
2315
2330
  }
2316
- magicContainer(name) {
2331
+ magicContainer(name, registry) {
2317
2332
  if (!this.magicCache) this.magicCache = /* @__PURE__ */ new Map();
2318
2333
  const cached = this.magicCache.get(name);
2319
2334
  if (cached) return cached;
2320
- const getter = magics.get(name);
2335
+ const getter = registry.get(name);
2321
2336
  const scope = this;
2322
2337
  const container2 = {};
2323
2338
  Object.defineProperty(container2, name, {
@@ -2714,7 +2729,16 @@ function runDirective(el, attr2, scope) {
2714
2729
  },
2715
2730
  effect(fn) {
2716
2731
  const owner = ownerScope();
2717
- owner.run(() => effect(fn, { scope: owner }));
2732
+ const hosted = () => {
2733
+ const previous = hookHost;
2734
+ hookHost = el;
2735
+ try {
2736
+ fn();
2737
+ } finally {
2738
+ hookHost = previous;
2739
+ }
2740
+ };
2741
+ owner.run(() => effect(hosted, { scope: owner }));
2718
2742
  },
2719
2743
  cleanup(fn) {
2720
2744
  addCleanup(el, fn);
@@ -2762,6 +2786,56 @@ function walk(node, scope) {
2762
2786
  return;
2763
2787
  }
2764
2788
  initialized.add(el);
2789
+ const previousHost = hookHost;
2790
+ hookHost = el;
2791
+ try {
2792
+ walkElementDirectives(el, attrs, tagComponent, current2);
2793
+ } finally {
2794
+ hookHost = previousHost;
2795
+ }
2796
+ }
2797
+ var hookHost = null;
2798
+ function currentHookHost() {
2799
+ return hookHost;
2800
+ }
2801
+ function createDataScope(expression, parent, el) {
2802
+ let ast = null;
2803
+ try {
2804
+ ast = parse(expression);
2805
+ } catch {
2806
+ ast = null;
2807
+ }
2808
+ const plain = ast && ast.t === "obj" && ast.props.every(
2809
+ (p2) => !p2.spread && !p2.keyExpr
2810
+ );
2811
+ if (!plain) {
2812
+ const raw = evaluateIn(expression, parent, "v-data");
2813
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2814
+ }
2815
+ const scope = parent.reactiveChild({}, el);
2816
+ const props = ast.props;
2817
+ for (const prop of props) {
2818
+ if (prop.key === null) continue;
2819
+ try {
2820
+ if (prop.getter) {
2821
+ const compute2 = evaluate(prop.value, scope);
2822
+ Object.defineProperty(scope.data, prop.key, {
2823
+ enumerable: true,
2824
+ configurable: true,
2825
+ get: () => compute2.call(scope.data)
2826
+ });
2827
+ } else {
2828
+ scope.data[prop.key] = evaluate(prop.value, scope);
2829
+ }
2830
+ } catch (err) {
2831
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
2832
+ else handleError(err, "v-data");
2833
+ }
2834
+ }
2835
+ return scope;
2836
+ }
2837
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
2838
+ let current2 = scope;
2765
2839
  for (const attr2 of attrs) {
2766
2840
  const def = directives.get(attr2.name);
2767
2841
  if (def?.terminal) {
@@ -2781,11 +2855,10 @@ function walk(node, scope) {
2781
2855
  nodeScopes.set(el, current2);
2782
2856
  }
2783
2857
  } else if (dataAttr || componentAttr) {
2784
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current2, "v-data") : {};
2785
- current2 = current2.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2858
+ current2 = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current2, el);
2786
2859
  nodeScopes.set(el, current2);
2787
2860
  }
2788
- const attributeScope = mountedComponent ? activeScope2 : current2;
2861
+ const attributeScope = mountedComponent ? scope : current2;
2789
2862
  for (const attr2 of attrs) {
2790
2863
  if (attr2.name === "data" || attr2.name === "component") continue;
2791
2864
  runDirective(el, attr2, attributeScope);
@@ -2940,13 +3013,13 @@ var componentAliases = /* @__PURE__ */ new Map();
2940
3013
  var started = false;
2941
3014
  var observer = null;
2942
3015
  var startHooks = [];
2943
- function onStart(hook) {
2944
- startHooks.push(hook);
3016
+ function onStart(hook2) {
3017
+ startHooks.push(hook2);
2945
3018
  }
2946
3019
  function runPhase(target2, after) {
2947
- for (const hook of startHooks) {
3020
+ for (const hook2 of startHooks) {
2948
3021
  try {
2949
- hook(target2, after);
3022
+ hook2(target2, after);
2950
3023
  } catch (error) {
2951
3024
  console.error("[Voodoo] a start hook failed", error);
2952
3025
  }
@@ -3324,10 +3397,10 @@ var LIFECYCLE = /* @__PURE__ */ new Set([
3324
3397
  "destroyed"
3325
3398
  ]);
3326
3399
  function callHook(def, instance, name) {
3327
- const hook = def[name];
3328
- if (typeof hook !== "function") return;
3400
+ const hook2 = def[name];
3401
+ if (typeof hook2 !== "function") return;
3329
3402
  try {
3330
- hook.call(instance);
3403
+ hook2.call(instance);
3331
3404
  } catch (err) {
3332
3405
  handleError(err, `hook ${name}`);
3333
3406
  }
@@ -4767,6 +4840,7 @@ var toast = Object.assign(
4767
4840
  );
4768
4841
 
4769
4842
  // src/storage/index.ts
4843
+ init_reactivity();
4770
4844
  function createStorage(getStore, prefix = "") {
4771
4845
  const full = (key) => prefix + key;
4772
4846
  return {
@@ -4936,21 +5010,28 @@ var cache2 = {
4936
5010
  };
4937
5011
  var THEME_KEY = "voodoo:theme";
4938
5012
  var picked = null;
5013
+ var revision = ref(0);
4939
5014
  var theme = {
4940
5015
  /** Theme chosen by the user, or `system` when never set. */
4941
5016
  get current() {
5017
+ void revision.value;
4942
5018
  return storage.get(THEME_KEY) ?? picked ?? "system";
4943
5019
  },
4944
5020
  /** Theme effectively applied, resolving `system`. */
4945
5021
  get resolved() {
4946
5022
  const value = this.current;
4947
5023
  if (value !== "system") return value;
5024
+ if (typeof document !== "undefined") {
5025
+ const authored = document.documentElement.getAttribute("data-theme");
5026
+ if (authored === "light" || authored === "dark") return authored;
5027
+ }
4948
5028
  if (typeof matchMedia === "undefined") return "light";
4949
5029
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
4950
5030
  },
4951
5031
  set(value) {
4952
5032
  picked = value;
4953
5033
  storage.set(THEME_KEY, value);
5034
+ revision.value++;
4954
5035
  this.apply();
4955
5036
  },
4956
5037
  toggle() {
@@ -4984,8 +5065,17 @@ var theme = {
4984
5065
  init() {
4985
5066
  if (typeof document === "undefined") return;
4986
5067
  this.apply();
5068
+ if (typeof MutationObserver !== "undefined") {
5069
+ new MutationObserver(() => {
5070
+ revision.value++;
5071
+ }).observe(document.documentElement, {
5072
+ attributes: true,
5073
+ attributeFilter: ["data-theme"]
5074
+ });
5075
+ }
4987
5076
  if (typeof matchMedia === "undefined") return;
4988
5077
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
5078
+ revision.value++;
4989
5079
  if (this.current === "system") this.apply();
4990
5080
  });
4991
5081
  }
@@ -5118,6 +5208,140 @@ function installMagics() {
5118
5208
  );
5119
5209
  }
5120
5210
 
5211
+ // src/hooks/index.ts
5212
+ init_reactivity();
5213
+ var tables = /* @__PURE__ */ new WeakMap();
5214
+ function tableFor(scope) {
5215
+ const host = currentHookHost() ?? scope.el ?? scope;
5216
+ let table = tables.get(host);
5217
+ if (!table) {
5218
+ table = { slots: [], index: 0, scheduled: false, bound: false };
5219
+ tables.set(host, table);
5220
+ }
5221
+ const el = currentHookHost() ?? scope.el;
5222
+ if (!table.bound && el) {
5223
+ table.bound = true;
5224
+ const owned = table;
5225
+ addCleanup(el, () => {
5226
+ for (const slot of owned.slots) {
5227
+ if (slot.dispose) slot.dispose();
5228
+ if (slot.runner) stop(slot.runner);
5229
+ }
5230
+ owned.slots.length = 0;
5231
+ });
5232
+ }
5233
+ if (!table.scheduled) {
5234
+ table.scheduled = true;
5235
+ const pending2 = table;
5236
+ queueMicrotask(() => {
5237
+ pending2.index = 0;
5238
+ pending2.scheduled = false;
5239
+ });
5240
+ }
5241
+ return table;
5242
+ }
5243
+ function nextSlot(scope, kind) {
5244
+ const table = tableFor(scope);
5245
+ const at = table.index++;
5246
+ let slot = table.slots[at];
5247
+ if (!slot || slot.kind !== kind) {
5248
+ if (slot) {
5249
+ if (slot.dispose) slot.dispose();
5250
+ if (slot.runner) stop(slot.runner);
5251
+ }
5252
+ slot = { kind, deps: null, value: void 0 };
5253
+ table.slots[at] = slot;
5254
+ }
5255
+ return slot;
5256
+ }
5257
+ function depsChanged(previous, next) {
5258
+ if (!previous || !next) return true;
5259
+ if (previous.length !== next.length) return true;
5260
+ for (let i = 0; i < next.length; i++) {
5261
+ if (!Object.is(previous[i], next[i])) return true;
5262
+ }
5263
+ return false;
5264
+ }
5265
+ function normalizeDeps(deps) {
5266
+ return Array.isArray(deps) ? deps.slice() : null;
5267
+ }
5268
+ function useState(scope, initial) {
5269
+ const slot = nextSlot(scope, "state");
5270
+ if (slot.value === void 0) slot.value = ref(initial);
5271
+ return slot.value;
5272
+ }
5273
+ function useEffect(scope, fn, deps) {
5274
+ const slot = nextSlot(scope, "effect");
5275
+ const next = normalizeDeps(deps);
5276
+ const runCleanup = () => {
5277
+ if (slot.dispose) {
5278
+ const dispose = slot.dispose;
5279
+ slot.dispose = void 0;
5280
+ dispose();
5281
+ }
5282
+ };
5283
+ if (next) {
5284
+ const first = slot.deps === null && !slot.runner;
5285
+ if (first || depsChanged(slot.deps, next)) {
5286
+ slot.deps = next;
5287
+ runCleanup();
5288
+ const result = fn();
5289
+ if (typeof result === "function") slot.dispose = result;
5290
+ }
5291
+ return;
5292
+ }
5293
+ if (slot.runner) return;
5294
+ slot.deps = null;
5295
+ slot.runner = effect(() => {
5296
+ runCleanup();
5297
+ const result = fn();
5298
+ if (typeof result === "function") slot.dispose = result;
5299
+ });
5300
+ }
5301
+ function useMemo(scope, fn, deps) {
5302
+ const slot = nextSlot(scope, "memo");
5303
+ const next = normalizeDeps(deps);
5304
+ if (!next) {
5305
+ if (!slot.value) slot.value = computed(fn);
5306
+ return slot.value;
5307
+ }
5308
+ if (!slot.value) {
5309
+ slot.value = ref(fn());
5310
+ slot.deps = next;
5311
+ } else if (depsChanged(slot.deps, next)) {
5312
+ slot.deps = next;
5313
+ slot.value.value = fn();
5314
+ }
5315
+ return slot.value;
5316
+ }
5317
+ function useRef(scope, initial) {
5318
+ const slot = nextSlot(scope, "ref");
5319
+ if (!slot.value) slot.value = markRaw({ current: initial });
5320
+ return slot.value;
5321
+ }
5322
+ function useContext(name, initial) {
5323
+ if (initial !== void 0 && !(name in allStores)) {
5324
+ store(name, initial);
5325
+ }
5326
+ return allStores[name];
5327
+ }
5328
+ function installHooks() {
5329
+ hook("useState", (scope) => (initial) => useState(scope, initial));
5330
+ hook(
5331
+ "useEffect",
5332
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
5333
+ );
5334
+ hook(
5335
+ "useMemo",
5336
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
5337
+ );
5338
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
5339
+ hook(
5340
+ "useContext",
5341
+ () => (name, initial) => useContext(name, initial)
5342
+ );
5343
+ }
5344
+
5121
5345
  // src/http/resource.ts
5122
5346
  init_reactivity();
5123
5347
  function pick(value, path) {
@@ -6700,6 +6924,7 @@ setComponentMounter(mountComponent);
6700
6924
  setScopeMarker(markNodeScope);
6701
6925
  setDirectiveRegistrar(directive);
6702
6926
  installMagics();
6927
+ installHooks();
6703
6928
  var eventBus = /* @__PURE__ */ new Map();
6704
6929
  function on(name, handler) {
6705
6930
  let set2 = eventBus.get(name);
@@ -6733,7 +6958,7 @@ function off(name, handler) {
6733
6958
  eventBus.get(name)?.delete(handler);
6734
6959
  }
6735
6960
  function directive(name, definition) {
6736
- const hooks = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6961
+ const hooks2 = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6737
6962
  defineDirective(
6738
6963
  name,
6739
6964
  (ctx) => {
@@ -6749,36 +6974,36 @@ function directive(name, definition) {
6749
6974
  scope: ctx.scope,
6750
6975
  instance: ctx.scope.owner?.component ?? null
6751
6976
  });
6752
- const initial = hooks.raw ? ctx.expression : ctx.evaluate();
6753
- hooks.created?.(ctx.el, makeBinding(initial));
6754
- hooks.beforeMount?.(ctx.el, makeBinding(initial));
6977
+ const initial = hooks2.raw ? ctx.expression : ctx.evaluate();
6978
+ hooks2.created?.(ctx.el, makeBinding(initial));
6979
+ hooks2.beforeMount?.(ctx.el, makeBinding(initial));
6755
6980
  ctx.effect(() => {
6756
- const value = hooks.raw ? ctx.expression : ctx.evaluate();
6981
+ const value = hooks2.raw ? ctx.expression : ctx.evaluate();
6757
6982
  if (!mounted2) {
6758
6983
  mounted2 = true;
6759
6984
  oldValue = value;
6760
- hooks.mounted?.(ctx.el, makeBinding(value));
6985
+ hooks2.mounted?.(ctx.el, makeBinding(value));
6761
6986
  return;
6762
6987
  }
6763
6988
  if (value === oldValue) return;
6764
6989
  const binding = makeBinding(value);
6765
- hooks.updated?.(ctx.el, binding);
6990
+ hooks2.updated?.(ctx.el, binding);
6766
6991
  oldValue = value;
6767
6992
  });
6768
6993
  ctx.cleanup(() => {
6769
6994
  const binding = makeBinding(oldValue);
6770
- hooks.beforeUnmount?.(ctx.el, binding);
6771
- hooks.unmounted?.(ctx.el, binding);
6995
+ hooks2.beforeUnmount?.(ctx.el, binding);
6996
+ hooks2.unmounted?.(ctx.el, binding);
6772
6997
  });
6773
6998
  },
6774
- { priority: hooks.priority ?? exports.PRIORITY.DEFAULT, terminal: hooks.terminal ?? false }
6999
+ { priority: hooks2.priority ?? exports.PRIORITY.DEFAULT, terminal: hooks2.terminal ?? false }
6775
7000
  );
6776
7001
  }
6777
7002
  function data(values) {
6778
7003
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
6779
7004
  return rootScope.data;
6780
7005
  }
6781
- var version2 = "0.11.1";
7006
+ var version2 = "0.12.1";
6782
7007
  var core = {
6783
7008
  // Utilities first: Voodoo's own names can override.
6784
7009
  ...utils_exports,
@@ -13140,12 +13365,15 @@ magic("$history", (scope) => scope.el ? findController(scope.el) : null);
13140
13365
 
13141
13366
  // src/sound/index.ts
13142
13367
  init_registry();
13368
+ init_reactivity();
13369
+ init_style();
13143
13370
  var audioContext = null;
13144
13371
  var masterVolume = 0.35;
13145
13372
  var isMuted = false;
13146
13373
  var hasLoadedPreference = false;
13147
13374
  var VOLUME_KEY = "voodoo:sound:volume";
13148
13375
  var MUTE_KEY = "voodoo:sound:muted";
13376
+ var muteRevision = ref(0);
13149
13377
  function loadPreference() {
13150
13378
  if (hasLoadedPreference) return;
13151
13379
  hasLoadedPreference = true;
@@ -13457,6 +13685,7 @@ var sound = {
13457
13685
  loadPreference();
13458
13686
  isMuted = value;
13459
13687
  storage.set(MUTE_KEY, isMuted);
13688
+ muteRevision.value++;
13460
13689
  },
13461
13690
  /** Unmutes sound. */
13462
13691
  unmute() {
@@ -13468,8 +13697,18 @@ var sound = {
13468
13697
  this.mute(!isMuted);
13469
13698
  return isMuted;
13470
13699
  },
13471
- /** `true` when muted. */
13700
+ /**
13701
+ * `true` when muted.
13702
+ *
13703
+ * Reads a revision ref first, so that an expression asking whether sound is
13704
+ * muted actually re-runs when it changes. The state lives in a module
13705
+ * variable and in localStorage, both invisible to the Proxy, so
13706
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
13707
+ * no way to show whether it was muted, which made the mute button look like
13708
+ * it did nothing at all.
13709
+ */
13472
13710
  get muted() {
13711
+ void muteRevision.value;
13473
13712
  loadPreference();
13474
13713
  return isMuted;
13475
13714
  },
@@ -13519,11 +13758,18 @@ defineDirective("sound", ({ el, arg, expression, modifiers, scope, cleanup, eval
13519
13758
  el.addEventListener(event, play);
13520
13759
  cleanup(() => el.removeEventListener(event, play));
13521
13760
  });
13761
+ var MUTE_CSS = `
13762
+ .v-muted{opacity:.55}
13763
+ .v-muted::after{content:" \\1F507";font-size:.9em}
13764
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
13765
+ `;
13522
13766
  defineDirective("mute", ({ el, cleanup }) => {
13767
+ injectStyle("mute", MUTE_CSS);
13523
13768
  const sync = () => {
13524
13769
  const isMuted2 = sound.muted;
13525
13770
  el.setAttribute("aria-pressed", String(isMuted2));
13526
13771
  el.classList.toggle("v-muted", isMuted2);
13772
+ el.classList.toggle("v-mute-on", !isMuted2);
13527
13773
  };
13528
13774
  const toggle = () => {
13529
13775
  sound.toggle();
@@ -23561,11 +23807,14 @@ exports.getLocale = getLocale;
23561
23807
  exports.getScope = getScope;
23562
23808
  exports.gpu = gpu;
23563
23809
  exports.groupBy = groupBy;
23810
+ exports.hook = hook;
23811
+ exports.hooks = hooks;
23564
23812
  exports.hotkey = hotkey;
23565
23813
  exports.http = http;
23566
23814
  exports.i18n = i18n;
23567
23815
  exports.inView = inView;
23568
23816
  exports.injectStyle = injectStyle;
23817
+ exports.installHooks = installHooks;
23569
23818
  exports.instances = instances;
23570
23819
  exports.isBrowser = isBrowser;
23571
23820
  exports.isDevtoolsWidgetMounted = isDevtoolsWidgetMounted;
@@ -23652,6 +23901,11 @@ exports.unmask = unmask;
23652
23901
  exports.unmountDevtoolsWidget = unmountDevtoolsWidget;
23653
23902
  exports.unref = unref;
23654
23903
  exports.url = url;
23904
+ exports.useContext = useContext;
23905
+ exports.useEffect = useEffect;
23906
+ exports.useMemo = useMemo;
23907
+ exports.useRef = useRef;
23908
+ exports.useState = useState;
23655
23909
  exports.uuid = uuid;
23656
23910
  exports.validate = validate;
23657
23911
  exports.validator = validator;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
- import { S as Scope, c as core, V as VoodooCollection } from './query-CbHihkvO.cjs';
2
- export { A as App, a as AppOptions, C as ComponentDefinition, D as DirectiveBinding, b as DirectiveHooks, P as PRIORITY, R as Resource, d as ResourceOptions, e as VoodooConfig, f as VoodooPlugin, g as VoodooRuntimeError, h as VoodooSyntaxError, i as addCleanup, j as allStores, k as allowedGlobals, l as cache, m as clearParseCache, n as config, o as cookie, p as createApp, q as createResource, r as defineComponent, s as defineDirective, t as destroy, u as documentReady, v as ensureTokens, w as enter, x as evaluate, y as fadeIn, z as fadeOut, B as findScope, E as fromHtml, F as getScope, G as injectStyle, H as instances, I as leave, J as magic, K as magics, L as mountComponent, M as parse, N as query, O as ready, Q as refresh, T as removeStore, q as resource, U as rootScope, W as session, X as slideDown, Y as slideUp, Z as start, _ as storage, $ as store, a0 as storeNames, a1 as stringify, a2 as theme, a3 as toast, a4 as tokenize, a5 as url, a6 as viewTransition, a7 as walk, a8 as whenElement, a9 as whenReady } from './query-CbHihkvO.cjs';
1
+ import { S as Scope, c as core, V as VoodooCollection } from './query-XooOh4vB.cjs';
2
+ export { A as App, a as AppOptions, C as ComponentDefinition, D as DirectiveBinding, b as DirectiveHooks, P as PRIORITY, R as Resource, d as ResourceOptions, e as VoodooConfig, f as VoodooPlugin, g as VoodooRuntimeError, h as VoodooSyntaxError, i as addCleanup, j as allStores, k as allowedGlobals, l as cache, m as clearParseCache, n as config, o as cookie, p as createApp, q as createResource, r as defineComponent, s as defineDirective, t as destroy, u as documentReady, v as ensureTokens, w as enter, x as evaluate, y as fadeIn, z as fadeOut, B as findScope, E as fromHtml, F as getScope, G as hook, H as hooks, I as injectStyle, J as instances, K as leave, L as magic, M as magics, N as mountComponent, O as parse, Q as query, T as ready, U as refresh, W as removeStore, q as resource, X as rootScope, Y as session, Z as slideDown, _ as slideUp, $ as start, a0 as storage, a1 as store, a2 as storeNames, a3 as stringify, a4 as theme, a5 as toast, a6 as tokenize, a7 as url, a8 as viewTransition, a9 as walk, aa as whenElement, ab as whenReady } from './query-XooOh4vB.cjs';
3
+ import { Ref } from './reactivity.cjs';
3
4
  export { EffectScope, computed, effect, effectScope, flushSync, isReactive, markRaw, nextTick, reactive, ref, shallowRef, stop, toRaw, unref, watch, watchEffect } from './reactivity.cjs';
4
5
  export { HttpError, HttpMethod, HttpResponse, RequestConfig, http, request } from './http.cjs';
5
6
  export { R as RoomOptions, a as RoomState, S as SocketMessage, b as SocketOptions, c as SocketRoom, d as SocketState, e as SocketTransport, V as VoodooSocket, f as createSocket, s as socket, g as socketSupported } from './index-DTllqUtj.cjs';
@@ -171,7 +172,16 @@ declare const sound: {
171
172
  unmute(): void;
172
173
  /** Toggles between muted and unmuted, and returns the new state. */
173
174
  toggle(): boolean;
174
- /** `true` when muted. */
175
+ /**
176
+ * `true` when muted.
177
+ *
178
+ * Reads a revision ref first, so that an expression asking whether sound is
179
+ * muted actually re-runs when it changes. The state lives in a module
180
+ * variable and in localStorage, both invisible to the Proxy, so
181
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
182
+ * no way to show whether it was muted, which made the mute button look like
183
+ * it did nothing at all.
184
+ */
175
185
  readonly muted: boolean;
176
186
  /** Names of all available effects. */
177
187
  readonly names: string[];
@@ -481,6 +491,80 @@ declare const charts: {
481
491
  colors: string[];
482
492
  };
483
493
 
494
+ /**
495
+ * @module hooks
496
+ *
497
+ * React-style hooks, reachable by bare name inside any expression.
498
+ *
499
+ * These are a thin surface over primitives the library already had — `effect`,
500
+ * `computed`, `ref` and `store`. The value is not new machinery, it is that
501
+ * someone arriving from React can write what they already know and have it mean
502
+ * the right thing here.
503
+ *
504
+ * Two differences from React are deliberate, and both are improvements rather
505
+ * than gaps:
506
+ *
507
+ * 1. `deps` is optional everywhere. Voodoo tracks reads through a Proxy, so a
508
+ * hook with no dependency array re-runs exactly when something it actually
509
+ * read changes. The array is there for when you want to narrow that, not
510
+ * because the library cannot work it out.
511
+ *
512
+ * 2. There is no rule against calling a hook in a branch. Slots are keyed per
513
+ * scope in call order within one evaluation, and `v-data` and `v-init` each
514
+ * evaluate once per scope, so the order is stable without a rule asking you
515
+ * to keep it stable.
516
+ *
517
+ * `useState` and `useMemo` return refs, and `reactive()` unwraps refs on read,
518
+ * so inside `v-data` they read as plain values with no `.value` anywhere.
519
+ * `useRef` deliberately does not: it returns a raw `{ current }` box that no
520
+ * effect subscribes to, which is the whole point of it.
521
+ */
522
+
523
+ /**
524
+ * A value that survives re-evaluation and re-renders whatever reads it.
525
+ *
526
+ * Returns a ref. Written into `v-data` it reads and assigns as a plain value,
527
+ * because reactive objects unwrap refs, so `count++` works with no `.value`.
528
+ */
529
+ declare function useState<T>(scope: Scope, initial: T): Ref<T>;
530
+ /**
531
+ * Runs a side effect, and re-runs it when what it depends on changes.
532
+ *
533
+ * With no dependency array it tracks its own reads and re-runs when any of them
534
+ * change. With `[]` it runs once. With `[a, b]` it re-runs only when one of
535
+ * those differs from last time. Returning a function from `fn` registers
536
+ * cleanup, called before the next run and again when the element is removed.
537
+ */
538
+ declare function useEffect(scope: Scope, fn: () => void | (() => void), deps?: unknown): void;
539
+ /**
540
+ * Caches a computation.
541
+ *
542
+ * With no dependency array it becomes a `computed`: lazy, cached, and
543
+ * recomputed only when something it read actually changed. With an array it
544
+ * recomputes when that array changes. Returns a ref, so inside `v-data` it
545
+ * reads as the value itself.
546
+ */
547
+ declare function useMemo<T>(scope: Scope, fn: () => T, deps?: unknown): Ref<T>;
548
+ /**
549
+ * A box that survives re-evaluation and that nothing subscribes to.
550
+ *
551
+ * Use it for a DOM element, a timer id, or a previous value — anything you want
552
+ * to keep without the page reacting when it changes. Deliberately not a ref, so
553
+ * reading `.current` inside an effect does not make that effect depend on it.
554
+ */
555
+ declare function useRef<T>(scope: Scope, initial: T): {
556
+ current: T;
557
+ };
558
+ /**
559
+ * Shared state, reachable from any component without threading it through the
560
+ * markup by hand.
561
+ *
562
+ * Backed by the store registry, so `useContext('user')` and `$store.user` are
563
+ * the same object. Passing a second argument creates the store on first use.
564
+ */
565
+ declare function useContext<T extends Record<string, any>>(name: string, initial?: T): T;
566
+ declare function installHooks(): void;
567
+
484
568
  /**
485
569
  * @module runtime/magics
486
570
  *
@@ -1714,4 +1798,4 @@ interface Voodoo extends Omit<typeof core, never> {
1714
1798
  }
1715
1799
  declare const V: Voodoo;
1716
1800
 
1717
- export { Scope, V, type Voodoo, VoodooCollection, activateJsx, alert, animate, applyMask, applyRegions, charts, clearErrors, clipboard, confirm, V as default, devtoolsBus, devtoolsWidget, dialog, disableXray, easings, enableXray, extractJsx, getLocale, hotkey, i18n, inView, isDevtoolsWidgetMounted, isXrayEnabled, jsx, mask, masks, modal, motionPresets, mountDevtoolsWidget, navigate, network, palette, prompt, readDeclarationBlock, registerMask, renderChart, route, router, screen, scrollProgress, serializeForm, setLocale, showFormErrors, sound, efeitos as soundEffects, spring, stagger, t, unmask, unmountDevtoolsWidget, validate, validator, xray };
1801
+ export { Scope, V, type Voodoo, VoodooCollection, activateJsx, alert, animate, applyMask, applyRegions, charts, clearErrors, clipboard, confirm, V as default, devtoolsBus, devtoolsWidget, dialog, disableXray, easings, enableXray, extractJsx, getLocale, hotkey, i18n, inView, installHooks, isDevtoolsWidgetMounted, isXrayEnabled, jsx, mask, masks, modal, motionPresets, mountDevtoolsWidget, navigate, network, palette, prompt, readDeclarationBlock, registerMask, renderChart, route, router, screen, scrollProgress, serializeForm, setLocale, showFormErrors, sound, efeitos as soundEffects, spring, stagger, t, unmask, unmountDevtoolsWidget, useContext, useEffect, useMemo, useRef, useState, validate, validator, xray };