sibujs 3.3.3 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/browser.cjs +1 -1
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +3348 -3021
  4. package/dist/build.js +11 -10
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-IWFE4AHO.js → chunk-335KJEN2.js} +1 -1
  7. package/dist/{chunk-QCQQ2N5H.js → chunk-3FFCUIGV.js} +6 -6
  8. package/dist/{chunk-F3HO2MP7.js → chunk-4I3M326N.js} +1 -1
  9. package/dist/{chunk-IQN5W7GE.js → chunk-5MFF5EPT.js} +4 -4
  10. package/dist/{chunk-ZXCZWMZT.js → chunk-6CAJU5TP.js} +1 -1
  11. package/dist/{chunk-Y35NQNLG.js → chunk-7PNKW3FJ.js} +8 -8
  12. package/dist/{chunk-UOMYIAG6.js → chunk-BQLVTAUZ.js} +1 -1
  13. package/dist/{chunk-25LY5SRH.js → chunk-CV7FAQ2R.js} +1 -1
  14. package/dist/{chunk-SBPUXWCE.js → chunk-DLBFEAIS.js} +1 -1
  15. package/dist/{chunk-QTOOBABV.js → chunk-FWFEBYQ6.js} +2 -2
  16. package/dist/{chunk-5Y34FWED.js → chunk-MPE75KOA.js} +7 -7
  17. package/dist/{chunk-RZKGMHH4.js → chunk-NXR2YFZB.js} +2 -2
  18. package/dist/{chunk-IEYFHN5V.js → chunk-PBZWJQNY.js} +3 -3
  19. package/dist/{chunk-TBDTU7UO.js → chunk-QHPUFNHZ.js} +1 -1
  20. package/dist/{chunk-MYXEBADX.js → chunk-S2VW4NBA.js} +2907 -2577
  21. package/dist/{chunk-5K6RVA2S.js → chunk-VQZDHAEK.js} +1 -1
  22. package/dist/{chunk-WTFMV2RU.js → chunk-WEZ5SP7L.js} +2 -2
  23. package/dist/{chunk-23VT3KZB.js → chunk-X4JS367G.js} +1 -1
  24. package/dist/{chunk-TVCCXPU2.js → chunk-Y22QAKJX.js} +4 -4
  25. package/dist/{chunk-WFUUT6TG.js → chunk-YDWP3ZOU.js} +3 -3
  26. package/dist/data.cjs +1 -1
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +1 -1
  29. package/dist/devtools.js +4 -4
  30. package/dist/ecosystem.cjs +1 -1
  31. package/dist/ecosystem.d.cts +1 -1
  32. package/dist/ecosystem.d.ts +1 -1
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +1 -1
  35. package/dist/extras.d.cts +1 -1
  36. package/dist/extras.d.ts +1 -1
  37. package/dist/extras.js +20 -20
  38. package/dist/index.cjs +3350 -3017
  39. package/dist/index.d.cts +1175 -1056
  40. package/dist/index.d.ts +1175 -1056
  41. package/dist/index.js +23 -10
  42. package/dist/motion.cjs +1 -1
  43. package/dist/motion.js +3 -3
  44. package/dist/patterns.cjs +1 -1
  45. package/dist/patterns.js +5 -5
  46. package/dist/performance.cjs +1 -1
  47. package/dist/performance.js +5 -5
  48. package/dist/plugins.cjs +5 -2
  49. package/dist/plugins.js +13 -10
  50. package/dist/ssr.cjs +1 -1
  51. package/dist/ssr.js +8 -8
  52. package/dist/{tagFactory-S17H2qxu.d.cts → tagFactory-Bzupt4Pj.d.cts} +1 -1
  53. package/dist/{tagFactory-S17H2qxu.d.ts → tagFactory-Bzupt4Pj.d.ts} +1 -1
  54. package/dist/testing.cjs +1 -1
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +1 -1
  57. package/dist/ui.js +9 -9
  58. package/dist/widgets.cjs +1 -1
  59. package/dist/widgets.js +6 -6
  60. package/package.json +4 -1
package/dist/index.d.ts CHANGED
@@ -1,414 +1,347 @@
1
- import { T as TagProps, N as NodeChildren, a as NodeChild } from './tagFactory-S17H2qxu.js';
2
- export { D as Dispose, S as SVG_NS, t as tagFactory } from './tagFactory-S17H2qxu.js';
3
- import { R as ReactiveSignal } from './signal-BnWpq6WB.js';
1
+ import { N as NodeChild, a as NodeChildren, T as TagProps } from './tagFactory-Bzupt4Pj.js';
2
+ export { D as Dispose, S as SVG_NS, t as tagFactory } from './tagFactory-Bzupt4Pj.js';
4
3
  export { T as TrustedHTML, t as trustHTML } from './ssr-D62yFwuw.js';
4
+ import { R as ReactiveSignal } from './signal-BnWpq6WB.js';
5
5
 
