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
package/dist/voodoo.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Voodoo.js v0.11.2
2
+ * Voodoo.js v0.12.2
3
3
  * JavaScript feels like magic.
4
4
  * (c) 2026 Voodoo.js contributors. MIT License.
5
5
  */
@@ -1798,6 +1798,23 @@ ${pointer}`);
1798
1798
  "SharedWorker",
1799
1799
  "ServiceWorker"
1800
1800
  ]);
1801
+ function guardedTimer(name) {
1802
+ return function(handler, timeout, ...rest) {
1803
+ if (typeof handler !== "function") {
1804
+ throw new VoodooRuntimeError(
1805
+ `${name} needs a function. Passing a string would compile it, which this library never does.`
1806
+ );
1807
+ }
1808
+ const timer = globalThis[name];
1809
+ return timer(handler, timeout, ...rest);
1810
+ };
1811
+ }
1812
+ function forwardGlobal(name) {
1813
+ return function(...args) {
1814
+ const fn = globalThis[name];
1815
+ return fn(...args);
1816
+ };
1817
+ }
1801
1818
  var allowedGlobals = {
1802
1819
  Math,
1803
1820
  JSON,
@@ -1816,7 +1833,17 @@ ${pointer}`);
1816
1833
  isFinite,
1817
1834
  encodeURIComponent,
1818
1835
  decodeURIComponent,
1819
- console
1836
+ console,
1837
+ ...typeof setTimeout !== "undefined" ? {
1838
+ setTimeout: guardedTimer("setTimeout"),
1839
+ setInterval: guardedTimer("setInterval"),
1840
+ clearTimeout: forwardGlobal("clearTimeout"),
1841
+ clearInterval: forwardGlobal("clearInterval")
1842
+ } : {},
1843
+ ...typeof requestAnimationFrame !== "undefined" ? {
1844
+ requestAnimationFrame: forwardGlobal("requestAnimationFrame"),
1845
+ cancelAnimationFrame: forwardGlobal("cancelAnimationFrame")
1846
+ } : {}
1820
1847
  };
1821
1848
  var VoodooRuntimeError = class extends Error {
1822
1849
  constructor(message, expression) {
@@ -2263,6 +2290,10 @@ Expression: ${expression}` : message);
2263
2290
  function magic(name, getter) {
2264
2291
  magics.set(name.startsWith("$") ? name : `$${name}`, getter);
2265
2292
  }
2293
+ var hooks = /* @__PURE__ */ new Map();
2294
+ function hook(name, getter) {
2295
+ hooks.set(name, getter);
2296
+ }
2266
2297
  var Scope = class _Scope {
2267
2298
  // Assignment order matches the order the fields were declared in before, so
2268
2299
  // the properties are created in the same sequence they always were.
@@ -2318,7 +2349,10 @@ Expression: ${expression}` : message);
2318
2349
  s = s.parent;
2319
2350
  }
2320
2351
  if (name.charCodeAt(0) === 36 && magics.has(name)) {
2321
- return this.magicContainer(name);
2352
+ return this.magicContainer(name, magics);
2353
+ }
2354
+ if (hooks.has(name)) {
2355
+ return this.magicContainer(name, hooks);
2322
2356
  }
2323
2357
  return void 0;
2324
2358
  }
