sibujs 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +29 -25
  2. package/dist/browser.cjs +804 -2
  3. package/dist/browser.d.cts +591 -1
  4. package/dist/browser.d.ts +591 -1
  5. package/dist/browser.js +50 -8
  6. package/dist/build.cjs +655 -237
  7. package/dist/build.js +15 -93
  8. package/dist/cdn.global.js +188 -7
  9. package/dist/chunk-2BYQDGN3.js +742 -0
  10. package/dist/chunk-32DY64NT.js +282 -0
  11. package/dist/chunk-3AIRKM3B.js +1263 -0
  12. package/dist/chunk-3X2YG6YM.js +505 -0
  13. package/dist/chunk-5X6PP2UK.js +28 -0
  14. package/dist/chunk-77L6NL3X.js +1097 -0
  15. package/dist/chunk-BGN5ZMP4.js +26 -0
  16. package/dist/chunk-BTU3TJDS.js +365 -0
  17. package/dist/chunk-CHF5OHIA.js +61 -0
  18. package/dist/chunk-CMBFNA7L.js +27 -0
  19. package/dist/chunk-CNZ35WI2.js +178 -0
  20. package/dist/chunk-DAHRH4ON.js +331 -0
  21. package/dist/chunk-EBGIRKQY.js +616 -0
  22. package/dist/chunk-EUZND3CB.js +27 -0
  23. package/dist/chunk-F3FA4F32.js +292 -0
  24. package/dist/chunk-JAKHTMQU.js +1000 -0
  25. package/dist/chunk-JCI5M6U6.js +956 -0
  26. package/dist/chunk-KQPDEVVS.js +398 -0
  27. package/dist/chunk-M4NLBH4I.js +725 -0
  28. package/dist/chunk-NEKUBFPT.js +60 -0
  29. package/dist/chunk-NYVAC6P5.js +37 -0
  30. package/dist/chunk-PTQJDMRT.js +146 -0
  31. package/dist/chunk-QWZG56ET.js +2744 -0
  32. package/dist/chunk-TSOKIX5Z.js +654 -0
  33. package/dist/chunk-UHNL42EF.js +2730 -0
  34. package/dist/chunk-VRW3FULF.js +725 -0
  35. package/dist/chunk-WZSPOOER.js +84 -0
  36. package/dist/chunk-YT6HQ6AM.js +14 -0
  37. package/dist/chunk-ZD6OAMTH.js +277 -0
  38. package/dist/chunk-ZWKZCBO6.js +317 -0
  39. package/dist/contracts-DDrwxvJ-.d.cts +245 -0
  40. package/dist/contracts-DDrwxvJ-.d.ts +245 -0
  41. package/dist/contracts-xo5ckdRP.d.cts +240 -0
  42. package/dist/contracts-xo5ckdRP.d.ts +240 -0
  43. package/dist/data.cjs +35 -2
  44. package/dist/data.d.cts +7 -0
  45. package/dist/data.d.ts +7 -0
  46. package/dist/data.js +9 -8
  47. package/dist/devtools.cjs +122 -0
  48. package/dist/devtools.d.cts +69 -461
  49. package/dist/devtools.d.ts +69 -461
  50. package/dist/devtools.js +127 -6
  51. package/dist/ecosystem.cjs +23 -6
  52. package/dist/ecosystem.d.cts +1 -1
  53. package/dist/ecosystem.d.ts +1 -1
  54. package/dist/ecosystem.js +10 -9
  55. package/dist/extras.cjs +1208 -88
  56. package/dist/extras.d.cts +6 -6
  57. package/dist/extras.d.ts +6 -6
  58. package/dist/extras.js +70 -33
  59. package/dist/index.cjs +663 -158
  60. package/dist/index.d.cts +398 -40
  61. package/dist/index.d.ts +398 -40
  62. package/dist/index.js +39 -21
  63. package/dist/introspect-BumjnBKr.d.cts +477 -0
  64. package/dist/introspect-CZrlcaYy.d.ts +477 -0
  65. package/dist/introspect-Cb0zgpi2.d.cts +477 -0
  66. package/dist/introspect-Y2xNXGSf.d.ts +477 -0
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +51 -24
  69. package/dist/patterns.d.cts +19 -57
  70. package/dist/patterns.d.ts +19 -57
  71. package/dist/patterns.js +8 -16
  72. package/dist/performance.js +4 -4
  73. package/dist/plugins.cjs +429 -82
  74. package/dist/plugins.d.cts +27 -4
  75. package/dist/plugins.d.ts +27 -4
  76. package/dist/plugins.js +156 -37
  77. package/dist/ssr-4PBXAOO3.js +40 -0
  78. package/dist/ssr-Do_SiVoL.d.cts +201 -0
  79. package/dist/ssr-Do_SiVoL.d.ts +201 -0
  80. package/dist/ssr.cjs +312 -60
  81. package/dist/ssr.d.cts +10 -1
  82. package/dist/ssr.d.ts +10 -1
  83. package/dist/ssr.js +13 -10
  84. package/dist/tagFactory-DaJ0YWX6.d.cts +47 -0
  85. package/dist/tagFactory-DaJ0YWX6.d.ts +47 -0
  86. package/dist/testing.cjs +233 -2
  87. package/dist/testing.d.cts +42 -1
  88. package/dist/testing.d.ts +42 -1
  89. package/dist/testing.js +129 -2
  90. package/dist/ui.cjs +374 -8
  91. package/dist/ui.d.cts +252 -2
  92. package/dist/ui.d.ts +252 -2
  93. package/dist/ui.js +329 -11
  94. package/dist/widgets.js +7 -7
  95. package/package.json +1 -1
