@enigmax/primitives 0.17.0 → 0.19.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 (97) hide show
  1. package/dist/button-CaXaqG_K.d.ts +63 -0
  2. package/dist/chunk-2QFTRNAZ.js +88 -0
  3. package/dist/chunk-6BGBYUSZ.js +114 -0
  4. package/dist/chunk-AU3H5WIY.js +107 -0
  5. package/dist/{chunk-UZFEEFMF.js → chunk-D5A2ZMAG.js} +34 -1
  6. package/dist/chunk-DTWZDONY.js +99 -0
  7. package/dist/chunk-F25CGNQC.js +19 -0
  8. package/dist/chunk-FWVWX67R.js +462 -0
  9. package/dist/chunk-HC2ME5PU.js +168 -0
  10. package/dist/chunk-HS3X3XCW.js +43 -0
  11. package/dist/chunk-IXVMRVD4.js +96 -0
  12. package/dist/chunk-KJINGUQN.js +188 -0
  13. package/dist/chunk-MMQPZGSU.js +161 -0
  14. package/dist/chunk-OCMI7R6H.js +79 -0
  15. package/dist/chunk-QQFNAKMY.js +47 -0
  16. package/dist/chunk-QYMUIW5I.js +28 -0
  17. package/dist/chunk-R4ZAEE7V.js +249 -0
  18. package/dist/chunk-S653GLSF.js +17 -0
  19. package/dist/chunk-SNYUBXWQ.js +149 -0
  20. package/dist/chunk-U3V4EHOB.js +41 -0
  21. package/dist/chunk-UOSSNUSC.js +309 -0
  22. package/dist/chunk-XNNQRA35.js +31 -0
  23. package/dist/chunk-XQHCZAPJ.js +102 -0
  24. package/dist/chunk-ZCUFYBPB.js +154 -0
  25. package/dist/chunk-ZWR2EXHQ.js +55 -0
  26. package/dist/flags-BBJc9unY.d.ts +133 -0
  27. package/dist/index-dTdAbOWl.d.ts +144 -0
  28. package/dist/index.d.ts +18 -603
  29. package/dist/index.js +11 -2
  30. package/dist/input-BwXjFenq.d.ts +77 -0
  31. package/dist/marquee-CJ3Uwy3E.d.ts +81 -0
  32. package/dist/network-D2LsBG_k.d.ts +39 -0
  33. package/dist/next/index.d.ts +22 -4
  34. package/dist/next/index.js +25 -4
  35. package/dist/notifications-BpVV6sel.d.ts +70 -0
  36. package/dist/palette-D7iuQh_T.d.ts +86 -0
  37. package/dist/password-3DRQYAYQ.js +2 -0
  38. package/dist/password-C8lG4Zm9.d.ts +71 -0
  39. package/dist/password-FB2CUEKJ.js +1 -0
  40. package/dist/react/button.d.ts +75 -0
  41. package/dist/react/button.js +4 -0
  42. package/dist/react/flag.d.ts +37 -0
  43. package/dist/react/flag.js +3 -0
  44. package/dist/react/index.d.ts +29 -312
  45. package/dist/react/index.js +24 -3
  46. package/dist/react/input.d.ts +4 -0
  47. package/dist/react/input.js +3 -0
  48. package/dist/react/marquee.d.ts +44 -0
  49. package/dist/react/marquee.js +3 -0
  50. package/dist/react/network.d.ts +20 -0
  51. package/dist/react/network.js +3 -0
  52. package/dist/react/notifications.d.ts +17 -0
  53. package/dist/react/notifications.js +3 -0
  54. package/dist/react/palette.d.ts +231 -0
  55. package/dist/react/palette.js +5 -0
  56. package/dist/react/relative-time.d.ts +21 -0
  57. package/dist/react/relative-time.js +3 -0
  58. package/dist/react/search.d.ts +30 -0
  59. package/dist/react/search.js +3 -0
  60. package/dist/react/slot.d.ts +47 -0
  61. package/dist/react/slot.js +2 -0
  62. package/dist/react/toast.d.ts +40 -0
  63. package/dist/react/toast.js +4 -0
  64. package/dist/react-router/index.d.ts +22 -4
  65. package/dist/react-router/index.js +25 -4
  66. package/dist/relative-time-YpRTG7YH.d.ts +106 -0
  67. package/dist/search/index.d.ts +2 -2
  68. package/dist/search/index.js +1 -1
  69. package/dist/{search-CsO3L1Lw.d.ts → search-DXxY8SEH.d.ts} +15 -1
  70. package/dist/search-UQEXAPQB.js +50 -0
  71. package/package.json +54 -3
  72. package/recipes/input/styles.css +60 -20
  73. package/recipes/palette/styles.css +243 -0
  74. package/registry.json +186 -18
  75. package/src/core/flags.ts +88 -42
  76. package/src/core/input-icons.ts +34 -0
  77. package/src/core/input.ts +5 -21
  78. package/src/core/palette.ts +0 -0
  79. package/src/core/search.ts +60 -0
  80. package/src/index.ts +19 -3
  81. package/src/react/button.tsx +70 -4
  82. package/src/react/flag.tsx +18 -6
  83. package/src/react/index.ts +50 -5
  84. package/src/react/input/icon.tsx +17 -0
  85. package/src/react/input/index.tsx +307 -0
  86. package/src/react/input/password.tsx +174 -0
  87. package/src/react/input/search.tsx +82 -0
  88. package/src/react/input/types.ts +146 -0
  89. package/src/react/input/write-value.ts +18 -0
  90. package/src/react/palette/context.ts +60 -0
  91. package/src/react/palette/index.tsx +66 -0
  92. package/src/react/palette/root.tsx +661 -0
  93. package/src/react/slot.tsx +91 -0
  94. package/src/react/use-button.ts +3 -1
  95. package/dist/chunk-4GLYRF4B.js +0 -1259
  96. package/dist/chunk-JFNND6P4.js +0 -749
  97. package/src/react/input.tsx +0 -429
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Input affordances: the buttons that live inside a field, with no styling of their own.
3
+ *
4
+ * A password field gets its reveal toggle automatically, because a field the visitor
5
+ * cannot read back is the single most common cause of a failed sign-in. Everything about
6
+ * it is replaceable - the icon, the label, the position, the container, whether it exists
7
+ * at all - and the same mechanism takes any other action you want in there.
8
+ *
9
+ * The details below are the ones the hand-rolled version gets wrong; each is commented
10
+ * with the failure it prevents.
11
+ */
12
+ /** What an action renders. A string is parsed as HTML, so an inline SVG works. */
13
+ type InputIcon = string | Node;
14
+ interface InputActionState {
15
+ /** The field the action belongs to. */
16
+ input: HTMLInputElement;
17
+ /** True while the password is readable. Always false for a non-password field. */
18
+ revealed: boolean;
19
+ /** The field cannot be acted on: disabled or read-only. */
20
+ locked: boolean;
21
+ }
22
+ interface InputAction {
23
+ /** Stable id. Used for `data-action` and to replace a built-in (e.g. "reveal"). */
24
+ name: string;
25
+ /** Accessible name. Becomes both `aria-label` and `title`. */
26
+ label: string | ((state: InputActionState) => string);
27
+ icon: InputIcon | ((state: InputActionState) => InputIcon);
28
+ /** Runs on click and on Enter/Space, because it is a real button. */
29
+ onSelect: (state: InputActionState) => void;
30
+ /** Renders `aria-pressed`. Omit for actions that are not a toggle. */
31
+ pressed?: (state: InputActionState) => boolean;
32
+ /** Hide the action without removing it, e.g. a clear button on an empty field. */
33
+ visible?: (state: InputActionState) => boolean;
34
+ }
35
+ interface InputOptions {
36
+ /**
37
+ * The password reveal. `false` removes it. An object overrides parts of it - the
38
+ * icons, the labels - without giving up the caret and focus handling.
39
+ */
40
+ reveal?: boolean | {
41
+ /** Shown while the password is hidden; selecting it reveals. */
42
+ showIcon?: InputIcon;
43
+ /** Shown while the password is readable. */
44
+ hideIcon?: InputIcon;
45
+ showLabel?: string;
46
+ hideLabel?: string;
47
+ };
48
+ /** Extra actions, or a replacement for a built-in when `name` matches. */
49
+ actions?: InputAction[];
50
+ /** Which side the actions mount on. Position them yourself with CSS. */
51
+ position?: "start" | "end";
52
+ /**
53
+ * Mount the actions here instead of in a container created next to the input.
54
+ * Use it when your markup already has a slot for them.
55
+ */
56
+ container?: HTMLElement;
57
+ /** Called whenever the password's visibility changes. */
58
+ onRevealChange?: (revealed: boolean) => void;
59
+ }
60
+ interface InputInstance {
61
+ readonly revealed: boolean;
62
+ /** Show or hide the password. Toggles when the argument is omitted. */
63
+ reveal(next?: boolean): void;
64
+ /** Re-read the field and re-render the actions. Call after changing it yourself. */
65
+ refresh(): void;
66
+ update(options: Partial<InputOptions>): void;
67
+ destroy(): void;
68
+ }
69
+
70
+ /**
71
+ * Wire a field's in-field actions.
72
+ *
73
+ * @param input The field itself. A `type="password"` field gets the reveal for free.
74
+ */
75
+ declare function createInput(input: HTMLInputElement, options?: InputOptions): InputInstance;
76
+
77
+ export { type InputAction as I, type InputActionState as a, type InputIcon as b, type InputInstance as c, type InputOptions as d, createInput as e };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Draggable infinite marquee: the behaviour engine, with no styling of its own.
3
+ *
4
+ * The contract is a SPEED in pixels per second, never a duration. A lap of a
5
+ * looping row is as long as its content, so a duration makes the row run faster
6
+ * every time an item is added to it. See the package README.
7
+ *
8
+ * Every rule encoded here was measured in a browser, not reasoned about. The
9
+ * comments marked "non-negotiable" name the bug that the obvious implementation
10
+ * shipped with.
11
+ */
12
+ /**
13
+ * What a MOUSE resting on the row does to its speed. One option covers every case, so
14
+ * there is no pile of booleans to reconcile:
15
+ *
16
+ * - `"off"` ignore hover entirely (the default)
17
+ * - `"pause"` stop while the pointer is on it
18
+ * - a number multiply the cruise speed: `0.15` crawls, `1` changes nothing, `2` doubles
19
+ * - `{ speed }` an ABSOLUTE speed in px/s, whatever the cruise is; direction still
20
+ * follows `reverse`, so you give a magnitude and not a sign
21
+ *
22
+ * Touch never triggers any of it - see the pointerType note on the hover handler.
23
+ */
24
+ type MarqueeHover = number | "off" | "pause" | {
25
+ speed: number;
26
+ };
27
+ interface MarqueeOptions {
28
+ /**
29
+ * Pixels per second. NOT a duration: a duration ties the speed to the item
30
+ * count and the row silently accelerates as content grows.
31
+ */
32
+ speed?: number;
33
+ /** Scroll towards the start instead of the end. */
34
+ reverse?: boolean;
35
+ /** Scroll on the Y axis instead of the X axis. */
36
+ vertical?: boolean;
37
+ /** Allow grabbing and throwing the row. */
38
+ draggable?: boolean;
39
+ /** What a mouse resting on the row does to its speed. See {@link MarqueeHover}. */
40
+ hover?: MarqueeHover;
41
+ /** Fraction of the remaining velocity gap left after one second. */
42
+ decay?: number;
43
+ /**
44
+ * Apply the styles the behaviour needs (overflow, touch-action, user-select,
45
+ * will-change, transform). Never theme styles. Turn off to own them yourself.
46
+ */
47
+ manageStyles?: boolean;
48
+ /**
49
+ * How the repeated copies get into the DOM.
50
+ * - "clone": the engine clones the first child of the track (vanilla, Astro).
51
+ * - "external": the consumer renders them and is told the count (React, Vue).
52
+ */
53
+ copies?: "clone" | "external";
54
+ /** Called when the required copy count changes. Required for "external". */
55
+ onCopyCountChange?: (count: number) => void;
56
+ /** Called on a click that was not the end of a drag. */
57
+ onItemClick?: (event: MouseEvent) => void;
58
+ /** Called whenever the measured lap period changes. */
59
+ onMeasure?: (period: number, copyCount: number) => void;
60
+ }
61
+ interface MarqueeInstance {
62
+ /** Current offset in px, normalized to [0, period). */
63
+ readonly offset: number;
64
+ /** Measured distance between two copies of the content, in px. */
65
+ readonly period: number;
66
+ /** Copies currently required to cover the lane plus one whole period. */
67
+ readonly copyCount: number;
68
+ /** True between pointerdown and pointerup while the row is being grabbed. */
69
+ readonly dragging: boolean;
70
+ /** True while the OS asks for reduced motion. Autoplay stops, drag does not. */
71
+ readonly reducedMotion: boolean;
72
+ update(options: Partial<MarqueeOptions>): void;
73
+ /** Re-read the lap from the DOM. Called for you on resize and on font load. */
74
+ measure(): void;
75
+ pause(): void;
76
+ resume(): void;
77
+ destroy(): void;
78
+ }
79
+ declare function createMarquee(lane: HTMLElement, track: HTMLElement, options?: MarqueeOptions): MarqueeInstance;
80
+
81
+ export { type MarqueeHover as M, type MarqueeInstance as a, type MarqueeOptions as b, createMarquee as c };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Connection state: online/offline plus whatever the Network Information API will say.
3
+ *
4
+ * The part worth not rewriting is the TRANSITION. "You are back online" needs to know the
5
+ * connection had dropped, and every implementation of that ends up as a stray `wasOffline`
6
+ * boolean next to the effect, re-derived per screen and wrong the first time the page
7
+ * loads offline. It is tracked here instead.
8
+ */
9
+ interface NetworkState {
10
+ online: boolean;
11
+ /** Estimated downlink in Mbps. Null where the browser does not report it. */
12
+ downlink: number | null;
13
+ downlinkMax: number | null;
14
+ /** "slow-2g" | "2g" | "3g" | "4g", or null. */
15
+ effectiveType: string | null;
16
+ /** Estimated round-trip time in ms. */
17
+ rtt: number | null;
18
+ /** The visitor asked for reduced data use. */
19
+ saveData: boolean | null;
20
+ /** "wifi" | "cellular" | "ethernet" | ..., or null. */
21
+ type: string | null;
22
+ /**
23
+ * The connection dropped earlier and is back. False on a first load, so a page that
24
+ * opens online never announces a recovery that did not happen.
25
+ */
26
+ recovered: boolean;
27
+ /** On a connection worth degrading for: 2g or slower, or data saver on. */
28
+ slow: boolean;
29
+ }
30
+ interface NetworkMonitor {
31
+ readonly state: NetworkState;
32
+ subscribe(listener: (state: NetworkState) => void): () => void;
33
+ destroy(): void;
34
+ }
35
+ /** What a server render sees. Assuming offline would flash a warning on every page. */
36
+ declare const SERVER_NETWORK_STATE: NetworkState;
37
+ declare function createNetworkMonitor(): NetworkMonitor;
38
+
39
+ export { type NetworkMonitor as N, SERVER_NETWORK_STATE as S, type NetworkState as a, createNetworkMonitor as c };
@@ -1,8 +1,26 @@
1
1
  import * as react from 'react';
