sibujs 3.3.1 → 3.3.3

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 (78) hide show
  1. package/dist/browser.cjs +41 -18
  2. package/dist/browser.js +5 -5
  3. package/dist/build.cjs +57 -22
  4. package/dist/build.js +14 -14
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-MHBCEJQO.js → chunk-23VT3KZB.js} +2 -2
  7. package/dist/{chunk-WL7BIR6O.js → chunk-25LY5SRH.js} +31 -24
  8. package/dist/{chunk-37BUKSLH.js → chunk-5K6RVA2S.js} +1 -1
  9. package/dist/{chunk-AMIKDMLP.js → chunk-5Y34FWED.js} +17 -6
  10. package/dist/chunk-7ZHH77QA.js +9 -0
  11. package/dist/chunk-CCSJMTRN.js +15 -0
  12. package/dist/{chunk-R3QEDXFS.js → chunk-EX77FXTT.js} +1 -1
  13. package/dist/{chunk-WW6DAGGR.js → chunk-F3HO2MP7.js} +1 -1
  14. package/dist/{chunk-ZUVLC7TM.js → chunk-F7HW2NI5.js} +2 -4
  15. package/dist/{chunk-ULLTNDRA.js → chunk-IEYFHN5V.js} +72 -25
  16. package/dist/{chunk-Z37APKBV.js → chunk-IQN5W7GE.js} +73 -16
  17. package/dist/{chunk-WZG2SZOT.js → chunk-IWFE4AHO.js} +1 -1
  18. package/dist/{chunk-IKLYI3RF.js → chunk-MYXEBADX.js} +53 -23
  19. package/dist/{chunk-6LTFHJQG.js → chunk-QCQQ2N5H.js} +41 -20
  20. package/dist/{chunk-VOVVTOEA.js → chunk-QTOOBABV.js} +22 -5
  21. package/dist/{chunk-LU2MQXQQ.js → chunk-RZKGMHH4.js} +6 -6
  22. package/dist/{chunk-GOJMFRBL.js → chunk-S3NFJO6L.js} +20 -14
  23. package/dist/{chunk-NHKQKKZU.js → chunk-SBPUXWCE.js} +1 -1
  24. package/dist/{chunk-SLMFA3ZZ.js → chunk-TBDTU7UO.js} +2 -2
  25. package/dist/{chunk-4UUMSLSL.js → chunk-TVCCXPU2.js} +5 -5
  26. package/dist/{chunk-TEFZT5PJ.js → chunk-UOMYIAG6.js} +1 -1
  27. package/dist/{chunk-XH2RTYEQ.js → chunk-WFUUT6TG.js} +15 -8
  28. package/dist/{chunk-M5KBNOSJ.js → chunk-WTFMV2RU.js} +70 -59
  29. package/dist/{chunk-3JHCYHWN.js → chunk-WWV3SJ3L.js} +19 -13
  30. package/dist/{chunk-RYMOSG5B.js → chunk-Y35NQNLG.js} +90 -60
  31. package/dist/{chunk-F7FXQ3QS.js → chunk-ZXCZWMZT.js} +1 -1
  32. package/dist/data.cjs +36 -16
  33. package/dist/data.js +8 -7
  34. package/dist/devtools.cjs +91 -35
  35. package/dist/devtools.d.cts +12 -5
  36. package/dist/devtools.d.ts +12 -5
  37. package/dist/devtools.js +7 -6
  38. package/dist/ecosystem.cjs +31 -16
  39. package/dist/ecosystem.js +11 -10
  40. package/dist/extras.cjs +485 -280
  41. package/dist/extras.d.cts +1 -1
  42. package/dist/extras.d.ts +1 -1
  43. package/dist/extras.js +28 -24
  44. package/dist/index.cjs +59 -22
  45. package/dist/index.d.cts +24 -8
  46. package/dist/index.d.ts +24 -8
  47. package/dist/index.js +25 -21
  48. package/dist/motion.cjs +223 -204
  49. package/dist/motion.d.cts +8 -1
  50. package/dist/motion.d.ts +8 -1
  51. package/dist/motion.js +6 -3
  52. package/dist/patterns.cjs +33 -20
  53. package/dist/patterns.d.cts +4 -1
  54. package/dist/patterns.d.ts +4 -1
  55. package/dist/patterns.js +6 -6
  56. package/dist/performance.cjs +90 -70
  57. package/dist/performance.js +8 -7
  58. package/dist/plugins.cjs +207 -144
  59. package/dist/plugins.d.cts +2 -1
  60. package/dist/plugins.d.ts +2 -1
  61. package/dist/plugins.js +178 -124
  62. package/dist/{ssr-6D67RAVB.js → ssr-2PPULEK2.js} +2 -2
  63. package/dist/ssr.cjs +132 -105
  64. package/dist/ssr.d.cts +8 -1
  65. package/dist/ssr.d.ts +8 -1
  66. package/dist/ssr.js +11 -10
  67. package/dist/testing.cjs +38 -18
  68. package/dist/testing.js +19 -8
  69. package/dist/ui.cjs +337 -294
  70. package/dist/ui.d.cts +0 -20
  71. package/dist/ui.d.ts +0 -20
  72. package/dist/ui.js +30 -24
  73. package/dist/widgets.cjs +95 -27
  74. package/dist/widgets.d.cts +6 -1
  75. package/dist/widgets.d.ts +6 -1
  76. package/dist/widgets.js +8 -7
  77. package/package.json +1 -1
  78. package/dist/chunk-YT6HQ6AM.js +0 -14