6
- type reactive<T> = T | (() => T);
7
- interface AnchorProps extends TagProps {
8
- href?: reactive<string>;
9
- target?: "_self" | "_blank" | "_parent" | "_top" | (string & {});
10
- rel?: reactive<string>;
11
- download?: reactive<string | boolean>;
12
- hreflang?: reactive<string>;
13
- referrerpolicy?: reactive<"" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url">;
14
- ping?: reactive<string>;
15
- }
16
- type InputType = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
17
- interface InputProps extends TagProps {
18
- type?: InputType | (string & {});
19
- name?: reactive<string>;
20
- value?: reactive<string | number>;
21
- placeholder?: reactive<string>;
22
- required?: reactive<boolean>;
23
- disabled?: reactive<boolean>;
24
- readonly?: reactive<boolean>;
25
- checked?: reactive<boolean>;
26
- min?: reactive<string | number>;
27
- max?: reactive<string | number>;
28
- step?: reactive<string | number>;
29
- minlength?: reactive<number>;
30
- maxlength?: reactive<number>;
31
- pattern?: reactive<string>;
32
- autocomplete?: reactive<string>;
33
- autofocus?: reactive<boolean>;
34
- multiple?: reactive<boolean>;
35
- accept?: reactive<string>;
36
- size?: reactive<number>;
37
- form?: reactive<string>;
38
- list?: reactive<string>;
39
- inputmode?: reactive<"none" | "text" | "decimal" | "numeric" | "tel" | "search" | "email" | "url">;
40
- }
41
- interface ImgProps extends TagProps {
42
- src?: reactive<string>;
43
- alt?: reactive<string>;
44
- width?: reactive<number | string>;
45
- height?: reactive<number | string>;
46
- loading?: reactive<"lazy" | "eager">;
47
- decoding?: reactive<"sync" | "async" | "auto">;
48
- srcset?: reactive<string>;
49
- sizes?: reactive<string>;
50
- crossorigin?: reactive<"anonymous" | "use-credentials">;
51
- referrerpolicy?: reactive<string>;
52
- }
53
- interface ButtonProps extends TagProps {
54
- type?: reactive<"button" | "submit" | "reset">;
55
- name?: reactive<string>;
56
- value?: reactive<string>;
57
- disabled?: reactive<boolean>;
58
- form?: reactive<string>;
59
- formaction?: reactive<string>;
60
- formenctype?: reactive<string>;
61
- formmethod?: reactive<"get" | "post" | "dialog">;
62
- formnovalidate?: reactive<boolean>;
63
- formtarget?: reactive<string>;
64
- autofocus?: reactive<boolean>;
65
- }
66
- interface FormProps extends TagProps {
67
- action?: reactive<string>;
68
- method?: reactive<"get" | "post" | "dialog">;
69
- enctype?: reactive<"application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain">;
70
- name?: reactive<string>;
71
- novalidate?: reactive<boolean>;
72
- target?: reactive<string>;
73
- autocomplete?: reactive<"on" | "off">;
74
- acceptcharset?: reactive<string>;
75
- }
76
- interface SelectProps extends TagProps {
77
- name?: reactive<string>;
78
- value?: reactive<string>;
79
- disabled?: reactive<boolean>;
80
- multiple?: reactive<boolean>;
81
- required?: reactive<boolean>;
82
- size?: reactive<number>;
83
- autocomplete?: reactive<string>;
84
- autofocus?: reactive<boolean>;
85
- form?: reactive<string>;
86
- }
87
- interface TextareaProps extends TagProps {
88
- name?: reactive<string>;
89
- value?: reactive<string>;
90
- placeholder?: reactive<string>;
91
- disabled?: reactive<boolean>;
92
- readonly?: reactive<boolean>;
93
- required?: reactive<boolean>;
94
- rows?: reactive<number>;
95
- cols?: reactive<number>;
96
- minlength?: reactive<number>;
97
- maxlength?: reactive<number>;
98
- wrap?: reactive<"hard" | "soft" | "off">;
99
- autocomplete?: reactive<string>;
100
- autofocus?: reactive<boolean>;
101
- form?: reactive<string>;
102
- spellcheck?: reactive<boolean>;
103
- }
104
- interface LabelProps extends TagProps {
105
- for?: reactive<string>;
106
- form?: reactive<string>;
107
- }
108
- interface OptionProps extends TagProps {
109
- value?: reactive<string | number>;
110
- selected?: reactive<boolean>;
111
- disabled?: reactive<boolean>;
112
- label?: reactive<string>;
113
- }
114
- interface MediaProps extends TagProps {
115
- src?: reactive<string>;
116
- autoplay?: reactive<boolean>;
117
- controls?: reactive<boolean>;
118
- loop?: reactive<boolean>;
119
- muted?: reactive<boolean>;
120
- preload?: reactive<"none" | "metadata" | "auto">;
121
- crossorigin?: reactive<"anonymous" | "use-credentials">;
122
- }
123
- interface VideoProps extends MediaProps {
124
- poster?: reactive<string>;
125
- width?: reactive<number | string>;
126
- height?: reactive<number | string>;
127
- playsinline?: reactive<boolean>;
6
+ interface ErrorBoundaryOptions {
7
+ /**
8
+ * Fallback renderer given an Error and retry callback.
9
+ * Memoized internally only re-created when the error changes.
10
+ */
11
+ fallback?: (error: Error, retry: () => void) => Element;
12
+ /**
13
+ * Called when an error is caught (sync or async).
14
+ */
15
+ onError?: (error: Error) => void;
16
+ /**
17
+ * A list of reactive getters. Whenever any of these values change
18
+ * after an error has been caught, the boundary automatically resets
19
+ * (clears the error and re-renders). Useful for recovering from a
20
+ * failed render after the user navigates, changes filters, or
21
+ * otherwise picks a new input that might not fail this time.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const [route, setRoute] = signal("/");
26
+ * ErrorBoundary(
27
+ * { resetKeys: [route] },
28
+ * () => div(riskyPageFor(route())),
29
+ * );
30
+ * ```
31
+ */
32
+ resetKeys?: Array<() => unknown>;
128
33
  }
129
- type AudioProps = MediaProps;
130
-
131
- type TypedTagFunction<Props extends TagProps, El extends Element> = (first?: Props | NodeChildren, second?: NodeChildren) => El;
34
+ /** @deprecated Renamed to `ErrorBoundaryOptions`; kept for typing compatibility. */
35
+ type ErrorBoundaryProps = ErrorBoundaryOptions;
36
+ /**
37
+ * ErrorBoundary component using SibuJS reactive pattern.
38
+ *
39
+ * Features:
40
+ * - Catches sync errors thrown by children
41
+ * - Catches the rejection of a Promise *returned* by children (async components)
42
+ * — it does NOT observe fire-and-forget async errors (rejections from timers,
43
+ * event handlers, or detached/unawaited promises), which surface as global
44
+ * `unhandledrejection` events instead.
45
+ * - Supports nested ErrorBoundaries (inner catches first, outer catches propagation)
46
+ * - Retry functionality to clear error and re-render children
47
+ * - Memoized fallback to avoid re-creating fallback UI on every render
48
+ * - onError callback for logging/telemetry
49
+ * - Improved CSS styling
50
+ */
51
+ declare function ErrorBoundary(children: () => Element): Element;
52
+ declare function ErrorBoundary(options: ErrorBoundaryOptions, children: () => Element): Element;
132
53
 
133
- declare const head: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
134
- declare const body: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
135
- declare const title: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
136
- declare const div: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
137
- declare const span: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
138
- declare const section: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
139
- declare const article: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
140
- declare const header: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
141
- declare const footer: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
142
- declare const nav: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
143
- declare const main: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
144
- declare const aside: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
145
- declare const address: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
146
- declare const p: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
147
- declare const h1: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
148
- declare const h2: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
149
- declare const h3: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
150
- declare const h4: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
151
- declare const h5: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
152
- declare const h6: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
153
- declare const blockquote: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
154
- declare const dd: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
155
- declare const dl: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
156
- declare const dt: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
157
- declare const figcaption: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
158
- declare const figure: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
159
- declare const hr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
160
- declare const li: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
161
- declare const ol: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
162
- declare const ul: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
163
- declare const pre: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
164
- declare const a: TypedTagFunction<AnchorProps, HTMLAnchorElement>;
165
- declare const abbr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
166
- declare const b: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
167
- declare const bdi: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
168
- declare const bdo: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
169
- declare const br: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
170
- declare const cite: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
171
- declare const code: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
172
- declare const data: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
173
- declare const dfn: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
174
- declare const em: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
175
- declare const i: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
176
- declare const kbd: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
177
- declare const mark: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
178
- declare const q: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
179
- declare const rp: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
180
- declare const rt: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
181
- declare const ruby: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
182
- declare const s: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
183
- declare const samp: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
184
- declare const small: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
185
- declare const strong: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
186
- declare const sub: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
187
- declare const sup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
188
- declare const time: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
189
- declare const u: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
190
- declare const var_: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
191
- declare const area: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
192
- declare const audio: TypedTagFunction<AudioProps, HTMLAudioElement>;
193
- declare const img: TypedTagFunction<ImgProps, HTMLImageElement>;
194
- declare const map: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
195
- declare const track$1: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
196
- declare const video: TypedTagFunction<VideoProps, HTMLVideoElement>;
197
- declare const embed: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
198
- declare const iframe: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
199
- declare const object: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
200
- declare const param: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
201
- declare const picture: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
202
- declare const portal: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
203
- declare const source: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
204
- declare const svg: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
205
- declare const math: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
206
- declare const canvas: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
207
- declare const noscript: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
208
- declare const script: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
209
- declare const del: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
210
- declare const ins: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
211
- declare const caption: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
212
- declare const col: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
213
- declare const colgroup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
214
- declare const table: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
215
- declare const tbody: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
216
- declare const td: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
217
- declare const tfoot: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
218
- declare const th: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
219
- declare const thead: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
220
- declare const tr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
221
- declare const button: TypedTagFunction<ButtonProps, HTMLButtonElement>;
222
- declare const datalist: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
223
- declare const fieldset: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
224
- declare const form: TypedTagFunction<FormProps, HTMLFormElement>;
225
- declare const input: TypedTagFunction<InputProps, HTMLInputElement>;
226
- declare const label: TypedTagFunction<LabelProps, HTMLLabelElement>;
227
- declare const legend: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
228
- declare const meter: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
229
- declare const optgroup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
230
- declare const option: TypedTagFunction<OptionProps, HTMLOptionElement>;
231
- declare const output: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
232
- declare const progress: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
233
- declare const select: TypedTagFunction<SelectProps, HTMLSelectElement>;
234
- declare const textarea: TypedTagFunction<TextareaProps, HTMLTextAreaElement>;
235
- declare const details: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
236
- declare const dialog: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
237
- declare const menu: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
238
- declare const summary: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
239
- declare const slot: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
240
- declare const template: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
241
- declare const base: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
242
- declare const link: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
243
- declare const meta: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
244
- declare const style: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
245
- declare const circle: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
246
- declare const ellipse: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
247
- declare const g: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
248
- declare const line: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
249
- declare const path: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
250
- declare const polygon: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
251
- declare const polyline: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
252
- declare const rect: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
253
- declare const text: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
254
- declare const tspan: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
255
- declare const defs: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
256
- declare const clipPath: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
257
- declare const mask: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
258
- declare const pattern: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
259
- declare const linearGradient: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
260
- declare const radialGradient: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
261
- declare const stop: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
262
- declare const use: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
263
- declare const symbol: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
264
- declare const marker: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
265
- declare const center: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
266
- declare const font: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
267
- declare const marquee: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
268
- declare const customElement: (tagName: string) => (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
54
+ type ErrorSeverity = "error" | "warning" | "info";
55
+ interface ErrorDisplayProps {
56
+ /** The Error (or error-like value) to show. */
57
+ error: unknown;
58
+ /** Severity colour. Default `"error"`. */
59
+ severity?: ErrorSeverity;
60
+ /** Optional headline override. By default `error.message` is used. */
61
+ title?: string;
62
+ /**
63
+ * Label for the primary action. Shown next to a Reload button.
64
+ * Leave unset to hide the retry action.
65
+ */
66
+ retryLabel?: string;
67
+ /** Callback for the retry button. */
68
+ onRetry?: () => void;
69
+ /**
70
+ * If `true`, the Reload button is hidden. Useful for embedded
71
+ * error panels where a full page reload is inappropriate.
72
+ */
73
+ hideReload?: boolean;
74
+ /**
75
+ * If `true`, the stack trace and metadata are always shown even in
76
+ * production builds. Default: only shown in dev.
77
+ */
78
+ alwaysShowDetails?: boolean;
79
+ /**
80
+ * Extra metadata rendered as a key/value list under the message.
81
+ * Useful for attaching request IDs, user IDs, etc.
82
+ */
83
+ metadata?: Record<string, string | number | boolean | null | undefined>;
84
+ }
85
+ /**
86
+ * Rich error display component. Wire an error-like value in and get
87
+ * a colored panel back with copy, retry, reload, stack, cause chain,
88
+ * and metadata all built from tag factories. Reusable anywhere
89
+ * (inside `ErrorBoundary`, from a fetch failure, from a form submit).
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * button(
94
+ * { on: { click: async () => {
95
+ * try { await save(); }
96
+ * catch (err) { mount(ErrorDisplay({ error: err, onRetry: save }), errorHost); }
97
+ * }}},
98
+ * "Save",
99
+ * );
100
+ * ```
101
+ */
102
+ declare function ErrorDisplay(props: ErrorDisplayProps): Element;
269
103
 
104
+ interface LoadingProps {
105
+ /** Text to show alongside the spinner */
106
+ text?: string;
107
+ /** Visual variant: "spinner" (default) or "dots" */
108
+ variant?: "spinner" | "dots";
109
+ /** Size: "sm", "md" (default), "lg" */
110
+ size?: "sm" | "md" | "lg";
111
+ }
270
112
  /**
271
- * Tagged template literal for building Sibu elements with HTML-like syntax.
272
- * Runtime-only — no compiler or build step required.
113
+ * Built-in loading indicator component.
273
114
  *
274
- * Templates are parsed once per call site and cached. Subsequent calls at the
275
- * same source location skip parsing entirely and only replay the cached
276
- * structure with fresh expression values.
115
+ * @example
116
+ * ```ts
117
+ * Loading(); // Default spinner
118
+ * Loading({ text: "Loading data..." }); // With text
119
+ * Loading({ variant: "dots" }); // Dots animation
120
+ * Loading({ size: "lg" }); // Large spinner
121
+ * ```
277
122
  */
278
- declare function html(strings: TemplateStringsArray, ...values: unknown[]): Element;
123
+ declare function Loading(props?: LoadingProps): HTMLElement;
279
124
 
280
125
  /**
281
- * Mounts a root component into a DOM element.
282
- * Supports both function components and pre-created HTMLElements.
126
+ * An action is a reusable element-level behavior.
127
+ * It receives the element and an optional parameter, and may return
128
+ * a cleanup function that runs when the element is disposed.
283
129
  */
284
- declare function mount(component: (() => Element) | Element | Node, container: Element | null): {
285
- node: Node;
286
- unmount: () => void;
287
- };
288
-
130
+ type ActionFn<T = void> = (element: HTMLElement, param: T) => (() => void) | undefined;
289
131
  /**
290
- * Renders a list of nodes efficiently with key-based diffing and
291
- * LIS-based move minimization.
132
+ * Register a reusable action under a name so it can be applied by string —
133
+ * `action(el, "name", param)` — or looked up via {@link getAction}.
292
134
  *
293
- * The reconciliation algorithm works as follows:
294
- * 1. Build or reuse nodes by key (create new, keep existing).
295
- * 2. Remove nodes whose keys no longer exist.
296
- * 3. For nodes that existed in both old and new lists, compute their
297
- * old indices and find the Longest Increasing Subsequence (LIS).
298
- * Nodes in the LIS are already in the correct relative order and
299
- * do NOT need to be moved. Only nodes outside the LIS are moved.
300
- * 4. Walk the new key list in reverse and insert/position each node,
301
- * skipping DOM operations for nodes that are part of the LIS.
135
+ * Re-registering the same name overwrites the previous action. The built-in
136
+ * actions (`clickOutside`, `longPress`, `copyOnClick`, `autoResize`,
137
+ * `trapFocus`) are auto-registered under their export names.
138
+ */
139
+ declare function registerAction<T>(name: string, fn: ActionFn<T>): void;
140
+ /** Look up a registered action by name, or `undefined` if none is registered. */
141
+ declare function getAction<T = unknown>(name: string): ActionFn<T> | undefined;
142
+ /**
143
+ * Attach a reusable action (element-level behavior) to an element.
144
+ * The action's cleanup function (if returned) is automatically registered
145
+ * via `registerDisposer`, so it runs when the element is disposed.
302
146
  *
303
- * The render callback receives reactive getters `() => T` and `() => number`
304
- * instead of plain values. This ensures the callback always reads fresh data
305
- * when a keyed item's data changes but its key stays the same, since the DOM
306
- * is reused without re-calling render.
147
+ * The action may be passed directly, or by the name it was registered under
148
+ * (see {@link registerAction}). Actions are composable multiple can be
149
+ * applied to the same element.
307
150
  *
308
- * @param getArray A reactive getter returning an array.
309
- * @param render A function that receives reactive item and index getters and returns a NodeChild.
310
- * @param options A key function for unique identity of items.
311
- * @returns A Comment node serving as the anchor for the list.
151
+ * @param element The target element
152
+ * @param action The action function, or the name of a registered action
153
+ * @param param Optional parameter passed to the action
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * div({
158
+ * onElement: (el) => {
159
+ * action(el, clickOutside, () => setOpen(false)); // by reference
160
+ * action(el, "longPress", { duration: 500, callback: onLongPress }); // by name
161
+ * },
162
+ * }, "Content");
163
+ * ```
312
164
  */
313
- declare function each<T>(getArray: () => T[], render: (item: () => T, index: () => number) => NodeChild, options: {
314
- key: (item: T) => string | number;
315
- }): Comment;
316
-
165
+ declare function action<T>(element: HTMLElement, action: ActionFn<T> | string, param: T): void;
166
+ declare function action(element: HTMLElement, action: ActionFn<void> | string): void;
317
167
  /**
318
- * Fragment groups multiple nodes without adding a wrapper DOM element.
319
- * Returns a DocumentFragment that can be appended to any parent.
168
+ * Fires a callback when the user clicks outside the element.
169
+ * Useful for closing dropdowns, modals, and popovers.
320
170
  *
321
171
  * @example
322
172
  * ```ts
323
- * div([
324
- * Fragment([
325
- * p("First"),
326
- * p("Second"),
327
- * ])
328
- * ]);
173
+ * action(el, clickOutside, () => setOpen(false));
329
174
  * ```
175
+ */
176
+ declare const clickOutside: ActionFn<() => void>;
177
+ /**
178
+ * Options for the longPress action.
179
+ */
180
+ interface LongPressOptions {
181
+ /** Duration in milliseconds before the press is considered "long". Default: 500 */
182
+ duration?: number;
183
+ /** Callback fired when the long press is detected. */
184
+ callback: () => void;
185
+ }
186
+ /**
187
+ * Fires a callback after a sustained press on the element.
330
188
  *
331
- * @param nodes Array of child nodes to include in the fragment
332
- * @returns A DocumentFragment containing all nodes
189
+ * @example
190
+ * ```ts
191
+ * action(el, longPress, { duration: 800, callback: onLongPress });
192
+ * ```
333
193
  */
334
- declare function Fragment(nodes: NodeChildren[]): DocumentFragment;
335
-
194
+ declare const longPress: ActionFn<LongPressOptions>;
336
195
  /**
337
- * Portal renders nodes into a DOM node outside the parent component hierarchy.
338
- * Useful for modals, tooltips, dropdowns, and overlays.
196
+ * Copies the element's textContent to the clipboard on click.
197
+ * Optionally accepts a custom getter for the text to copy.
339
198
  *
340
- * Cleanup integrates with `dispose()` / `registerDisposer()` so portals
341
- * are properly torn down when the anchor is disposed by `when()`, `match()`,
342
- * `each()`, or manual `dispose(anchor)`.
199
+ * @example
200
+ * ```ts
201
+ * // Copy element text
202
+ * action(el, copyOnClick);
343
203
  *
344
- * @param nodes Function that returns the content to render
345
- * @param target Target DOM element (defaults to document.body)
346
- * @returns A Comment anchor node in the original position
204
+ * // Copy custom value
205
+ * action(el, copyOnClick, () => secretToken());
206
+ * ```
207
+ */
208
+ declare const copyOnClick: ActionFn<(() => string) | undefined>;
209
+ /**
210
+ * Auto-resizes a textarea to fit its content.
211
+ * Adjusts height on input and on initial attach.
347
212
  *
348
213
  * @example
349
214
  * ```ts
350
- * // Render modal at document.body
351
- * Portal(() => div("modal", "Modal content"));
215
+ * const ta = textarea({ placeholder: "Type here..." });
216
+ * action(ta, autoResize);
217
+ * ```
218
+ */
219
+ declare const autoResize: ActionFn<void>;
220
+ /**
221
+ * Traps keyboard focus within the element (Tab and Shift+Tab cycle).
222
+ * Essential for accessible modals and dialogs.
352
223
  *
353
- * // Render into specific container
354
- * const overlay = document.getElementById("overlay-root")!;
355
- * Portal(() => div("Tooltip"), overlay);
224
+ * @example
225
+ * ```ts
226
+ * action(el, trapFocus);
356
227
  * ```
357
228
  */
358
- declare function Portal(nodes: () => HTMLElement, target?: HTMLElement): Comment;
229
+ declare const trapFocus: ActionFn<void>;
359
230
 
360
- type Component$1 = () => HTMLElement;
231
+ type ErrorHandler = (error: unknown, context?: string) => void;
361
232
  /**
362
- * Register a component by name for dynamic resolution.
233
+ * Wraps a function in a try/catch block with typed error handling.
234
+ * Supports both sync and async functions (catches Promise rejections).
363
235
  *
364
- * @param name Unique component identifier
365
- * @param component The component function
236
+ * @param fn Function to execute safely
237
+ * @param onError Optional error handler (receives error and optional context)
238
+ * @returns The function's return value, or null on error
239
+ */
240
+ declare function catchError<T>(fn: () => T, onError?: ErrorHandler): T | null;
241
+ /**
242
+ * Async version of catchError for explicit async/await usage.
243
+ *
244
+ * @param fn Async function to execute safely
245
+ * @param onError Optional error handler
246
+ * @returns Promise resolving to the result or null on error
247
+ */
248
+ declare function catchErrorAsync<T>(fn: () => Promise<T>, onError?: ErrorHandler): Promise<T | null>;
249
+ /**
250
+ * Sets a global error handler used by default if no onError is provided.
251
+ */
252
+ declare function setGlobalErrorHandler(handler: ErrorHandler): void;
253
+
254
+ /**
255
+ * Context API for SibuJS — a reactive global value that any component
256
+ * can read without prop drilling.
257
+ *
258
+ * Note: this is a **global reactive store**, not a subtree-scoped DI
259
+ * system. Calling `provide()` sets the value for ALL consumers, not
260
+ * just descendants of the provider. For most apps this is sufficient
261
+ * — use separate `context()` instances for independent scopes.
366
262
  *
367
263
  * @example
368
264
  * ```ts
369
- * registerComponent("UserCard", UserCard);
370
- * registerComponent("AdminPanel", AdminPanel);
265
+ * // Create a context with a default value
266
+ * const ThemeContext = context("light");
267
+ *
268
+ * // Set the value (global — affects all consumers)
269
+ * ThemeContext.provide("dark");
270
+ *
271
+ * // Read reactively from any component
272
+ * function Child() {
273
+ * const theme = ThemeContext.use(); // reactive getter
274
+ * return div(() => `Theme: ${theme()}`);
275
+ * }
371
276
  * ```
372
277
  */
373
- declare function registerComponent(name: string, component: Component$1): void;
278
+ interface Context<T> {
279
+ /**
280
+ * Set the context value globally. Affects all consumers.
281
+ *
282
+ * Returns a `restore` function that re-sets the context to the value it
283
+ * had *before* this `provide` call. Useful for scoped overrides:
284
+ *
285
+ * ```ts
286
+ * const restore = Theme.provide("dark");
287
+ * try { renderChild(); } finally { restore(); }
288
+ * ```
289
+ *
290
+ * Callers that don't need scoping can ignore the return value — existing
291
+ * semantics are preserved.
292
+ */
293
+ provide(value: T): () => void;
294
+ /** Get a reactive getter for the current context value. */
295
+ use(): () => T;
296
+ /** Get the current value directly (non-reactive). */
297
+ get(): T;
298
+ /** Update the provided value reactively. */
299
+ set(value: T): void;
300
+ /**
301
+ * Run `fn` with the context temporarily set to `value`, then restore the
302
+ * previous value (even if `fn` throws). Returns the result of `fn`.
303
+ */
304
+ withContext<R>(value: T, fn: () => R): R;
305
+ }
374
306
  /**
375
- * Unregister a previously registered component.
307
+ * Creates a new context with an optional default value.
308
+ *
309
+ * @param defaultValue The fallback value when no provider is found
310
+ * @returns A Context object with provide, use, get, and set methods
376
311
  */
377
- declare function unregisterComponent(name: string): void;
312
+ declare function context<T>(defaultValue: T): Context<T>;
313
+
378
314
  /**
379
- * Resolve and render a dynamically registered component by name.
380
- * Returns a placeholder if the component is not found.
315
+ * Generate a stable, framework-unique ID string suitable for a11y pairing
316
+ * (`aria-labelledby`, `htmlFor` + `id`, etc.).
381
317
  *
382
- * @param name Component name to resolve
383
- * @returns The rendered HTMLElement or a fallback
318
+ * Each call returns a fresh incrementing id. Optionally accepts a prefix.
319
+ *
320
+ * IDs are plain strings (not reactive) — call once per component instance
321
+ * and reuse the returned value for both sides of the association.
322
+ *
323
+ * @param prefix Optional prefix, default "sibu"
324
+ * @returns A unique id like `"sibu-1"` or `"my-input-2"`
384
325
  *
385
326
  * @example
386
327
  * ```ts
387
- * registerComponent("Widget", MyWidget);
388
- * div([resolveComponent("Widget")]);
328
+ * function Field(labelText: string) {
329
+ * const id = createId("field");
330
+ * return div([
331
+ * label({ for: id }, labelText),
332
+ * input({ id }),
333
+ * ]);
334
+ * }
389
335
  * ```
390
336
  */
391
- declare function resolveComponent(name: string): HTMLElement;
337
+ declare function createId(prefix?: string): string;
392
338
  /**
393
- * Dynamic component that reactively switches between components
394
- * based on a reactive getter returning a component name or function.
395
- *
396
- * @param is Reactive getter returning component name (string) or component function
397
- * @param props Optional props to pass
398
- * @returns Container element that swaps content reactively
339
+ * Reset the id counter. Intended for tests and SSR setups that want
340
+ * deterministic ids across runs.
399
341
  *
400
- * @example
401
- * ```ts
402
- * const [view, setView] = signal("list");
403
- * DynamicComponent(() => view()); // Renders registered "list" component
404
- * setView("grid"); // Swaps to registered "grid" component
405
- * ```
342
+ * @internal
406
343
  */
407
- declare function DynamicComponent(is: () => string | Component$1): HTMLElement;
408
-
409
- type SlotFn = () => Element | string | number | null | undefined;
410
- type Slots = Record<string, SlotFn>;
411
- declare function getSlot(slots: Slots | undefined, name?: string): SlotFn | undefined;
344
+ declare function __resetIdCounter(): void;
412
345
 
413
346
  /**
414
347
  * Conditional rendering directive. Shows or hides an element reactively.
@@ -481,6 +414,397 @@ declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBr
481
414
  */
482
415
  declare function match<T extends string | number>(value: () => T, cases: Record<string, () => NodeChild>, fallback?: () => NodeChild): Comment;
483
416
 
417
+ /**
418
+ * Register a teardown function for a DOM node.
419
+ * When dispose(node) is called, all registered teardowns run.
420
+ */
421
+ declare function registerDisposer(node: Node, teardown: () => void): void;
422
+ /**
423
+ * Run all registered teardowns for a node and its descendants,
424
+ * cleaning up reactive subscriptions to prevent memory leaks.
425
+ * Call this when removing elements from the DOM.
426
+ *
427
+ * Uses an iterative depth-first traversal to avoid stack overflow
428
+ * on deeply nested DOM trees.
429
+ */
430
+ declare function dispose(node: Node): void;
431
+ /**
432
+ * Check for potential binding leaks. Returns the number of active DOM bindings.
433
+ * In dev mode, logs a warning if the count exceeds the threshold.
434
+ * In production, _isDev is false so the counter is always 0.
435
+ */
436
+ declare function checkLeaks(warnThreshold?: number): number;
437
+
438
+ type Component$1 = () => HTMLElement;
439
+ /**
440
+ * Register a component by name for dynamic resolution.
441
+ *
442
+ * @param name Unique component identifier
443
+ * @param component The component function
444
+ *
445
+ * @example
446
+ * ```ts
447
+ * registerComponent("UserCard", UserCard);
448
+ * registerComponent("AdminPanel", AdminPanel);
449
+ * ```
450
+ */
451
+ declare function registerComponent(name: string, component: Component$1): void;
452
+ /**
453
+ * Unregister a previously registered component.
454
+ */
455
+ declare function unregisterComponent(name: string): void;
456
+ /**
457
+ * Resolve and render a dynamically registered component by name.
458
+ * Returns a placeholder if the component is not found.
459
+ *
460
+ * @param name Component name to resolve
461
+ * @returns The rendered HTMLElement or a fallback
462
+ *
463
+ * @example
464
+ * ```ts
465
+ * registerComponent("Widget", MyWidget);
466
+ * div([resolveComponent("Widget")]);
467
+ * ```
468
+ */
469
+ declare function resolveComponent(name: string): HTMLElement;
470
+ /**
471
+ * Dynamic component that reactively switches between components
472
+ * based on a reactive getter returning a component name or function.
473
+ *
474
+ * @param is Reactive getter returning component name (string) or component function
475
+ * @param props Optional props to pass
476
+ * @returns Container element that swaps content reactively
477
+ *
478
+ * @example
479
+ * ```ts
480
+ * const [view, setView] = signal("list");
481
+ * DynamicComponent(() => view()); // Renders registered "list" component
482
+ * setView("grid"); // Swaps to registered "grid" component
483
+ * ```
484
+ */
485
+ declare function DynamicComponent(is: () => string | Component$1): HTMLElement;
486
+
487
+ /**
488
+ * Renders a list of nodes efficiently with key-based diffing and
489
+ * LIS-based move minimization.
490
+ *
491
+ * The reconciliation algorithm works as follows:
492
+ * 1. Build or reuse nodes by key (create new, keep existing).
493
+ * 2. Remove nodes whose keys no longer exist.
494
+ * 3. For nodes that existed in both old and new lists, compute their
495
+ * old indices and find the Longest Increasing Subsequence (LIS).
496
+ * Nodes in the LIS are already in the correct relative order and
497
+ * do NOT need to be moved. Only nodes outside the LIS are moved.
498
+ * 4. Walk the new key list in reverse and insert/position each node,
499
+ * skipping DOM operations for nodes that are part of the LIS.
500
+ *
501
+ * The render callback receives reactive getters `() => T` and `() => number`
502
+ * instead of plain values. This ensures the callback always reads fresh data
503
+ * when a keyed item's data changes but its key stays the same, since the DOM
504
+ * is reused without re-calling render.
505
+ *
506
+ * @param getArray A reactive getter returning an array.
507
+ * @param render A function that receives reactive item and index getters and returns a NodeChild.
508
+ * @param options A key function for unique identity of items.
509
+ * @returns A Comment node serving as the anchor for the list.
510
+ */
511
+ declare function each<T>(getArray: () => T[], render: (item: () => T, index: () => number) => NodeChild, options: {
512
+ key: (item: T) => string | number;
513
+ }): Comment;
514
+
515
+ /**
516
+ * Fragment groups multiple nodes without adding a wrapper DOM element.
517
+ * Returns a DocumentFragment that can be appended to any parent.
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * div([
522
+ * Fragment([
523
+ * p("First"),
524
+ * p("Second"),
525
+ * ])
526
+ * ]);
527
+ * ```
528
+ *
529
+ * @param nodes Array of child nodes to include in the fragment
530
+ * @returns A DocumentFragment containing all nodes
531
+ */
532
+ declare function Fragment(nodes: NodeChildren[]): DocumentFragment;
533
+
534
+ /**
535
+ * Tagged template literal for building Sibu elements with HTML-like syntax.
536
+ * Runtime-only — no compiler or build step required.
537
+ *
538
+ * Templates are parsed once per call site and cached. Subsequent calls at the
539
+ * same source location skip parsing entirely and only replay the cached
540
+ * structure with fresh expression values.
541
+ */
542
+ declare function html(strings: TemplateStringsArray, ...values: unknown[]): Element;
543
+
544
+ type reactive<T> = T | (() => T);
545
+ interface AnchorProps extends TagProps {
546
+ href?: reactive<string>;
547
+ target?: "_self" | "_blank" | "_parent" | "_top" | (string & {});
548
+ rel?: reactive<string>;
549
+ download?: reactive<string | boolean>;
550
+ hreflang?: reactive<string>;
551
+ referrerpolicy?: reactive<"" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url">;
552
+ ping?: reactive<string>;
553
+ }
554
+ type InputType = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
555
+ interface InputProps extends TagProps {
556
+ type?: InputType | (string & {});
557
+ name?: reactive<string>;
558
+ value?: reactive<string | number>;
559
+ placeholder?: reactive<string>;
560
+ required?: reactive<boolean>;
561
+ disabled?: reactive<boolean>;
562
+ readonly?: reactive<boolean>;
563
+ checked?: reactive<boolean>;
564
+ min?: reactive<string | number>;
565
+ max?: reactive<string | number>;
566
+ step?: reactive<string | number>;
567
+ minlength?: reactive<number>;
568
+ maxlength?: reactive<number>;
569
+ pattern?: reactive<string>;
570
+ autocomplete?: reactive<string>;
571
+ autofocus?: reactive<boolean>;
572
+ multiple?: reactive<boolean>;
573
+ accept?: reactive<string>;
574
+ size?: reactive<number>;
575
+ form?: reactive<string>;
576
+ list?: reactive<string>;
577
+ inputmode?: reactive<"none" | "text" | "decimal" | "numeric" | "tel" | "search" | "email" | "url">;
578
+ }
579
+ interface ImgProps extends TagProps {
580
+ src?: reactive<string>;
581
+ alt?: reactive<string>;
582
+ width?: reactive<number | string>;
583
+ height?: reactive<number | string>;
584
+ loading?: reactive<"lazy" | "eager">;
585
+ decoding?: reactive<"sync" | "async" | "auto">;
586
+ srcset?: reactive<string>;
587
+ sizes?: reactive<string>;
588
+ crossorigin?: reactive<"anonymous" | "use-credentials">;
589
+ referrerpolicy?: reactive<string>;
590
+ }
591
+ interface ButtonProps extends TagProps {
592
+ type?: reactive<"button" | "submit" | "reset">;
593
+ name?: reactive<string>;
594
+ value?: reactive<string>;
595
+ disabled?: reactive<boolean>;
596
+ form?: reactive<string>;
597
+ formaction?: reactive<string>;
598
+ formenctype?: reactive<string>;
599
+ formmethod?: reactive<"get" | "post" | "dialog">;
600
+ formnovalidate?: reactive<boolean>;
601
+ formtarget?: reactive<string>;
602
+ autofocus?: reactive<boolean>;
603
+ }
604
+ interface FormProps extends TagProps {
605
+ action?: reactive<string>;
606
+ method?: reactive<"get" | "post" | "dialog">;
607
+ enctype?: reactive<"application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain">;
608
+ name?: reactive<string>;
609
+ novalidate?: reactive<boolean>;
610
+ target?: reactive<string>;
611
+ autocomplete?: reactive<"on" | "off">;
612
+ acceptcharset?: reactive<string>;
613
+ }
614
+ interface SelectProps extends TagProps {
615
+ name?: reactive<string>;
616
+ value?: reactive<string>;
617
+ disabled?: reactive<boolean>;
618
+ multiple?: reactive<boolean>;
619
+ required?: reactive<boolean>;
620
+ size?: reactive<number>;
621
+ autocomplete?: reactive<string>;
622
+ autofocus?: reactive<boolean>;
623
+ form?: reactive<string>;
624
+ }
625
+ interface TextareaProps extends TagProps {
626
+ name?: reactive<string>;
627
+ value?: reactive<string>;
628
+ placeholder?: reactive<string>;
629
+ disabled?: reactive<boolean>;
630
+ readonly?: reactive<boolean>;
631
+ required?: reactive<boolean>;
632
+ rows?: reactive<number>;
633
+ cols?: reactive<number>;
634
+ minlength?: reactive<number>;
635
+ maxlength?: reactive<number>;
636
+ wrap?: reactive<"hard" | "soft" | "off">;
637
+ autocomplete?: reactive<string>;
638
+ autofocus?: reactive<boolean>;
639
+ form?: reactive<string>;
640
+ spellcheck?: reactive<boolean>;
641
+ }
642
+ interface LabelProps extends TagProps {
643
+ for?: reactive<string>;
644
+ form?: reactive<string>;
645
+ }
646
+ interface OptionProps extends TagProps {
647
+ value?: reactive<string | number>;
648
+ selected?: reactive<boolean>;
649
+ disabled?: reactive<boolean>;
650
+ label?: reactive<string>;
651
+ }
652
+ interface MediaProps extends TagProps {
653
+ src?: reactive<string>;
654
+ autoplay?: reactive<boolean>;
655
+ controls?: reactive<boolean>;
656
+ loop?: reactive<boolean>;
657
+ muted?: reactive<boolean>;
658
+ preload?: reactive<"none" | "metadata" | "auto">;
659
+ crossorigin?: reactive<"anonymous" | "use-credentials">;
660
+ }
661
+ interface VideoProps extends MediaProps {
662
+ poster?: reactive<string>;
663
+ width?: reactive<number | string>;
664
+ height?: reactive<number | string>;
665
+ playsinline?: reactive<boolean>;
666
+ }
667
+ type AudioProps = MediaProps;
668
+
669
+ type TypedTagFunction<Props extends TagProps, El extends Element> = (first?: Props | NodeChildren, second?: NodeChildren) => El;
670
+
671
+ declare const head: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
672
+ declare const body: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
673
+ declare const title: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
674
+ declare const div: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
675
+ declare const span: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
676
+ declare const section: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
677
+ declare const article: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
678
+ declare const header: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
679
+ declare const footer: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
680
+ declare const nav: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
681
+ declare const main: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
682
+ declare const aside: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
683
+ declare const address: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
684
+ declare const p: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
685
+ declare const h1: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
686
+ declare const h2: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
687
+ declare const h3: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
688
+ declare const h4: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
689
+ declare const h5: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
690
+ declare const h6: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
691
+ declare const blockquote: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
692
+ declare const dd: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
693
+ declare const dl: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
694
+ declare const dt: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
695
+ declare const figcaption: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
696
+ declare const figure: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
697
+ declare const hr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
698
+ declare const li: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
699
+ declare const ol: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
700
+ declare const ul: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
701
+ declare const pre: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
702
+ declare const a: TypedTagFunction<AnchorProps, HTMLAnchorElement>;
703
+ declare const abbr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
704
+ declare const b: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
705
+ declare const bdi: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
706
+ declare const bdo: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
707
+ declare const br: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
708
+ declare const cite: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
709
+ declare const code: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
710
+ declare const data: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
711
+ declare const dfn: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
712
+ declare const em: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
713
+ declare const i: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
714
+ declare const kbd: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
715
+ declare const mark: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
716
+ declare const q: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
717
+ declare const rp: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
718
+ declare const rt: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
719
+ declare const ruby: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
720
+ declare const s: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
721
+ declare const samp: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
722
+ declare const small: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
723
+ declare const strong: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
724
+ declare const sub: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
725
+ declare const sup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
726
+ declare const time: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
727
+ declare const u: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
728
+ declare const var_: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
729
+ declare const area: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
730
+ declare const audio: TypedTagFunction<AudioProps, HTMLAudioElement>;
731
+ declare const img: TypedTagFunction<ImgProps, HTMLImageElement>;
732
+ declare const map: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
733
+ declare const track$1: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
734
+ declare const video: TypedTagFunction<VideoProps, HTMLVideoElement>;
735
+ declare const embed: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
736
+ declare const iframe: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
737
+ declare const object: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
738
+ declare const param: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
739
+ declare const picture: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
740
+ declare const portal: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
741
+ declare const source: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
742
+ declare const svg: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
743
+ declare const math: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
744
+ declare const canvas: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
745
+ declare const noscript: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
746
+ declare const script: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
747
+ declare const del: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
748
+ declare const ins: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
749
+ declare const caption: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
750
+ declare const col: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
751
+ declare const colgroup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
752
+ declare const table: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
753
+ declare const tbody: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
754
+ declare const td: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
755
+ declare const tfoot: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
756
+ declare const th: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
757
+ declare const thead: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
758
+ declare const tr: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
759
+ declare const button: TypedTagFunction<ButtonProps, HTMLButtonElement>;
760
+ declare const datalist: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
761
+ declare const fieldset: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
762
+ declare const form: TypedTagFunction<FormProps, HTMLFormElement>;
763
+ declare const input: TypedTagFunction<InputProps, HTMLInputElement>;
764
+ declare const label: TypedTagFunction<LabelProps, HTMLLabelElement>;
765
+ declare const legend: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
766
+ declare const meter: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
767
+ declare const optgroup: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
768
+ declare const option: TypedTagFunction<OptionProps, HTMLOptionElement>;
769
+ declare const output: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
770
+ declare const progress: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
771
+ declare const select: TypedTagFunction<SelectProps, HTMLSelectElement>;
772
+ declare const textarea: TypedTagFunction<TextareaProps, HTMLTextAreaElement>;
773
+ declare const details: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
774
+ declare const dialog: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
775
+ declare const menu: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
776
+ declare const summary: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
777
+ declare const slot: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
778
+ declare const template: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
779
+ declare const base: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
780
+ declare const link: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
781
+ declare const meta: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
782
+ declare const style: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
783
+ declare const circle: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
784
+ declare const ellipse: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
785
+ declare const g: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
786
+ declare const line: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
787
+ declare const path: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
788
+ declare const polygon: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
789
+ declare const polyline: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
790
+ declare const rect: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
791
+ declare const text: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
792
+ declare const tspan: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
793
+ declare const defs: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
794
+ declare const clipPath: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
795
+ declare const mask: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
796
+ declare const pattern: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
797
+ declare const linearGradient: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
798
+ declare const radialGradient: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
799
+ declare const stop: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
800
+ declare const use: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
801
+ declare const symbol: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
802
+ declare const marker: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
803
+ declare const center: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
804
+ declare const font: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
805
+ declare const marquee: (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
806
+ declare const customElement: (tagName: string) => (first?: TagProps | NodeChildren, second?: NodeChildren) => Element;
807
+
484
808
  /**
485
809
  * Options for KeepAlive.
486
810
  */
@@ -519,221 +843,356 @@ interface KeepAliveOptions {
519
843
  */
520
844
  declare function KeepAlive(activeKey: () => string, cases: Record<string, () => Node>, options?: KeepAliveOptions): Comment;
521
845
 
846
+ declare function takePendingError(node: Element): Error | undefined;
847
+ type Component = () => HTMLElement;
848
+ type LazyImport = () => Promise<{
849
+ default: Component;
850
+ }>;
522
851
  /**
523
- * An action is a reusable element-level behavior.
524
- * It receives the element and an optional parameter, and may return
525
- * a cleanup function that runs when the element is disposed.
852
+ * lazy() enables code-splitting by deferring the import of a component
853
+ * until it is first rendered. Returns a wrapper component that shows a
854
+ * loading state while the import resolves.
855
+ *
856
+ * @example
857
+ * ```ts
858
+ * const LazyDashboard = lazy(() => import("./Dashboard"));
859
+ *
860
+ * // Use inside Suspense for custom loading UI
861
+ * Suspense({
862
+ * nodes: () => LazyDashboard(),
863
+ * fallback: () => div("Loading dashboard..."),
864
+ * });
865
+ *
866
+ * // Or use standalone — shows default "Loading..." text
867
+ * LazyDashboard();
868
+ * ```
869
+ *
870
+ * @param importFn Dynamic import function returning `{ default: Component }`
871
+ * @returns A component function that lazy-loads on first call
526
872
  */
527
- type ActionFn<T = void> = (element: HTMLElement, param: T) => (() => void) | undefined;
873
+ declare function lazy(importFn: LazyImport): Component;
528
874
  /**
529
- * Register a reusable action under a name so it can be applied by string
530
- * `action(el, "name", param)` — or looked up via {@link getAction}.
875
+ * Suspense provides a fallback UI while lazy or async nodes are loading.
531
876
  *
532
- * Re-registering the same name overwrites the previous action. The built-in
533
- * actions (`clickOutside`, `longPress`, `copyOnClick`, `autoResize`,
534
- * `trapFocus`) are auto-registered under their export names.
877
+ * @example
878
+ * ```ts
879
+ * Suspense({
880
+ * nodes: () => LazyChart(),
881
+ * fallback: () => div("Loading chart..."),
882
+ * });
883
+ * ```
884
+ *
885
+ * @param props.nodes Function that returns the async/lazy component
886
+ * @param props.fallback Function that returns the loading UI
887
+ * @returns An HTMLElement that swaps from fallback to content when ready
535
888
  */
536
- declare function registerAction<T>(name: string, fn: ActionFn<T>): void;
537
- /** Look up a registered action by name, or `undefined` if none is registered. */
538
- declare function getAction<T = unknown>(name: string): ActionFn<T> | undefined;
889
+ interface SuspenseProps {
890
+ nodes: () => HTMLElement;
891
+ fallback: () => HTMLElement;
892
+ }
893
+ declare function Suspense({ nodes, fallback }: SuspenseProps): HTMLElement;
894
+
539
895
  /**
540
- * Attach a reusable action (element-level behavior) to an element.
541
- * The action's cleanup function (if returned) is automatically registered
542
- * via `registerDisposer`, so it runs when the element is disposed.
896
+ * Lifecycle hooks for SibuJS components.
543
897
  *
544
- * The action may be passed directly, or by the name it was registered under
545
- * (see {@link registerAction}). Actions are composable multiple can be
546
- * applied to the same element.
898
+ * These hooks schedule callbacks to run after the component's DOM
899
+ * has been mounted or when it is removed from the document.
547
900
  *
548
- * @param element The target element
549
- * @param action The action function, or the name of a registered action
550
- * @param param Optional parameter passed to the action
901
+ * @example
902
+ * ```ts
903
+ * function MyComponent() {
904
+ * onMount(() => {
905
+ * console.log("Component is in the DOM");
906
+ * });
907
+ *
908
+ * onUnmount(() => {
909
+ * console.log("Component was removed");
910
+ * });
551
911
  *
552
- * @example
553
- * ```ts
554
- * div({
555
- * onElement: (el) => {
556
- * action(el, clickOutside, () => setOpen(false)); // by reference
557
- * action(el, "longPress", { duration: 500, callback: onLongPress }); // by name
558
- * },
559
- * }, "Content");
912
+ * return div("Hello");
913
+ * }
560
914
  * ```
561
915
  */
562
- declare function action<T>(element: HTMLElement, action: ActionFn<T> | string, param: T): void;
563
- declare function action(element: HTMLElement, action: ActionFn<void> | string): void;
916
+ type CleanupFn = () => void;
564
917
  /**
565
- * Fires a callback when the user clicks outside the element.
566
- * Useful for closing dropdowns, modals, and popovers.
918
+ * Runs a callback once the component's element has been inserted into the DOM.
919
+ * Uses queueMicrotask to defer execution until after the current synchronous
920
+ * rendering pass completes.
567
921
  *
568
- * @example
569
- * ```ts
570
- * action(el, clickOutside, () => setOpen(false));
571
- * ```
922
+ * Optionally returns a cleanup function that will be called on unmount
923
+ * (if you also use onUnmount, prefer that for explicit cleanup).
924
+ *
925
+ * @param callback Function to run after mount. May return a cleanup function.
926
+ * @param element Optional element to observe; if provided, waits until it's connected.
572
927
  */
573
- declare const clickOutside: ActionFn<() => void>;
928
+ declare function onMount(callback: () => undefined | CleanupFn, element?: HTMLElement): void;
574
929
  /**
575
- * Options for the longPress action.
930
+ * Runs a callback when the given element is removed from the DOM.
931
+ * Uses a shared MutationObserver to watch for disconnection, plus
932
+ * `registerDisposer` so explicit dispose() paths also trigger the callback.
933
+ *
934
+ * @param callback Function to run on unmount
935
+ * @param element The element to watch for removal
576
936
  */
577
- interface LongPressOptions {
578
- /** Duration in milliseconds before the press is considered "long". Default: 500 */
579
- duration?: number;
580
- /** Callback fired when the long press is detected. */
581
- callback: () => void;
582
- }
937
+ declare function onUnmount(callback: CleanupFn, element: HTMLElement): void;
583
938
  /**
584
- * Fires a callback after a sustained press on the element.
939
+ * Register a cleanup callback that runs when the given element is disposed.
940
+ * Integrates with `when()`, `match()`, and `each()` which call `dispose()`
941
+ * on removed nodes, triggering all registered cleanup functions.
942
+ *
943
+ * @param callback Cleanup function (close sockets, clear intervals, etc.)
944
+ * @param element The component's root node to attach cleanup to
585
945
  *
586
946
  * @example
587
947
  * ```ts
588
- * action(el, longPress, { duration: 800, callback: onLongPress });
948
+ * function RealtimeBar(siteId: string) {
949
+ * const ws = new WebSocket(`/ws/sites/${siteId}/realtime`);
950
+ * const root = div("Realtime data...");
951
+ * onCleanup(() => ws.close(), root);
952
+ * return root;
953
+ * }
589
954
  * ```
590
955
  */
591
- declare const longPress: ActionFn<LongPressOptions>;
956
+ declare function onCleanup(callback: CleanupFn, element: Node): void;
957
+
592
958
  /**
593
- * Copies the element's textContent to the clipboard on click.
594
- * Optionally accepts a custom getter for the text to copy.
959
+ * Mounts a root component into a DOM element.
960
+ * Supports both function components and pre-created HTMLElements.
961
+ */
962
+ declare function mount(component: (() => Element) | Element | Node, container: Element | null): {
963
+ node: Node;
964
+ unmount: () => void;
965
+ };
966
+
967
+ /**
968
+ * Portal renders nodes into a DOM node outside the parent component hierarchy.
969
+ * Useful for modals, tooltips, dropdowns, and overlays.
970
+ *
971
+ * Cleanup integrates with `dispose()` / `registerDisposer()` so portals
972
+ * are properly torn down when the anchor is disposed by `when()`, `match()`,
973
+ * `each()`, or manual `dispose(anchor)`.
974
+ *
975
+ * @param nodes Function that returns the content to render
976
+ * @param target Target DOM element (defaults to document.body)
977
+ * @returns A Comment anchor node in the original position
595
978
  *
596
979
  * @example
597
980
  * ```ts
598
- * // Copy element text
599
- * action(el, copyOnClick);
981
+ * // Render modal at document.body
982
+ * Portal(() => div("modal", "Modal content"));
600
983
  *
601
- * // Copy custom value
602
- * action(el, copyOnClick, () => secretToken());
984
+ * // Render into specific container
985
+ * const overlay = document.getElementById("overlay-root")!;
986
+ * Portal(() => div("Tooltip"), overlay);
603
987
  * ```
604
988
  */
605
- declare const copyOnClick: ActionFn<(() => string) | undefined>;
989
+ declare function Portal(nodes: () => HTMLElement, target?: HTMLElement): Comment;
990
+
991
+ type SlotFn = () => Element | string | number | null | undefined;
992
+ type Slots = Record<string, SlotFn>;
993
+ declare function getSlot(slots: Slots | undefined, name?: string): SlotFn | undefined;
994
+
995
+ declare const __accessor: unique symbol;
606
996
  /**
607
- * Auto-resizes a textarea to fit its content.
608
- * Adjusts height on input and on initial attach.
997
+ * A reactive signal getter returned by signal(), derived(), and similar primitives.
609
998
  *
610
- * @example
999
+ * Pass an Accessor directly into reactive prop positions — never call it there:
611
1000
  * ```ts
612
- * const ta = textarea({ placeholder: "Type here..." });
613
- * action(ta, autoResize);
1001
+ * const [count, setCount] = signal(0);
1002
+ *
1003
+ * div(count) // ✓ reactive — Accessor passed directly
1004
+ * div(() => count()) // ✓ reactive — explicit arrow wrapper
1005
+ * div(count()) // ✗ static — evaluated once, not reactive
614
1006
  * ```
615
1007
  */
616
- declare const autoResize: ActionFn<void>;
1008
+ type Accessor<T> = (() => T) & {
1009
+ readonly [__accessor]?: never;
1010
+ };
1011
+ type SetState<T> = (next: T | ((prev: T) => T)) => void;
1012
+ type StateTuple<T> = [Accessor<T>, SetState<T>];
1013
+ /** Options for signal */
1014
+ interface SignalOptions<T = unknown> {
1015
+ /** Debug name for devtools inspection. Only used in development. */
1016
+ name?: string;
1017
+ /** Custom equality function. Defaults to Object.is(). */
1018
+ equals?: (prev: T, next: T) => boolean;
1019
+ }
617
1020
  /**
618
- * Traps keyboard focus within the element (Tab and Shift+Tab cycle).
619
- * Essential for accessible modals and dialogs.
1021
+ * signal creates a reactive signal that holds a value of type T.
1022
+ * Returns a tuple: [getter, setter].
620
1023
  *
621
- * @example
622
- * ```ts
623
- * action(el, trapFocus);
624
- * ```
1024
+ * @param initial Initial value
1025
+ * @param options Optional config: `{ name: "count" }` for devtools labeling
625
1026
  */
626
- declare const trapFocus: ActionFn<void>;
1027
+ declare function signal<T>(initial: T, options?: SignalOptions<T>): StateTuple<T>;
627
1028
 
628
- type ErrorHandler = (error: unknown, context?: string) => void;
629
1029
  /**
630
- * Wraps a function in a try/catch block with typed error handling.
631
- * Supports both sync and async functions (catches Promise rejections).
1030
+ * Reactive array hook. Provides common array operations that
1031
+ * automatically trigger reactive updates.
632
1032
  *
633
- * @param fn Function to execute safely
634
- * @param onError Optional error handler (receives error and optional context)
635
- * @returns The function's return value, or null on error
636
- */
637
- declare function catchError<T>(fn: () => T, onError?: ErrorHandler): T | null;
638
- /**
639
- * Async version of catchError for explicit async/await usage.
1033
+ * @param initial Initial array value
1034
+ * @returns Tuple [getter, actions]
640
1035
  *
641
- * @param fn Async function to execute safely
642
- * @param onError Optional error handler
643
- * @returns Promise resolving to the result or null on error
644
- */
645
- declare function catchErrorAsync<T>(fn: () => Promise<T>, onError?: ErrorHandler): Promise<T | null>;
646
- /**
647
- * Sets a global error handler used by default if no onError is provided.
1036
+ * @example
1037
+ * ```ts
1038
+ * const [items, { push, remove, clear }] = array([1, 2, 3]);
1039
+ * push(4); // [1, 2, 3, 4]
1040
+ * remove(1); // [1, 3, 4] (removes index 1)
1041
+ * clear(); // []
1042
+ * ```
648
1043
  */
649
- declare function setGlobalErrorHandler(handler: ErrorHandler): void;
650
-
1044
+ interface ArrayActions<T> {
1045
+ /** Add one or more items to the end */
1046
+ push(...items: T[]): void;
1047
+ /** Remove and return the last item */
1048
+ pop(): T | undefined;
1049
+ /** Remove and return the first item */
1050
+ shift(): T | undefined;
1051
+ /** Add one or more items to the beginning */
1052
+ unshift(...items: T[]): void;
1053
+ /** Remove/replace elements at a position */
1054
+ splice(start: number, deleteCount?: number, ...items: T[]): T[];
1055
+ /** Remove item at index */
1056
+ remove(index: number): void;
1057
+ /** Remove first item matching predicate */
1058
+ removeWhere(predicate: (item: T) => boolean): void;
1059
+ /** Replace the entire array */
1060
+ set(items: T[]): void;
1061
+ /** Update item at a specific index */
1062
+ update(index: number, value: T): void;
1063
+ /** Update item at a specific index via updater function */
1064
+ updateWhere(predicate: (item: T) => boolean, updater: (item: T) => T): void;
1065
+ /** Sort the array in place */
1066
+ sort(compareFn?: (a: T, b: T) => number): void;
1067
+ /** Reverse the array in place */
1068
+ reverse(): void;
1069
+ /** Filter the array (returns new reactive array) */
1070
+ filter(predicate: (item: T, index: number) => boolean): void;
1071
+ /** Map and replace (transforms all items) */
1072
+ map(transform: (item: T, index: number) => T): void;
1073
+ /** Clear all items */
1074
+ clear(): void;
1075
+ }
1076
+ declare function array<T>(initial?: T[]): [Accessor<T[]>, ArrayActions<T>];
651
1077
  /**
652
- * Generate a stable, framework-unique ID string suitable for a11y pairing
653
- * (`aria-labelledby`, `htmlFor` + `id`, etc.).
1078
+ * Optimized reactive array hook. Uses in-place mutations with a version
1079
+ * counter to avoid full array copies on every operation.
654
1080
  *
655
- * Each call returns a fresh incrementing id. Optionally accepts a prefix.
1081
+ * Internally maintains a mutable array and only creates a frozen snapshot
1082
+ * when the getter is called after a mutation. Operations like push, pop,
1083
+ * splice, sort, and reverse mutate in-place (O(1) or O(n) as appropriate)
1084
+ * instead of copying the entire array.
656
1085
  *
657
- * IDs are plain strings (not reactive) — call once per component instance
658
- * and reuse the returned value for both sides of the association.
1086
+ * The public API is identical to `array`.
659
1087
  *
660
- * @param prefix Optional prefix, default "sibu"
661
- * @returns A unique id like `"sibu-1"` or `"my-input-2"`
1088
+ * @param initial Initial array value
1089
+ * @returns Tuple [getter, actions]
662
1090
  *
663
1091
  * @example
664
1092
  * ```ts
665
- * function Field(labelText: string) {
666
- * const id = createId("field");
667
- * return div([
668
- * label({ for: id }, labelText),
669
- * input({ id }),
670
- * ]);
671
- * }
1093
+ * const [items, { push, remove, clear }] = reactiveArray([1, 2, 3]);
1094
+ * push(4); // [1, 2, 3, 4] — mutates in-place, no copy
1095
+ * remove(1); // [1, 3, 4]
1096
+ * clear(); // []
672
1097
  * ```
673
1098
  */
674
- declare function createId(prefix?: string): string;
1099
+ declare function reactiveArray<T>(initial?: T[]): [Accessor<readonly T[]>, ArrayActions<T>];
1100
+
1101
+ interface AsyncDerivedState<T> {
1102
+ /** Resolved value, or `initial` while loading. */
1103
+ value: () => T;
1104
+ /** True while the underlying promise is in-flight. */
1105
+ loading: () => boolean;
1106
+ /** The last caught error, or `null`. */
1107
+ error: () => unknown | null;
1108
+ /** Manually re-run the async computation. */
1109
+ refresh: () => void;
1110
+ }
675
1111
  /**
676
- * Reset the id counter. Intended for tests and SSR setups that want
677
- * deterministic ids across runs.
1112
+ * `asyncDerived` is the async counterpart of `derived`: it takes a factory
1113
+ * that returns a Promise and re-runs whenever its reactive dependencies
1114
+ * change. The returned object exposes reactive `value`, `loading`, and
1115
+ * `error` getters, plus a `refresh()` trigger.
1116
+ *
1117
+ * Stale responses are dropped: if a new run starts before an older one
1118
+ * resolves, the older one's result is ignored. This prevents flicker when
1119
+ * dependencies change rapidly (e.g. typing in a search box).
1120
+ *
1121
+ * Unlike `query()` or `resource()`, `asyncDerived` has no caching or retry
1122
+ * logic — it's a minimal async-reactivity primitive suited for ad-hoc
1123
+ * derivations (parsing, formatting, validation against a server).
1124
+ *
1125
+ * @param factory Async function returning the derived value
1126
+ * @param initial Value used while the first computation is pending
678
1127
  *
679
- * @internal
1128
+ * @example
1129
+ * ```ts
1130
+ * const [query, setQuery] = signal("");
1131
+ * const results = asyncDerived(async () => {
1132
+ * const q = query();
1133
+ * if (!q) return [];
1134
+ * const r = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
1135
+ * return r.json();
1136
+ * }, []);
1137
+ * ```
680
1138
  */
681
- declare function __resetIdCounter(): void;
1139
+ declare function asyncDerived<T>(factory: () => Promise<T>, initial: T): AsyncDerivedState<T>;
682
1140
 
683
1141
  /**
684
- * Register a teardown function for a DOM node.
685
- * When dispose(node) is called, all registered teardowns run.
686
- */
687
- declare function registerDisposer(node: Node, teardown: () => void): void;
688
- /**
689
- * Run all registered teardowns for a node and its descendants,
690
- * cleaning up reactive subscriptions to prevent memory leaks.
691
- * Call this when removing elements from the DOM.
1142
+ * Deep equality comparison for objects and arrays.
1143
+ * Falls back to Object.is for primitives.
1144
+ * Handles circular references, shared sub-references, and common
1145
+ * built-in types (Date, RegExp, Map, Set, ArrayBuffer, TypedArrays).
692
1146
  *
693
- * Uses an iterative depth-first traversal to avoid stack overflow
694
- * on deeply nested DOM trees.
695
- */
696
- declare function dispose(node: Node): void;
697
- /**
698
- * Check for potential binding leaks. Returns the number of active DOM bindings.
699
- * In dev mode, logs a warning if the count exceeds the threshold.
700
- * In production, _isDev is false so the counter is always 0.
1147
+ * The `seen` parameter tracks `(a, b)` pairs not just `a` — so that
1148
+ * a shared sub-object compared against two different partners is always
1149
+ * fully checked, while genuine cycles (same a-with-same-b revisited)
1150
+ * still terminate.
701
1151
  */
702
- declare function checkLeaks(warnThreshold?: number): number;
703
-
704
- declare const __accessor: unique symbol;
1152
+ declare function deepEqual(a: unknown, b: unknown, seen?: Map<object, Set<object>>): boolean;
705
1153
  /**
706
- * A reactive signal getter returned by signal(), derived(), and similar primitives.
1154
+ * Like signal but uses deep equality comparison instead of Object.is.
1155
+ * This prevents unnecessary re-renders when setting an object/array
1156
+ * to a structurally identical value.
707
1157
  *
708
- * Pass an Accessor directly into reactive prop positions — never call it there:
709
- * ```ts
710
- * const [count, setCount] = signal(0);
1158
+ * @param initial Initial value
1159
+ * @returns Tuple [getter, setter] — same shape as `signal()`, preserving
1160
+ * the `Accessor<T>` brand on the getter.
711
1161
  *
712
- * div(count) // ✓ reactive — Accessor passed directly
713
- * div(() => count()) // ✓ reactive — explicit arrow wrapper
714
- * div(count()) // static — evaluated once, not reactive
1162
+ * @example
1163
+ * ```ts
1164
+ * const [user, setUser] = deepSignal({ name: "Alice", age: 25 });
1165
+ * setUser({ name: "Alice", age: 25 }); // No notification — same structure
1166
+ * setUser({ name: "Bob", age: 25 }); // Notifies — different value
715
1167
  * ```
716
1168
  */
717
- type Accessor<T> = (() => T) & {
718
- readonly [__accessor]?: never;
719
- };
720
- type SetState<T> = (next: T | ((prev: T) => T)) => void;
721
- type StateTuple<T> = [Accessor<T>, SetState<T>];
722
- /** Options for signal */
723
- interface SignalOptions<T = unknown> {
724
- /** Debug name for devtools inspection. Only used in development. */
725
- name?: string;
726
- /** Custom equality function. Defaults to Object.is(). */
727
- equals?: (prev: T, next: T) => boolean;
728
- }
1169
+ declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T) => T)) => void];
1170
+
729
1171
  /**
730
- * signal creates a reactive signal that holds a value of type T.
731
- * Returns a tuple: [getter, setter].
1172
+ * derived creates a derived reactive signal whose value updates when dependencies change.
732
1173
  *
733
- * @param initial Initial value
734
- * @param options Optional config: `{ name: "count" }` for devtools labeling
1174
+ * Uses lazy pull-based evaluation with a single dirty flag:
1175
+ * - When a dependency changes, the computed is marked dirty (no re-evaluation).
1176
+ * - Dirtiness propagates downstream via propagateDirty.
1177
+ * - The getter only re-evaluates when actually read (pull-based).
1178
+ * - On re-evaluation, dependencies are re-tracked via retrack() so that
1179
+ * derived-of-derived chains propagate correctly without paying the full
1180
+ * Set-delete + re-add cost of track()'s cleanup phase.
1181
+ *
1182
+ * NOTE: a previous revision experimented with three-color (CLEAN/CHECK/DIRTY)
1183
+ * state for read-side value-change short-circuiting. It regressed every
1184
+ * benchmark except Memory (Deep Chain +122%, Component Tree +20%) because
1185
+ * the workloads always produce a new downstream value and CHECK had no
1186
+ * work to skip — only overhead to add. Keeping the simpler boolean flag
1187
+ * here; revisit CHECK propagation when we have benchmarks that exercise
1188
+ * stabilisation on diamond / conditional-branch patterns.
735
1189
  */
