sibujs 4.5.0 → 4.7.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 (116) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +326 -133
  3. package/dist/browser.d.cts +46 -14
  4. package/dist/browser.d.ts +46 -14
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +2094 -1033
  7. package/dist/build.d.cts +133 -44
  8. package/dist/build.d.ts +133 -44
  9. package/dist/build.js +1372 -574
  10. package/dist/cdn.dev.global.js +10 -10
  11. package/dist/cdn.full.dev.global.js +11 -11
  12. package/dist/cdn.full.global.js +10 -10
  13. package/dist/cdn.global.js +10 -10
  14. package/dist/{chunk-2DCGACUU.js → chunk-2BPG2XDA.js} +251 -66
  15. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  16. package/dist/{chunk-4PMLNECI.js → chunk-3QWBSL5R.js} +98 -29
  17. package/dist/{chunk-KKLW7YWL.js → chunk-4AWA2PVD.js} +275 -144
  18. package/dist/chunk-5HZXGZ6T.js +24 -0
  19. package/dist/{chunk-PCT43HW3.js → chunk-7LUJQAOJ.js} +1 -1
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/chunk-BC2SECJD.js +44 -0
  22. package/dist/{chunk-TBYTO6BS.js → chunk-BSY63EM6.js} +136 -80
  23. package/dist/{chunk-FTIR4QW2.js → chunk-DKXRACVN.js} +6 -8
  24. package/dist/{chunk-ONOHFDLG.js → chunk-FNJXNGYZ.js} +3 -3
  25. package/dist/{chunk-XZZOBQAY.js → chunk-FQRUXCEE.js} +6 -5
  26. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-UGRX3S57.js → chunk-JWKYU5GV.js} +22 -4
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-RBTPLM32.js → chunk-RUSSKG6G.js} +13 -12
  31. package/dist/{chunk-7LN645I6.js → chunk-SLM3IA34.js} +3 -3
  32. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  33. package/dist/{chunk-B3WHI2QA.js → chunk-UNWRJRKC.js} +55 -31
  34. package/dist/{chunk-KEISJXBU.js → chunk-UOL2ECCS.js} +44 -17
  35. package/dist/{chunk-RJE2BNI4.js → chunk-VZSG24LS.js} +108 -45
  36. package/dist/{chunk-RIXRAYIU.js → chunk-WEQ3DMVL.js} +10 -4
  37. package/dist/{chunk-3JZ4L5TJ.js → chunk-WOLJZUFQ.js} +354 -90
  38. package/dist/{chunk-S373NSMK.js → chunk-XYV3EDB7.js} +283 -123
  39. package/dist/{chunk-VZKNK2V7.js → chunk-XZR4PXRE.js} +296 -38
  40. package/dist/{chunk-GW3SCCZG.js → chunk-Z3OHK6QT.js} +153 -110
  41. package/dist/{chunk-OMJJM3KM.js → chunk-ZVL7TY4K.js} +287 -130
  42. package/dist/{contracts-DBdg9J_a.d.ts → contracts-CLqzJnOV.d.ts} +36 -17
  43. package/dist/{contracts-DBdg9J_a.d.cts → contracts-CTOJXu-x.d.cts} +36 -17
  44. package/dist/{customElement-OB9CIsc5.d.cts → customElement-MmInOW1U.d.cts} +21 -0
  45. package/dist/{customElement-OB9CIsc5.d.ts → customElement-MmInOW1U.d.ts} +21 -0
  46. package/dist/data.cjs +410 -155
  47. package/dist/data.d.cts +166 -12
  48. package/dist/data.d.ts +166 -12
  49. package/dist/data.js +12 -9
  50. package/dist/devtools.cjs +98 -61
  51. package/dist/devtools.js +7 -8
  52. package/dist/dispose-GEIG2KOF.js +28 -0
  53. package/dist/ecosystem.cjs +372 -126
  54. package/dist/ecosystem.d.cts +20 -3
  55. package/dist/ecosystem.d.ts +20 -3
  56. package/dist/ecosystem.js +12 -12
  57. package/dist/extras.cjs +2366 -909
  58. package/dist/extras.d.cts +11 -9
  59. package/dist/extras.d.ts +11 -9
  60. package/dist/extras.js +39 -28
  61. package/dist/index.cjs +421 -158
  62. package/dist/index.d.cts +213 -171
  63. package/dist/index.d.ts +213 -171
  64. package/dist/index.js +24 -27
  65. package/dist/motion.cjs +118 -44
  66. package/dist/motion.js +5 -5
  67. package/dist/patterns.cjs +344 -53
  68. package/dist/patterns.d.cts +28 -9
  69. package/dist/patterns.d.ts +28 -9
  70. package/dist/patterns.js +8 -8
  71. package/dist/performance.cjs +325 -220
  72. package/dist/performance.d.cts +2 -2
  73. package/dist/performance.d.ts +2 -2
  74. package/dist/performance.js +8 -9
  75. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  76. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  77. package/dist/plugins.cjs +664 -233
  78. package/dist/plugins.d.cts +127 -14
  79. package/dist/plugins.d.ts +127 -14
  80. package/dist/plugins.js +96 -42
  81. package/dist/signal-EotCj4hS.d.cts +110 -0
  82. package/dist/signal-EotCj4hS.d.ts +110 -0
  83. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  84. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  85. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  86. package/dist/ssr.cjs +229 -84
  87. package/dist/ssr.d.cts +9 -3
  88. package/dist/ssr.d.ts +9 -3
  89. package/dist/ssr.js +11 -12
  90. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  91. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  92. package/dist/tagFactory-8qL9LCIx.d.cts +156 -0
  93. package/dist/tagFactory-BL2fymez.d.ts +156 -0
  94. package/dist/testing.cjs +2503 -2191
  95. package/dist/testing.d.cts +56 -5
  96. package/dist/testing.d.ts +56 -5
  97. package/dist/testing.js +580 -307
  98. package/dist/types-CJFViL6Q.d.cts +26 -0
  99. package/dist/types-CJFViL6Q.d.ts +26 -0
  100. package/dist/ui.cjs +732 -329
  101. package/dist/ui.d.cts +41 -7
  102. package/dist/ui.d.ts +41 -7
  103. package/dist/ui.js +151 -56
  104. package/dist/widgets.cjs +267 -291
  105. package/dist/widgets.js +9 -10
  106. package/package.json +4 -2
  107. package/dist/chunk-2WLZ6757.js +0 -149
  108. package/dist/chunk-CCSJMTRN.js +0 -15
  109. package/dist/chunk-QKRPLZ2V.js +0 -108
  110. package/dist/chunk-VUF4ALSW.js +0 -60
  111. package/dist/chunk-WWV3SJ3L.js +0 -131
  112. package/dist/dispose-46BOMMQJ.js +0 -19
  113. package/dist/plugin-D30wlGW5.d.cts +0 -71
  114. package/dist/plugin-D30wlGW5.d.ts +0 -71
  115. package/dist/tagFactory-DVoDpHye.d.cts +0 -215
  116. package/dist/tagFactory-DVoDpHye.d.ts +0 -215
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 = [];
@@ -302,6 +376,7 @@ function retrack(effectFn, subscriber) {
302
376
  }
