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/voodoo.js CHANGED
@@ -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 (t.type === "punct") {
1650
1650
  if (t.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 (t.value === "[") return this.parseArrayLiteral();
1657
1665
  if (t.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, {
@@ -2736,7 +2751,16 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2736
2751
  },
2737
2752
  effect(fn) {
2738
2753
  const owner = ownerScope();
2739
- owner.run(() => effect(fn, { scope: owner }));
2754
+ const hosted = () => {
2755
+ const previous = hookHost;
2756
+ hookHost = el;
2757
+ try {
2758
+ fn();
2759
+ } finally {
2760
+ hookHost = previous;
2761
+ }
2762
+ };
2763
+ owner.run(() => effect(hosted, { scope: owner }));
2740
2764
  },
2741
2765
  cleanup(fn) {
2742
2766
  addCleanup(el, fn);
@@ -2784,6 +2808,56 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2784
2808
  return;
2785
2809
  }
2786
2810
  initialized.add(el);
2811
+ const previousHost = hookHost;
2812
+ hookHost = el;
2813
+ try {
2814
+ walkElementDirectives(el, attrs, tagComponent, current2);
2815
+ } finally {
2816
+ hookHost = previousHost;
2817
+ }
2818
+ }
2819
+ var hookHost = null;
2820
+ function currentHookHost() {
2821
+ return hookHost;
2822
+ }
2823
+ function createDataScope(expression, parent, el) {
2824
+ let ast = null;
2825
+ try {
2826
+ ast = parse(expression);
2827
+ } catch (e) {
2828
+ ast = null;
2829
+ }
2830
+ const plain = ast && ast.t === "obj" && ast.props.every(
2831
+ (p2) => !p2.spread && !p2.keyExpr
2832
+ );
2833
+ if (!plain) {
2834
+ const raw = evaluateIn(expression, parent, "v-data");
2835
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2836
+ }
2837
+ const scope = parent.reactiveChild({}, el);
2838
+ const props = ast.props;
2839
+ for (const prop of props) {
2840
+ if (prop.key === null) continue;
2841
+ try {
2842
+ if (prop.getter) {
2843
+ const compute = evaluate(prop.value, scope);
2844
+ Object.defineProperty(scope.data, prop.key, {
2845
+ enumerable: true,
2846
+ configurable: true,
2847
+ get: () => compute.call(scope.data)
2848
+ });
2849
+ } else {
2850
+ scope.data[prop.key] = evaluate(prop.value, scope);
2851
+ }
2852
+ } catch (err) {
2853
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
2854
+ else handleError(err, "v-data");
2855
+ }
2856
+ }
2857
+ return scope;
2858
+ }
2859
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
2860
+ let current2 = scope;
2787
2861
  for (const attr2 of attrs) {
2788
2862
  const def = directives.get(attr2.name);
2789
2863
  if (def == null ? void 0 : def.terminal) {
@@ -2803,11 +2877,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2803
2877
  nodeScopes.set(el, current2);
2804
2878
  }
2805
2879
  } else if (dataAttr || componentAttr) {
2806
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current2, "v-data") : {};
2807
- current2 = current2.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
2880
+ current2 = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current2, el);
2808
2881
  nodeScopes.set(el, current2);
2809
2882
  }