736
- declare function signal<T>(initial: T, options?: SignalOptions<T>): StateTuple<T>;
1190
+ declare function derived<T>(getter: () => T, options?: {
1191
+ name?: string;
1192
+ /** Custom equality — when the recomputed value equals the previous,
1193
+ * downstream subscribers are not notified. Defaults to `Object.is`. */
1194
+ equals?: (a: T, b: T) => boolean;
1195
+ }): Accessor<T>;
737
1196
 
738
1197
  /** Options for effect */
739
1198
  interface EffectOptions {
@@ -786,41 +1245,26 @@ type EffectBody = (onCleanup: OnCleanup) => void;
786
1245
  declare function effect(effectFn: EffectBody | (() => void), options?: EffectOptions): () => void;
787
1246
 
788
1247
  /**
789
- * derived creates a derived reactive signal whose value updates when dependencies change.
790
- *
791
- * Uses lazy pull-based evaluation with a single dirty flag:
792
- * - When a dependency changes, the computed is marked dirty (no re-evaluation).
793
- * - Dirtiness propagates downstream via propagateDirty.
794
- * - The getter only re-evaluates when actually read (pull-based).
795
- * - On re-evaluation, dependencies are re-tracked via retrack() so that
796
- * derived-of-derived chains propagate correctly without paying the full
797
- * Set-delete + re-add cost of track()'s cleanup phase.
1248
+ * ref creates a mutable reference object with a reactive `current` property.
798
1249
  *
799
- * NOTE: a previous revision experimented with three-color (CLEAN/CHECK/DIRTY)
800
- * state for read-side value-change short-circuiting. It regressed every
801
- * benchmark except Memory (Deep Chain +122%, Component Tree +20%) because
802
- * the workloads always produce a new downstream value and CHECK had no
803
- * work to skip — only overhead to add. Keeping the simpler boolean flag
804
- * here; revisit CHECK propagation when we have benchmarks that exercise
805
- * stabilisation on diamond / conditional-branch patterns.
806
- */
807
- declare function derived<T>(getter: () => T, options?: {
808
- name?: string;
809
- /** Custom equality — when the recomputed value equals the previous,
810
- * downstream subscribers are not notified. Defaults to `Object.is`. */
811
- equals?: (a: T, b: T) => boolean;
812
- }): Accessor<T>;
813
-
814
- /**
815
- * Watches a reactive getter and calls callback with (newValue, oldValue) when it changes.
1250
+ * Reading `.current` tracks the dependency (like a signal getter).
1251
+ * Writing `.current` notifies subscribers (like a signal setter).
1252
+ * This makes ref compatible with APIs that take reactive getters,
1253
+ * such as resize(), draggable(), and dropZone().
816
1254
  *
817
- * In SSR mode, watch is a no-op — subscriptions should not run on the server.
1255
+ * Common uses:
1256
+ * - Storing DOM element references (works with tagFactory's ref prop)
1257
+ * - Holding mutable values with optional reactivity
1258
+ * - Imperative API handles (e.g., focus, scroll)
818
1259
  *
819
- * @param getter Function that returns the value to watch (reads reactive signals).
820
- * @param callback Function called when the watched value changes.
821
- * @returns Teardown function to cancel the watcher.
1260
+ * @param initial Optional initial value for the ref
1261
+ * @returns An object with a reactive `current` property
822
1262
  */
823
- declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefined) => void): () => void;
1263
+ interface Ref<T> {
1264
+ current: T;
1265
+ }
1266
+ declare function ref<T>(initial: T): Ref<T>;
1267
+ declare function ref<T = undefined>(): Ref<T | undefined>;
824
1268
 
