@lettuceai/crisp 1.0.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/components/Accordion.d.ts +15 -0
  4. package/dist/components/ActionBar.d.ts +25 -0
  5. package/dist/components/Alert.d.ts +19 -0
  6. package/dist/components/AppBar.d.ts +29 -0
  7. package/dist/components/AppShell.d.ts +42 -0
  8. package/dist/components/AudioItem.d.ts +22 -0
  9. package/dist/components/Avatar.d.ts +11 -0
  10. package/dist/components/AvatarGroup.d.ts +17 -0
  11. package/dist/components/AvatarPicker.d.ts +20 -0
  12. package/dist/components/Badge.d.ts +8 -0
  13. package/dist/components/Banner.d.ts +17 -0
  14. package/dist/components/Breadcrumb.d.ts +23 -0
  15. package/dist/components/Button.d.ts +15 -0
  16. package/dist/components/ButtonGroup.d.ts +21 -0
  17. package/dist/components/Card.d.ts +16 -0
  18. package/dist/components/Carousel.d.ts +16 -0
  19. package/dist/components/CharacterCard.d.ts +30 -0
  20. package/dist/components/Chart.d.ts +35 -0
  21. package/dist/components/Checkbox.d.ts +11 -0
  22. package/dist/components/Chip.d.ts +11 -0
  23. package/dist/components/ChoiceCard.d.ts +22 -0
  24. package/dist/components/CodeBlock.d.ts +16 -0
  25. package/dist/components/Collapsible.d.ts +20 -0
  26. package/dist/components/ColorPicker.d.ts +15 -0
  27. package/dist/components/Combobox.d.ts +30 -0
  28. package/dist/components/CommandPalette.d.ts +34 -0
  29. package/dist/components/Composer.d.ts +49 -0
  30. package/dist/components/ContextMenu.d.ts +18 -0
  31. package/dist/components/CopyButton.d.ts +13 -0
  32. package/dist/components/DatePicker.d.ts +23 -0
  33. package/dist/components/Dialog.d.ts +15 -0
  34. package/dist/components/Diff.d.ts +17 -0
  35. package/dist/components/Dock.d.ts +6 -0
  36. package/dist/components/DownloadItem.d.ts +28 -0
  37. package/dist/components/Dropzone.d.ts +21 -0
  38. package/dist/components/EmptyState.d.ts +24 -0
  39. package/dist/components/Field.d.ts +20 -0
  40. package/dist/components/FilterBar.d.ts +21 -0
  41. package/dist/components/Form.d.ts +44 -0
  42. package/dist/components/Highlight.d.ts +14 -0
  43. package/dist/components/HoverCard.d.ts +15 -0
  44. package/dist/components/Icon.d.ts +37 -0
  45. package/dist/components/IconButton.d.ts +14 -0
  46. package/dist/components/ImageViewer.d.ts +16 -0
  47. package/dist/components/InfiniteScroll.d.ts +14 -0
  48. package/dist/components/Input.d.ts +14 -0
  49. package/dist/components/Kbd.d.ts +5 -0
  50. package/dist/components/List.d.ts +25 -0
  51. package/dist/components/Masonry.d.ts +16 -0
  52. package/dist/components/MediaCard.d.ts +23 -0
  53. package/dist/components/MediaTile.d.ts +21 -0
  54. package/dist/components/Menu.d.ts +34 -0
  55. package/dist/components/Message.d.ts +76 -0
  56. package/dist/components/Meter.d.ts +20 -0
  57. package/dist/components/ModelSelect.d.ts +35 -0
  58. package/dist/components/MultiItemDownloadCard.d.ts +39 -0
  59. package/dist/components/NavRail.d.ts +37 -0
  60. package/dist/components/NumberInput.d.ts +19 -0
  61. package/dist/components/Page.d.ts +18 -0
  62. package/dist/components/Pagination.d.ts +10 -0
  63. package/dist/components/Panel.d.ts +18 -0
  64. package/dist/components/PartitionBar.d.ts +39 -0
  65. package/dist/components/Popover.d.ts +45 -0
  66. package/dist/components/Progress.d.ts +9 -0
  67. package/dist/components/Prose.d.ts +13 -0
  68. package/dist/components/Radio.d.ts +19 -0
  69. package/dist/components/ReorderableList.d.ts +21 -0
  70. package/dist/components/Resizable.d.ts +20 -0
  71. package/dist/components/ScrollArea.d.ts +17 -0
  72. package/dist/components/SearchField.d.ts +14 -0
  73. package/dist/components/SecretInput.d.ts +26 -0
  74. package/dist/components/Segmented.d.ts +16 -0
  75. package/dist/components/Select.d.ts +15 -0
  76. package/dist/components/SelectMenu.d.ts +48 -0
  77. package/dist/components/Sheet.d.ts +50 -0
  78. package/dist/components/Shelf.d.ts +17 -0
  79. package/dist/components/Skeleton.d.ts +12 -0
  80. package/dist/components/Slider.d.ts +11 -0
  81. package/dist/components/Spinner.d.ts +13 -0
  82. package/dist/components/StatTile.d.ts +33 -0
  83. package/dist/components/Stepper.d.ts +21 -0
  84. package/dist/components/Surface.d.ts +56 -0
  85. package/dist/components/Switch.d.ts +9 -0
  86. package/dist/components/TabBar.d.ts +12 -0
  87. package/dist/components/Table.d.ts +35 -0
  88. package/dist/components/Tabs.d.ts +31 -0
  89. package/dist/components/TagInput.d.ts +18 -0
  90. package/dist/components/Text.d.ts +293 -0
  91. package/dist/components/ThemeEditor.d.ts +8 -0
  92. package/dist/components/Timeline.d.ts +17 -0
  93. package/dist/components/TitleBar.d.ts +17 -0
  94. package/dist/components/Toast.d.ts +50 -0
  95. package/dist/components/ToggleGroup.d.ts +22 -0
  96. package/dist/components/Toolbar.d.ts +18 -0
  97. package/dist/components/Tooltip.d.ts +15 -0
  98. package/dist/components/Tour.d.ts +22 -0
  99. package/dist/components/Tree.d.ts +21 -0
  100. package/dist/components/VirtualList.d.ts +18 -0
  101. package/dist/components/WindowControls.d.ts +38 -0
  102. package/dist/components/Wizard.d.ts +33 -0
  103. package/dist/components/confirm.d.ts +24 -0
  104. package/dist/components/index.d.ts +103 -0
  105. package/dist/components/nav.d.ts +19 -0
  106. package/dist/components/primitives.d.ts +14 -0
  107. package/dist/index.d.ts +15 -0
  108. package/dist/index.js +8035 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/layout/Box.d.ts +6 -0
  111. package/dist/layout/Center.d.ts +2 -0
  112. package/dist/layout/Container.d.ts +8 -0
  113. package/dist/layout/Divider.d.ts +9 -0
  114. package/dist/layout/Flex.d.ts +16 -0
  115. package/dist/layout/Grid.d.ts +13 -0
  116. package/dist/layout/Spacer.d.ts +4 -0
  117. package/dist/layout/Stack.d.ts +13 -0
  118. package/dist/layout/index.d.ts +8 -0
  119. package/dist/lib/cn.d.ts +2 -0
  120. package/dist/lib/color.d.ts +16 -0
  121. package/dist/lib/format.d.ts +6 -0
  122. package/dist/lib/frame.d.ts +25 -0
  123. package/dist/lib/icon.d.ts +24 -0
  124. package/dist/lib/imageBrightness.d.ts +8 -0
  125. package/dist/lib/motion.d.ts +54 -0
  126. package/dist/lib/polymorphic.d.ts +12 -0
  127. package/dist/lib/tint.d.ts +9 -0
  128. package/dist/lib/useLongPress.d.ts +19 -0
  129. package/dist/lib/useMediaQuery.d.ts +8 -0
  130. package/dist/lib/useModal.d.ts +8 -0
  131. package/dist/theme/ThemeProvider.d.ts +19 -0
  132. package/dist/theme/index.d.ts +2 -0
  133. package/dist/theme/theme.d.ts +36 -0
  134. package/package.json +81 -0
  135. package/src/styles.css +392 -0