@@ -2347,11 +2381,11 @@ Expression: ${expression}` : message);
2347
2381
  reactiveChild(vars, el = null) {
2348
2382
  return new _Scope(reactive(vars), this, el != null ? el : this.el);
2349
2383
  }
2350
- magicContainer(name) {
2384
+ magicContainer(name, registry) {
2351
2385
  if (!this.magicCache) this.magicCache = /* @__PURE__ */ new Map();
2352
2386
  const cached = this.magicCache.get(name);
2353
2387
  if (cached) return cached;
2354
- const getter = magics.get(name);
2388
+ const getter = registry.get(name);
2355
2389
  const scope = this;
2356
2390
  const container2 = {};
2357
2391
  Object.defineProperty(container2, name, {
@@ -2744,7 +2778,16 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2744
2778
  },
2745
2779
  effect(fn) {
2746
2780
  const owner = ownerScope();
2747
- owner.run(() => effect(fn, { scope: owner }));
2781
+ const hosted = () => {
2782
+ const previous = hookHost;
2783
+ hookHost = el;
2784
+ try {
2785
+ fn();
2786
+ } finally {
2787
+ hookHost = previous;
2788
+ }
2789
+ };
2790
+ owner.run(() => effect(hosted, { scope: owner }));
2748
2791
  },
2749
2792
  cleanup(fn) {
2750
2793
  addCleanup(el, fn);
@@ -2792,6 +2835,56 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2792
2835
  return;
2793
2836
  }
2794
2837
  initialized.add(el);
2838
+ const previousHost = hookHost;
2839
+ hookHost = el;
2840
+ try {
2841
+ walkElementDirectives(el, attrs, tagComponent, current2);
2842
+ } finally {
2843
+ hookHost = previousHost;
2844
+ }
2845
+ }
2846
+ var hookHost = null;
2847
+ function currentHookHost() {
2848
+ return hookHost;
2849
+ }
2850
+ function createDataScope(expression, parent, el) {
2851
+ let ast = null;
2852
+ try {
2853
+ ast = parse(expression);
2854
+ } catch (e) {
2855
+ ast = null;
2856
+ }
2857
+ const plain = ast && ast.t === "obj" && ast.props.every(
2858
+ (p2) => !p2.spread && !p2.keyExpr
2859
+ );
2860
+ if (!plain) {
2861
+ const raw = evaluateIn(expression, parent, "v-data");
2862
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2863
+ }
2864
+ const scope = parent.reactiveChild({}, el);
2865
+ const props = ast.props;
2866
+ for (const prop of props) {
2867
+ if (prop.key === null) continue;
2868
+ try {
2869
+ if (prop.getter) {
2870
+ const compute = evaluate(prop.value, scope);
2871
+ Object.defineProperty(scope.data, prop.key, {
2872
+ enumerable: true,
2873
+ configurable: true,
2874
+ get: () => compute.call(scope.data)
2875
+ });
2876
+ } else {
2877
+ scope.data[prop.key] = evaluate(prop.value, scope);
2878
+ }
2879
+ } catch (err) {
2880
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
2881
+ else handleError(err, "v-data");
2882
+ }
2883
+ }
2884
+ return scope;
2885
+ }
2886
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
2887
+ let current2 = scope;
2795
2888
  for (const attr2 of attrs) {
2796
2889
  const def = directives.get(attr2.name);
2797
2890
  if (def == null ? void 0 : def.terminal) {
@@ -2811,11 +2904,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2811
2904
  nodeScopes.set(el, current2);
2812
2905
  }
2813
2906
  } else if (dataAttr || componentAttr) {
2814
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current2, "v-data") : {};
2815
- current2 = current2.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2907
+ current2 = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current2, el);
2816
2908
  nodeScopes.set(el, current2);
2817
2909
  }
2818
- const attributeScope = mountedComponent ? activeScope2 : current2;
2910
+ const attributeScope = mountedComponent ? scope : current2;
2819
2911
  for (const attr2 of attrs) {
2820
2912
  if (attr2.name === "data" || attr2.name === "component") continue;
2821
2913
  runDirective(el, attr2, attributeScope);
@@ -2972,9 +3064,9 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2972
3064
  var observer = null;
2973
3065
  var startHooks = [];
2974
3066
  function runPhase(target, after) {
2975
- for (const hook of startHooks) {
3067
+ for (const hook2 of startHooks) {
2976
3068
  try {
2977
- hook(target, after);
3069
+ hook2(target, after);
2978
3070
  } catch (error) {
2979
3071
  console.error("[Voodoo] a start hook failed", error);
2980
3072
  }
@@ -3358,10 +3450,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
3358
3450
  "destroyed"
3359
3451
  ]);
3360
3452
  function callHook(def, instance, name) {
3361
- const hook = def[name];
3362
- if (typeof hook !== "function") return;
3453
+ const hook2 = def[name];
3454
+ if (typeof hook2 !== "function") return;
3363
3455
  try {
3364
- hook.call(instance);
3456
+ hook2.call(instance);
3365
3457
  } catch (err) {
3366
3458
  handleError(err, `hook ${name}`);
3367
3459
  }
@@ -4820,6 +4912,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4820
4912
  );
4821
4913
 
4822
4914
  // src/storage/index.ts
4915
+ init_reactivity();
4823
4916
  function createStorage(getStore, prefix = "") {
4824
4917
  const full = (key) => prefix + key;
4825
4918
  return {
@@ -4995,22 +5088,29 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4995
5088
  };
4996
5089
  var THEME_KEY = "voodoo:theme";
4997
5090
  var picked = null;
5091
+ var revision = ref(0);
4998
5092
  var theme = {
4999
5093
  /** Theme chosen by the user, or `system` when never set. */
5000
5094
  get current() {
5001
5095
  var _a2, _b;
5096
+ void revision.value;
5002
5097
  return (_b = (_a2 = storage.get(THEME_KEY)) != null ? _a2 : picked) != null ? _b : "system";
5003
5098
  },
5004
5099
  /** Theme effectively applied, resolving `system`. */
5005
5100
  get resolved() {
5006
5101
  const value = this.current;
5007
5102
  if (value !== "system") return value;
5103
+ if (typeof document !== "undefined") {
5104
+ const authored = document.documentElement.getAttribute("data-theme");
5105
+ if (authored === "light" || authored === "dark") return authored;
5106
+ }
5008
5107
  if (typeof matchMedia === "undefined") return "light";
5009
5108
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
5010
5109
  },
5011
5110
  set(value) {
5012
5111
  picked = value;
5013
5112
  storage.set(THEME_KEY, value);
5113
+ revision.value++;
5014
5114
  this.apply();
5015
5115
  },
5016
5116
  toggle() {
@@ -5044,8 +5144,17 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5044
5144
  init() {
5045
5145
  if (typeof document === "undefined") return;
5046
5146
  this.apply();
5147
+ if (typeof MutationObserver !== "undefined") {
5148
+ new MutationObserver(() => {
5149
+ revision.value++;
5150
+ }).observe(document.documentElement, {
5151
+ attributes: true,
5152
+ attributeFilter: ["data-theme"]
5153
+ });
5154
+ }
5047
5155
  if (typeof matchMedia === "undefined") return;
5048
5156
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
5157
+ revision.value++;
5049
5158
  if (this.current === "system") this.apply();
5050
5159
  });
5051
5160
  }
@@ -5189,6 +5298,141 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5189
5298
  );
5190
5299
  }
5191
5300
 
5301
+ // src/hooks/index.ts
5302
+ init_reactivity();
5303
+ var tables = /* @__PURE__ */ new WeakMap();
5304
+ function tableFor(scope) {
5305
+ var _a2, _b, _c;
5306
+ const host = (_b = (_a2 = currentHookHost()) != null ? _a2 : scope.el) != null ? _b : scope;
5307
+ let table = tables.get(host);
5308
+ if (!table) {
5309
+ table = { slots: [], index: 0, scheduled: false, bound: false };
5310
+ tables.set(host, table);
5311
+ }
5312
+ const el = (_c = currentHookHost()) != null ? _c : scope.el;
5313
+ if (!table.bound && el) {
5314
+ table.bound = true;
5315
+ const owned = table;
5316
+ addCleanup(el, () => {
5317
+ for (const slot of owned.slots) {
5318
+ if (slot.dispose) slot.dispose();
5319
+ if (slot.runner) stop(slot.runner);
5320
+ }
5321
+ owned.slots.length = 0;
5322
+ });
5323
+ }
5324
+ if (!table.scheduled) {
5325
+ table.scheduled = true;
5326
+ const pending = table;
5327
+ queueMicrotask(() => {
5328
+ pending.index = 0;
5329
+ pending.scheduled = false;
5330
+ });
5331
+ }
5332
+ return table;
5333
+ }
5334
+ function nextSlot(scope, kind) {
5335
+ const table = tableFor(scope);
5336
+ const at = table.index++;
5337
+ let slot = table.slots[at];
5338
+ if (!slot || slot.kind !== kind) {
5339
+ if (slot) {
5340
+ if (slot.dispose) slot.dispose();
5341
+ if (slot.runner) stop(slot.runner);
5342
+ }
5343
+ slot = { kind, deps: null, value: void 0 };
5344
+ table.slots[at] = slot;
5345
+ }
5346
+ return slot;
5347
+ }
5348
+ function depsChanged(previous, next) {
5349
+ if (!previous || !next) return true;
5350
+ if (previous.length !== next.length) return true;
5351
+ for (let i = 0; i < next.length; i++) {
5352
+ if (!Object.is(previous[i], next[i])) return true;
5353
+ }
5354
+ return false;
5355
+ }
5356
+ function normalizeDeps(deps) {
5357
+ return Array.isArray(deps) ? deps.slice() : null;
5358
+ }
5359
+ function useState(scope, initial) {
5360
+ const slot = nextSlot(scope, "state");
5361
+ if (slot.value === void 0) slot.value = ref(initial);
5362
+ return slot.value;
5363
+ }
5364
+ function useEffect(scope, fn, deps) {
5365
+ const slot = nextSlot(scope, "effect");
5366
+ const next = normalizeDeps(deps);
5367
+ const runCleanup = () => {
5368
+ if (slot.dispose) {
5369
+ const dispose = slot.dispose;
5370
+ slot.dispose = void 0;
5371
+ dispose();
5372
+ }
5373
+ };
5374
+ if (next) {
5375
+ const first = slot.deps === null && !slot.runner;
5376
+ if (first || depsChanged(slot.deps, next)) {
5377
+ slot.deps = next;
5378
+ runCleanup();
5379
+ const result = fn();
5380
+ if (typeof result === "function") slot.dispose = result;
5381
+ }
5382
+ return;
5383
+ }
5384
+ if (slot.runner) return;
5385
+ slot.deps = null;
5386
+ slot.runner = effect(() => {
5387
+ runCleanup();
5388
+ const result = fn();
5389
+ if (typeof result === "function") slot.dispose = result;
5390
+ });
5391
+ }
5392
+ function useMemo(scope, fn, deps) {
5393
+ const slot = nextSlot(scope, "memo");
5394
+ const next = normalizeDeps(deps);
5395
+ if (!next) {
5396
+ if (!slot.value) slot.value = computed(fn);
5397
+ return slot.value;
5398
+ }
5399
+ if (!slot.value) {
5400
+ slot.value = ref(fn());
5401
+ slot.deps = next;
5402
+ } else if (depsChanged(slot.deps, next)) {
5403
+ slot.deps = next;
5404
+ slot.value.value = fn();
5405
+ }
5406
+ return slot.value;
5407
+ }
5408
+ function useRef(scope, initial) {
5409
+ const slot = nextSlot(scope, "ref");
5410
+ if (!slot.value) slot.value = markRaw({ current: initial });
5411
+ return slot.value;
5412
+ }
5413
+ function useContext(name, initial) {
5414
+ if (initial !== void 0 && !(name in allStores)) {
5415
+ store(name, initial);
5416
+ }
5417
+ return allStores[name];
5418
+ }
5419
+ function installHooks() {
5420
+ hook("useState", (scope) => (initial) => useState(scope, initial));
5421
+ hook(
5422
+ "useEffect",
5423
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
5424
+ );
5425
+ hook(
5426
+ "useMemo",
5427
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
5428
+ );
5429
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
5430
+ hook(
5431
+ "useContext",
5432
+ () => (name, initial) => useContext(name, initial)
5433
+ );
5434
+ }
5435
+
5192
5436
  // src/http/resource.ts
5193
5437
  init_reactivity();
5194
5438
  function pick(value, path) {
@@ -6793,6 +7037,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6793
7037
  setScopeMarker(markNodeScope);
6794
7038
  setDirectiveRegistrar(directive);
6795
7039
  installMagics();
7040
+ installHooks();
6796
7041
  var eventBus = /* @__PURE__ */ new Map();
6797
7042
  function on(name, handler) {
6798
7043
  let set2 = eventBus.get(name);
@@ -6828,7 +7073,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6828
7073
  }
6829
7074
  function directive(name, definition) {
6830
7075
  var _a2, _b;
6831
- const hooks = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
7076
+ const hooks2 = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6832
7077
  defineDirective(
6833
7078
  name,
6834
7079
  (ctx) => {
@@ -6848,38 +7093,38 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6848
7093
  instance: (_b3 = (_a4 = ctx.scope.owner) == null ? void 0 : _a4.component) != null ? _b3 : null
6849
7094
  };
6850
7095
  };
6851
- const initial = hooks.raw ? ctx.expression : ctx.evaluate();
6852
- (_a3 = hooks.created) == null ? void 0 : _a3.call(hooks, ctx.el, makeBinding(initial));
6853
- (_b2 = hooks.beforeMount) == null ? void 0 : _b2.call(hooks, ctx.el, makeBinding(initial));
7096
+ const initial = hooks2.raw ? ctx.expression : ctx.evaluate();
7097
+ (_a3 = hooks2.created) == null ? void 0 : _a3.call(hooks2, ctx.el, makeBinding(initial));
7098
+ (_b2 = hooks2.beforeMount) == null ? void 0 : _b2.call(hooks2, ctx.el, makeBinding(initial));
6854
7099
  ctx.effect(() => {
6855
7100
  var _a4, _b3;
6856
- const value = hooks.raw ? ctx.expression : ctx.evaluate();
7101
+ const value = hooks2.raw ? ctx.expression : ctx.evaluate();
6857
7102
  if (!mounted) {
6858
7103
  mounted = true;
6859
7104
  oldValue = value;
6860
- (_a4 = hooks.mounted) == null ? void 0 : _a4.call(hooks, ctx.el, makeBinding(value));
7105
+ (_a4 = hooks2.mounted) == null ? void 0 : _a4.call(hooks2, ctx.el, makeBinding(value));
6861
7106
  return;
6862
7107
  }
6863
7108
  if (value === oldValue) return;
6864
7109
  const binding = makeBinding(value);
6865
- (_b3 = hooks.updated) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7110
+ (_b3 = hooks2.updated) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6866
7111
  oldValue = value;
6867
7112
  });
6868
7113
  ctx.cleanup(() => {
6869
7114
  var _a4, _b3;
6870
7115
  const binding = makeBinding(oldValue);
6871
- (_a4 = hooks.beforeUnmount) == null ? void 0 : _a4.call(hooks, ctx.el, binding);
6872
- (_b3 = hooks.unmounted) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7116
+ (_a4 = hooks2.beforeUnmount) == null ? void 0 : _a4.call(hooks2, ctx.el, binding);
7117
+ (_b3 = hooks2.unmounted) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6873
7118
  });
6874
7119
  },
6875
- { priority: (_a2 = hooks.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks.terminal) != null ? _b : false }
7120
+ { priority: (_a2 = hooks2.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks2.terminal) != null ? _b : false }
6876
7121
  );
6877
7122
  }
6878
7123
  function data(values) {
6879
7124
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
6880
7125
  return rootScope.data;
6881
7126
  }
6882
- var version2 = "0.11.2";
7127
+ var version2 = "0.12.2";
6883
7128
  var core = {
6884
7129
  // Utilities first: Voodoo's own names can override.
6885
7130
  ...utils_exports,
@@ -9098,6 +9343,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
9098
9343
  ensureUi();
9099
9344
  collapseOf(el);
9100
9345
  });
9346
+ var selfToggling = /* @__PURE__ */ new WeakSet();
9101
9347
  defineDirective("collapse-toggle", ({ el, expression, cleanup }) => {
9102
9348
  ensureUi();
9103
9349
  const target = resolveTarget(el, expression);
@@ -9106,6 +9352,8 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
9106
9352
  controller.triggers.add(el);
9107
9353
  controller.sync();
9108
9354
  makeInteractive(el, cleanup);
9355
+ selfToggling.add(el);
9356
+ cleanup(() => selfToggling.delete(el));
9109
9357
  const onClick = (event) => {
9110
9358
  event.preventDefault();
9111
9359
  controller.toggle();
@@ -9520,10 +9768,11 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
9520
9768
  if (index === -1) return;
9521
9769
  event.preventDefault();
9522
9770
  const controller = controllers2[index];
9523
- if (single && !controller.open) {
9771
+ const ownToggle = selfToggling.has(header);
9772
+ if (single && (ownToggle ? controller.open : !controller.open)) {
9524
9773
  for (const other of controllers2) if (other !== controller) other.hide();
9525
9774
  }
9526
- controller.toggle();
9775
+ if (!ownToggle) controller.toggle();
9527
9776
  };
9528
9777
  const onKeyDown = (event) => {
9529
9778
  var _a2;
@@ -12401,12 +12650,15 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12401
12650
 
12402
12651
  // src/sound/index.ts
12403
12652
  init_registry();
12653
+ init_reactivity();
12654
+ init_style();
12404
12655
  var audioContext = null;
12405
12656
  var masterVolume = 0.35;
12406
12657
  var isMuted = false;
12407
12658
  var hasLoadedPreference = false;
12408
12659
  var VOLUME_KEY = "voodoo:sound:volume";
12409
12660
  var MUTE_KEY = "voodoo:sound:muted";
12661
+ var muteRevision = ref(0);
12410
12662
  function loadPreference() {
12411
12663
  if (hasLoadedPreference) return;
12412
12664
  hasLoadedPreference = true;
@@ -12722,6 +12974,7 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12722
12974
  loadPreference();
12723
12975
  isMuted = value;
12724
12976
  storage.set(MUTE_KEY, isMuted);
12977
+ muteRevision.value++;
12725
12978
  },
12726
12979
  /** Unmutes sound. */
12727
12980
  unmute() {
@@ -12733,8 +12986,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12733
12986
  this.mute(!isMuted);
12734
12987
  return isMuted;
12735
12988
  },
12736
- /** `true` when muted. */
12989
+ /**
12990
+ * `true` when muted.
12991
+ *
12992
+ * Reads a revision ref first, so that an expression asking whether sound is
12993
+ * muted actually re-runs when it changes. The state lives in a module
12994
+ * variable and in localStorage, both invisible to the Proxy, so
12995
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
12996
+ * no way to show whether it was muted, which made the mute button look like
12997
+ * it did nothing at all.
12998
+ */
12737
12999
  get muted() {
13000
+ void muteRevision.value;
12738
13001
  loadPreference();
12739
13002
  return isMuted;
12740
13003
  },
@@ -12785,11 +13048,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12785
13048
  cleanup(() => el.removeEventListener(event, play));
12786
13049
  void scope;
12787
13050
  });
13051
+ var MUTE_CSS = `
13052
+ .v-muted{opacity:.55}
13053
+ .v-muted::after{content:" \\1F507";font-size:.9em}
13054
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
13055
+ `;
12788
13056
  defineDirective("mute", ({ el, cleanup }) => {
13057
+ injectStyle("mute", MUTE_CSS);
12789
13058
  const sync = () => {
12790
13059
  const isMuted2 = sound.muted;
12791
13060
  el.setAttribute("aria-pressed", String(isMuted2));
12792
13061
  el.classList.toggle("v-muted", isMuted2);
13062
+ el.classList.toggle("v-mute-on", !isMuted2);
12793
13063
  };
12794
13064
  const toggle = () => {
12795
13065
  sound.toggle();
@@ -14441,9 +14711,14 @@ textarea.v-dialog-input{min-height:96px;resize:vertical}
14441
14711
  allowedGlobals.V = V2;
14442
14712
  allowedGlobals.Voodoo = V2;
14443
14713
  if (config.baseURL && ((_a2 = V2.http) == null ? void 0 : _a2.setBaseURL)) V2.http.setBaseURL(config.baseURL);
14444
- if (options.manual || !config.autoStart) return;
14714
+ theme.init();
14715
+ if (options.manual || !config.autoStart) {
14716
+ whenReady(() => {
14717
+ applySavedPalette();
14718
+ });
14719
+ return;
14720
+ }
14445
14721
  const boot = () => {
14446
- theme.init();
14447
14722
  applySavedPalette();
14448
14723
  V2.start();
14449
14724
  if (typeof V2.enableXrayShortcut === "function") V2.enableXrayShortcut();