825
1269
  type StoreSubscriber<T> = (state: T) => void;
826
1270
  interface StoreActions<T> {
@@ -859,131 +1303,18 @@ interface StoreActions<T> {
859
1303
  */
860
1304
  declare function store<T extends object>(initialState: T): [store: {
861
1305
  readonly [K in keyof T]: T[K];
862
- }, actions: StoreActions<T>];
863
-
864
- /**
865
- * ref creates a mutable reference object with a reactive `current` property.
866
- *
867
- * Reading `.current` tracks the dependency (like a signal getter).
868
- * Writing `.current` notifies subscribers (like a signal setter).
869
- * This makes ref compatible with APIs that take reactive getters,
870
- * such as resize(), draggable(), and dropZone().
871
- *
872
- * Common uses:
873
- * - Storing DOM element references (works with tagFactory's ref prop)
874
- * - Holding mutable values with optional reactivity
875
- * - Imperative API handles (e.g., focus, scroll)
876
- *
877
- * @param initial Optional initial value for the ref
878
- * @returns An object with a reactive `current` property
879
- */
880
- interface Ref<T> {
881
- current: T;
882
- }
883
- declare function ref<T>(initial: T): Ref<T>;
884
- declare function ref<T = undefined>(): Ref<T | undefined>;
885
-
886
- /**
887
- * Reactive array hook. Provides common array operations that
888
- * automatically trigger reactive updates.
889
- *
890
- * @param initial Initial array value
891
- * @returns Tuple [getter, actions]
892
- *
893
- * @example
894
- * ```ts
895
- * const [items, { push, remove, clear }] = array([1, 2, 3]);
896
- * push(4); // [1, 2, 3, 4]
897
- * remove(1); // [1, 3, 4] (removes index 1)
898
- * clear(); // []
899
- * ```
900
- */
901
- interface ArrayActions<T> {
902
- /** Add one or more items to the end */
903
- push(...items: T[]): void;
904
- /** Remove and return the last item */
905
- pop(): T | undefined;
906
- /** Remove and return the first item */
907
- shift(): T | undefined;
908
- /** Add one or more items to the beginning */
909
- unshift(...items: T[]): void;
910
- /** Remove/replace elements at a position */
911
- splice(start: number, deleteCount?: number, ...items: T[]): T[];
912
- /** Remove item at index */
913
- remove(index: number): void;
914
- /** Remove first item matching predicate */
915
- removeWhere(predicate: (item: T) => boolean): void;
916
- /** Replace the entire array */
917
- set(items: T[]): void;
918
- /** Update item at a specific index */
919
- update(index: number, value: T): void;
920
- /** Update item at a specific index via updater function */
921
- updateWhere(predicate: (item: T) => boolean, updater: (item: T) => T): void;
922
- /** Sort the array in place */
923
- sort(compareFn?: (a: T, b: T) => number): void;
924
- /** Reverse the array in place */
925
- reverse(): void;
926
- /** Filter the array (returns new reactive array) */
927
- filter(predicate: (item: T, index: number) => boolean): void;
928
- /** Map and replace (transforms all items) */
929
- map(transform: (item: T, index: number) => T): void;
930
- /** Clear all items */
931
- clear(): void;
932
- }
933
- declare function array<T>(initial?: T[]): [Accessor<T[]>, ArrayActions<T>];
934
- /**
935
- * Optimized reactive array hook. Uses in-place mutations with a version
936
- * counter to avoid full array copies on every operation.
937
- *
938
- * Internally maintains a mutable array and only creates a frozen snapshot
939
- * when the getter is called after a mutation. Operations like push, pop,
940
- * splice, sort, and reverse mutate in-place (O(1) or O(n) as appropriate)
941
- * instead of copying the entire array.
942
- *
943
- * The public API is identical to `array`.
944
- *
945
- * @param initial Initial array value
946
- * @returns Tuple [getter, actions]
947
- *
948
- * @example
949
- * ```ts
950
- * const [items, { push, remove, clear }] = reactiveArray([1, 2, 3]);
951
- * push(4); // [1, 2, 3, 4] — mutates in-place, no copy
952
- * remove(1); // [1, 3, 4]
953
- * clear(); // []
954
- * ```
955
- */
956
- declare function reactiveArray<T>(initial?: T[]): [Accessor<readonly T[]>, ArrayActions<T>];
957
-
958
- /**
959
- * Deep equality comparison for objects and arrays.
960
- * Falls back to Object.is for primitives.
961
- * Handles circular references, shared sub-references, and common
962
- * built-in types (Date, RegExp, Map, Set, ArrayBuffer, TypedArrays).
963
- *
964
- * The `seen` parameter tracks `(a, b)` pairs — not just `a` — so that
965
- * a shared sub-object compared against two different partners is always
966
- * fully checked, while genuine cycles (same a-with-same-b revisited)
967
- * still terminate.
968
- */
969
- declare function deepEqual(a: unknown, b: unknown, seen?: Map<object, Set<object>>): boolean;
1306
+ }, actions: StoreActions<T>];
1307
+
970
1308
  /**
971
- * Like signal but uses deep equality comparison instead of Object.is.
972
- * This prevents unnecessary re-renders when setting an object/array
973
- * to a structurally identical value.
1309
+ * Watches a reactive getter and calls callback with (newValue, oldValue) when it changes.
974
1310
  *
975
- * @param initial Initial value
976
- * @returns Tuple [getter, setter] — same shape as `signal()`, preserving
977
- * the `Accessor<T>` brand on the getter.
1311
+ * In SSR mode, watch is a no-op — subscriptions should not run on the server.
978
1312
  *
979
- * @example
980
- * ```ts
981
- * const [user, setUser] = deepSignal({ name: "Alice", age: 25 });
982
- * setUser({ name: "Alice", age: 25 }); // No notification — same structure
983
- * setUser({ name: "Bob", age: 25 }); // Notifies — different value
984
- * ```
1313
+ * @param getter Function that returns the value to watch (reads reactive signals).
1314
+ * @param callback Function called when the watched value changes.
1315
+ * @returns Teardown function to cancel the watcher.
985
1316
  */
