sibujs 4.4.0 → 4.6.0

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 (112) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +339 -130
  3. package/dist/browser.d.cts +61 -16
  4. package/dist/browser.d.ts +61 -16
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +273 -82
  7. package/dist/build.js +15 -16
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  13. package/dist/chunk-3ISI6ACU.js +44 -0
  14. package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
  15. package/dist/chunk-5HZXGZ6T.js +24 -0
  16. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  17. package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
  18. package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
  19. package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
  22. package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
  23. package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
  24. package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
  25. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  26. package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
  31. package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
  32. package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
  33. package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
  34. package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
  35. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  36. package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
  37. package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
  38. package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
  39. package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
  40. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  41. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  42. package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
  43. package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
  44. package/dist/data.cjs +293 -125
  45. package/dist/data.d.cts +47 -9
  46. package/dist/data.d.ts +47 -9
  47. package/dist/data.js +12 -9
  48. package/dist/devtools.cjs +121 -58
  49. package/dist/devtools.js +7 -8
  50. package/dist/dispose-GEIG2KOF.js +28 -0
  51. package/dist/ecosystem.cjs +391 -112
  52. package/dist/ecosystem.d.cts +31 -7
  53. package/dist/ecosystem.d.ts +31 -7
  54. package/dist/ecosystem.js +12 -12
  55. package/dist/extras.cjs +2250 -879
  56. package/dist/extras.d.cts +10 -9
  57. package/dist/extras.d.ts +10 -9
  58. package/dist/extras.js +39 -28
  59. package/dist/index.cjs +273 -82
  60. package/dist/index.d.cts +44 -154
  61. package/dist/index.d.ts +44 -154
  62. package/dist/index.js +24 -27
  63. package/dist/motion.cjs +137 -42
  64. package/dist/motion.js +5 -5
  65. package/dist/patterns.cjs +397 -48
  66. package/dist/patterns.d.cts +32 -9
  67. package/dist/patterns.d.ts +32 -9
  68. package/dist/patterns.js +8 -8
  69. package/dist/performance.cjs +338 -217
  70. package/dist/performance.d.cts +2 -2
  71. package/dist/performance.d.ts +2 -2
  72. package/dist/performance.js +8 -9
  73. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  74. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  75. package/dist/plugins.cjs +587 -203
  76. package/dist/plugins.d.cts +3 -3
  77. package/dist/plugins.d.ts +3 -3
  78. package/dist/plugins.js +35 -23
  79. package/dist/signal-EotCj4hS.d.cts +110 -0
  80. package/dist/signal-EotCj4hS.d.ts +110 -0
  81. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  82. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  83. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  84. package/dist/ssr.cjs +214 -73
  85. package/dist/ssr.d.cts +9 -3
  86. package/dist/ssr.d.ts +9 -3
  87. package/dist/ssr.js +11 -12
  88. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  89. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  90. package/dist/tagFactory-DFstCLQV.d.cts +117 -0
  91. package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
  92. package/dist/testing.cjs +2481 -2153
  93. package/dist/testing.d.cts +56 -5
  94. package/dist/testing.d.ts +56 -5
  95. package/dist/testing.js +580 -307
  96. package/dist/ui.cjs +798 -328
  97. package/dist/ui.d.cts +40 -7
  98. package/dist/ui.d.ts +40 -7
  99. package/dist/ui.js +151 -56
  100. package/dist/widgets.cjs +319 -285
  101. package/dist/widgets.js +9 -10
  102. package/package.json +2 -2
  103. package/dist/chunk-2WLZ6757.js +0 -149
  104. package/dist/chunk-7NBDXVHS.js +0 -60
  105. package/dist/chunk-CCSJMTRN.js +0 -15
  106. package/dist/chunk-QKRPLZ2V.js +0 -108
  107. package/dist/chunk-WWV3SJ3L.js +0 -131
  108. package/dist/dispose-46BOMMQJ.js +0 -19
  109. package/dist/plugin-D30wlGW5.d.cts +0 -71
  110. package/dist/plugin-D30wlGW5.d.ts +0 -71
  111. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  112. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
package/dist/widgets.cjs CHANGED
@@ -32,14 +32,6 @@ __export(widgets_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(widgets_exports);
34
34
 
35
- // src/core/rendering/createId.ts
36
- var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
37
- var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
38
- function createId(prefix = "sibu") {
39
- _counter.n++;
40
- return `${prefix}-${_counter.n}`;
41
- }
42
-
43
35
  // src/core/dev.ts
44
36
  var DEV = typeof __SIBU_DEV__ !== "undefined" ? !!__SIBU_DEV__ : typeof globalThis.__SIBU_DEV__ !== "undefined" ? !!globalThis.__SIBU_DEV__ : (
45
37
  // safe default: off in browser, on in test/dev Node
@@ -56,6 +48,69 @@ function devWarn(message) {
56
48
  }
57
49
  }
58
50
 
51
+ // src/core/ssr-context.ts
52
+ var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
53
+ function detectSSRShared() {
54
+ let detected = null;
55
+ try {
56
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
57
+ let mod = null;
58
+ const getBuiltin = process.getBuiltinModule;
59
+ if (typeof getBuiltin === "function") {
60
+ mod = getBuiltin("node:async_hooks");
61
+ } else if (typeof require === "function") {
62
+ mod = require("async_hooks");
63
+ }
64
+ if (mod) detected = new mod.AsyncLocalStorage();
65
+ }
66
+ } catch {
67
+ detected = null;
68
+ }
69
+ return {
70
+ als: detected,
71
+ fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
72
+ fallbackDepth: 0
73
+ };
74
+ }
75
+ var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
76
+ var als = _shared.als;
77
+ var fallbackStore = _shared.fallbackStore;
78
+ _shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
79
+ function getSSRStore() {
80
+ if (als) {
81
+ const s = als.getStore();
82
+ if (s) return s;
83
+ }
84
+ return fallbackStore;
85
+ }
86
+ function getRequestStore() {
87
+ if (als) return als.getStore() ?? null;
88
+ return _shared.fallbackDepth > 0 ? fallbackStore : null;
89
+ }
90
+ function isSSR() {
91
+ return getSSRStore().ssr;
92
+ }
93
+
94
+ // src/core/rendering/createId.ts
95
+ var COUNTER_KEY = /* @__PURE__ */ Symbol.for("sibujs.createId.v1");
96
+ var _counter = globalThis[COUNTER_KEY] ?? (globalThis[COUNTER_KEY] = { n: 0 });
97
+ function createId(prefix = "sibu") {
98
+ const request = getRequestStore();
99
+ if (request) {
100
+ request.idCounter = (request.idCounter ?? 0) + 1;
101
+ return `${prefix}-${request.idCounter}`;
102
+ }
103
+ _counter.n++;
104
+ return `${prefix}-${_counter.n}`;
105
+ }
106
+ function idSegment(key) {
107
+ let out = "";
108
+ for (const ch of String(key)) {
109
+ out += /[A-Za-z0-9-]/.test(ch) ? ch : `_${(ch.codePointAt(0) ?? 0).toString(16)}_`;
110
+ }
111
+ return out;
112
+ }
113
+
59
114
  // src/core/errors.ts
