voodoojs 0.11.2 → 0.12.2

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 +1 -1
  2. package/dist/{chunk-IGZSDZKU.js → chunk-7AKBELZA.js} +4 -4
  3. package/dist/{chunk-V3O3WOZH.js → chunk-AVXQPDQH.js} +4 -4
  4. package/dist/{chunk-AH2VXDZD.js → chunk-K2U3O36M.js} +193 -20
  5. package/dist/{chunk-IS3DR2KY.js → chunk-MEE2ZAKJ.js} +3 -3
  6. package/dist/{chunk-DJJB35SR.js → chunk-NVANUFWZ.js} +6 -6
  7. package/dist/{chunk-H2ZUEQWV.js → chunk-OWCQV42D.js} +112 -20
  8. package/dist/{chunk-KQYSJHZR.js → chunk-RTZEIVNA.js} +5 -5
  9. package/dist/{chunk-NFDXVIII.js → chunk-RXOLGHSU.js} +3 -3
  10. package/dist/{chunk-WJP3YGUI.js → chunk-S4BXAGAW.js} +4 -4
  11. package/dist/{chunk-DCE5WIGA.js → chunk-UGLD36KV.js} +5 -5
  12. package/dist/{chunk-UV5PMS7P.js → chunk-YV4I45SK.js} +4 -4
  13. package/dist/essential.cjs +297 -28
  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 +325 -34
  22. package/dist/index.d.cts +88 -4
  23. package/dist/index.d.ts +88 -4
  24. package/dist/index.js +38 -24
  25. package/dist/{query-DiQAS73Q.d.cts → query-D_BGQw8m.d.cts} +12 -1
  26. package/dist/{query-Cf-7iibE.d.ts → query-Dl6Urlwd.d.ts} +12 -1
  27. package/dist/reactivity.cjs +1 -1
  28. package/dist/reactivity.js +2 -2
  29. package/dist/socket.cjs +29 -2
  30. package/dist/socket.js +9 -9
  31. package/dist/style-GWGXWHB4.js +5 -0
  32. package/dist/utils.cjs +1 -1
  33. package/dist/utils.js +2 -2
  34. package/dist/voodoo.core.js +277 -27
  35. package/dist/voodoo.core.min.js +17 -17
  36. package/dist/voodoo.full.js +325 -36
  37. package/dist/voodoo.full.min.js +59 -55
  38. package/dist/voodoo.js +305 -30
  39. package/dist/voodoo.min.js +29 -25
  40. package/package.json +1 -1
  41. package/dist/style-4X62SABN.js +0 -5
@@ -1,7 +1,7 @@
1
- import { __publicField } from './chunk-IS3DR2KY.js';
1
+ import { __publicField } from './chunk-MEE2ZAKJ.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.2
4
+ * Voodoo.js v0.12.2
5
5
  * JavaScript feels like magic.
6
6
  * (c) 2026 Voodoo.js contributors. MIT License.
7
7
  */
@@ -637,5 +637,5 @@ function traverse(value, seen = /* @__PURE__ */ new Set()) {
637
637
  }
638
638
 
639
639
  export { EffectScope, ITERATE_KEY, ReactiveEffect, TriggerType, computed, effect, effectScope, enableTracking, flushSync, getActiveEffect, getActiveScope, handleError, hasChanged, isReactive, isRef, markRaw, nextTick, pauseTracking, queueJob, queuePostFlush, reactive, ref, resetTracking, setErrorHandler, shallowRef, stop, toRaw, track, trigger, unref, warn, watch, watchEffect };
640
- //# sourceMappingURL=chunk-WJP3YGUI.js.map
641
- //# sourceMappingURL=chunk-WJP3YGUI.js.map
640
+ //# sourceMappingURL=chunk-S4BXAGAW.js.map
641
+ //# sourceMappingURL=chunk-S4BXAGAW.js.map
@@ -1,8 +1,8 @@
1
- import { reactive } from './chunk-WJP3YGUI.js';
2
- import { warnOnce } from './chunk-UV5PMS7P.js';
1
+ import { reactive } from './chunk-S4BXAGAW.js';
2
+ import { warnOnce } from './chunk-YV4I45SK.js';
3
3
 
4
4
  /**
5
- * Voodoo.js v0.11.2
5
+ * Voodoo.js v0.12.2
6
6
  * JavaScript feels like magic.
7
7
  * (c) 2026 Voodoo.js contributors. MIT License.
8
8
  */
