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/README.md CHANGED
@@ -22,7 +22,7 @@ npm install voodoojs
22
22
  Or drop it into a page, with nothing else:
23
23
 
24
24
  ```html
25
- <script src="https://cdn.jsdelivr.net/npm/voodoojs@0.11/dist/voodoo.min.js" defer></script>
25
+ <script src="https://cdn.jsdelivr.net/npm/voodoojs@0.12.1/dist/voodoo.full.min.js" defer></script>
26
26
 
27
27
  <div v-data="{ count: 0 }">
28
28
  <button @click="count++">Clicked { count } times</button>
@@ -30,8 +30,9 @@ Or drop it into a page, with nothing else:
30
30
  ```
31
31
 
32
32
  That page is a complete application. There is no build step, no bundler and no
33
- configuration. The tag is pinned to the `0.6` line, so patch releases arrive
34
- without an edit; pin the exact version if you would rather approve each one.
33
+ configuration. The tag names an exact version, so what you load never changes
34
+ under you. It is the FULL build, which is the one that carries JSX; drop `.full`
35
+ for the essential build, 84 KB gzipped against 132, if you do not need it.
35
36
 
36
37
  ## Two ways to write it
37
38
 
@@ -1,10 +1,10 @@
1
- import { reactive, handleError, EffectScope, effect } from './chunk-FITEA237.js';
2
- import { inDevelopment, warnInvalidExpression, warnUnknownDirective } from './chunk-TXWSOPF2.js';
3
- import { config, directives, components } from './chunk-GC3BKNPT.js';
4
- import { __publicField } from './chunk-BEQEBFQ2.js';
1
+ import { reactive, handleError, EffectScope, effect } from './chunk-LLNDLLKV.js';
2
+ import { inDevelopment, warnInvalidExpression, warnUnknownDirective } from './chunk-72PMUUMT.js';
3
+ import { config, directives, components } from './chunk-T5ION5NG.js';
4
+ import { __publicField } from './chunk-GXPNWCGE.js';
5
5
 
6
6
  /**
7
- * Voodoo.js v0.11.1
7
+ * Voodoo.js v0.12.1
8
8
  * JavaScript feels like magic.
9
9
  * (c) 2026 Voodoo.js contributors. MIT License.
10
10
  */
