@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LettuceAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ <p align="center"><img src=".github/banner.png" alt="Crisp — the design language behind LettuceAI" width="100%"></p>
2
+
3
+ # Crisp
4
+
5
+ Crisp is LettuceAI's React component library. It provides application layouts, form controls, navigation, overlays, and media components, with shared design tokens and motion presets.
6
+
7
+ Components are written in TypeScript and styled with Tailwind CSS v4. Themes support light and dark palettes, custom colors, corner radii, and compact density.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ bun add @lettuceai/crisp
13
+ ```
14
+
15
+ Your application must also provide these peer dependencies:
16
+
17
+ | Package | Required version |
18
+ | --- | --- |
19
+ | `react`, `react-dom` | 19 or later |
20
+ | `tailwindcss` | 4 or later |
21
+ | `framer-motion` | 12 or later |
22
+ | `lucide-react` | 1 or later |
23
+
24
+ ## Setup
25
+
26
+ Crisp requires a working Tailwind CSS v4 build in your application. Import its stylesheet after Tailwind and include the package in Tailwind's source scanning:
27
+
28
+ ```css
29
+ @import "tailwindcss";
30
+ @import "@lettuceai/crisp/styles.css";
31
+
32
+ @source "../node_modules/@lettuceai/crisp/dist";
33
+ ```
34
+
35
+ The `@source` path is relative to your stylesheet; the example assumes it lives in `src/`. Crisp supplies tokens and CSS utilities, while your application's Tailwind build generates the component classes.
36
+
37
+ The stylesheet also sets global typography, page colors, and focus styles. It uses Inter when available, with system font fallbacks; it does not load a font file.
38
+
39
+ ## Usage
40
+
41
+ Import components from `@lettuceai/crisp`. This example opens a sheet containing a labeled input:
42
+
43
+ ```tsx
44
+ import { useState } from "react";
45
+ import { Button, Field, Input, Sheet, ThemeProvider } from "@lettuceai/crisp";
46
+
47
+ export function App() {
48
+ const [open, setOpen] = useState(false);
49
+
50
+ return (
51
+ <ThemeProvider>
52
+ <main className="p-6">
53
+ <Button variant="primary" onClick={() => setOpen(true)}>
54
+ Edit profile
55
+ </Button>
56
+ <Sheet open={open} onClose={() => setOpen(false)} title="Edit profile">
57
+ <Field label="Display name">
58
+ <Input name="displayName" autoComplete="nickname" />
59
+ </Field>
60
+ </Sheet>
61
+ </main>
62
+ </ThemeProvider>
63
+ );
64
+ }
65
+ ```
66
+
67
+ For notifications, mount `Toaster` once at the application root and call `toast.success()`, `toast.error()`, or the other toast helpers. To use `await confirm(...)`, mount `ConfirmHost` there as well. Both hosts are optional.
68
+
69
+ ## Components
70
+
71
+ | Area | Examples |
72
+ | --- | --- |
73
+ | Layout | `Box`, `Stack`, `Flex`, `Grid`, `Container`, `AppShell` |
74
+ | Navigation | `AppBar`, `NavRail`, `TabBar`, `Dock`, `Tabs`, `Breadcrumb` |
75
+ | Forms | `Field`, `Input`, `Select`, `Combobox`, `Checkbox`, `Switch`, `DatePicker` |
76
+ | Overlays | `Sheet`, `Dialog`, `Popover`, `Menu`, `CommandPalette` |
77
+ | Feedback | `Alert`, `Banner`, `Toaster`, `Progress`, `Skeleton`, `EmptyState` |
78
+ | Data and content | `Table`, `Tree`, `VirtualList`, `Chart`, `Prose`, `CodeBlock` |
79
+ | Media and chat | `MediaCard`, `ImageViewer`, `AudioItem`, `Message`, `Composer`, `AvatarPicker` |
80
+ | Flows and setup | `Wizard`, `ActionBar`, `Page`, `ChoiceCard`, `SecretInput`, `ModelSelect`, `ReorderableList` |
81
+
82
+ See the [component exports](src/components/index.ts) and [layout exports](src/layout/index.ts) for the full list. Component source files include their TypeScript prop definitions.
83
+
84
+ ## Theming
85
+
86
+ `ThemeProvider` applies theme variables to the document root and stores the selected theme in `localStorage` under `crisp.theme`. It restores the saved theme on startup, falling back to Midnight. Pass an `initial` theme to override that starting selection.
87
+
88
+ Use `useTheme()` inside the provider to change colors, radius, or density:
89
+
90
+ ```tsx
91
+ import { Button, useTheme } from "@lettuceai/crisp";
92
+
93
+ export function AccentPicker() {
94
+ const { setColor } = useTheme();
95
+
96
+ return (
97
+ <Button onClick={() => setColor("accent", "#38bdf8")}>
98
+ Use blue accent
99
+ </Button>
100
+ );
101
+ }
102
+ ```
103
+
104
+ The provider exposes six presets through `presets`: Midnight, Ink, Vellum, Paper, Slate, and Ember. Select one with `setTheme()`. Radius options are `sharp`, `default`, and `round`; density options are `default` and `compact`.
105
+
106
+ Text, fill, border, and elevated surface colors are derived from the theme palette. For custom integrations, `themeToVars()` returns the CSS variables and `applyTheme()` applies a theme to an HTML element. The [theme definitions](src/theme/theme.ts) document the available values.
107
+
108
+ ## Utilities
109
+
110
+ - `cn()` merges conditional class names and resolves Tailwind class conflicts.
111
+ - `icon` defines five icon sizes, from `icon.xs` (12px) to `icon.xl` (20px).
112
+ - `motion` provides Framer Motion transition presets such as `motion.settle` and `motion.sheetIn`.
113
+ - `relativeTime()`, `compactNumber()` and `fileSize()` format the numbers a list shows, one way everywhere.
114
+
115
+ Hooks and color helpers are also available from the [package entry point](src/index.ts).
116
+
117
+ ## Development
118
+
119
+ ```sh
120
+ bun install
121
+ bun run typecheck
122
+ bun run lint
123
+ bun run build
124
+ ```
125
+
126
+ The build writes an ES module, source map, and TypeScript declarations to `dist/`. Runtime dependencies remain external. The stylesheet is distributed separately as `@lettuceai/crisp/styles.css`.
127
+
128
+ ## License
129
+
130
+ [MIT](LICENSE) © 2026 LettuceAI.
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from "react";
2
+ export interface AccordionProps {
3
+ /** Allow several items open at once. */
4
+ multiple?: boolean;
5
+ defaultOpen?: string[];
6
+ className?: string;
7
+ children: ReactNode;
8
+ }
9
+ export declare function Accordion({ multiple, defaultOpen, className, children }: AccordionProps): import("react").JSX.Element;
10
+ export declare function AccordionItem({ value, title, description, children }: {
11
+ value: string;
12
+ title: ReactNode;
13
+ description?: ReactNode;
14
+ children: ReactNode;
15
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ActionBarProps {
3
+ /** The one thing to do next. */
4
+ primary?: ReactNode;
5
+ /** Beside it — Skip, Save draft. */
6
+ secondary?: ReactNode;
7
+ /** A Back button on the left; pass the handler. */
8
+ onBack?: () => void;
9
+ backLabel?: string;
10
+ /** Anything else on the left — a count, a hint. */
11
+ leading?: ReactNode;
12
+ /**
13
+ * Pin to the bottom of the screen with the home indicator kept clear. Default: on
14
+ * compact layouts. Off, it is an ordinary row at the end of the content.
15
+ */
16
+ pinned?: boolean;
17
+ className?: string;
18
+ }
19
+ /**
20
+ * The row that moves a flow forward: Back on the left, the main action on the right,
21
+ * a secondary beside it if the screen offers one. On the phone it stays put above the
22
+ * home indicator; on the desktop it sits under the content. Every wizard, every
23
+ * multi-step form uses this, so the buttons are in the same place on every screen.
24
+ */
25
+ export declare function ActionBar({ primary, secondary, onBack, backLabel, leading, pinned, className }: ActionBarProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from "react";
2
+ export type AlertTone = "info" | "success" | "warning" | "danger";
3
+ export interface AlertProps {
4
+ tone?: AlertTone;
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ /** Small tag next to the title — an HTTP status, a model name. */
8
+ meta?: ReactNode;
9
+ /** Collapsed by default; raw payloads, stack traces. */
10
+ details?: string;
11
+ action?: ReactNode;
12
+ onDismiss?: () => void;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Inline notice. Explains what happened and what to do next, in the interface's voice.
17
+ * Replaces the chat error banner; same information, one type scale.
18
+ */
19
+ export declare function Alert({ tone: toneKey, title, description, meta, details, action, onDismiss, className }: AlertProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { type ReactNode } from "react";
2
+ export interface AppBarProps {
3
+ title: ReactNode;
4
+ /** Small text after the title — a count, a status. */
5
+ meta?: ReactNode;
6
+ onBack?: () => void;
7
+ backLabel?: string;
8
+ /** Renders a search field on desktop and a search icon that expands on compact. */
9
+ searchValue?: string;
10
+ onSearchChange?: (value: string) => void;
11
+ searchPlaceholder?: string;
12
+ actions?: ReactNode;
13
+ /** A filter row under the title. */
14
+ filters?: ReactNode;
15
+ /**
16
+ * Shrink the title when the page scrolls. Pass the scrolling element;
17
+ * defaults to the window.
18
+ */
19
+ scrollRef?: React.RefObject<HTMLElement | null>;
20
+ /** Desktop window chrome sits above; leave room for it. */
21
+ inset?: boolean;
22
+ className?: string;
23
+ }
24
+ /**
25
+ * The page header. One component for both platforms: on desktop the title is large
26
+ * and search is always visible; on compact it condenses on scroll and search expands
27
+ * from an icon, because a permanent search field costs a third of a phone's width.
28
+ */
29
+ export declare function AppBar({ title, meta, onBack, backLabel, searchValue, onSearchChange, searchPlaceholder, actions, filters, scrollRef, inset, className, }: AppBarProps): import("react").JSX.Element;
@@ -0,0 +1,42 @@
1
+ import type { ReactNode, Ref } from "react";
2
+ import type { NavDestination } from "./nav";
3
+ export type NavStyle = "auto" | "tabs" | "tabsLabelled" | "rail" | "sidebar" | "floatingRail" | "dock" | "header";
4
+ export interface AppShellProps<T extends string> {
5
+ items: readonly NavDestination<T>[];
6
+ value: T;
7
+ onChange: (id: T) => void;
8
+ onCreate?: () => void;
9
+ createLabel?: string;
10
+ footerItems?: readonly NavDestination<T>[];
11
+ /**
12
+ * `auto` picks tabs on touch/narrow and a rail on desktop. The others are the
13
+ * user's explicit choice — the app already lets people pick, and a phone-sized
14
+ * window still falls back to tabs so nothing becomes unreachable.
15
+ */
16
+ navStyle?: NavStyle;
17
+ side?: "left" | "right";
18
+ dockEdge?: "top" | "bottom";
19
+ /** Desktop window chrome, rendered above everything. */
20
+ titleBar?: ReactNode;
21
+ /** Header inside the content column. */
22
+ header?: ReactNode;
23
+ /** The scrolling content element — for a header that condenses on scroll, or for
24
+ resetting the scroll position when the page's content changes underneath it. */
25
+ mainRef?: Ref<HTMLElement>;
26
+ /** Content under the destinations — recent chats. Shown folded on the rail too, so
27
+ it does not appear and vanish as the sidebar opens and closes. */
28
+ navSection?: ReactNode;
29
+ /** Let the person switch the side navigation between sidebar and rail. */
30
+ onNavToggle?: () => void;
31
+ children: ReactNode;
32
+ className?: string;
33
+ }
34
+ /**
35
+ * The application frame. Owns where navigation lives so pages never have to think
36
+ * about it: on a phone the bar is at the bottom, on desktop the rail is at the side,
37
+ * and the content column is the same component either way.
38
+ *
39
+ * It fills its parent (`h-full`). Mounted at the app root, give it a real height —
40
+ * `<AppShell className="h-dvh">` — since #root only sets a min-height.
41
+ */
42
+ export declare function AppShell<T extends string>({ items, value, onChange, onCreate, createLabel, footerItems, navStyle, side, dockEdge, titleBar, header, mainRef, navSection, onNavToggle, children, className, }: AppShellProps<T>): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { type ReactNode } from "react";
2
+ import { type BadgeTone } from "./Badge";
3
+ export interface AudioItemProps {
4
+ title: string;
5
+ /** One line under the title when nothing is playing. */
6
+ description?: ReactNode;
7
+ src?: string | null;
8
+ /** Called the first time play is pressed, to fetch the data lazily. */
9
+ onLoadSrc?: () => Promise<string>;
10
+ badge?: {
11
+ label: string;
12
+ tone?: BadgeTone;
13
+ };
14
+ /** Trailing slot — an overflow menu. */
15
+ actions?: ReactNode;
16
+ className?: string;
17
+ }
18
+ /**
19
+ * A clip in the audio library. The scrub bar is a real range input, so it is
20
+ * keyboard- and screen-reader-operable — the app's version is a div with onClick.
21
+ */
22
+ export declare function AudioItem({ title, description, src: srcProp, onLoadSrc, badge, actions, className }: AudioItemProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ export interface AvatarProps {
2
+ src?: string | null;
3
+ name: string;
4
+ size?: "xs" | "sm" | "md" | "lg" | "xl";
5
+ /** Accent ring — the "active" / "speaking" state. */
6
+ ring?: boolean;
7
+ className?: string;
8
+ /** Used by AvatarGroup to cut the overlap notch. */
9
+ style?: React.CSSProperties;
10
+ }
11
+ export declare function Avatar({ src, name, size, ring, className, style }: AvatarProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ import { type AvatarProps } from "./Avatar";
3
+ export interface AvatarGroupProps {
4
+ people: readonly {
5
+ name: string;
6
+ src?: string | null;
7
+ }[];
8
+ /** Show at most this many, then a +N counter. Default 4. */
9
+ max?: number;
10
+ size?: AvatarProps["size"];
11
+ /** Hover/focus a face to bring it forward — desktop only, it's decoration. */
12
+ interactive?: boolean;
13
+ /** Replaces the +N chip. */
14
+ overflow?: ReactNode;
15
+ className?: string;
16
+ }
17
+ export declare function AvatarGroup({ people, max, size, interactive, overflow, className }: AvatarGroupProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type AvatarProps } from "./Avatar";
2
+ export interface AvatarPickerProps {
3
+ value: string | null;
4
+ /** Called with a square data URL, or null when the picture is removed. */
5
+ onChange: (dataUrl: string | null) => void;
6
+ /** Initials when there's no picture. */
7
+ name?: string;
8
+ size?: AvatarProps["size"];
9
+ /** Pixel size of the square that comes out. Default 512. */
10
+ output?: number;
11
+ /** Skip the crop step and take the picture as it comes. */
12
+ crop?: boolean;
13
+ disabled?: boolean;
14
+ className?: string;
15
+ }
16
+ /**
17
+ * The avatar as a target: tap or drop to pick, then frame it — drag to move, slide to
18
+ * zoom — and out comes a square. Characters, personas and groups all start here.
19
+ */
20
+ export declare function AvatarPicker({ value, onChange, name, size, output, crop, disabled, className }: AvatarPickerProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from "react";
2
+ export type BadgeTone = "neutral" | "accent" | "danger" | "warning" | "info" | "secondary";
3
+ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
4
+ tone?: BadgeTone;
5
+ /** Leading status dot. */
6
+ dot?: boolean;
7
+ }
8
+ export declare function Badge({ tone, dot, className, children, ...rest }: BadgeProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ export type BannerTone = "info" | "warning" | "danger" | "offline";
3
+ export interface BannerProps {
4
+ tone?: BannerTone;
5
+ children: ReactNode;
6
+ action?: ReactNode;
7
+ onDismiss?: () => void;
8
+ /** Stick to the top of the scroll container. */
9
+ sticky?: boolean;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * A full-width notice about the whole app or page — you're offline, a migration is
14
+ * running, an update is ready. Alert is about one thing on the page; a Banner is about
15
+ * the page. One line only: if it needs a paragraph, it's an Alert.
16
+ */
17
+ export declare function Banner({ tone, children, action, onDismiss, sticky, className }: BannerProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from "react";
2
+ export interface Crumb {
3
+ label: string;
4
+ icon?: ReactNode;
5
+ /** Omit on the last crumb — the page you are on is not a link. */
6
+ onClick?: () => void;
7
+ href?: string;
8
+ }
9
+ export interface BreadcrumbProps {
10
+ items: readonly Crumb[];
11
+ /**
12
+ * Keep the first crumb and the last `n`; everything between collapses into a menu.
13
+ * Deep settings paths would otherwise wrap or scroll on a phone. Default 1.
14
+ */
15
+ maxVisible?: number;
16
+ className?: string;
17
+ }
18
+ /**
19
+ * Where you are in a nested screen. Desktop shows the whole path; on a narrow
20
+ * viewport the middle collapses into a menu rather than wrapping to a second line.
21
+ * It complements the back button, it does not replace it.
22
+ */
23
+ export declare function Breadcrumb({ items, maxVisible, className }: BreadcrumbProps): import("react").JSX.Element | null;
@@ -0,0 +1,15 @@
1
+ import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ export type ButtonVariant = "primary" | "tonal" | "secondary" | "ghost" | "danger";
3
+ export type ButtonSize = "sm" | "md" | "lg";
4
+ export interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
5
+ variant?: ButtonVariant;
6
+ size?: ButtonSize;
7
+ loading?: boolean;
8
+ leading?: ReactNode;
9
+ trailing?: ReactNode;
10
+ /** Fill the width. For two buttons sharing a row, give each `className="flex-1"` instead. */
11
+ block?: boolean;
12
+ children?: ReactNode;
13
+ }
14
+ export declare const iconSize: Record<ButtonSize, number>;
15
+ export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ButtonProps } from "./Button";
3
+ export interface ButtonGroupProps {
4
+ /** Buttons joined into one control — the inner corners are squared off. */
5
+ attached?: boolean;
6
+ className?: string;
7
+ children: ReactNode;
8
+ }
9
+ export declare function ButtonGroup({ attached, className, children }: ButtonGroupProps): import("react").JSX.Element;
10
+ export interface SplitButtonProps extends Omit<ButtonProps, "children"> {
11
+ children: ReactNode;
12
+ /** Menu items for the attached chevron. */
13
+ menu: ReactNode;
14
+ menuLabel?: string;
15
+ }
16
+ /**
17
+ * A primary action with alternatives behind a chevron. The composer already does this
18
+ * with hold-to-send-as-system — a gesture nobody discovers. On desktop this is the
19
+ * visible version of the same thing.
20
+ */
21
+ export declare function SplitButton({ children, menu, menuLabel, variant, size, ...rest }: SplitButtonProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { type HTMLAttributes } from "react";
2
+ import { type HTMLMotionProps } from "framer-motion";
3
+ export interface CardProps extends HTMLAttributes<HTMLDivElement> {
4
+ /** `raised` sits on the page; `overlay` floats above everything. */
5
+ level?: "raised" | "overlay";
6
+ /** A hairline edge. Off by default — elevation already separates it. */
7
+ bordered?: boolean;
8
+ padding?: "none" | "sm" | "md";
9
+ }
10
+ export declare const Card: import("react").ForwardRefExoticComponent<CardProps & import("react").RefAttributes<HTMLDivElement>>;
11
+ /** A clickable card is a button, not a div with onClick. */
12
+ export interface CardButtonProps extends HTMLMotionProps<"button"> {
13
+ selected?: boolean;
14
+ padding?: "none" | "sm" | "md";
15
+ }
16
+ export declare const CardButton: import("react").ForwardRefExoticComponent<Omit<CardButtonProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from "react";
2
+ export interface CarouselProps {
3
+ /** One node per slide. */
4
+ children: ReactNode[];
5
+ /** Dots and arrows. Arrows are hidden where there is no pointer. */
6
+ controls?: boolean;
7
+ "aria-label": string;
8
+ className?: string;
9
+ slideClassName?: string;
10
+ }
11
+ /**
12
+ * A horizontal pager built on native scroll-snap: swipe on touch, arrows and keys on
13
+ * desktop, and it keeps working with JavaScript busy. Onboarding, feature tours,
14
+ * image variants.
15
+ */
16
+ export declare function Carousel({ children, controls, className, slideClassName, ...aria }: CarouselProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { type ReactNode } from "react";
2
+ import { type HTMLMotionProps } from "framer-motion";
3
+ export type CharacterCardVariant = "row" | "banner";
4
+ export interface CharacterCardProps extends Omit<HTMLMotionProps<"button">, "title"> {
5
+ name: string;
6
+ description?: ReactNode;
7
+ avatar?: string | null;
8
+ /** CSS colour or gradient extracted from the avatar. Falls back to a tint from the name. */
9
+ gradient?: string | null;
10
+ /** Text colours that go with the gradient. */
11
+ fg?: string | null;
12
+ fgMuted?: string | null;
13
+ /** `row` is the horizontal list card; `banner` is the wide cover card. */
14
+ variant?: CharacterCardVariant;
15
+ /** Slot on the trailing edge — a badge, a menu. Replaces the chevron. */
16
+ trailing?: ReactNode;
17
+ selected?: boolean;
18
+ }
19
+ /**
20
+ * The home-screen character card, in the app's two shapes. When the avatar yields a
21
+ * gradient, the whole card becomes a Surface and every child re-colours itself —
22
+ * no per-element `style={{ color: textColor }}`.
23
+ *
24
+ * It grows with the column it sits in, not with the window: a container query, so a
25
+ * narrow pane on a wide monitor still gets the phone-sized card.
26
+ */
27
+ export declare const CharacterCard: import("react").ForwardRefExoticComponent<Omit<CharacterCardProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
28
+ export declare function CharacterCardSkeleton({ variant }: {
29
+ variant?: CharacterCardVariant;
30
+ }): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { Cell } from "recharts";
2
+ export declare const CHART_SERIES: readonly ["var(--color-accent)", "var(--color-info)", "var(--color-secondary)", "var(--color-warning)", "var(--color-danger)"];
3
+ export interface ChartProps<Row extends Record<string, unknown>> {
4
+ data: readonly Row[];
5
+ /** Key holding the category or timestamp. */
6
+ x: string;
7
+ /** One entry per series. */
8
+ series: readonly {
9
+ key: string;
10
+ label: string;
11
+ color?: string;
12
+ }[];
13
+ kind?: "line" | "area" | "bar";
14
+ height?: number;
15
+ /** Formats values in the axis and tooltip. */
16
+ format?: (value: number) => string;
17
+ /** Hide the y axis for a sparkline-sized chart. */
18
+ minimal?: boolean;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * Charts on the token palette, so they follow a custom accent and stay legible on the
23
+ * app's near-black surface. The app pulls in recharts already but styles each chart
24
+ * inline with hard-coded colours.
25
+ */
26
+ export declare function Chart<Row extends Record<string, unknown>>({ data, x, series, kind, height, format, minimal, className, }: ChartProps<Row>): import("react").JSX.Element;
27
+ export interface SparklineProps {
28
+ data: readonly number[];
29
+ color?: string;
30
+ height?: number;
31
+ className?: string;
32
+ }
33
+ /** A trend with no axes, sized to sit inside a StatTile. */
34
+ export declare function Sparkline({ data, color, height, className }: SparklineProps): import("react").JSX.Element;
35
+ export { Cell as ChartCell };
@@ -0,0 +1,11 @@
1
+ import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ export interface CheckboxProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> {
3
+ checked: boolean;
4
+ onChange: (checked: boolean) => void;
5
+ /** Partially selected (a parent of a mixed list). */
6
+ indeterminate?: boolean;
7
+ /** Inline label. For label + hint layouts wrap in <Field inline>. */
8
+ label?: ReactNode;
9
+ size?: "sm" | "md";
10
+ }
11
+ export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,11 @@
1
+ import type { ButtonHTMLAttributes, ReactNode } from "react";
2
+ export interface ChipProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onSelect"> {
3
+ /** Filter chip: toggles. Without `selected` it's a plain tag. */
4
+ selected?: boolean;
5
+ /** Removable tag: renders an × and calls this. */
6
+ onRemove?: () => void;
7
+ icon?: ReactNode;
8
+ children: ReactNode;
9
+ }
10
+ /** Tags and filters. Badge is read-only; Chip is something you tap. */
11
+ export declare function Chip({ selected, onRemove, icon, className, children, ...rest }: ChipProps): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ export interface ChoiceCardProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "title"> {
3
+ selected: boolean;
4
+ title: ReactNode;
5
+ description?: ReactNode;
6
+ /** Left of the title, in a tile. */
7
+ icon?: ReactNode;
8
+ /** Right of the title — a badge, a price. */
9
+ meta?: ReactNode;
10
+ /** The selection mark: a tick for one-of-many, a box for any-of-many, or none when the border says enough. */
11
+ indicator?: "radio" | "checkbox" | "none";
12
+ /** Below the description — bullet lists, a form that opens once chosen. */
13
+ children?: ReactNode;
14
+ /** `row` puts icon, text and mark on one line; `stack` puts the icon above the text, for grids of equal cards. */
15
+ layout?: "row" | "stack";
16
+ }
17
+ /**
18
+ * One option among a few: a provider, a plan, a memory mode, a character to add to a
19
+ * group. The selected border is the state, the mark confirms it, and every card in
20
+ * a grid is the same height so the row reads as a set.
21
+ */
22
+ export declare const ChoiceCard: import("react").ForwardRefExoticComponent<ChoiceCardProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,16 @@
1
+ export interface CodeBlockProps {
2
+ code: string;
3
+ /** Shown in the header — a filename, a language, "Response". */
4
+ label?: string;
5
+ /** Wrap long lines instead of scrolling. Good for prompts, bad for JSON. */
6
+ wrap?: boolean;
7
+ /** Adds a gutter of line numbers. */
8
+ lineNumbers?: boolean;
9
+ maxHeight?: number | string;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * Monospace content with a copy button: prompt templates, raw payloads, error bodies.
14
+ * Long lines scroll inside the block, so the page never scrolls sideways because of it.
15
+ */
16
+ export declare function CodeBlock({ code, label, wrap, lineNumbers, maxHeight, className }: CodeBlockProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from "react";
2
+ export interface CollapsibleProps {
3
+ title: ReactNode;
4
+ description?: ReactNode;
5
+ defaultOpen?: boolean;
6
+ /** Controlled mode. */
7
+ open?: boolean;
8
+ onOpenChange?: (open: boolean) => void;
9
+ /** Right of the title — a count, a badge, a switch. */
10
+ trailing?: ReactNode;
11
+ /** `panel` draws a bordered container; `plain` is just the toggle and its content. */
12
+ variant?: "panel" | "plain";
13
+ className?: string;
14
+ children: ReactNode;
15
+ }
16
+ /**
17
+ * A single disclosure. Accordion is for a set that share open/closed state; this is
18
+ * one section that stands alone — advanced settings, a long definition, a raw payload.
19
+ */
20
+ export declare function Collapsible({ title, description, defaultOpen, open: openProp, onOpenChange, trailing, variant, className, children, }: CollapsibleProps): import("react").JSX.Element;