@@ -0,0 +1,15 @@
1
+ export interface ColorPickerProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ /** Quick picks shown above the free-form field. */
5
+ swatches?: readonly string[];
6
+ disabled?: boolean;
7
+ id?: string;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * A colour swatch that opens a picker. The custom-colour settings page rolls its own
12
+ * inputs; this pairs the native colour input (so the OS picker is available) with a hex
13
+ * field and a preset row, which is how people actually choose.
14
+ */
15
+ export declare function ColorPicker({ value, onChange, swatches, disabled, id, className }: ColorPickerProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from "react";
2
+ export interface ComboboxOption<T extends string = string> {
3
+ value: T;
4
+ label: string;
5
+ description?: string;
6
+ icon?: ReactNode;
7
+ group?: string;
8
+ disabled?: boolean;
9
+ }
10
+ export interface ComboboxProps<T extends string> {
11
+ options: readonly ComboboxOption<T>[];
12
+ value: T | null;
13
+ onChange: (value: T | null) => void;
14
+ placeholder?: string;
15
+ searchPlaceholder?: string;
16
+ /** Take over filtering — for a remote list like the HuggingFace catalogue. */
17
+ onSearch?: (query: string) => void;
18
+ loading?: boolean;
19
+ emptyMessage?: string;
20
+ clearable?: boolean;
21
+ disabled?: boolean;
22
+ id?: string;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * A select you can type into. The app has four hand-rolled versions of this
27
+ * (PersonaSelector, ReferenceSelector, CharacterSelectorSingle, LoraSelector) and a
28
+ * 5,000-line HuggingFace browser that a native select can't serve.
29
+ */
30
+ export declare function Combobox<T extends string>({ options, value, onChange, placeholder, searchPlaceholder, onSearch, loading, emptyMessage, clearable, disabled, id, className, }: ComboboxProps<T>): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { type ReactNode } from "react";
2
+ export interface Command {
3
+ id: string;
4
+ label: string;
5
+ /** Second line — where it goes, or what it does. */
6
+ description?: string;
7
+ icon?: ReactNode;
8
+ group?: string;
9
+ /** Extra words to match on that aren't in the label. */
10
+ keywords?: string;
11
+ shortcut?: string;
12
+ onRun: () => void;
13
+ }
14
+ export interface CommandPaletteProps {
15
+ open: boolean;
16
+ onOpenChange: (open: boolean) => void;
17
+ commands: readonly Command[];
18
+ placeholder?: string;
19
+ /** Take over filtering — for searching messages or characters remotely. */
20
+ onQueryChange?: (query: string) => void;
21
+ loading?: boolean;
22
+ emptyMessage?: string;
23
+ }
24
+ /**
25
+ * ⌘K. A desktop affordance the app has the data for already — characters, chats,
26
+ * settings and every navigation target are all searchable, just not from one place.
27
+ * It never replaces a visible control; it's a shortcut to ones that exist.
28
+ */
29
+ export declare function CommandPalette({ open, onOpenChange, commands, placeholder, onQueryChange, loading, emptyMessage, }: CommandPaletteProps): import("react").ReactPortal;
30
+ /** Binds ⌘K / Ctrl+K. Ignores the shortcut while a field is focused. */
31
+ export declare function useCommandPalette(): {
32
+ open: boolean;
33
+ setOpen: import("react").Dispatch<import("react").SetStateAction<boolean>>;
34
+ };
@@ -0,0 +1,49 @@
1
+ import { type ReactNode } from "react";
2
+ export interface ComposerAttachment {
3
+ id: string;
4
+ /** Image data URL, or undefined for non-image files (shown as a chip). */
5
+ src?: string;
6
+ name?: string;
7
+ }
8
+ export interface ComposerMic {
9
+ active: boolean;
10
+ transcribing?: boolean;
11
+ elapsedMs: number;
12
+ analyser: AnalyserNode | null;
13
+ onStart: () => void;
14
+ onStop: () => void;
15
+ onCancel: () => void;
16
+ disabled?: boolean;
17
+ }
18
+ export interface ComposerProps {
19
+ value: string;
20
+ onChange: (value: string) => void;
21
+ onSend: () => void;
22
+ /** Shown while `sending`; if absent the button just spins. */
23
+ onStop?: () => void;
24
+ /** Empty composer + tap send = let the character continue. */
25
+ onContinue?: () => void;
26
+ /** Long-press / right-click on send. */
27
+ onSendAsSystem?: () => void;
28
+ onAttach?: () => void;
29
+ attachments?: ComposerAttachment[];
30
+ onRemoveAttachment?: (id: string) => void;
31
+ mic?: ComposerMic;
32
+ sending?: boolean;
33
+ disabled?: boolean;
34
+ placeholder?: string;
35
+ /** Enter sends on desktop; on touch, Enter is a newline. */
36
+ enterSends?: boolean;
37
+ /** Rendered above the pill — an <Alert>. */
38
+ notice?: ReactNode;
39
+ /** Slot inside the pill above the input — author's note, quick snippets. */
40
+ topSlot?: ReactNode;
41
+ className?: string;
42
+ }
43
+ export declare function Composer({ value, onChange, onSend, onStop, onContinue, onSendAsSystem, onAttach, attachments, onRemoveAttachment, mic, sending, disabled, placeholder, enterSends, notice, topSlot, className, }: ComposerProps): import("react").JSX.Element;
44
+ /** Live waveform while recording. Same sampling as the app; drawn in tokens. */
45
+ export declare function RecordingWave({ elapsedMs, analyser, frozen }: {
46
+ elapsedMs: number;
47
+ analyser: AnalyserNode | null;
48
+ frozen?: boolean;
49
+ }): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ export interface ContextMenuProps {
3
+ /** The menu body — reuse MenuItem / MenuSeparator / MenuLabel. */
4
+ menu: ReactNode;
5
+ /** ms to hold before it opens on touch. Default 450. */
6
+ longPressDelay?: number;
7
+ /** Fires when the menu opens and closes — use it to mark the target as held. */
8
+ onOpenChange?: (open: boolean) => void;
9
+ disabled?: boolean;
10
+ className?: string;
11
+ children: ReactNode;
12
+ }
13
+ /**
14
+ * Right-click on desktop, long-press on touch — one component for both, which is what
15
+ * the app writes by hand at every message and library item. Opens at the pointer and
16
+ * flips to stay on screen.
17
+ */
18
+ export declare function ContextMenu({ menu, longPressDelay, onOpenChange, disabled, className, children }: ContextMenuProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { type ButtonProps } from "./Button";
2
+ export interface CopyButtonProps extends Omit<ButtonProps, "children" | "onClick"> {
3
+ value: string;
4
+ /** Omit for an icon-only button. */
5
+ children?: React.ReactNode;
6
+ label?: string;
7
+ copiedLabel?: string;
8
+ }
9
+ /**
10
+ * Copy with a confirmation that lives on the button itself, so the action doesn't need
11
+ * a toast. The app writes this by hand wherever it copies a prompt, an error or an id.
12
+ */
13
+ export declare function CopyButton({ value, children, label, copiedLabel, className, ...rest }: CopyButtonProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ export interface CalendarProps {
2
+ value: Date | null;
3
+ onChange: (date: Date) => void;
4
+ min?: Date;
5
+ max?: Date;
6
+ /** 0 = Sunday, 1 = Monday. Defaults to the locale's own first day. */
7
+ weekStartsOn?: 0 | 1;
8
+ locale?: string;
9
+ className?: string;
10
+ }
11
+ /** The month grid. Uses Intl for day and month names, so it follows the app's 20 locales. */
12
+ export declare function Calendar({ value, onChange, min, max, weekStartsOn, locale, className }: CalendarProps): import("react").JSX.Element;
13
+ export interface DatePickerProps extends Omit<CalendarProps, "className"> {
14
+ /** Shown when nothing is chosen. */
15
+ placeholder?: string;
16
+ /** Also offer a time field. */
17
+ withTime?: boolean;
18
+ disabled?: boolean;
19
+ id?: string;
20
+ className?: string;
21
+ }
22
+ /** A field that opens a Calendar. The app's DateTimePicker is 323 lines used in one place. */
23
+ export declare function DatePicker({ value, onChange, placeholder, withTime, disabled, id, className, ...calendar }: DatePickerProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from "react";
2
+ export interface DialogProps {
3
+ open: boolean;
4
+ onClose: () => void;
5
+ title?: ReactNode;
6
+ description?: ReactNode;
7
+ footer?: ReactNode;
8
+ size?: "sm" | "md" | "lg";
9
+ /** Hide the corner close button — for confirmations where the footer is the only way out. */
10
+ dismissible?: boolean;
11
+ className?: string;
12
+ children?: ReactNode;
13
+ }
14
+ /** Centred modal. Confirmations, short forms, anything that should not feel like a page. */
15
+ export declare function Dialog({ open, onClose, title, description, footer, size, dismissible, className, children }: DialogProps): import("react").ReactPortal;
@@ -0,0 +1,17 @@
1
+ export interface DiffProps {
2
+ before: string;
3
+ after: string;
4
+ /** Labels for the two sides. */
5
+ beforeLabel?: string;
6
+ afterLabel?: string;
7
+ /** `unified` stacks the changes; `split` shows both sides. Split needs room. */
8
+ view?: "unified" | "split";
9
+ /** Collapse runs of unchanged lines longer than this. 0 shows everything. */
10
+ context?: number;
11
+ className?: string;
12
+ }
13
+ /**
14
+ * What changed between two versions. The sync conflict inbox asks people to choose a
15
+ * side without showing them what differs; this is the missing half.
16
+ */
17
+ export declare function Diff({ before, after, beforeLabel, afterLabel, view, context, className }: DiffProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import type { NavProps } from "./nav";
2
+ export interface DockProps<T extends string> extends NavProps<T> {
3
+ edge?: "top" | "bottom";
4
+ }
5
+ /** A floating pill of destinations. Desktop and tablet; it does not reach the thumb on a phone. */
6
+ export declare function Dock<T extends string>({ items, value, onChange, onCreate, createLabel, edge, className }: DockProps<T>): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ export type DownloadStatus = "queued" | "downloading" | "paused" | "verifying" | "done" | "failed";
3
+ export interface DownloadItemProps {
4
+ name: string;
5
+ /** Under the name — the repo, the quantisation, the format. */
6
+ detail?: ReactNode;
7
+ status: DownloadStatus;
8
+ /** 0–1. Omit while queued or verifying. */
9
+ progress?: number;
10
+ /** Bytes per second. */
11
+ speed?: number;
12
+ /** Seconds remaining. */
13
+ eta?: number;
14
+ /** Total bytes, for the "x of y" line. */
15
+ size?: number;
16
+ error?: string;
17
+ onPause?: () => void;
18
+ onResume?: () => void;
19
+ onCancel?: () => void;
20
+ onRetry?: () => void;
21
+ className?: string;
22
+ }
23
+ /**
24
+ * One download, with everything a person needs to decide whether to wait: how far
25
+ * along, how fast, how long left, and how to stop. The app has four different
26
+ * download UIs and none of them shows all four.
27
+ */
28
+ export declare function DownloadItem({ name, detail, status, progress, speed, eta, size, error, onPause, onResume, onCancel, onRetry, className, }: DownloadItemProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ export interface DropzoneProps {
3
+ onFiles: (files: File[]) => void;
4
+ /** An `accept` string, e.g. "image/*" or ".png,.webp". */
5
+ accept?: string;
6
+ multiple?: boolean;
7
+ /** Bytes. Files over this are dropped and reported through `onReject`. */
8
+ maxSize?: number;
9
+ onReject?: (files: File[], reason: "size" | "type") => void;
10
+ disabled?: boolean;
11
+ /** Replaces the default prompt — a preview, say. */
12
+ children?: ReactNode;
13
+ hint?: ReactNode;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * Drop, click or paste a file. Avatars, character cards, background images and audio
18
+ * all get their own picker today; this is the one, and it keeps the keyboard path —
19
+ * the hidden input is focusable, not `display: none`.
20
+ */
21
+ export declare function Dropzone({ onFiles, accept, multiple, maxSize, onReject, disabled, children, hint, className, }: DropzoneProps): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import type { ReactNode } from "react";
2
+ export interface EmptyStateProps {
3
+ icon?: ReactNode;
4
+ title: ReactNode;
5
+ /** Say what would put something here. */
6
+ description?: ReactNode;
7
+ action?: ReactNode;
8
+ /** Compact variant for inside cards and sheets. */
9
+ size?: "sm" | "md";
10
+ className?: string;
11
+ }
12
+ /** An empty screen is an invitation to act, not an apology. */
13
+ export declare function EmptyState({ icon, title, description, action, size, className }: EmptyStateProps): import("react").JSX.Element;
14
+ export interface ErrorStateProps {
15
+ title?: ReactNode;
16
+ /** What went wrong and, if it can be helped, what to do. Never an apology. */
17
+ description?: ReactNode;
18
+ onRetry?: () => void;
19
+ retryLabel?: string;
20
+ size?: "sm" | "md";
21
+ className?: string;
22
+ }
23
+ /** Something failed to load. Says what, and offers the one thing that might fix it. */
24
+ export declare function ErrorState({ title, description, onRetry, retryLabel, size, className }: ErrorStateProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from "react";
2
+ interface FieldContextValue {
3
+ id: string;
4
+ describedBy?: string;
5
+ invalid: boolean;
6
+ }
7
+ /** Inputs call this to pick up id / aria wiring from the surrounding Field. */
8
+ export declare function useFieldContext(): FieldContextValue | null;
9
+ export interface FieldProps {
10
+ label?: ReactNode;
11
+ hint?: ReactNode;
12
+ error?: ReactNode;
13
+ required?: boolean;
14
+ /** Label and control on one row — for switches and compact selects. */
15
+ inline?: boolean;
16
+ className?: string;
17
+ children: ReactNode;
18
+ }
19
+ export declare function Field({ label, hint, error, required, inline, className, children }: FieldProps): import("react").JSX.Element;
20
+ export {};
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ export interface FilterOption<T extends string> {
3
+ value: T;
4
+ label: string;
5
+ count?: number;
6
+ icon?: ReactNode;
7
+ }
8
+ export interface FilterBarProps<T extends string> {
9
+ options: readonly FilterOption<T>[];
10
+ value: T;
11
+ onChange: (value: T) => void;
12
+ "aria-label"?: string;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Horizontal filter row. It narrows one list by a facet; the list stays the same
17
+ * list. When the choice changes what the screen is, that is Tabs, not a filter. Counts sit inside the chip so the row stays one line.
18
+ * The overflow hint is a mask on the scroller, not a gradient overlay — an overlay
19
+ * would have to know the parent's background colour, and it never does.
20
+ */
21
+ export declare function FilterBar<T extends string>({ options, value, onChange, className, ...aria }: FilterBarProps<T>): import("react").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { type ReactNode } from "react";
2
+ interface FormContextValue<T> {
3
+ values: T;
4
+ initial: T;
5
+ dirty: boolean;
6
+ submitting: boolean;
7
+ errors: Partial<Record<keyof T, string>>;
8
+ set: <K extends keyof T>(key: K, value: T[K]) => void;
9
+ patch: (values: Partial<T>) => void;
10
+ reset: () => void;
11
+ }
12
+ export declare function useForm<T extends Record<string, unknown>>(): FormContextValue<T>;
13
+ export interface FormProps<T extends Record<string, unknown>> {
14
+ initial: T;
15
+ onSubmit: (values: T) => void | Promise<void>;
16
+ /** Return a message per invalid field. Runs on submit. */
17
+ validate?: (values: T) => Partial<Record<keyof T, string>>;
18
+ /** Warn before the tab closes while there are unsaved edits. */
19
+ guardUnload?: boolean;
20
+ children: ReactNode | ((form: FormContextValue<T>) => ReactNode);
21
+ className?: string;
22
+ }
23
+ /**
24
+ * Owns a form's values, dirty state and validation, so pages stop tracking them by
25
+ * hand — the app's editors run to thousands of lines each and the top bar carries a
26
+ * `wasUnsavedRef` of its own.
27
+ */
28
+ export declare function Form<T extends Record<string, unknown>>({ initial, onSubmit, validate, guardUnload, children, className, }: FormProps<T>): import("react").JSX.Element;
29
+ export interface FormActionsProps {
30
+ submitLabel?: string;
31
+ cancelLabel?: string;
32
+ onCancel?: () => void;
33
+ /** Keep the bar visible at the bottom of a long form. */
34
+ sticky?: boolean;
35
+ children?: ReactNode;
36
+ }
37
+ /** Save / cancel, disabled until something actually changed. */
38
+ export declare function FormActions({ submitLabel, cancelLabel, onCancel, sticky, children }: FormActionsProps): import("react").JSX.Element;
39
+ /**
40
+ * Ask before leaving a dirty form. Wire it to your router's navigation guard:
41
+ * `const leave = useUnsavedGuard(dirty); if (await leave()) navigate(to);`
42
+ */
43
+ export declare function useUnsavedGuard(dirty: boolean): () => Promise<boolean>;
44
+ export {};
@@ -0,0 +1,14 @@
1
+ export interface HighlightProps {
2
+ children: string;
3
+ /** The search term(s) to mark. Empty renders the text untouched. */
4
+ query: string;
5
+ /** Treat the query as separate words rather than one phrase. */
6
+ words?: boolean;
7
+ className?: string;
8
+ markClassName?: string;
9
+ }
10
+ /**
11
+ * Marks the matching part of a search result. Uses `<mark>`, so a screen reader
12
+ * announces the match rather than it being colour-only — which is what a span would be.
13
+ */
14
+ export declare function Highlight({ children, query, words, className, markClassName }: HighlightProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { type ReactElement, type ReactNode } from "react";
2
+ export interface HoverCardProps {
3
+ content: ReactNode;
4
+ /** ms before it appears / disappears. */
5
+ openDelay?: number;
6
+ closeDelay?: number;
7
+ side?: "top" | "bottom" | "left" | "right";
8
+ children: ReactElement<Record<string, unknown>>;
9
+ }
10
+ /**
11
+ * A preview on hover — a character card under a mention, model details under a name.
12
+ * Pointer-only by design: it never opens on touch, so it must never hold anything you
13
+ * can't reach another way.
14
+ */
15
+ export declare function HoverCard({ content, openDelay, closeDelay, side, children }: HoverCardProps): import("react").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { type ComponentType } from "react";
2
+ /**
3
+ * Four icon sizes, and only four. The app uses fifteen — from 9px to 36px — in two
4
+ * different notations (`size={14}` and `className="h-4 w-4"`). Going through this
5
+ * component makes that impossible to drift back into.
6
+ */
7
+ export declare const ICON_SIZE: {
8
+ /** 14 — inside dense controls, badges, chips. */
9
+ readonly sm: 14;
10
+ /** 16 — buttons, list rows, inline with body text. */
11
+ readonly md: 16;
12
+ /** 18 — icon buttons, toolbars. */
13
+ readonly lg: 18;
14
+ /** 22 — navigation, empty states. */
15
+ readonly xl: 22;
16
+ };
17
+ export type IconSize = keyof typeof ICON_SIZE;
18
+ export interface IconProps {
19
+ /** A lucide icon component. */
20
+ as: ComponentType<{
21
+ size?: number;
22
+ strokeWidth?: number;
23
+ className?: string;
24
+ "aria-hidden"?: boolean;
25
+ }>;
26
+ size?: IconSize;
27
+ /** Give it a label only when the icon is the sole carrier of meaning. */
28
+ label?: string;
29
+ strokeWidth?: number;
30
+ className?: string;
31
+ }
32
+ export declare function Icon({ as: Component, size, label, strokeWidth, className }: IconProps): import("react").ReactElement<{
33
+ size?: number;
34
+ strokeWidth?: number;
35
+ className?: string;
36
+ "aria-hidden"?: boolean;
37
+ }, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,14 @@
1
+ import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ export type IconButtonVariant = "ghost" | "secondary" | "tonal";
3
+ export type IconButtonSize = "sm" | "md" | "lg";
4
+ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
5
+ /** Required: icon buttons have no visible text. */
6
+ label: string;
7
+ variant?: IconButtonVariant;
8
+ size?: IconButtonSize;
9
+ /** Round for standalone (close, back); square inside toolbars. */
10
+ shape?: "square" | "round";
11
+ active?: boolean;
12
+ children: ReactNode;
13
+ }
14
+ export declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,16 @@
1
+ export interface ViewerImage {
2
+ src: string;
3
+ alt: string;
4
+ caption?: string;
5
+ }
6
+ export interface ImageViewerProps {
7
+ images: readonly ViewerImage[];
8
+ /** Index to show; null closes the viewer. */
9
+ index: number | null;
10
+ onIndexChange: (index: number | null) => void;
11
+ }
12
+ /**
13
+ * Full-screen image inspection for the image library and chat attachments. Arrow keys
14
+ * page, Escape closes, +/- zoom, and a zoomed image pans by drag.
15
+ */
16
+ export declare function ImageViewer({ images, index, onIndexChange }: ImageViewerProps): import("react").ReactPortal;
@@ -0,0 +1,14 @@
1
+ export interface InfiniteScrollProps {
2
+ /** Called when the sentinel comes near the viewport. Guard against re-entry yourself. */
3
+ onLoadMore: () => void;
4
+ /** Stop observing — nothing left, or a request is in flight. */
5
+ disabled?: boolean;
6
+ loading?: boolean;
7
+ /** Shown once there is nothing more to fetch. */
8
+ endMessage?: string;
9
+ /** How far ahead of the viewport to trigger. Default 600px. */
10
+ rootMargin?: string;
11
+ className?: string;
12
+ }
13
+ /** Sentinel for "load the next page". Pair with VirtualList for very long lists. */
14
+ export declare function InfiniteScroll({ onLoadMore, disabled, loading, endMessage, rootMargin, className, }: InfiniteScrollProps): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { type InputHTMLAttributes, type ReactNode, type TextareaHTMLAttributes } from "react";
2
+ export declare const inputSurface: string;
3
+ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
4
+ leading?: ReactNode;
5
+ trailing?: ReactNode;
6
+ }
7
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
8
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
9
+ /** Grow with the text instead of scrolling inside a fixed box. `rows` is the minimum. */
10
+ autoGrow?: boolean;
11
+ /** With `autoGrow`, stop growing here and scroll. Default 12. */
12
+ maxRows?: number;
13
+ }
14
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function Kbd({ children, className }: {
3
+ children: ReactNode;
4
+ className?: string;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ import { type HTMLMotionProps } from "framer-motion";
3
+ export interface ListGroupProps extends HTMLAttributes<HTMLDivElement> {
4
+ /** Small tracked heading above the group — the app's section-label style, kept to this one role. */
5
+ label?: ReactNode;
6
+ }
7
+ export declare function ListGroup({ label, className, children, ...rest }: ListGroupProps): import("react").JSX.Element;
8
+ type Tone = "neutral" | "accent" | "danger" | "warning" | "info" | "secondary";
9
+ interface RowContentProps {
10
+ icon?: ReactNode;
11
+ tone?: Tone;
12
+ title: ReactNode;
13
+ description?: ReactNode;
14
+ trailing?: ReactNode;
15
+ chevron?: boolean;
16
+ }
17
+ /** Static row (shows a value, hosts a switch, etc.). */
18
+ export interface ListRowProps extends Omit<HTMLAttributes<HTMLDivElement>, "title">, RowContentProps {
19
+ }
20
+ export declare function ListRow({ icon, tone, title, description, trailing, chevron, className, ...rest }: ListRowProps): import("react").JSX.Element;
21
+ /** Navigational / action row. Chevron on by default. */
22
+ export interface ListRowButtonProps extends Omit<HTMLMotionProps<"button">, "title">, RowContentProps {
23
+ }
24
+ export declare const ListRowButton: import("react").ForwardRefExoticComponent<Omit<ListRowButtonProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
25
+ export {};
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from "react";
2
+ export interface MasonryProps {
3
+ children: ReactNode;
4
+ /** Smallest acceptable column width; the count follows the container. */
5
+ minColumnWidth?: number;
6
+ /** Hard cap on columns. */
7
+ maxColumns?: number;
8
+ gap?: number;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * A column layout for items of uneven height — the image library, where a square grid
13
+ * crops every portrait. Balances by placing each item in the shortest column, which
14
+ * keeps DOM order per column and so keeps tab order sane.
15
+ */
16
+ export declare function Masonry({ children, minColumnWidth, maxColumns, gap, className }: MasonryProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from "react";
2
+ import { type HTMLMotionProps } from "framer-motion";
3
+ export type MediaCardTone = "accent" | "info" | "warning" | "secondary" | "neutral";
4
+ export interface MediaCardProps extends Omit<HTMLMotionProps<"button">, "title"> {
5
+ title: string;
6
+ description?: ReactNode;
7
+ image?: string | null;
8
+ /** What kind of thing this is — a dot plus a word, only where it disambiguates. */
9
+ kind?: string;
10
+ tone?: MediaCardTone;
11
+ /** Small pill next to the title (a persona's nickname, an entry count). */
12
+ tag?: ReactNode;
13
+ /** Bottom-right corner slot — a count, a duration. */
14
+ meta?: ReactNode;
15
+ selected?: boolean;
16
+ }
17
+ /**
18
+ * The poster card: a character, persona or lorebook at a glance.
19
+ * Full-bleed art, one scrim behind the text only, title and one line of description.
20
+ */
21
+ export declare const MediaCard: import("react").ForwardRefExoticComponent<Omit<MediaCardProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
22
+ /** Matches MediaCard's aspect and radius exactly, so the grid never reflows on load. */
23
+ export declare function MediaCardSkeleton(): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { type HTMLMotionProps } from "framer-motion";
2
+ export interface MediaTileProps extends Omit<HTMLMotionProps<"button">, "title" | "variants"> {
3
+ src: string;
4
+ /** Describes the image for screen readers; also the fallback text. */
5
+ alt: string;
6
+ /** Badge in the corner — "Avatar", "Background". Omit when it isn't useful. */
7
+ kind?: string;
8
+ /** Number of variants stacked under this one. */
9
+ variantCount?: number;
10
+ selected?: boolean;
11
+ aspect?: "square" | "portrait";
12
+ }
13
+ /**
14
+ * A square image in a grid. Deliberately bare: on a touch screen there is no hover,
15
+ * so metadata that only appears on hover is metadata nobody reads. Size, dimensions
16
+ * and format belong in the detail sheet this tile opens.
17
+ */
18
+ export declare const MediaTile: import("react").ForwardRefExoticComponent<Omit<MediaTileProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
19
+ export declare function MediaTileSkeleton({ aspect }: {
20
+ aspect?: "square" | "portrait";
21
+ }): import("react").JSX.Element;