@@ -195,4 +195,594 @@ declare function formatCurrency(value: number, currency: string, options?: Intl.
195
195
  locale?: string;
196
196
  }): string;
197
197
 
198
- export { battery, clipboard, colorScheme, draggable, dropZone, formatCurrency, formatNumber, geo, idle, media, online, permissions, resize, scroll, title };
198
+ /**
199
+ * visibility tracks the document's Page Visibility state.
200
+ * Returns a reactive boolean that is `true` while the tab is visible.
201
+ *
202
+ * Useful for pausing animations, polling, or video playback while the tab
203
+ * is hidden — a common optimization to save CPU and battery.
204
+ *
205
+ * @returns Object with reactive `visible` getter and `dispose` function
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const { visible, dispose } = visibility();
210
+ * effect(() => {
211
+ * if (visible()) resumePolling();
212
+ * else pausePolling();
213
+ * });
214
+ * ```
215
+ */
216
+ declare function visibility(): {
217
+ visible: () => boolean;
218
+ dispose: () => void;
219
+ };
220
+
221
+ type EffectiveType = "slow-2g" | "2g" | "3g" | "4g" | "unknown";
222
+ /**
223
+ * network tracks the browser's Network Information API.
224
+ * Returns reactive getters for effective connection type, downlink bandwidth,
225
+ * round-trip time, and save-data preference.
226
+ *
227
+ * Useful for adapting content (image quality, prefetching, polling intervals)
228
+ * to the user's actual connection — not just online/offline state.
229
+ *
230
+ * Falls back to sensible defaults on browsers without the API (notably Safari).
231
+ *
232
+ * @returns Reactive network info with dispose function
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * const { effectiveType, saveData } = network();
237
+ * const imageQuality = derived(() =>
238
+ * saveData() || effectiveType() === "2g" ? "low" : "high"
239
+ * );
240
+ * ```
241
+ */
242
+ declare function network(): {
243
+ effectiveType: () => EffectiveType;
244
+ downlink: () => number;
245
+ rtt: () => number;
246
+ saveData: () => boolean;
247
+ dispose: () => void;
248
+ };
249
+
250
+ interface MouseOptions {
251
+ /** Track touch events as well (unified pointer tracking). Default: true */
252
+ touch?: boolean;
253
+ /** Target element. Defaults to `window`. */
254
+ target?: HTMLElement;
255
+ }
256
+ /**
257
+ * mouse tracks the pointer position (mouse + optional touch) as reactive signals.
258
+ * Returns `x`, `y` getters updated on every `pointermove` / `touchmove`.
259
+ *
260
+ * @param options Optional tracking configuration
261
+ * @returns Reactive mouse coordinates with dispose function
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * const { x, y, dispose } = mouse();
266
+ * effect(() => console.log(`${x()}, ${y()}`));
267
+ * ```
268
+ */
269
+ declare function mouse(options?: MouseOptions): {
270
+ x: () => number;
271
+ y: () => number;
272
+ dispose: () => void;
273
+ };
274
+
275
+ type SwipeDirection = "left" | "right" | "up" | "down";
276
+ interface SwipeOptions {
277
+ /** Minimum distance in pixels for a swipe to count. Default: 50 */
278
+ threshold?: number;
279
+ /** Fired when a swipe is detected. */
280
+ onSwipe?: (direction: SwipeDirection, distance: number) => void;
281
+ }
282
+ /**
283
+ * swipe detects touch swipe gestures on a target element.
284
+ * Returns a reactive signal of the last-detected direction plus dispose.
285
+ *
286
+ * Works on touch devices (touchstart/touchend). Uses only native events —
287
+ * no external library.
288
+ *
289
+ * @param target Element to attach listeners to
290
+ * @param options Threshold and onSwipe callback
291
+ * @returns Reactive direction getter and dispose
292
+ *
293
+ * @example
294
+ * ```ts
295
+ * const el = div({ class: "card" });
296
+ * swipe(el, {
297
+ * threshold: 80,
298
+ * onSwipe: (dir) => {
299
+ * if (dir === "left") goNext();
300
+ * if (dir === "right") goPrev();
301
+ * },
302
+ * });
303
+ * ```
304
+ */
305
+ declare function swipe(target: HTMLElement, options?: SwipeOptions): {
306
+ direction: () => SwipeDirection | null;
307
+ dispose: () => void;
308
+ };
309
+
310
+ /**
311
+ * windowSize tracks the viewport dimensions as reactive signals.
312
+ * Unlike `resize()` (which observes a specific element), this watches the
313
+ * full window via the `resize` event.
314
+ *
315
+ * Useful for responsive layouts, breakpoint logic, and canvas sizing.
316
+ *
317
+ * @returns Reactive `width`, `height` getters and dispose
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * const { width, height } = windowSize();
322
+ * const isMobile = derived(() => width() < 768);
323
+ * ```
324
+ */
325
+ declare function windowSize(): {
326
+ width: () => number;
327
+ height: () => number;
328
+ dispose: () => void;
329
+ };
330
+
331
+ /**
332
+ * urlState returns reactive getters for the current URL's search params and
333
+ * hash, plus setters that call `history.pushState` / `replaceState`.
334
+ *
335
+ * Works independently of `createRouter()` — useful for apps that only need
336
+ * to sync a handful of UI state bits with the URL (filters, tabs, modals)
337
+ * without a full router setup.
338
+ *
339
+ * Listens to `popstate` so browser back/forward updates the signals.
340
+ *
341
+ * @example
342
+ * ```ts
343
+ * const url = urlState();
344
+ * const search = derived(() => url.params().get("q") ?? "");
345
+ * input({
346
+ * value: search,
347
+ * on: { input: (e) => {
348
+ * const p = new URLSearchParams(url.params());
349
+ * p.set("q", (e.target as HTMLInputElement).value);
350
+ * url.setParams(p, { replace: true });
351
+ * }},
352
+ * });
353
+ * ```
354
+ */
355
+ interface UrlStateOptions {
356
+ /** Use `replaceState` instead of `pushState`. Default: false */
357
+ replace?: boolean;
358
+ }
359
+ declare function urlState(): {
360
+ params: () => URLSearchParams;
361
+ hash: () => string;
362
+ setParams: (next: URLSearchParams | Record<string, string>, opts?: UrlStateOptions) => void;
363
+ setHash: (next: string, opts?: UrlStateOptions) => void;
364
+ dispose: () => void;
365
+ };
366
+
367
+ /**
368
+ * broadcast wraps the BroadcastChannel API as a reactive signal.
369
+ * Unlike the `storage` event (which only fires for localStorage writes and
370
+ * sends only the serialized value), a `BroadcastChannel` can send arbitrary
371
+ * structured-cloneable payloads between same-origin tabs, iframes, and
372
+ * workers — instantly, without touching storage.
373
+ *
374
+ * Returns the last received message as a reactive signal plus a `post()`
375
+ * sender and `dispose()` cleanup. The `post()` call does NOT echo back into
376
+ * the local `last()` signal — BroadcastChannel doesn't deliver to its own
377
+ * sender, matching the browser's native behavior.
378
+ *
379
+ * @param channelName Name of the broadcast channel
380
+ * @returns `{ last, post, dispose }`
381
+ *
382
+ * @example
383
+ * ```ts
384
+ * const chat = broadcast<{ user: string; text: string }>("chat");
385
+ * chat.post({ user: "alice", text: "hi" });
386
+ * effect(() => {
387
+ * const m = chat.last();
388
+ * if (m) renderIncoming(m);
389
+ * });
390
+ * ```
391
+ */
392
+ declare function broadcast<T = unknown>(channelName: string): {
393
+ last: () => T | null;
394
+ post: (message: T) => void;
395
+ dispose: () => void;
396
+ };
397
+
398
+ /**
399
+ * fullscreen wraps the Fullscreen API as a reactive signal plus `enter`/
400
+ * `exit`/`toggle` actions. Tracks `document.fullscreenElement` via the
401
+ * `fullscreenchange` event so the signal stays in sync when the user presses
402
+ * Escape or the browser forces an exit.
403
+ *
404
+ * @returns `{ isFullscreen, element, enter, exit, toggle, dispose }`
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * const fs = fullscreen();
409
+ * button({
410
+ * nodes: () => (fs.isFullscreen() ? "Exit fullscreen" : "Enter fullscreen"),
411
+ * on: { click: () => fs.toggle(videoEl) },
412
+ * });
413
+ * ```
414
+ */
415
+ declare function fullscreen(): {
416
+ isFullscreen: () => boolean;
417
+ element: () => Element | null;
418
+ enter: (el: Element) => Promise<void>;
419
+ exit: () => Promise<void>;
420
+ toggle: (el: Element) => Promise<void>;
421
+ dispose: () => void;
422
+ };
423
+
424
+ /**
425
+ * wakeLock wraps the Screen Wake Lock API to keep the screen awake while the
426
+ * app is doing something the user is watching (video, timer, recipe, nav).
427
+ *
428
+ * Returns a reactive `active` boolean plus `request` / `release` actions.
429
+ * The lock is automatically re-requested if the page becomes visible again
430
+ * after being hidden (browsers auto-release wake locks on tab hide).
431
+ *
432
+ * Gracefully degrades on browsers without the API.
433
+ *
434
+ * @example
435
+ * ```ts
436
+ * const lock = wakeLock();
437
+ * await lock.request();
438
+ * // ... later
439
+ * await lock.release();
440
+ * ```
441
+ */
442
+ declare function wakeLock(): {
443
+ active: () => boolean;
444
+ request: () => Promise<void>;
445
+ release: () => Promise<void>;
446
+ dispose: () => void;
447
+ };
448
+
449
+ interface AnimationFrameOptions {
450
+ /** Maximum FPS. Frames that would exceed this are skipped. Default: unlimited. */
451
+ fpsLimit?: number;
452
+ /** Start immediately. Default: true. */
453
+ immediate?: boolean;
454
+ }
455
+ /**
456
+ * animationFrame emits a reactive `delta` (ms since previous frame) and
457
+ * `elapsed` (ms since start) tracked via `requestAnimationFrame`. Useful for
458
+ * declarative animations, game loops, or real-time visual updates — without
459
+ * forcing callers to manage the rAF id manually.
460
+ *
461
+ * The loop is paused automatically when `pause()` is called and resumed with
462
+ * `resume()`. `dispose()` cancels the loop permanently.
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * const frame = animationFrame();
467
+ * effect(() => {
468
+ * const dt = frame.delta();
469
+ * setAngle((a) => (a + dt * 0.1) % 360);
470
+ * });
471
+ * ```
472
+ */
473
+ declare function animationFrame(options?: AnimationFrameOptions): {
474
+ delta: () => number;
475
+ elapsed: () => number;
476
+ running: () => boolean;
477
+ pause: () => void;
478
+ resume: () => void;
479
+ dispose: () => void;
480
+ };
481
+
482
+ interface MutationObserverOptions extends MutationObserverInit {
483
+ }
484
+ /**
485
+ * mutationObserver wraps the DOM MutationObserver as a reactive signal of
486
+ * the latest batch of mutations. Typical uses: reacting to externally
487
+ * injected content (third-party embeds), watching attribute flips the app
488
+ * doesn't directly control, or migrating legacy non-reactive code.
489
+ *
490
+ * For anything inside a single component prefer reactive DOM bindings —
491
+ * this is an escape hatch for cross-boundary observation.
492
+ *
493
+ * @param target Element to observe
494
+ * @param options Standard MutationObserverInit (childList, subtree, …)
495
+ *
496
+ * @example
497
+ * ```ts
498
+ * const obs = mutationObserver(document.body, { childList: true, subtree: true });
499
+ * effect(() => {
500
+ * const records = obs.records();
501
+ * if (records.length) handleExternalChanges(records);
502
+ * });
503
+ * ```
504
+ */
505
+ declare function mutationObserver(target: Node, options?: MutationObserverOptions): {
506
+ records: () => MutationRecord[];
507
+ dispose: () => void;
508
+ };
509
+
510
+ interface BoundsRect {
511
+ x: number;
512
+ y: number;
513
+ width: number;
514
+ height: number;
515
+ top: number;
516
+ left: number;
517
+ right: number;
518
+ bottom: number;
519
+ }
520
+ /**
521
+ * bounds tracks an element's `getBoundingClientRect()` as a reactive signal.
522
+ * Updates when the element resizes OR when the window scrolls (so absolute
523
+ * top/left stay accurate for overlays, tooltips, and popovers).
524
+ *
525
+ * Implementation detail: uses a `ResizeObserver` for size changes and a
526
+ * passive window `scroll` listener for position changes. Both are torn down
527
+ * on `dispose()`.
528
+ *
529
+ * @example
530
+ * ```ts
531
+ * const el = div({ class: "anchor" });
532
+ * const rect = bounds(el);
533
+ * effect(() => {
534
+ * const r = rect.rect();
535
+ * positionTooltip(r.left, r.bottom);
536
+ * });
537
+ * ```
538
+ */
539
+ declare function bounds(target: Element): {
540
+ rect: () => BoundsRect;
541
+ refresh: () => void;
542
+ dispose: () => void;
543
+ };
544
+
545
+ interface KeyboardOptions {
546
+ /** Target element. Defaults to `window`. */
547
+ target?: HTMLElement;
548
+ /** Filter: only track these keys (matches `KeyboardEvent.key`). */
549
+ keys?: string[];
550
+ }
551
+ /**
552
+ * keyboard tracks the set of keys currently held down as a reactive signal.
553
+ * Useful for combo detection, editor-like keybindings, games, and modifier
554
+ * gating ("Shift+click").
555
+ *
556
+ * The returned `pressed` is a reactive `Set<string>` (keyed by
557
+ * `KeyboardEvent.key`). On every key event the signal is replaced with a
558
+ * new Set instance so reactive subscribers see the change.
559
+ *
560
+ * Also listens to `window.blur` to clear stuck keys if the window loses
561
+ * focus mid-press — otherwise held modifiers can "ghost" forever.
562
+ *
563
+ * @example
564
+ * ```ts
565
+ * const kb = keyboard();
566
+ * const isShift = derived(() => kb.pressed().has("Shift"));
567
+ * ```
568
+ */
569
+ declare function keyboard(options?: KeyboardOptions): {
570
+ pressed: () => Set<string>;
571
+ isPressed: (key: string) => boolean;
572
+ dispose: () => void;
573
+ };
574
+
575
+ interface SpeakOptions {
576
+ /** BCP-47 language tag. Defaults to the utterance's default. */
577
+ lang?: string;
578
+ /** Playback speed (0.1–10). Default: 1. */
579
+ rate?: number;
580
+ /** Pitch (0–2). Default: 1. */
581
+ pitch?: number;
582
+ /** Volume (0–1). Default: 1. */
583
+ volume?: number;
584
+ /** Voice name (match against `getVoices()[i].name`). */
585
+ voice?: string;
586
+ }
587
+ /**
588
+ * speech wraps the Web Speech Synthesis API as a reactive controller.
589
+ * Exposes `speaking` / `paused` reactive booleans plus `speak()`, `pause()`,
590
+ * `resume()`, `cancel()` actions.
591
+ *
592
+ * Automatically gracefully degrades on runtimes without `speechSynthesis`.
593
+ *
594
+ * @example
595
+ * ```ts
596
+ * const tts = speech();
597
+ * button({
598
+ * nodes: "Read it to me",
599
+ * on: { click: () => tts.speak("Hello, world!", { rate: 1.1 }) },
600
+ * });
601
+ * ```
602
+ */
603
+ declare function speech(): {
604
+ speaking: () => boolean;
605
+ paused: () => boolean;
606
+ speak: (text: string, options?: SpeakOptions) => void;
607
+ pause: () => void;
608
+ resume: () => void;
609
+ cancel: () => void;
610
+ dispose: () => void;
611
+ };
612
+
613
+ interface GamepadSnapshot {
614
+ index: number;
615
+ id: string;
616
+ connected: boolean;
617
+ buttons: readonly {
618
+ pressed: boolean;
619
+ value: number;
620
+ }[];
621
+ axes: readonly number[];
622
+ }
623
+ /**
624
+ * gamepad exposes the Gamepad API as reactive snapshots. Unlike the
625
+ * native API (which requires polling each frame), this wrapper polls for
626
+ * you via `requestAnimationFrame` and emits a signal update whenever ANY
627
+ * button or axis changes.
628
+ *
629
+ * Returns `pads()` — a reactive array of currently-connected gamepads — and
630
+ * `dispose()` to stop polling. Listens to `gamepadconnected` and
631
+ * `gamepaddisconnected` to auto-start/stop the poll loop.
632
+ *
633
+ * @example
634
+ * ```ts
635
+ * const gp = gamepad();
636
+ * effect(() => {
637
+ * const pad = gp.pads()[0];
638
+ * if (pad?.buttons[0]?.pressed) jump();
639
+ * setAngle((pad?.axes[0] ?? 0) * 90);
640
+ * });
641
+ * ```
642
+ */
643
+ declare function gamepad(): {
644
+ pads: () => GamepadSnapshot[];
645
+ dispose: () => void;
646
+ };
647
+
648
+ /**
649
+ * pointerLock wraps the Pointer Lock API as a reactive controller.
650
+ * Exposes a `locked` signal plus `request(el)` / `exit()` actions.
651
+ *
652
+ * Pointer lock hides the cursor and delivers unbounded relative-motion
653
+ * mouse events — essential for FPS games, 3D viewers, sketching apps.
654
+ *
655
+ * @example
656
+ * ```ts
657
+ * const pl = pointerLock();
658
+ * canvas.addEventListener("click", () => pl.request(canvas));
659
+ * window.addEventListener("mousemove", (e) => {
660
+ * if (pl.locked()) turnCamera(e.movementX, e.movementY);
661
+ * });
662
+ * ```
663
+ */
664
+ declare function pointerLock(): {
665
+ locked: () => boolean;
666
+ request: (element: Element) => void;
667
+ exit: () => void;
668
+ dispose: () => void;
669
+ };
670
+
671
+ /**
672
+ * vibrate triggers the Vibration API. Accepts a single duration in ms or
673
+ * a pattern array alternating vibration and pause durations. Returns `true`
674
+ * if the call was dispatched, `false` if the API is unsupported.
675
+ *
676
+ * Wrapped for consistency with the rest of `sibujs/browser` and to avoid
677
+ * runtime errors on non-mobile browsers.
678
+ *
679
+ * @example
680
+ * ```ts
681
+ * vibrate(50); // single 50ms tap
682
+ * vibrate([100, 30, 100]); // pulse-pause-pulse
683
+ * vibrate(0); // cancel any active vibration
684
+ * ```
685
+ */
686
+ declare function vibrate(pattern: number | number[]): boolean;
687
+
688
+ /**
689
+ * favicon sets or updates the page favicon at runtime.
690
+ *
691
+ * Passes a `url` (to set `href`) or accepts an inline SVG string via
692
+ * `data:image/svg+xml` encoding. Useful for notification badges, theme
693
+ * switching, dynamic status indicators.
694
+ *
695
+ * Ensures a `<link rel="icon">` exists — creates one if missing, updates
696
+ * the `href` otherwise.
697
+ *
698
+ * @param url Favicon URL or `data:` URI
699
+ *
700
+ * @example
701
+ * ```ts
702
+ * favicon("/icons/default.png");
703
+ * // Unread count badge on the favicon
704
+ * effect(() => {
705
+ * const n = unreadCount();
706
+ * favicon(n > 0 ? "/icons/badge.png" : "/icons/default.png");
707
+ * });
708
+ * ```
709
+ */
710
+ declare function favicon(url: string): void;
711
+ /**
712
+ * Encode an SVG string into a `data:image/svg+xml` URI suitable for use
713
+ * with `favicon()`. Handles URL encoding of special characters so inline
714
+ * SVG content can be embedded safely.
715
+ *
716
+ * @example
717
+ * ```ts
718
+ * favicon(svgFavicon(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><circle cx="5" cy="5" r="5" fill="red"/></svg>`));
719
+ * ```
720
+ */
721
+ declare function svgFavicon(svg: string): string;
722
+
723
+ interface TextSelectionState {
724
+ /** Selected text, or empty string. */
725
+ text: () => string;
726
+ /** DOMRect of the selection (for positioning floating action bars) or null. */
727
+ rect: () => DOMRect | null;
728
+ /** True when there is a non-empty selection. */
729
+ hasSelection: () => boolean;
730
+ /** Programmatically clear the current selection. */
731
+ clear: () => void;
732
+ dispose: () => void;
733
+ }
734
+ /**
735
+ * textSelection tracks the user's current text selection as reactive state.
736
+ * Great for "selection toolbars" (bold/italic popovers), citation tools,
737
+ * and any UI that needs to show contextual actions when the user highlights
738
+ * text on the page.
739
+ *
740
+ * Listens to `selectionchange` on the document, which fires for mouse drag,
741
+ * keyboard selection (Shift+arrow), and touch selection all the same.
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * const sel = textSelection();
746
+ * effect(() => {
747
+ * const rect = sel.rect();
748
+ * if (rect) positionToolbar(rect);
749
+ * else hideToolbar();
750
+ * });
751
+ * ```
752
+ */
753
+ declare function textSelection(): TextSelectionState;
754
+
755
+ interface ImageLoaderState {
756
+ /** Reactive loading state: "pending" | "loaded" | "error". */
757
+ status: () => "pending" | "loaded" | "error";
758
+ /** The loaded HTMLImageElement (null until `status === "loaded"`). */
759
+ image: () => HTMLImageElement | null;
760
+ /** Intrinsic width, 0 until loaded. */
761
+ width: () => number;
762
+ /** Intrinsic height, 0 until loaded. */
763
+ height: () => number;
764
+ /** Abort in-flight load and reset state. */
765
+ dispose: () => void;
766
+ }
767
+ /**
768
+ * imageLoader reactively loads an image via a hidden `Image()` instance.
769
+ * Exposes `status`, `image`, `width`, `height` as reactive signals — useful
770
+ * for responsive layouts that need the intrinsic dimensions before render,
771
+ * lazy-loaded galleries, and preloading checks.
772
+ *
773
+ * Accepts a reactive `src` getter OR a plain string. When a getter is given
774
+ * and its value changes, the previous load is abandoned and a new one
775
+ * starts.
776
+ *
777
+ * @example
778
+ * ```ts
779
+ * const img = imageLoader("/hero.jpg");
780
+ * // Size the container so there's no layout jump
781
+ * div({ style: () => ({
782
+ * aspectRatio: `${img.width()} / ${img.height() || 1}`,
783
+ * })});
784
+ * ```
785
+ */
786
+ declare function imageLoader(src: string | (() => string)): ImageLoaderState;
787
+
788
+ export { type AnimationFrameOptions, type BoundsRect, type GamepadSnapshot, type ImageLoaderState, type KeyboardOptions, type MouseOptions, type MutationObserverOptions, type SpeakOptions, type SwipeDirection, type SwipeOptions, type TextSelectionState, type UrlStateOptions, animationFrame, battery, bounds, broadcast, clipboard, colorScheme, draggable, dropZone, favicon, formatCurrency, formatNumber, fullscreen, gamepad, geo, idle, imageLoader, keyboard, media, mouse, mutationObserver, network, online, permissions, pointerLock, resize, scroll, speech, svgFavicon, swipe, textSelection, title, urlState, vibrate, visibility, wakeLock, windowSize };