2
- import { ButtonProps } from '../react/index.js';
3
- export { BreachChecker, BreachState, BreachStatus, FieldAction, Flag, FlagProps, Input, InputProps, PasswordStrength, PasswordStrengthProps, RelativeTime, RelativeTimeProps, ToastControls, ToastPosition, Toaster, ToasterProps, UseButtonResult, UseInputResult, UseMarqueeResult, UseNotificationsResult, UseSearchResult, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../react/index.js';
4
- export { ButtonOptions, ButtonState, EstimateOptions, FlagFormat, FlagOptions, FlagShape, FlagSource, GeneratePasswordOptions, InputAction, InputIcon, InputOptions, MarqueeHover, MarqueeInstance, MarqueeOptions, NetworkState, PasswordScore, PasswordStrengthReport, configureFlags, estimatePasswordStrength, flagAttributes, flagSrc, flagView, generatePassword, normalizeFlagCode } from '../index.js';
5
- export { F as FuseConstructor, b as SearchMatch, c as SearchOptions } from '../search-CsO3L1Lw.js';
2
+ import { ButtonProps } from '../react/button.js';
3
+ export { UseMarqueeResult, useMarquee } from '../react/marquee.js';
4
+ export { M as MarqueeHover, a as MarqueeInstance, b as MarqueeOptions } from '../marquee-CJ3Uwy3E.js';
5
+ export { PasswordStrength, PasswordStrengthProps, UseButtonResult, UseInputResult, getLinkComponent, setLinkComponent, useButton, useInput } from '../react/index.js';
6
+ export { UseSearchResult, useSearch } from '../react/search.js';
7
+ export { I as InputAction, b as InputIcon, d as InputOptions } from '../input-BwXjFenq.js';
8
+ export { F as FuseConstructor, a as FuseLike, S as SearchInstance, b as SearchMatch, c as SearchOptions } from '../search-DXxY8SEH.js';
9
+ export { c as ButtonOptions, d as ButtonState } from '../button-CaXaqG_K.js';
10
+ export { B as BreachChecker, a as BreachState, b as BreachStatus, F as FieldAction, I as Input, c as InputBaseProps, d as InputProps, e as InputType, P as PasswordOnlyProps, g as PlainOnlyProps, S as SearchOnlyProps } from '../index-dTdAbOWl.js';
11
+ export { Slot, SlotProps, mergeSlotProps } from '../react/slot.js';
12
+ export { E as EstimateOptions, G as GeneratePasswordOptions, a as PasswordScore, b as PasswordStrengthReport, e as estimatePasswordStrength, g as generatePassword } from '../password-C8lG4Zm9.js';
13
+ export { UseNotificationsResult, createNotificationQueue, defaultQueue, useNotifications } from '../react/notifications.js';
14
+ export { ToastControls, ToastPosition, Toaster, ToasterProps } from '../react/toast.js';
15
+ export { useNetworkState } from '../react/network.js';
16
+ export { a as NetworkState } from '../network-D2LsBG_k.js';
17
+ export { RelativeTime, RelativeTimeProps } from '../react/relative-time.js';
18
+ export { R as RelativeTimeFormat, a as RelativeTimeOptions, b as RelativeTimePrecision, c as RelativeTimeStyle, d as RelativeTimeTense } from '../relative-time-YpRTG7YH.js';
19
+ export { N as Notification, a as NotificationAction, b as NotificationInput, c as NotificationTone, e as NotificationsOptions } from '../notifications-BpVV6sel.js';
20
+ export { Flag, FlagProps } from '../react/flag.js';
21
+ export { c as FlagFormat, d as FlagOptions, e as FlagShape, f as FlagSource, h as configureFlags, i as flagAttributes, k as flagName, l as flagSrc, m as flagView, n as normalizeFlagCode } from '../flags-BBJc9unY.js';
22
+ export { PaletteContent, PaletteContextValue, PaletteField, PaletteFooter, PaletteItem, PaletteList, PaletteListProps, PaletteRoot, PaletteRootProps, PaletteRow, PaletteSection, PaletteTrigger, SearchPalette, SearchPaletteProps, usePaletteContext } from '../react/palette.js';
23
+ export { R as RecentEntry, b as RecentStore, e as createRecentStore, g as groupRows, i as isPaletteShortcut, m as moveActive, r as recentKey, s as shortcutLabel } from '../palette-D7iuQh_T.js';
6
24
 
7
25
  /**
8
26
  * The Next.js entry. Same components, wired to `next/link`.
@@ -1,8 +1,29 @@
1
1
  "use client";
2
- import { Button } from '../chunk-JFNND6P4.js';
3
- export { Flag, Input, PasswordStrength, RelativeTime, Toaster, createNotificationQueue, defaultQueue, getLinkComponent, setLinkComponent, useButton, useInput, useMarquee, useNetworkState, useNotifications, useSearch } from '../chunk-JFNND6P4.js';
4
- export { configureFlags, estimatePasswordStrength, flagAttributes, flagSrc, flagView, generatePassword, normalizeFlagCode } from '../chunk-4GLYRF4B.js';
5
- import '../chunk-UZFEEFMF.js';
2
+ export { useInput } from '../chunk-U3V4EHOB.js';
3
+ export { PasswordStrength } from '../chunk-XQHCZAPJ.js';
4
+ export { Flag } from '../chunk-XNNQRA35.js';
5
+ export { Toaster } from '../chunk-ZCUFYBPB.js';
6
+ export { useMarquee } from '../chunk-ZWR2EXHQ.js';
7
+ export { useSearch } from '../chunk-QQFNAKMY.js';
8
+ export { useNetworkState } from '../chunk-S653GLSF.js';
9
+ export { createNotificationQueue, defaultQueue, useNotifications } from '../chunk-QYMUIW5I.js';
10
+ export { RelativeTime } from '../chunk-OCMI7R6H.js';
11
+ import '../chunk-KJINGUQN.js';
12
+ export { estimatePasswordStrength, generatePassword } from '../chunk-HC2ME5PU.js';
13
+ export { configureFlags, flagAttributes, flagName, flagSrc, flagView, normalizeFlagCode } from '../chunk-AU3H5WIY.js';
14
+ import '../chunk-UOSSNUSC.js';
15
+ import '../chunk-2QFTRNAZ.js';
16
+ import '../chunk-SNYUBXWQ.js';
17
+ import '../chunk-IXVMRVD4.js';
18
+ export { Input } from '../chunk-R4ZAEE7V.js';
19
+ import '../chunk-F25CGNQC.js';
20
+ export { PaletteContent, PaletteField, PaletteFooter, PaletteItem, PaletteList, PaletteRoot, PaletteTrigger, SearchPalette, usePaletteContext } from '../chunk-FWVWX67R.js';
21
+ export { createRecentStore, groupRows, isPaletteShortcut, moveActive, recentKey, shortcutLabel } from '../chunk-DTWZDONY.js';
22
+ import '../chunk-D5A2ZMAG.js';
23
+ import { Button } from '../chunk-6BGBYUSZ.js';
24
+ export { getLinkComponent, setLinkComponent, useButton } from '../chunk-6BGBYUSZ.js';
25
+ import '../chunk-MMQPZGSU.js';
26
+ export { Slot, mergeSlotProps } from '../chunk-HS3X3XCW.js';
6
27
  import Link from 'next/link';
7
28
  import { forwardRef, createElement } from 'react';
8
29
 
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Notification queue: ordering, timers, dedupe and pause. No rendering, no styles.
3
+ *
4
+ * The timer is the part that is always wrong when hand-rolled. A dismiss timer
5
+ * that keeps counting while the tab is hidden fires the moment the visitor comes
6
+ * back and they never see the message, so the remaining time is held, not run.
7
+ */
8
+ type NotificationTone = "info" | "success" | "warning" | "error" | "loading";
9
+ /** One button on a notification. Anything more belongs on the page, not in a toast. */
10
+ interface NotificationAction {
11
+ label: string;
12
+ onSelect: () => void;
13
+ /** Dismiss once it has been pressed. On by default. */
14
+ dismiss?: boolean;
15
+ }
16
+ interface NotificationInput {
17
+ /** Reuses the slot of a live notification with the same key instead of stacking. */
18
+ key?: string;
19
+ title: string;
20
+ body?: string;
21
+ tone?: NotificationTone;
22
+ /** ms before it dismisses itself. 0 or Infinity keeps it until dismissed. */
23
+ duration?: number;
24
+ action?: NotificationAction;
25
+ /** Arbitrary payload for the renderer: an href, an icon name. */
26
+ data?: unknown;
27
+ }
28
+ interface Notification extends Required<Omit<NotificationInput, "body" | "data" | "key" | "action">> {
29
+ id: string;
30
+ key?: string;
31
+ body?: string;
32
+ action?: NotificationAction;
33
+ data?: unknown;
34
+ createdAt: number;
35
+ }
36
+ /** What `promise()` shows at each stage. A function receives the resolved value or the error. */
37
+ interface PromiseMessages<T> {
38
+ loading: string | NotificationInput;
39
+ success: string | ((value: T) => string | NotificationInput);
40
+ error: string | ((error: unknown) => string | NotificationInput);
41
+ }
42
+ interface NotificationsOptions {
43
+ /** Live notifications kept at once. The oldest dismissable one makes room. */
44
+ max?: number;
45
+ /** Default ms before self-dismissal. */
46
+ duration?: number;
47
+ /** Errors default to staying until dismissed. */
48
+ stickyTones?: NotificationTone[];
49
+ }
50
+ interface Notifications {
51
+ readonly items: readonly Notification[];
52
+ notify(input: NotificationInput): string;
53
+ /** Change a live notification in place, keeping its slot and restarting its timer. */
54
+ update(id: string, patch: Partial<NotificationInput>): void;
55
+ /**
56
+ * One notification that follows an async operation from loading to its outcome, in the
57
+ * same slot. The alternative is three toasts stacking up for one action.
58
+ */
59
+ promise<T>(work: Promise<T>, messages: PromiseMessages<T>): Promise<T>;
60
+ dismiss(id: string): void;
61
+ dismissAll(): void;
62
+ /** Hold every timer, e.g. while a pointer rests on the stack. */
63
+ pause(): void;
64
+ resume(): void;
65
+ subscribe(listener: (items: readonly Notification[]) => void): () => void;
66
+ destroy(): void;
67
+ }
68
+ declare function createNotifications(options?: NotificationsOptions): Notifications;
69
+
70
+ export { type Notification as N, type PromiseMessages as P, type NotificationAction as a, type NotificationInput as b, type NotificationTone as c, type Notifications as d, type NotificationsOptions as e, createNotifications as f };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The parts of a command palette that are not rendering: what was searched before, how a
3
+ * flat list becomes groups, and where the highlight goes when a key is pressed.
4
+ *
5
+ * Pure functions and one storage wrapper, so all three are testable without a browser and
6
+ * reusable by a renderer that is not React. The palette's React parts are a thin layer over
7
+ * this file.
8
+ */
9
+ interface RecentEntry {
10
+ /** What was typed. The palette puts it back in the field when the row is chosen. */
11
+ term: string;
12
+ /** A result that was opened, rather than a query that was run. */
13
+ label?: string;
14
+ /** Where that result was. */
15
+ href?: string;
16
+ /** Which scope it was searched in, when the palette has scopes. */
17
+ scope?: string | null;
18
+ /** Epoch ms. Used only for ordering. */
19
+ at: number;
20
+ }
21
+ interface RecentStoreOptions {
22
+ /** Storage key. One per palette, so two palettes on a site do not share a history. */
23
+ key?: string;
24
+ /** How many entries to keep. */
25
+ limit?: number;
26
+ /** `local` outlives the tab, `session` does not. Default local. */
27
+ storage?: "local" | "session";
28
+ }
29
+ interface RecentStore {
30
+ list(): RecentEntry[];
31
+ remember(entry: Omit<RecentEntry, "at"> & {
32
+ at?: number;
33
+ }): RecentEntry[];
34
+ forget(entry: RecentEntry): RecentEntry[];
35
+ clear(): void;
36
+ }
37
+ /** Two entries are the same memory when they point at the same thing in the same scope. */
38
+ declare function recentKey(entry: Pick<RecentEntry, "term" | "href" | "scope">): string;
39
+ /**
40
+ * What was searched before, in this browser.
41
+ *
42
+ * Every read and write is guarded: storage throws in a private window, when the quota is
43
+ * full, and whenever a page is opened from a file URL - and a palette that cannot remember
44
+ * is still a working palette, so none of that is allowed to reach the renderer. Reads also
45
+ * survive a corrupted value, because the key is shared with whatever else the site stores.
46
+ */
47
+ declare function createRecentStore(options?: RecentStoreOptions): RecentStore;
48
+ /** A row after grouping: its own index in the FLAT list is what the keyboard moves through. */
49
+ interface PositionedRow<Row> {
50
+ row: Row;
51
+ position: number;
52
+ }
53
+ interface RowGroup<Row> {
54
+ label: string;
55
+ rows: PositionedRow<Row>[];
56
+ }
57
+ /**
58
+ * Group a flat list while keeping every row's flat position.
59
+ *
60
+ * The flat position is the whole point: the highlight moves through ONE sequence, so a
61
+ * group boundary is invisible to the arrow keys - and a renderer that grouped rows into
62
+ * separate arrays would have to reconstruct that sequence, which is where an off-by-one
63
+ * makes Enter open the row above the highlighted one. Groups keep first-seen order.
64
+ */
65
+ declare function groupRows<Row>(rows: Row[], labelOf: (row: Row) => string): RowGroup<Row>[];
66
+ /** Keys the list responds to. Anything else belongs to the field. */
67
+ type PaletteKey = "ArrowDown" | "ArrowUp" | "Home" | "End" | "PageDown" | "PageUp";
68
+ /**
69
+ * Where the highlight lands.
70
+ *
71
+ * It WRAPS, because a palette is a short list and the row after the last one is the first:
72
+ * clamping instead leaves the arrow key doing nothing at the end, which reads as the panel
73
+ * having frozen. An empty list stays at 0 rather than going negative.
74
+ */
75
+ declare function moveActive(current: number, length: number, key: PaletteKey, page?: number): number;
76
+ /**
77
+ * The shortcut label for this platform: `Cmd K` on a Mac, `Ctrl K` everywhere else.
78
+ *
79
+ * Read from the platform rather than hardcoded, because the wrong modifier in the trigger
80
+ * is a hint that actively misleads - and it is the first thing anyone tries.
81
+ */
82
+ declare function shortcutLabel(key?: string): string;
83
+ /** Whether an event is the palette's open shortcut. Meta on a Mac, Control elsewhere. */
84
+ declare function isPaletteShortcut(event: KeyboardEvent, key?: string): boolean;
85
+
86
+ export { type PaletteKey as P, type RecentEntry as R, type PositionedRow as a, type RecentStore as b, type RecentStoreOptions as c, type RowGroup as d, createRecentStore as e, groupRows as g, isPaletteShortcut as i, moveActive as m, recentKey as r, shortcutLabel as s };
@@ -0,0 +1,2 @@
1
+ export { PasswordExtras, PasswordStrength } from './chunk-XQHCZAPJ.js';
2
+ import './chunk-HC2ME5PU.js';
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Making a password, and judging one.
3
+ *
4
+ * Both are opt-in. A sign-in form wants neither: offering to generate a password where one
5
+ * already exists is noise, and scoring one the visitor cannot change is worse. They belong
6
+ * on a registration form and a change-password form, which is where an agent should switch
7
+ * them on.
8
+ */
9
+ /** Character classes a generated password can draw from. */
10
+ interface PasswordAlphabet {
11
+ lowercase?: boolean;
12
+ uppercase?: boolean;
13
+ digits?: boolean;
14
+ symbols?: boolean;
15
+ }
16
+ interface GeneratePasswordOptions extends PasswordAlphabet {
17
+ /** Default 20. Long beats clever: length is the only term that scales. */
18
+ length?: number;
19
+ /**
20
+ * Drop the characters that are read wrong off a screen or off paper - I l 1 O 0.
21
+ * Worth it when the password will be typed by hand, not worth the entropy otherwise.
22
+ */
23
+ excludeAmbiguous?: boolean;
24
+ /** Characters to remove from every class, e.g. ones your backend rejects. */
25
+ exclude?: string;
26
+ /**
27
+ * Guarantee at least one character from every class asked for. Most password policies
28
+ * demand it; it costs a little entropy, because it removes every password that happens
29
+ * to lack one.
30
+ */
31
+ requireEachClass?: boolean;
32
+ }
33
+ /**
34
+ * A random password from the classes asked for.
35
+ *
36
+ * @throws when the runtime has no CSPRNG, or when the options ask for something impossible
37
+ * (every class excluded, or a length too short to hold one of each).
38
+ */
39
+ declare function generatePassword(options?: GeneratePasswordOptions): string;
40
+ type PasswordScore = 0 | 1 | 2 | 3 | 4;
41
+ interface PasswordStrengthReport {
42
+ /** 0 worst, 4 best. What the bars under the field render. */
43
+ score: PasswordScore;
44
+ /** Estimated bits of entropy after the penalties below. */
45
+ bits: number;
46
+ /**
47
+ * Why it scored what it scored, worst first. Show the first one; showing all of them
48
+ * turns a hint into a lecture.
49
+ */
50
+ warnings: string[];
51
+ /** Empty field. Render nothing rather than a zero score, which reads as a failure. */
52
+ empty: boolean;
53
+ }
54
+ interface EstimateOptions {
55
+ /**
56
+ * Values the visitor has already typed elsewhere - email, name, company. A password
57
+ * containing one of them is guessable by anyone who has the sign-up form in front of
58
+ * them, and no character-class rule catches it.
59
+ */
60
+ userInputs?: string[];
61
+ }
62
+ /**
63
+ * Score a password.
64
+ *
65
+ * The bits are an estimate and the bands are a convention, not a measurement - they exist
66
+ * to move a bar, not to certify anything. Swap this out for zxcvbn where the number has to
67
+ * mean something, and check the breach corpus for the cases no estimator can see.
68
+ */
69
+ declare function estimatePasswordStrength(password: string, options?: EstimateOptions): PasswordStrengthReport;
70
+
71
+ export { type EstimateOptions as E, type GeneratePasswordOptions as G, type PasswordAlphabet as P, type PasswordScore as a, type PasswordStrengthReport as b, estimatePasswordStrength as e, generatePassword as g };
@@ -0,0 +1 @@
1
+ export { estimatePasswordStrength, generatePassword } from './chunk-HC2ME5PU.js';
@@ -0,0 +1,75 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
3
+ import { c as ButtonOptions, d as ButtonState } from '../button-CaXaqG_K.js';
4
+
5
+ /**
6
+ * `<Button>` - the component, for the ninety percent.
7
+ *
8
+ * ```tsx
9
+ * <Button onPress={save}>Save</Button>
10
+ * ```
11
+ *
12
+ * That is the whole thing: disabled while the work runs, `aria-busy`, `data-loading`, and a
13
+ * cooldown afterwards if you asked for one. `useButton` is still there for a button whose
14
+ * markup is nothing like a button - a card, a table row - but reaching for it to render an
15
+ * ordinary one means writing the same six lines at every call site, which is how they drift.
16
+ */
17
+ interface ButtonProps extends ButtonOptions, Omit<ComponentPropsWithoutRef<"button">, "onClick" | "onChange" | "children" | "type"> {
18
+ /**
19
+ * What to run when it is pressed. The same thing as `onPress`, under the name React
20
+ * already uses - write whichever reads better.
21
+ *
22
+ * It is an ALIAS, not a second handler: the component owns the element's real onClick,
23
+ * because that is where a press is refused while the button is loading, disabled or
24
+ * cooling down. Passing one through would replace that and silently take the behaviour
25
+ * with it. Returning a promise drives `loading` for its duration, same as `onPress`.
26
+ */
27
+ onClick?: ButtonOptions["onPress"];
28
+ /**
29
+ * Override what to render. You rarely need it: an href already renders whatever
30
+ * `setLinkComponent` registered - your router's Link - and a button otherwise.
31
+ */
32
+ as?: ElementType;
33
+ /**
34
+ * The label, or a function of the state for one that changes with it.
35
+ *
36
+ * ```tsx
37
+ * <Button onPress={send} cooldown={30_000}>
38
+ * {({ loading, cooldown }) => loading ? "Sending" : cooldown ? `Wait ${Math.ceil(cooldown / 1000)}s` : "Resend"}
39
+ * </Button>
40
+ * ```
41
+ */
42
+ children?: ReactNode | ((state: ButtonState) => ReactNode);
43
+ /** Replaces the label while async work runs - a spinner, or just different words. */
44
+ pending?: ReactNode;
45
+ /** `type` on a real button. Ignored on a link, which has none. */
46
+ type?: "button" | "submit" | "reset";
47
+ /**
48
+ * Show the shortcut as a key badge after the label, the way Stripe writes
49
+ * "Create invoice n".
50
+ *
51
+ * `"auto"` (the default) shows it when there is a shortcut AND the label renders text:
52
+ * beside a lone glyph on an icon-only button the badge is noise, and it would double the
53
+ * width of the smallest control on the screen. `true` and `false` decide it outright.
54
+ */
55
+ shortcutHint?: boolean | "auto";
56
+ /** Render the badge yourself. Return null to drop it for one button. */
57
+ renderShortcut?: (key: string) => ReactNode;
58
+ /**
59
+ * Put the behaviour on YOUR element instead of ours - a `motion.button`, your design
60
+ * system's button, a router Link, anything.
61
+ *
62
+ * ```tsx
63
+ * <Button asChild cooldown={30_000} onPress={resend}>
64
+ * <motion.button whileTap={{ scale: 0.98 }}>Resend</motion.button>
65
+ * </Button>
66
+ * ```
67
+ *
68
+ * The child owns its own markup, so the shortcut badge is not injected - the child is
69
+ * rendered exactly as written. Read `state.shortcut` from `useButton` to place your own.
70
+ */
71
+ asChild?: boolean;
72
+ }
73
+ declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLElement>>;
74
+
75
+ export { Button, type ButtonProps };
@@ -0,0 +1,4 @@
1
+ "use client";
2
+ export { Button } from '../chunk-6BGBYUSZ.js';
3
+ import '../chunk-MMQPZGSU.js';
4
+ import '../chunk-HS3X3XCW.js';
@@ -0,0 +1,37 @@
1
+ import { ImgHTMLAttributes, ReactNode } from 'react';
2
+ import { e as FlagShape, c as FlagFormat, f as FlagSource } from '../flags-BBJc9unY.js';
3
+
4
+ interface FlagProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt" | "width" | "height"> {
5
+ /** `es`, `ES`, `en-GB`, `gb-eng`, or the emoji flag you are replacing. */
6
+ code: string | null | undefined;
7
+ /**
8
+ * Your own accessible name. Left out, the country's own name is used, in the reader's
9
+ * language - so a flag standing on its own is never a silent image.
10
+ */
11
+ label?: string;
12
+ /**
13
+ * Beside a country name that is already on screen the flag is decoration, and repeating
14
+ * the name only makes a screen reader say it twice.
15
+ */
16
+ decorative?: boolean;
17
+ /** Language the automatic name is written in. Follows the document's `lang` otherwise. */
18
+ locale?: string;
19
+ shape?: FlagShape;
20
+ format?: FlagFormat;
21
+ source?: FlagSource;
22
+ basePath?: string;
23
+ /** Rendered height in px. The width follows the shape. Default 16. */
24
+ size?: number;
25
+ /** Rendered when the code resolves to nothing. Nothing, by default. */
26
+ fallback?: ReactNode;
27
+ }
28
+ /**
29
+ * `<Flag code="es" />` - a country flag as an image, at the size you asked for, from the
30
+ * source the app configured once.
31
+ *
32
+ * The defaults come from `configureFlags`, so moving the whole application from the CDN to
33
+ * a downloaded set is one line at startup and no change at any call site.
34
+ */
35
+ declare function Flag({ code, label, decorative, locale, shape, format, source, basePath, size, fallback, ...rest }: FlagProps): ReactNode;
36
+
37
+ export { Flag, type FlagProps };
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ export { Flag } from '../chunk-XNNQRA35.js';
3
+ import '../chunk-AU3H5WIY.js';