package/dist/extras.cjs CHANGED
@@ -209,6 +209,7 @@ __export(extras_exports, {
209
209
  pointerLock: () => pointerLock,
210
210
  popover: () => popover,
211
211
  precompile: () => precompile,
212
+ prefersReducedMotion: () => prefersReducedMotion,
212
213
  prefetch: () => prefetch,
213
214
  preloadCritical: () => preloadCritical,
214
215
  preloadImage: () => preloadImage,
@@ -753,7 +754,7 @@ function forEachSubscriber(signal2, visit) {
753
754
 
754
755
  // src/reactivity/track.ts
755
756
  var _isDev3 = isDev();
756
- var _runtimeVersion = typeof __SIBU_VERSION__ !== "undefined" ? __SIBU_VERSION__ : "dev";
757
+ var _runtimeVersion = true ? "3.3.3" : "dev";
757
758
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
758
759
  function resolveReactiveApi() {
759
760
  const g2 = globalThis;
@@ -889,23 +890,29 @@ function derived(getter, options) {
889
890
  }
890
891
 
891
892
  // src/core/ssr-context.ts
892
- var als = null;
893
- try {
894
- if (typeof process !== "undefined" && process.versions && process.versions.node) {
895
- let mod = null;
896
- const getBuiltin = process.getBuiltinModule;
897
- if (typeof getBuiltin === "function") {
898
- mod = getBuiltin("node:async_hooks");
899
- } else {
900
- const req = Function("return typeof require==='function'?require:null")();
901
- if (req) mod = req("node:async_hooks");
893
+ var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
894
+ function detectSSRShared() {
895
+ let detected = null;
896
+ try {
897
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
898
+ let mod = null;
899
+ const getBuiltin = process.getBuiltinModule;
900
+ if (typeof getBuiltin === "function") {
901
+ mod = getBuiltin("node:async_hooks");
902
+ } else {
903
+ const req = Function("return typeof require==='function'?require:null")();
904
+ if (req) mod = req("node:async_hooks");
905
+ }
906
+ if (mod) detected = new mod.AsyncLocalStorage();
902
907
  }
903
- if (mod) als = new mod.AsyncLocalStorage();
908
+ } catch {
909
+ detected = null;
904
910
  }
905
- } catch {
906
- als = null;
911
+ return { als: detected, fallbackStore: { ssr: false, suspenseIdCounter: 0 } };
907
912
  }
908
- var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
913
+ var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
914
+ var als = _shared.als;
915
+ var fallbackStore = _shared.fallbackStore;
909
916
  function getSSRStore() {
910
917
  if (als) {
911
918
  const s2 = als.getStore();
@@ -1169,6 +1176,12 @@ function signal(initial, options) {
1169
1176
  return [get, set];
1170
1177
  }
1171
1178
 
1179
+ // src/utils/globalSingleton.ts
1180
+ function globalSingleton(key, create) {
1181
+ const g2 = globalThis;
1182
+ return g2[key] ?? (g2[key] = create());
1183
+ }
1184
+
1172
1185
  // src/data/retry.ts
1173
1186
  function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
1174
1187
  let delay;
@@ -1230,7 +1243,7 @@ async function withRetry(fn, options, onRetry, signal2) {
1230
1243
  }
1231
1244
 
1232
1245
  // src/data/query.ts
1233
- var globalQueryCache = /* @__PURE__ */ new Map();
1246
+ var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
1234
1247
  function getActiveQueryCache() {
1235
1248
  return getRequestScopedCache("query") ?? globalQueryCache;
1236
1249
  }
@@ -2252,6 +2265,10 @@ function socket(url, options) {
2252
2265
  }
2253
2266
  function connect() {
2254
2267
  if (disposed) return;
2268
+ if (typeof WebSocket === "undefined") {
2269
+ setStatus("closed");
2270
+ return;
2271
+ }
2255
2272
  const safeUrl = validateWsUrl(getUrl());
2256
2273
  if (safeUrl === null) {
2257
2274
  setStatus("closed");
@@ -2445,6 +2462,10 @@ function stream(url, options) {
2445
2462
  let attempts = 0;
2446
2463
  function connect() {
2447
2464
  if (disposed) return;
2465
+ if (typeof EventSource === "undefined") {
2466
+ setStatus("closed");
2467
+ return;
2468
+ }
2448
2469
  const safeUrl = validateSseUrl(url);
2449
2470
  if (safeUrl === null) {
2450
2471
  setStatus("closed");
@@ -2681,7 +2702,7 @@ function battery() {
2681
2702
  let disposed = false;
2682
2703
  if (typeof navigator !== "undefined" && "getBattery" in navigator) {
2683
2704
  setSupported(true);
2684
- navigator.getBattery().then((bm) => {
2705
+ const batteryPromise = navigator.getBattery().then((bm) => {
2685
2706
  if (disposed) return;
2686
2707
  battery2 = bm;
2687
2708
  batch(() => {
@@ -2699,6 +2720,9 @@ function battery() {
2699
2720
  bm.addEventListener("chargingtimechange", onChargingTimeChange);
2700
2721
  bm.addEventListener("dischargingtimechange", onDischargingTimeChange);
2701
2722
  });
2723
+ batteryPromise.catch(() => {
2724
+ if (!disposed) setSupported(false);
2725
+ });
2702
2726
  }
2703
2727
  function dispose2() {
2704
2728
  disposed = true;
@@ -3263,9 +3287,17 @@ function wakeLock() {
3263
3287
  }
3264
3288
  const api = navigator.wakeLock;
3265
3289
  let sentinel = null;
3290
+ let disposed = false;
3266
3291
  async function request() {
3292
+ if (disposed) return;
3267
3293
  try {
3268
- sentinel = await api.request("screen");
3294
+ const s2 = await api.request("screen");
3295
+ if (disposed) {
3296
+ s2.release().catch(() => {
3297
+ });
3298
+ return;
3299
+ }
3300
+ sentinel = s2;
3269
3301
  setActive(true);
3270
3302
  sentinel.addEventListener("release", () => {
3271
3303
  setActive(false);
@@ -3292,6 +3324,7 @@ function wakeLock() {
3292
3324
  };
3293
3325
  document.addEventListener("visibilitychange", onVisibility);
3294
3326
  function dispose2() {
3327
+ disposed = true;
3295
3328
  document.removeEventListener("visibilitychange", onVisibility);
3296
3329
  release().catch((err) => {
3297
3330
  if (typeof console !== "undefined") {
@@ -3323,6 +3356,7 @@ function animationFrame(options = {}) {
3323
3356
  let id = null;
3324
3357
  let prev = -1;
3325
3358
  let start = -1;
3359
+ let disposed = false;
3326
3360
  const minFrameMs = options.fpsLimit ? 1e3 / options.fpsLimit : 0;
3327
3361
  const step = (now) => {
3328
3362
  if (start < 0) start = now;
@@ -3336,7 +3370,7 @@ function animationFrame(options = {}) {
3336
3370
  id = requestAnimationFrame(step);
3337
3371
  };
3338
3372
  function resume() {
3339
- if (id !== null) return;
3373
+ if (disposed || id !== null) return;
3340
3374
  setRunning(true);
3341
3375
  id = requestAnimationFrame(step);
3342
3376
  }
@@ -3350,6 +3384,7 @@ function animationFrame(options = {}) {
3350
3384
  start = -1;
3351
3385
  }
3352
3386
  function dispose2() {
3387
+ disposed = true;
3353
3388
  pause();
3354
3389
  }
3355
3390
  if (options.immediate !== false) resume();
@@ -3408,7 +3443,9 @@ function bounds(target) {
3408
3443
  }
3409
3444
  };
3410
3445
  }
3446
+ let disposed = false;
3411
3447
  function refresh() {
3448
+ if (disposed) return;
3412
3449
  setRect(readRect(target));
3413
3450
  }
3414
3451
  refresh();
@@ -3420,6 +3457,7 @@ function bounds(target) {
3420
3457
  const onScroll = () => refresh();
3421
3458
  window.addEventListener("scroll", onScroll, { passive: true, capture: true });
3422
3459
  function dispose2() {
3460
+ disposed = true;
3423
3461
  resizeObserver?.disconnect();
3424
3462
  window.removeEventListener("scroll", onScroll, { capture: true });
3425
3463
  }
@@ -3865,7 +3903,14 @@ function machine(config) {
3865
3903
 
3866
3904
  // src/patterns/persist.ts
3867
3905
  function persisted(key, initial, options = {}) {
3868
- const storage = options.session ? sessionStorage : localStorage;
3906
+ const storage = (() => {
3907
+ try {
3908
+ if (options.session) return typeof sessionStorage !== "undefined" ? sessionStorage : null;
3909
+ return typeof localStorage !== "undefined" ? localStorage : null;
3910
+ } catch {
3911
+ return null;
3912
+ }
3913
+ })();
3869
3914
  const serialize = options.serialize || JSON.stringify;
3870
3915
  const safeReviver = (k, v) => isUnsafeKey(k) ? void 0 : v;
3871
3916
  const deserialize = options.deserialize || ((raw) => JSON.parse(raw, safeReviver));
@@ -3874,7 +3919,7 @@ function persisted(key, initial, options = {}) {
3874
3919
  const syncTabs = options.session ? false : options.syncTabs ?? true;
3875
3920
  let restored = initial;
3876
3921
  try {
3877
- let raw = storage.getItem(key);
3922
+ let raw = storage ? storage.getItem(key) : null;
3878
3923
  if (raw !== null) {
3879
3924
  if (decrypt) raw = decrypt(raw);
3880
3925
  const parsed = deserialize(raw);
@@ -3890,12 +3935,12 @@ function persisted(key, initial, options = {}) {
3890
3935
  try {
3891
3936
  let serialized = serialize(current);
3892
3937
  if (encrypt) serialized = encrypt(serialized);
3893
- storage.setItem(key, serialized);
3938
+ storage?.setItem(key, serialized);
3894
3939
  } catch {
3895
3940
  }
3896
3941
  });
3897
3942
  let storageListener = null;
3898
- if (syncTabs && typeof window !== "undefined") {
3943
+ if (syncTabs && storage && typeof window !== "undefined") {
3899
3944
  storageListener = (e) => {
3900
3945
  if (e.storageArea !== storage || e.key !== key) return;
3901
3946
  if (e.newValue === null) {
@@ -4198,7 +4243,7 @@ function globalStore(config) {
4198
4243
  next();
4199
4244
  }
4200
4245
  function select3(selector) {
4201
- return derived(() => selector(getState()));
4246
+ return () => selector(getState());
4202
4247
  }
4203
4248
  function subscribe(callback) {
4204
4249
  listeners.add(callback);
@@ -4375,6 +4420,67 @@ function createGuard(validator) {
4375
4420
  };
4376
4421
  }
4377
4422
 
4423
+ // src/core/rendering/dispose.ts
4424
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
4425
+ var _isDev5 = isDev();
4426
+ var activeBindingCount = 0;
4427
+ function registerDisposer(node, teardown) {
4428
+ let disposers = elementDisposers.get(node);
4429
+ if (!disposers) {
4430
+ disposers = [];
4431
+ elementDisposers.set(node, disposers);
4432
+ }
4433
+ disposers.push(teardown);
4434
+ if (_isDev5) activeBindingCount++;
4435
+ }
4436
+ function dispose(node) {
4437
+ const stack = [node];
4438
+ const order = [];
4439
+ while (stack.length > 0) {
4440
+ const current = stack.pop();
4441
+ order.push(current);
4442
+ const children = Array.from(current.childNodes);
4443
+ for (let i2 = 0; i2 < children.length; i2++) {
4444
+ stack.push(children[i2]);
4445
+ }
4446
+ }
4447
+ for (let i2 = order.length - 1; i2 >= 0; i2--) {
4448
+ const current = order[i2];
4449
+ const disposers = elementDisposers.get(current);
4450
+ if (disposers) {
4451
+ const snapshot = disposers.slice();
4452
+ elementDisposers.delete(current);
4453
+ if (_isDev5) activeBindingCount -= snapshot.length;
4454
+ for (const d of snapshot) {
4455
+ try {
4456
+ d();
4457
+ } catch (err) {
4458
+ if (_isDev5 && typeof console !== "undefined") {
4459
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
4460
+ }
4461
+ }
4462
+ }
4463
+ let extraPasses = 0;
4464
+ while (extraPasses++ < 8) {
4465
+ const added = elementDisposers.get(current);
4466
+ if (!added || added.length === 0) break;
4467
+ const moreSnapshot = added.slice();
4468
+ elementDisposers.delete(current);
4469
+ if (_isDev5) activeBindingCount -= moreSnapshot.length;
4470
+ for (const d of moreSnapshot) {
4471
+ try {
4472
+ d();
4473
+ } catch (err) {
4474
+ if (_isDev5 && typeof console !== "undefined") {
4475
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
4476
+ }
4477
+ }
4478
+ }
4479
+ }
4480
+ }
4481
+ }
4482
+ }
4483
+
4378
4484
  // src/ui/transition.ts
4379
4485
  function transition(element, options = {}) {
4380
4486
  const {
@@ -4447,6 +4553,7 @@ function transition(element, options = {}) {
4447
4553
  }
4448
4554
  });
4449
4555
  }
4556
+ registerDisposer(element, cancelPending);
4450
4557
  return { enter, leave };
4451
4558
  }
4452
4559
  function spring(element, keyframes, options = {}) {
@@ -4462,8 +4569,29 @@ function spring(element, keyframes, options = {}) {
4462
4569
  });
4463
4570
  }
4464
4571
 
4572
+ // src/ui/reducedMotion.ts
4573
+ function prefersReducedMotion() {
4574
+ return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
4575
+ }
4576
+ function reducedMotion() {
4577
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
4578
+ const [reduced2] = signal(false);
4579
+ return { reduced: reduced2, dispose: () => {
4580
+ } };
4581
+ }
4582
+ const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
4583
+ const [reduced, setReduced] = signal(mql.matches);
4584
+ const handler = (event) => {
4585
+ setReduced(event.matches);
4586
+ };
4587
+ mql.addEventListener("change", handler);
4588
+ function dispose2() {
4589
+ mql.removeEventListener("change", handler);
4590
+ }
4591
+ return { reduced, dispose: dispose2 };
4592
+ }
4593
+
4465
4594
  // src/ui/animationPresets.ts
4466
- var prefersReducedMotion = () => typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
4467
4595
  function createPreset(keyframes, defaults, overrides) {
4468
4596
  const opts = {
4469
4597
  ...defaults,
@@ -4632,6 +4760,7 @@ function TransitionGroup(options) {
4632
4760
  if (options.leave) {
4633
4761
  await options.leave(el);
4634
4762
  }
4763
+ positions.delete(el);
4635
4764
  setElements((prev) => prev.filter((e) => e !== el));
4636
4765
  }
4637
4766
  function track4(newElements) {
@@ -4698,27 +4827,7 @@ function viewTransition(callback) {
4698
4827
  return { start, isTransitioning };
4699
4828
  }
4700
4829
 
4701
- // src/ui/reducedMotion.ts
4702
- function reducedMotion() {
4703
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
4704
- const [reduced2] = signal(false);
4705
- return { reduced: reduced2, dispose: () => {
4706
- } };
4707
- }
4708
- const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
4709
- const [reduced, setReduced] = signal(mql.matches);
4710
- const handler = (event) => {
4711
- setReduced(event.matches);
4712
- };
4713
- mql.addEventListener("change", handler);
4714
- function dispose2() {
4715
- mql.removeEventListener("change", handler);
4716
- }
4717
- return { reduced, dispose: dispose2 };
4718
- }
4719
-
4720
4830
  // src/ui/springSignal.ts
4721
- var prefersReducedMotion2 = () => typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
4722
4831
  function springSignal(initial, options) {
4723
4832
  const stiffness = options?.stiffness ?? 0.15;
4724
4833
  const damping = options?.damping ?? 0.8;
@@ -4754,7 +4863,7 @@ function springSignal(initial, options) {
4754
4863
  }
4755
4864
  function set(newTarget) {
4756
4865
  target = newTarget;
4757
- if (prefersReducedMotion2()) {
4866
+ if (prefersReducedMotion()) {
4758
4867
  current = newTarget;
4759
4868
  velocity = 0;
4760
4869
  if (rafId !== null) {
@@ -4836,30 +4945,46 @@ function custom(fn, message) {
4836
4945
  return (value) => fn(value) ? null : message;
4837
4946
  }
4838
4947
  function bindField(field, extras) {
4948
+ const readControlValue = (target) => {
4949
+ if ("checked" in target && target.type === "checkbox") {
4950
+ return target.checked;
4951
+ }
4952
+ if (target instanceof HTMLSelectElement && target.multiple) {
4953
+ return Array.from(target.selectedOptions, (o) => o.value);
4954
+ }
4955
+ return target.value;
4956
+ };
4839
4957
  const fieldOn = {
4840
- input: (e) => {
4841
- const target = e.target;
4842
- if (target.type === "checkbox") {
4843
- field.set(target.checked);
4844
- } else {
4845
- field.set(target.value);
4846
- }
4847
- },
4848
- change: (e) => {
4849
- const target = e.target;
4850
- if ("checked" in target && target.type === "checkbox") {
4851
- field.set(target.checked);
4852
- } else {
4853
- field.set(target.value);
4854
- }
4855
- },
4958
+ input: (e) => field.set(readControlValue(e.target)),
4959
+ change: (e) => field.set(readControlValue(e.target)),
4856
4960
  blur: () => field.touch()
4857
4961
  };
4858
- const { on: extraOn, value: _ignoreValue, ...restExtras } = extras ?? {};
4962
+ const {
4963
+ on: extraOn,
4964
+ value: _ignoreValue,
4965
+ onElement: extraOnElement,
4966
+ ...restExtras
4967
+ } = extras ?? {};
4859
4968
  const mergedOn = extraOn && typeof extraOn === "object" ? { ...extraOn, ...fieldOn } : fieldOn;
4969
+ const onElement = (el) => {
4970
+ if (el instanceof HTMLSelectElement && el.multiple) {
4971
+ const stop2 = effect(() => {
4972
+ const v = field.value();
4973
+ const selected = Array.isArray(v) ? v.map(String) : [];
4974
+ for (const opt of Array.from(el.options)) {
4975
+ opt.selected = selected.includes(opt.value);
4976
+ }
4977
+ });
4978
+ registerDisposer(el, stop2);
4979
+ }
4980
+ if (typeof extraOnElement === "function") {
4981
+ extraOnElement(el);
4982
+ }
4983
+ };
4860
4984
  return {
4861
4985
  value: field.value,
4862
4986
  on: mergedOn,
4987
+ onElement,
4863
4988
  ...restExtras
4864
4989
  };
4865
4990
  }
@@ -4916,9 +5041,21 @@ function form(config) {
4916
5041
  }
4917
5042
  return true;
4918
5043
  });
5044
+ const valueEquals = (a2, b2) => {
5045
+ if (Object.is(a2, b2)) return true;
5046
+ if (Array.isArray(a2) && Array.isArray(b2)) {
5047
+ return a2.length === b2.length && a2.every((v, i2) => Object.is(v, b2[i2]));
5048
+ }
5049
+ if (a2 && b2 && typeof a2 === "object" && typeof b2 === "object") {
5050
+ const ak = Object.keys(a2);
5051
+ const bk = Object.keys(b2);
5052
+ return ak.length === bk.length && ak.every((k) => Object.is(a2[k], b2[k]));
5053
+ }
5054
+ return false;
5055
+ };
4919
5056
  const isDirty = derived(() => {
4920
5057
  for (const [name, cfg] of fieldEntries) {
4921
- if (!Object.is(fieldMap[name].value(), cfg.initial)) return true;
5058
+ if (!valueEquals(fieldMap[name].value(), cfg.initial)) return true;
4922
5059
  }
4923
5060
  return false;
4924
5061
  });
@@ -4978,67 +5115,6 @@ function form(config) {
4978
5115
  };
4979
5116
  }
4980
5117
 
4981
- // src/core/rendering/dispose.ts
4982
- var elementDisposers = /* @__PURE__ */ new WeakMap();
4983
- var _isDev5 = isDev();
4984
- var activeBindingCount = 0;
4985
- function registerDisposer(node, teardown) {
4986
- let disposers = elementDisposers.get(node);
4987
- if (!disposers) {
4988
- disposers = [];
4989
- elementDisposers.set(node, disposers);
4990
- }
4991
- disposers.push(teardown);
4992
- if (_isDev5) activeBindingCount++;
4993
- }
4994
- function dispose(node) {
4995
- const stack = [node];
4996
- const order = [];
4997
- while (stack.length > 0) {
4998
- const current = stack.pop();
4999
- order.push(current);
5000
- const children = Array.from(current.childNodes);
5001
- for (let i2 = 0; i2 < children.length; i2++) {
5002
- stack.push(children[i2]);
5003
- }
5004
- }
5005
- for (let i2 = order.length - 1; i2 >= 0; i2--) {
5006
- const current = order[i2];
5007
- const disposers = elementDisposers.get(current);
5008
- if (disposers) {
5009
- const snapshot = disposers.slice();
5010
- elementDisposers.delete(current);
5011
- if (_isDev5) activeBindingCount -= snapshot.length;
5012
- for (const d of snapshot) {
5013
- try {
5014
- d();
5015
- } catch (err) {
5016
- if (_isDev5 && typeof console !== "undefined") {
5017
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
5018
- }
5019
- }
5020
- }
5021
- let extraPasses = 0;
5022
- while (extraPasses++ < 8) {
5023
- const added = elementDisposers.get(current);
5024
- if (!added || added.length === 0) break;
5025
- const moreSnapshot = added.slice();
5026
- elementDisposers.delete(current);
5027
- if (_isDev5) activeBindingCount -= moreSnapshot.length;
5028
- for (const d of moreSnapshot) {
5029
- try {
5030
- d();
5031
- } catch (err) {
5032
- if (_isDev5 && typeof console !== "undefined") {
5033
- console.warn("[SibuJS] Disposer threw during cleanup:", err);
5034
- }
5035
- }
5036
- }
5037
- }
5038
- }
5039
- }
5040
- }
5041
-
5042
5118
  // src/ui/virtualList.ts
5043
5119
  function VirtualList(props) {
5044
5120
  const overscan = props.overscan ?? 3;
@@ -5195,7 +5271,6 @@ function inputMask(options) {
5195
5271
  return /[^a-zA-Z0-9]/g;
5196
5272
  }
5197
5273
  const stripRegex = buildStripRegex();
5198
- const rawCharTest = options.pattern.includes("*") ? () => true : (c) => /[a-zA-Z0-9]/.test(c);
5199
5274
  function bind(input2) {
5200
5275
  const onInput = () => {
5201
5276
  const cursorBefore = input2.selectionStart ?? input2.value.length;
@@ -5205,20 +5280,14 @@ function inputMask(options) {
5205
5280
  setValue(masked);
5206
5281
  setRawValue(extractRaw(masked));
5207
5282
  input2.value = masked;
5208
- let rawBefore = 0;
5209
- for (let i2 = 0; i2 < cursorBefore && i2 < oldValue.length; i2++) {
5210
- if (rawCharTest(oldValue[i2])) rawBefore++;
5211
- }
5283
+ const rawBefore = oldValue.slice(0, cursorBefore).replace(stripRegex, "").length;
5212
5284
  let newCursor = 0;
5213
5285
  let counted = 0;
5214
- for (; newCursor < masked.length; newCursor++) {
5286
+ while (newCursor < masked.length && counted < rawBefore) {
5215
5287
  if (newCursor < options.pattern.length && isSlot(options.pattern[newCursor])) {
5216
5288
  counted++;
5217
- if (counted >= rawBefore) {
5218
- newCursor++;
5219
- break;
5220
- }
5221
5289
  }
5290
+ newCursor++;
5222
5291
  }
5223
5292
  try {
5224
5293
  input2.setSelectionRange(newCursor, newCursor);
@@ -5417,14 +5486,12 @@ function hotkey(combo, handler, options = {}) {
5417
5486
  document.addEventListener("keydown", listener);
5418
5487
  return () => document.removeEventListener("keydown", listener);
5419
5488
  }
5420
- var announceQueues = {
5421
- polite: [],
5422
- assertive: []
5423
- };
5424
- var announceDraining = {
5425
- polite: false,
5426
- assertive: false
5427
- };
5489
+ var _announce = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.a11y.announce.v1"), () => ({
5490
+ queues: { polite: [], assertive: [] },
5491
+ draining: { polite: false, assertive: false }
5492
+ }));
5493
+ var announceQueues = _announce.queues;
5494
+ var announceDraining = _announce.draining;
5428
5495
  var ANNOUNCE_INTERVAL_MS = 150;
5429
5496
  function ensureLiveRegion(priority) {
5430
5497
  let region = document.getElementById(`sibu-announce-${priority}`);
@@ -5466,7 +5533,7 @@ function announce(message, priority = "polite") {
5466
5533
  }
5467
5534
 
5468
5535
  // src/ui/scopedStyle.ts
5469
- var scopeCounter = 0;
5536
+ var _scope = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scopedStyle.v1"), () => ({ n: 0 }));
5470
5537
  function decodeCssEscapes(css) {
5471
5538
  return css.replace(/\\([0-9a-f]{1,6})[ \t\n\r\f]?|\\([^\n])/gi, (_match, hex, ch) => {
5472
5539
  if (hex) {
@@ -5493,7 +5560,7 @@ function sanitizeCSS(css) {
5493
5560
  return sanitized;
5494
5561
  }
5495
5562
  function scopedStyle(css) {
5496
- const id = `sibu-s${scopeCounter++}`;
5563
+ const id = `sibu-s${_scope.n++}`;
5497
5564
  const attr = `data-${id}`;
5498
5565
  const safeCss = sanitizeCSS(css);
5499
5566
  const scopedCSS = safeCss.replace(/([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/g, (match, selector, delimiter) => {
@@ -5643,13 +5710,16 @@ function bindData(el, key, getter) {
5643
5710
  }
5644
5711
 
5645
5712
  // src/ui/dialog.ts
5646
- var dialogStack = [];
5647
- var globalListenerAttached = false;
5713
+ var _dlg = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.dialog.v1"), () => ({
5714
+ stack: [],
5715
+ listenerAttached: false
5716
+ }));
5717
+ var dialogStack = _dlg.stack;
5648
5718
  function __resetDialogStack() {
5649
5719
  while (dialogStack.length > 0) dialogStack.pop();
5650
- if (typeof window !== "undefined" && globalListenerAttached) {
5720
+ if (typeof window !== "undefined" && _dlg.listenerAttached) {
5651
5721
  window.removeEventListener("keydown", handleGlobalKeydown);
5652
- globalListenerAttached = false;
5722
+ _dlg.listenerAttached = false;
5653
5723
  }
5654
5724
  }
5655
5725
  function handleGlobalKeydown(event) {
@@ -5658,16 +5728,16 @@ function handleGlobalKeydown(event) {
5658
5728
  if (top) top.close();
5659
5729
  }
5660
5730
  function ensureGlobalListener() {
5661
- if (typeof window === "undefined" || globalListenerAttached) return;
5731
+ if (typeof window === "undefined" || _dlg.listenerAttached) return;
5662
5732
  window.addEventListener("keydown", handleGlobalKeydown);
5663
- globalListenerAttached = true;
5733
+ _dlg.listenerAttached = true;
5664
5734
  }
5665
5735
  function removeGlobalListenerIfIdle() {
5666
5736
  if (typeof window === "undefined") return;
5667
- if (!globalListenerAttached) return;
5737
+ if (!_dlg.listenerAttached) return;
5668
5738
  if (dialogStack.length > 0) return;
5669
5739
  window.removeEventListener("keydown", handleGlobalKeydown);
5670
- globalListenerAttached = false;
5740
+ _dlg.listenerAttached = false;
5671
5741
  }
5672
5742
  function dialog() {
5673
5743
  const [isOpen, setIsOpen] = signal(false);
@@ -5793,6 +5863,10 @@ function infiniteScroll(options) {
5793
5863
  await onLoadMore();
5794
5864
  } finally {
5795
5865
  setLoading(false);
5866
+ if (!disposed && observer && _current && hasMore()) {
5867
+ observer.unobserve(_current);
5868
+ observer.observe(_current);
5869
+ }
5796
5870
  }
5797
5871
  }
5798
5872
  const originalRef = sentinelRef;
@@ -5832,29 +5906,30 @@ function pagination(options) {
5832
5906
  const total = options.totalItems();
5833
5907
  return Math.max(1, Math.ceil(total / pageSizeValue));
5834
5908
  });
5909
+ const currentPage = derived(() => Math.min(Math.max(1, page()), totalPages()));
5835
5910
  const startIndex = derived(() => {
5836
- return (page() - 1) * pageSizeValue;
5911
+ return (currentPage() - 1) * pageSizeValue;
5837
5912
  });
5838
5913
  const endIndex = derived(() => {
5839
- const end = page() * pageSizeValue;
5914
+ const end = currentPage() * pageSizeValue;
5840
5915
  const total = options.totalItems();
5841
5916
  return Math.min(end, total);
5842
5917
  });
5843
5918
  function next() {
5844
- if (page() < totalPages()) {
5845
- setPage((p2) => p2 + 1);
5919
+ if (currentPage() < totalPages()) {
5920
+ setPage(currentPage() + 1);
5846
5921
  }
5847
5922
  }
5848
5923
  function prev() {
5849
- if (page() > 1) {
5850
- setPage((p2) => p2 - 1);
5924
+ if (currentPage() > 1) {
5925
+ setPage(currentPage() - 1);
5851
5926
  }
5852
5927
  }
5853
5928
  function goTo(target) {
5854
5929
  const clamped = Math.max(1, Math.min(target, totalPages()));
5855
5930
  setPage(clamped);
5856
5931
  }
5857
- return { page, pageSize, totalPages, next, prev, goTo, startIndex, endIndex };
5932
+ return { page: currentPage, pageSize, totalPages, next, prev, goTo, startIndex, endIndex };
5858
5933
  }
5859
5934
 
5860
5935
  // src/ui/eventBus.ts
@@ -5900,12 +5975,15 @@ var Priority = {
5900
5975
  LOW: 3,
5901
5976
  IDLE: 4
5902
5977
  };
5903
- var taskIdCounter = 0;
5904
- var taskQueue = [];
5905
- var isProcessing = false;
5906
- var scheduledKind = null;
5907
- var scheduledHandle = null;
5908
- var microtaskScheduled = false;
5978
+ var _sched = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.scheduler.v1"), () => ({
5979
+ taskIdCounter: 0,
5980
+ taskQueue: [],
5981
+ isProcessing: false,
5982
+ scheduledKind: null,
5983
+ scheduledHandle: null,
5984
+ microtaskScheduled: false
5985
+ }));
5986
+ var taskQueue = _sched.taskQueue;
5909
5987
  function insertTask(task) {
5910
5988
  let i2 = taskQueue.length;
5911
5989
  while (i2 > 0 && taskQueue[i2 - 1].priority > task.priority) {
@@ -5914,8 +5992,8 @@ function insertTask(task) {
5914
5992
  taskQueue.splice(i2, 0, task);
5915
5993
  }
5916
5994
  function processQueue() {
5917
- if (isProcessing || taskQueue.length === 0) return;
5918
- isProcessing = true;
5995
+ if (_sched.isProcessing || taskQueue.length === 0) return;
5996
+ _sched.isProcessing = true;
5919
5997
  const startTime = performance.now();
5920
5998
  const timeSlice = 5;
5921
5999
  while (taskQueue.length > 0) {
@@ -5934,64 +6012,69 @@ function processQueue() {
5934
6012
  console.error("[Scheduler] Task error:", e);
5935
6013
  }
5936
6014
  }
5937
- isProcessing = false;
6015
+ _sched.isProcessing = false;
5938
6016
  if (taskQueue.length > 0) {
5939
6017
  scheduleFrame();
5940
6018
  }
5941
6019
  }
5942
6020
  var TIER_SPEED = { microtask: 0, frame: 1, timeout: 2, idle: 3 };
5943
6021
  function cancelScheduled() {
5944
- if (scheduledHandle !== null) {
5945
- if (scheduledKind === "frame") cancelAnimationFrame(scheduledHandle);
5946
- else if (scheduledKind === "idle" && typeof cancelIdleCallback !== "undefined") cancelIdleCallback(scheduledHandle);
5947
- else if (scheduledKind === "timeout") clearTimeout(scheduledHandle);
6022
+ if (_sched.scheduledHandle !== null) {
6023
+ if (_sched.scheduledKind === "frame") cancelAnimationFrame(_sched.scheduledHandle);
6024
+ else if (_sched.scheduledKind === "idle" && typeof cancelIdleCallback !== "undefined")
6025
+ cancelIdleCallback(_sched.scheduledHandle);
6026
+ else if (_sched.scheduledKind === "timeout") clearTimeout(_sched.scheduledHandle);
5948
6027
  }
5949
- scheduledHandle = null;
5950
- scheduledKind = null;
6028
+ _sched.scheduledHandle = null;
6029
+ _sched.scheduledKind = null;
5951
6030
  }
5952
6031
  function scheduleFrame() {
5953
6032
  const nextTask = taskQueue.find((t) => !t.cancelled);
5954
6033
  if (!nextTask) return;
5955
- const desired = nextTask.priority <= Priority.USER_BLOCKING ? "microtask" : nextTask.priority === Priority.IDLE ? typeof requestIdleCallback !== "undefined" ? "idle" : "timeout" : "frame";
5956
- if (microtaskScheduled) return;
5957
- if (scheduledKind !== null) {
5958
- if (TIER_SPEED[scheduledKind] <= TIER_SPEED[desired]) return;
6034
+ const desired = nextTask.priority <= Priority.USER_BLOCKING ? "microtask" : nextTask.priority === Priority.IDLE ? typeof requestIdleCallback !== "undefined" ? "idle" : "timeout" : (
6035
+ // NORMAL/LOW prefer a frame, but rAF is absent under SSR — fall back
6036
+ // to a timeout so startTransition()/deferredValue() don't throw.
6037
+ typeof requestAnimationFrame !== "undefined" ? "frame" : "timeout"
6038
+ );
6039
+ if (_sched.microtaskScheduled) return;
6040
+ if (_sched.scheduledKind !== null) {
6041
+ if (TIER_SPEED[_sched.scheduledKind] <= TIER_SPEED[desired]) return;
5959
6042
  cancelScheduled();
5960
6043
  }
5961
6044
  if (desired === "microtask") {
5962
- microtaskScheduled = true;
5963
- scheduledKind = "microtask";
6045
+ _sched.microtaskScheduled = true;
6046
+ _sched.scheduledKind = "microtask";
5964
6047
  queueMicrotask(() => {
5965
- microtaskScheduled = false;
5966
- scheduledKind = null;
6048
+ _sched.microtaskScheduled = false;
6049
+ _sched.scheduledKind = null;
5967
6050
  processQueue();
5968
6051
  });
5969
6052
  } else if (desired === "idle") {
5970
- scheduledKind = "idle";
5971
- scheduledHandle = requestIdleCallback(() => {
5972
- scheduledKind = null;
5973
- scheduledHandle = null;
6053
+ _sched.scheduledKind = "idle";
6054
+ _sched.scheduledHandle = requestIdleCallback(() => {
6055
+ _sched.scheduledKind = null;
6056
+ _sched.scheduledHandle = null;
5974
6057
  processQueue();
5975
6058
  });
5976
6059
  } else if (desired === "timeout") {
5977
- scheduledKind = "timeout";
5978
- scheduledHandle = setTimeout(() => {
5979
- scheduledKind = null;
5980
- scheduledHandle = null;
6060
+ _sched.scheduledKind = "timeout";
6061
+ _sched.scheduledHandle = setTimeout(() => {
6062
+ _sched.scheduledKind = null;
6063
+ _sched.scheduledHandle = null;
5981
6064
  processQueue();
5982
6065
  }, 50);
5983
6066
  } else {
5984
- scheduledKind = "frame";
5985
- scheduledHandle = requestAnimationFrame(() => {
5986
- scheduledKind = null;
5987
- scheduledHandle = null;
6067
+ _sched.scheduledKind = "frame";
6068
+ _sched.scheduledHandle = requestAnimationFrame(() => {
6069
+ _sched.scheduledKind = null;
6070
+ _sched.scheduledHandle = null;
5988
6071
  processQueue();
5989
6072
  });
5990
6073
  }
5991
6074
  }
5992
6075
  function scheduleUpdate(priority, callback) {
5993
6076
  const task = {
5994
- id: taskIdCounter++,
6077
+ id: _sched.taskIdCounter++,
5995
6078
  priority,
5996
6079
  callback,
5997
6080
  cancelled: false
@@ -6012,15 +6095,15 @@ function scheduleUpdate(priority, callback) {
6012
6095
  };
6013
6096
  }
6014
6097
  function flushScheduler() {
6015
- if (scheduledHandle !== null) {
6016
- if (scheduledKind === "frame") cancelAnimationFrame(scheduledHandle);
6017
- else if (scheduledKind === "idle" && typeof cancelIdleCallback !== "undefined") {
6018
- cancelIdleCallback(scheduledHandle);
6019
- } else if (scheduledKind === "timeout") clearTimeout(scheduledHandle);
6020
- }
6021
- scheduledHandle = null;
6022
- scheduledKind = null;
6023
- microtaskScheduled = false;
6098
+ if (_sched.scheduledHandle !== null) {
6099
+ if (_sched.scheduledKind === "frame") cancelAnimationFrame(_sched.scheduledHandle);
6100
+ else if (_sched.scheduledKind === "idle" && typeof cancelIdleCallback !== "undefined") {
6101
+ cancelIdleCallback(_sched.scheduledHandle);
6102
+ } else if (_sched.scheduledKind === "timeout") clearTimeout(_sched.scheduledHandle);
6103
+ }
6104
+ _sched.scheduledHandle = null;
6105
+ _sched.scheduledKind = null;
6106
+ _sched.microtaskScheduled = false;
6024
6107
  while (taskQueue.length > 0) {
6025
6108
  const task = taskQueue.shift();
6026
6109
  if (!task) break;
@@ -6028,7 +6111,7 @@ function flushScheduler() {
6028
6111
  task.callback();
6029
6112
  }
6030
6113
  }
6031
- isProcessing = false;
6114
+ _sched.isProcessing = false;
6032
6115
  }
6033
6116
  function pendingTasks() {
6034
6117
  return taskQueue.filter((t) => !t.cancelled).length;
@@ -6076,16 +6159,18 @@ function transitionState() {
6076
6159
  }
6077
6160
  return [isPending, transition2];
6078
6161
  }
6079
- var idCounter = 0;
6080
- var idPrefix = "sibu";
6162
+ var _ids = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.uniqueId.v1"), () => ({
6163
+ counter: 0,
6164
+ prefix: "sibu"
6165
+ }));
6081
6166
  function resetIdCounter() {
6082
- idCounter = 0;
6167
+ _ids.counter = 0;
6083
6168
  }
6084
6169
  function setIdPrefix(prefix) {
6085
- idPrefix = prefix;
6170
+ _ids.prefix = prefix;
6086
6171
  }
6087
6172
  function uniqueId(suffix) {
6088
- const id = `${idPrefix}-${idCounter++}`;
6173
+ const id = `${_ids.prefix}-${_ids.counter++}`;
6089
6174
  return suffix ? `${id}-${suffix}` : id;
6090
6175
  }
6091
6176
 
@@ -6833,7 +6918,6 @@ function normalizedStore(schema) {
6833
6918
  }
6834
6919
  function normalize(data2, schema) {
6835
6920
  const entities = {};
6836
- const idKey = schema.idKey || "id";
6837
6921
  function ensureTable(name) {
6838
6922
  if (!entities[name]) {
6839
6923
  entities[name] = {};
@@ -6849,7 +6933,7 @@ function normalize(data2, schema) {
6849
6933
  for (const [field, relationType] of Object.entries(entitySchema.relations)) {
6850
6934
  const value = entity[field];
6851
6935
  if (value == null) continue;
6852
- const relSchema = { name: relationType, idKey };
6936
+ const relSchema = { name: relationType };
6853
6937
  if (Array.isArray(value)) {
6854
6938
  flat[field] = value.map((item) => normalizeEntity(item, relSchema));
6855
6939
  } else if (typeof value === "object") {
@@ -6872,13 +6956,12 @@ function denormalize(id, entities, schema) {
6872
6956
  if (!table2) return void 0;
6873
6957
  const entity = table2[id];
6874
6958
  if (!entity) return void 0;
6875
- const idKey = schema.idKey || "id";
6876
6959
  const result = { ...entity };
6877
6960
  if (schema.relations) {
6878
6961
  for (const [field, relationType] of Object.entries(schema.relations)) {
6879
6962
  const value = entity[field];
6880
6963
  if (value == null) continue;
6881
- const relSchema = { name: relationType, idKey };
6964
+ const relSchema = { name: relationType };
6882
6965
  if (Array.isArray(value)) {
6883
6966
  result[field] = value.map((relId) => denormalize(relId, entities, relSchema));
6884
6967
  } else if (typeof value === "string") {
@@ -7211,6 +7294,7 @@ function Head(props) {
7211
7294
  }
7212
7295
  };
7213
7296
  apply();
7297
+ registerDisposer(anchor, cleanup3);
7214
7298
  return anchor;
7215
7299
  }
7216
7300
  function setStructuredData(data2) {
@@ -7525,8 +7609,14 @@ function createWorkerPool(workerFn2, poolSize) {
7525
7609
  }
7526
7610
 
7527
7611
  // src/platform/wasm.ts
7528
- var moduleCache = /* @__PURE__ */ new Map();
7529
- var instanceCache = /* @__PURE__ */ new Map();
7612
+ var moduleCache = globalSingleton(
7613
+ /* @__PURE__ */ Symbol.for("sibujs.wasm.moduleCache.v1"),
7614
+ () => /* @__PURE__ */ new Map()
7615
+ );
7616
+ var instanceCache = globalSingleton(
7617
+ /* @__PURE__ */ Symbol.for("sibujs.wasm.instanceCache.v1"),
7618
+ () => /* @__PURE__ */ new Map()
7619
+ );
7530
7620
  function wasm(source2, config = {}) {
7531
7621
  const [instance, setInstance] = signal(null);
7532
7622
  const [loading, setLoading] = signal(true);
@@ -8156,7 +8246,10 @@ var font = tagFactory("font");
8156
8246
  var marquee = tagFactory("marquee");
8157
8247
 
8158
8248
  // src/platform/microfrontend.ts
8159
- var moduleCache2 = /* @__PURE__ */ new Map();
8249
+ var moduleCache2 = globalSingleton(
8250
+ /* @__PURE__ */ Symbol.for("sibujs.microfrontend.moduleCache.v1"),
8251
+ () => /* @__PURE__ */ new Map()
8252
+ );
8160
8253
  function createMicroApp(config) {
8161
8254
  const host = config.container ?? document.createElement("div");
8162
8255
  host.setAttribute("data-micro-app", config.name);
@@ -8427,24 +8520,30 @@ function createAction(actionFn) {
8427
8520
  const [data2, setData] = signal(void 0);
8428
8521
  const [error, setError] = signal(void 0);
8429
8522
  const [loading, setLoading] = signal(false);
8523
+ let activeRun = 0;
8430
8524
  const submit = async (input2) => {
8525
+ const runId = ++activeRun;
8431
8526
  batch(() => {
8432
8527
  setLoading(true);
8433
8528
  setError(void 0);
8434
8529
  });
8435
8530
  try {
8436
8531
  const result = await actionFn(input2);
8437
- batch(() => {
8438
- setData(result);
8439
- setLoading(false);
8440
- });
8532
+ if (runId === activeRun) {
8533
+ batch(() => {
8534
+ setData(result);
8535
+ setLoading(false);
8536
+ });
8537
+ }
8441
8538
  return result;
8442
8539
  } catch (err) {
8443
8540
  const actionError = err instanceof Error ? err : new Error(String(err));
8444
- batch(() => {
8445
- setError(actionError);
8446
- setLoading(false);
8447
- });
8541
+ if (runId === activeRun) {
8542
+ batch(() => {
8543
+ setError(actionError);
8544
+ setLoading(false);
8545
+ });
8546
+ }
8448
8547
  throw actionError;
8449
8548
  }
8450
8549
  };
@@ -8458,15 +8557,14 @@ function scrollRestoration(options) {
8458
8557
  let popstateHandler = null;
8459
8558
  let currentKey = null;
8460
8559
  const save = (key) => {
8461
- positions.set(key, {
8462
- x: window.scrollX,
8463
- y: window.scrollY
8464
- });
8560
+ if (typeof window !== "undefined") {
8561
+ positions.set(key, { x: window.scrollX, y: window.scrollY });
8562
+ }
8465
8563
  currentKey = key;
8466
8564
  };
8467
8565
  const restore = (key) => {
8468
8566
  const pos = positions.get(key);
8469
- if (pos) {
8567
+ if (pos && typeof window !== "undefined") {
8470
8568
  window.scrollTo(pos.x, pos.y);
8471
8569
  }
8472
8570
  currentKey = key;
@@ -8474,7 +8572,7 @@ function scrollRestoration(options) {
8474
8572
  const getPosition = (key) => {
8475
8573
  return positions.get(key);
8476
8574
  };
8477
- if (mode === "auto") {
8575
+ if (mode === "auto" && typeof window !== "undefined") {
8478
8576
  popstateHandler = () => {
8479
8577
  if (currentKey) {
8480
8578
  save(currentKey);
@@ -8602,39 +8700,41 @@ function createPluginRegistry() {
8602
8700
  };
8603
8701
  return registry;
8604
8702
  }
8605
- var defaultRegistry = createPluginRegistry();
8606
- var defaultRegistryTouched = false;
8703
+ var _defaults = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.plugins.defaultRegistry.v1"), () => ({
8704
+ registry: createPluginRegistry(),
8705
+ touched: false
8706
+ }));
8607
8707
  function createPlugin(name, install) {
8608
8708
  return { name, install };
8609
8709
  }
8610
8710
  function plugin(plugin2, options) {
8611
- defaultRegistryTouched = true;
8612
- defaultRegistry.plugin(plugin2, options);
8711
+ _defaults.touched = true;
8712
+ _defaults.registry.plugin(plugin2, options);
8613
8713
  }
8614
8714
  function inject(key, defaultValue) {
8615
- return defaultRegistry.inject(key, defaultValue);
8715
+ return _defaults.registry.inject(key, defaultValue);
8616
8716
  }
8617
8717
  function triggerPluginMount(element) {
8618
- defaultRegistry.triggerMount(element);
8718
+ _defaults.registry.triggerMount(element);
8619
8719
  }
8620
8720
  function triggerPluginUnmount(element) {
8621
- defaultRegistry.triggerUnmount(element);
8721
+ _defaults.registry.triggerUnmount(element);
8622
8722
  }
8623
8723
  function triggerPluginError(error) {
8624
- defaultRegistry.triggerError(error);
8724
+ _defaults.registry.triggerError(error);
8625
8725
  }
8626
8726
  function resetPlugins() {
8627
- defaultRegistry.reset();
8628
- defaultRegistryTouched = false;
8727
+ _defaults.registry.reset();
8728
+ _defaults.touched = false;
8629
8729
  }
8630
8730
  function setDefaultPluginRegistry(registry) {
8631
- if (defaultRegistryTouched && defaultRegistry.installedPlugins.size > 0) {
8731
+ if (_defaults.touched && _defaults.registry.installedPlugins.size > 0) {
8632
8732
  console.warn(
8633
8733
  "[Plugin] Replacing default plugin registry while plugins are already installed on the singleton. This may indicate mixed singleton/registry usage."
8634
8734
  );
8635
8735
  }
8636
- defaultRegistry = registry;
8637
- defaultRegistryTouched = true;
8736
+ _defaults.registry = registry;
8737
+ _defaults.touched = true;
8638
8738
  }
8639
8739
 
8640
8740
  // src/plugins/modular.ts
@@ -8736,9 +8836,7 @@ function lazyModule(loader) {
8736
8836
  return loadedFlag;
8737
8837
  },
8738
8838
  async get() {
8739
- if (loadedFlag && cached !== void 0) {
8740
- return cached;
8741
- }
8839
+ if (loadedFlag) return cached;
8742
8840
  cached = await loader();
8743
8841
  loadedFlag = true;
8744
8842
  return cached;
@@ -9466,10 +9564,50 @@ function createGlobalHook() {
9466
9564
  listeners.set(event, set);
9467
9565
  }
9468
9566
  set.add(fn);
9567
+ return () => {
9568
+ set?.delete(fn);
9569
+ };
9469
9570
  },
9470
9571
  off(event, fn) {
9471
9572
  listeners.get(event)?.delete(fn);
9472
9573
  },
9574
+ getSignalNodes() {
9575
+ const out = [];
9576
+ for (const [, node] of nodes) {
9577
+ let value = "";
9578
+ try {
9579
+ const ref = node.ref;
9580
+ let raw;
9581
+ if (node.type === "effect") {
9582
+ raw = void 0;
9583
+ } else if (node.type === "computed" && ref) {
9584
+ raw = ref._v;
9585
+ } else if (ref && "value" in ref) {
9586
+ raw = ref.value;
9587
+ }
9588
+ if (raw === void 0) value = "undefined";
9589
+ else if (raw === null) value = "null";
9590
+ else if (typeof raw === "object") value = JSON.stringify(raw);
9591
+ else value = String(raw);
9592
+ } catch {
9593
+ value = "?";
9594
+ }
9595
+ if (value.length > 200) value = `${value.slice(0, 200)}...`;
9596
+ out.push({
9597
+ id: String(node.id),
9598
+ name: node.name || null,
9599
+ kind: node.type === "computed" ? "derived" : node.type,
9600
+ value,
9601
+ // Edge identities aren't tracked by this registry (only a subscriber
9602
+ // count, via node.ref.__sc) — left empty until the core exposes the
9603
+ // dependency graph. The node inventory itself is real.
9604
+ subscribers: [],
9605
+ dependencies: [],
9606
+ evalCount: 0
9607
+ });
9608
+ }
9609
+ return out;
9610
+ },
9473
9611
  emit(event, payload) {
9474
9612
  events.push({ event, payload, ts: Date.now() });
9475
9613
  if (events.length > 2e3) events.splice(0, events.length - 2e3);
@@ -9495,34 +9633,38 @@ function getOrCreateHook() {
9495
9633
  }
9496
9634
  return g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
9497
9635
  }
9498
- var activeDevTools = null;
9499
- var nextNodeId = 0;
9500
- var inferNameArmed = false;
9636
+ var _dt = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.devtools.state.v1"), () => ({
9637
+ active: null,
9638
+ nextNodeId: 0,
9639
+ // Gate for inferName(): only `true` while devtools are initialized AND in dev
9640
+ // mode — otherwise skip the Error-stack walk so prod hot paths don't allocate.
9641
+ inferNameArmed: false
9642
+ }));
9501
9643
  function getActiveDevTools() {
9502
- return activeDevTools;
9644
+ return _dt.active;
9503
9645
  }
9504
9646
  function initDevTools(config) {
9505
9647
  const maxEvents = config?.maxEvents ?? 1e3;
9506
9648
  const enabled = config?.enabled ?? isDev();
9507
9649
  const expose = config?.expose ?? isDev();
9508
9650
  if (!enabled) return createNoopApi();
9509
- inferNameArmed = true;
9651
+ _dt.inferNameArmed = true;
9510
9652
  const hook = getOrCreateHook();
9511
- nextNodeId = 0;
9653
+ _dt.nextNodeId = 0;
9512
9654
  const eventLog = [];
9513
9655
  hook.on("signal:create", (payload) => {
9514
9656
  const p2 = payload;
9515
- nextNodeId++;
9657
+ _dt.nextNodeId++;
9516
9658
  const name = inferName();
9517
9659
  const node = {
9518
- id: nextNodeId,
9660
+ id: _dt.nextNodeId,
9519
9661
  type: "signal",
9520
9662
  name,
9521
9663
  ref: p2.signal,
9522
9664
  getter: p2.getter,
9523
9665
  createdAt: Date.now()
9524
9666
  };
9525
- hook.nodes.set(nextNodeId, node);
9667
+ hook.nodes.set(_dt.nextNodeId, node);
9526
9668
  emit();
9527
9669
  });
9528
9670
  hook.on("signal:update", (payload) => {
@@ -9542,17 +9684,17 @@ function initDevTools(config) {
9542
9684
  });
9543
9685
  hook.on("computed:create", (payload) => {
9544
9686
  const p2 = payload;
9545
- nextNodeId++;
9687
+ _dt.nextNodeId++;
9546
9688
  const name = inferName();
9547
9689
  const node = {
9548
- id: nextNodeId,
9690
+ id: _dt.nextNodeId,
9549
9691
  type: "computed",
9550
9692
  name,
9551
9693
  ref: p2.signal,
9552
9694
  getter: p2.getter,
9553
9695
  createdAt: Date.now()
9554
9696
  };
9555
- hook.nodes.set(nextNodeId, node);
9697
+ hook.nodes.set(_dt.nextNodeId, node);
9556
9698
  emit();
9557
9699
  });
9558
9700
  hook.on("computed:update", (payload) => {
@@ -9569,11 +9711,11 @@ function initDevTools(config) {
9569
9711
  emit();
9570
9712
  });
9571
9713
  hook.on("effect:create", (payload) => {
9572
- nextNodeId++;
9714
+ _dt.nextNodeId++;
9573
9715
  const name = inferName();
9574
9716
  const p2 = payload;
9575
- const node = { id: nextNodeId, type: "effect", name, ref: p2.effectFn, createdAt: Date.now() };
9576
- hook.nodes.set(nextNodeId, node);
9717
+ const node = { id: _dt.nextNodeId, type: "effect", name, ref: p2.effectFn, createdAt: Date.now() };
9718
+ hook.nodes.set(_dt.nextNodeId, node);
9577
9719
  emit();
9578
9720
  });
9579
9721
  hook.on("effect:run", (payload) => {
@@ -9730,8 +9872,8 @@ function initDevTools(config) {
9730
9872
  hook.components.clear();
9731
9873
  hook.events.length = 0;
9732
9874
  isActive = false;
9733
- inferNameArmed = false;
9734
- activeDevTools = null;
9875
+ _dt.inferNameArmed = false;
9876
+ _dt.active = null;
9735
9877
  const g2 = globalThis;
9736
9878
  if (g2.__SIBU__) {
9737
9879
  delete g2.__SIBU__.devtools;
@@ -9899,7 +10041,7 @@ function initDevTools(config) {
9899
10041
  w.__SIBU_DEVTOOLS_VERSION__ = () => changeVersion;
9900
10042
  w.__SIBU_DEVTOOLS_DATA__ = buildData;
9901
10043
  }
9902
- activeDevTools = api;
10044
+ _dt.active = api;
9903
10045
  if (typeof document !== "undefined") {
9904
10046
  domObserver = new MutationObserver((mutations) => {
9905
10047
  let changed = false;
@@ -9975,7 +10117,7 @@ function initDevTools(config) {
9975
10117
  return api;
9976
10118
  }
9977
10119
  function inferName() {
9978
- if (!inferNameArmed || !isDev()) return "anonymous";
10120
+ if (!_dt.inferNameArmed || !isDev()) return "anonymous";
9979
10121
  try {
9980
10122
  const stack = new Error().stack || "";
9981
10123
  for (const line2 of stack.split("\n")) {
@@ -10056,7 +10198,7 @@ function devState(name, initial) {
10056
10198
  const oldValue = get();
10057
10199
  set(next);
10058
10200
  const newValue = get();
10059
- const dt2 = activeDevTools;
10201
+ const dt2 = _dt.active;
10060
10202
  if (dt2?.isEnabled() && !Object.is(oldValue, newValue)) {
10061
10203
  dt2.record({ type: "state-change", component, key, oldValue, newValue, timestamp: Date.now() });
10062
10204
  }
@@ -10507,6 +10649,14 @@ function createDevtoolsOverlay(options) {
10507
10649
  return { isEnabled, toggle, addPanel, removePanel, getPanels, dispose: dispose2 };
10508
10650
  }
10509
10651
 
10652
+ // src/core/rendering/createId.ts
10653
+ var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
10654
+ var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
10655
+ function createId(prefix = "sibu") {
10656
+ _counter.n++;
10657
+ return `${prefix}-${_counter.n}`;
10658
+ }
10659
+
10510
10660
  // src/core/signals/watch.ts
10511
10661
  function watch(getter, callback) {
10512
10662
  devAssert(typeof getter === "function", "watch: first argument must be a getter function.");
@@ -10538,7 +10688,6 @@ function watch(getter, callback) {
10538
10688
  }
10539
10689
 
10540
10690
  // src/widgets/Combobox.ts
10541
- var comboboxIdCounter = 0;
10542
10691
  var boundComboboxes = /* @__PURE__ */ new WeakMap();
10543
10692
  function combobox(options) {
10544
10693
  const { items, filterFn, itemToString } = options;
@@ -10599,7 +10748,13 @@ function combobox(options) {
10599
10748
  function bind(els) {
10600
10749
  const existing = boundComboboxes.get(els.input);
10601
10750
  if (existing) return existing;
10602
- const listboxId = `sibu-combobox-listbox-${++comboboxIdCounter}`;
10751
+ const prevListboxId = els.listbox.id;
10752
+ const prevListboxRole = els.listbox.getAttribute("role");
10753
+ const prevListboxHidden = els.listbox.hidden;
10754
+ const prevInputRole = els.input.getAttribute("role");
10755
+ const prevInputAutocomplete = els.input.getAttribute("aria-autocomplete");
10756
+ const prevInputControls = els.input.getAttribute("aria-controls");
10757
+ const listboxId = createId("sibu-combobox-listbox");
10603
10758
  els.listbox.id = listboxId;
10604
10759
  els.listbox.setAttribute("role", "listbox");
10605
10760
  els.input.setAttribute("role", "combobox");
@@ -10663,10 +10818,14 @@ function combobox(options) {
10663
10818
  if (document.activeElement !== els.input) close();
10664
10819
  }, 100);
10665
10820
  };
10821
+ const onListboxPointerDown = (e) => {
10822
+ e.preventDefault();
10823
+ };
10666
10824
  els.input.addEventListener("input", onInput);
10667
10825
  els.input.addEventListener("keydown", onKey);
10668
10826
  els.input.addEventListener("focus", onFocus);
10669
10827
  els.input.addEventListener("blur", onBlur);
10828
+ els.listbox.addEventListener("mousedown", onListboxPointerDown);
10670
10829
  const teardown = () => {
10671
10830
  boundComboboxes.delete(els.input);
10672
10831
  fxTeardown();
@@ -10674,10 +10833,24 @@ function combobox(options) {
10674
10833
  els.input.removeEventListener("keydown", onKey);
10675
10834
  els.input.removeEventListener("focus", onFocus);
10676
10835
  els.input.removeEventListener("blur", onBlur);
10836
+ els.listbox.removeEventListener("mousedown", onListboxPointerDown);
10677
10837
  if (blurTimer !== null) {
10678
10838
  clearTimeout(blurTimer);
10679
10839
  blurTimer = null;
10680
10840
  }
10841
+ if (prevListboxId === "") els.listbox.removeAttribute("id");
10842
+ else els.listbox.id = prevListboxId;
10843
+ if (prevListboxRole === null) els.listbox.removeAttribute("role");
10844
+ else els.listbox.setAttribute("role", prevListboxRole);
10845
+ els.listbox.hidden = prevListboxHidden;
10846
+ if (prevInputRole === null) els.input.removeAttribute("role");
10847
+ else els.input.setAttribute("role", prevInputRole);
10848
+ if (prevInputAutocomplete === null) els.input.removeAttribute("aria-autocomplete");
10849
+ else els.input.setAttribute("aria-autocomplete", prevInputAutocomplete);
10850
+ if (prevInputControls === null) els.input.removeAttribute("aria-controls");
10851
+ else els.input.setAttribute("aria-controls", prevInputControls);
10852
+ els.input.removeAttribute("aria-expanded");
10853
+ els.input.removeAttribute("aria-activedescendant");
10681
10854
  };
10682
10855
  boundComboboxes.set(els.input, teardown);
10683
10856
  return teardown;
@@ -11013,7 +11186,6 @@ function accordion(options) {
11013
11186
  }
11014
11187
 
11015
11188
  // src/widgets/Popover.ts
11016
- var popoverIdCounter = 0;
11017
11189
  var boundPopovers = /* @__PURE__ */ new WeakMap();
11018
11190
  function popover() {
11019
11191
  const [isOpen, setIsOpen] = signal(false);
@@ -11029,7 +11201,7 @@ function popover() {
11029
11201
  function bind(els) {
11030
11202
  const existing = boundPopovers.get(els.trigger);
11031
11203
  if (existing) return existing;
11032
- const id = `sibu-popover-${++popoverIdCounter}`;
11204
+ const id = createId("sibu-popover");
11033
11205
  const prevPopoverRole = els.popover.getAttribute("role");
11034
11206
  const prevPopoverId = els.popover.id;
11035
11207
  const prevLabelledBy = els.popover.getAttribute("aria-labelledby");
@@ -11103,7 +11275,6 @@ function popover() {
11103
11275
  }
11104
11276
 
11105
11277
  // src/widgets/Select.ts
11106
- var selectIdCounter = 0;
11107
11278
  var boundSelects = /* @__PURE__ */ new WeakMap();
11108
11279
  function select2(options) {
11109
11280
  const { items, multiple = false, itemToString, isDisabled } = options;
@@ -11184,7 +11355,11 @@ function select2(options) {
11184
11355
  function bind(els) {
11185
11356
  const existing = boundSelects.get(els.listbox);
11186
11357
  if (existing) return existing;
11187
- const listboxId = `sibu-select-${++selectIdCounter}`;
11358
+ const prevId = els.listbox.id;
11359
+ const prevRole = els.listbox.getAttribute("role");
11360
+ const prevMultiselectable = els.listbox.getAttribute("aria-multiselectable");
11361
+ const prevTabIndex = els.listbox.getAttribute("tabindex");
11362
+ const listboxId = createId("sibu-select");
11188
11363
  els.listbox.id = listboxId;
11189
11364
  els.listbox.setAttribute("role", "listbox");
11190
11365
  els.listbox.setAttribute("aria-multiselectable", multiple ? "true" : "false");
@@ -11244,6 +11419,15 @@ function select2(options) {
11244
11419
  fxTeardown();
11245
11420
  els.listbox.removeEventListener("keydown", onKey);
11246
11421
  if (typeTimer !== null) clearTimeout(typeTimer);
11422
+ if (prevId === "") els.listbox.removeAttribute("id");
11423
+ else els.listbox.id = prevId;
11424
+ if (prevRole === null) els.listbox.removeAttribute("role");
11425
+ else els.listbox.setAttribute("role", prevRole);
11426
+ if (prevMultiselectable === null) els.listbox.removeAttribute("aria-multiselectable");
11427
+ else els.listbox.setAttribute("aria-multiselectable", prevMultiselectable);
11428
+ if (prevTabIndex === null) els.listbox.removeAttribute("tabindex");
11429
+ else els.listbox.setAttribute("tabindex", prevTabIndex);
11430
+ els.listbox.removeAttribute("aria-activedescendant");
11247
11431
  };
11248
11432
  boundSelects.set(els.listbox, teardown);
11249
11433
  return teardown;
@@ -11268,7 +11452,6 @@ function select2(options) {
11268
11452
  }
11269
11453
 
11270
11454
  // src/widgets/Tooltip.ts
11271
- var tooltipIdCounter = 0;
11272
11455
  var boundTriggers = /* @__PURE__ */ new WeakMap();
11273
11456
  function tooltip(options) {
11274
11457
  const delay = options?.delay ?? 0;
@@ -11313,7 +11496,7 @@ function tooltip(options) {
11313
11496
  function bind(els) {
11314
11497
  const existing = boundTriggers.get(els.trigger);
11315
11498
  if (existing) return existing;
11316
- const id = `sibu-tooltip-${++tooltipIdCounter}`;
11499
+ const id = createId("sibu-tooltip");
11317
11500
  els.tooltip.setAttribute("role", "tooltip");
11318
11501
  els.tooltip.id = id;
11319
11502
  const prevDescribedBy = els.trigger.getAttribute("aria-describedby");
@@ -11373,7 +11556,6 @@ function tooltip(options) {
11373
11556
  }
11374
11557
 
11375
11558
  // src/widgets/FileUpload.ts
11376
- var fileUploadIdCounter = 0;
11377
11559
  var boundFileUploads = /* @__PURE__ */ new WeakMap();
11378
11560
  function fileUpload(options) {
11379
11561
  const accept = options?.accept;
@@ -11445,7 +11627,7 @@ function fileUpload(options) {
11445
11627
  function bind(els) {
11446
11628
  const existing = boundFileUploads.get(els.input);
11447
11629
  if (existing) return existing;
11448
- const id = `sibu-fileupload-${++fileUploadIdCounter}`;
11630
+ const id = createId("sibu-fileupload");
11449
11631
  const restore = [];
11450
11632
  if (accept) els.input.accept = accept;
11451
11633
  els.input.multiple = multiple;
@@ -11556,7 +11738,7 @@ function fileUpload(options) {
11556
11738
  }
11557
11739
 
11558
11740
  // src/widgets/contentEditable.ts
11559
- function contentEditable() {
11741
+ function contentEditable(element) {
11560
11742
  const [content, setContentInternal] = signal("");
11561
11743
  const [isFocused, setFocused] = signal(false);
11562
11744
  function setContent(input2) {
@@ -11580,6 +11762,7 @@ function contentEditable() {
11580
11762
  const selection = window.getSelection();
11581
11763
  if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
11582
11764
  const range = selection.getRangeAt(0);
11765
+ if (element && !element.contains(range.commonAncestorContainer)) return;
11583
11766
  const ancestor = range.commonAncestorContainer;
11584
11767
  const existingWrap = findAncestorByTag(
11585
11768
  ancestor instanceof HTMLElement ? ancestor : ancestor.parentElement,
@@ -11759,12 +11942,18 @@ function datePicker(options) {
11759
11942
  function bind(els) {
11760
11943
  const existing = boundDatePickers.get(els.grid);
11761
11944
  if (existing) return existing;
11945
+ const prevGridRole = els.grid.getAttribute("role");
11946
+ const prevGridTabIndex = els.grid.getAttribute("tabindex");
11947
+ const prevGridLabel = els.grid.getAttribute("aria-label");
11762
11948
  els.grid.setAttribute("role", "grid");
11763
11949
  if (els.grid.tabIndex < 0) els.grid.tabIndex = 0;
11950
+ let pendingFocus = false;
11764
11951
  const fxTeardown = effect(() => {
11765
11952
  const sel = selectedDate();
11766
11953
  const view = viewDate();
11767
11954
  const days = daysInMonth();
11955
+ els.grid.setAttribute("aria-label", view.toLocaleDateString(void 0, { month: "long", year: "numeric" }));
11956
+ let viewCell = null;
11768
11957
  for (const d of days) {
11769
11958
  const cell = els.cell(d.date);
11770
11959
  if (!cell) continue;
@@ -11772,7 +11961,13 @@ function datePicker(options) {
11772
11961
  cell.setAttribute("aria-selected", sel && isSameCalendarDay(sel, d.date) ? "true" : "false");
11773
11962
  if (d.isDisabled) cell.setAttribute("aria-disabled", "true");
11774
11963
  else cell.removeAttribute("aria-disabled");
11775
- cell.tabIndex = isSameCalendarDay(view, d.date) ? 0 : -1;
11964
+ const isView = isSameCalendarDay(view, d.date);
11965
+ cell.tabIndex = isView ? 0 : -1;
11966
+ if (isView) viewCell = cell;
11967
+ }
11968
+ if (pendingFocus && viewCell && typeof viewCell.focus === "function") {
11969
+ pendingFocus = false;
11970
+ viewCell.focus();
11776
11971
  }
11777
11972
  });
11778
11973
  function isSameCalendarDay(a2, b2) {
@@ -11782,6 +11977,9 @@ function datePicker(options) {
11782
11977
  setViewDate((prev) => new Date(prev.getFullYear(), prev.getMonth(), prev.getDate() + delta));
11783
11978
  }
11784
11979
  const onKey = (e) => {
11980
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Home" || e.key === "End" || e.key === "PageUp" || e.key === "PageDown") {
11981
+ pendingFocus = true;
11982
+ }
11785
11983
  switch (e.key) {
11786
11984
  case "ArrowLeft":
11787
11985
  e.preventDefault();
@@ -11830,6 +12028,12 @@ function datePicker(options) {
11830
12028
  boundDatePickers.delete(els.grid);
11831
12029
  fxTeardown();
11832
12030
  els.grid.removeEventListener("keydown", onKey);
12031
+ if (prevGridRole === null) els.grid.removeAttribute("role");
12032
+ else els.grid.setAttribute("role", prevGridRole);
12033
+ if (prevGridTabIndex === null) els.grid.removeAttribute("tabindex");
12034
+ else els.grid.setAttribute("tabindex", prevGridTabIndex);
12035
+ if (prevGridLabel === null) els.grid.removeAttribute("aria-label");
12036
+ else els.grid.setAttribute("aria-label", prevGridLabel);
11833
12037
  };
11834
12038
  boundDatePickers.set(els.grid, teardown);
11835
12039
  return teardown;
@@ -12378,6 +12582,7 @@ var materialAdapter = componentAdapter(materialConfig);
12378
12582
  pointerLock,
12379
12583
  popover,
12380
12584
  precompile,
12585
+ prefersReducedMotion,
12381
12586
  prefetch,
12382
12587
  preloadCritical,
12383
12588
  preloadImage,