2810
- const attributeScope = mountedComponent ? activeScope2 : current2;
2883
+ const attributeScope = mountedComponent ? scope : current2;
2811
2884
  for (const attr2 of attrs) {
2812
2885
  if (attr2.name === "data" || attr2.name === "component") continue;
2813
2886
  runDirective(el, attr2, attributeScope);
@@ -2964,9 +3037,9 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
2964
3037
  var observer = null;
2965
3038
  var startHooks = [];
2966
3039
  function runPhase(target, after) {
2967
- for (const hook of startHooks) {
3040
+ for (const hook2 of startHooks) {
2968
3041
  try {
2969
- hook(target, after);
3042
+ hook2(target, after);
2970
3043
  } catch (error) {
2971
3044
  console.error("[Voodoo] a start hook failed", error);
2972
3045
  }
@@ -3350,10 +3423,10 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
3350
3423
  "destroyed"
3351
3424
  ]);
3352
3425
  function callHook(def, instance, name) {
3353
- const hook = def[name];
3354
- if (typeof hook !== "function") return;
3426
+ const hook2 = def[name];
3427
+ if (typeof hook2 !== "function") return;
3355
3428
  try {
3356
- hook.call(instance);
3429
+ hook2.call(instance);
3357
3430
  } catch (err) {
3358
3431
  handleError(err, `hook ${name}`);
3359
3432
  }
@@ -4812,6 +4885,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4812
4885
  );
4813
4886
 
4814
4887
  // src/storage/index.ts
4888
+ init_reactivity();
4815
4889
  function createStorage(getStore, prefix = "") {
4816
4890
  const full = (key) => prefix + key;
4817
4891
  return {
@@ -4987,22 +5061,29 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
4987
5061
  };
4988
5062
  var THEME_KEY = "voodoo:theme";
4989
5063
  var picked = null;
5064
+ var revision = ref(0);
4990
5065
  var theme = {
4991
5066
  /** Theme chosen by the user, or `system` when never set. */
4992
5067
  get current() {
4993
5068
  var _a2, _b;
5069
+ void revision.value;
4994
5070
  return (_b = (_a2 = storage.get(THEME_KEY)) != null ? _a2 : picked) != null ? _b : "system";
4995
5071
  },
4996
5072
  /** Theme effectively applied, resolving `system`. */
4997
5073
  get resolved() {
4998
5074
  const value = this.current;
4999
5075
  if (value !== "system") return value;
5076
+ if (typeof document !== "undefined") {
5077
+ const authored = document.documentElement.getAttribute("data-theme");
5078
+ if (authored === "light" || authored === "dark") return authored;
5079
+ }
5000
5080
  if (typeof matchMedia === "undefined") return "light";
5001
5081
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
5002
5082
  },
5003
5083
  set(value) {
5004
5084
  picked = value;
5005
5085
  storage.set(THEME_KEY, value);
5086
+ revision.value++;
5006
5087
  this.apply();
5007
5088
  },
5008
5089
  toggle() {
@@ -5036,8 +5117,17 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5036
5117
  init() {
5037
5118
  if (typeof document === "undefined") return;
5038
5119
  this.apply();
5120
+ if (typeof MutationObserver !== "undefined") {
5121
+ new MutationObserver(() => {
5122
+ revision.value++;
5123
+ }).observe(document.documentElement, {
5124
+ attributes: true,
5125
+ attributeFilter: ["data-theme"]
5126
+ });
5127
+ }
5039
5128
  if (typeof matchMedia === "undefined") return;
5040
5129
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
5130
+ revision.value++;
5041
5131
  if (this.current === "system") this.apply();
5042
5132
  });
5043
5133
  }
@@ -5181,6 +5271,141 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
5181
5271
  );
5182
5272
  }
5183
5273
 
5274
+ // src/hooks/index.ts
5275
+ init_reactivity();
5276
+ var tables = /* @__PURE__ */ new WeakMap();
5277
+ function tableFor(scope) {
5278
+ var _a2, _b, _c;
5279
+ const host = (_b = (_a2 = currentHookHost()) != null ? _a2 : scope.el) != null ? _b : scope;
5280
+ let table = tables.get(host);
5281
+ if (!table) {
5282
+ table = { slots: [], index: 0, scheduled: false, bound: false };
5283
+ tables.set(host, table);
5284
+ }
5285
+ const el = (_c = currentHookHost()) != null ? _c : scope.el;
5286
+ if (!table.bound && el) {
5287
+ table.bound = true;
5288
+ const owned = table;
5289
+ addCleanup(el, () => {
5290
+ for (const slot of owned.slots) {
5291
+ if (slot.dispose) slot.dispose();
5292
+ if (slot.runner) stop(slot.runner);
5293
+ }
5294
+ owned.slots.length = 0;
5295
+ });
5296
+ }
5297
+ if (!table.scheduled) {
5298
+ table.scheduled = true;
5299
+ const pending = table;
5300
+ queueMicrotask(() => {
5301
+ pending.index = 0;
5302
+ pending.scheduled = false;
5303
+ });
5304
+ }
5305
+ return table;
5306
+ }
5307
+ function nextSlot(scope, kind) {
5308
+ const table = tableFor(scope);
5309
+ const at = table.index++;
5310
+ let slot = table.slots[at];
5311
+ if (!slot || slot.kind !== kind) {
5312
+ if (slot) {
5313
+ if (slot.dispose) slot.dispose();
5314
+ if (slot.runner) stop(slot.runner);
5315
+ }
5316
+ slot = { kind, deps: null, value: void 0 };
5317
+ table.slots[at] = slot;
5318
+ }
5319
+ return slot;
5320
+ }
5321
+ function depsChanged(previous, next) {
5322
+ if (!previous || !next) return true;
5323
+ if (previous.length !== next.length) return true;
5324
+ for (let i = 0; i < next.length; i++) {
5325
+ if (!Object.is(previous[i], next[i])) return true;
5326
+ }
5327
+ return false;
5328
+ }
5329
+ function normalizeDeps(deps) {
5330
+ return Array.isArray(deps) ? deps.slice() : null;
5331
+ }
5332
+ function useState(scope, initial) {
5333
+ const slot = nextSlot(scope, "state");
5334
+ if (slot.value === void 0) slot.value = ref(initial);
5335
+ return slot.value;
5336
+ }
5337
+ function useEffect(scope, fn, deps) {
5338
+ const slot = nextSlot(scope, "effect");
5339
+ const next = normalizeDeps(deps);
5340
+ const runCleanup = () => {
5341
+ if (slot.dispose) {
5342
+ const dispose = slot.dispose;
5343
+ slot.dispose = void 0;
5344
+ dispose();
5345
+ }
5346
+ };
5347
+ if (next) {
5348
+ const first = slot.deps === null && !slot.runner;
5349
+ if (first || depsChanged(slot.deps, next)) {
5350
+ slot.deps = next;
5351
+ runCleanup();
5352
+ const result = fn();
5353
+ if (typeof result === "function") slot.dispose = result;
5354
+ }
5355
+ return;
5356
+ }
5357
+ if (slot.runner) return;
5358
+ slot.deps = null;
5359
+ slot.runner = effect(() => {
5360
+ runCleanup();
5361
+ const result = fn();
5362
+ if (typeof result === "function") slot.dispose = result;
5363
+ });
5364
+ }
5365
+ function useMemo(scope, fn, deps) {
5366
+ const slot = nextSlot(scope, "memo");
5367
+ const next = normalizeDeps(deps);
5368
+ if (!next) {
5369
+ if (!slot.value) slot.value = computed(fn);
5370
+ return slot.value;
5371
+ }
5372
+ if (!slot.value) {
5373
+ slot.value = ref(fn());
5374
+ slot.deps = next;
5375
+ } else if (depsChanged(slot.deps, next)) {
5376
+ slot.deps = next;
5377
+ slot.value.value = fn();
5378
+ }
5379
+ return slot.value;
5380
+ }
5381
+ function useRef(scope, initial) {
5382
+ const slot = nextSlot(scope, "ref");
5383
+ if (!slot.value) slot.value = markRaw({ current: initial });
5384
+ return slot.value;
5385
+ }
5386
+ function useContext(name, initial) {
5387
+ if (initial !== void 0 && !(name in allStores)) {
5388
+ store(name, initial);
5389
+ }
5390
+ return allStores[name];
5391
+ }
5392
+ function installHooks() {
5393
+ hook("useState", (scope) => (initial) => useState(scope, initial));
5394
+ hook(
5395
+ "useEffect",
5396
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
5397
+ );
5398
+ hook(
5399
+ "useMemo",
5400
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
5401
+ );
5402
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
5403
+ hook(
5404
+ "useContext",
5405
+ () => (name, initial) => useContext(name, initial)
5406
+ );
5407
+ }
5408
+
5184
5409
  // src/http/resource.ts
5185
5410
  init_reactivity();
5186
5411
  function pick(value, path) {
@@ -6785,6 +7010,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6785
7010
  setScopeMarker(markNodeScope);
6786
7011
  setDirectiveRegistrar(directive);
6787
7012
  installMagics();
7013
+ installHooks();
6788
7014
  var eventBus = /* @__PURE__ */ new Map();
6789
7015
  function on(name, handler) {
6790
7016
  let set2 = eventBus.get(name);
@@ -6820,7 +7046,7 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6820
7046
  }
6821
7047
  function directive(name, definition) {
6822
7048
  var _a2, _b;
6823
- const hooks = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
7049
+ const hooks2 = typeof definition === "function" ? { mounted: definition, updated: definition } : definition;
6824
7050
  defineDirective(
6825
7051
  name,
6826
7052
  (ctx) => {
@@ -6840,38 +7066,38 @@ Suggestion: attribute expressions accept a single value. If the logic spans more
6840
7066
  instance: (_b3 = (_a4 = ctx.scope.owner) == null ? void 0 : _a4.component) != null ? _b3 : null
6841
7067
  };
6842
7068
  };
6843
- const initial = hooks.raw ? ctx.expression : ctx.evaluate();
6844
- (_a3 = hooks.created) == null ? void 0 : _a3.call(hooks, ctx.el, makeBinding(initial));
6845
- (_b2 = hooks.beforeMount) == null ? void 0 : _b2.call(hooks, ctx.el, makeBinding(initial));
7069
+ const initial = hooks2.raw ? ctx.expression : ctx.evaluate();
7070
+ (_a3 = hooks2.created) == null ? void 0 : _a3.call(hooks2, ctx.el, makeBinding(initial));
7071
+ (_b2 = hooks2.beforeMount) == null ? void 0 : _b2.call(hooks2, ctx.el, makeBinding(initial));
6846
7072
  ctx.effect(() => {
6847
7073
  var _a4, _b3;
6848
- const value = hooks.raw ? ctx.expression : ctx.evaluate();
7074
+ const value = hooks2.raw ? ctx.expression : ctx.evaluate();
6849
7075
  if (!mounted) {
6850
7076
  mounted = true;
6851
7077
  oldValue = value;
6852
- (_a4 = hooks.mounted) == null ? void 0 : _a4.call(hooks, ctx.el, makeBinding(value));
7078
+ (_a4 = hooks2.mounted) == null ? void 0 : _a4.call(hooks2, ctx.el, makeBinding(value));
6853
7079
  return;
6854
7080
  }
6855
7081
  if (value === oldValue) return;
6856
7082
  const binding = makeBinding(value);
6857
- (_b3 = hooks.updated) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7083
+ (_b3 = hooks2.updated) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6858
7084
  oldValue = value;
6859
7085
  });
6860
7086
  ctx.cleanup(() => {
6861
7087
  var _a4, _b3;
6862
7088
  const binding = makeBinding(oldValue);
6863
- (_a4 = hooks.beforeUnmount) == null ? void 0 : _a4.call(hooks, ctx.el, binding);
6864
- (_b3 = hooks.unmounted) == null ? void 0 : _b3.call(hooks, ctx.el, binding);
7089
+ (_a4 = hooks2.beforeUnmount) == null ? void 0 : _a4.call(hooks2, ctx.el, binding);
7090
+ (_b3 = hooks2.unmounted) == null ? void 0 : _b3.call(hooks2, ctx.el, binding);
6865
7091
  });
6866
7092
  },
6867
- { priority: (_a2 = hooks.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks.terminal) != null ? _b : false }
7093
+ { priority: (_a2 = hooks2.priority) != null ? _a2 : PRIORITY.DEFAULT, terminal: (_b = hooks2.terminal) != null ? _b : false }
6868
7094
  );
6869
7095
  }