303
377
  currentSubscriber = subscriber;
304
378
  const sub = subscriber;
379
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
305
380
  const epoch = ++subscriberEpochCounter;
306
381
  sub._epoch = epoch;
307
382
  sub._structDirty = false;
@@ -493,7 +568,12 @@ function drainQueue() {
493
568
  quarantined.add(sub);
494
569
  continue;
495
570
  }
496
- safeInvoke(sub);
571
+ const pushed = beginDisposerCapture(sub._cap ?? null);
572
+ try {
573
+ safeInvoke(sub);
574
+ } finally {
575
+ if (pushed) endDisposerCapture();
576
+ }
497
577
  }
498
578
  }
499
579
  function drainNotificationQueue() {
@@ -629,7 +709,7 @@ function forEachSubscriber(signal2, visit) {
629
709
  }
630
710
 
631
711
  // src/reactivity/track.ts
632
- var _runtimeVersion = true ? "4.5.0" : "dev";
712
+ var _runtimeVersion = true ? "4.7.0" : "dev";
633
713
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
634
714
  function resolveReactiveApi() {
635
715
  const g = globalThis;
@@ -685,6 +765,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
685
765
  var getSubscriberDeps2 = API.getSubscriberDeps;
686
766
  var forEachSubscriber2 = API.forEachSubscriber;
687
767
 
768
+ // src/core/devtoolsHook.ts
769
+ function emitDevtools(hook, event, payload) {
770
+ try {
771
+ hook.emit(event, payload);
772
+ } catch {
773
+ }
774
+ }
775
+
688
776
  // src/core/signals/derived.ts
689
777
  function derived(getter, options) {
690
778
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
@@ -711,17 +799,17 @@ function derived(getter, options) {
711
799
  cs._d = false;
712
800
  cs._init = true;
713
801
  };
714
- track2(() => {
715
- let threw = true;
716
- try {
802
+ try {
803
+ track2(() => {
717
804
  cs._v = getter();
718
805
  cs._d = false;
719
806
  cs._init = true;
720
- threw = false;
721
- } finally {
722
- if (threw) cs._d = true;
723
- }
724
- }, markDirty);
807
+ }, markDirty);
808
+ } catch (err) {
809
+ disposed = true;
810
+ cleanup2(markDirty);
811
+ throw err;
812
+ }
725
813
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
726
814
  const validate = () => {
727
815
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -740,7 +828,7 @@ function derived(getter, options) {
740
828
  if (disposed) cleanup2(markDirty);
741
829
  }
742
830
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
743
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
831
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
744
832
  }
745
833
  };
746
834
  cs._validate = validate;
@@ -791,173 +879,10 @@ function derived(getter, options) {
791
879
  }
792
880
  }
793
881
  };
794
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
882
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
795
883
  return computedGetter;
796
884
  }
797
885
 