60
115
  var ERROR_STATE_KEY = /* @__PURE__ */ Symbol.for("sibujs.runtime-errors.v1");
61
116
  function state() {
@@ -144,6 +199,25 @@ function reportError(error, context) {
144
199
  });
145
200
  }
146
201
 
202
+ // src/core/rendering/dispose.ts
203
+ function resolveCapture(capture) {
204
+ let frame = capture;
205
+ while (frame?.closed) frame = frame.forwardTo;
206
+ return frame ?? null;
207
+ }
208
+ var registrationCaptures = [];
209
+ function currentDisposerCapture() {
210
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
211
+ }
212
+ function beginDisposerCapture(capture) {
213
+ if (capture === null && registrationCaptures.length === 0) return false;
214
+ registrationCaptures.push(resolveCapture(capture));
215
+ return true;
216
+ }
217
+ function endDisposerCapture() {
218
+ registrationCaptures.pop();
219
+ }
220
+
147
221
  // src/reactivity/track-core.ts
148
222
  var POOL_MAX = 4096;
149
223
  var nodePool = [];
@@ -291,8 +365,18 @@ function untracked(fn) {
291
365
  var subscriberEpochCounter = 0;
292
366
  function retrack(effectFn, subscriber) {
293
367
  const prev = currentSubscriber;
368
+ let savedDepth = 0;
369
+ let savedSuspendSub = null;
370
+ if (trackingSuspended) {
371
+ savedDepth = suspendDepth;
372
+ savedSuspendSub = suspendSavedSub;
373
+ suspendDepth = 0;
374
+ suspendSavedSub = null;
375
+ trackingSuspended = false;
376
+ }
294
377
  currentSubscriber = subscriber;
295
378
  const sub = subscriber;
379
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
296
380
  const epoch = ++subscriberEpochCounter;
297
381
  sub._epoch = epoch;
298
382
  sub._structDirty = false;
@@ -305,6 +389,11 @@ function retrack(effectFn, subscriber) {
305
389
  effectFn();
306
390
  } finally {
307
391
  currentSubscriber = prev;
392
+ if (savedDepth !== 0) {
393
+ suspendDepth = savedDepth;
394
+ suspendSavedSub = savedSuspendSub;
395
+ trackingSuspended = true;
396
+ }
308
397
  let node = sub.depsHead ?? null;
309
398
  while (node !== null) {
310
399
  const next = node.subNext;
@@ -324,11 +413,25 @@ function track(effectFn, subscriber) {
324
413
  if (!subscriber) return reactiveBinding(effectFn);
325
414
  cleanup(subscriber);
326
415
  const prev = currentSubscriber;
416
+ let savedDepth = 0;
417
+ let savedSuspendSub = null;
418
+ if (trackingSuspended) {
419
+ savedDepth = suspendDepth;
420
+ savedSuspendSub = suspendSavedSub;
421
+ suspendDepth = 0;
422
+ suspendSavedSub = null;
423
+ trackingSuspended = false;
424
+ }
327
425
  currentSubscriber = subscriber;
328
426
  try {
329
427
  effectFn();
330
428
  } finally {
331
429
  currentSubscriber = prev;
430
+ if (savedDepth !== 0) {
431
+ suspendDepth = savedDepth;
432
+ suspendSavedSub = savedSuspendSub;
433
+ trackingSuspended = true;
434
+ }
332
435
  const sub2 = subscriber;
333
436
  for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
334
437
  const sig = n.sig;
@@ -465,7 +568,12 @@ function drainQueue() {
465
568
  quarantined.add(sub);
466
569
  continue;
467
570
  }
468
- safeInvoke(sub);
571
+ const pushed = beginDisposerCapture(sub._cap ?? null);
572
+ try {
573
+ safeInvoke(sub);
574
+ } finally {
575
+ if (pushed) endDisposerCapture();
576
+ }
469
577
  }
470
578
  }
471
579
  function drainNotificationQueue() {
@@ -499,8 +607,9 @@ function propagateDirty(sub) {
499
607
  if (s._c) {
500
608
  const nSig = s._sig;
501
609
  if (nSig) {
502
- if (!nSig._d) {
610
+ if (!nSig._d || nSig._f === true) {
503
611
  nSig._d = true;
612
+ nSig._f = false;
504
613
  stack.push(nSig);
505
614
  }
506
615
  } else {
@@ -600,7 +709,7 @@ function forEachSubscriber(signal2, visit) {
600
709
  }
601
710
 
602
711
  // src/reactivity/track.ts
603
- var _runtimeVersion = true ? "4.4.0" : "dev";
712
+ var _runtimeVersion = true ? "4.6.0" : "dev";
604
713
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
605
714
  function resolveReactiveApi() {
606
715
  const g = globalThis;
@@ -664,10 +773,14 @@ function derived(getter, options) {
664
773
  const cs = {};
665
774
  cs._d = false;
666
775
  cs._init = false;
776
+ cs._f = false;
667
777
  cs._g = getter;
668
778
  cs.__v = 0;
779
+ let evaluating = false;
780
+ let disposed = false;
781
+ let pendingError;
669
782
  const markDirty = () => {
670
- if (cs._d) return;
783
+ if (cs._d || disposed) return;
671
784
  cs._d = true;
672
785
  };
673
786
  markDirty._c = 1;
@@ -690,33 +803,53 @@ function derived(getter, options) {
690
803
  }
691
804
  }, markDirty);
692
805
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
693
- let evaluating = false;
694
806
  const validate = () => {
695
- if (!cs._d) return;
807
+ if (!cs._d || disposed || pendingError !== void 0) return;
696
808
  const oldValue = cs._v;
697
809
  evaluating = true;
698
810
  try {
699
811
  retrack2(recompute, markDirty);
700
812
  if (!Object.is(oldValue, cs._v)) cs.__v++;
813
+ } catch (err) {
814
+ pendingError = { error: err };
815
+ cs.__v++;
816
+ if (disposed) cs._d = true;
817
+ else cs._f = true;
701
818
  } finally {
702
819
  evaluating = false;
820
+ if (disposed) cleanup2(markDirty);
703
821
  }
704
- if (hook && !Object.is(oldValue, cs._v)) {
822
+ if (hook && !disposed && !Object.is(oldValue, cs._v)) {
705
823
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
706
824
  }
707
825
  };
708
826
  cs._validate = validate;
827
+ const throwPending = () => {
828
+ const { error } = pendingError;
829
+ pendingError = void 0;
830
+ if (disposed) cs._d = false;
831
+ throw error;
832
+ };
709
833
  function computedGetter() {
710
834
  if (evaluating) {
711
835
  throw new Error(
712
836
  `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
713
837
  );
714
838
  }
715
- if (isTrackingSuspended2()) {
716
- if (cs._d) validate();
839
+ if (isTrackingSuspended2() || disposed) {
840
+ if (cs._d) {
841
+ validate();
842
+ if (pendingError !== void 0) throwPending();
843
+ }
717
844
  return cs._v;
718
845
  }
719
- if (cs._d) validate();
846
+ if (cs._d) {
847
+ validate();
848
+ if (pendingError !== void 0) {
849
+ recordDependency2(cs);
850
+ throwPending();
851
+ }
852
+ }
720
853
  recordDependency2(cs);
721
854
  return cs._v;
722
855
  }
@@ -725,171 +858,21 @@ function derived(getter, options) {
725
858
  cs.__name = debugName;
726
859
  }
727
860
  computedGetter.__signal = cs;
728
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
729
- return computedGetter;
730
- }
731
-
732
- // src/core/ssr-context.ts
733
- var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
734
- function detectSSRShared() {
735
- let detected = null;
736
- try {
737
- if (typeof process !== "undefined" && process.versions && process.versions.node) {
738
- let mod = null;
739
- const getBuiltin = process.getBuiltinModule;
740
- if (typeof getBuiltin === "function") {
741
- mod = getBuiltin("node:async_hooks");
742
- } else if (typeof require === "function") {
743
- mod = require("async_hooks");
861
+ computedGetter.dispose = () => {
862
+ if (disposed) return;
863
+ disposed = true;
864
+ cs._d = pendingError !== void 0;
865
+ cleanup2(markDirty);
866
+ const h = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
867
+ if (h) {
868
+ try {
869
+ h.emit("computed:destroy", { signal: cs, getter: computedGetter });
870
+ } catch {
744
871
  }
745
- if (mod) detected = new mod.AsyncLocalStorage();
746
872
  }
747
- } catch {
748
- detected = null;
749
- }
750
- return {
751
- als: detected,
752
- fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
753
- fallbackDepth: 0
754
- };
755
- }
756
- var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
757
- var als = _shared.als;
758
- var fallbackStore = _shared.fallbackStore;
759
- _shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
760
- function getSSRStore() {
761
- if (als) {
762
- const s = als.getStore();
763
- if (s) return s;
764
- }
765
- return fallbackStore;
766
- }
767
- function isSSR() {
768
- return getSSRStore().ssr;
769
- }
770
-
771
- // src/core/signals/effect.ts
772
- var _g = globalThis;
773
- var MAX_RERUNS = 100;
774
- function flushUserCleanups(ctx) {
775
- const list = ctx.userCleanups;
776
- if (list.length === 0) return;
777
- ctx.userCleanups = [];
778
- for (let i = list.length - 1; i >= 0; i--) {
779
- try {
780
- list[i]();
781
- } catch (err) {
782
- reportError(err, { phase: "cleanup" });
783
- }
784
- }
785
- }
786
- function drainReruns(ctx) {
787
- let reruns = 1;
788
- do {
789
- ctx.rerunPending = false;
790
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
791
- retrack2(ctx.bodyFn, ctx.subscriber);
792
- } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
793
- if (ctx.rerunPending) {
794
- ctx.rerunPending = false;
795
- reportError(
796
- new Error(
797
- `effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
798
- ),
799
- { phase: "effect" }
800
- );
801
- }
802
- }
803
- function disposeEffect(ctx) {
804
- if (ctx.disposed) return;
805
- ctx.disposed = true;
806
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
807
- if (h) {
808
- try {
809
- h.emit("effect:destroy", { effectFn: ctx.fn });
810
- } catch {
811
- }
812
- }
813
- try {
814
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
815
- } catch (err) {
816
- if (typeof console !== "undefined") {
817
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
818
- }
819
- }
820
- try {
821
- cleanup2(ctx.subscriber);
822
- } catch (err) {
823
- if (typeof console !== "undefined") {
824
- console.warn("[SibuJS effect] dispose threw:", err);
825
- }
826
- }
827
- }
828
- function effect(effectFn, options) {
829
- devAssert(typeof effectFn === "function", "effect: argument must be a function.");
830
- if (isSSR()) return () => {
831
- };
832
- const ctx = {
833
- fn: effectFn,
834
- onError: options?.onError,
835
- userCleanups: [],
836
- running: false,
837
- rerunPending: false,
838
- disposed: false,
839
- onCleanup: null,
840
- subscriber: null,
841
- bodyFn: null
842
- };
843
- ctx.onCleanup = (fn) => {
844
- ctx.userCleanups.push(fn);
845
- };
846
- const onErrorCaptured = ctx.onError;
847
- ctx.bodyFn = onErrorCaptured ? () => {
848
- try {
849
- ctx.fn(ctx.onCleanup);
850
- } catch (err) {
851
- onErrorCaptured(err);
852
- }
853
- } : () => {
854
- ctx.fn(ctx.onCleanup);
855
873
  };
856
- const sub = (() => {
857
- if (ctx.disposed) return;
858
- if (ctx.running) {
859
- ctx.rerunPending = true;
860
- return;
861
- }
862
- ctx.running = true;
863
- try {
864
- ctx.rerunPending = false;
865
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
866
- retrack2(ctx.bodyFn, sub);
867
- if (ctx.rerunPending) drainReruns(ctx);
868
- } finally {
869
- ctx.running = false;
870
- ctx.rerunPending = false;
871
- }
872
- });
873
- sub.depsHead = null;
874
- sub.depsTail = null;
875
- sub._epoch = 0;
876
- sub._structDirty = false;
877
- sub._hasComputedDep = false;
878
- sub._errorPhase = "effect";
879
- sub._runEpoch = 0;
880
- sub._runs = 0;
881
- ctx.subscriber = sub;
882
- ctx.running = true;
883
- try {
884
- retrack2(ctx.bodyFn, ctx.subscriber);
885
- if (ctx.rerunPending) drainReruns(ctx);
886
- } finally {
887
- ctx.running = false;
888
- ctx.rerunPending = false;
889
- }
890
- const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
891
- if (hook) hook.emit("effect:create", { effectFn });
892
- return () => disposeEffect(ctx);
874
+ if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
875
+ return computedGetter;
893
876
  }
894
877
 
895
878
  // src/reactivity/batch.ts
@@ -943,7 +926,7 @@ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
943
926
  var isBatching = API2.isBatching;
944
927
 
945
928
  // src/core/signals/signal.ts
946
- var _g2 = globalThis;
929
+ var _g = globalThis;
947
930
  function signal(initial, options) {
948
931
  const state2 = {
949
932
  value: initial,
@@ -974,7 +957,7 @@ function signal(initial, options) {
974
957
  state2.value = newValue;
975
958
  state2.__v++;
976
959
  if (DEV) {
977
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
960
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
978
961
  if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
979
962
  }
980
963
  if (!enqueueBatchedSignal(state2)) {
@@ -988,7 +971,7 @@ function signal(initial, options) {
988
971
  if (Object.is(newValue, prev)) return;
989
972
  state2.value = newValue;
990
973
  state2.__v++;
991
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
974
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
992
975
  if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
993
976
  if (!enqueueBatchedSignal(state2)) {
994
977
  notifySubscribers2(state2);
@@ -1007,7 +990,7 @@ function signal(initial, options) {
1007
990
  };
1008
991
  }
1009
992
  if (DEV) {
1010
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
993
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1011
994
  if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1012
995
  }
1013
996
  return [get, set];
@@ -1043,6 +1026,13 @@ function watch(getter, callback) {
1043
1026
  return teardown;
1044
1027
  }
1045
1028
 
1029
+ // src/reactivity/domBinding.ts
1030
+ function domBinding(commit, ownerNode) {
1031
+ if (isSSR()) return () => {
1032
+ };
1033
+ return reactiveBinding2(commit, ownerNode);
1034
+ }
1035
+
1046
1036
  // src/widgets/Combobox.ts
1047
1037
  var boundComboboxes = /* @__PURE__ */ new WeakMap();
1048
1038
  function combobox(options) {
@@ -1116,7 +1106,7 @@ function combobox(options) {
1116
1106
  els.input.setAttribute("role", "combobox");
1117
1107
  els.input.setAttribute("aria-autocomplete", "list");
1118
1108
  els.input.setAttribute("aria-controls", listboxId);
1119
- const fxTeardown = effect(() => {
1109
+ const fxTeardown = domBinding(() => {
1120
1110
  const open2 = isOpen();
1121
1111
  els.input.setAttribute("aria-expanded", open2 ? "true" : "false");
1122
1112
  els.listbox.hidden = !open2;
@@ -1134,7 +1124,7 @@ function combobox(options) {
1134
1124
  }
1135
1125
  if (activeId) els.input.setAttribute("aria-activedescendant", activeId);
1136
1126
  else els.input.removeAttribute("aria-activedescendant");
1137
- });
1127
+ }, els.input);
1138
1128
  const onInput = () => {
1139
1129
  setQuery(els.input.value);
1140
1130
  open();
@@ -1182,8 +1172,11 @@ function combobox(options) {
1182
1172
  els.input.addEventListener("focus", onFocus);
1183
1173
  els.input.addEventListener("blur", onBlur);
1184
1174
  els.listbox.addEventListener("mousedown", onListboxPointerDown);
1175
+ let tornDown = false;
1185
1176
  const teardown = () => {
1186
- boundComboboxes.delete(els.input);
1177
+ if (tornDown) return;
1178
+ tornDown = true;
1179
+ if (boundComboboxes.get(els.input) === teardown) boundComboboxes.delete(els.input);
1187
1180
  fxTeardown();
1188
1181
  els.input.removeEventListener("input", onInput);
1189
1182
  els.input.removeEventListener("keydown", onKey);
@@ -1228,11 +1221,23 @@ function combobox(options) {
1228
1221
  };
1229
1222
  }
1230
1223
 
1224
+ // src/widgets/attributeSnapshot.ts
1225
+ function snapshotAttributes(el, names) {
1226
+ const previous = names.map((name) => [name, el.getAttribute(name)]);
1227
+ return () => {
1228
+ for (const [name, value] of previous) {
1229
+ if (value === null) el.removeAttribute(name);
1230
+ else el.setAttribute(name, value);
1231
+ }
1232
+ };
1233
+ }
1234
+
1231
1235
  // src/widgets/Tabs.ts
1232
1236
  var boundTablists = /* @__PURE__ */ new WeakMap();
1233
1237
  function tabs(options) {
1234
1238
  const { tabs: tabDefs, defaultTab } = options;
1235
- const initialTab = defaultTab ?? tabDefs.find((t) => !t.disabled)?.id ?? tabDefs[0]?.id ?? "";
1239
+ const firstEnabled = tabDefs.find((t) => !t.disabled)?.id ?? "";
1240
+ const initialTab = defaultTab !== void 0 && tabDefs.some((t) => t.id === defaultTab && !t.disabled) ? defaultTab : firstEnabled;
1236
1241
  const [activeTab, setActiveTabState] = signal(initialTab);
1237
1242
  function setActiveTab(id) {
1238
1243
  const tab = tabDefs.find((t) => t.id === id);
@@ -1279,58 +1284,29 @@ function tabs(options) {
1279
1284
  function bind(els) {
1280
1285
  const existing = boundTablists.get(els.tablist);
1281
1286
  if (existing) return existing;
1282
- const restore = [];
1283
- const prevTablistRole = els.tablist.getAttribute("role");
1287
+ const restore = [snapshotAttributes(els.tablist, ["role"])];
1284
1288
  els.tablist.setAttribute("role", "tablist");
1285
- restore.push(() => {
1286
- if (prevTablistRole === null) els.tablist.removeAttribute("role");
1287
- else els.tablist.setAttribute("role", prevTablistRole);
1288
- });
1289
+ const idPrefix = createId("sibu-tabs");
1289
1290
  for (const def of tabDefs) {
1290
1291
  const tabEl = els.tabs[def.id];
1291
1292
  if (!tabEl) continue;
1292
- const prevRole = tabEl.getAttribute("role");
1293
- const prevId = tabEl.id;
1294
- const prevDisabled = tabEl.getAttribute("aria-disabled");
1295
- const prevControls = tabEl.getAttribute("aria-controls");
1293
+ restore.push(
1294
+ snapshotAttributes(tabEl, ["role", "id", "aria-disabled", "aria-controls", "aria-selected", "tabindex"])
1295
+ );
1296
1296
  tabEl.setAttribute("role", "tab");
1297
- tabEl.setAttribute("id", `sibu-tab-${def.id}`);
1297
+ if (!tabEl.id) tabEl.id = `${idPrefix}-tab-${idSegment(def.id)}`;
1298
1298
  if (def.disabled) tabEl.setAttribute("aria-disabled", "true");
1299
+ else tabEl.removeAttribute("aria-disabled");
1299
1300
  const panelEl = els.panels?.[def.id];
1300
- let prevPanelRole = null;
1301
- let prevPanelId = "";
1302
- let prevPanelLabelledBy = null;
1303
1301
  if (panelEl) {
1304
- prevPanelRole = panelEl.getAttribute("role");
1305
- prevPanelId = panelEl.id;
1306
- prevPanelLabelledBy = panelEl.getAttribute("aria-labelledby");
1302
+ restore.push(snapshotAttributes(panelEl, ["role", "id", "aria-labelledby", "hidden"]));
1307
1303
  panelEl.setAttribute("role", "tabpanel");
1308
- panelEl.setAttribute("id", `sibu-tabpanel-${def.id}`);
1309
- panelEl.setAttribute("aria-labelledby", `sibu-tab-${def.id}`);
1310
- tabEl.setAttribute("aria-controls", `sibu-tabpanel-${def.id}`);
1304
+ if (!panelEl.id) panelEl.id = `${idPrefix}-panel-${idSegment(def.id)}`;
1305
+ panelEl.setAttribute("aria-labelledby", tabEl.id);
1306
+ tabEl.setAttribute("aria-controls", panelEl.id);
1311
1307
  }
1312
- restore.push(() => {
1313
- if (prevRole === null) tabEl.removeAttribute("role");
1314
- else tabEl.setAttribute("role", prevRole);
1315
- if (prevId === "") tabEl.removeAttribute("id");
1316
- else tabEl.id = prevId;
1317
- if (prevDisabled === null) tabEl.removeAttribute("aria-disabled");
1318
- else tabEl.setAttribute("aria-disabled", prevDisabled);
1319
- if (prevControls === null) tabEl.removeAttribute("aria-controls");
1320
- else tabEl.setAttribute("aria-controls", prevControls);
1321
- tabEl.removeAttribute("aria-selected");
1322
- tabEl.removeAttribute("tabindex");
1323
- if (panelEl) {
1324
- if (prevPanelRole === null) panelEl.removeAttribute("role");
1325
- else panelEl.setAttribute("role", prevPanelRole);
1326
- if (prevPanelId === "") panelEl.removeAttribute("id");
1327
- else panelEl.id = prevPanelId;
1328
- if (prevPanelLabelledBy === null) panelEl.removeAttribute("aria-labelledby");
1329
- else panelEl.setAttribute("aria-labelledby", prevPanelLabelledBy);
1330
- }
1331
- });
1332
1308
  }
1333
- const fxTeardown = effect(() => {
1309
+ const fxTeardown = domBinding(() => {
1334
1310
  const active = activeTab();
1335
1311
  for (const def of tabDefs) {
1336
1312
  const tabEl = els.tabs[def.id];
@@ -1341,7 +1317,7 @@ function tabs(options) {
1341
1317
  const panelEl = els.panels?.[def.id];
1342
1318
  if (panelEl) panelEl.hidden = !isAct;
1343
1319
  }
1344
- });
1320
+ }, els.tablist);
1345
1321
  const onKey = (e) => {
1346
1322
  if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1347
1323
  e.preventDefault();
@@ -1378,7 +1354,10 @@ function tabs(options) {
1378
1354
  tabEl.addEventListener("click", fn);
1379
1355
  clickHandlers.push({ el: tabEl, fn });
1380
1356
  }
1357
+ let tornDown = false;
1381
1358
  const teardown = () => {
1359
+ if (tornDown) return;
1360
+ tornDown = true;
1382
1361
  boundTablists.delete(els.tablist);
1383
1362
  fxTeardown();
1384
1363
  els.tablist.removeEventListener("keydown", onKey);
@@ -1404,7 +1383,10 @@ function tabs(options) {
1404
1383
  var boundAccordions = /* @__PURE__ */ new WeakMap();
1405
1384
  function accordion(options) {
1406
1385
  const { items: itemDefs, multiple = false, defaultExpanded = [] } = options;
1407
- const [expandedIds, setExpandedIds] = signal(new Set(defaultExpanded));
1386
+ const knownIds = new Set(itemDefs.map((item) => item.id));
1387
+ const validDefaults = defaultExpanded.filter((id) => knownIds.has(id));
1388
+ const initialExpanded = multiple ? validDefaults : validDefaults.slice(0, 1);
1389
+ const [expandedIds, setExpandedIds] = signal(new Set(initialExpanded));
1408
1390
  const items = derived(
1409
1391
  () => itemDefs.map((item) => ({
1410
1392
  ...item,
@@ -1455,42 +1437,22 @@ function accordion(options) {
1455
1437
  if (existing) return existing;
1456
1438
  }
1457
1439
  const restore = [];
1440
+ const idPrefix = createId("sibu-accordion");
1458
1441
  for (const item of itemDefs) {
1459
1442
  const trig = els.triggers[item.id];
1460
1443
  const panel = els.panels[item.id];
1461
1444
  if (!trig) continue;
1462
- const prevTrigId = trig.id;
1463
- const prevTrigControls = trig.getAttribute("aria-controls");
1464
- trig.id = `sibu-accordion-trigger-${item.id}`;
1465
- let prevPanelRole = null;
1466
- let prevPanelId = "";
1467
- let prevPanelLabelledBy = null;
1445
+ restore.push(snapshotAttributes(trig, ["id", "aria-controls", "aria-expanded"]));
1446
+ if (!trig.id) trig.id = `${idPrefix}-trigger-${idSegment(item.id)}`;
1468
1447
  if (panel) {
1469
- prevPanelRole = panel.getAttribute("role");
1470
- prevPanelId = panel.id;
1471
- prevPanelLabelledBy = panel.getAttribute("aria-labelledby");
1448
+ restore.push(snapshotAttributes(panel, ["role", "id", "aria-labelledby", "hidden"]));
1472
1449
  panel.setAttribute("role", "region");
1473
- panel.id = `sibu-accordion-panel-${item.id}`;
1450
+ if (!panel.id) panel.id = `${idPrefix}-panel-${idSegment(item.id)}`;
1474
1451
  panel.setAttribute("aria-labelledby", trig.id);
1475
1452
  trig.setAttribute("aria-controls", panel.id);
1476
1453
  }
1477
- restore.push(() => {
1478
- if (prevTrigId === "") trig.removeAttribute("id");
1479
- else trig.id = prevTrigId;
1480
- if (prevTrigControls === null) trig.removeAttribute("aria-controls");
1481
- else trig.setAttribute("aria-controls", prevTrigControls);
1482
- trig.removeAttribute("aria-expanded");
1483
- if (panel) {
1484
- if (prevPanelRole === null) panel.removeAttribute("role");
1485
- else panel.setAttribute("role", prevPanelRole);
1486
- if (prevPanelId === "") panel.removeAttribute("id");
1487
- else panel.id = prevPanelId;
1488
- if (prevPanelLabelledBy === null) panel.removeAttribute("aria-labelledby");
1489
- else panel.setAttribute("aria-labelledby", prevPanelLabelledBy);
1490
- }
1491
- });
1492
1454
  }
1493
- const fxTeardown = effect(() => {
1455
+ const fxTeardown = domBinding(() => {
1494
1456
  const ids = expandedIds();
1495
1457
  for (const item of itemDefs) {
1496
1458
  const trig = els.triggers[item.id];
@@ -1500,7 +1462,7 @@ function accordion(options) {
1500
1462
  trig.setAttribute("aria-expanded", expanded ? "true" : "false");
1501
1463
  if (panel) panel.hidden = !expanded;
1502
1464
  }
1503
- });
1465
+ }, idempotencyKey);
1504
1466
  const handlers = [];
1505
1467
  for (const item of itemDefs) {
1506
1468
  const trig = els.triggers[item.id];
@@ -1516,7 +1478,10 @@ function accordion(options) {
1516
1478
  trig.addEventListener("keydown", key);
1517
1479
  handlers.push({ el: trig, click, key });
1518
1480
  }
1481
+ let tornDown = false;
1519
1482
  const teardown = () => {
1483
+ if (tornDown) return;
1484
+ tornDown = true;
1520
1485
  if (idempotencyKey) boundAccordions.delete(idempotencyKey);
1521
1486
  fxTeardown();
1522
1487
  for (const { el, click, key } of handlers) {
@@ -1575,11 +1540,11 @@ function popover() {
1575
1540
  }
1576
1541
  els.popover.setAttribute("aria-labelledby", els.labelledBy.id);
1577
1542
  }
1578
- const fxTeardown = effect(() => {
1543
+ const fxTeardown = domBinding(() => {
1579
1544
  const open2 = isOpen();
1580
1545
  els.trigger.setAttribute("aria-expanded", open2 ? "true" : "false");
1581
1546
  els.popover.hidden = !open2;
1582
- });
1547
+ }, els.trigger);
1583
1548
  const onTriggerClick = (e) => {
1584
1549
  e.preventDefault();
1585
1550
  toggle();
@@ -1602,8 +1567,11 @@ function popover() {
1602
1567
  els.popover.addEventListener("keydown", onKey);
1603
1568
  els.trigger.addEventListener("keydown", onKey);
1604
1569
  document.addEventListener("pointerdown", onDocPointer);
1570
+ let tornDown = false;
1605
1571
  const teardown = () => {
1606
- boundPopovers.delete(els.trigger);
1572
+ if (tornDown) return;
1573
+ tornDown = true;
1574
+ if (boundPopovers.get(els.trigger) === teardown) boundPopovers.delete(els.trigger);
1607
1575
  fxTeardown();
1608
1576
  els.trigger.removeEventListener("click", onTriggerClick);
1609
1577
  els.popover.removeEventListener("keydown", onKey);
@@ -1721,7 +1689,7 @@ function select(options) {
1721
1689
  els.listbox.setAttribute("aria-multiselectable", multiple ? "true" : "false");
1722
1690
  if (els.listbox.tabIndex < 0) els.listbox.tabIndex = 0;
1723
1691
  const toStr = els.itemToString ?? itemToString ?? ((it) => String(it));
1724
- const fxTeardown = effect(() => {
1692
+ const fxTeardown = domBinding(() => {
1725
1693
  const idx = highlightedIndex();
1726
1694
  const sel = selectedItems();
1727
1695
  let activeId = "";
@@ -1731,13 +1699,14 @@ function select(options) {
1731
1699
  if (!optEl.id) optEl.id = `${listboxId}-opt-${i}`;
1732
1700
  optEl.setAttribute("role", "option");
1733
1701
  optEl.setAttribute("aria-selected", sel.includes(items[i]) ? "true" : "false");
1734
- if (isItemDisabled(items[i])) optEl.setAttribute("aria-disabled", "true");
1702
+ const disabled = isItemDisabled(items[i]);
1703
+ if (disabled) optEl.setAttribute("aria-disabled", "true");
1735
1704
  else optEl.removeAttribute("aria-disabled");
1736
- if (i === idx) activeId = optEl.id;
1705
+ if (i === idx && !disabled) activeId = optEl.id;
1737
1706
  }
1738
1707
  if (activeId) els.listbox.setAttribute("aria-activedescendant", activeId);
1739
1708
  else els.listbox.removeAttribute("aria-activedescendant");
1740
- });
1709
+ }, els.listbox);
1741
1710
  let typeBuffer = "";
1742
1711
  let typeTimer = null;
1743
1712
  const onKey = (e) => {
@@ -1749,10 +1718,12 @@ function select(options) {
1749
1718
  highlightPrev();
1750
1719
  } else if (e.key === "Home") {
1751
1720
  e.preventDefault();
1752
- if (items.length > 0) setHighlightedIndex(0);
1721
+ const first = nextEnabled(-1, 1);
1722
+ if (first !== -1) setHighlightedIndex(first);
1753
1723
  } else if (e.key === "End") {
1754
1724
  e.preventDefault();
1755
- if (items.length > 0) setHighlightedIndex(items.length - 1);
1725
+ const last = nextEnabled(items.length, -1);
1726
+ if (last !== -1) setHighlightedIndex(last);
1756
1727
  } else if (e.key === "Enter" || e.key === " ") {
1757
1728
  if (highlightedIndex() >= 0) {
1758
1729
  e.preventDefault();
@@ -1770,8 +1741,11 @@ function select(options) {
1770
1741
  }
1771
1742
  };
1772
1743
  els.listbox.addEventListener("keydown", onKey);
1744
+ let tornDown = false;
1773
1745
  const teardown = () => {
1774
- boundSelects.delete(els.listbox);
1746
+ if (tornDown) return;
1747
+ tornDown = true;
1748
+ if (boundSelects.get(els.listbox) === teardown) boundSelects.delete(els.listbox);
1775
1749
  fxTeardown();
1776
1750
  els.listbox.removeEventListener("keydown", onKey);
1777
1751
  if (typeTimer !== null) clearTimeout(typeTimer);
@@ -1857,9 +1831,9 @@ function tooltip(options) {
1857
1831
  els.tooltip.id = id;
1858
1832
  const prevDescribedBy = els.trigger.getAttribute("aria-describedby");
1859
1833
  els.trigger.setAttribute("aria-describedby", prevDescribedBy ? `${prevDescribedBy} ${id}` : id);
1860
- const fxTeardown = effect(() => {
1834
+ const fxTeardown = domBinding(() => {
1861
1835
  els.tooltip.hidden = !isVisible();
1862
- });
1836
+ }, els.trigger);
1863
1837
  const onTriggerEnter = () => show();
1864
1838
  const onTriggerLeave = () => scheduleHide();
1865
1839
  const onTooltipEnter = () => {
@@ -1884,8 +1858,11 @@ function tooltip(options) {
1884
1858
  els.trigger.addEventListener("keydown", onKey);
1885
1859
  els.tooltip.addEventListener("pointerenter", onTooltipEnter);
1886
1860
  els.tooltip.addEventListener("pointerleave", onTooltipLeave);
1861
+ let tornDown = false;
1887
1862
  const teardown = () => {
1888
- boundTriggers.delete(els.trigger);
1863
+ if (tornDown) return;
1864
+ tornDown = true;
1865
+ if (boundTriggers.get(els.trigger) === teardown) boundTriggers.delete(els.trigger);
1889
1866
  fxTeardown();
1890
1867
  els.trigger.removeEventListener("pointerenter", onTriggerEnter);
1891
1868
  els.trigger.removeEventListener("pointerleave", onTriggerLeave);
@@ -1923,7 +1900,8 @@ function fileUpload(options) {
1923
1900
  const [isDragOver, setDragOver] = signal(false);
1924
1901
  function isAccepted(file) {
1925
1902
  if (!accept) return true;
1926
- const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase());
1903
+ const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0);
1904
+ if (acceptedTypes.length === 0) return true;
1927
1905
  const fileName = file.name.toLowerCase();
1928
1906
  const fileType = file.type.toLowerCase();
1929
1907
  return acceptedTypes.some((pattern) => {
@@ -1952,16 +1930,17 @@ function fileUpload(options) {
1952
1930
  }
1953
1931
  validFiles.push(file);
1954
1932
  }
1933
+ const committed = multiple ? validFiles : validFiles.slice(-1);
1955
1934
  batch(() => {
1956
1935
  setErrors(newErrors);
1957
- if (validFiles.length > 0) {
1936
+ if (committed.length > 0) {
1958
1937
  if (multiple) {
1959
- setFiles((prev) => [...prev, ...validFiles]);
1938
+ setFiles((prev) => [...prev, ...committed]);
1960
1939
  } else {
1961
- setFiles([validFiles[validFiles.length - 1]]);
1940
+ setFiles(committed);
1962
1941
  }
1963
1942
  if (onFiles) {
1964
- onFiles(validFiles);
1943
+ onFiles(committed);
1965
1944
  }
1966
1945
  }
1967
1946
  });
@@ -1985,8 +1964,15 @@ function fileUpload(options) {
1985
1964
  if (existing) return existing;
1986
1965
  const id = createId("sibu-fileupload");
1987
1966
  const restore = [];
1967
+ const prevAccept = els.input.getAttribute("accept");
1968
+ const prevMultiple = els.input.hasAttribute("multiple");
1988
1969
  if (accept) els.input.accept = accept;
1989
1970
  els.input.multiple = multiple;
1971
+ restore.push(() => {
1972
+ if (prevAccept === null) els.input.removeAttribute("accept");
1973
+ else els.input.setAttribute("accept", prevAccept);
1974
+ els.input.multiple = prevMultiple;
1975
+ });
1990
1976
  let hintId = null;
1991
1977
  if (els.hint) {
1992
1978
  const assignedHintId = !els.hint.id;
@@ -2007,6 +1993,7 @@ function fileUpload(options) {
2007
1993
  if (els.errorRegion) {
2008
1994
  const prevRole = els.errorRegion.getAttribute("role");
2009
1995
  const prevLive = els.errorRegion.getAttribute("aria-live");
1996
+ const prevText = els.errorRegion.textContent;
2010
1997
  els.errorRegion.setAttribute("role", "status");
2011
1998
  els.errorRegion.setAttribute("aria-live", "polite");
2012
1999
  restore.push(() => {
@@ -2014,12 +2001,14 @@ function fileUpload(options) {
2014
2001
  else els.errorRegion.setAttribute("role", prevRole);
2015
2002
  if (prevLive === null) els.errorRegion.removeAttribute("aria-live");
2016
2003
  else els.errorRegion.setAttribute("aria-live", prevLive);
2004
+ els.errorRegion.textContent = prevText;
2017
2005
  });
2018
2006
  }
2019
2007
  if (els.dropZone) {
2020
2008
  const prevDzRole = els.dropZone.getAttribute("role");
2021
2009
  const prevDzLabel = els.dropZone.getAttribute("aria-label");
2022
2010
  const prevDzTabindex = els.dropZone.hasAttribute("tabindex") ? els.dropZone.getAttribute("tabindex") : null;
2011
+ const prevDzDragOver = els.dropZone.getAttribute("data-drag-over");
2023
2012
  els.dropZone.setAttribute("role", "button");
2024
2013
  els.dropZone.setAttribute("aria-label", "File drop zone \u2014 click or press Enter to browse");
2025
2014
  if (els.dropZone.tabIndex < 0) els.dropZone.tabIndex = 0;
@@ -2030,13 +2019,15 @@ function fileUpload(options) {
2030
2019
  else els.dropZone.setAttribute("aria-label", prevDzLabel);
2031
2020
  if (prevDzTabindex === null) els.dropZone.removeAttribute("tabindex");
2032
2021
  else els.dropZone.setAttribute("tabindex", prevDzTabindex);
2022
+ if (prevDzDragOver === null) els.dropZone.removeAttribute("data-drag-over");
2023
+ else els.dropZone.setAttribute("data-drag-over", prevDzDragOver);
2033
2024
  });
2034
2025
  }
2035
- const fxTeardown = effect(() => {
2026
+ const fxTeardown = domBinding(() => {
2036
2027
  const errs = errors();
2037
2028
  if (els.errorRegion) els.errorRegion.textContent = errs.join(". ");
2038
2029
  if (els.dropZone) els.dropZone.setAttribute("data-drag-over", isDragOver() ? "true" : "false");
2039
- });
2030
+ }, els.input);
2040
2031
  const onChange = () => {
2041
2032
  if (els.input.files) addFiles(els.input.files);
2042
2033
  };
@@ -2065,7 +2056,10 @@ function fileUpload(options) {
2065
2056
  els.dropZone.addEventListener("dragleave", onDragLeave);
2066
2057
  els.dropZone.addEventListener("drop", onDrop);
2067
2058
  }
2059
+ let tornDown = false;
2068
2060
  const teardown = () => {
2061
+ if (tornDown) return;
2062
+ tornDown = true;
2069
2063
  boundFileUploads.delete(els.input);
2070
2064
  fxTeardown();
2071
2065
  els.input.removeEventListener("change", onChange);
@@ -2112,12 +2106,15 @@ function stripHtml(html) {
2112
2106
  }
2113
2107
 
2114
2108
  // src/widgets/contentEditable.ts
2109
+ function toInertText(html) {
2110
+ return stripHtml(String(html)).replace(/<(?=[a-z!/?])/gi, "&lt;");
2111
+ }
2115
2112
  function contentEditable(element) {
2116
2113
  const [content, setContentInternal] = signal("");
2117
2114
  const [isFocused, setFocused] = signal(false);
2118
2115
  function setContent(input) {
2119
2116
  if (typeof input === "string") {
2120
- setContentInternal(input);
2117
+ setContentInternal(toInertText(input));
2121
2118
  return;
2122
2119
  }
2123
2120
  if (typeof input.text === "string") {
@@ -2126,7 +2123,7 @@ function contentEditable(element) {
2126
2123
  }
2127
2124
  if (typeof input.html === "string") {
2128
2125
  const shouldSanitize = input.sanitize !== false;
2129
- setContentInternal(shouldSanitize ? stripHtml(input.html) : input.html);
2126
+ setContentInternal(shouldSanitize ? toInertText(input.html) : input.html);
2130
2127
  return;
2131
2128
  }
2132
2129
  setContentInternal("");
@@ -2138,7 +2135,7 @@ function contentEditable(element) {
2138
2135
  const range = selection.getRangeAt(0);
2139
2136
  if (element && !element.contains(range.commonAncestorContainer)) return;
2140
2137
  const ancestor = range.commonAncestorContainer;
2141
- const existingWrap = findAncestorByTag(
2138
+ const existingWrap = findWrapperWithinEditor(
2142
2139
  ancestor instanceof HTMLElement ? ancestor : ancestor.parentElement,
2143
2140
  tagName
2144
2141
  );
@@ -2179,14 +2176,19 @@ function contentEditable(element) {
2179
2176
  selection.addRange(newRange);
2180
2177
  }
2181
2178
  }
2182
- function findAncestorByTag(el, tagName) {
2179
+ function findWrapperWithinEditor(el, tagName) {
2183
2180
  const upper = tagName.toUpperCase();
2184
2181
  while (el) {
2182
+ if (el === element || isEditingHost(el)) return null;
2185
2183
  if (el.tagName === upper) return el;
2186
2184
  el = el.parentElement;
2187
2185
  }
2188
2186
  return null;
2189
2187
  }
2188
+ function isEditingHost(el) {
2189
+ const attr = el.getAttribute("contenteditable");
2190
+ return attr !== null && attr.toLowerCase() !== "false";
2191
+ }
2190
2192
  function bold() {
2191
2193
  wrapSelection("strong");
2192
2194
  }
@@ -2322,15 +2324,36 @@ function datePicker(options) {
2322
2324
  els.grid.setAttribute("role", "grid");
2323
2325
  if (els.grid.tabIndex < 0) els.grid.tabIndex = 0;
2324
2326
  let pendingFocus = false;
2325
- const fxTeardown = effect(() => {
2327
+ const touchedCells = /* @__PURE__ */ new Map();
2328
+ const restoreAttr = (el, name, value) => {
2329
+ if (value === null) el.removeAttribute(name);
2330
+ else el.setAttribute(name, value);
2331
+ };
2332
+ const restoreCell = (cell, snap) => {
2333
+ restoreAttr(cell, "role", snap.role);
2334
+ restoreAttr(cell, "aria-selected", snap.selected);
2335
+ restoreAttr(cell, "aria-disabled", snap.disabled);
2336
+ restoreAttr(cell, "tabindex", snap.tabindex);
2337
+ };
2338
+ const fxTeardown = domBinding(() => {
2326
2339
  const sel = selectedDate();
2327
2340
  const view = viewDate();
2328
2341
  const days = daysInMonth();
2329
2342
  els.grid.setAttribute("aria-label", view.toLocaleDateString(void 0, { month: "long", year: "numeric" }));
2330
2343
  let viewCell = null;
2344
+ const displayed = /* @__PURE__ */ new Set();
2331
2345
  for (const d of days) {
2332
2346
  const cell = els.cell(d.date);
2333
2347
  if (!cell) continue;
2348
+ displayed.add(cell);
2349
+ if (!touchedCells.has(cell)) {
2350
+ touchedCells.set(cell, {
2351
+ role: cell.getAttribute("role"),
2352
+ selected: cell.getAttribute("aria-selected"),
2353
+ disabled: cell.getAttribute("aria-disabled"),
2354
+ tabindex: cell.getAttribute("tabindex")
2355
+ });
2356
+ }
2334
2357
  cell.setAttribute("role", "gridcell");
2335
2358
  cell.setAttribute("aria-selected", sel && isSameCalendarDay(sel, d.date) ? "true" : "false");
2336
2359
  if (d.isDisabled) cell.setAttribute("aria-disabled", "true");
@@ -2339,11 +2362,17 @@ function datePicker(options) {
2339
2362
  cell.tabIndex = isView ? 0 : -1;
2340
2363
  if (isView) viewCell = cell;
2341
2364
  }
2365
+ for (const [cell, snap] of touchedCells) {
2366
+ if (!displayed.has(cell)) {
2367
+ restoreCell(cell, snap);
2368
+ touchedCells.delete(cell);
2369
+ }
2370
+ }
2342
2371
  if (pendingFocus && viewCell && typeof viewCell.focus === "function") {
2343
2372
  pendingFocus = false;
2344
2373
  viewCell.focus();
2345
2374
  }
2346
- });
2375
+ }, els.grid);
2347
2376
  function isSameCalendarDay(a, b) {
2348
2377
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2349
2378
  }
@@ -2398,8 +2427,11 @@ function datePicker(options) {
2398
2427
  }
2399
2428
  };
2400
2429
  els.grid.addEventListener("keydown", onKey);
2430
+ let tornDown = false;
2401
2431
  const teardown = () => {
2402
- boundDatePickers.delete(els.grid);
2432
+ if (tornDown) return;
2433
+ tornDown = true;
2434
+ if (boundDatePickers.get(els.grid) === teardown) boundDatePickers.delete(els.grid);
2403
2435
  fxTeardown();
2404
2436
  els.grid.removeEventListener("keydown", onKey);
2405
2437
  if (prevGridRole === null) els.grid.removeAttribute("role");
@@ -2408,6 +2440,8 @@ function datePicker(options) {
2408
2440
  else els.grid.setAttribute("tabindex", prevGridTabIndex);
2409
2441
  if (prevGridLabel === null) els.grid.removeAttribute("aria-label");
2410
2442
  else els.grid.setAttribute("aria-label", prevGridLabel);
2443
+ for (const [cell, snap] of touchedCells) restoreCell(cell, snap);
2444
+ touchedCells.clear();
2411
2445
  };
2412
2446
  boundDatePickers.set(els.grid, teardown);
2413
2447
  return teardown;