@@ -810,9 +810,17 @@ var Parser = class {
810
810
  if (t.type === "punct") {
811
811
  if (t.value === "(") {
812
812
  this.next();
813
- const expr = this.parseExpression();
813
+ const body = [this.parseExpression()];
814
+ while (this.isPunct(",")) {
815
+ this.next();
816
+ if (this.isPunct(")")) {
817
+ const stray = this.peek();
818
+ throw new VoodooSyntaxError("Trailing comma in a group", this.source, stray.start);
819
+ }
820
+ body.push(this.parseExpression());
821
+ }
814
822
  this.expect(")");
815
- return expr;
823
+ return body.length === 1 ? body[0] : { t: "seq", body };
816
824
  }
817
825
  if (t.value === "[") return this.parseArrayLiteral();
818
826
  if (t.value === "{") return this.parseObjectLiteral();
@@ -1409,6 +1417,10 @@ var magics = /* @__PURE__ */ new Map();
1409
1417
  function magic(name, getter) {
1410
1418
  magics.set(name.startsWith("$") ? name : `$${name}`, getter);
1411
1419
  }
1420
+ var hooks = /* @__PURE__ */ new Map();
1421
+ function hook(name, getter) {
1422
+ hooks.set(name, getter);
1423
+ }
1412
1424
  var Scope = class _Scope {
1413
1425
  // Assignment order matches the order the fields were declared in before, so
1414
1426
  // the properties are created in the same sequence they always were.
@@ -1464,7 +1476,10 @@ var Scope = class _Scope {
1464
1476
  s = s.parent;
1465
1477
  }
1466
1478
  if (name.charCodeAt(0) === 36 && magics.has(name)) {
1467
- return this.magicContainer(name);
1479
+ return this.magicContainer(name, magics);
1480
+ }
1481
+ if (hooks.has(name)) {
1482
+ return this.magicContainer(name, hooks);
1468
1483
  }
1469
1484
  return void 0;
1470
1485
  }
@@ -1493,11 +1508,11 @@ var Scope = class _Scope {
1493
1508
  reactiveChild(vars, el = null) {
1494
1509
  return new _Scope(reactive(vars), this, el ?? this.el);
1495
1510
  }
1496
- magicContainer(name) {
1511
+ magicContainer(name, registry) {
1497
1512
  if (!this.magicCache) this.magicCache = /* @__PURE__ */ new Map();
1498
1513
  const cached = this.magicCache.get(name);
1499
1514
  if (cached) return cached;
1500
- const getter = magics.get(name);
1515
+ const getter = registry.get(name);
1501
1516
  const scope = this;
1502
1517
  const container = {};
1503
1518
  Object.defineProperty(container, name, {
@@ -1818,7 +1833,16 @@ function runDirective(el, attr, scope) {
1818
1833
  },
1819
1834
  effect(fn) {
1820
1835
  const owner = ownerScope();
1821
- owner.run(() => effect(fn, { scope: owner }));
1836
+ const hosted = () => {
1837
+ const previous = hookHost;
1838
+ hookHost = el;
1839
+ try {
1840
+ fn();
1841
+ } finally {
1842
+ hookHost = previous;
1843
+ }
1844
+ };
1845
+ owner.run(() => effect(hosted, { scope: owner }));
1822
1846
  },
1823
1847
  cleanup(fn) {
1824
1848
  addCleanup(el, fn);
@@ -1866,6 +1890,56 @@ function walk(node, scope) {
1866
1890
  return;
1867
1891
  }
1868
1892
  initialized.add(el);
1893
+ const previousHost = hookHost;
1894
+ hookHost = el;
1895
+ try {
1896
+ walkElementDirectives(el, attrs, tagComponent, current);
1897
+ } finally {
1898
+ hookHost = previousHost;
1899
+ }
1900
+ }
1901
+ var hookHost = null;
1902
+ function currentHookHost() {
1903
+ return hookHost;
1904
+ }
1905
+ function createDataScope(expression, parent, el) {
1906
+ let ast = null;
1907
+ try {
1908
+ ast = parse(expression);
1909
+ } catch {
1910
+ ast = null;
1911
+ }
1912
+ const plain = ast && ast.t === "obj" && ast.props.every(
1913
+ (p) => !p.spread && !p.keyExpr
1914
+ );
1915
+ if (!plain) {
1916
+ const raw = evaluateIn(expression, parent, "v-data");
1917
+ return parent.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
1918
+ }
1919
+ const scope = parent.reactiveChild({}, el);
1920
+ const props = ast.props;
1921
+ for (const prop of props) {
1922
+ if (prop.key === null) continue;
1923
+ try {
1924
+ if (prop.getter) {
1925
+ const compute = evaluate(prop.value, scope);
1926
+ Object.defineProperty(scope.data, prop.key, {
1927
+ enumerable: true,
1928
+ configurable: true,
1929
+ get: () => compute.call(scope.data)
1930
+ });
1931
+ } else {
1932
+ scope.data[prop.key] = evaluate(prop.value, scope);
1933
+ }
1934
+ } catch (err) {
1935
+ if (inDevelopment()) warnInvalidExpression(el, expression, expression, err);
1936
+ else handleError(err, "v-data");
1937
+ }
1938
+ }
1939
+ return scope;
1940
+ }
1941
+ function walkElementDirectives(el, attrs, tagComponent, scope) {
1942
+ let current = scope;
1869
1943
  for (const attr of attrs) {
1870
1944
  const def = directives.get(attr.name);
1871
1945
  if (def?.terminal) {
@@ -1885,11 +1959,10 @@ function walk(node, scope) {
1885
1959
  nodeScopes.set(el, current);
1886
1960
  }
1887
1961
  } else if (dataAttr || componentAttr) {
1888
- const raw = dataAttr ? evaluateIn(dataAttr.expression || "{}", current, "v-data") : {};
1889
- current = current.reactiveChild(raw && typeof raw === "object" ? raw : {}, el);
1962
+ current = createDataScope(dataAttr ? dataAttr.expression || "{}" : "{}", current, el);
1890
1963
  nodeScopes.set(el, current);
1891
1964
  }
1892
- const attributeScope = mountedComponent ? activeScope : current;
1965
+ const attributeScope = mountedComponent ? scope : current;
1893
1966
  for (const attr of attrs) {
1894
1967
  if (attr.name === "data" || attr.name === "component") continue;
1895
1968
  runDirective(el, attr, attributeScope);
@@ -2044,13 +2117,13 @@ var componentAliases = /* @__PURE__ */ new Map();
2044
2117
  var started = false;
2045
2118
  var observer = null;
2046
2119
  var startHooks = [];
2047
- function onStart(hook) {
2048
- startHooks.push(hook);
2120
+ function onStart(hook2) {
2121
+ startHooks.push(hook2);
2049
2122
  }
2050
2123
  function runPhase(target, after) {
2051
- for (const hook of startHooks) {
2124
+ for (const hook2 of startHooks) {
2052
2125
  try {
2053
- hook(target, after);
2126
+ hook2(target, after);
2054
2127
  } catch (error) {
2055
2128
  console.error("[Voodoo] a start hook failed", error);
2056
2129
  }
@@ -2101,6 +2174,6 @@ function refresh(root) {
2101
2174
  walk(root ?? document.body, root ? findScope(root.parentNode) : rootScope);
2102
2175
  }
2103
2176
 
2104
- export { Scope, VoodooRuntimeError, VoodooSyntaxError, addCleanup, allowedGlobals, clearParseCache, closestDirective, collectDirectives, componentAliases, destroy, evaluate, evaluateIn, findScope, getEffectScopes, getScope, hadDirectives, hasAttr, hasDirective, hasDirectives, isInitialized, magic, magics, markInitialized, markNodeScope, markSkipChildren, onStart, originalAttributes, parse, parseAttribute, queryDirective, readAttr, refresh, removeQuietly, restoreAttributes, rootScope, setComponentMounter, start, stopObserving, stringify, tokenize, unwrap, walk };
2105
- //# sourceMappingURL=chunk-32J7GDLD.js.map
2106
- //# sourceMappingURL=chunk-32J7GDLD.js.map
2177
+ export { Scope, VoodooRuntimeError, VoodooSyntaxError, addCleanup, allowedGlobals, clearParseCache, closestDirective, collectDirectives, componentAliases, currentHookHost, destroy, evaluate, evaluateIn, findScope, getEffectScopes, getScope, hadDirectives, hasAttr, hasDirective, hasDirectives, hook, hooks, isInitialized, magic, magics, markInitialized, markNodeScope, markSkipChildren, onStart, originalAttributes, parse, parseAttribute, queryDirective, readAttr, refresh, removeQuietly, restoreAttributes, rootScope, setComponentMounter, start, stopObserving, stringify, tokenize, unwrap, walk };
2178
+ //# sourceMappingURL=chunk-4M4ZXHNY.js.map
2179
+ //# sourceMappingURL=chunk-4M4ZXHNY.js.map
@@ -1,7 +1,7 @@
1
- import { config } from './chunk-GC3BKNPT.js';
1
+ import { config } from './chunk-T5ION5NG.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.1
4
+ * Voodoo.js v0.12.1
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-TXWSOPF2.js.map
82
- //# sourceMappingURL=chunk-TXWSOPF2.js.map
81
+ //# sourceMappingURL=chunk-72PMUUMT.js.map
82
+ //# sourceMappingURL=chunk-72PMUUMT.js.map
@@ -1,8 +1,8 @@
1
- import { handleError } from './chunk-FITEA237.js';
2
- import { warn } from './chunk-TXWSOPF2.js';
1
+ import { handleError } from './chunk-LLNDLLKV.js';
2
+ import { warn } from './chunk-72PMUUMT.js';
3
3
 
4
4
  /**
5
- * Voodoo.js v0.11.1
5
+ * Voodoo.js v0.12.1
6
6
  * JavaScript feels like magic.
7
7
  * (c) 2026 Voodoo.js contributors. MIT License.
8
8
  */
@@ -1192,5 +1192,5 @@ var gpu = {
1192
1192
  };
1193
1193
 
1194
1194
  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 };
1195
- //# sourceMappingURL=chunk-OHSGDVPA.js.map
1196
- //# sourceMappingURL=chunk-OHSGDVPA.js.map
1195
+ //# sourceMappingURL=chunk-AFMCDNYS.js.map
1196
+ //# sourceMappingURL=chunk-AFMCDNYS.js.map
@@ -1,9 +1,9 @@
1
- import { warnOnce } from './chunk-TXWSOPF2.js';
2
- import { parseDuration } from './chunk-OJDW7KXT.js';
3
- import { __publicField } from './chunk-BEQEBFQ2.js';
1
+ import { warnOnce } from './chunk-72PMUUMT.js';
2
+ import { parseDuration } from './chunk-KN7NAKBL.js';
3
+ import { __publicField } from './chunk-GXPNWCGE.js';
4
4
 
5
5
  /**
6
- * Voodoo.js v0.11.1
6
+ * Voodoo.js v0.12.1
7
7
  * JavaScript feels like magic.
8
8
  * (c) 2026 Voodoo.js contributors. MIT License.
9
9
  */
@@ -444,5 +444,5 @@ var http = {
444
444
  };
445
445
 
446
446
  export { HttpError, clearCache, flushOfflineQueue, http, request };
447
- //# sourceMappingURL=chunk-KE6WWJOB.js.map
448
- //# sourceMappingURL=chunk-KE6WWJOB.js.map
447
+ //# sourceMappingURL=chunk-D7675IJG.js.map
448
+ //# sourceMappingURL=chunk-D7675IJG.js.map
@@ -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
  */
@@ -12,5 +12,5 @@ var __export = (target, all) => {
12
12
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
13
13
 
14
14
  export { __export, __publicField };
15
- //# sourceMappingURL=chunk-BEQEBFQ2.js.map
16
- //# sourceMappingURL=chunk-BEQEBFQ2.js.map
15
+ //# sourceMappingURL=chunk-GXPNWCGE.js.map
16
+ //# sourceMappingURL=chunk-GXPNWCGE.js.map
@@ -1,8 +1,8 @@
1
- import { reactive } from './chunk-FITEA237.js';
2
- import { warnOnce } from './chunk-TXWSOPF2.js';
1
+ import { reactive } from './chunk-LLNDLLKV.js';
2
+ import { warnOnce } from './chunk-72PMUUMT.js';
3
3
 
4
4
  /**
5
- * Voodoo.js v0.11.1
5
+ * Voodoo.js v0.12.1
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-FCJ2APGN.js.map
946
- //# sourceMappingURL=chunk-FCJ2APGN.js.map
945
+ //# sourceMappingURL=chunk-HNM3CTBD.js.map
946
+ //# sourceMappingURL=chunk-HNM3CTBD.js.map
@@ -1,7 +1,7 @@
1
- import { __export } from './chunk-BEQEBFQ2.js';
1
+ import { __export } from './chunk-GXPNWCGE.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.1
4
+ * Voodoo.js v0.12.1
5
5
  * JavaScript feels like magic.
6
6
  * (c) 2026 Voodoo.js contributors. MIT License.
7
7
  */
@@ -397,5 +397,5 @@ var device = {
397
397
  };
398
398
 
399
399
  export { capitalize, chunk, clone, debounce, device, escapeHtml, formatCurrency, formatDate, formatFileSize, formatNumber, formatPercent, get, groupBy, isBrowser, matchesMedia, memoize, merge, once, parseDuration, random, relativeTime, sample, set, setFormatDefaults, sleep, slugify, sortBy, stripTags, throttle, titleCase, truncate, uid, unique, utils_exports, uuid };
400
- //# sourceMappingURL=chunk-OJDW7KXT.js.map
401
- //# sourceMappingURL=chunk-OJDW7KXT.js.map
400
+ //# sourceMappingURL=chunk-KN7NAKBL.js.map
401
+ //# sourceMappingURL=chunk-KN7NAKBL.js.map
@@ -1,7 +1,7 @@
1
- import { __publicField } from './chunk-BEQEBFQ2.js';
1
+ import { __publicField } from './chunk-GXPNWCGE.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.1
4
+ * Voodoo.js v0.12.1
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-FITEA237.js.map
641
- //# sourceMappingURL=chunk-FITEA237.js.map
640
+ //# sourceMappingURL=chunk-LLNDLLKV.js.map
641
+ //# sourceMappingURL=chunk-LLNDLLKV.js.map
@@ -1,7 +1,7 @@
1
- import { config } from './chunk-GC3BKNPT.js';
1
+ import { config } from './chunk-T5ION5NG.js';
2
2
 
3
3
  /**
4
- * Voodoo.js v0.11.1
4
+ * Voodoo.js v0.12.1
5
5
  * JavaScript feels like magic.
6
6
  * (c) 2026 Voodoo.js contributors. MIT License.
7
7
  */
@@ -68,5 +68,5 @@ function ensureTokens() {
68
68
  }
69
69
 
70
70
  export { BASE_TOKENS, ensureTokens, injectStyle };
71
- //# sourceMappingURL=chunk-J6RTM5P4.js.map
72
- //# sourceMappingURL=chunk-J6RTM5P4.js.map
71
+ //# sourceMappingURL=chunk-NP66JM2P.js.map
72
+ //# sourceMappingURL=chunk-NP66JM2P.js.map
@@ -1,14 +1,14 @@
1
- import { http, HttpError, request } from './chunk-KE6WWJOB.js';
2
- import { stringify, markInitialized, markSkipChildren, destroy, walk, removeQuietly, evaluateIn, addCleanup, markNodeScope, setComponentMounter, closestDirective, magic, readAttr, hasAttr, componentAliases, Scope, queryDirective, parse, evaluate, parseAttribute, originalAttributes, hasDirective, findScope, VoodooRuntimeError, VoodooSyntaxError, allowedGlobals, clearParseCache, tokenize, getScope, stopObserving, refresh, start, magics, rootScope, isInitialized, restoreAttributes, hasDirectives } from './chunk-32J7GDLD.js';
3
- import { ref, reactive, handleError, nextTick, queuePostFlush, warn, watch, EffectScope, computed, effect, toRaw, flushSync, effectScope, stop, unref, markRaw, watchEffect, shallowRef, setErrorHandler } from './chunk-FITEA237.js';
4
- import { warnDuplicateKey, warn as warn$1, describeElement, warnUnknownComponent, warnAlias, warnRequiredProp } from './chunk-TXWSOPF2.js';
5
- import { parseDuration, debounce, utils_exports, throttle, uid, device, escapeHtml } from './chunk-OJDW7KXT.js';
6
- import { injectStyle, ensureTokens } from './chunk-J6RTM5P4.js';
7
- import { defineDirective, config, PRIORITY, directives, normalizeComponentName, components, usePlugin } from './chunk-GC3BKNPT.js';
8
- import { __publicField } from './chunk-BEQEBFQ2.js';
1
+ import { http, HttpError, request } from './chunk-D7675IJG.js';
2
+ import { stringify, markInitialized, markSkipChildren, destroy, walk, removeQuietly, evaluateIn, addCleanup, markNodeScope, setComponentMounter, closestDirective, magic, readAttr, hasAttr, componentAliases, Scope, hook, queryDirective, parse, evaluate, parseAttribute, originalAttributes, hasDirective, currentHookHost, findScope, VoodooRuntimeError, VoodooSyntaxError, allowedGlobals, clearParseCache, tokenize, getScope, stopObserving, refresh, start, magics, rootScope, isInitialized, restoreAttributes, hasDirectives } from './chunk-4M4ZXHNY.js';
3
+ import { ref, reactive, handleError, nextTick, queuePostFlush, warn, watch, EffectScope, computed, effect, toRaw, markRaw, stop, flushSync, effectScope, unref, watchEffect, shallowRef, setErrorHandler } from './chunk-LLNDLLKV.js';
4
+ import { warnDuplicateKey, warn as warn$1, describeElement, warnUnknownComponent, warnAlias, warnRequiredProp } from './chunk-72PMUUMT.js';
5
+ import { parseDuration, debounce, utils_exports, throttle, uid, device, escapeHtml } from './chunk-KN7NAKBL.js';
6
+ import { injectStyle, ensureTokens } from './chunk-NP66JM2P.js';
7
+ import { defineDirective, config, PRIORITY, directives, normalizeComponentName, components, usePlugin } from './chunk-T5ION5NG.js';
8
+ import { __publicField } from './chunk-GXPNWCGE.js';
9
9
 
10
10
  /**
11
- * Voodoo.js v0.11.1
11
+ * Voodoo.js v0.12.1
12
12
  * JavaScript feels like magic.
13
13
  * (c) 2026 Voodoo.js contributors. MIT License.
14
14
  */
@@ -338,10 +338,10 @@ var LIFECYCLE = /* @__PURE__ */ new Set([
338
338
  "destroyed"
339
339
  ]);
340
340
  function callHook(def, instance, name) {
341
- const hook = def[name];
342
- if (typeof hook !== "function") return;
341
+ const hook2 = def[name];
342
+ if (typeof hook2 !== "function") return;
343
343
  try {
344
- hook.call(instance);
344
+ hook2.call(instance);
345
345
  } catch (err) {
346
346
  handleError(err, `hook ${name}`);
347
347
  }
@@ -1116,21 +1116,28 @@ var cache = {
1116
1116
  };
1117
1117
  var THEME_KEY = "voodoo:theme";
1118
1118
  var picked = null;
1119
+ var revision = ref(0);
1119
1120
  var theme = {
1120
1121
  /** Theme chosen by the user, or `system` when never set. */
1121
1122
  get current() {
1123
+ void revision.value;
1122
1124
  return storage.get(THEME_KEY) ?? picked ?? "system";
1123
1125
  },
1124
1126
  /** Theme effectively applied, resolving `system`. */
1125
1127
  get resolved() {
1126
1128
  const value = this.current;
1127
1129
  if (value !== "system") return value;
1130
+ if (typeof document !== "undefined") {
1131
+ const authored = document.documentElement.getAttribute("data-theme");
1132
+ if (authored === "light" || authored === "dark") return authored;
1133
+ }
1128
1134
  if (typeof matchMedia === "undefined") return "light";
1129
1135
  return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1130
1136
  },
1131
1137
  set(value) {
1132
1138
  picked = value;
1133
1139
  storage.set(THEME_KEY, value);
1140
+ revision.value++;
1134
1141
  this.apply();
1135
1142
  },
1136
1143
  toggle() {
@@ -1164,8 +1171,17 @@ var theme = {
1164
1171
  init() {
1165
1172
  if (typeof document === "undefined") return;
1166
1173
  this.apply();
1174
+ if (typeof MutationObserver !== "undefined") {
1175
+ new MutationObserver(() => {
1176
+ revision.value++;
1177
+ }).observe(document.documentElement, {
1178
+ attributes: true,
1179
+ attributeFilter: ["data-theme"]
1180
+ });
1181
+ }
1167
1182
  if (typeof matchMedia === "undefined") return;
1168
1183
  matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
1184
+ revision.value++;
1169
1185
  if (this.current === "system") this.apply();
1170
1186
  });
1171
1187
  }
@@ -1298,6 +1314,139 @@ function installMagics() {
1298
1314
  );
1299
1315
  }
1300
1316
 
1317
+ // src/hooks/index.ts
1318
+ var tables = /* @__PURE__ */ new WeakMap();
1319
+ function tableFor(scope) {
1320
+ const host = currentHookHost() ?? scope.el ?? scope;
1321
+ let table = tables.get(host);
1322
+ if (!table) {
1323
+ table = { slots: [], index: 0, scheduled: false, bound: false };
1324
+ tables.set(host, table);
1325
+ }
1326
+ const el = currentHookHost() ?? scope.el;
1327
+ if (!table.bound && el) {
1328
+ table.bound = true;
1329
+ const owned = table;
1330
+ addCleanup(el, () => {
1331
+ for (const slot of owned.slots) {
1332
+ if (slot.dispose) slot.dispose();
1333
+ if (slot.runner) stop(slot.runner);
1334
+ }
1335
+ owned.slots.length = 0;
1336
+ });
1337
+ }
1338
+ if (!table.scheduled) {
1339
+ table.scheduled = true;
1340
+ const pending = table;
1341
+ queueMicrotask(() => {
1342
+ pending.index = 0;
1343
+ pending.scheduled = false;
1344
+ });
1345
+ }
1346
+ return table;
1347
+ }
1348
+ function nextSlot(scope, kind) {
1349
+ const table = tableFor(scope);
1350
+ const at = table.index++;
1351
+ let slot = table.slots[at];
1352
+ if (!slot || slot.kind !== kind) {
1353
+ if (slot) {
1354
+ if (slot.dispose) slot.dispose();
1355
+ if (slot.runner) stop(slot.runner);
1356
+ }
1357
+ slot = { kind, deps: null, value: void 0 };
1358
+ table.slots[at] = slot;
1359
+ }
1360
+ return slot;
1361
+ }
1362
+ function depsChanged(previous, next) {
1363
+ if (!previous || !next) return true;
1364
+ if (previous.length !== next.length) return true;
1365
+ for (let i = 0; i < next.length; i++) {
1366
+ if (!Object.is(previous[i], next[i])) return true;
1367
+ }
1368
+ return false;
1369
+ }
1370
+ function normalizeDeps(deps) {
1371
+ return Array.isArray(deps) ? deps.slice() : null;
1372
+ }
1373
+ function useState(scope, initial) {
1374
+ const slot = nextSlot(scope, "state");
1375
+ if (slot.value === void 0) slot.value = ref(initial);
1376
+ return slot.value;
1377
+ }
1378
+ function useEffect(scope, fn, deps) {
1379
+ const slot = nextSlot(scope, "effect");
1380
+ const next = normalizeDeps(deps);
1381
+ const runCleanup = () => {
1382
+ if (slot.dispose) {
1383
+ const dispose = slot.dispose;
1384
+ slot.dispose = void 0;
1385
+ dispose();
1386
+ }
1387
+ };
1388
+ if (next) {
1389
+ const first = slot.deps === null && !slot.runner;
1390
+ if (first || depsChanged(slot.deps, next)) {
1391
+ slot.deps = next;
1392
+ runCleanup();
1393
+ const result = fn();
1394
+ if (typeof result === "function") slot.dispose = result;
1395
+ }
1396
+ return;
1397
+ }
1398
+ if (slot.runner) return;
1399
+ slot.deps = null;
1400
+ slot.runner = effect(() => {
1401
+ runCleanup();
1402
+ const result = fn();
1403
+ if (typeof result === "function") slot.dispose = result;
1404
+ });
1405
+ }
1406
+ function useMemo(scope, fn, deps) {
1407
+ const slot = nextSlot(scope, "memo");
1408
+ const next = normalizeDeps(deps);
1409
+ if (!next) {
1410
+ if (!slot.value) slot.value = computed(fn);
1411
+ return slot.value;
1412
+ }
1413
+ if (!slot.value) {
1414
+ slot.value = ref(fn());
1415
+ slot.deps = next;
1416
+ } else if (depsChanged(slot.deps, next)) {
1417
+ slot.deps = next;
1418
+ slot.value.value = fn();
1419
+ }
1420
+ return slot.value;
1421
+ }
1422
+ function useRef(scope, initial) {
1423
+ const slot = nextSlot(scope, "ref");
1424
+ if (!slot.value) slot.value = markRaw({ current: initial });
1425
+ return slot.value;
1426
+ }
1427
+ function useContext(name, initial) {
1428
+ if (initial !== void 0 && !(name in allStores)) {
1429
+ store(name, initial);
1430
+ }
1431
+ return allStores[name];
1432
+ }
1433
+ function installHooks() {
1434
+ hook("useState", (scope) => (initial) => useState(scope, initial));
1435
+ hook(
1436
+ "useEffect",
1437
+ (scope) => (fn, deps) => useEffect(scope, fn, deps)
1438
+ );
1439
+ hook(
1440
+ "useMemo",
1441
+ (scope) => (fn, deps) => useMemo(scope, fn, deps)
1442
+ );
1443
+ hook("useRef", (scope) => (initial) => useRef(scope, initial));
1444
+ hook(
1445
+ "useContext",
1446
+ () => (name, initial) => useContext(name, initial)
1447
+ );
1448
+ }
1449
+
1301
1450
  // src/http/resource.ts
1302
1451
  function pick(value, path) {
1303
1452
  if (!path) return value;
@@ -2662,7 +2811,7 @@ var jsonStylesInjected = false;
2662
2811
  function injectJSONStyles() {
2663
2812
  if (jsonStylesInjected) return;
2664
2813
  jsonStylesInjected = true;
2665
- void import('./style-MX6ZATL4.js').then(({ injectStyle: injectStyle2 }) => {
2814
+ void import('./style-GR4X6O7I.js').then(({ injectStyle: injectStyle2 }) => {
2666
2815
  injectStyle2(
2667
2816
  "json-render",
2668
2817
  `
@@ -2871,6 +3020,7 @@ setComponentMounter(mountComponent);
2871
3020
  setScopeMarker(markNodeScope);
2872
3021
  setDirectiveRegistrar(directive);
2873
3022
  installMagics();
3023
+ installHooks();
2874
3024
  var eventBus = /* @__PURE__ */ new Map();
2875
3025
  function on(name, handler) {
2876
3026
  let set = eventBus.get(name);
@@ -2949,7 +3099,7 @@ function data(values) {
2949
3099
  Object.defineProperties(rootScope.data, Object.getOwnPropertyDescriptors(values));
2950
3100
  return rootScope.data;
2951
3101
  }
2952
- var version2 = "0.11.1";
3102
+ var version2 = "0.12.1";
2953
3103
  var core = {
2954
3104
  // Utilities first: Voodoo's own names can override.
2955
3105
  ...utils_exports,
@@ -8367,6 +8517,7 @@ var isMuted = false;
8367
8517
  var hasLoadedPreference = false;
8368
8518
  var VOLUME_KEY = "voodoo:sound:volume";
8369
8519
  var MUTE_KEY = "voodoo:sound:muted";
8520
+ var muteRevision = ref(0);
8370
8521
  function loadPreference() {
8371
8522
  if (hasLoadedPreference) return;
8372
8523
  hasLoadedPreference = true;
@@ -8678,6 +8829,7 @@ var sound = {
8678
8829
  loadPreference();
8679
8830
  isMuted = value;
8680
8831
  storage.set(MUTE_KEY, isMuted);
8832
+ muteRevision.value++;
8681
8833
  },
8682
8834
  /** Unmutes sound. */
8683
8835
  unmute() {
@@ -8689,8 +8841,18 @@ var sound = {
8689
8841
  this.mute(!isMuted);
8690
8842
  return isMuted;
8691
8843
  },
8692
- /** `true` when muted. */
8844
+ /**
8845
+ * `true` when muted.
8846
+ *
8847
+ * Reads a revision ref first, so that an expression asking whether sound is
8848
+ * muted actually re-runs when it changes. The state lives in a module
8849
+ * variable and in localStorage, both invisible to the Proxy, so
8850
+ * `v-show="$sound.muted"` used to render once and then never move: a page had
8851
+ * no way to show whether it was muted, which made the mute button look like
8852
+ * it did nothing at all.
8853
+ */
8693
8854
  get muted() {
8855
+ void muteRevision.value;
8694
8856
  loadPreference();
8695
8857
  return isMuted;
8696
8858
  },
@@ -8740,11 +8902,18 @@ defineDirective("sound", ({ el, arg, expression, modifiers, scope, cleanup, eval
8740
8902
  el.addEventListener(event, play);
8741
8903
  cleanup(() => el.removeEventListener(event, play));
8742
8904
  });
8905
+ var MUTE_CSS = `
8906
+ .v-muted{opacity:.55}
8907
+ .v-muted::after{content:" \\1F507";font-size:.9em}
8908
+ .v-mute-on::after{content:" \\1F50A";font-size:.9em}
8909
+ `;
8743
8910
  defineDirective("mute", ({ el, cleanup }) => {
8911
+ injectStyle("mute", MUTE_CSS);
8744
8912
  const sync = () => {
8745
8913
  const isMuted2 = sound.muted;
8746
8914
  el.setAttribute("aria-pressed", String(isMuted2));
8747
8915
  el.classList.toggle("v-muted", isMuted2);
8916
+ el.classList.toggle("v-mute-on", !isMuted2);
8748
8917
  };
8749
8918
  const toggle = () => {
8750
8919
  sound.toggle();
@@ -10270,6 +10439,6 @@ defineDirective(
10270
10439
  { priority: PRIORITY.MODEL + 5 }
10271
10440
  );
10272
10441
 
10273
- export { VoodooCollection, alert, allStores, applyMask, cache, clearErrors, clipboard, confirm, cookie, core, createApp, createResource, defineComponent, dialog, efeitos, ensurePalette, enter, fadeIn, fadeOut, fromHtml, hotkey, instances, leave, mask, masks, messages, modal, mountComponent, network, palette, prompt, query, ready, ready2, registerMask, removeStore, screen, serializeForm, session, showFieldError, showFormErrors, slideDown, slideUp, sound, storage, store, storeNames, theme, toast, unmask, url, validate, validator, viewTransition, whenElement, whenReady };
10274
- //# sourceMappingURL=chunk-XG2UDP4O.js.map
10275
- //# sourceMappingURL=chunk-XG2UDP4O.js.map
10442
+ export { VoodooCollection, alert, allStores, applyMask, cache, clearErrors, clipboard, confirm, cookie, core, createApp, createResource, defineComponent, dialog, efeitos, ensurePalette, enter, fadeIn, fadeOut, fromHtml, hotkey, installHooks, instances, leave, mask, masks, messages, modal, mountComponent, network, palette, prompt, query, ready, ready2, registerMask, removeStore, screen, serializeForm, session, showFieldError, showFormErrors, slideDown, slideUp, sound, storage, store, storeNames, theme, toast, unmask, url, useContext, useEffect, useMemo, useRef, useState, validate, validator, viewTransition, whenElement, whenReady };
10443
+ //# sourceMappingURL=chunk-RDHYPGH6.js.map
10444
+ //# sourceMappingURL=chunk-RDHYPGH6.js.map