798
- // src/core/ssr-context.ts
799
- var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
800
- function detectSSRShared() {
801
- let detected = null;
802
- try {
803
- if (typeof process !== "undefined" && process.versions && process.versions.node) {
804
- let mod = null;
805
- const getBuiltin = process.getBuiltinModule;
806
- if (typeof getBuiltin === "function") {
807
- mod = getBuiltin("node:async_hooks");
808
- } else if (typeof require === "function") {
809
- mod = require("async_hooks");
810
- }
811
- if (mod) detected = new mod.AsyncLocalStorage();
812
- }
813
- } catch {
814
- detected = null;
815
- }
816
- return {
817
- als: detected,
818
- fallbackStore: { ssr: false, suspenseIdCounter: 0, locale: void 0 },
819
- fallbackDepth: 0
820
- };
821
- }
822
- var _shared = globalThis[SSR_KEY] ?? (globalThis[SSR_KEY] = detectSSRShared());
823
- var als = _shared.als;
824
- var fallbackStore = _shared.fallbackStore;
825
- _shared.fallbackDepth ?? (_shared.fallbackDepth = 0);
826
- function getSSRStore() {
827
- if (als) {
828
- const s = als.getStore();
829
- if (s) return s;
830
- }
831
- return fallbackStore;
832
- }
833
- function isSSR() {
834
- return getSSRStore().ssr;
835
- }
836
-
837
- // src/core/signals/effect.ts
838
- var _g = globalThis;
839
- var MAX_RERUNS = 100;
840
- function flushUserCleanups(ctx) {
841
- const list = ctx.userCleanups;
842
- if (list.length === 0) return;
843
- ctx.userCleanups = [];
844
- for (let i = list.length - 1; i >= 0; i--) {
845
- try {
846
- list[i]();
847
- } catch (err) {
848
- reportError(err, { phase: "cleanup" });
849
- }
850
- }
851
- }
852
- function drainReruns(ctx) {
853
- let reruns = 1;
854
- do {
855
- ctx.rerunPending = false;
856
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
857
- retrack2(ctx.bodyFn, ctx.subscriber);
858
- } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
859
- if (ctx.rerunPending) {
860
- ctx.rerunPending = false;
861
- reportError(
862
- new Error(
863
- `effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
864
- ),
865
- { phase: "effect" }
866
- );
867
- }
868
- }
869
- function disposeEffect(ctx) {
870
- if (ctx.disposed) return;
871
- ctx.disposed = true;
872
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
873
- if (h) {
874
- try {
875
- h.emit("effect:destroy", { effectFn: ctx.fn });
876
- } catch {
877
- }
878
- }
879
- try {
880
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
881
- } catch (err) {
882
- if (typeof console !== "undefined") {
883
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
884
- }
885
- }
886
- try {
887
- cleanup2(ctx.subscriber);
888
- } catch (err) {
889
- if (typeof console !== "undefined") {
890
- console.warn("[SibuJS effect] dispose threw:", err);
891
- }
892
- }
893
- }
894
- function effect(effectFn, options) {
895
- devAssert(typeof effectFn === "function", "effect: argument must be a function.");
896
- if (isSSR()) return () => {
897
- };
898
- const ctx = {
899
- fn: effectFn,
900
- onError: options?.onError,
901
- userCleanups: [],
902
- running: false,
903
- rerunPending: false,
904
- disposed: false,
905
- onCleanup: null,
906
- subscriber: null,
907
- bodyFn: null
908
- };
909
- ctx.onCleanup = (fn) => {
910
- ctx.userCleanups.push(fn);
911
- };
912
- const onErrorCaptured = ctx.onError;
913
- ctx.bodyFn = onErrorCaptured ? () => {
914
- try {
915
- ctx.fn(ctx.onCleanup);
916
- } catch (err) {
917
- onErrorCaptured(err);
918
- }
919
- } : () => {
920
- ctx.fn(ctx.onCleanup);
921
- };
922
- const sub = (() => {
923
- if (ctx.disposed) return;
924
- if (ctx.running) {
925
- ctx.rerunPending = true;
926
- return;
927
- }
928
- ctx.running = true;
929
- try {
930
- ctx.rerunPending = false;
931
- if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
932
- retrack2(ctx.bodyFn, sub);
933
- if (ctx.rerunPending) drainReruns(ctx);
934
- } finally {
935
- ctx.running = false;
936
- ctx.rerunPending = false;
937
- }
938
- });
939
- sub.depsHead = null;
940
- sub.depsTail = null;
941
- sub._epoch = 0;
942
- sub._structDirty = false;
943
- sub._hasComputedDep = false;
944
- sub._errorPhase = "effect";
945
- sub._runEpoch = 0;
946
- sub._runs = 0;
947
- ctx.subscriber = sub;
948
- ctx.running = true;
949
- try {
950
- retrack2(ctx.bodyFn, ctx.subscriber);
951
- if (ctx.rerunPending) drainReruns(ctx);
952
- } finally {
953
- ctx.running = false;
954
- ctx.rerunPending = false;
955
- }
956
- const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
957
- if (hook) hook.emit("effect:create", { effectFn });
958
- return () => disposeEffect(ctx);
959
- }
960
-
961
886
  // src/reactivity/batch.ts
962
887
  var batchDepth = 0;
963
888
  var pendingSignals = /* @__PURE__ */ new Set();
@@ -1009,7 +934,7 @@ var enqueueBatchedSignal = API2.enqueueBatchedSignal;
1009
934
  var isBatching = API2.isBatching;
1010
935
 
1011
936
  // src/core/signals/signal.ts
1012
- var _g2 = globalThis;
937
+ var _g = globalThis;
1013
938
  function signal(initial, options) {
1014
939
  const state2 = {
1015
940
  value: initial,
@@ -1040,8 +965,8 @@ function signal(initial, options) {
1040
965
  state2.value = newValue;
1041
966
  state2.__v++;
1042
967
  if (DEV) {
1043
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1044
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
968
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
969
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1045
970
  }
1046
971
  if (!enqueueBatchedSignal(state2)) {
1047
972
  notifySubscribers2(state2);
@@ -1054,8 +979,8 @@ function signal(initial, options) {
1054
979
  if (Object.is(newValue, prev)) return;
1055
980
  state2.value = newValue;
1056
981
  state2.__v++;
1057
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1058
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
982
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
983
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1059
984
  if (!enqueueBatchedSignal(state2)) {
1060
985
  notifySubscribers2(state2);
1061
986
  }
@@ -1073,8 +998,8 @@ function signal(initial, options) {
1073
998
  };
1074
999
  }
1075
1000
  if (DEV) {
1076
- const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1077
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1001
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1002
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
1078
1003
  }
1079
1004
  return [get, set];
1080
1005
  }
@@ -1109,6 +1034,13 @@ function watch(getter, callback) {
1109
1034
  return teardown;
1110
1035
  }
1111
1036
 
1037
+ // src/reactivity/domBinding.ts
1038
+ function domBinding(commit, ownerNode) {
1039
+ if (isSSR()) return () => {
1040
+ };
1041
+ return reactiveBinding2(commit, ownerNode);
1042
+ }
1043
+
1112
1044
  // src/widgets/Combobox.ts
1113
1045
  var boundComboboxes = /* @__PURE__ */ new WeakMap();
1114
1046
  function combobox(options) {
@@ -1182,7 +1114,7 @@ function combobox(options) {
1182
1114
  els.input.setAttribute("role", "combobox");
1183
1115
  els.input.setAttribute("aria-autocomplete", "list");
1184
1116
  els.input.setAttribute("aria-controls", listboxId);
1185
- const fxTeardown = effect(() => {
1117
+ const fxTeardown = domBinding(() => {
1186
1118
  const open2 = isOpen();
1187
1119
  els.input.setAttribute("aria-expanded", open2 ? "true" : "false");
1188
1120
  els.listbox.hidden = !open2;
@@ -1200,7 +1132,7 @@ function combobox(options) {
1200
1132
  }
1201
1133
  if (activeId) els.input.setAttribute("aria-activedescendant", activeId);
1202
1134
  else els.input.removeAttribute("aria-activedescendant");
1203
- });
1135
+ }, els.input);
1204
1136
  const onInput = () => {
1205
1137
  setQuery(els.input.value);
1206
1138
  open();
@@ -1248,8 +1180,11 @@ function combobox(options) {
1248
1180
  els.input.addEventListener("focus", onFocus);
1249
1181
  els.input.addEventListener("blur", onBlur);
1250
1182
  els.listbox.addEventListener("mousedown", onListboxPointerDown);
1183
+ let tornDown = false;
1251
1184
  const teardown = () => {
1252
- boundComboboxes.delete(els.input);
1185
+ if (tornDown) return;
1186
+ tornDown = true;
1187
+ if (boundComboboxes.get(els.input) === teardown) boundComboboxes.delete(els.input);
1253
1188
  fxTeardown();
1254
1189
  els.input.removeEventListener("input", onInput);
1255
1190
  els.input.removeEventListener("keydown", onKey);
@@ -1294,11 +1229,23 @@ function combobox(options) {
1294
1229
  };
1295
1230
  }
1296
1231
 
1232
+ // src/widgets/attributeSnapshot.ts
1233
+ function snapshotAttributes(el, names) {
1234
+ const previous = names.map((name) => [name, el.getAttribute(name)]);
1235
+ return () => {
1236
+ for (const [name, value] of previous) {
1237
+ if (value === null) el.removeAttribute(name);
1238
+ else el.setAttribute(name, value);
1239
+ }
1240
+ };
1241
+ }
1242
+
1297
1243
  // src/widgets/Tabs.ts
1298
1244
  var boundTablists = /* @__PURE__ */ new WeakMap();
1299
1245
  function tabs(options) {
1300
1246
  const { tabs: tabDefs, defaultTab } = options;
1301
- const initialTab = defaultTab ?? tabDefs.find((t) => !t.disabled)?.id ?? tabDefs[0]?.id ?? "";
1247
+ const firstEnabled = tabDefs.find((t) => !t.disabled)?.id ?? "";
1248
+ const initialTab = defaultTab !== void 0 && tabDefs.some((t) => t.id === defaultTab && !t.disabled) ? defaultTab : firstEnabled;
1302
1249
  const [activeTab, setActiveTabState] = signal(initialTab);
1303
1250
  function setActiveTab(id) {
1304
1251
  const tab = tabDefs.find((t) => t.id === id);
@@ -1345,58 +1292,29 @@ function tabs(options) {
1345
1292
  function bind(els) {
1346
1293
  const existing = boundTablists.get(els.tablist);
1347
1294
  if (existing) return existing;
1348
- const restore = [];
1349
- const prevTablistRole = els.tablist.getAttribute("role");
1295
+ const restore = [snapshotAttributes(els.tablist, ["role"])];
1350
1296
  els.tablist.setAttribute("role", "tablist");
1351
- restore.push(() => {
1352
- if (prevTablistRole === null) els.tablist.removeAttribute("role");
1353
- else els.tablist.setAttribute("role", prevTablistRole);
1354
- });
1297
+ const idPrefix = createId("sibu-tabs");
1355
1298
  for (const def of tabDefs) {
1356
1299
  const tabEl = els.tabs[def.id];
1357
1300
  if (!tabEl) continue;
1358
- const prevRole = tabEl.getAttribute("role");
1359
- const prevId = tabEl.id;
1360
- const prevDisabled = tabEl.getAttribute("aria-disabled");
1361
- const prevControls = tabEl.getAttribute("aria-controls");
1301
+ restore.push(
1302
+ snapshotAttributes(tabEl, ["role", "id", "aria-disabled", "aria-controls", "aria-selected", "tabindex"])
1303
+ );
1362
1304
  tabEl.setAttribute("role", "tab");
1363
- tabEl.setAttribute("id", `sibu-tab-${def.id}`);
1305
+ if (!tabEl.id) tabEl.id = `${idPrefix}-tab-${idSegment(def.id)}`;
1364
1306
  if (def.disabled) tabEl.setAttribute("aria-disabled", "true");
1307
+ else tabEl.removeAttribute("aria-disabled");
1365
1308
  const panelEl = els.panels?.[def.id];
1366
- let prevPanelRole = null;
1367
- let prevPanelId = "";
1368
- let prevPanelLabelledBy = null;
1369
1309
  if (panelEl) {
1370
- prevPanelRole = panelEl.getAttribute("role");
1371
- prevPanelId = panelEl.id;
1372
- prevPanelLabelledBy = panelEl.getAttribute("aria-labelledby");
1310
+ restore.push(snapshotAttributes(panelEl, ["role", "id", "aria-labelledby", "hidden"]));
1373
1311
  panelEl.setAttribute("role", "tabpanel");
1374
- panelEl.setAttribute("id", `sibu-tabpanel-${def.id}`);
1375
- panelEl.setAttribute("aria-labelledby", `sibu-tab-${def.id}`);
1376
- tabEl.setAttribute("aria-controls", `sibu-tabpanel-${def.id}`);
1312
+ if (!panelEl.id) panelEl.id = `${idPrefix}-panel-${idSegment(def.id)}`;
1313
+ panelEl.setAttribute("aria-labelledby", tabEl.id);
1314
+ tabEl.setAttribute("aria-controls", panelEl.id);
1377
1315
  }
1378
- restore.push(() => {
1379
- if (prevRole === null) tabEl.removeAttribute("role");
1380
- else tabEl.setAttribute("role", prevRole);
1381
- if (prevId === "") tabEl.removeAttribute("id");
1382
- else tabEl.id = prevId;
1383
- if (prevDisabled === null) tabEl.removeAttribute("aria-disabled");
1384
- else tabEl.setAttribute("aria-disabled", prevDisabled);
1385
- if (prevControls === null) tabEl.removeAttribute("aria-controls");
1386
- else tabEl.setAttribute("aria-controls", prevControls);
1387
- tabEl.removeAttribute("aria-selected");
1388
- tabEl.removeAttribute("tabindex");
1389
- if (panelEl) {
1390
- if (prevPanelRole === null) panelEl.removeAttribute("role");
1391
- else panelEl.setAttribute("role", prevPanelRole);
1392
- if (prevPanelId === "") panelEl.removeAttribute("id");
1393
- else panelEl.id = prevPanelId;
1394
- if (prevPanelLabelledBy === null) panelEl.removeAttribute("aria-labelledby");
1395
- else panelEl.setAttribute("aria-labelledby", prevPanelLabelledBy);
1396
- }
1397
- });
1398
1316
  }
1399
- const fxTeardown = effect(() => {
1317
+ const fxTeardown = domBinding(() => {
1400
1318
  const active = activeTab();
1401
1319
  for (const def of tabDefs) {
1402
1320
  const tabEl = els.tabs[def.id];
@@ -1407,7 +1325,7 @@ function tabs(options) {
1407
1325
  const panelEl = els.panels?.[def.id];
1408
1326
  if (panelEl) panelEl.hidden = !isAct;
1409
1327
  }
1410
- });
1328
+ }, els.tablist);
1411
1329
  const onKey = (e) => {
1412
1330
  if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1413
1331
  e.preventDefault();
@@ -1444,7 +1362,10 @@ function tabs(options) {
1444
1362
  tabEl.addEventListener("click", fn);
1445
1363
  clickHandlers.push({ el: tabEl, fn });
1446
1364
  }
1365
+ let tornDown = false;
1447
1366
  const teardown = () => {
1367
+ if (tornDown) return;
1368
+ tornDown = true;
1448
1369
  boundTablists.delete(els.tablist);
1449
1370
  fxTeardown();
1450
1371
  els.tablist.removeEventListener("keydown", onKey);
@@ -1470,7 +1391,10 @@ function tabs(options) {
1470
1391
  var boundAccordions = /* @__PURE__ */ new WeakMap();
1471
1392
  function accordion(options) {
1472
1393
  const { items: itemDefs, multiple = false, defaultExpanded = [] } = options;
1473
- const [expandedIds, setExpandedIds] = signal(new Set(defaultExpanded));
1394
+ const knownIds = new Set(itemDefs.map((item) => item.id));
1395
+ const validDefaults = defaultExpanded.filter((id) => knownIds.has(id));
1396
+ const initialExpanded = multiple ? validDefaults : validDefaults.slice(0, 1);
1397
+ const [expandedIds, setExpandedIds] = signal(new Set(initialExpanded));
1474
1398
  const items = derived(
1475
1399
  () => itemDefs.map((item) => ({
1476
1400
  ...item,
@@ -1521,42 +1445,22 @@ function accordion(options) {
1521
1445
  if (existing) return existing;
1522
1446
  }
1523
1447
  const restore = [];
1448
+ const idPrefix = createId("sibu-accordion");
1524
1449
  for (const item of itemDefs) {
1525
1450
  const trig = els.triggers[item.id];
1526
1451
  const panel = els.panels[item.id];
1527
1452
  if (!trig) continue;
1528
- const prevTrigId = trig.id;
1529
- const prevTrigControls = trig.getAttribute("aria-controls");
1530
- trig.id = `sibu-accordion-trigger-${item.id}`;
1531
- let prevPanelRole = null;
1532
- let prevPanelId = "";
1533
- let prevPanelLabelledBy = null;
1453
+ restore.push(snapshotAttributes(trig, ["id", "aria-controls", "aria-expanded"]));
1454
+ if (!trig.id) trig.id = `${idPrefix}-trigger-${idSegment(item.id)}`;
1534
1455
  if (panel) {
1535
- prevPanelRole = panel.getAttribute("role");
1536
- prevPanelId = panel.id;
1537
- prevPanelLabelledBy = panel.getAttribute("aria-labelledby");
1456
+ restore.push(snapshotAttributes(panel, ["role", "id", "aria-labelledby", "hidden"]));
1538
1457
  panel.setAttribute("role", "region");
1539
- panel.id = `sibu-accordion-panel-${item.id}`;
1458
+ if (!panel.id) panel.id = `${idPrefix}-panel-${idSegment(item.id)}`;
1540
1459
  panel.setAttribute("aria-labelledby", trig.id);
1541
1460
  trig.setAttribute("aria-controls", panel.id);
1542
1461
  }
1543
- restore.push(() => {
1544
- if (prevTrigId === "") trig.removeAttribute("id");
1545
- else trig.id = prevTrigId;
1546
- if (prevTrigControls === null) trig.removeAttribute("aria-controls");
1547
- else trig.setAttribute("aria-controls", prevTrigControls);
1548
- trig.removeAttribute("aria-expanded");
1549
- if (panel) {
1550
- if (prevPanelRole === null) panel.removeAttribute("role");
1551
- else panel.setAttribute("role", prevPanelRole);
1552
- if (prevPanelId === "") panel.removeAttribute("id");
1553
- else panel.id = prevPanelId;
1554
- if (prevPanelLabelledBy === null) panel.removeAttribute("aria-labelledby");
1555
- else panel.setAttribute("aria-labelledby", prevPanelLabelledBy);
1556
- }
1557
- });
1558
1462
  }
1559
- const fxTeardown = effect(() => {
1463
+ const fxTeardown = domBinding(() => {
1560
1464
  const ids = expandedIds();
1561
1465
  for (const item of itemDefs) {
1562
1466
  const trig = els.triggers[item.id];
@@ -1566,7 +1470,7 @@ function accordion(options) {
1566
1470
  trig.setAttribute("aria-expanded", expanded ? "true" : "false");
1567
1471
  if (panel) panel.hidden = !expanded;
1568
1472
  }
1569
- });
1473
+ }, idempotencyKey);
1570
1474
  const handlers = [];
1571
1475
  for (const item of itemDefs) {
1572
1476
  const trig = els.triggers[item.id];
@@ -1582,7 +1486,10 @@ function accordion(options) {
1582
1486
  trig.addEventListener("keydown", key);
1583
1487
  handlers.push({ el: trig, click, key });
1584
1488
  }
1489
+ let tornDown = false;
1585
1490
  const teardown = () => {
1491
+ if (tornDown) return;
1492
+ tornDown = true;
1586
1493
  if (idempotencyKey) boundAccordions.delete(idempotencyKey);
1587
1494
  fxTeardown();
1588
1495
  for (const { el, click, key } of handlers) {
@@ -1641,11 +1548,11 @@ function popover() {
1641
1548
  }
1642
1549
  els.popover.setAttribute("aria-labelledby", els.labelledBy.id);
1643
1550
  }
1644
- const fxTeardown = effect(() => {
1551
+ const fxTeardown = domBinding(() => {
1645
1552
  const open2 = isOpen();
1646
1553
  els.trigger.setAttribute("aria-expanded", open2 ? "true" : "false");
1647
1554
  els.popover.hidden = !open2;
1648
- });
1555
+ }, els.trigger);
1649
1556
  const onTriggerClick = (e) => {
1650
1557
  e.preventDefault();
1651
1558
  toggle();
@@ -1668,8 +1575,11 @@ function popover() {
1668
1575
  els.popover.addEventListener("keydown", onKey);
1669
1576
  els.trigger.addEventListener("keydown", onKey);
1670
1577
  document.addEventListener("pointerdown", onDocPointer);
1578
+ let tornDown = false;
1671
1579
  const teardown = () => {
1672
- boundPopovers.delete(els.trigger);
1580
+ if (tornDown) return;
1581
+ tornDown = true;
1582
+ if (boundPopovers.get(els.trigger) === teardown) boundPopovers.delete(els.trigger);
1673
1583
  fxTeardown();
1674
1584
  els.trigger.removeEventListener("click", onTriggerClick);
1675
1585
  els.popover.removeEventListener("keydown", onKey);
@@ -1787,7 +1697,7 @@ function select(options) {
1787
1697
  els.listbox.setAttribute("aria-multiselectable", multiple ? "true" : "false");
1788
1698
  if (els.listbox.tabIndex < 0) els.listbox.tabIndex = 0;
1789
1699
  const toStr = els.itemToString ?? itemToString ?? ((it) => String(it));
1790
- const fxTeardown = effect(() => {
1700
+ const fxTeardown = domBinding(() => {
1791
1701
  const idx = highlightedIndex();
1792
1702
  const sel = selectedItems();
1793
1703
  let activeId = "";
@@ -1797,13 +1707,14 @@ function select(options) {
1797
1707
  if (!optEl.id) optEl.id = `${listboxId}-opt-${i}`;
1798
1708
  optEl.setAttribute("role", "option");
1799
1709
  optEl.setAttribute("aria-selected", sel.includes(items[i]) ? "true" : "false");
1800
- if (isItemDisabled(items[i])) optEl.setAttribute("aria-disabled", "true");
1710
+ const disabled = isItemDisabled(items[i]);
1711
+ if (disabled) optEl.setAttribute("aria-disabled", "true");
1801
1712
  else optEl.removeAttribute("aria-disabled");
1802
- if (i === idx) activeId = optEl.id;
1713
+ if (i === idx && !disabled) activeId = optEl.id;
1803
1714
  }
1804
1715
  if (activeId) els.listbox.setAttribute("aria-activedescendant", activeId);
1805
1716
  else els.listbox.removeAttribute("aria-activedescendant");
1806
- });
1717
+ }, els.listbox);
1807
1718
  let typeBuffer = "";
1808
1719
  let typeTimer = null;
1809
1720
  const onKey = (e) => {
@@ -1815,10 +1726,12 @@ function select(options) {
1815
1726
  highlightPrev();
1816
1727
  } else if (e.key === "Home") {
1817
1728
  e.preventDefault();
1818
- if (items.length > 0) setHighlightedIndex(0);
1729
+ const first = nextEnabled(-1, 1);
1730
+ if (first !== -1) setHighlightedIndex(first);
1819
1731
  } else if (e.key === "End") {
1820
1732
  e.preventDefault();
1821
- if (items.length > 0) setHighlightedIndex(items.length - 1);
1733
+ const last = nextEnabled(items.length, -1);
1734
+ if (last !== -1) setHighlightedIndex(last);
1822
1735
  } else if (e.key === "Enter" || e.key === " ") {
1823
1736
  if (highlightedIndex() >= 0) {
1824
1737
  e.preventDefault();
@@ -1836,8 +1749,11 @@ function select(options) {
1836
1749
  }
1837
1750
  };
1838
1751
  els.listbox.addEventListener("keydown", onKey);
1752
+ let tornDown = false;
1839
1753
  const teardown = () => {
1840
- boundSelects.delete(els.listbox);
1754
+ if (tornDown) return;
1755
+ tornDown = true;
1756
+ if (boundSelects.get(els.listbox) === teardown) boundSelects.delete(els.listbox);
1841
1757
  fxTeardown();
1842
1758
  els.listbox.removeEventListener("keydown", onKey);
1843
1759
  if (typeTimer !== null) clearTimeout(typeTimer);
@@ -1923,9 +1839,9 @@ function tooltip(options) {
1923
1839
  els.tooltip.id = id;
1924
1840
  const prevDescribedBy = els.trigger.getAttribute("aria-describedby");
1925
1841
  els.trigger.setAttribute("aria-describedby", prevDescribedBy ? `${prevDescribedBy} ${id}` : id);
1926
- const fxTeardown = effect(() => {
1842
+ const fxTeardown = domBinding(() => {
1927
1843
  els.tooltip.hidden = !isVisible();
1928
- });
1844
+ }, els.trigger);
1929
1845
  const onTriggerEnter = () => show();
1930
1846
  const onTriggerLeave = () => scheduleHide();
1931
1847
  const onTooltipEnter = () => {
@@ -1950,8 +1866,11 @@ function tooltip(options) {
1950
1866
  els.trigger.addEventListener("keydown", onKey);
1951
1867
  els.tooltip.addEventListener("pointerenter", onTooltipEnter);
1952
1868
  els.tooltip.addEventListener("pointerleave", onTooltipLeave);
1869
+ let tornDown = false;
1953
1870
  const teardown = () => {
1954
- boundTriggers.delete(els.trigger);
1871
+ if (tornDown) return;
1872
+ tornDown = true;
1873
+ if (boundTriggers.get(els.trigger) === teardown) boundTriggers.delete(els.trigger);
1955
1874
  fxTeardown();
1956
1875
  els.trigger.removeEventListener("pointerenter", onTriggerEnter);
1957
1876
  els.trigger.removeEventListener("pointerleave", onTriggerLeave);
@@ -1989,7 +1908,8 @@ function fileUpload(options) {
1989
1908
  const [isDragOver, setDragOver] = signal(false);
1990
1909
  function isAccepted(file) {
1991
1910
  if (!accept) return true;
1992
- const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase());
1911
+ const acceptedTypes = accept.split(",").map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0);
1912
+ if (acceptedTypes.length === 0) return true;
1993
1913
  const fileName = file.name.toLowerCase();
1994
1914
  const fileType = file.type.toLowerCase();
1995
1915
  return acceptedTypes.some((pattern) => {
@@ -2018,16 +1938,17 @@ function fileUpload(options) {
2018
1938
  }
2019
1939
  validFiles.push(file);
2020
1940
  }
1941
+ const committed = multiple ? validFiles : validFiles.slice(-1);
2021
1942
  batch(() => {
2022
1943
  setErrors(newErrors);
2023
- if (validFiles.length > 0) {
1944
+ if (committed.length > 0) {
2024
1945
  if (multiple) {
2025
- setFiles((prev) => [...prev, ...validFiles]);
1946
+ setFiles((prev) => [...prev, ...committed]);
2026
1947
  } else {
2027
- setFiles([validFiles[validFiles.length - 1]]);
1948
+ setFiles(committed);
2028
1949
  }
2029
1950
  if (onFiles) {
2030
- onFiles(validFiles);
1951
+ onFiles(committed);
2031
1952
  }
2032
1953
  }
2033
1954
  });
@@ -2051,8 +1972,15 @@ function fileUpload(options) {
2051
1972
  if (existing) return existing;
2052
1973
  const id = createId("sibu-fileupload");
2053
1974
  const restore = [];
1975
+ const prevAccept = els.input.getAttribute("accept");
1976
+ const prevMultiple = els.input.hasAttribute("multiple");
2054
1977
  if (accept) els.input.accept = accept;
2055
1978
  els.input.multiple = multiple;
1979
+ restore.push(() => {
1980
+ if (prevAccept === null) els.input.removeAttribute("accept");
1981
+ else els.input.setAttribute("accept", prevAccept);
1982
+ els.input.multiple = prevMultiple;
1983
+ });
2056
1984
  let hintId = null;
2057
1985
  if (els.hint) {
2058
1986
  const assignedHintId = !els.hint.id;
@@ -2073,6 +2001,7 @@ function fileUpload(options) {
2073
2001
  if (els.errorRegion) {
2074
2002
  const prevRole = els.errorRegion.getAttribute("role");
2075
2003
  const prevLive = els.errorRegion.getAttribute("aria-live");
2004
+ const prevText = els.errorRegion.textContent;
2076
2005
  els.errorRegion.setAttribute("role", "status");
2077
2006
  els.errorRegion.setAttribute("aria-live", "polite");
2078
2007
  restore.push(() => {
@@ -2080,12 +2009,14 @@ function fileUpload(options) {
2080
2009
  else els.errorRegion.setAttribute("role", prevRole);
2081
2010
  if (prevLive === null) els.errorRegion.removeAttribute("aria-live");
2082
2011
  else els.errorRegion.setAttribute("aria-live", prevLive);
2012
+ els.errorRegion.textContent = prevText;
2083
2013
  });
2084
2014
  }
2085
2015
  if (els.dropZone) {
2086
2016
  const prevDzRole = els.dropZone.getAttribute("role");
2087
2017
  const prevDzLabel = els.dropZone.getAttribute("aria-label");
2088
2018
  const prevDzTabindex = els.dropZone.hasAttribute("tabindex") ? els.dropZone.getAttribute("tabindex") : null;
2019
+ const prevDzDragOver = els.dropZone.getAttribute("data-drag-over");
2089
2020
  els.dropZone.setAttribute("role", "button");
2090
2021
  els.dropZone.setAttribute("aria-label", "File drop zone \u2014 click or press Enter to browse");
2091
2022
  if (els.dropZone.tabIndex < 0) els.dropZone.tabIndex = 0;
@@ -2096,13 +2027,15 @@ function fileUpload(options) {
2096
2027
  else els.dropZone.setAttribute("aria-label", prevDzLabel);
2097
2028
  if (prevDzTabindex === null) els.dropZone.removeAttribute("tabindex");
2098
2029
  else els.dropZone.setAttribute("tabindex", prevDzTabindex);
2030
+ if (prevDzDragOver === null) els.dropZone.removeAttribute("data-drag-over");
2031
+ else els.dropZone.setAttribute("data-drag-over", prevDzDragOver);
2099
2032
  });
2100
2033
  }
2101
- const fxTeardown = effect(() => {
2034
+ const fxTeardown = domBinding(() => {
2102
2035
  const errs = errors();
2103
2036
  if (els.errorRegion) els.errorRegion.textContent = errs.join(". ");
2104
2037
  if (els.dropZone) els.dropZone.setAttribute("data-drag-over", isDragOver() ? "true" : "false");
2105
- });
2038
+ }, els.input);
2106
2039
  const onChange = () => {
2107
2040
  if (els.input.files) addFiles(els.input.files);
2108
2041
  };
@@ -2131,7 +2064,10 @@ function fileUpload(options) {
2131
2064
  els.dropZone.addEventListener("dragleave", onDragLeave);
2132
2065
  els.dropZone.addEventListener("drop", onDrop);
2133
2066
  }
2067
+ let tornDown = false;
2134
2068
  const teardown = () => {
2069
+ if (tornDown) return;
2070
+ tornDown = true;
2135
2071
  boundFileUploads.delete(els.input);
2136
2072
  fxTeardown();
2137
2073
  els.input.removeEventListener("change", onChange);
@@ -2178,12 +2114,15 @@ function stripHtml(html) {
2178
2114
  }
2179
2115
 
2180
2116
  // src/widgets/contentEditable.ts
2117
+ function toInertText(html) {
2118
+ return stripHtml(String(html)).replace(/<(?=[a-z!/?])/gi, "&lt;");
2119
+ }
2181
2120
  function contentEditable(element) {
2182
2121
  const [content, setContentInternal] = signal("");
2183
2122
  const [isFocused, setFocused] = signal(false);
2184
2123
  function setContent(input) {
2185
2124
  if (typeof input === "string") {
2186
- setContentInternal(input);
2125
+ setContentInternal(toInertText(input));
2187
2126
  return;
2188
2127
  }
2189
2128
  if (typeof input.text === "string") {
@@ -2192,7 +2131,7 @@ function contentEditable(element) {
2192
2131
  }
2193
2132
  if (typeof input.html === "string") {
2194
2133
  const shouldSanitize = input.sanitize !== false;
2195
- setContentInternal(shouldSanitize ? stripHtml(input.html) : input.html);
2134
+ setContentInternal(shouldSanitize ? toInertText(input.html) : input.html);
2196
2135
  return;
2197
2136
  }
2198
2137
  setContentInternal("");
@@ -2204,7 +2143,7 @@ function contentEditable(element) {
2204
2143
  const range = selection.getRangeAt(0);
2205
2144
  if (element && !element.contains(range.commonAncestorContainer)) return;
2206
2145
  const ancestor = range.commonAncestorContainer;
2207
- const existingWrap = findAncestorByTag(
2146
+ const existingWrap = findWrapperWithinEditor(
2208
2147
  ancestor instanceof HTMLElement ? ancestor : ancestor.parentElement,
2209
2148
  tagName
2210
2149
  );
@@ -2245,14 +2184,19 @@ function contentEditable(element) {
2245
2184
  selection.addRange(newRange);
2246
2185
  }
2247
2186
  }
2248
- function findAncestorByTag(el, tagName) {
2187
+ function findWrapperWithinEditor(el, tagName) {
2249
2188
  const upper = tagName.toUpperCase();
2250
2189
  while (el) {
2190
+ if (el === element || isEditingHost(el)) return null;
2251
2191
  if (el.tagName === upper) return el;
2252
2192
  el = el.parentElement;
2253
2193
  }
2254
2194
  return null;
2255
2195
  }
2196
+ function isEditingHost(el) {
2197
+ const attr = el.getAttribute("contenteditable");
2198
+ return attr !== null && attr.toLowerCase() !== "false";
2199
+ }
2256
2200
  function bold() {
2257
2201
  wrapSelection("strong");
2258
2202
  }
@@ -2388,15 +2332,36 @@ function datePicker(options) {
2388
2332
  els.grid.setAttribute("role", "grid");
2389
2333
  if (els.grid.tabIndex < 0) els.grid.tabIndex = 0;
2390
2334
  let pendingFocus = false;
2391
- const fxTeardown = effect(() => {
2335
+ const touchedCells = /* @__PURE__ */ new Map();
2336
+ const restoreAttr = (el, name, value) => {
2337
+ if (value === null) el.removeAttribute(name);
2338
+ else el.setAttribute(name, value);
2339
+ };
2340
+ const restoreCell = (cell, snap) => {
2341
+ restoreAttr(cell, "role", snap.role);
2342
+ restoreAttr(cell, "aria-selected", snap.selected);
2343
+ restoreAttr(cell, "aria-disabled", snap.disabled);
2344
+ restoreAttr(cell, "tabindex", snap.tabindex);
2345
+ };
2346
+ const fxTeardown = domBinding(() => {
2392
2347
  const sel = selectedDate();
2393
2348
  const view = viewDate();
2394
2349
  const days = daysInMonth();
2395
2350
  els.grid.setAttribute("aria-label", view.toLocaleDateString(void 0, { month: "long", year: "numeric" }));
2396
2351
  let viewCell = null;
2352
+ const displayed = /* @__PURE__ */ new Set();
2397
2353
  for (const d of days) {
2398
2354
  const cell = els.cell(d.date);
2399
2355
  if (!cell) continue;
2356
+ displayed.add(cell);
2357
+ if (!touchedCells.has(cell)) {
2358
+ touchedCells.set(cell, {
2359
+ role: cell.getAttribute("role"),
2360
+ selected: cell.getAttribute("aria-selected"),
2361
+ disabled: cell.getAttribute("aria-disabled"),
2362
+ tabindex: cell.getAttribute("tabindex")
2363
+ });
2364
+ }
2400
2365
  cell.setAttribute("role", "gridcell");
2401
2366
  cell.setAttribute("aria-selected", sel && isSameCalendarDay(sel, d.date) ? "true" : "false");
2402
2367
  if (d.isDisabled) cell.setAttribute("aria-disabled", "true");
@@ -2405,11 +2370,17 @@ function datePicker(options) {
2405
2370
  cell.tabIndex = isView ? 0 : -1;
2406
2371
  if (isView) viewCell = cell;
2407
2372
  }
2373
+ for (const [cell, snap] of touchedCells) {
2374
+ if (!displayed.has(cell)) {
2375
+ restoreCell(cell, snap);
2376
+ touchedCells.delete(cell);
2377
+ }
2378
+ }
2408
2379
  if (pendingFocus && viewCell && typeof viewCell.focus === "function") {
2409
2380
  pendingFocus = false;
2410
2381
  viewCell.focus();
2411
2382
  }
2412
- });
2383
+ }, els.grid);
2413
2384
  function isSameCalendarDay(a, b) {
2414
2385
  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2415
2386
  }
@@ -2464,8 +2435,11 @@ function datePicker(options) {
2464
2435
  }
2465
2436
  };
2466
2437
  els.grid.addEventListener("keydown", onKey);
2438
+ let tornDown = false;
2467
2439
  const teardown = () => {
2468
- boundDatePickers.delete(els.grid);
2440
+ if (tornDown) return;
2441
+ tornDown = true;
2442
+ if (boundDatePickers.get(els.grid) === teardown) boundDatePickers.delete(els.grid);
2469
2443
  fxTeardown();
2470
2444
  els.grid.removeEventListener("keydown", onKey);
2471
2445
  if (prevGridRole === null) els.grid.removeAttribute("role");
@@ -2474,6 +2448,8 @@ function datePicker(options) {
2474
2448
  else els.grid.setAttribute("tabindex", prevGridTabIndex);
2475
2449
  if (prevGridLabel === null) els.grid.removeAttribute("aria-label");
2476
2450
  else els.grid.setAttribute("aria-label", prevGridLabel);
2451
+ for (const [cell, snap] of touchedCells) restoreCell(cell, snap);
2452
+ touchedCells.clear();
2477
2453
  };
2478
2454
  boundDatePickers.set(els.grid, teardown);
2479
2455
  return teardown;