986
- declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T) => T)) => void];
1317
+ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefined) => void): () => void;
987
1318
 
988
1319
  /**
989
1320
  * Creates a writable computed value — a derived getter paired with
@@ -1022,168 +1353,71 @@ declare function writable<T>(get: () => T, set: (value: T) => void, options?: {
1022
1353
  name?: string;
1023
1354
  }): [Accessor<T>, (value: T) => void];
1024
1355
 
1025
- interface AsyncDerivedState<T> {
1026
- /** Resolved value, or `initial` while loading. */
1027
- value: () => T;
1028
- /** True while the underlying promise is in-flight. */
1029
- loading: () => boolean;
1030
- /** The last caught error, or `null`. */
1031
- error: () => unknown | null;
1032
- /** Manually re-run the async computation. */
1033
- refresh: () => void;
1034
- }
1035
- /**
1036
- * `asyncDerived` is the async counterpart of `derived`: it takes a factory
1037
- * that returns a Promise and re-runs whenever its reactive dependencies
1038
- * change. The returned object exposes reactive `value`, `loading`, and
1039
- * `error` getters, plus a `refresh()` trigger.
1040
- *
1041
- * Stale responses are dropped: if a new run starts before an older one
1042
- * resolves, the older one's result is ignored. This prevents flicker when
1043
- * dependencies change rapidly (e.g. typing in a search box).
1044
- *
1045
- * Unlike `query()` or `resource()`, `asyncDerived` has no caching or retry
1046
- * logic — it's a minimal async-reactivity primitive suited for ad-hoc
1047
- * derivations (parsing, formatting, validation against a server).
1048
- *
1049
- * @param factory Async function returning the derived value
1050
- * @param initial Value used while the first computation is pending
1051
- *
1052
- * @example
1053
- * ```ts
1054
- * const [query, setQuery] = signal("");
1055
- * const results = asyncDerived(async () => {
1056
- * const q = query();
1057
- * if (!q) return [];
1058
- * const r = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
1059
- * return r.json();
1060
- * }, []);
1061
- * ```
1062
- */
1063
- declare function asyncDerived<T>(factory: () => Promise<T>, initial: T): AsyncDerivedState<T>;
1064
-
1065
1356
  /**
1066
- * Lifecycle hooks for SibuJS components.
1067
- *
1068
- * These hooks schedule callbacks to run after the component's DOM
1069
- * has been mounted or when it is removed from the document.
1070
- *
1071
- * @example
1072
- * ```ts
1073
- * function MyComponent() {
1074
- * onMount(() => {
1075
- * console.log("Component is in the DOM");
1076
- * });
1357
+ * SSR context for SibuJS.
1077
1358
  *
1078
- * onUnmount(() => {
1079
- * console.log("Component was removed");
1080
- * });
1359
+ * During server-side rendering, side effects (effect, watch, onMount)
1360
+ * should not run. This module provides a flag to enable/disable SSR mode.
1081
1361
  *
1082
- * return div("Hello");
1083
- * }
1084
- * ```
1085
- */
1086
- type CleanupFn = () => void;
1087
- /**
1088
- * Runs a callback once the component's element has been inserted into the DOM.
1089
- * Uses queueMicrotask to defer execution until after the current synchronous
1090
- * rendering pass completes.
1362
+ * Concurrency: on Node we back the flag with AsyncLocalStorage so
1363
+ * simultaneous requests get independent SSR scopes. On runtimes without
1364
+ * AsyncLocalStorage (browser, some edge runtimes) we fall back to a
1365
+ * module-global boolean.
1091
1366
  *
1092
- * Optionally returns a cleanup function that will be called on unmount
1093
- * (if you also use onUnmount, prefer that for explicit cleanup).
1367
+ * Usage:
1368
+ * enableSSR(); // Call before rendering on the server
1369
+ * renderToString(...);
1370
+ * disableSSR(); // Call after rendering (cleanup)
1094
1371
  *
1095
- * @param callback Function to run after mount. May return a cleanup function.
1096
- * @param element Optional element to observe; if provided, waits until it's connected.
1372
+ * Or use the scoped helper:
1373
+ * withSSR(() => renderToString(...));
1374
+ * runInSSRContext(() => renderToString(...));
1097
1375
  */