6870
7096
  function data(values) {
6871
7097
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
6872
7098
  return rootScope.data;
6873
7099
  }
6874
- var version2 = "0.11.1";
7100
+ var version2 = "0.12.1";
6875
7101
  var core = {
6876
7102
  // Utilities first: Voodoo's own names can override.
6877
7103
  ...utils_exports,
@@ -12393,12 +12619,15 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12393
12619
 
12394
12620
  // src/sound/index.ts
12395
12621
  init_registry();
12622
+ init_reactivity();
12623
+ init_style();
12396
12624
  var audioContext = null;
12397
12625
  var masterVolume = 0.35;
12398
12626
  var isMuted = false;
12399
12627
  var hasLoadedPreference = false;
12400
12628
  var VOLUME_KEY = "voodoo:sound:volume";
12401
12629
  var MUTE_KEY = "voodoo:sound:muted";
12630
+ var muteRevision = ref(0);
12402
12631
  function loadPreference() {
12403
12632
  if (hasLoadedPreference) return;
12404
12633
  hasLoadedPreference = true;
@@ -12714,6 +12943,7 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12714
12943
  loadPreference();
12715
12944
  isMuted = value;
12716
12945
  storage.set(MUTE_KEY, isMuted);
12946
+ muteRevision.value++;
12717
12947
  },
12718
12948
  /** Unmutes sound. */
12719
12949
  unmute() {
@@ -12725,8 +12955,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12725
12955
  this.mute(!isMuted);
12726
12956
  return isMuted;
12727
12957
  },
12728
- /** `true` when muted. */
12958
+ /**
12959
+ * `true` when muted.
12960
+ *
12961
+ * Reads a revision ref first, so that an expression asking whether sound is
12962
+ * muted actually re-runs when it changes. The state lives in a module
12963
+ * variable and in localStorage, both invisible to the Proxy, so
12964
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
12965
+ * no way to show whether it was muted, which made the mute button look like
12966
+ * it did nothing at all.
12967
+ */
12729
12968
  get muted() {
12969
+ void muteRevision.value;
12730
12970
  loadPreference();
12731
12971
  return isMuted;
12732
12972
  },
@@ -12777,11 +13017,18 @@ form.v-loading [type="submit"],form.v-loading button[disabled]{opacity:.6}
12777
13017
  cleanup(() => el.removeEventListener(event, play));
12778
13018
  void scope;
12779
13019
  });
13020
+ var MUTE_CSS = `
13021
+ .v-muted{opacity:.55}
13022
+ .v-muted::after{content:" \\1F507";font-size:.9em}
13023
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
13024
+ `;
12780
13025
  defineDirective("mute", ({ el, cleanup }) => {
13026
+ injectStyle("mute", MUTE_CSS);
12781
13027
  const sync = () => {
12782
13028
  const isMuted2 = sound.muted;
12783
13029
  el.setAttribute("aria-pressed", String(isMuted2));
12784
13030
  el.classList.toggle("v-muted", isMuted2);
13031
+ el.classList.toggle("v-mute-on", !isMuted2);
12785
13032
  };
12786
13033
  const toggle = () => {
12787
13034
  sound.toggle();