harebear-ui 0.9.0 → 0.11.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.
- package/dist/components/admin/app-nav.d.ts +11 -1
- package/dist/components/admin/app-nav.js +26 -9
- package/dist/components/admin/checklist-editor.d.ts +20 -0
- package/dist/components/admin/checklist-editor.js +69 -0
- package/dist/components/admin/comment-thread.d.ts +26 -0
- package/dist/components/admin/comment-thread.js +68 -0
- package/dist/components/admin/copy-field.d.ts +3 -1
- package/dist/components/admin/copy-field.js +10 -5
- package/dist/components/admin/date-time-picker.js +1 -1
- package/dist/components/admin/detail-sheet.js +3 -0
- package/dist/components/admin/markdown-editor.d.ts +2 -5
- package/dist/components/admin/markdown-editor.js +13 -9
- package/dist/components/admin/page-header.d.ts +3 -1
- package/dist/components/admin/page-header.js +17 -9
- package/dist/components/admin/page.d.ts +16 -0
- package/dist/components/admin/page.js +42 -0
- package/dist/components/admin/prompt-dialog.d.ts +19 -0
- package/dist/components/admin/prompt-dialog.js +83 -0
- package/dist/components/admin/settings.d.ts +4 -2
- package/dist/components/admin/settings.js +37 -30
- package/dist/components/admin/time-zone-picker.d.ts +12 -0
- package/dist/components/admin/time-zone-picker.js +31 -0
- package/dist/components/admin/user-menu.d.ts +3 -1
- package/dist/components/admin/user-menu.js +21 -10
- package/dist/components/ai/action-receipt.d.ts +1 -1
- package/dist/components/ai/action-receipt.js +1 -1
- package/dist/components/ai/approval.d.ts +1 -1
- package/dist/components/ai/approval.js +2 -2
- package/dist/components/ai/ask-box.js +1 -1
- package/dist/components/ai/assistant.d.ts +3 -1
- package/dist/components/ai/assistant.js +3 -2
- package/dist/components/ai/chat.js +1 -1
- package/dist/components/ai/code-block.js +3 -3
- package/dist/components/ai/markdown.d.ts +13 -0
- package/dist/components/ai/markdown.js +41 -0
- package/dist/components/ai/tool-steps.d.ts +2 -2
- package/dist/components/ai/tool-steps.js +5 -4
- package/dist/components/auth/auth.d.ts +41 -5
- package/dist/components/auth/auth.js +165 -155
- package/dist/components/data/day-strip.d.ts +6 -1
- package/dist/components/data/day-strip.js +15 -5
- package/dist/components/layout/collapse.js +1 -1
- package/dist/components/layout/text.d.ts +3 -3
- package/dist/components/layout/text.js +6 -1
- package/dist/components/ui/button.js +1 -1
- package/dist/components/ui/check-circle.js +1 -1
- package/dist/components/ui/code-block.js +2 -2
- package/dist/components/ui/combobox.js +1 -0
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.js +3 -1
- package/dist/components/ui/drawer.d.ts +1 -1
- package/dist/components/ui/drawer.js +3 -1
- package/dist/components/ui/input-group.js +1 -1
- package/dist/components/ui/input.js +1 -1
- package/dist/components/ui/sheet.d.ts +1 -1
- package/dist/components/ui/sheet.js +3 -1
- package/dist/components/ui/sonner.d.ts +2 -2
- package/dist/components/ui/sonner.js +3 -2
- package/dist/components/ui/toggle.js +1 -1
- package/dist/components/ui/trace.js +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.js +9 -3
- package/dist/lib/{date.js → internal/date.js} +1 -1
- package/dist/lib/{toast-clearance.js → internal/toast-clearance.js} +12 -2
- package/dist/styles.css +25 -5
- package/docs/COMPONENTS.md +157 -33
- package/docs/ROADMAP.md +2 -1
- package/llms.txt +7 -6
- package/package.json +3 -1
- package/skills/harebear-ui/SKILL.md +5 -4
package/docs/COMPONENTS.md
CHANGED
|
@@ -15,6 +15,7 @@ Full working screens that combine these live in the repo under `playground/scree
|
|
|
15
15
|
- **Compose, don't fork.** Every component accepts `className` (merged with `cn`, later classes win). Reach for a primitive before writing a new component.
|
|
16
16
|
- **Icons are lucide-react elements.** Pass `<SearchIcon />`, not the component. Buttons size icons automatically.
|
|
17
17
|
- **Status uses tones**, not colors: `"success" | "warning" | "danger" | "info" | "neutral"`.
|
|
18
|
+
- **Touch targets are built in.** Under `pointer: coarse`, `Button` (every size) and `Toggle`/`ToggleGroupItem` get a 44px hit area (an invisible `::after`, which an `overflow-hidden` ancestor or a later sibling clips: in such a spot, size the button itself, e.g. `pointer-coarse:size-11`, as the `CodeBlock` and `CodeSnippet` copy buttons do) and `Input`/`InputGroup` grow to 44px.
|
|
18
19
|
|
|
19
20
|
## Agent-first apps
|
|
20
21
|
|
|
@@ -27,7 +28,7 @@ A household or personal app where an assistant sits on every page and safe actio
|
|
|
27
28
|
<AppNav
|
|
28
29
|
brand={<AppBrand name="Homeclub" icon={<HouseIcon />} />}
|
|
29
30
|
places={[{ label: "Today", icon: <SunIcon />, href: "/", active: true, count: 3 }, ...]}
|
|
30
|
-
group={{ label: "Lists", items: lists, onAdd: newList, addLabel: "New list" }}
|
|
31
|
+
group={{ label: "Lists", href: "/lists", items: lists, onAdd: newList, addLabel: "New list" }}
|
|
31
32
|
action={{ label: "New to-do", icon: <PlusIcon />, onSelect: openQuickAdd }}
|
|
32
33
|
footer={<UserMenu name={user.name} onSignOut={signOut} />}
|
|
33
34
|
>
|
|
@@ -40,8 +41,10 @@ A household or personal app where an assistant sits on every page and safe actio
|
|
|
40
41
|
</ThemeProvider>
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
- Pages: `Page` (the column) with a `PageHeader` (h1; `UserMenu compact className="md:hidden"` in `actions`, hidden from md up, since the sidebar footer is hidden on phones) and `PageSection`s (h2 regions). A chat page sets `fill` on `AppNav`.
|
|
43
45
|
- Lists: `SectionedList` (`TaskRow` rows); when the agent or a bulk action changes data, confirm with `toastUndo` and set `lingerMs` to its window so a ticked row stays struck through for about as long as Undo shows.
|
|
44
|
-
- A week agenda: `DayStrip` above one `SectionedList` with a section per day (`empty` keeps empty days) and `AgendaRow` rows from `renderItem`.
|
|
46
|
+
- A week agenda: `DayStrip sticky` above one `SectionedList` with a section per day (`empty` keeps empty days) and `AgendaRow` rows from `renderItem`.
|
|
47
|
+
- A record's details (`DetailSheet`): `ChecklistEditor` for steps, `CommentThread` for comments, `MarkdownEditor` for notes.
|
|
45
48
|
- In the assistant thread, each action it took is an `ActionReceipt` with Undo (append it from `onSend`).
|
|
46
49
|
- A page with its own ask box (Today) answers under it: `AskBox` with `reply` and `pending` from `useAssistant()`, `onStop={stop}`, and `send(text, { open: false })`.
|
|
47
50
|
- Settings and setup flows leave the frame: `FocusLayout` around `SettingsSection`s of `SettingsRow`s.
|
|
@@ -89,9 +92,9 @@ Every component in the shadcn registry is included. (shadcn's `form` item is an
|
|
|
89
92
|
| Carousel | `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel`, type `CarouselApi` | embla. |
|
|
90
93
|
| Chart | `ChartContainer`, `ChartTooltip`, `ChartTooltipContent`, `ChartLegend`, `ChartLegendContent`, `ChartStyle` | Recharts wrappers. Prefer `TrendChart` for line/area; use these for custom charts. |
|
|
91
94
|
| Checkbox | `Checkbox` | |
|
|
92
|
-
| Check circle | `CheckCircle` | harebear. 24px round checkbox for to-do rows (44px touch target), pops when ticked. `tone="important"` tints it; pair with a word. Name it with `aria-labelledby` the row title.
|
|
95
|
+
| Check circle | `CheckCircle` | harebear. 24px round checkbox for to-do rows (44px touch target), pops when ticked (no pop or fade under reduced motion). Focus shows a solid primary outline whatever the `tone`. `tone="important"` tints it; pair with a word. Name it with `aria-labelledby` the row title. |
|
|
93
96
|
| Collapsible | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | |
|
|
94
|
-
| Combobox | `Combobox`, `ComboboxInput`, `ComboboxContent`, `ComboboxList`, `ComboboxItem`, `ComboboxGroup`, `ComboboxLabel`, `ComboboxCollection`, `ComboboxEmpty`, `ComboboxSeparator`, `ComboboxChips`, `ComboboxChip`, `ComboboxChipsInput`, `ComboboxTrigger`, `ComboboxValue`, `useComboboxAnchor` | Searchable select (Base UI). `<Combobox items={list}>` + `<ComboboxList>{(item) => <ComboboxItem value={item}>…}</ComboboxList>`. Chips for multi-select.
|
|
97
|
+
| Combobox | `Combobox`, `ComboboxInput`, `ComboboxContent`, `ComboboxList`, `ComboboxItem`, `ComboboxGroup`, `ComboboxLabel`, `ComboboxCollection`, `ComboboxEmpty`, `ComboboxSeparator`, `ComboboxChips`, `ComboboxChip`, `ComboboxChipsInput`, `ComboboxTrigger`, `ComboboxValue`, `useComboboxAnchor` | Searchable select (Base UI). `<Combobox items={list}>` + `<ComboboxList>{(item) => <ComboboxItem value={item}>…}</ComboboxList>`. Chips for multi-select. `ComboboxInput`'s built-in trigger is named "Show options". |
|
|
95
98
|
| Command | `Command`, `CommandDialog`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandShortcut`, `CommandSeparator` | Use for ⌘K palettes. |
|
|
96
99
|
| Direction | `DirectionProvider`, `useDirection` | RTL/LTR context for Radix components. |
|
|
97
100
|
| Context menu | `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, `ContextMenuCheckboxItem`, `ContextMenuRadioItem`, `ContextMenuRadioGroup`, `ContextMenuLabel`, `ContextMenuSeparator`, `ContextMenuShortcut`, `ContextMenuGroup`, `ContextMenuPortal`, `ContextMenuSub`, `ContextMenuSubContent`, `ContextMenuSubTrigger` | |
|
|
@@ -101,7 +104,7 @@ Every component in the shadcn registry is included. (shadcn's `form` item is an
|
|
|
101
104
|
| Empty state | `Empty`, `EmptyHeader`, `EmptyMedia`, `EmptyTitle`, `EmptyDescription`, `EmptyContent` | Use for zero-data screens. harebear adds `size="sm"`: compact, no flex-grow, for empties inside cards and panels. |
|
|
102
105
|
| Field | `Field`, `FieldLabel`, `FieldDescription`, `FieldError`, `FieldGroup`, `FieldLegend`, `FieldSeparator`, `FieldSet`, `FieldContent`, `FieldTitle` | Form layout. Works with react-hook-form or plain state. |
|
|
103
106
|
| Hover card | `HoverCard`, `HoverCardTrigger`, `HoverCardContent` | |
|
|
104
|
-
| Input | `Input` | 40px, filled `bg-muted`, turns `bg-card` with a primary ring on focus.
|
|
107
|
+
| Input | `Input` | 40px, filled `bg-muted`, turns `bg-card` with a primary ring on focus. 44px under `pointer: coarse` (also `InputGroup`). |
|
|
105
108
|
| Input group | `InputGroup`, `InputGroupAddon`, `InputGroupButton`, `InputGroupText`, `InputGroupInput`, `InputGroupTextarea` | Inputs with icons, prefixes, buttons. |
|
|
106
109
|
| Input OTP | `InputOTP`, `InputOTPGroup`, `InputOTPSlot`, `InputOTPSeparator` | |
|
|
107
110
|
| Item | `Item`, `ItemMedia`, `ItemContent`, `ItemTitle`, `ItemDescription`, `ItemActions`, `ItemGroup`, `ItemSeparator`, `ItemHeader`, `ItemFooter` | Generic list rows. harebear fix: `ItemTitle` clamps to one line (long words break) and shows the full text as a tooltip when clamped, `ItemContent` has `min-w-0`. harebear adds `ItemTitle mono` for commands, paths and IDs. |
|
|
@@ -131,8 +134,8 @@ Every component in the shadcn registry is included. (shadcn's `form` item is an
|
|
|
131
134
|
| Table | `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption` | Borderless rows with rounded hover; quiet 12px headers. `data-state="selected"` tints a row. |
|
|
132
135
|
| Tabs | `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `tabsListVariants` | `TabsList variant`: `default` (segmented control), `pill` (rounded top-level nav), `line`. Use `Tabs` as a segmented filter too (date ranges, status filters). |
|
|
133
136
|
| Textarea | `Textarea` | Same filled style as `Input`, auto-grows. |
|
|
134
|
-
| Toast | `Toaster`
|
|
135
|
-
| Toggle | `Toggle`, `toggleVariants`, `ToggleGroup`, `ToggleGroupItem` |
|
|
137
|
+
| Toast | `Toaster`, `toast`, `toastUndo` | sonner. Mount once; call `toast("Saved")`, `toast.success(...)`, `toast.error(...)` (sonner's `toast`, re-exported by harebear-ui). Follows the theme mode. `toastUndo(message, { onUndo, undoLabel?, duration = 9000 })` shows an Undo toast for something already done. |
|
|
138
|
+
| Toggle | `Toggle`, `toggleVariants`, `ToggleGroup`, `ToggleGroupItem` | 44px hit area under `pointer: coarse`. |
|
|
136
139
|
| Tooltip | `TooltipProvider`, `Tooltip`, `TooltipTrigger`, `TooltipContent` | Mount `TooltipProvider` once near the root. |
|
|
137
140
|
| Trace | `Trace`, `TraceLine`, `TraceIcon`, `TraceTitle`, `TraceDescription`, `TraceCollapsible`, `TraceCollapsibleTrigger`, `TraceCollapsibleContent` | Agent step log (searched, read, ran). `TraceLine status`: `running` (shimmer), `error`, `pending`. |
|
|
138
141
|
|
|
@@ -198,7 +201,31 @@ Logo tile + name. Props: `name`, `subtitle?`, `icon?`, `action?` (trailing contr
|
|
|
198
201
|
|
|
199
202
|
### `PageHeader`
|
|
200
203
|
|
|
201
|
-
Title row
|
|
204
|
+
Title row with the page's `h1`. Props: `title`, `description?`, `count?` (badge after title), `eyebrow?` (small text above), `icon?` (leading mark before the title: `CategoryDot`, `MemberAvatar`, a lucide icon at 24px), `actions?` (right side, wraps on mobile).
|
|
205
|
+
|
|
206
|
+
### `Page`, `PageSection`
|
|
207
|
+
|
|
208
|
+
`Page`: the page column inside `AppNav` or `SidebarLayout`: centred, 16px gutters on phones (32px from 768px), children spaced `gap-7`. Props: `width?` (`sm | md | lg`, default `md` = 42rem, like `FocusLayout`), div props. `PageSection`: a region named by its `h2` (so headings go h1, h2 without skipping), with `title`, `action?` (right of the heading, 44px tall on touch screens) and `children`. Inside a card, use `Heading level={3}`.
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
;<Page>
|
|
212
|
+
<PageHeader
|
|
213
|
+
title="Groceries"
|
|
214
|
+
icon={<CategoryDot color={1} />}
|
|
215
|
+
actions={<UserMenu compact name={user.name} className="md:hidden" />}
|
|
216
|
+
/>
|
|
217
|
+
<PageSection
|
|
218
|
+
title="This week"
|
|
219
|
+
action={
|
|
220
|
+
<Button variant="ghost" size="sm">
|
|
221
|
+
See all
|
|
222
|
+
</Button>
|
|
223
|
+
}
|
|
224
|
+
>
|
|
225
|
+
<SectionedList sections={sections} onToggle={setDone} />
|
|
226
|
+
</PageSection>
|
|
227
|
+
</Page>
|
|
228
|
+
```
|
|
202
229
|
|
|
203
230
|
### `StatCard`
|
|
204
231
|
|
|
@@ -248,7 +275,7 @@ Avatar + name + subtitle. Props: `name`, `subtitle?`, `avatarUrl?`, `color?` (`C
|
|
|
248
275
|
|
|
249
276
|
### `UserMenu`
|
|
250
277
|
|
|
251
|
-
The signed-in user as a menu button (sidebar footer or top bar). The menu has a header, your items (`children`, e.g. a Settings `DropdownMenuItem`), a Light / Dark / System theme choice, and Sign out.
|
|
278
|
+
The signed-in user as a menu button (sidebar footer or top bar). The menu has a header, your items (`children`, e.g. a Settings `DropdownMenuItem`), a Light / Dark / System theme choice, and Sign out. The trigger is named by its visible name and subtitle (in a sidebar icon rail, where it is just the avatar, by `name`) and the menu opens to the side there. `compact` makes the trigger just the avatar anywhere (40px, 44px on touch screens, named "Account menu for {name}") with the menu below it, for a phone page header. Upgrading from 0.9: the non-compact trigger is no longer named "Account menu for {name}"; find it by `name` instead. Props: `name`, `subtitle?`, `avatarUrl?`, `color?` (`CategoryColor` behind the initials), `children?`, `themeSwitch?` (default true, needs `ThemeProvider`), `onSignOut?`, `signOutLabel?`, `compact?`.
|
|
252
279
|
|
|
253
280
|
```tsx
|
|
254
281
|
;<UserMenu name={email} subtitle="Signed in" onSignOut={signOut}>
|
|
@@ -307,17 +334,46 @@ Profile photo: a 72px avatar, "Upload a photo" / "Change photo" and "Remove phot
|
|
|
307
334
|
|
|
308
335
|
Controlled "are you sure" dialog. Props: `open`, `onOpenChange`, `title`, `description?`, `confirmLabel?`, `cancelLabel?`, `destructive?` (red confirm), `onConfirm`.
|
|
309
336
|
|
|
337
|
+
### `PromptDialog`
|
|
338
|
+
|
|
339
|
+
Controlled dialog that asks for one line of text (New list, Rename). Props: `open`, `onOpenChange`, `title`, `description?`, `label` (field label), `defaultValue?`, `submitLabel?` (default "Save"), `cancelLabel?` (default "Cancel"), `maxLength?`, `onSubmit(value): void | Promise<void>` (trimmed; empty is blocked). It closes when `onSubmit` returns or resolves and starts fresh on every open; a thrown error's message shows under the field and the dialog stays open.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
;<PromptDialog
|
|
343
|
+
open={renaming}
|
|
344
|
+
onOpenChange={setRenaming}
|
|
345
|
+
title="Rename list"
|
|
346
|
+
label="Name"
|
|
347
|
+
defaultValue={list.name}
|
|
348
|
+
submitLabel="Rename"
|
|
349
|
+
maxLength={60}
|
|
350
|
+
onSubmit={(name) => api.renameList(list.id, name)}
|
|
351
|
+
/>
|
|
352
|
+
```
|
|
353
|
+
|
|
310
354
|
### `CopyField`
|
|
311
355
|
|
|
312
|
-
Read-only value with a copy button (IDs, paths, URLs). Props: `value` (copied), `display?` (shown instead, full value as tooltip), `label?` (leading addon), `aria-label?`, `mono?` (default true).
|
|
356
|
+
Read-only value with a copy button (IDs, paths, URLs). Props: `value` (copied), `display?` (shown instead, full value as tooltip), `label?` (leading addon), `aria-label?`, `mono?` (default true), `copiedLabel?` (default "Copied"; after copying, the button's name and a label beside it read this for a moment).
|
|
313
357
|
|
|
314
358
|
### `DateTimePicker`
|
|
315
359
|
|
|
316
360
|
Date field: a button that opens a `Calendar` in a `Popover`, with an optional time field and a Clear button. Value is a floating local date, no time zone: `DateTimeValue = { date: "YYYY-MM-DD", time?: "HH:MM" }`. Props: `value: DateTimeValue | null`, `onValueChange(value | null)`, `withTime?` (time field under the calendar; enabled once a date is set), `placeholder?` (default "Pick a date"). Other props (`id`, `aria-label`, `disabled`) go to the trigger button. Day cells are 44px on phones.
|
|
317
361
|
|
|
362
|
+
### `TimeZonePicker`
|
|
363
|
+
|
|
364
|
+
Searchable IANA time zone field built on `Combobox`, listing `Intl.supportedValuesOf("timeZone")` (underscores shown as spaces, so "new york" matches). A `value` not in that list (an alias such as "US/Pacific") is kept at the top so it still shows. Props: `value` (e.g. `"America/Vancouver"`), `onValueChange(zone)`, `id?` (for a `FieldLabel` or `SettingsRow` `htmlFor`), `className?`.
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
;<SettingsRow
|
|
368
|
+
label="Time zone"
|
|
369
|
+
htmlFor="tz"
|
|
370
|
+
control={<TimeZonePicker id="tz" value={me.timeZone} onValueChange={(timeZone) => save({ timeZone })} />}
|
|
371
|
+
/>
|
|
372
|
+
```
|
|
373
|
+
|
|
318
374
|
### `DetailSheet`
|
|
319
375
|
|
|
320
|
-
A record's details, one API for both sizes: a floating side sheet from 768px up, a full-screen drawer with a close button on phones. Props: `open`, `onOpenChange`, `title` (heading and accessible name), `description?`, `footer?` (pinned under the scrolling body, e.g. Delete), `children` (the fields). `data-layout` is `"sheet"` or `"drawer"`.
|
|
376
|
+
A record's details, one API for both sizes: a floating side sheet from 768px up, a full-screen drawer with a close button on phones. Props: `open`, `onOpenChange`, `title` (heading and accessible name), `description?`, `footer?` (pinned under the scrolling body, e.g. Delete), `children` (the fields). `data-layout` is `"sheet"` or `"drawer"`. While open, toasts (e.g. `toastUndo`) stay off it: on desktop they move to the left of the sheet and narrow to fit; on phones they sit above the footer (one row of buttons) or the bottom safe area, in place of the hidden tab bar's space.
|
|
321
377
|
|
|
322
378
|
```tsx
|
|
323
379
|
;<DetailSheet
|
|
@@ -351,23 +407,49 @@ Muted tile that lifts on hover, for service launchers. Props: `title`, `subtitle
|
|
|
351
407
|
|
|
352
408
|
### `MarkdownEditor`
|
|
353
409
|
|
|
354
|
-
Markdown field with Write and Preview tabs; Preview renders
|
|
410
|
+
Markdown field with Write and Preview tabs; Preview renders with `Markdown`. Props: `value`, `onValueChange(value)`, `renderPreview?(markdown)` (custom renderer, wrapped in `Prose`), `wrapperClassName?`. Other props (`id`, `placeholder`, `aria-*`, `className`) go to the `Textarea`.
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
;<MarkdownEditor id="body" value={body} onValueChange={setBody} />
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### `ChecklistEditor`
|
|
417
|
+
|
|
418
|
+
Steps inside a record, e.g. a to-do's checklist: `CheckCircle` per item (the text is its label), a Remove button, an add field (Enter adds the trimmed text and clears), and an "x of y done" line when there are items. Props: `items: { id; text; done }[]`, `onToggle(id, done)`, `onRemove(id)`, `onAdd(text)`, `addPlaceholder?` (also the field's name, default "Add an item"), `progressLabel?(done, total)`. The root is a `role="group"`: name it with `aria-label` or `aria-labelledby`.
|
|
355
419
|
|
|
356
420
|
```tsx
|
|
357
|
-
;<
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
421
|
+
;<ChecklistEditor
|
|
422
|
+
aria-label="Checklist"
|
|
423
|
+
items={task.checklist}
|
|
424
|
+
onToggle={toggleStep}
|
|
425
|
+
onRemove={removeStep}
|
|
426
|
+
onAdd={addStep}
|
|
427
|
+
addPlaceholder="Add a step"
|
|
362
428
|
/>
|
|
363
429
|
```
|
|
364
430
|
|
|
431
|
+
### `CommentThread`
|
|
432
|
+
|
|
433
|
+
Comments on a record, oldest first, then a box and a submit button (disabled while empty; submits the trimmed text and clears). Bodies render with `Markdown`. Props: `comments: { id; author: { name; color?; avatarUrl? }; meta?; body }[]` (`meta` is muted text after the name, e.g. "via HomeBot · 2 h ago"), `onSubmit(text)`, `placeholder?` (also the box's name, default "Add a comment"), `submitLabel?` (default "Comment"). Show your own empty state if you want one.
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
;<CommentThread comments={task.comments} onSubmit={(text) => api.comment(task.id, text)} />
|
|
437
|
+
```
|
|
438
|
+
|
|
365
439
|
### `AppNav`
|
|
366
440
|
|
|
367
441
|
One nav config, two renderings: on desktop a `SidebarLayout` sidebar (brand, action button, places, one collapsible group, footer; active place is a raised card; collapses to the icon rail), under 768px a bottom tab bar (first 5 places, 56px targets, `aria-current="page"`) plus a round floating action button. Use it instead of `AppShell` for consumer apps that need phone navigation.
|
|
368
442
|
|
|
369
|
-
Props: `places: AppNavPlace[]` (`label`, `icon?`, `href?` or `onSelect?`, `active?`, `count?`, `tab?: false` to keep out of the tab bar, `sidebar?: false` for a tab-bar-only place such as Lists when the sidebar has a Lists group), `group?: AppNavGroup` (`label`, `items`, `defaultOpen?`, `onAdd?`, `addLabel?`), `action?: { label; icon; onSelect; sidebar?: false }` (`sidebar: false` keeps only the round phone button), `brand?`, `footer?` (usually `UserMenu`), `label?` (nav name, default "Main"), `linkComponent?`, `collapsible?`, `storageKey?`, `defaultOpen?`, `width?`, `children`.
|
|
370
|
-
On phones the group and footer live in the sidebar sheet: put a `SidebarTrigger` in the page header if users need them there. Render `AssistantDock` inside `children` so it clears the tab bar. Toasts stack above the tab bar, the action button and the dock launcher on their own; no `Toaster` offset needed.
|
|
443
|
+
Props: `places: AppNavPlace[]` (`label`, `icon?`, `href?` or `onSelect?`, `active?`, `count?`, `tab?: false` to keep out of the tab bar, `sidebar?: false` for a tab-bar-only place such as Lists when the sidebar has a Lists group), `group?: AppNavGroup` (`label`, `href?` (heading becomes a link, e.g. to all lists; the chevron is a separate "Collapse Lists" button), `active?` (the heading link is the current page), `items`, `defaultOpen?`, `onAdd?`, `addLabel?`), `action?: { label; icon; onSelect; sidebar?: false }` (`sidebar: false` keeps only the round phone button), `brand?`, `footer?` (usually `UserMenu`), `label?` (nav name, default "Main"), `linkComponent?`, `collapsible?`, `storageKey?`, `defaultOpen?`, `width?`, `fill?` (main column is exactly the screen height minus the tab bar (and the round action button's space when `action` is set) and does not scroll, for a chat: the child takes it with `flex-1 min-h-0`, which `ChatPanel` already has), `children`.
|
|
444
|
+
On phones the group and footer live in the sidebar sheet: put a `SidebarTrigger` in the page header if users need them there. With `action`, the main column's bottom padding also clears the round button, so a trailing control on the last row is not under it. Render `AssistantDock` inside `children` so it clears the tab bar. Toasts stack above the tab bar, the action button and the dock launcher on their own; no `Toaster` offset needed.
|
|
445
|
+
|
|
446
|
+
```tsx
|
|
447
|
+
;<AppNav fill places={places} footer={<UserMenu name={user.name} />}>
|
|
448
|
+
<ChatPanel title="Assistant" composer={<PromptInput … />}>
|
|
449
|
+
{thread}
|
|
450
|
+
</ChatPanel>
|
|
451
|
+
</AppNav>
|
|
452
|
+
```
|
|
371
453
|
|
|
372
454
|
### `FocusLayout`
|
|
373
455
|
|
|
@@ -375,7 +457,7 @@ A page outside the app frame (settings, onboarding, setup): slim sticky top bar,
|
|
|
375
457
|
|
|
376
458
|
### `SettingsSection`, `SettingsRow`
|
|
377
459
|
|
|
378
|
-
`SettingsSection`: card with `title`, `description?` and rows divided by hairlines (a labelled `region`). `SettingsRow`: `label`, `description?`, `control?` (Switch, Select, ToggleGroup, Button), `htmlFor?` (makes the label a `<label>` for the control). Add `content` to expand inline (change password, add a connection): without a `control` the row gets a "Change"/"Cancel" button (`expandLabel?`); control it with `open`/`onOpenChange` or `defaultOpen`.
|
|
460
|
+
`SettingsSection`: card with `title`, `description?` and rows divided by hairlines (a labelled `region`). `SettingsRow`: `label`, `description?`, `control?` (Switch, Select, ToggleGroup, Button), `htmlFor?` (makes the label a `<label>` for the control), `body?` (always shown under the label, full width, no toggle: a list, a hint, a short form). Add `content` to expand inline (change password, add a connection): without a `control` the row gets a "Change"/"Cancel" button (`expandLabel?`); control it with `open`/`onOpenChange` or `defaultOpen`.
|
|
379
461
|
|
|
380
462
|
```tsx
|
|
381
463
|
;<FocusLayout
|
|
@@ -396,6 +478,7 @@ A page outside the app frame (settings, onboarding, setup): slim sticky top bar,
|
|
|
396
478
|
control={<Switch id="daily" />}
|
|
397
479
|
/>
|
|
398
480
|
<SettingsRow label="Password" content={<ChangePasswordForm />} />
|
|
481
|
+
<SettingsRow label="Connections" body={<ConnectionList />} />
|
|
399
482
|
</SettingsSection>
|
|
400
483
|
</FocusLayout>
|
|
401
484
|
```
|
|
@@ -410,7 +493,7 @@ Props instead of class names, for blocks that should look right without styling
|
|
|
410
493
|
|
|
411
494
|
### `Text`, `Heading`
|
|
412
495
|
|
|
413
|
-
`Text` takes `size` (`xs | sm | base | lg | xl | 2xl`), `tone` (`default | muted | success | warning | danger | info`), `weight`, `mono`, `truncate`, `lines` (1 to 3) and `tabular`, and renders `as` a span, p or div. `Heading` is a card section title (`level={3}
|
|
496
|
+
`Text` takes `size` (`xs | sm | base | lg | xl | 2xl`), `tone` (`default | muted | success | warning | danger | info`), `weight`, `mono`, `truncate`, `lines` (1 to 3) and `tabular`, and renders `as` a span, p or div. `Heading` is a page section title (`level={2}`, under the page `h1`; `PageSection` uses it), a card section title (`level={3}`, default) or a small uppercase group label (`level={4}`). Do not skip levels: a page with an h1 needs h2s before h3s.
|
|
414
497
|
|
|
415
498
|
### `Metric`, `MetricGroup`
|
|
416
499
|
|
|
@@ -426,7 +509,7 @@ Shows the first `initial` children (default 5) and a "Show N more" button for th
|
|
|
426
509
|
|
|
427
510
|
### `Collapse`
|
|
428
511
|
|
|
429
|
-
Animates children between full height and 0 (CSS `grid-template-rows`), so neighbours slide instead of jumping. Closed content is `inert` and `aria-hidden`. Props: `open`, `contentClassName?`. No motion under `prefers-reduced-motion`.
|
|
512
|
+
Animates children between full height and 0 (CSS `grid-template-rows`), so neighbours slide instead of jumping. Closed content is `inert` and `aria-hidden`. Clips vertically only, so focus rings and hit areas at the sides are not cut off. Props: `open`, `contentClassName?`. No motion under `prefers-reduced-motion`.
|
|
430
513
|
|
|
431
514
|
## Data display
|
|
432
515
|
|
|
@@ -500,11 +583,12 @@ One line of a day's agenda: time column, event icon (or a `CheckCircle` for a to
|
|
|
500
583
|
|
|
501
584
|
### `DayStrip`
|
|
502
585
|
|
|
503
|
-
A row of day links (short weekday, date, a dot when `hasPlans`) that smooth-scroll to the element whose id is the day's `id` (instant under reduced motion) and move focus to it; scrolls sideways when the days do not fit. Props: `days: { id; date: "YYYY-MM-DD"; hasPlans? }[]` (floating local date, like `DateTimePicker`), `value?` (selected id, `aria-current`), `onValueChange?(id)`, `aria-label?` (default "Jump to a day")
|
|
586
|
+
A row of day links (short weekday, date, a dot when `hasPlans`) that smooth-scroll to the element whose id is the day's `id` (instant under reduced motion) and move focus to it; scrolls sideways when the days do not fit. Props: `days: { id; date: "YYYY-MM-DD"; hasPlans? }[]` (floating local date, like `DateTimePicker`), `value?` (selected id, `aria-current`), `onValueChange?(id)`, `aria-label?` (default "Jump to a day"), `sticky?` (sticks at `top-0` with the page background while the page or any container scrolls, and gives the jumped-to element a `scroll-margin-top` as tall as the strip so its heading lands below it; add `className="top-14"` under a sticky header). Each link's name is the full date ("Thursday, November 12, has plans") as visually hidden text, so it contains what is shown.
|
|
504
587
|
|
|
505
588
|
```tsx
|
|
506
589
|
;<>
|
|
507
590
|
<DayStrip
|
|
591
|
+
sticky
|
|
508
592
|
days={week.map((d) => ({ id: d.key, date: d.date, hasPlans: d.items.length > 0 }))}
|
|
509
593
|
value={day}
|
|
510
594
|
onValueChange={setDay}
|
|
@@ -567,7 +651,7 @@ Scrollable log that sticks to the bottom as content streams in (unless the user
|
|
|
567
651
|
|
|
568
652
|
### `ChatMessage`
|
|
569
653
|
|
|
570
|
-
One turn. Props: `role: "user" | "assistant"` (matches AI SDK message roles), `avatar?` (assistant only, defaults to `AssistantAvatar`; `false` for none and full-width content, for agent threads where one turn is many blocks), `actions?` (assistant only, under the content), `variant?` (user bubble: `"muted"` default or `"soft"` accent tint), `children`. Long words wrap.
|
|
654
|
+
One turn. Props: `role: "user" | "assistant"` (matches AI SDK message roles), `avatar?` (assistant only, defaults to `AssistantAvatar`; `false` for none and full-width content, for agent threads where one turn is many blocks), `actions?` (assistant only, under the content), `variant?` (user bubble: `"muted"` default or `"soft"` accent tint), `children`. Long words wrap, in assistant content too.
|
|
571
655
|
User turns render as a right-aligned bubble with preserved newlines. Assistant turns render free-flowing: put `Prose`, `CodeBlock`, `ToolCall`, `TypingIndicator` inside.
|
|
572
656
|
|
|
573
657
|
```tsx
|
|
@@ -638,9 +722,9 @@ Three pulsing dots. Props: `label?` (text after the dots, e.g. "Working"), `vari
|
|
|
638
722
|
|
|
639
723
|
### `ToolGroup`, `ToolStep`, `ToolStatusIcon`
|
|
640
724
|
|
|
641
|
-
Agent tool calls as a bordered, collapsible card. `ToolStatus = "queued" | "waiting" | "running" | "done" | "error" | "stopped"`.
|
|
725
|
+
Agent tool calls as a bordered, collapsible card. Titles, labels and details wrap instead of cutting off. `ToolStatus = "queued" | "waiting" | "running" | "done" | "error" | "stopped"`.
|
|
642
726
|
`ToolGroup` props: `title`, `status`, `meta?` (e.g. time), `autoOpen?` (default open while active, until the user toggles), `open?`/`onOpenChange?`.
|
|
643
|
-
`ToolStep` props: `icon?`, `kind` (verb column), `detail` (monospace command or path), `status`, `meta?` (default from status), `output?` (monospace, shown when expanded; omit for a fixed row), `autoOpen?` (default open while running).
|
|
727
|
+
`ToolStep` props: `icon?`, `kind` (verb column; with an empty `detail` it is the whole label, for plain-language steps such as "Added milk"), `detail` (monospace command or path, or `""`), `status`, `meta?` (default from status), `output?` (monospace, shown when expanded; omit for a fixed row), `autoOpen?` (default open while running).
|
|
644
728
|
`ToolStatusIcon`: spinner while running, else a toned dot. Lighter alternative: `Trace`.
|
|
645
729
|
|
|
646
730
|
```tsx
|
|
@@ -652,7 +736,7 @@ Agent tool calls as a bordered, collapsible card. `ToolStatus = "queued" | "wait
|
|
|
652
736
|
### `ApprovalCard`, `ApprovalResult`
|
|
653
737
|
|
|
654
738
|
`ApprovalCard`: warning-ringed card for anything the agent waits on the user for. Props: `title`, `description?`, `icon?` (default shield, `null` for none), `reason?` (shown first after a bold "Why:"), `code?` + `codeLabel?` (wrapped code block with copy), `actions?` (buttons), `footer?`, `children` (more body).
|
|
655
|
-
`ApprovalResult`:
|
|
739
|
+
`ApprovalResult`: short record of a decision in the thread; wraps when long. Props: `decision: "approved" | "denied"`, `time?`, `children`.
|
|
656
740
|
|
|
657
741
|
### `AssistantProvider`, `useAssistant`, `useAssistantPage`
|
|
658
742
|
|
|
@@ -660,7 +744,7 @@ One assistant conversation across pages. `AssistantProvider` props: `onSend(text
|
|
|
660
744
|
|
|
661
745
|
### `AssistantDock`
|
|
662
746
|
|
|
663
|
-
The assistant as a floating panel over the page (never reflows it): header (icon, title, full-screen link, close), empty state with suggestions, thread, `AskBox` (Stop while the assistant works, wired to `useAssistant().stop`). Closed, it is a soft launcher button. Escape closes and returns focus to the launcher. Props: `title?` (default "Assistant"), `icon?`, `fullScreenHref?`, `emptyTitle?`, `emptyDescription?`, `suggestions?` (default: the page's), `placeholder?`, `dictation?`, `launcher?` (false when another control opens it), `launcherLabel?`.
|
|
747
|
+
The assistant as a floating panel over the page (never reflows it): header (icon, title, your `actions`, full-screen link, close), empty state with suggestions, thread, `AskBox` (Stop while the assistant works, wired to `useAssistant().stop`). Closed, it is a soft launcher button. Escape closes and returns focus to the launcher. Props: `title?` (default "Assistant"), `icon?`, `fullScreenHref?`, `actions?` (header controls before the full-screen link, e.g. a "New chat" icon button with an `aria-label`), `emptyTitle?`, `emptyDescription?`, `suggestions?` (default: the page's), `placeholder?`, `dictation?`, `launcher?` (false when another control opens it), `launcherLabel?`.
|
|
664
748
|
|
|
665
749
|
### `AskBox`
|
|
666
750
|
|
|
@@ -690,14 +774,22 @@ const { send, pending, stop, reply } = useAssistant()
|
|
|
690
774
|
|
|
691
775
|
### `ActionReceipt`
|
|
692
776
|
|
|
693
|
-
What the assistant did, as a compact card: round check, `title` (past tense), `meta
|
|
777
|
+
What the assistant did, as a compact card: round check, `title` (past tense), `meta?` (wraps when long), Undo (`onUndo?`, `undoLabel?`). `undone` strikes it through and shows "Undone".
|
|
694
778
|
|
|
695
779
|
### `CodeSnippet`, `InlineCode`, `Prose`, `ProseBlockquote`
|
|
696
780
|
|
|
697
781
|
`CodeSnippet`: one-prop code panel with language label, copy button and shiki syntax highlighting (each language loads on first use). Props: `code`, `language?` (`bash`, `tsx`, `ts`, `js`, `json`, `python`, `sql`, `yaml`, `css`, `html`, `markdown`, `diff`; others render plain), `highlight?` (default true), `lineNumbers?`, `hideCopy?`. Built on the `CodeBlock` primitives; drop to those for diffs or highlighted lines.
|
|
698
782
|
`InlineCode`: monospace span. `truncate?: number` cuts longer string children with an ellipsis and puts the full text in the tooltip.
|
|
699
|
-
`Prose`: typography for model output and markdown. Styles plain `p, ul, ol, li, strong, a, code, pre, h1-h4, blockquote, table` children
|
|
700
|
-
Tip boxes: a blockquote whose first word is `Tip:` (`> Tip: Turn the power off first.`) renders as a `Callout` without the word. Prose does this for plain `<blockquote>` children; with react-markdown pass `components={{ blockquote: ProseBlockquote }}`.
|
|
783
|
+
`Prose`: typography for model output and markdown. Styles plain `p, ul, ol, li, strong, a, code, pre, h1-h4, blockquote, table` children.
|
|
784
|
+
Tip boxes: a blockquote whose first word is `Tip:` (`> Tip: Turn the power off first.`) renders as a `Callout` without the word. Prose does this for plain `<blockquote>` children; with your own react-markdown pass `components={{ blockquote: ProseBlockquote }}`.
|
|
785
|
+
|
|
786
|
+
### `Markdown`
|
|
787
|
+
|
|
788
|
+
Renders a Markdown string inside `Prose`: GitHub flavoured (tables, task lists, strikethrough, autolinks), `> Tip:` blockquotes as a `Callout`. Safe for user and model text: raw HTML is dropped and `javascript:` links are removed. Props: `children: string`, `linkComponent?` (your router `Link`, gets `href`; used for links without a scheme such as `/lists/2`). Other props go to the `Prose` wrapper.
|
|
789
|
+
|
|
790
|
+
```tsx
|
|
791
|
+
;<Markdown linkComponent={Link}>{message.text}</Markdown>
|
|
792
|
+
```
|
|
701
793
|
|
|
702
794
|
### Chat primitives vs. composites
|
|
703
795
|
|
|
@@ -711,17 +803,49 @@ Parameterized versions of shadcn's login-01..05 and signup-01..05 blocks.
|
|
|
711
803
|
|
|
712
804
|
Full-page wrapper. Props: `variant` (`"centered"` card on the page, `"muted"` same on a muted background, `"split"` form left and cover image right, `"card-image"` one wide card with form and image), `brand?` (usually `AppBrand`), `image?: { src; alt }` (split and card-image), `footer?` (terms text), `children` (the form).
|
|
713
805
|
|
|
806
|
+
### `AuthForm`
|
|
807
|
+
|
|
808
|
+
The shell the auth forms share; use it for other signed-out steps (join with a code, verify an email). Props: `title`, `description?`, `children` (fields, each with a `name`), `error?`, `submitLabel`, `loading?` (spinner, disables every control), `onSubmit?(data: FormData)` (after browser validation, which `noValidate` skips; omit to submit natively), `footer?` (under the button: links, provider buttons).
|
|
809
|
+
|
|
810
|
+
```tsx
|
|
811
|
+
;<AuthForm title="Join your household" submitLabel="Join" onSubmit={(d) => join(String(d.get("code")))}>
|
|
812
|
+
<Field>
|
|
813
|
+
<FieldLabel htmlFor="code">Invite code</FieldLabel>
|
|
814
|
+
<Input id="code" name="code" required />
|
|
815
|
+
</Field>
|
|
816
|
+
</AuthForm>
|
|
817
|
+
```
|
|
818
|
+
|
|
714
819
|
### `LoginForm`
|
|
715
820
|
|
|
716
821
|
Props: `onSubmit?({ email, password? })` (omit to submit natively to an `action`), `mode?` (`"password"` or `"email-only"` for magic links), `providers?: { id; label; icon? }[]` + `onProviderClick?(id)` (3+ providers with icons collapse to an icon row), `providersFirst?`, `forgotPasswordHref?`, `signupHref?`, `title?`, `description?`, `submitLabel?`, `loading?`, `error?`.
|
|
717
822
|
|
|
718
823
|
### `PasswordInput`
|
|
719
824
|
|
|
720
|
-
Password `Input` with a Show/Hide button (`aria-label` "Show password" / "Hide password") that switches `type` between `password` and `text`. Input props go to the input; `wrapperClassName?` styles the wrapper. `LoginForm` and `
|
|
825
|
+
Password `Input` with a Show/Hide button (`aria-label` "Show password" / "Hide password") that switches `type` between `password` and `text`. Input props go to the input; `wrapperClassName?` styles the wrapper. `LoginForm`, `SignupForm` and `NewPasswordForm` use it for every password field.
|
|
721
826
|
|
|
722
827
|
### `SignupForm`
|
|
723
828
|
|
|
724
|
-
Props: `onSubmit?({ name?, email, password })`, `nameField?` (default true), `confirmPassword?` (default true; mismatches are blocked with a browser validation message), `minPasswordLength?` (default 8), `passwordHint?`, `providers?`, `onProviderClick?`, `providersFirst?`, `loginHref?`, `title?`, `description?`, `submitLabel?`, `loading?`, `error?`.
|
|
829
|
+
Props: `onSubmit?({ name?, email, password }, data: FormData)`, `children?` (extra fields before the name, e.g. a household name; read them from `data`), `nameField?` (default true), `confirmPassword?` (default true; mismatches are blocked with a browser validation message), `minPasswordLength?` (default 8), `passwordHint?`, `providers?`, `onProviderClick?`, `providersFirst?`, `loginHref?`, `title?`, `description?`, `submitLabel?`, `loading?`, `error?`.
|
|
830
|
+
|
|
831
|
+
```tsx
|
|
832
|
+
;<SignupForm onSubmit={(v, data) => create({ ...v, household: String(data.get("household")) })}>
|
|
833
|
+
<Field>
|
|
834
|
+
<FieldLabel htmlFor="household">Household name</FieldLabel>
|
|
835
|
+
<Input id="household" name="household" required />
|
|
836
|
+
</Field>
|
|
837
|
+
</SignupForm>
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### `NewPasswordForm`
|
|
841
|
+
|
|
842
|
+
Reset or first password: "New password" + "Confirm password" (a mismatch is blocked with a browser validation message). Props: `onSubmit?({ password })`, `minPasswordLength?` (default 8), `passwordHint?`, `title?` (default "Choose a new password"), `description?`, `submitLabel?` (default "Save password"), `loading?`, `error?`.
|
|
843
|
+
|
|
844
|
+
```tsx
|
|
845
|
+
;<AuthLayout variant="centered">
|
|
846
|
+
<NewPasswordForm onSubmit={({ password }) => reset(token, password)} loading={pending} error={error} />
|
|
847
|
+
</AuthLayout>
|
|
848
|
+
```
|
|
725
849
|
|
|
726
850
|
`GitHubIcon`, `GoogleIcon`, `AppleIcon`: brand marks for provider buttons.
|
|
727
851
|
|
package/docs/ROADMAP.md
CHANGED
|
@@ -33,6 +33,7 @@ Calendar views and SmartInput follow when a second app needs them.
|
|
|
33
33
|
| :----------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------- |
|
|
34
34
|
| AppNav (built) | Takes one config (places, a collapsible group such as Lists, an "add" action, the profile menu). Renders the desktop sidebar (no border, active item as a raised card, profile popover at the bottom, sticky on long pages) and, under a breakpoint, a bottom tab bar with one round action button. | Extends SidebarLayout and UserMenu. The phone half is new. |
|
|
35
35
|
| FocusLayout (built) | A page outside the app frame: slim top bar with a close or back link and a title, then one centred column. | Settings, onboarding, setup flows. |
|
|
36
|
+
| Page, PageSection (built) | The page column inside the app frame (centred, 16px phone gutters) and titled h2 regions under the PageHeader h1. | Replaces per-app page wrappers; keeps heading order. |
|
|
36
37
|
| SettingsSection, SettingsRow (built) | Card with a heading; rows with label, one-line description and a control slot (switch, select, segmented control, button). Rows can expand inline (change password, add a connection). | Most settings pages become composition only. |
|
|
37
38
|
| DetailSheet (built) | A record's details: side sheet on desktop, full-screen sheet on a phone, with a close button and a scrollable body. | Extends Sheet and Drawer with one responsive API. |
|
|
38
39
|
| ReaderLayout | Back link, title, author and updated line, actions at the top (Edit, Chat with HomeBot), then Prose. | Docs, help, notes. |
|
|
@@ -47,7 +48,7 @@ Calendar views and SmartInput follow when a second app needs them.
|
|
|
47
48
|
| AssistantDock (built) | Floating panel over the page (never reflows it) with header, empty state, thread, receipts and composer; a soft launcher button when closed; a link to full screen. Same on every page; pages pass suggestions only. | Extends ChatShell. |
|
|
48
49
|
| AskBox (built) | Large rounded input with chips that appear as you type, suggestion pills under it, and the dictation button. Focus ring on the box, not the field. | One placeholder per app ("Ask HomeBot anything"). |
|
|
49
50
|
| DictationButton, `useDictation` (built) | One button that is a microphone when empty, a stop button while listening (with listening bars and a live region), and send once there is text. The speech source plugs in (browser speech recognition, a server model). | Off-animation under reduced motion. |
|
|
50
|
-
| ActionReceipt (built) | What the assistant did, as a compact card: round check, title,
|
|
51
|
+
| ActionReceipt (built) | What the assistant did, as a compact card: round check, title, meta, Undo. One per action. | The core of "safe actions run on their own". |
|
|
51
52
|
| WhatIDid | Collapsed list of plain past-tense steps under an answer. | A plain-language variant of ToolSteps. |
|
|
52
53
|
| ProgressCard | A long-running job: title, status word, progress bar, time left, an action when finished. | Downloads, imports, syncs. |
|
|
53
54
|
| ChatsHome | Recent chats grouped Today, This week, Earlier, with search and a row menu (Rename, Delete with inline confirm). | Extends ConversationList. |
|
package/llms.txt
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
- Use harebear-ui components before writing new UI. Primitives keep shadcn/ui names and props.
|
|
15
15
|
- Style with theme tokens only: bg-background, bg-card, bg-muted, text-muted-foreground, bg-primary, bg-primary-soft, text-success, text-warning, text-destructive, border-border, shadow-card, rounded-md/lg/xl. No hex colors or Tailwind palette colors.
|
|
16
16
|
- Status is a tone: success | warning | danger | info | neutral. Members, lists and calendars use category-1..6 (bg-category-3, or `color={3}` on MemberAvatar, CategoryDot, ColorSwatchPicker), never status colors.
|
|
17
|
-
- Agent-first apps (assistant on every page, undoable actions): AppNav + AssistantProvider/AssistantDock + SectionedList + toastUndo + FocusLayout/SettingsSection. A page's own AskBox answers inline with `reply` from useAssistant and stops with `pending` + `onStop={stop}`. Recipe: `npx harebear-ui docs "Agent-first apps"`.
|
|
17
|
+
- Agent-first apps (assistant on every page, undoable actions): AppNav + Page/PageHeader/PageSection + AssistantProvider/AssistantDock + SectionedList + toastUndo + FocusLayout/SettingsSection. A page's own AskBox answers inline with `reply` from useAssistant and stops with `pending` + `onStop={stop}`. Recipe: `npx harebear-ui docs "Agent-first apps"`.
|
|
18
18
|
- Icons: lucide-react elements, e.g. `<SearchIcon />`. Icon-only buttons need aria-label.
|
|
19
|
+
- Touch: Button, Toggle and Input already meet 44px under `pointer: coarse` (buttons via an invisible ::after, which overflow-hidden ancestors clip: there, size the button itself with pointer-coarse:size-11).
|
|
19
20
|
- Change the brand with `<ThemeProvider accent="#hex" radius={14}>` or `--brand` / `--radius` in CSS.
|
|
20
21
|
|
|
21
22
|
## Docs
|
|
@@ -32,14 +33,14 @@
|
|
|
32
33
|
## Component index
|
|
33
34
|
|
|
34
35
|
Theme: ThemeProvider, useTheme, ThemeToggle, ThemeScript
|
|
35
|
-
Layout/admin: SidebarLayout, SidebarCollapseButton, AppShell, AppNav, FocusLayout, SettingsSection, SettingsRow, AppBrand, PageHeader, StatCard, SearchInput, StatusDot, StatusBadge, UsageMeter, ActivityFeed, Timeline, KeyValueGrid, UserChip, UserMenu, MemberAvatar, AvatarStack, VisibilityBadge, CategoryDot, ColorSwatchPicker, AvatarUpload, Callout, LinkTile, ConfirmDialog, CopyField, DetailSheet
|
|
36
|
+
Layout/admin: SidebarLayout, SidebarCollapseButton, AppShell, AppNav, FocusLayout, SettingsSection, SettingsRow, AppBrand, PageHeader, Page, PageSection, StatCard, SearchInput, StatusDot, StatusBadge, UsageMeter, ActivityFeed, Timeline, KeyValueGrid, UserChip, UserMenu, MemberAvatar, AvatarStack, VisibilityBadge, CategoryDot, ColorSwatchPicker, AvatarUpload, Callout, LinkTile, ConfirmDialog, PromptDialog, CopyField, DetailSheet
|
|
36
37
|
Layout and text: Stack, Row, Grid, Text, Heading, Metric, MetricGroup, MediaItem, ShowMore, Collapse
|
|
37
38
|
Data: TaskRow, SectionedList, AgendaRow, DayStrip, DataTable, createDataTableColumnHelper, TrendChart, TrendLegend, BarList, Sparkline, StatusBars
|
|
38
39
|
Assistant layer: AssistantProvider, useAssistant, useAssistantPage, AssistantDock, AskBox, DictationButton, useDictation, browserSpeechSource, ActionReceipt
|
|
39
|
-
AI/chat composites: ChatShell, ChatPanel, ChatThread, ChatMessage, AssistantAvatar, MessageActions, PromptInput, PromptInputChip, PromptInputSelect, ModelPicker, ConversationList, NewConversationButton, groupByDate, formatWhen, ToolCall, ToolGroup, ToolStep, ToolStatusIcon, ApprovalCard, ApprovalResult, TypingIndicator, CodeSnippet, InlineCode, Prose, ProseBlockquote
|
|
40
|
+
AI/chat composites: ChatShell, ChatPanel, ChatThread, ChatMessage, AssistantAvatar, MessageActions, PromptInput, PromptInputChip, PromptInputSelect, ModelPicker, ConversationList, NewConversationButton, groupByDate, formatWhen, ToolCall, ToolGroup, ToolStep, ToolStatusIcon, ApprovalCard, ApprovalResult, TypingIndicator, CodeSnippet, InlineCode, Prose, ProseBlockquote, Markdown
|
|
40
41
|
AI/chat primitives (shadcn): Message, Bubble, MessageScroller, Attachment, Trace, Marker, Questionnaire, CodeBlock
|
|
41
|
-
Auth: AuthLayout, LoginForm, SignupForm, GitHubIcon, GoogleIcon, AppleIcon
|
|
42
|
-
Forms: PasswordInput, DateTimePicker, MarkdownEditor
|
|
43
|
-
Feedback: Toaster, toastUndo, CheckCircle, Celebration, celebrate
|
|
42
|
+
Auth: AuthLayout, AuthForm, LoginForm, SignupForm, NewPasswordForm, GitHubIcon, GoogleIcon, AppleIcon
|
|
43
|
+
Forms: PasswordInput, DateTimePicker, TimeZonePicker, MarkdownEditor, ChecklistEditor, CommentThread
|
|
44
|
+
Feedback: Toaster, toast, toastUndo, CheckCircle, Celebration, celebrate
|
|
44
45
|
Primitives (the full shadcn/ui registry): Accordion, Alert, AlertDialog, AspectRatio, Attachment, Avatar, Badge, Breadcrumb, Bubble, Button, ButtonGroup, Calendar, Card, Carousel, Chart, Checkbox, CodeBlock, Collapsible, Combobox, Command, ContextMenu, Dialog, DirectionProvider, Drawer, DropdownMenu, Empty, Field, HoverCard, Input, InputGroup, InputOTP, Item, Kbd, Label, Marker, Menubar, Message, MessageScroller, NativeSelect, NavigationMenu, Pagination, Popover, Progress, Questionnaire, RadioGroup, Resizable, ScrollArea, Select, Separator, Sheet, Sidebar, Skeleton, Slider, Spinner, Switch, Table, Tabs, Textarea, Toaster, Toggle, ToggleGroup, Tooltip, Trace
|
|
45
46
|
Hooks/utils: categoryBg, cropImageToSquare, useCopyToClipboard, useHighlightedCode, useIsMobile, useMediaQuery, useSidebarOptional, cn
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harebear-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Opinionated, pre-themed shadcn/ui component library for React 19 + Tailwind CSS v4. Admin, data, and AI chat building blocks, ready for agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Harrison Milbradt",
|
|
@@ -89,8 +89,10 @@
|
|
|
89
89
|
"lucide-react": "^1.70.0",
|
|
90
90
|
"radix-ui": "^1.8.1",
|
|
91
91
|
"react-day-picker": "^10.0.2",
|
|
92
|
+
"react-markdown": "^10.1.0",
|
|
92
93
|
"react-resizable-panels": "^4.21.0",
|
|
93
94
|
"recharts": "^3.10.1",
|
|
95
|
+
"remark-gfm": "^4.0.1",
|
|
94
96
|
"shiki": "^4.6.0",
|
|
95
97
|
"sonner": "^2.0.8",
|
|
96
98
|
"tailwind-merge": "^3.7.0",
|
|
@@ -21,16 +21,17 @@ This project uses `harebear-ui` for all UI. Its theme, tokens, and components ar
|
|
|
21
21
|
- Style only with theme tokens: `bg-background`, `bg-card`, `bg-muted`, `text-foreground`, `text-muted-foreground`, `bg-primary`, `bg-primary-soft text-primary`, `text-success`, `text-warning`, `text-destructive`, `border-border`, `shadow-card`, `rounded-md` (controls), `rounded-xl` (cards). No hex colors, no Tailwind palette colors like `bg-blue-500`, no inline color styles.
|
|
22
22
|
- Status uses a tone prop: `success | warning | danger | info | neutral` (StatusBadge, StatusDot, Timeline, StatusBars, LinkTile).
|
|
23
23
|
- Icons are lucide-react elements passed as nodes: `icon={<UsersIcon />}`. Icon-only buttons need `aria-label`.
|
|
24
|
+
- Touch: `Button`, `Toggle` and `Input` already meet 44px under `pointer: coarse` (buttons via an invisible `::after`, which `overflow-hidden` ancestors clip: there, size the button itself with `pointer-coarse:size-11`); don't override their size.
|
|
24
25
|
- Compose `className` with `cn()` from harebear-ui.
|
|
25
26
|
- Lay out pages as columns of `Card`s with `gap-5`, page sections with `gap-7`. Use `PageHeader` for page titles and `AppShell` for sidebar apps. Every shell (`AppShell`, `ChatShell`) is built on `SidebarLayout`, which gives the collapsible icon rail; use it directly for other sidebar layouts instead of the raw Sidebar primitives.
|
|
26
27
|
- DataTable columns: define with `createDataTableColumnHelper<Row>()` at module scope, pass `getRowId`.
|
|
27
28
|
- Chat apps: `ChatShell` (sidebar: `AppBrand`, `NewConversationButton`, `ConversationList` + `groupByDate`, `UserMenu`; optional `details` panel) around a `ChatPanel` (title, actions, `notice`, `composer`). Don't hand-build the sidebar, rail or panel.
|
|
28
29
|
- Chat content: `ChatMessage role="user" | "assistant"`; render model output inside `Prose`; composer is `PromptInput` (+ `PromptInputChip`, `PromptInputSelect`); code is `CodeSnippet`; agent tool calls are `ToolGroup`/`ToolStep` (or the lighter `Trace`); permission prompts are `ApprovalCard`/`ApprovalResult`; working state is `TypingIndicator label`; clarifying questions are `Questionnaire`.
|
|
29
|
-
- Agent-first and household apps: `AppNav` (sidebar on desktop, tab bar on phones) around pages; `AssistantProvider` + `AssistantDock` (pages call `useAssistantPage`); `AskBox` with `DictationButton` (inline answers: `AskBox` `reply={reply}` from `useAssistant()` plus your own `chatHref`, `send(text, { open: false })`; Stop: `pending` + `onStop={stop}`); `ActionReceipt` for each action the agent took; `toastUndo` after undoable changes; to-dos are `SectionedList` / `TaskRow` / `CheckCircle` (`lingerMs` = the Undo window); a week agenda is `DayStrip` + one `SectionedList` with a section per day and `AgendaRow` rows via `renderItem`; settings are `FocusLayout` + `SettingsSection` + `SettingsRow`. Recipe: `npx harebear-ui docs "Agent-first apps"`. Categories (members, lists) use `category-1..6`. People are `MemberAvatar` (photo or initials on their `color`), `AvatarStack` and `VisibilityBadge`; list dots are `CategoryDot` (`locked` for private); profile settings use `AvatarUpload` and `ColorSwatchPicker`.
|
|
30
|
-
- Notices: `Alert variant="warning" | "success" | "info" | "destructive"`. Confirmations: `ConfirmDialog`. Copyable IDs: `CopyField`. Live status: `StatusDot`/`StatusBadge` with `pulse`.
|
|
30
|
+
- Agent-first and household apps: `AppNav` (sidebar on desktop, tab bar on phones; `fill` for a chat page, minus the round action button's space when `action` is set) around pages, each a `Page` with a `PageHeader` (`UserMenu compact className="md:hidden"` in its actions, hidden from md up) and `PageSection`s (h2; `Heading level={3}` inside cards); `AssistantProvider` + `AssistantDock` (pages call `useAssistantPage`); `AskBox` with `DictationButton` (inline answers: `AskBox` `reply={reply}` from `useAssistant()` plus your own `chatHref`, `send(text, { open: false })`; Stop: `pending` + `onStop={stop}`); `ActionReceipt` for each action the agent took; `toastUndo` after undoable changes; to-dos are `SectionedList` / `TaskRow` / `CheckCircle` (`lingerMs` = the Undo window); a week agenda is `DayStrip sticky` + one `SectionedList` with a section per day and `AgendaRow` rows via `renderItem`; settings are `FocusLayout` + `SettingsSection` + `SettingsRow`. Recipe: `npx harebear-ui docs "Agent-first apps"`. Categories (members, lists) use `category-1..6`. People are `MemberAvatar` (photo or initials on their `color`), `AvatarStack` and `VisibilityBadge`; list dots are `CategoryDot` (`locked` for private); profile settings use `AvatarUpload` and `ColorSwatchPicker`.
|
|
31
|
+
- Notices: `Alert variant="warning" | "success" | "info" | "destructive"`. Confirmations: `ConfirmDialog`; one line of text (New, Rename): `PromptDialog`. Toasts: `toast` / `toast.success` / `toast.error` from harebear-ui. Copyable IDs: `CopyField`. Live status: `StatusDot`/`StatusBadge` with `pulse`.
|
|
31
32
|
- If you need a className to fix spacing, color, wrapping or borders on a harebear component, that is a library gap: report it instead of overriding.
|
|
32
|
-
- Sign-in and sign-up pages: `AuthLayout` + `LoginForm` / `SignupForm`. Don't hand-build auth forms.
|
|
33
|
-
- Form fields beyond `Input`: `PasswordInput` (Show/Hide; every password field), `DateTimePicker` (`{ date: "YYYY-MM-DD", time?: "HH:MM" }`, floating local), `MarkdownEditor` (Write/Preview
|
|
33
|
+
- Sign-in and sign-up pages: `AuthLayout` + `LoginForm` / `SignupForm` (extra fields as `children`) / `NewPasswordForm`; other signed-out steps use `AuthForm`. Don't hand-build auth forms.
|
|
34
|
+
- Form fields beyond `Input`: `PasswordInput` (Show/Hide; every password field), `DateTimePicker` (`{ date: "YYYY-MM-DD", time?: "HH:MM" }`, floating local), `TimeZonePicker` (IANA name), `MarkdownEditor` (Write/Preview), `ChecklistEditor` (steps: tick, remove, add), `CommentThread` (comments + box). A record's details open in `DetailSheet` (side sheet on desktop, full screen on phones). Render Markdown strings with `Markdown` (GFM, no raw HTML, `> Tip:` becomes a `Callout`, `linkComponent` for in-app links), not your own parser. Rare joy moments: mount `Celebration` once, call `celebrate()`.
|
|
34
35
|
- Theme: `ThemeProvider` at the root (`accent`, `radius`, `defaultMode`), `ThemeToggle` for the light/dark button. Both modes must work: never style for only one.
|
|
35
36
|
|
|
36
37
|
## Check your work
|