1098
- declare function onMount(callback: () => undefined | CleanupFn, element?: HTMLElement): void;
1099
1376
  /**
1100
- * Runs a callback when the given element is removed from the DOM.
1101
- * Uses a shared MutationObserver to watch for disconnection, plus
1102
- * `registerDisposer` so explicit dispose() paths also trigger the callback.
1103
- *
1104
- * @param callback Function to run on unmount
1105
- * @param element The element to watch for removal
1377
+ * Per-request SSR store. Currently holds the SSR flag plus a
1378
+ * suspense-id counter so concurrent streaming renders never collide.
1106
1379
  */
1107
- declare function onUnmount(callback: CleanupFn, element: HTMLElement): void;
1380
+ interface SSRStore {
1381
+ ssr: boolean;
1382
+ suspenseIdCounter: number;
1383
+ /**
1384
+ * Per-request data caches (e.g. the query cache). Lazily created and keyed
1385
+ * by subsystem so request-scoped data never bleeds between concurrent
1386
+ * server renders. Typed loosely to avoid a dependency cycle with data/.
1387
+ */
1388
+ caches?: Map<string, Map<string, unknown>>;
1389
+ }
1390
+ /** Returns the active store (ALS or fallback). */
1391
+ declare function getSSRStore(): SSRStore;
1392
+ /** Returns true when running in SSR mode. */
1393
+ declare function isSSR(): boolean;
1108
1394
  /**
1109
- * Register a cleanup callback that runs when the given element is disposed.
1110
- * Integrates with `when()`, `match()`, and `each()` which call `dispose()`
1111
- * on removed nodes, triggering all registered cleanup functions.
1112
- *
1113
- * @param callback Cleanup function (close sockets, clear intervals, etc.)
1114
- * @param element The component's root node to attach cleanup to
1115
- *
1116
- * @example
1117
- * ```ts
1118
- * function RealtimeBar(siteId: string) {
1119
- * const ws = new WebSocket(`/ws/sites/${siteId}/realtime`);
1120
- * const root = div("Realtime data...");
1121
- * onCleanup(() => ws.close(), root);
1122
- * return root;
1123
- * }
1124
- * ```
1395
+ * Returns a request-scoped cache map for the given subsystem when running
1396
+ * under SSR (so concurrent requests never share it), or `null` on the client
1397
+ * where a process-global cache is correct. On Node the store is backed by
1398
+ * AsyncLocalStorage, giving each request its own caches.
1125
1399
  */
