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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Voodoo.js v0.11.1
2
+ * Voodoo.js v0.12.1
3
3
  * JavaScript feels like magic.
4
4
  * (c) 2026 Voodoo.js contributors. MIT License.
5
5
  */
@@ -1649,9 +1649,17 @@ ${pointer}`);
1649
1649
  if (t2.type === "punct") {
1650
1650
  if (t2.value === "(") {
1651
1651
  this.next();
1652
- const expr = this.parseExpression();
1652
+ const body = [this.parseExpression()];
1653
+ while (this.isPunct(",")) {
1654
+ this.next();
1655
+ if (this.isPunct(")")) {
1656
+ const stray = this.peek();
1657
+ throw new VoodooSyntaxError("Trailing comma in a group", this.source, stray.start);
1658
+ }
1659
+ body.push(this.parseExpression());
1660
+ }
1653
1661
  this.expect(")");
1654
- return expr;
1662
+ return body.length === 1 ? body[0] : { t: "seq", body };
1655
1663
  }
1656
1664
  if (t2.value === "[") return this.parseArrayLiteral();
1657
1665
  if (t2.value === "{") return this.parseObjectLiteral();
@@ -2255,6 +2263,10 @@ Expression: ${expression}` : message);
2255
2263
  function magic(name, getter) {
2256
2264
  magics.set(name.startsWith("$") ? name : `$${name}`, getter);
2257
2265
  }
2266
+ var hooks = /* @__PURE__ */ new Map();
2267
+ function hook(name, getter) {
2268
+ hooks.set(name, getter);
2269
+ }
2258
2270
  var Scope = class _Scope {
2259
2271
  // Assignment order matches the order the fields were declared in before, so
2260
2272
  // the properties are created in the same sequence they always were.
@@ -2310,7 +2322,10 @@ Expression: ${expression}` : message);
2310
2322
  s = s.parent;
2311
2323
  }
2312
2324
  if (name.charCodeAt(0) === 36 && magics.has(name)) {
2313
- return this.magicContainer(name);
2325
+ return this.magicContainer(name, magics);
2326
+ }
2327
+ if (hooks.has(name)) {
2328
+ return this.magicContainer(name, hooks);
2314
2329
  }
2315
2330
  return void 0;
2316
2331
  }
@@ -2339,11 +2354,11 @@ Expression: ${expression}` : message);
2339
2354
  reactiveChild(vars, el = null) {
2340
2355
  return new _Scope(reactive(vars), this, el != null ? el : this.el);
2341
2356
  }
2342
- magicContainer(name) {
2357
+ magicContainer(name, registry) {
2343
2358
  if (!this.magicCache) this.magicCache = /* @__PURE__ */ new Map();
2344
2359
  const cached = this.magicCache.get(name);
2345
2360
  if (cached) return cached;
2346
- const getter = magics.get(name);
2361
+ const getter = registry.get(name);
2347
2362
  const scope = this;
2348
2363
  const container2 = {};
2349
2364
  Object.defineProperty(container2, name, {
@@ -2745,7 +2760,16 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2745
2760
  },
2746
2761
  effect(fn) {
2747
2762
  const owner = ownerScope();
2748
- owner.run(() => effect(fn, { scope: owner }));
2763
+ const hosted = () => {
2764
+ const previous = hookHost;
2765
+ hookHost = el;
2766
+ try {
2767
+ fn();
2768
+ } finally {
2769
+ hookHost = previous;
2770
+ }
2771
+ };
2772
+ owner.run(() => effect(hosted, { scope: owner }));
2749
2773
  },
2750
2774
  cleanup(fn) {
2751
2775
  addCleanup(el, fn);
@@ -2793,6 +2817,56 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2793
2817
  return;
2794
2818
  }
2795
2819
  initialized.add(el);
2820
+ const previousHost = hookHost;
2821
+ hookHost = el;
2822
+ try {
2823
+ walkElementDirectives(el, attrs, tagComponent, current2);
2824
+ } finally {
2825
+ hookHost = previousHost;
2826
+ }
2827
+ }
2828
+ var hookHost = null;
2829
+ function currentHookHost() {
2830
+ return hookHost;
2831
+ }
2832
+ function createDataScope(expression, parent, el) {
2833
+ let ast = null;
2834
+ try {
2835
+ ast = parse(expression);
2836
+ } catch (e) {
2837
+ ast = null;
2838
+ }
2839
+ const plain = ast && ast.t === "obj" && ast.props.every(
2840
+ (p2) => !p2.spread && !p2.keyExpr
2841
+ );
2842
+ if (!plain) {
2843
+ const raw = evaluateIn(expression, parent, "v-data");
2844
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2845
+ }
2846
+ const scope = parent.reactiveChild({}, el);
2847
+ const props = ast.props;
2848
+ for (const prop of props) {
2849
+ if (prop.key === null) continue;
2850
+ try {
2851
+ if (prop.getter) {
2852
+ const compute = evaluate(prop.value, scope);
2853
+ Object.defineProperty(scope.data, prop.key, {
2854
+ enumerable: true,
2855
+ configurable: true,
2856
+ get: () => compute.call(scope.data)
2857
+ });
2858
+ } else {
2859
+ scope.data[prop.key] = evaluate(prop.value, scope);
2860
+ }
2861
+ } catch (err) {
2862
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
2863
+ else handleError(err, "v-data");
2864
+ }
2865
+ }
2866
+ return scope;
2867
+ }
2868
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
2869
+ let current2 = scope;
2796
2870
  for (const attr2 of attrs) {
2797
2871
  const def = directives.get(attr2.name);
2798
2872
  if (def == null ? void 0 : def.terminal) {
@@ -2812,11 +2886,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2812
2886
  nodeScopes.set(el, current2);
2813
2887
  }
2814
2888
  } else if (dataAttr || componentAttr) {
2815
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current2, "v-data") : {};
2816
- current2 = current2.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2889
+ current2 = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current2, el);
2817
2890
  nodeScopes.set(el, current2);
2818
2891
  }