@@ -942,5 +942,5 @@ Object.defineProperty(factory, "open", {
942
942
  var socket = factory;
943
943
 
944
944
  export { ENGINE, SIO, createSocket, decodeEngine, decodeSocketIo, devtoolsBus, encodeSocketIo, engineURL, resolveSocketURL, socket, socketSupported };
945
- //# sourceMappingURL=chunk-DCE5WIGA.js.map
946
- //# sourceMappingURL=chunk-DCE5WIGA.js.map
945
+ //# sourceMappingURL=chunk-UGLD36KV.js.map
946
+ //# sourceMappingURL=chunk-UGLD36KV.js.map
@@ -1,7 +1,7 @@
1
- import { config } from './chunk-NFDXVIII.js';
1
+ import { config } from './chunk-RXOLGHSU.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.2
4
+ * Voodoo.js v0.12.2
5
5
  * JavaScript feels like magic.
6
6
  * (c) 2026 Voodoo.js contributors. MIT License.
7
7
  */
@@ -78,5 +78,5 @@ function warnAlias(alias, canonical) {
78
78
  }
79
79
 
80
80
  export { describeElement, inDevelopment, warn, warnAlias, warnDuplicateKey, warnInvalidExpression, warnOnce, warnRequiredProp, warnUnknownComponent, warnUnknownDirective };
81
- //# sourceMappingURL=chunk-UV5PMS7P.js.map
82
- //# sourceMappingURL=chunk-UV5PMS7P.js.map
81
+ //# sourceMappingURL=chunk-YV4I45SK.js.map
82
+ //# sourceMappingURL=chunk-YV4I45SK.js.map
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  /**
6
- * Voodoo.js v0.11.2
6
+ * Voodoo.js v0.12.2
7
7
  * JavaScript feels like magic.
8
8
  * (c) 2026 Voodoo.js contributors. MIT License.
9
9
  */
@@ -1773,6 +1773,23 @@ var DELIBERATELY_WITHHELD = /* @__PURE__ */ new Set([
1773
1773
  "SharedWorker",
1774
1774
  "ServiceWorker"
1775
1775
  ]);
1776
+ function guardedTimer(name) {
1777
+ return function(handler, timeout, ...rest) {
1778
+ if (typeof handler !== "function") {
1779
+ throw new VoodooRuntimeError(
1780
+ `${name} needs a function. Passing a string would compile it, which this library never does.`
1781
+ );
1782
+ }
1783
+ const timer = globalThis[name];
1784
+ return timer(handler, timeout, ...rest);
1785
+ };
1786
+ }
1787
+ function forwardGlobal(name) {
1788
+ return function(...args) {
1789
+ const fn = globalThis[name];
1790
+ return fn(...args);
1791
+ };
1792
+ }
1776
1793
  var allowedGlobals = {
1777
1794
  Math,
1778
1795
  JSON,
@@ -1791,7 +1808,17 @@ var allowedGlobals = {
1791
1808
  isFinite,
1792
1809
  encodeURIComponent,
1793
1810
  decodeURIComponent,
1794
- console
1811
+ console,
1812
+ ...typeof setTimeout !== "undefined" ? {
1813
+ setTimeout: guardedTimer("setTimeout"),
1814
+ setInterval: guardedTimer("setInterval"),
1815
+ clearTimeout: forwardGlobal("clearTimeout"),
1816
+ clearInterval: forwardGlobal("clearInterval")
1817
+ } : {},
1818
+ ...typeof requestAnimationFrame !== "undefined" ? {
1819
+ requestAnimationFrame: forwardGlobal("requestAnimationFrame"),
1820
+ cancelAnimationFrame: forwardGlobal("cancelAnimationFrame")
1821
+ } : {}
1795
1822
  };
1796
1823
  var VoodooRuntimeError = class extends Error {
1797
1824
  constructor(message, expression) {
@@ -2237,6 +2264,10 @@ var magics = /* @__PURE__ */ new Map();
2237
2264
  function magic(name, getter) {
2238
2265
  magics.set(name.startsWith("$") ? name : `$${name}`, getter);
2239
2266
  }
2267
+ var hooks = /* @__PURE__ */ new Map();
2268
+ function hook(name, getter) {
2269
+ hooks.set(name, getter);
2270
+ }
2240
2271
  var Scope = class _Scope {
2241
2272
  // Assignment order matches the order the fields were declared in before, so
2242
2273
  // the properties are created in the same sequence they always were.
@@ -2292,7 +2323,10 @@ var Scope = class _Scope {
2292
2323
  s = s.parent;
2293
2324
  }
2294
2325
  if (name.charCodeAt(0) === 36 && magics.has(name)) {
2295
- return this.magicContainer(name);
2326
+ return this.magicContainer(name, magics);
2327
+ }
2328
+ if (hooks.has(name)) {
2329
+ return this.magicContainer(name, hooks);
2296
2330
  }
2297
2331
  return void 0;
2298
2332
  }
@@ -2321,11 +2355,11 @@ var Scope = class _Scope {
2321
2355
  reactiveChild(vars, el = null) {
2322
2356
  return new _Scope(reactive(vars), this, el ?? this.el);
2323
2357
  }
2324
- magicContainer(name) {
2358
+ magicContainer(name, registry) {
2325
2359
  if (!this.magicCache) this.magicCache = /* @__PURE__ */ new Map();
2326
2360
  const cached = this.magicCache.get(name);
2327
2361
  if (cached) return cached;
2328
- const getter = magics.get(name);
2362
+ const getter = registry.get(name);
2329
2363
  const scope = this;
2330
2364
  const container2 = {};
2331
2365
  Object.defineProperty(container2, name, {
@@ -2714,7 +2748,16 @@ function runDirective(el, attr2, scope) {
2714
2748
  },
2715
2749
  effect(fn) {
2716
2750
  const owner = ownerScope();
2717
- owner.run(() => effect(fn, { scope: owner }));
2751
+ const hosted = () => {
2752
+ const previous = hookHost;
2753
+ hookHost = el;
2754
+ try {
2755
+ fn();
2756
+ } finally {
2757
+ hookHost = previous;
2758
+ }
2759
+ };
2760
+ owner.run(() => effect(hosted, { scope: owner }));
2718
2761
  },
2719
2762
  cleanup(fn) {
2720
2763
  addCleanup(el, fn);
@@ -2762,6 +2805,56 @@ function walk(node, scope) {
2762
2805
  return;
2763
2806
  }
2764
2807
  initialized.add(el);
2808
+ const previousHost = hookHost;
2809
+ hookHost = el;
2810
+ try {
2811
+ walkElementDirectives(el, attrs, tagComponent, current2);
2812
+ } finally {
2813
+ hookHost = previousHost;
2814
+ }
2815
+ }
2816
+ var hookHost = null;
2817
+ function currentHookHost() {
2818
+ return hookHost;
2819
+ }
2820
+ function createDataScope(expression, parent, el) {
2821
+ let ast = null;
2822
+ try {
2823
+ ast = parse(expression);
2824
+ } catch {
2825
+ ast = null;
2826
+ }
2827
+ const plain = ast && ast.t === "obj" && ast.props.every(
2828
+ (p2) => !p2.spread && !p2.keyExpr
2829
+ );
2830
+ if (!plain) {
2831
+ const raw = evaluateIn(expression, parent, "v-data");
2832
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2833
+ }
2834
+ const scope = parent.reactiveChild({}, el);
2835
+ const props = ast.props;
2836
+ for (const prop of props) {
2837
+ if (prop.key === null) continue;
2838
+ try {
2839
+ if (prop.getter) {
2840
+ const compute = evaluate(prop.value, scope);
2841
+ Object.defineProperty(scope.data, prop.key, {
2842
+ enumerable: true,
2843
+ configurable: true,
2844
+ get: () => compute.call(scope.data)
2845
+ });
2846
+ } else {
2847
+ scope.data[prop.key] = evaluate(prop.value, scope);
2848
+ }
2849
+ } catch (err) {
2850
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
2851
+ else handleError(err, "v-data");
2852
+ }
2853
+ }
2854
+ return scope;
2855
+ }
2856
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
2857
+ let current2 = scope;
2765
2858
  for (const attr2 of attrs) {
2766
2859
  const def = directives.get(attr2.name);
2767
2860
  if (def?.terminal) {
@@ -2781,11 +2874,10 @@ function walk(node, scope) {
2781
2874
  nodeScopes.set(el, current2);
2782
2875
  }
2783
2876
  } else if (dataAttr || componentAttr) {
2784
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current2, "v-data") : {};
2785
- current2 = current2.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2877
+ current2 = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current2, el);
2786
2878
  nodeScopes.set(el, current2);
2787
2879
  }
2788
- const attributeScope = mountedComponent ? activeScope2 : current2;
2880
+ const attributeScope = mountedComponent ? scope : current2;
2789
2881
  for (const attr2 of attrs) {
2790
2882
  if (attr2.name === "data" || attr2.name === "component") continue;
2791
2883
  runDirective(el, attr2, attributeScope);
@@ -2941,9 +3033,9 @@ var started = false;
2941
3033
  var observer = null;
2942
3034
  var startHooks = [];
2943
3035
  function runPhase(target, after) {
2944
- for (const hook of startHooks) {
3036
+ for (const hook2 of startHooks) {
2945
3037
  try {
2946
- hook(target, after);
3038
+ hook2(target, after);
2947
3039
  } catch (error) {
2948
3040
  console.error("[Voodoo] a start hook failed", error);
2949
3041
  }
@@ -3321,10 +3413,10 @@ var LIFECYCLE = /* @__PURE__ */ new Set([
3321
3413
  "destroyed"
3322
3414
  ]);
3323
3415
  function callHook(def, instance, name) {
3324
- const hook = def[name];
3325
- if (typeof hook !== "function") return;
3416
+ const hook2 = def[name];
3417
+ if (typeof hook2 !== "function") return;
3326
3418
  try {
3327
- hook.call(instance);
3419
+ hook2.call(instance);
3328
3420
  } catch (err) {
3329
3421
  handleError(err, `hook ${name}`);
3330
3422
  }
@@ -4761,6 +4853,7 @@ var toast = Object.assign(
4761
4853
  );
4762
4854
 
4763
4855
  // src/storage/index.ts
4856
+ init_reactivity();
4764
4857
  function createStorage(getStore, prefix = "") {
4765
4858
  const full = (key) => prefix + key;
4766
4859
  return {
@@ -4930,21 +5023,28 @@ var cache2 = {
4930
5023
  };
4931
5024
  var THEME_KEY = "voodoo:theme";
4932
5025
  var picked = null;
5026
+ var revision = ref(0);
4933
5027
  var theme = {
4934
5028
  /** Theme chosen by the user, or `system` when never set. */
4935
5029
  get current() {
5030
+ void revision.value;
4936
5031
  return storage.get(THEME_KEY) ?? picked ?? "system";
4937
5032
  },
4938
5033
  /** Theme effectively applied, resolving `system`. */
4939
5034
  get resolved() {
4940
5035
  const value = this.current;
4941
5036
  if (value !== "system") return value;
5037
+ if (typeof document !== "undefined") {
5038
+ const authored = document.documentElement.getAttribute("data-theme");
5039
+ if (authored === "light" || authored === "dark") return authored;
5040
+ }
4942
5041
  if (typeof matchMedia === "undefined") return "light";
4943
5042
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
4944
5043
  },
4945
5044
  set(value) {
4946
5045
  picked = value;
4947
5046
  storage.set(THEME_KEY, value);
5047
+ revision.value++;
4948
5048
  this.apply();
4949
5049
  },
4950
5050
  toggle() {
@@ -4978,8 +5078,17 @@ var theme = {
4978
5078
  init() {
4979
5079
  if (typeof document === "undefined") return;
4980
5080
  this.apply();
5081
+ if (typeof MutationObserver !== "undefined") {
5082
+ new MutationObserver(() => {
5083
+ revision.value++;
5084
+ }).observe(document.documentElement, {
5085
+ attributes: true,
5086
+ attributeFilter: ["data-theme"]
5087
+ });
5088
+ }
4981
5089
  if (typeof matchMedia === "undefined") return;
4982
5090
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
5091
+ revision.value++;
4983
5092
  if (this.current === "system") this.apply();
4984
5093
  });
4985
5094
  }
@@ -5112,6 +5221,140 @@ function installMagics() {
5112
5221
  );
5113
5222
  }
5114
5223
 
5224
+ // src/hooks/index.ts
5225
+ init_reactivity();
5226
+ var tables = /* @__PURE__ */ new WeakMap();
5227
+ function tableFor(scope) {
5228
+ const host = currentHookHost() ?? scope.el ?? scope;
5229
+ let table = tables.get(host);
5230
+ if (!table) {
5231
+ table = { slots: [], index: 0, scheduled: false, bound: false };
5232
+ tables.set(host, table);
5233
+ }
5234
+ const el = currentHookHost() ?? scope.el;
5235
+ if (!table.bound && el) {
5236
+ table.bound = true;
5237
+ const owned = table;
5238
+ addCleanup(el, () => {
5239
+ for (const slot of owned.slots) {
5240
+ if (slot.dispose) slot.dispose();
5241
+ if (slot.runner) stop(slot.runner);
5242
+ }
5243
+ owned.slots.length = 0;
5244
+ });
5245
+ }
5246
+ if (!table.scheduled) {
5247
+ table.scheduled = true;
5248
+ const pending = table;
5249
+ queueMicrotask(() => {
5250
+ pending.index = 0;
5251
+ pending.scheduled = false;
5252
+ });
5253
+ }
5254
+ return table;
5255
+ }
5256
+ function nextSlot(scope, kind) {
5257
+ const table = tableFor(scope);
5258
+ const at = table.index++;
5259
+ let slot = table.slots[at];
5260
+ if (!slot || slot.kind !== kind) {
5261
+ if (slot) {
5262
+ if (slot.dispose) slot.dispose();
5263
+ if (slot.runner) stop(slot.runner);
5264
+ }
5265
+ slot = { kind, deps: null, value: void 0 };
5266
+ table.slots[at] = slot;
5267
+ }
5268
+ return slot;
5269
+ }
5270
+ function depsChanged(previous, next) {
5271
+ if (!previous || !next) return true;
5272
+ if (previous.length !== next.length) return true;
5273
+ for (let i = 0; i < next.length; i++) {
5274
+ if (!Object.is(previous[i], next[i])) return true;
5275
+ }
5276
+ return false;
5277
+ }
5278
+ function normalizeDeps(deps) {
5279
+ return Array.isArray(deps) ? deps.slice() : null;
5280
+ }
5281
+ function useState(scope, initial) {
5282
+ const slot = nextSlot(scope, "state");
5283
+ if (slot.value === void 0) slot.value = ref(initial);
5284
+ return slot.value;
5285
+ }
5286
+ function useEffect(scope, fn, deps) {
5287
+ const slot = nextSlot(scope, "effect");
5288
+ const next = normalizeDeps(deps);
5289
+ const runCleanup = () => {
5290
+ if (slot.dispose) {
5291
+ const dispose = slot.dispose;
5292
+ slot.dispose = void 0;
5293
+ dispose();
5294
+ }
5295
+ };
5296
+ if (next) {
5297
+ const first = slot.deps === null && !slot.runner;
5298
+ if (first || depsChanged(slot.deps, next)) {
5299
+ slot.deps = next;
5300
+ runCleanup();
5301
+ const result = fn();
5302
+ if (typeof result === "function") slot.dispose = result;
5303
+ }
5304
+ return;
5305
+ }
5306
+ if (slot.runner) return;
5307
+ slot.deps = null;
5308
+ slot.runner = effect(() => {
5309
+ runCleanup();
5310
+ const result = fn();
5311
+ if (typeof result === "function") slot.dispose = result;
5312
+ });
5313
+ }
5314
+ function useMemo(scope, fn, deps) {
5315
+ const slot = nextSlot(scope, "memo");
5316
+ const next = normalizeDeps(deps);
5317
+ if (!next) {
5318
+ if (!slot.value) slot.value = computed(fn);
5319
+ return slot.value;
5320
+ }
5321
+ if (!slot.value) {
5322
+ slot.value = ref(fn());
5323
+ slot.deps = next;
5324
+ } else if (depsChanged(slot.deps, next)) {
5325
+ slot.deps = next;
5326
+ slot.value.value = fn();
5327
+ }
5328
+ return slot.value;
5329
+ }
5330
+ function useRef(scope, initial) {
5331
+ const slot = nextSlot(scope, "ref");
5332
+ if (!slot.value) slot.value = markRaw({ current: initial });
5333
+ return slot.value;
5334
+ }
5335
+ function useContext(name, initial) {
5336
+ if (initial !== void 0 && !(name in allStores)) {
5337
+ store(name, initial);
5338
+ }
5339
+ return allStores[name];
5340
+ }
5341
+ function installHooks() {
5342
+ hook("useState", (scope) => (initial) => useState(scope, initial));
5343
+ hook(
5344
+ "useEffect",
5345
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
5346
+ );
5347
+ hook(
5348
+ "useMemo",
5349
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
5350
+ );
5351
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
5352
+ hook(
5353
+ "useContext",
5354
+ () => (name, initial) => useContext(name, initial)
5355
+ );
5356
+ }
5357
+
5115
5358
  // src/http/resource.ts
5116
5359
  init_reactivity();
5117
5360
  function pick(value, path) {
@@ -6694,6 +6937,7 @@ setComponentMounter(mountComponent);
6694
6937
  setScopeMarker(markNodeScope);
6695
6938
  setDirectiveRegistrar(directive);
6696
6939
  installMagics();
6940
+ installHooks();
6697
6941
  var eventBus = /* @__PURE__ */ new Map();
6698
6942
  function on(name, handler) {
6699
6943
  let set2 = eventBus.get(name);
@@ -6727,7 +6971,7 @@ function off(name, handler) {
6727
6971
  eventBus.get(name)?.delete(handler);
6728
6972
  }
6729
6973
  function directive(name, definition) {
6730
- const hooks = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6974
+ const hooks2 = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6731
6975
  defineDirective(
6732
6976
  name,
6733
6977
  (ctx) => {
@@ -6743,36 +6987,36 @@ function directive(name, definition) {
6743
6987
  scope: ctx.scope,
6744
6988
  instance: ctx.scope.owner?.component ?? null
6745
6989
  });
6746
- const initial = hooks.raw ? ctx.expression : ctx.evaluate();
6747
- hooks.created?.(ctx.el, makeBinding(initial));
6748
- hooks.beforeMount?.(ctx.el, makeBinding(initial));
6990
+ const initial = hooks2.raw ? ctx.expression : ctx.evaluate();
6991
+ hooks2.created?.(ctx.el, makeBinding(initial));
6992
+ hooks2.beforeMount?.(ctx.el, makeBinding(initial));
6749
6993
  ctx.effect(() => {
6750
- const value = hooks.raw ? ctx.expression : ctx.evaluate();
6994
+ const value = hooks2.raw ? ctx.expression : ctx.evaluate();
6751
6995
  if (!mounted) {
6752
6996
  mounted = true;
6753
6997
  oldValue = value;
6754
- hooks.mounted?.(ctx.el, makeBinding(value));
6998
+ hooks2.mounted?.(ctx.el, makeBinding(value));
6755
6999
  return;
6756
7000
  }
6757
7001
  if (value === oldValue) return;
6758
7002
  const binding = makeBinding(value);
6759
- hooks.updated?.(ctx.el, binding);
7003
+ hooks2.updated?.(ctx.el, binding);
6760
7004
  oldValue = value;
6761
7005
  });
6762
7006
  ctx.cleanup(() => {
6763
7007
  const binding = makeBinding(oldValue);
6764
- hooks.beforeUnmount?.(ctx.el, binding);
6765
- hooks.unmounted?.(ctx.el, binding);
7008
+ hooks2.beforeUnmount?.(ctx.el, binding);
7009
+ hooks2.unmounted?.(ctx.el, binding);
6766
7010
  });
6767
7011
  },
6768
- { priority: hooks.priority ?? PRIORITY.DEFAULT, terminal: hooks.terminal ?? false }
7012
+ { priority: hooks2.priority ?? PRIORITY.DEFAULT, terminal: hooks2.terminal ?? false }
6769
7013
  );
6770
7014
  }
6771
7015
  function data(values) {
6772
7016
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
6773
7017
  return rootScope.data;
6774
7018
  }
6775
- var version2 = "0.11.2";
7019
+ var version2 = "0.12.2";
6776
7020
  var core = {
6777
7021
  // Utilities first: Voodoo's own names can override.
6778
7022
  ...utils_exports,
@@ -8965,6 +9209,7 @@ defineDirective("collapse", ({ el }) => {
8965
9209
  ensureUi();
8966
9210
  collapseOf(el);
8967
9211
  });
9212
+ var selfToggling = /* @__PURE__ */ new WeakSet();
8968
9213
  defineDirective("collapse-toggle", ({ el, expression, cleanup }) => {
8969
9214
  ensureUi();
8970
9215
  const target = resolveTarget(el, expression);
@@ -8973,6 +9218,8 @@ defineDirective("collapse-toggle", ({ el, expression, cleanup }) => {
8973
9218
  controller.triggers.add(el);
8974
9219
  controller.sync();
8975
9220
  makeInteractive(el, cleanup);
9221
+ selfToggling.add(el);
9222
+ cleanup(() => selfToggling.delete(el));
8976
9223
  const onClick = (event) => {
8977
9224
  event.preventDefault();
8978
9225
  controller.toggle();
@@ -9383,10 +9630,11 @@ defineDirective("accordion", ({ el, cleanup }) => {
9383
9630
  if (index === -1) return;
9384
9631
  event.preventDefault();
9385
9632
  const controller = controllers2[index];
9386
- if (single && !controller.open) {
9633
+ const ownToggle = selfToggling.has(header);
9634
+ if (single && (ownToggle ? controller.open : !controller.open)) {
9387
9635
  for (const other of controllers2) if (other !== controller) other.hide();
9388
9636
  }
9389
- controller.toggle();
9637
+ if (!ownToggle) controller.toggle();
9390
9638
  };
9391
9639
  const onKeyDown = (event) => {
9392
9640
  const header = event.target?.closest(".v-accordion-header");
@@ -12205,12 +12453,15 @@ magic("$history", (scope) => scope.el ? findController(scope.el) : null);
12205
12453
 
12206
12454
  // src/sound/index.ts
12207
12455
  init_registry();
12456
+ init_reactivity();
12457
+ init_style();
12208
12458
  var audioContext = null;
12209
12459
  var masterVolume = 0.35;
12210
12460
  var isMuted = false;
12211
12461
  var hasLoadedPreference = false;
12212
12462
  var VOLUME_KEY = "voodoo:sound:volume";
12213
12463
  var MUTE_KEY = "voodoo:sound:muted";
12464
+ var muteRevision = ref(0);
12214
12465
  function loadPreference() {
12215
12466
  if (hasLoadedPreference) return;
12216
12467
  hasLoadedPreference = true;
@@ -12522,6 +12773,7 @@ var sound = {
12522
12773
  loadPreference();
12523
12774
  isMuted = value;
12524
12775
  storage.set(MUTE_KEY, isMuted);
12776
+ muteRevision.value++;
12525
12777
  },
12526
12778
  /** Unmutes sound. */
12527
12779
  unmute() {
@@ -12533,8 +12785,18 @@ var sound = {
12533
12785
  this.mute(!isMuted);
12534
12786
  return isMuted;
12535
12787
  },
12536
- /** `true` when muted. */
12788
+ /**
12789
+ * `true` when muted.
12790
+ *
12791
+ * Reads a revision ref first, so that an expression asking whether sound is
12792
+ * muted actually re-runs when it changes. The state lives in a module
12793
+ * variable and in localStorage, both invisible to the Proxy, so
12794
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
12795
+ * no way to show whether it was muted, which made the mute button look like
12796
+ * it did nothing at all.
12797
+ */
12537
12798
  get muted() {
12799
+ void muteRevision.value;
12538
12800
  loadPreference();
12539
12801
  return isMuted;
12540
12802
  },
@@ -12584,11 +12846,18 @@ defineDirective("sound", ({ el, arg, expression, modifiers, scope, cleanup, eval
12584
12846
  el.addEventListener(event, play);
12585
12847
  cleanup(() => el.removeEventListener(event, play));
12586
12848
  });
12849
+ var MUTE_CSS = `
12850
+ .v-muted{opacity:.55}
12851
+ .v-muted::after{content:" \\1F507";font-size:.9em}
12852
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
12853
+ `;
12587
12854
  defineDirective("mute", ({ el, cleanup }) => {
12855
+ injectStyle("mute", MUTE_CSS);
12588
12856
  const sync = () => {
12589
12857
  const isMuted2 = sound.muted;
12590
12858
  el.setAttribute("aria-pressed", String(isMuted2));
12591
12859
  el.classList.toggle("v-muted", isMuted2);
12860
+ el.classList.toggle("v-mute-on", !isMuted2);
12592
12861
  };
12593
12862
  const toggle = () => {
12594
12863
  sound.toggle();
@@ -1,4 +1,4 @@
1
- import { c as core, V as VoodooCollection } from './query-DiQAS73Q.cjs';
1
+ import { c as core, V as VoodooCollection } from './query-D_BGQw8m.cjs';
2
2
  import './http.cjs';
3
3
  import './utils.cjs';
4
4
  import './reactivity.cjs';
@@ -1,4 +1,4 @@
1
- import { c as core, V as VoodooCollection } from './query-Cf-7iibE.js';
1
+ import { c as core, V as VoodooCollection } from './query-Dl6Urlwd.js';
2
2
  import './http.js';
3
3
  import './utils.js';
4
4
  import './reactivity.js';
package/dist/essential.js CHANGED
@@ -1,15 +1,15 @@
1
- import { core, sound, hotkey, palette, registerMask, unmask, applyMask, masks, mask, clearErrors, showFieldError, showFormErrors, messages, serializeForm, validate, validator, dialog, prompt, confirm, alert, modal, VoodooCollection, fromHtml, ready2, query } from './chunk-AH2VXDZD.js';
2
- import './chunk-DJJB35SR.js';
3
- import { magic } from './chunk-H2ZUEQWV.js';
4
- import './chunk-WJP3YGUI.js';
5
- import './chunk-UV5PMS7P.js';
6
- import './chunk-IGZSDZKU.js';
7
- import './chunk-V3O3WOZH.js';
8
- import './chunk-NFDXVIII.js';
9
- import './chunk-IS3DR2KY.js';
1
+ import { core, sound, hotkey, palette, registerMask, unmask, applyMask, masks, mask, clearErrors, showFieldError, showFormErrors, messages, serializeForm, validate, validator, dialog, prompt, confirm, alert, modal, VoodooCollection, fromHtml, ready2, query } from './chunk-K2U3O36M.js';
2
+ import './chunk-NVANUFWZ.js';
3
+ import { magic } from './chunk-OWCQV42D.js';
4
+ import './chunk-S4BXAGAW.js';
5
+ import './chunk-YV4I45SK.js';
6
+ import './chunk-7AKBELZA.js';
7
+ import './chunk-AVXQPDQH.js';
8
+ import './chunk-RXOLGHSU.js';
9
+ import './chunk-MEE2ZAKJ.js';
10
10
 
11
11
  /**
12
- * Voodoo.js v0.11.2
12
+ * Voodoo.js v0.12.2
13
13
  * JavaScript feels like magic.
14
14
  * (c) 2026 Voodoo.js contributors. MIT License.
15
15
  */
package/dist/gpu.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  /**
6
- * Voodoo.js v0.11.2
6
+ * Voodoo.js v0.12.2
7
7
  * JavaScript feels like magic.
8
8
  * (c) 2026 Voodoo.js contributors. MIT License.
9
9
  */
package/dist/gpu.js CHANGED
@@ -1,15 +1,15 @@
1
- import { supported, clock, shared, gpu, surface, effect, frameLoop, frame } from './chunk-KQYSJHZR.js';
2
- export { clock, compute, describeWgslType, destroy, effect, findEntry, flattenValue, frame, frameLoop, gpu, inferStruct, init, packStruct, reflectBindings, reflectEntries, reflectStructs, reflectWgsl, resetShared, shared, splitTopLevel, stripWgslComments, supported, surface, target, uniforms, writeField, writeStruct } from './chunk-KQYSJHZR.js';
3
- import { http } from './chunk-DJJB35SR.js';
4
- import { originalAttributes, destroy } from './chunk-H2ZUEQWV.js';
5
- import { handleError } from './chunk-WJP3YGUI.js';
6
- import { warn, describeElement } from './chunk-UV5PMS7P.js';
7
- import './chunk-IGZSDZKU.js';
8
- import { defineDirective } from './chunk-NFDXVIII.js';
9
- import './chunk-IS3DR2KY.js';
1
+ import { supported, clock, shared, gpu, surface, effect, frameLoop, frame } from './chunk-RTZEIVNA.js';
2
+ export { clock, compute, describeWgslType, destroy, effect, findEntry, flattenValue, frame, frameLoop, gpu, inferStruct, init, packStruct, reflectBindings, reflectEntries, reflectStructs, reflectWgsl, resetShared, shared, splitTopLevel, stripWgslComments, supported, surface, target, uniforms, writeField, writeStruct } from './chunk-RTZEIVNA.js';
3
+ import { http } from './chunk-NVANUFWZ.js';
4
+ import { originalAttributes, destroy } from './chunk-OWCQV42D.js';
5
+ import { handleError } from './chunk-S4BXAGAW.js';
6
+ import { warn, describeElement } from './chunk-YV4I45SK.js';
7
+ import './chunk-7AKBELZA.js';
8
+ import { defineDirective } from './chunk-RXOLGHSU.js';
9
+ import './chunk-MEE2ZAKJ.js';
10
10
 
11
11
  /**
12
- * Voodoo.js v0.11.2
12
+ * Voodoo.js v0.12.2
13
13
  * JavaScript feels like magic.
14
14
  * (c) 2026 Voodoo.js contributors. MIT License.
15
15
  */