1126
- declare function onCleanup(callback: CleanupFn, element: Node): void;
1127
-
1400
+ declare function getRequestScopedCache<V>(name: string): Map<string, V> | null;
1401
+ /** Enable SSR mode. Side effects (effect, watch, onMount) become no-ops. */
1402
+ declare function enableSSR(): void;
1403
+ /** Disable SSR mode. Side effects resume normal behavior. */
1404
+ declare function disableSSR(): void;
1128
1405
  /**
1129
- * Context API for SibuJS a reactive global value that any component
1130
- * can read without prop drilling.
1131
- *
1132
- * Note: this is a **global reactive store**, not a subtree-scoped DI
1133
- * system. Calling `provide()` sets the value for ALL consumers, not
1134
- * just descendants of the provider. For most apps this is sufficient
1135
- * — use separate `context()` instances for independent scopes.
1136
- *
1137
- * @example
1138
- * ```ts
1139
- * // Create a context with a default value
1140
- * const ThemeContext = context("light");
1141
- *
1142
- * // Set the value (global — affects all consumers)
1143
- * ThemeContext.provide("dark");
1144
- *
1145
- * // Read reactively from any component
1146
- * function Child() {
1147
- * const theme = ThemeContext.use(); // reactive getter
1148
- * return div(() => `Theme: ${theme()}`);
1149
- * }
1150
- * ```
1406
+ * Run `fn` inside a fresh request-scoped SSR context. On Node this uses
1407
+ * AsyncLocalStorage so concurrent requests never share state; elsewhere
1408
+ * it falls back to mutating the module-global store.
1151
1409
  */
1152
- interface Context<T> {
1153
- /**
1154
- * Set the context value globally. Affects all consumers.
1155
- *
1156
- * Returns a `restore` function that re-sets the context to the value it
1157
- * had *before* this `provide` call. Useful for scoped overrides:
1158
- *
1159
- * ```ts
1160
- * const restore = Theme.provide("dark");
1161
- * try { renderChild(); } finally { restore(); }
1162
- * ```
1163
- *
1164
- * Callers that don't need scoping can ignore the return value — existing
1165
- * semantics are preserved.
1166
- */
1167
- provide(value: T): () => void;
1168
- /** Get a reactive getter for the current context value. */
1169
- use(): () => T;
1170
- /** Get the current value directly (non-reactive). */
1171
- get(): T;
1172
- /** Update the provided value reactively. */
1173
- set(value: T): void;
1174
- /**
1175
- * Run `fn` with the context temporarily set to `value`, then restore the
1176
- * previous value (even if `fn` throws). Returns the result of `fn`.
1177
- */
1178
- withContext<R>(value: T, fn: () => R): R;
1179
- }
1410
+ declare function runInSSRContext<T>(fn: () => T): T;
1180
1411
  /**
1181
- * Creates a new context with an optional default value.
1412
+ * Run a function in SSR mode. Automatically enables/disables SSR around the callback.
1413
+ * Returns whatever the callback returns.
1182
1414
  *
1183
- * @param defaultValue The fallback value when no provider is found
1184
- * @returns A Context object with provide, use, get, and set methods
1415
+ * Nesting-safe: saves the prior SSR flag and restores it in the `finally`
1416
+ * block. A nested `withSSR(...)` call cannot prematurely flip the outer
1417
+ * scope's SSR flag back to `false`, and an exception thrown inside `fn`
1418
+ * still leaves the flag in its original state.
1185
1419
  */
1186
- declare function context<T>(defaultValue: T): Context<T>;
1420
+ declare function withSSR<T>(fn: () => T): T;
1187
1421
 
1188
1422
  /**
1189
1423
  * Dev-only wrapper that runs `fn` twice — once now, once on the next
@@ -1226,70 +1460,123 @@ declare function strict<T>(fn: () => T): T;
1226
1460
  declare function strictEffect(fn: () => void): () => void;
1227
1461
 
1228
1462
  /**
1229
- * SSR context for SibuJS.
1230
- *
1231
- * During server-side rendering, side effects (effect, watch, onMount)
1232
- * should not run. This module provides a flag to enable/disable SSR mode.
1233
- *
1234
- * Concurrency: on Node we back the flag with AsyncLocalStorage so
1235
- * simultaneous requests get independent SSR scopes. On runtimes without
1236
- * AsyncLocalStorage (browser, some edge runtimes) we fall back to a
1237
- * module-global boolean.
1463
+ * Helpers handed to an `enhance` setup. Every binding is fine-grained (its own
1464
+ * effect) and auto-disposed when the root element (or the returned dispose) is
1465
+ * torn down.
1466
+ *
1467
+ * Target resolution for every helper:
1468
+ * - `"@name"` → a descendant marked `data-ref="name"` (the ergonomic form).
1469
+ * - any other string a raw CSS selector, queried within the root.
1470
+ * - an `Element` used as-is.
1471
+ * - `null` / omitted (where allowed) → the root element itself.
1472
+ */
1473
+ interface EnhanceContext {
1474
+ /** The enhanced root element (the server-rendered node). */
1475
+ root: HTMLElement;
1476
+ /** First descendant matching a `@ref`/selector (or the root for `null`). */
1477
+ ref<T extends Element = HTMLElement>(target: string | null): T | null;
1478
+ /** All descendants matching a `@ref`/selector. */
1479
+ refs<T extends Element = HTMLElement>(target: string): T[];
1480
+ /** Attach an auto-removed event listener to a target (root if `null`). */
1481
+ on<K extends keyof HTMLElementEventMap>(target: string | Element | null, event: K, handler: (event: HTMLElementEventMap[K], el: HTMLElement) => void, options?: AddEventListenerOptions): void;
1482
+ /** Reactively drive `textContent` of an existing node. */
1483
+ text(target: string | Element | null, value: () => unknown): void;
1484
+ /** Reactively drive an attribute; a `null`/`false`/`undefined` value removes it. */
1485
+ attr(target: string | Element | null, name: string, value: () => unknown): void;
1486
+ /** Reactively toggle a class on a node. */
1487
+ classed(target: string | Element | null, name: string, on: () => boolean): void;
1488
+ /** Reactively toggle visibility (sets `display:none` when `false`). */
1489
+ show(target: string | Element | null, when: () => boolean): void;
1490
+ /** Two-way bind a form control to a `[get, set]` signal tuple. */
1491
+ model<T>(target: string | Element, state: readonly [() => T, (value: T) => void], options?: {
1492
+ event?: string;
1493
+ }): void;
1494
+ /** Register arbitrary teardown to run on disposal. */
1495
+ cleanup(fn: () => void): void;
1496
+ }
1497
+ /** A setup function for `enhance` — wire reactivity, optionally return cleanup.
1498
+ * The `void | (() => void)` shape mirrors `useEffect`'s "return a teardown, or
1499
+ * nothing". */
1500
+ type EnhanceSetup = (ctx: EnhanceContext) => void | (() => void);
1501
+ /**
1502
+ * Attach reactivity to an existing element (typically server-rendered) without
1503
+ * replacing it. Returns a dispose function; disposal is also wired to the
1504
+ * element, so removing its subtree cleans everything up.
1238
1505
  *
1239
- * Usage:
1240
- * enableSSR(); // Call before rendering on the server
1241
- * renderToString(...);
1242
- * disableSSR(); // Call after rendering (cleanup)
1506
+ * @param target An `Element` or a CSS selector resolved against `document`
1507
+ * (the first match is used; see {@link enhanceAll} for many).
1508
+ * @param setup Wires reactivity via the {@link EnhanceContext}.
1243
1509
  *
1244
- * Or use the scoped helper:
1245
- * withSSR(() => renderToString(...));
1246
- * runInSSRContext(() => renderToString(...));
1247
- */
1248
- /**
1249
- * Per-request SSR store. Currently holds the SSR flag plus a
1250
- * suspense-id counter so concurrent streaming renders never collide.
1251
- */
1252
- interface SSRStore {
1253
- ssr: boolean;
1254
- suspenseIdCounter: number;
1255
- /**
1256
- * Per-request data caches (e.g. the query cache). Lazily created and keyed
1257
- * by subsystem so request-scoped data never bleeds between concurrent
1258
- * server renders. Typed loosely to avoid a dependency cycle with data/.
1259
- */
1260
- caches?: Map<string, Map<string, unknown>>;
1261
- }
1262
- /** Returns the active store (ALS or fallback). */
1263
- declare function getSSRStore(): SSRStore;
1264
- /** Returns true when running in SSR mode. */
1265
- declare function isSSR(): boolean;
1510
+ * @example
1511
+ * ```ts
1512
+ * // server HTML: <div data-counter><b data-ref="n">0</b><button data-ref="inc">+1</button></div>
1513
+ * const [n, setN] = signal(0);
1514
+ * enhance("[data-counter]", (ctx) => {
1515
+ * ctx.text("@n", () => n());
1516
+ * ctx.on("@inc", "click", () => setN((v) => v + 1));
1517
+ * });
1518
+ * ```
1519
+ */
1520
+ declare function enhance(target: Element | string, setup: EnhanceSetup): () => void;
1266
1521
  /**
1267
- * Returns a request-scoped cache map for the given subsystem when running
1268
- * under SSR (so concurrent requests never share it), or `null` on the client
1269
- * where a process-global cache is correct. On Node the store is backed by
1270
- * AsyncLocalStorage, giving each request its own caches.
1522
+ * Enhance every element matching a selector. Returns a single dispose that
1523
+ * tears down all of them.
1271
1524
  */
1272
- declare function getRequestScopedCache<V>(name: string): Map<string, V> | null;
1273
- /** Enable SSR mode. Side effects (effect, watch, onMount) become no-ops. */
1274
- declare function enableSSR(): void;
1275
- /** Disable SSR mode. Side effects resume normal behavior. */
1276
- declare function disableSSR(): void;
1525
+ declare function enhanceAll(selector: string, setup: EnhanceSetup): () => void;
1526
+
1527
+ /** When an island activates. Declared per-element via `data-sibu-load`. */
1528
+ type IslandStrategy = "load" | "idle" | "visible" | "interaction" | "media";
1529
+ /** A lazy code loader: resolves to an island setup (or a module whose `default`
1530
+ * is one). Only fetched when the island activates. Wrap with {@link lazyIsland}. */
1531
+ type IslandLoader = () => Promise<EnhanceSetup | {
1532
+ default: EnhanceSetup;
1533
+ }>;
1534
+ /** Either an inline setup, or a {@link lazyIsland}-branded loader. */
1535
+ type IslandRegistration = EnhanceSetup | IslandLoader;
1277
1536
  /**
1278
- * Run `fn` inside a fresh request-scoped SSR context. On Node this uses
1279
- * AsyncLocalStorage so concurrent requests never share state; elsewhere
1280
- * it falls back to mutating the module-global store.
1537
+ * Mark a loader as lazy island code its module is fetched only when the island
1538
+ * activates, so a page ships ~0 JS for islands that never trigger.
1539
+ *
1540
+ * @example
1541
+ * ```ts
1542
+ * registerIsland("chart", lazyIsland(() => import("./islands/chart.js")));
1543
+ * ```
1281
1544
  */