2819
- const attributeScope = mountedComponent ? activeScope2 : current2;
2892
+ const attributeScope = mountedComponent ? scope : current2;
2820
2893
  for (const attr2 of attrs) {
2821
2894
  if (attr2.name === "data" || attr2.name === "component") continue;
2822
2895
  runDirective(el, attr2, attributeScope);
@@ -2972,13 +3045,13 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2972
3045
  var started = false;
2973
3046
  var observer = null;
2974
3047
  var startHooks = [];
2975
- function onStart(hook) {
2976
- startHooks.push(hook);
3048
+ function onStart(hook2) {
3049
+ startHooks.push(hook2);
2977
3050
  }
2978
3051
  function runPhase(target, after) {
2979
- for (const hook of startHooks) {
3052
+ for (const hook2 of startHooks) {
2980
3053
  try {
2981
- hook(target, after);
3054
+ hook2(target, after);
2982
3055
  } catch (error) {
2983
3056
  console.error("[Voodoo] a start hook failed", error);
2984
3057
  }
@@ -3362,10 +3435,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
3362
3435
  "destroyed"
3363
3436
  ]);
3364
3437
  function callHook(def, instance, name) {
3365
- const hook = def[name];
3366
- if (typeof hook !== "function") return;
3438
+ const hook2 = def[name];
3439
+ if (typeof hook2 !== "function") return;
3367
3440
  try {
3368
- hook.call(instance);
3441
+ hook2.call(instance);
3369
3442
  } catch (err) {
3370
3443
  handleError(err, `hook ${name}`);
3371
3444
  }
@@ -4824,6 +4897,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4824
4897
  );
4825
4898
 
4826
4899
  // src/storage/index.ts
4900
+ init_reactivity();
4827
4901
  function createStorage(getStore, prefix = "") {
4828
4902
  const full = (key) => prefix + key;
4829
4903
  return {
@@ -4999,22 +5073,29 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4999
5073
  };
5000
5074
  var THEME_KEY = "voodoo:theme";
5001
5075
  var picked = null;
5076
+ var revision = ref(0);
5002
5077
  var theme = {
5003
5078
  /** Theme chosen by the user, or `system` when never set. */
5004
5079
  get current() {
5005
5080
  var _a2, _b;
5081
+ void revision.value;
5006
5082
  return (_b = (_a2 = storage.get(THEME_KEY)) != null ? _a2 : picked) != null ? _b : "system";
5007
5083
  },
5008
5084
  /** Theme effectively applied, resolving `system`. */
5009
5085
  get resolved() {
5010
5086
  const value = this.current;
5011
5087
  if (value !== "system") return value;
5088
+ if (typeof document !== "undefined") {
5089
+ const authored = document.documentElement.getAttribute("data-theme");
5090
+ if (authored === "light" || authored === "dark") return authored;
5091
+ }
5012
5092
  if (typeof matchMedia === "undefined") return "light";
5013
5093
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
5014
5094
  },
5015
5095
  set(value) {
5016
5096
  picked = value;
5017
5097
  storage.set(THEME_KEY, value);
5098
+ revision.value++;
5018
5099
  this.apply();
5019
5100
  },
5020
5101
  toggle() {
@@ -5048,8 +5129,17 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5048
5129
  init() {
5049
5130
  if (typeof document === "undefined") return;
5050
5131
  this.apply();
5132
+ if (typeof MutationObserver !== "undefined") {
5133
+ new MutationObserver(() => {
5134
+ revision.value++;
5135
+ }).observe(document.documentElement, {
5136
+ attributes: true,
5137
+ attributeFilter: ["data-theme"]
5138
+ });
5139
+ }
5051
5140
  if (typeof matchMedia === "undefined") return;
5052
5141
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
5142
+ revision.value++;
5053
5143
  if (this.current === "system") this.apply();
5054
5144
  });
5055
5145
  }
@@ -5193,6 +5283,141 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5193
5283
  );
5194
5284
  }
5195
5285
 
5286
+ // src/hooks/index.ts
5287
+ init_reactivity();
5288
+ var tables = /* @__PURE__ */ new WeakMap();
5289
+ function tableFor(scope) {
5290
+ var _a2, _b, _c;
5291
+ const host = (_b = (_a2 = currentHookHost()) != null ? _a2 : scope.el) != null ? _b : scope;
5292
+ let table = tables.get(host);
5293
+ if (!table) {
5294
+ table = { slots: [], index: 0, scheduled: false, bound: false };
5295
+ tables.set(host, table);
5296
+ }
5297
+ const el = (_c = currentHookHost()) != null ? _c : scope.el;
5298
+ if (!table.bound && el) {
5299
+ table.bound = true;
5300
+ const owned = table;
5301
+ addCleanup(el, () => {
5302
+ for (const slot of owned.slots) {
5303
+ if (slot.dispose) slot.dispose();
5304
+ if (slot.runner) stop(slot.runner);
5305
+ }
5306
+ owned.slots.length = 0;
5307
+ });
5308
+ }
5309
+ if (!table.scheduled) {
5310
+ table.scheduled = true;
5311
+ const pending2 = table;
5312
+ queueMicrotask(() => {
5313
+ pending2.index = 0;
5314
+ pending2.scheduled = false;
5315
+ });
5316
+ }
5317
+ return table;
5318
+ }
5319
+ function nextSlot(scope, kind) {
5320
+ const table = tableFor(scope);
5321
+ const at = table.index++;
5322
+ let slot = table.slots[at];
5323
+ if (!slot || slot.kind !== kind) {
5324
+ if (slot) {
5325
+ if (slot.dispose) slot.dispose();
5326
+ if (slot.runner) stop(slot.runner);
5327
+ }
5328
+ slot = { kind, deps: null, value: void 0 };
5329
+ table.slots[at] = slot;
5330
+ }
5331
+ return slot;
5332
+ }
5333
+ function depsChanged(previous, next) {
5334
+ if (!previous || !next) return true;
5335
+ if (previous.length !== next.length) return true;
5336
+ for (let i = 0; i < next.length; i++) {
5337
+ if (!Object.is(previous[i], next[i])) return true;
5338
+ }
5339
+ return false;
5340
+ }
5341
+ function normalizeDeps(deps) {
5342
+ return Array.isArray(deps) ? deps.slice() : null;
5343
+ }
5344
+ function useState(scope, initial) {
5345
+ const slot = nextSlot(scope, "state");
5346
+ if (slot.value === void 0) slot.value = ref(initial);
5347
+ return slot.value;
5348
+ }
5349
+ function useEffect(scope, fn, deps) {
5350
+ const slot = nextSlot(scope, "effect");
5351
+ const next = normalizeDeps(deps);
5352
+ const runCleanup = () => {
5353
+ if (slot.dispose) {
5354
+ const dispose = slot.dispose;
5355
+ slot.dispose = void 0;
5356
+ dispose();
5357
+ }
5358
+ };
5359
+ if (next) {
5360
+ const first = slot.deps === null && !slot.runner;
5361
+ if (first || depsChanged(slot.deps, next)) {
5362
+ slot.deps = next;
5363
+ runCleanup();
5364
+ const result = fn();
5365
+ if (typeof result === "function") slot.dispose = result;
5366
+ }
5367
+ return;
5368
+ }
5369
+ if (slot.runner) return;
5370
+ slot.deps = null;
5371
+ slot.runner = effect(() => {
5372
+ runCleanup();
5373
+ const result = fn();
5374
+ if (typeof result === "function") slot.dispose = result;
5375
+ });
5376
+ }
5377
+ function useMemo(scope, fn, deps) {
5378
+ const slot = nextSlot(scope, "memo");
5379
+ const next = normalizeDeps(deps);
5380
+ if (!next) {
5381
+ if (!slot.value) slot.value = computed(fn);
5382
+ return slot.value;
5383
+ }
5384
+ if (!slot.value) {
5385
+ slot.value = ref(fn());
5386
+ slot.deps = next;
5387
+ } else if (depsChanged(slot.deps, next)) {
5388
+ slot.deps = next;
5389
+ slot.value.value = fn();
5390
+ }
5391
+ return slot.value;
5392
+ }
5393
+ function useRef(scope, initial) {
5394
+ const slot = nextSlot(scope, "ref");
5395
+ if (!slot.value) slot.value = markRaw({ current: initial });
5396
+ return slot.value;
5397
+ }
5398
+ function useContext(name, initial) {
5399
+ if (initial !== void 0 && !(name in allStores)) {
5400
+ store(name, initial);
5401
+ }
5402
+ return allStores[name];
5403
+ }
5404
+ function installHooks() {
5405
+ hook("useState", (scope) => (initial) => useState(scope, initial));
5406
+ hook(
5407
+ "useEffect",
5408
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
5409
+ );
5410
+ hook(
5411
+ "useMemo",
5412
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
5413
+ );
5414
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
5415
+ hook(
5416
+ "useContext",
5417
+ () => (name, initial) => useContext(name, initial)
5418
+ );
5419
+ }
5420
+
5196
5421
  // src/http/resource.ts
5197
5422
  init_reactivity();
5198
5423
  function pick(value, path) {
@@ -6797,6 +7022,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6797
7022
  setScopeMarker(markNodeScope);
6798
7023
  setDirectiveRegistrar(directive);
6799
7024
  installMagics();
7025
+ installHooks();
6800
7026
  var eventBus = /* @__PURE__ */ new Map();
6801
7027
  function on(name, handler) {
6802
7028
  let set2 = eventBus.get(name);
@@ -6832,7 +7058,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6832
7058
  }
6833
7059
  function directive(name, definition) {
6834
7060
  var _a2, _b;
6835
- const hooks = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
7061
+ const hooks2 = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6836
7062
  defineDirective(
6837
7063
  name,
6838
7064
  (ctx) => {
@@ -6852,38 +7078,38 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6852
7078
  instance: (_b3 = (_a4 = ctx.scope.owner) == null ? void 0 : _a4.component) != null ? _b3 : null
6853
7079
  };
6854
7080
  };
6855
- const initial = hooks.raw ? ctx.expression : ctx.evaluate();
6856
- (_a3 = hooks.created) == null ? void 0 : _a3.call(hooks, ctx.el, makeBinding(initial));
6857
- (_b2 = hooks.beforeMount) == null ? void 0 : _b2.call(hooks, ctx.el, makeBinding(initial));
7081
+ const initial = hooks2.raw ? ctx.expression : ctx.evaluate();
7082
+ (_a3 = hooks2.created) == null ? void 0 : _a3.call(hooks2, ctx.el, makeBinding(initial));
7083
+ (_b2 = hooks2.beforeMount) == null ? void 0 : _b2.call(hooks2, ctx.el, makeBinding(initial));
6858
7084
  ctx.effect(() => {
6859
7085
  var _a4, _b3;
6860
- const value = hooks.raw ? ctx.expression : ctx.evaluate();
7086
+ const value = hooks2.raw ? ctx.expression : ctx.evaluate();
6861
7087
  if (!mounted2) {
6862
7088
  mounted2 = true;
6863
7089
  oldValue = value;
6864
- (_a4 = hooks.mounted) == null ? void 0 : _a4.call(hooks, ctx.el, makeBinding(value));
7090
+ (_a4 = hooks2.mounted) == null ? void 0 : _a4.call(hooks2, ctx.el, makeBinding(value));
6865
7091
  return;
6866
7092
  }
6867
7093
  if (value === oldValue) return;
6868
7094
  const binding = makeBinding(value);
6869
- (_b3 = hooks.updated) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7095
+ (_b3 = hooks2.updated) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6870
7096
  oldValue = value;
6871
7097
  });
6872
7098
  ctx.cleanup(() => {
6873
7099
  var _a4, _b3;
6874
7100
  const binding = makeBinding(oldValue);
6875
- (_a4 = hooks.beforeUnmount) == null ? void 0 : _a4.call(hooks, ctx.el, binding);
6876
- (_b3 = hooks.unmounted) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7101
+ (_a4 = hooks2.beforeUnmount) == null ? void 0 : _a4.call(hooks2, ctx.el, binding);
7102
+ (_b3 = hooks2.unmounted) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6877
7103
  });
6878
7104
  },
6879
- { priority: (_a2 = hooks.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks.terminal) != null ? _b : false }
7105
+ { priority: (_a2 = hooks2.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks2.terminal) != null ? _b : false }
6880
7106
  );
6881
7107
  }
6882
7108
  function data(values) {
6883
7109
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
6884
7110
  return rootScope.data;
6885
7111
  }
6886
- var version2 = "0.11.1";
7112
+ var version2 = "0.12.1";
6887
7113
  var core = {
6888
7114
  // Utilities first: Voodoo's own names can override.
6889
7115
  ...utils_exports,
@@ -13352,12 +13578,15 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
13352
13578
 
13353
13579
  // src/sound/index.ts
13354
13580
  init_registry();
13581
+ init_reactivity();
13582
+ init_style();
13355
13583
  var audioContext = null;
13356
13584
  var masterVolume = 0.35;
13357
13585
  var isMuted = false;
13358
13586
  var hasLoadedPreference = false;
13359
13587
  var VOLUME_KEY = "voodoo:sound:volume";
13360
13588
  var MUTE_KEY = "voodoo:sound:muted";
13589
+ var muteRevision = ref(0);
13361
13590
  function loadPreference() {
13362
13591
  if (hasLoadedPreference) return;
13363
13592
  hasLoadedPreference = true;
@@ -13673,6 +13902,7 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
13673
13902
  loadPreference();
13674
13903
  isMuted = value;
13675
13904
  storage.set(MUTE_KEY, isMuted);
13905
+ muteRevision.value++;
13676
13906
  },
13677
13907
  /** Unmutes sound. */
13678
13908
  unmute() {
@@ -13684,8 +13914,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
13684
13914
  this.mute(!isMuted);
13685
13915
  return isMuted;
13686
13916
  },
13687
- /** `true` when muted. */
13917
+ /**
13918
+ * `true` when muted.
13919
+ *
13920
+ * Reads a revision ref first, so that an expression asking whether sound is
13921
+ * muted actually re-runs when it changes. The state lives in a module
13922
+ * variable and in localStorage, both invisible to the Proxy, so
13923
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
13924
+ * no way to show whether it was muted, which made the mute button look like
13925
+ * it did nothing at all.
13926
+ */
13688
13927
  get muted() {
13928
+ void muteRevision.value;
13689
13929
  loadPreference();
13690
13930
  return isMuted;
13691
13931
  },
@@ -13736,11 +13976,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
13736
13976
  cleanup(() => el.removeEventListener(event, play));
13737
13977
  void scope;
13738
13978
  });
13979
+ var MUTE_CSS = `
13980
+ .v-muted{opacity:.55}
13981
+ .v-muted::after{content:" \\1F507";font-size:.9em}
13982
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
13983
+ `;
13739
13984
  defineDirective("mute", ({ el, cleanup }) => {
13985
+ injectStyle("mute", MUTE_CSS);
13740
13986
  const sync = () => {
13741
13987
  const isMuted2 = sound.muted;
13742
13988
  el.setAttribute("aria-pressed", String(isMuted2));
13743
13989
  el.classList.toggle("v-muted", isMuted2);
13990
+ el.classList.toggle("v-mute-on", !isMuted2);
13744
13991
  };
13745
13992
  const toggle = () => {
13746
13993
  sound.toggle();