1282
- declare function runInSSRContext<T>(fn: () => T): T;
1545
+ declare function lazyIsland(loader: IslandLoader): IslandLoader;
1283
1546
  /**
1284
- * Run a function in SSR mode. Automatically enables/disables SSR around the callback.
1285
- * Returns whatever the callback returns.
1547
+ * Register an island by name. The setup is applied via `enhance()` when a
1548
+ * matching `[data-sibu-island="name"]` element activates.
1286
1549
  *
1287
- * Nesting-safe: saves the prior SSR flag and restores it in the `finally`
1288
- * block. A nested `withSSR(...)` call cannot prematurely flip the outer
1289
- * scope's SSR flag back to `false`, and an exception thrown inside `fn`
1290
- * still leaves the flag in its original state.
1550
+ * @example
1551
+ * ```ts
1552
+ * registerIsland("counter", (ctx) => {
1553
+ * const [n, setN] = signal(0);
1554
+ * ctx.text("@n", () => n());
1555
+ * ctx.on("@inc", "click", () => setN((v) => v + 1));
1556
+ * });
1557
+ * ```
1291
1558
  */
1292
- declare function withSSR<T>(fn: () => T): T;
1559
+ declare function registerIsland(name: string, setup: IslandRegistration): void;
1560
+ /** Remove a registration (mainly for tests / HMR). */
1561
+ declare function unregisterIsland(name: string): void;
1562
+ interface MountIslandsOptions {
1563
+ /** IntersectionObserver options for the `visible` strategy. */
1564
+ rootMargin?: string;
1565
+ threshold?: number | number[];
1566
+ }
1567
+ /**
1568
+ * Scan a root for `[data-sibu-island]` elements and activate each according to
1569
+ * its `data-sibu-load` strategy (default `load`). Returns a cleanup function
1570
+ * that cancels pending schedulers and disposes every mounted island.
1571
+ *
1572
+ * Strategies (`data-sibu-load`):
1573
+ * - `load` — activate immediately (next microtask).
1574
+ * - `idle` — `requestIdleCallback` (falls back to a timeout).
1575
+ * - `visible` — when the element scrolls into view (IntersectionObserver).
1576
+ * - `interaction` — on first pointer/focus/key/touch interaction.
1577
+ * - `media` — when `data-sibu-media` (a media query) matches.
1578
+ */
1579
+ declare function mountIslands(root?: ParentNode | null, options?: MountIslandsOptions): () => void;
1293
1580
 
1294
1581
  /**
1295
1582
  * Batch multiple state updates into a single notification pass.
@@ -1333,23 +1620,14 @@ declare const enqueueBatchedSignal: BatchApi["enqueueBatchedSignal"];
1333
1620
  declare const isBatching: BatchApi["isBatching"];
1334
1621
 
1335
1622
  /**
1336
- * Wait for the next microtask after any currently-pending reactive updates
1337
- * have been flushed. Useful in imperative code that needs to read DOM state
1338
- * right after changing a signal.
1339
- *
1340
- * Under the hood this resolves on a microtask and again on an animation frame
1341
- * so both synchronous reactive passes and layout side-effects have settled.
1342
- *
1343
- * @returns Promise that resolves after the next DOM flush
1623
+ * Bind a dynamic attribute where both name and value can change reactively.
1624
+ * Useful for `:attr.name` style dynamic keys.
1344
1625
  *
1345
- * @example
1346
- * ```ts
1347
- * setMenuOpen(true);
1348
- * await nextTick();
1349
- * menuRef.current?.focus(); // DOM has the new menu rendered
1350
- * ```
1626
+ * When the attribute name changes, the old attribute is removed and the
1627
+ * new one is set. Returns a teardown function that stops reactive tracking
1628
+ * and removes the current attribute from the element.
1351
1629
  */
1352
- declare function nextTick(): Promise<void>;
1630
+ declare function bindDynamic(el: HTMLElement, nameGetter: string | (() => string), valueGetter: string | (() => unknown)): () => void;
1353
1631
 
1354
1632
  /**
1355
1633
  * Create a deferred mirror of a reactive getter. The returned accessor
@@ -1405,6 +1683,25 @@ interface TransitionState {
1405
1683
  */
1406
1684
  declare function transition(): TransitionState;
1407
1685
 
1686
+ /**
1687
+ * Wait for the next microtask — after any currently-pending reactive updates
1688
+ * have been flushed. Useful in imperative code that needs to read DOM state
1689
+ * right after changing a signal.
1690
+ *
1691
+ * Under the hood this resolves on a microtask and again on an animation frame
1692
+ * so both synchronous reactive passes and layout side-effects have settled.
1693
+ *
1694
+ * @returns Promise that resolves after the next DOM flush
1695
+ *
1696
+ * @example
1697
+ * ```ts
1698
+ * setMenuOpen(true);
1699
+ * await nextTick();
1700
+ * menuRef.current?.focus(); // DOM has the new menu rendered
1701
+ * ```
1702
+ */
1703
+ declare function nextTick(): Promise<void>;
1704
+
1408
1705
  type Subscriber = () => void;
1409
1706
  declare function suspendTracking(): void;
1410
1707
  declare function resumeTracking(): void;
@@ -1453,182 +1750,4 @@ declare const untracked: ReactiveApi["untracked"];
1453
1750
  declare const retrack: ReactiveApi["retrack"];
1454
1751
  declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
1455
1752
 
1456
- /**
1457
- * Bind a dynamic attribute where both name and value can change reactively.
1458
- * Useful for `:attr.name` style dynamic keys.
1459
- *
1460
- * When the attribute name changes, the old attribute is removed and the
1461
- * new one is set. Returns a teardown function that stops reactive tracking
1462
- * and removes the current attribute from the element.
1463
- */
1464
- declare function bindDynamic(el: HTMLElement, nameGetter: string | (() => string), valueGetter: string | (() => unknown)): () => void;
1465
-
1466
- declare function takePendingError(node: Element): Error | undefined;
1467
- type Component = () => HTMLElement;
1468
- type LazyImport = () => Promise<{
1469
- default: Component;
1470
- }>;
1471
- /**
1472
- * lazy() enables code-splitting by deferring the import of a component
1473
- * until it is first rendered. Returns a wrapper component that shows a
1474
- * loading state while the import resolves.
1475
- *
1476
- * @example
1477
- * ```ts
1478
- * const LazyDashboard = lazy(() => import("./Dashboard"));
1479
- *
1480
- * // Use inside Suspense for custom loading UI
1481
- * Suspense({
1482
- * nodes: () => LazyDashboard(),
1483
- * fallback: () => div("Loading dashboard..."),
1484
- * });
1485
- *
1486
- * // Or use standalone — shows default "Loading..." text
1487
- * LazyDashboard();
1488
- * ```
1489
- *
1490
- * @param importFn Dynamic import function returning `{ default: Component }`
1491
- * @returns A component function that lazy-loads on first call
1492
- */
1493
- declare function lazy(importFn: LazyImport): Component;
1494
- /**
1495
- * Suspense provides a fallback UI while lazy or async nodes are loading.
1496
- *
1497
- * @example
1498
- * ```ts
1499
- * Suspense({
1500
- * nodes: () => LazyChart(),
1501
- * fallback: () => div("Loading chart..."),
1502
- * });
1503
- * ```
1504
- *
1505
- * @param props.nodes Function that returns the async/lazy component
1506
- * @param props.fallback Function that returns the loading UI
1507
- * @returns An HTMLElement that swaps from fallback to content when ready
1508
- */
1509
- interface SuspenseProps {
1510
- nodes: () => HTMLElement;
1511
- fallback: () => HTMLElement;
1512
- }
1513
- declare function Suspense({ nodes, fallback }: SuspenseProps): HTMLElement;
1514
-
1515
- interface ErrorBoundaryOptions {
1516
- /**
1517
- * Fallback renderer given an Error and retry callback.
1518
- * Memoized internally — only re-created when the error changes.
1519
- */
1520
- fallback?: (error: Error, retry: () => void) => Element;
1521
- /**
1522
- * Called when an error is caught (sync or async).
1523
- */
1524
- onError?: (error: Error) => void;
1525
- /**
1526
- * A list of reactive getters. Whenever any of these values change
1527
- * after an error has been caught, the boundary automatically resets
1528
- * (clears the error and re-renders). Useful for recovering from a
1529
- * failed render after the user navigates, changes filters, or
1530
- * otherwise picks a new input that might not fail this time.
1531
- *
1532
- * @example
1533
- * ```ts
1534
- * const [route, setRoute] = signal("/");
1535
- * ErrorBoundary(
1536
- * { resetKeys: [route] },
1537
- * () => div(riskyPageFor(route())),
1538
- * );
1539
- * ```
1540
- */
1541
- resetKeys?: Array<() => unknown>;
1542
- }
1543
- /** @deprecated Renamed to `ErrorBoundaryOptions`; kept for typing compatibility. */
1544
- type ErrorBoundaryProps = ErrorBoundaryOptions;
1545
- /**
1546
- * ErrorBoundary component using SibuJS reactive pattern.
1547
- *
1548
- * Features:
1549
- * - Catches sync errors thrown by children
1550
- * - Catches the rejection of a Promise *returned* by children (async components)
1551
- * — it does NOT observe fire-and-forget async errors (rejections from timers,
1552
- * event handlers, or detached/unawaited promises), which surface as global
1553
- * `unhandledrejection` events instead.
1554
- * - Supports nested ErrorBoundaries (inner catches first, outer catches propagation)
1555
- * - Retry functionality to clear error and re-render children
1556
- * - Memoized fallback to avoid re-creating fallback UI on every render
1557
- * - onError callback for logging/telemetry
1558
- * - Improved CSS styling
1559
- */
1560
- declare function ErrorBoundary(children: () => Element): Element;
1561
- declare function ErrorBoundary(options: ErrorBoundaryOptions, children: () => Element): Element;
1562
-
1563
- type ErrorSeverity = "error" | "warning" | "info";
1564
- interface ErrorDisplayProps {
1565
- /** The Error (or error-like value) to show. */
1566
- error: unknown;
1567
- /** Severity colour. Default `"error"`. */
1568
- severity?: ErrorSeverity;
1569
- /** Optional headline override. By default `error.message` is used. */
1570
- title?: string;
1571
- /**
1572
- * Label for the primary action. Shown next to a Reload button.
1573
- * Leave unset to hide the retry action.
1574
- */
1575
- retryLabel?: string;
1576
- /** Callback for the retry button. */
1577
- onRetry?: () => void;
1578
- /**
1579
- * If `true`, the Reload button is hidden. Useful for embedded
1580
- * error panels where a full page reload is inappropriate.
1581
- */
1582
- hideReload?: boolean;
1583
- /**
1584
- * If `true`, the stack trace and metadata are always shown even in
1585
- * production builds. Default: only shown in dev.
1586
- */
1587
- alwaysShowDetails?: boolean;
1588
- /**
1589
- * Extra metadata rendered as a key/value list under the message.
1590
- * Useful for attaching request IDs, user IDs, etc.
1591
- */
1592
- metadata?: Record<string, string | number | boolean | null | undefined>;
1593
- }
1594
- /**
1595
- * Rich error display component. Wire an error-like value in and get
1596
- * a colored panel back with copy, retry, reload, stack, cause chain,
1597
- * and metadata — all built from tag factories. Reusable anywhere
1598
- * (inside `ErrorBoundary`, from a fetch failure, from a form submit).
1599
- *
1600
- * @example
1601
- * ```ts
1602
- * button(
1603
- * { on: { click: async () => {
1604
- * try { await save(); }
1605
- * catch (err) { mount(ErrorDisplay({ error: err, onRetry: save }), errorHost); }
1606
- * }}},
1607
- * "Save",
1608
- * );
1609
- * ```
1610
- */
1611
- declare function ErrorDisplay(props: ErrorDisplayProps): Element;
1612
-
1613
- interface LoadingProps {
1614
- /** Text to show alongside the spinner */
1615
- text?: string;
1616
- /** Visual variant: "spinner" (default) or "dots" */
1617
- variant?: "spinner" | "dots";
1618
- /** Size: "sm", "md" (default), "lg" */
1619
- size?: "sm" | "md" | "lg";
1620
- }
1621
- /**
1622
- * Built-in loading indicator component.
1623
- *
1624
- * @example
1625
- * ```ts
1626
- * Loading(); // Default spinner
1627
- * Loading({ text: "Loading data..." }); // With text
1628
- * Loading({ variant: "dots" }); // Dots animation
1629
- * Loading({ size: "lg" }); // Large spinner
1630
- * ```
1631
- */
1632
- declare function Loading(props?: LoadingProps): HTMLElement;
1633
-
1634
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getAction, getRequestScopedCache, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerAction, registerComponent, registerDisposer, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track$1 as track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
1753
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, type EnhanceContext, type EnhanceSetup, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, type IslandLoader, type IslandRegistration, type IslandStrategy, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, type MountIslandsOptions, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enhance, enhanceAll, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getAction, getRequestScopedCache, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, lazyIsland, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, mountIslands, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerAction, registerComponent, registerDisposer, registerIsland, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track$1 as track, transition, trapFocus, tspan, u, ul, unregisterComponent, unregisterIsland, untracked, use, var_, video, watch, when, withSSR, writable };