harebear-ui 0.5.0 → 0.7.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 (44) hide show
  1. package/AGENTS.md +19 -2
  2. package/README.md +108 -36
  3. package/bin/harebear-ui.mjs +66 -3
  4. package/dist/components/admin/status.d.ts +3 -1
  5. package/dist/components/admin/status.js +5 -2
  6. package/dist/components/ai/approval.d.ts +3 -1
  7. package/dist/components/ai/approval.js +6 -3
  8. package/dist/components/ai/chat.d.ts +5 -2
  9. package/dist/components/ai/chat.js +1 -1
  10. package/dist/components/ai/code-block.d.ts +5 -1
  11. package/dist/components/ai/code-block.js +5 -3
  12. package/dist/components/ai/conversation-list.d.ts +16 -2
  13. package/dist/components/ai/conversation-list.js +88 -37
  14. package/dist/components/ai/model-picker.d.ts +6 -1
  15. package/dist/components/ai/model-picker.js +43 -23
  16. package/dist/components/layout/media-item.d.ts +13 -0
  17. package/dist/components/layout/media-item.js +54 -0
  18. package/dist/components/layout/metric.d.ts +15 -0
  19. package/dist/components/layout/metric.js +51 -0
  20. package/dist/components/layout/show-more.d.ts +17 -0
  21. package/dist/components/layout/show-more.js +30 -0
  22. package/dist/components/layout/stack.d.ts +39 -0
  23. package/dist/components/layout/stack.js +114 -0
  24. package/dist/components/layout/text.d.ts +37 -0
  25. package/dist/components/layout/text.js +52 -0
  26. package/dist/components/theme/theme-toggle.d.ts +4 -1
  27. package/dist/components/theme/theme-toggle.js +11 -6
  28. package/dist/components/ui/badge.d.ts +4 -1
  29. package/dist/components/ui/badge.js +10 -1
  30. package/dist/components/ui/button.d.ts +3 -1
  31. package/dist/components/ui/button.js +13 -3
  32. package/dist/components/ui/empty.d.ts +4 -1
  33. package/dist/components/ui/empty.js +3 -2
  34. package/dist/components/ui/item.d.ts +4 -1
  35. package/dist/components/ui/item.js +7 -2
  36. package/dist/index.d.ts +8 -3
  37. package/dist/index.js +7 -2
  38. package/docs/COMPONENTS.md +96 -71
  39. package/docs/guides/agents.md +75 -0
  40. package/docs/guides/getting-started.md +217 -0
  41. package/docs/guides/theming.md +98 -0
  42. package/llms.txt +6 -1
  43. package/package.json +2 -1
  44. package/skills/harebear-ui/SKILL.md +1 -1
package/AGENTS.md CHANGED
@@ -14,6 +14,7 @@ Consumers get compiled ESM + types from `dist/` and a Tailwind stylesheet (`hare
14
14
  | `pnpm gen` | Regenerate `src/index.ts` from component export lists. Run after adding or renaming exports. |
15
15
  | `pnpm format` | Biome format + safe fixes. |
16
16
  | `pnpm shadcn add <name>` | Pull a new shadcn component into `src/components/ui/`. |
17
+ | `pnpm shots [filter]` | Regenerate README images in `.github/assets/` (needs `pnpm dev` running, Chrome and `cwebp`). |
17
18
 
18
19
  ## Layout
19
20
 
@@ -31,8 +32,11 @@ src/
31
32
  hooks/, lib/utils.ts Shared hooks and cn().
32
33
  playground/screens/ Reference screens that use only public exports (#admin, #chat, #records, #home, #auth, #catalog). Visual spec and usage examples.
33
34
  docs/COMPONENTS.md Consumer-facing catalog. Shipped. Test-enforced to mention every export.
35
+ docs/guides/ Getting started (per framework), theming, working with agents. Shipped.
34
36
  llms.txt, skills/ Consumer-facing agent docs. Shipped.
35
- bin/harebear-ui.mjs `npx harebear-ui init|docs` for consuming apps.
37
+ bin/harebear-ui.mjs `npx harebear-ui init|docs [name...]` for consuming apps. Zero dependencies.
38
+ scripts/screenshots.mjs `pnpm shots`: captures playground screens and renders .github/assets/art/*.html to images.
39
+ .github/assets/ README imagery (not shipped): logo.svg, screens/*.webp, hero/light-dark/agents art, social.png.
36
40
  test/ Vitest: API snapshot, docs coverage, token integrity, behavior.
37
41
  ```
38
42
 
@@ -41,11 +45,24 @@ test/ Vitest: API snapshot, docs coverage, token integrity, be
41
45
  1. Pick the folder by purpose (`admin`, `data`, `ai`, `auth`; `ui` only for shadcn primitives).
42
46
  2. Follow the existing files: `"use client"` first line, named function component, props interface exported as `<Name>Props`, `data-slot="<kebab-name>"` on the root, `className` merged last with `cn()`, rest props spread on the root.
43
47
  3. Style only with tokens (`bg-card`, `text-muted-foreground`, `bg-primary-soft`, `shadow-card`, `rounded-xl`). No hex, no palette colors. Status takes a `tone` (`success | warning | danger | info | neutral`).
44
- 4. `pnpm gen`, then add the component to `docs/COMPONENTS.md` (the docs test fails otherwise) and to the index in `llms.txt`.
48
+ 4. `pnpm gen`, then add the component to `docs/COMPONENTS.md` (the docs test fails otherwise) and to the index in `llms.txt`. Use a heading like ``### `Name` `` or a table row with the name in backticks, so `npx harebear-ui docs Name` finds it.
45
49
  5. Use it in a playground screen if it is visual, and look at it in light and dark mode and at 375px width.
46
50
  6. Add a behavior test in `test/components.test.tsx` if it has logic. `pnpm test -u` to accept the new export in the snapshot.
47
51
  7. `pnpm check`.
48
52
 
53
+ ## Looking at your work
54
+
55
+ - `pnpm dev`, then open http://localhost:5199/#admin (or `#chat`, `#agent`, `#records`, `#home`, `#auth`, `#catalog`).
56
+ - `?mode=dark` or `?mode=light` pins the theme without touching localStorage; `?shot` hides the screen switcher. Example: http://localhost:5199/?mode=dark&shot#agent
57
+ - A headless capture: `pnpm shots admin` writes `.github/assets/screens/admin-*.webp`.
58
+
59
+ ## README imagery
60
+
61
+ - `.github/assets/logo.svg` is the mark. It is hand-written SVG; edit it directly. `logo.png` is exported from it.
62
+ - Screens are real playground captures. Art (`hero`, `light-dark`, `agents`) is HTML in `.github/assets/art/` that composes those captures, rendered by `pnpm shots`. `social.png` is the hero at 1x for GitHub's social preview (upload it by hand in repo Settings).
63
+ - After a visible change to a screen or the theme, run `pnpm shots` and commit the images with the change. Check the results by eye before committing.
64
+ - If `npx harebear-ui init` output changes, update the terminal text in `art/agents.html` to match.
65
+
49
66
  ## Changing shadcn primitives
50
67
 
51
68
  - Keep upstream file structure so `pnpm shadcn add <name> -o` diffs stay readable. Change class strings, add variants; avoid restructuring.
package/README.md CHANGED
@@ -1,28 +1,72 @@
1
- # harebear-ui
2
-
3
- Pre-themed React components for admin tools, data apps, and AI chat. shadcn/ui primitives restyled to one opinionated design system, plus the composites you end up building every time: app shell, KPI cards, data table, trend charts, chat thread, prompt composer, model picker.
4
-
5
- React 19 · Tailwind CSS v4 · Radix · TanStack Table v9 · Recharts 3 · TypeScript
6
-
7
- ## Install
1
+ <p align="center">
2
+ <img src=".github/assets/logo.svg" width="72" height="72" alt="harebear-ui logo" />
3
+ </p>
4
+
5
+ <h1 align="center">harebear-ui</h1>
6
+
7
+ <p align="center">
8
+ Pre-themed React components for admin tools, data apps and AI chat.<br />
9
+ shadcn/ui restyled to one design system, plus the composites you build every time. Agent-ready out of the box.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/harebear-ui"><img src="https://img.shields.io/npm/v/harebear-ui?style=flat-square&color=5b84ff&labelColor=101012" alt="npm version" /></a>
14
+ <a href="https://github.com/HMilbradt/harebear-ui/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/HMilbradt/harebear-ui/ci.yml?branch=main&style=flat-square&labelColor=101012&label=CI" alt="CI status" /></a>
15
+ <img src="https://img.shields.io/badge/React-19-5b84ff?style=flat-square&labelColor=101012" alt="React 19" />
16
+ <img src="https://img.shields.io/badge/Tailwind_CSS-v4-a98ae0?style=flat-square&labelColor=101012" alt="Tailwind CSS v4" />
17
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/harebear-ui?style=flat-square&color=3cb8a8&labelColor=101012" alt="MIT license" /></a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <img src=".github/assets/hero.webp" alt="harebear-ui: an admin dashboard, an agent chat with approvals and a records table, all in dark mode" />
22
+ </p>
23
+
24
+ <p align="center">
25
+ <a href="docs/guides/getting-started.md"><b>Get started</b></a> ·
26
+ <a href="docs/COMPONENTS.md"><b>Components</b></a> ·
27
+ <a href="docs/guides/theming.md"><b>Theming</b></a> ·
28
+ <a href="docs/guides/agents.md"><b>AI agents</b></a> ·
29
+ <a href="playground/screens"><b>Reference screens</b></a>
30
+ </p>
31
+
32
+ ## Why
33
+
34
+ Every internal tool, dashboard and chat app ends up needing the same pieces: an app shell with a collapsible sidebar, KPI cards, a sortable table with a detail panel, trend charts, a chat thread with a composer and tool calls. shadcn/ui gives you primitives to build them. harebear-ui gives you them built, in one consistent look, so you (or your agent) spend the time on the product.
35
+
36
+ - **Every shadcn/ui primitive**, same names and props, restyled to one set of tokens.
37
+ - **Composites on top:** `AppShell`, `StatCard`, `DataTable`, `TrendChart`, `ChatShell`, `PromptInput`, `ToolStep`, `ApprovalCard`, `LoginForm` and more.
38
+ - **Light and dark from one accent.** Change the brand color, radius and font with props or CSS variables.
39
+ - **Docs agents can use.** `npx harebear-ui init` installs project rules and a Claude Code skill; `npx harebear-ui docs DataTable` prints one component's API.
40
+
41
+ ## Screens
42
+
43
+ Built only from public exports. Each one is a file in [`playground/screens/`](playground/screens) you can copy from.
44
+
45
+ <table>
46
+ <tr>
47
+ <td width="50%"><a href="playground/screens/admin.tsx"><img src=".github/assets/screens/admin-dark.webp" alt="Admin dashboard with KPI cards, a visitors trend chart and traffic sources" /></a><br /><b>Admin dashboard</b>: <code>AppShell</code>, <code>StatCard</code>, <code>TrendChart</code>, <code>BarList</code></td>
48
+ <td width="50%"><a href="playground/screens/agent.tsx"><img src=".github/assets/screens/agent-dark.webp" alt="Agent chat waiting on a command approval, with a details panel" /></a><br /><b>Agent with approvals</b>: <code>ChatShell</code>, <code>ToolGroup</code>, <code>ApprovalCard</code></td>
49
+ </tr>
50
+ <tr>
51
+ <td width="50%"><a href="playground/screens/chat.tsx"><img src=".github/assets/screens/chat-dark.webp" alt="AI chat with a conversation list, highlighted code and a prompt composer" /></a><br /><b>AI chat</b>: <code>ChatPanel</code>, <code>ChatMessage</code>, <code>CodeSnippet</code>, <code>PromptInput</code></td>
52
+ <td width="50%"><a href="playground/screens/records.tsx"><img src=".github/assets/screens/records-dark.webp" alt="Customer table with status badges and a detail panel for the selected row" /></a><br /><b>Records</b>: <code>DataTable</code>, <code>KeyValueGrid</code>, <code>Timeline</code></td>
53
+ </tr>
54
+ <tr>
55
+ <td width="50%"><a href="playground/screens/home.tsx"><img src=".github/assets/screens/home-dark.webp" alt="Status home page with service tiles and uptime bars" /></a><br /><b>Status home</b>: <code>LinkTile</code>, <code>StatusBars</code>, <code>Sparkline</code></td>
56
+ <td width="50%"><a href="playground/screens/auth.tsx"><img src=".github/assets/screens/auth-dark.webp" alt="Sign-in page" /></a><br /><b>Auth</b>: <code>AuthLayout</code>, <code>LoginForm</code>, <code>SignupForm</code></td>
57
+ </tr>
58
+ </table>
59
+
60
+ ## Quick start
61
+
62
+ Needs React 19 and Tailwind CSS v4.
8
63
 
9
64
  ```bash
10
65
  npm i harebear-ui
11
- ```
12
-
13
- ```bash
14
66
  npx harebear-ui init
15
67
  ```
16
68
 
17
- `init` adds the stylesheet import to your Tailwind entry CSS, writes a harebear-ui section into `AGENTS.md` (or `CLAUDE.md`), and installs a Claude Code skill in `.claude/skills/`. Flags: `--css <file>`, `--no-agents`, `--no-skill`.
18
-
19
- Manual setup, if you prefer:
20
-
21
- ```css
22
- /* app.css */
23
- @import "tailwindcss";
24
- @import "harebear-ui/styles.css";
25
- ```
69
+ `init` adds the stylesheet import after `@import "tailwindcss"`, writes a harebear-ui section into `AGENTS.md` (or `CLAUDE.md`), and installs a Claude Code skill. Then wrap your root:
26
70
 
27
71
  ```tsx
28
72
  import { ThemeProvider, Toaster, TooltipProvider } from "harebear-ui"
@@ -39,24 +83,26 @@ export function Root() {
39
83
  }
40
84
  ```
41
85
 
42
- Requires Tailwind CSS v4 in the app. Geist and Geist Mono are bundled.
43
-
44
- ## Use
86
+ And build:
45
87
 
46
88
  ```tsx
47
- import { AppShell, PageHeader, StatCard, Button, DataTable } from "harebear-ui"
89
+ import { AppShell, Button, DataTable, PageHeader, StatCard } from "harebear-ui"
48
90
  ```
49
91
 
50
- Everything imports from the package root. Primitives keep shadcn/ui names and props.
92
+ Everything imports from the package root. Geist and Geist Mono are bundled.
51
93
 
52
- - **Component catalog:** [docs/COMPONENTS.md](docs/COMPONENTS.md), also at `node_modules/harebear-ui/docs/COMPONENTS.md` or `npx harebear-ui docs`.
53
- - **Reference screens:** [playground/screens](playground/screens) has an admin dashboard, an AI chat, a data table with detail panel, and a status home page built only from public exports.
94
+ Step-by-step setup for **Vite**, **Next.js** and **React Router**, plus troubleshooting: [Getting started](docs/guides/getting-started.md).
54
95
 
55
96
  ## Theming
56
97
 
57
- Light and dark mode, accent, radius, and font are runtime props on `ThemeProvider` (`useTheme()` changes them live). Every component reads shadcn-named tokens, so you can override any of them in CSS:
98
+ <img src=".github/assets/light-dark.webp" alt="The same admin dashboard split diagonally between light and dark mode" />
99
+
100
+ Light, dark and system modes, accent, radius and font are `ThemeProvider` props, changeable live with `useTheme()`. Every component reads shadcn-named tokens, so you can override any of them in CSS:
58
101
 
59
102
  ```css
103
+ @import "tailwindcss";
104
+ @import "harebear-ui/styles.css";
105
+
60
106
  :root {
61
107
  --brand: #0f8a7e;
62
108
  --radius: 0.625rem;
@@ -66,19 +112,42 @@ Light and dark mode, accent, radius, and font are runtime props on `ThemeProvide
66
112
  }
67
113
  ```
68
114
 
69
- For SSR apps, render `<ThemeScript />` in `<head>` to avoid a flash of the wrong mode.
115
+ SSR apps render `<ThemeScript />` in `<head>` to avoid a flash of the wrong mode. More in [Theming](docs/guides/theming.md).
70
116
 
71
- ## For AI agents
117
+ ## Built for AI agents
72
118
 
73
- The package ships its own agent docs:
119
+ <img src=".github/assets/agents.webp" alt="Terminal running harebear-ui init and docs, with the four agent files it sets up" />
74
120
 
75
- | File | For |
76
- | ----------------------------- | ------------------------------------------------------ |
77
- | `llms.txt` | Short index: setup, rules, component list. |
78
- | `docs/COMPONENTS.md` | Every export with props and examples. |
79
- | `skills/harebear-ui/SKILL.md` | Claude Code skill; `npx harebear-ui init` installs it. |
121
+ The package carries its own agent docs, so Claude Code, Codex, Cursor and friends know what exists and how to use it:
80
122
 
81
- Working on this repo with an agent: see [AGENTS.md](AGENTS.md).
123
+ | File | For |
124
+ | ----------------------------- | --------------------------------------------------------------------- |
125
+ | `llms.txt` | Short index: setup, rules, every export by group. |
126
+ | `docs/COMPONENTS.md` | Every export with props and examples. Test-enforced to stay complete. |
127
+ | `docs/guides/` | Setup per framework, theming, working with agents. |
128
+ | `skills/harebear-ui/SKILL.md` | Claude Code skill; `npx harebear-ui init` installs it. |
129
+
130
+ Agents look up one component at a time instead of reading the whole catalog:
131
+
132
+ ```bash
133
+ npx harebear-ui docs DataTable StatCard # exports
134
+ npx harebear-ui docs Prompt # prefix: PromptInput, PromptInputChip, PromptInputSelect
135
+ npx harebear-ui docs Tokens # a section
136
+ ```
137
+
138
+ Prompts that work, and what to check before you accept an agent's UI: [Working with AI agents](docs/guides/agents.md).
139
+
140
+ ## What's inside
141
+
142
+ | Group | Exports |
143
+ | ------------- | --------------------------------------------------------------------------------------------------------------------------- |
144
+ | Layout, admin | `SidebarLayout`, `AppShell`, `PageHeader`, `StatCard`, `StatusBadge`, `UsageMeter`, `Timeline`, `UserMenu`, `ConfirmDialog` |
145
+ | Data | `DataTable`, `TrendChart`, `BarList`, `Sparkline`, `StatusBars` |
146
+ | AI and chat | `ChatShell`, `ChatPanel`, `ChatMessage`, `PromptInput`, `ModelPicker`, `ToolGroup`, `ApprovalCard`, `CodeSnippet`, `Prose` |
147
+ | Auth | `AuthLayout`, `LoginForm`, `SignupForm` |
148
+ | Primitives | The full shadcn/ui registry: `Button`, `Dialog`, `Select`, `Sidebar`, `Tabs`, `Command`, `Chart`, and the rest |
149
+
150
+ The full list with props: [docs/COMPONENTS.md](docs/COMPONENTS.md).
82
151
 
83
152
  ## Develop
84
153
 
@@ -86,8 +155,11 @@ Working on this repo with an agent: see [AGENTS.md](AGENTS.md).
86
155
  pnpm install
87
156
  pnpm dev # playground on :5199
88
157
  pnpm check # lint, typecheck, test, build, package lint
158
+ pnpm shots # regenerate the README images (needs pnpm dev running, Chrome and cwebp)
89
159
  ```
90
160
 
161
+ Working on this repo with an agent: see [AGENTS.md](AGENTS.md).
162
+
91
163
  ## License
92
164
 
93
165
  MIT
@@ -24,6 +24,7 @@ const AGENT_BLOCK = `${START}
24
24
  All UI in this project uses the \`harebear-ui\` component library (pre-themed shadcn/ui on Tailwind v4, plus admin, data table, chart, and AI chat components).
25
25
 
26
26
  - Read \`node_modules/harebear-ui/docs/COMPONENTS.md\` before building UI. It lists every component with props and examples.
27
+ - Look up single entries with \`npx harebear-ui docs DataTable PromptInput\` (export names, a prefix, or a section such as \`Tokens\`).
27
28
  - Import from the package root: \`import { Button, Card, DataTable } from "harebear-ui"\`.
28
29
  - Style only with theme tokens (\`bg-card\`, \`bg-muted\`, \`text-muted-foreground\`, \`bg-primary-soft\`, \`text-success\`, \`shadow-card\`, \`rounded-xl\`). No hex or Tailwind palette colors.
29
30
  - Check new screens in light and dark mode and at phone width.
@@ -86,6 +87,50 @@ function installSkill() {
86
87
  console.log(`✓ Installed Claude Code skill to ${relative(cwd, destDir)}/SKILL.md`)
87
88
  }
88
89
 
90
+ // Backticked identifiers in a heading or table cell: "`ChatMessage`" -> "chatmessage".
91
+ const namesIn = (text) => [...text.matchAll(/`([A-Za-z]\w*)/g)].map((m) => m[1].toLowerCase())
92
+
93
+ // The parts of the catalog about one export or section: "### `Name`" sections, primitive table rows
94
+ // (with their table header), or a whole "## Section" by title. Exact names first, then prefix matches.
95
+ function lookup(catalog, query) {
96
+ const q = query.toLowerCase()
97
+ const blocks = catalog.split(/^(?=#{2,3} )/m)
98
+ const section = blocks.find((b) => b.startsWith("## ") && b.split("\n", 1)[0].slice(3).toLowerCase() === q)
99
+ if (section) {
100
+ // A "## Section" block stops at its first "###"; take the subsections that follow it too.
101
+ const start = blocks.indexOf(section)
102
+ const end = blocks.findIndex((b, i) => i > start && b.startsWith("## "))
103
+ return blocks
104
+ .slice(start, end < 0 ? undefined : end)
105
+ .join("")
106
+ .trimEnd()
107
+ }
108
+ for (const match of [(n) => n === q, (n) => n.startsWith(q)]) {
109
+ const out = blocks.filter((b) => b.startsWith("### ") && namesIn(b.split("\n", 1)[0]).some(match))
110
+ let header = []
111
+ let headerShown = null
112
+ const rows = []
113
+ for (const line of catalog.split("\n")) {
114
+ if (!line.startsWith("|")) {
115
+ header = []
116
+ continue
117
+ }
118
+ if (header.length < 2) {
119
+ header.push(line)
120
+ continue
121
+ }
122
+ const [, first = "", second = ""] = line.split("|")
123
+ if (!namesIn(first + second).some(match)) continue
124
+ if (headerShown !== header) rows.push(...header)
125
+ headerShown = header
126
+ rows.push(line)
127
+ }
128
+ if (rows.length) out.push(rows.join("\n"))
129
+ if (out.length) return out.map((b) => b.trimEnd()).join("\n\n")
130
+ }
131
+ return undefined
132
+ }
133
+
89
134
  switch (cmd) {
90
135
  case "init": {
91
136
  if (!existsSync(join(cwd, "package.json"))) {
@@ -109,9 +154,25 @@ Next: wrap your app root.
109
154
  `)
110
155
  break
111
156
  }
112
- case "docs":
113
- process.stdout.write(readFileSync(join(pkgRoot, "docs/COMPONENTS.md"), "utf8"))
157
+ case "docs": {
158
+ const catalog = readFileSync(join(pkgRoot, "docs/COMPONENTS.md"), "utf8")
159
+ const queries = args.filter((a) => !a.startsWith("--"))
160
+ if (queries.length === 0) {
161
+ process.stdout.write(catalog)
162
+ break
163
+ }
164
+ let missed = false
165
+ for (const q of queries) {
166
+ const found = lookup(catalog, q)
167
+ if (found) process.stdout.write(`${found}\n\n`)
168
+ else {
169
+ missed = true
170
+ console.error(`! Nothing in the catalog for "${q}". Run \`npx harebear-ui docs\` for the full catalog.`)
171
+ }
172
+ }
173
+ if (missed) process.exitCode = 1
114
174
  break
175
+ }
115
176
  default:
116
177
  console.log(`harebear-ui
117
178
 
@@ -119,7 +180,9 @@ Next: wrap your app root.
119
180
  Add the stylesheet import after @import "tailwindcss", add a harebear-ui
120
181
  section to AGENTS.md (or CLAUDE.md), and install the Claude Code skill.
121
182
 
122
- npx harebear-ui docs
183
+ npx harebear-ui docs [name...]
123
184
  Print the component catalog (props and examples for every export).
185
+ With names, print only those entries: an export (DataTable, Button),
186
+ a name prefix (Prompt), or a section title (Tokens, Auth).
124
187
  `)
125
188
  }
@@ -12,7 +12,9 @@ export interface StatusBadgeProps extends React.ComponentProps<typeof Badge> {
12
12
  tone?: StatusTone;
13
13
  /** Pulse the dot. */
14
14
  pulse?: boolean;
15
+ /** Below 640px show only the dot; the label stays for screen readers. For crowded mobile headers. */
16
+ compact?: boolean;
15
17
  }
16
18
  /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. `size="lg"` lines up with sm buttons. */
17
- declare function StatusBadge({ tone, pulse, variant, children, className, ...props }: StatusBadgeProps): React.JSX.Element;
19
+ declare function StatusBadge({ tone, pulse, compact, variant, children, className, ...props }: StatusBadgeProps): React.JSX.Element;
18
20
  export { StatusBadge, StatusDot };
@@ -20,7 +20,7 @@ function StatusDot({ tone = "neutral", pulse, className, ...props }) {
20
20
  });
21
21
  }
22
22
  /** Pill with a status dot and label, e.g. Paid / Pending / Refunded. `size="lg"` lines up with sm buttons. */
23
- function StatusBadge({ tone = "neutral", pulse, variant = "secondary", children, className, ...props }) {
23
+ function StatusBadge({ tone = "neutral", pulse, compact, variant = "secondary", children, className, ...props }) {
24
24
  return /* @__PURE__ */ jsxs(Badge, {
25
25
  "data-slot": "status-badge",
26
26
  variant,
@@ -29,7 +29,10 @@ function StatusBadge({ tone = "neutral", pulse, variant = "secondary", children,
29
29
  children: [/* @__PURE__ */ jsx(StatusDot, {
30
30
  tone,
31
31
  pulse
32
- }), children]
32
+ }), compact ? /* @__PURE__ */ jsx("span", {
33
+ className: "sr-only sm:not-sr-only",
34
+ children
35
+ }) : children]
33
36
  });
34
37
  }
35
38
  export { StatusBadge, StatusDot };
@@ -14,12 +14,14 @@ export interface ApprovalCardProps extends Omit<React.ComponentProps<"section">,
14
14
  actions?: React.ReactNode;
15
15
  /** Small print under the actions. */
16
16
  footer?: React.ReactNode;
17
+ /** Why the agent wants this, shown first in the body after a bold "Why:". */
18
+ reason?: React.ReactNode;
17
19
  }
18
20
  /**
19
21
  * Highlighted card for anything the agent is waiting on the user for: a tool approval, a question, a choice.
20
22
  * Body text goes in children (e.g. the agent's reason).
21
23
  */
22
- declare function ApprovalCard({ title, description, icon, code, codeLabel, actions, footer, className, children, ...props }: ApprovalCardProps): React.JSX.Element;
24
+ declare function ApprovalCard({ title, description, icon, code, codeLabel, actions, footer, reason, className, children, ...props }: ApprovalCardProps): React.JSX.Element;
23
25
  export interface ApprovalResultProps extends React.ComponentProps<"div"> {
24
26
  decision: "approved" | "denied";
25
27
  /** Trailing time, e.g. "10:42". */
@@ -9,7 +9,7 @@ import { CheckIcon, ShieldAlertIcon, XIcon } from "lucide-react";
9
9
  * Highlighted card for anything the agent is waiting on the user for: a tool approval, a question, a choice.
10
10
  * Body text goes in children (e.g. the agent's reason).
11
11
  */
12
- function ApprovalCard({ title, description, icon = /* @__PURE__ */ jsx(ShieldAlertIcon, {}), code, codeLabel, actions, footer, className, children, ...props }) {
12
+ function ApprovalCard({ title, description, icon = /* @__PURE__ */ jsx(ShieldAlertIcon, {}), code, codeLabel, actions, footer, reason, className, children, ...props }) {
13
13
  const id = React.useId();
14
14
  return /* @__PURE__ */ jsxs("section", {
15
15
  "data-slot": "approval-card",
@@ -34,9 +34,12 @@ function ApprovalCard({ title, description, icon = /* @__PURE__ */ jsx(ShieldAle
34
34
  })]
35
35
  })]
36
36
  }),
37
- children && /* @__PURE__ */ jsx("div", {
37
+ (reason || children) && /* @__PURE__ */ jsxs("div", {
38
38
  className: "text-[14.5px] leading-relaxed whitespace-pre-wrap wrap-anywhere [&_strong]:font-semibold",
39
- children
39
+ children: [reason && /* @__PURE__ */ jsxs("p", { children: [/* @__PURE__ */ jsx("strong", { children: "Why: " }), reason] }), reason && children ? /* @__PURE__ */ jsx("div", {
40
+ className: "mt-2",
41
+ children
42
+ }) : children]
40
43
  }),
41
44
  code !== void 0 && /* @__PURE__ */ jsxs(CodeBlock, { children: [/* @__PURE__ */ jsxs(CodeBlockHeader, { children: [/* @__PURE__ */ jsx(CodeBlockTitle, { children: codeLabel ?? "command" }), /* @__PURE__ */ jsx(CodeBlockActions, { children: /* @__PURE__ */ jsx(CodeBlockCopyButton, { value: code }) })] }), /* @__PURE__ */ jsx(CodeBlockContent, {
42
45
  wrap: true,
@@ -10,8 +10,11 @@ export interface ChatThreadProps extends React.ComponentProps<"div"> {
10
10
  declare function ChatThread({ autoScroll, className, contentClassName, children, ...props }: ChatThreadProps): React.JSX.Element;
11
11
  export interface ChatMessageProps extends React.ComponentProps<"div"> {
12
12
  role: "user" | "assistant";
13
- /** Assistant avatar. Defaults to a sparkle in a tinted circle. */
14
- avatar?: React.ReactNode;
13
+ /**
14
+ * Assistant avatar. Defaults to a sparkle in a tinted circle. `false` renders no avatar and lets the content use
15
+ * the full width, for agent threads where one turn is many blocks (text, tool groups, approvals).
16
+ */
17
+ avatar?: React.ReactNode | false;
15
18
  /** Rendered under assistant content, usually <MessageActions />. */
16
19
  actions?: React.ReactNode;
17
20
  /** User bubble color: "muted" (gray, default) or "soft" (tinted with the accent). */
@@ -58,7 +58,7 @@ function ChatMessage({ role, avatar, actions, variant = "muted", className, chil
58
58
  "data-role": "assistant",
59
59
  className: cn("flex items-start gap-4", className),
60
60
  ...props,
61
- children: [avatar ?? /* @__PURE__ */ jsx(AssistantAvatar, {}), /* @__PURE__ */ jsxs("div", {
61
+ children: [avatar === false ? null : avatar ?? /* @__PURE__ */ jsx(AssistantAvatar, {}), /* @__PURE__ */ jsxs("div", {
62
62
  className: "flex min-w-0 flex-1 flex-col gap-3.5",
63
63
  children: [children, actions]
64
64
  })]
@@ -17,7 +17,11 @@ export interface CodeSnippetProps extends Omit<React.ComponentProps<typeof CodeB
17
17
  */
18
18
  declare function CodeSnippet({ code, language, highlight, lineNumbers, hideCopy, className, ...props }: CodeSnippetProps): React.JSX.Element;
19
19
  /** Inline monospace span for commands and identifiers. */
20
- declare function InlineCode({ className, ...props }: React.ComponentProps<"code">): React.JSX.Element;
20
+ export interface InlineCodeProps extends React.ComponentProps<"code"> {
21
+ /** Cut string children longer than this many characters with an ellipsis; the full text becomes the tooltip. */
22
+ truncate?: number;
23
+ }
24
+ declare function InlineCode({ truncate, className, children, title, ...props }: InlineCodeProps): React.JSX.Element;
21
25
  /**
22
26
  * Typography for long-form and model output. Styles plain HTML children
23
27
  * (p, ul, ol, li, strong, a, code, pre, h1-h4, blockquote, table), so it works
@@ -40,12 +40,14 @@ function CodeSnippet({ code, language, highlight = true, lineNumbers, hideCopy,
40
40
  })]
41
41
  });
42
42
  }
43
- /** Inline monospace span for commands and identifiers. */
44
- function InlineCode({ className, ...props }) {
43
+ function InlineCode({ truncate, className, children, title, ...props }) {
44
+ const cut = truncate !== void 0 && typeof children === "string" && children.length > truncate;
45
45
  return /* @__PURE__ */ jsx("code", {
46
46
  "data-slot": "inline-code",
47
+ title: title ?? (cut ? children : void 0),
47
48
  className: cn("rounded-[6px] bg-muted px-1.5 py-0.5 font-mono text-[12.5px]", className),
48
- ...props
49
+ ...props,
50
+ children: cut ? `${children.slice(0, truncate)}…` : children
49
51
  });
50
52
  }
51
53
  /**
@@ -10,6 +10,8 @@ export interface Conversation {
10
10
  tone?: StatusTone;
11
11
  /** Pulse the status dot. */
12
12
  pulse?: boolean;
13
+ /** With `onDelete`: block deleting this row. A string is shown as the reason, e.g. "Stop the agent first". */
14
+ deleteDisabled?: boolean | string;
13
15
  }
14
16
  export interface ConversationGroup {
15
17
  /** e.g. "Today", "Yesterday", "Previous 7 days". */
@@ -22,6 +24,13 @@ export interface ConversationListProps extends Omit<React.ComponentProps<"nav">,
22
24
  onSelect?: (id: string) => void;
23
25
  /** Trailing control per row, e.g. a delete button. On devices that can hover it shows on hover and focus; on touch it is always visible. */
24
26
  renderAction?: (conversation: Conversation) => React.ReactNode;
27
+ /**
28
+ * Adds a delete button to each row that asks for confirmation first, then calls this. Ignored when `renderAction`
29
+ * is set. Use `Conversation.deleteDisabled` to block it per row.
30
+ */
31
+ onDelete?: (conversation: Conversation) => void;
32
+ /** Body of the delete confirmation. Default "This cannot be undone." */
33
+ deleteDescription?: React.ReactNode;
25
34
  /** Shown when there are no conversations. Default "No conversations yet". */
26
35
  empty?: React.ReactNode;
27
36
  }
@@ -29,12 +38,17 @@ export interface ConversationListProps extends Omit<React.ComponentProps<"nav">,
29
38
  * Grouped list of chat threads for a sidebar. Inside a Sidebar it closes the mobile sheet on select,
30
39
  * and hides itself when the sidebar is collapsed to its icon rail.
31
40
  */
32
- declare function ConversationList({ groups, activeId, onSelect, renderAction, empty, className, ...props }: ConversationListProps): React.JSX.Element;
41
+ declare function ConversationList({ groups, activeId, onSelect, renderAction, onDelete, deleteDescription, empty, className, ...props }: ConversationListProps): React.JSX.Element;
33
42
  /**
34
43
  * Buckets items into Today / Yesterday / Previous 7 days / Earlier by a timestamp (ms or Date),
35
44
  * newest first, and drops empty groups. Pass `now` for deterministic tests.
36
45
  */
37
46
  declare function groupByDate<T>(items: T[], getDate: (item: T) => number | Date, toConversation: (item: T) => Conversation, now?: number | Date): ConversationGroup[];
47
+ /**
48
+ * Short timestamp for lists and headers: the time today ("9:41 AM"), the date this year ("Nov 3"), else with the
49
+ * year ("Nov 3, 2025"). Uses the browser locale unless `locale` is given. Pass `now` for deterministic tests.
50
+ */
51
+ declare function formatWhen(date: number | Date, now?: number | Date, locale?: string | string[]): string;
38
52
  export interface NewConversationButtonProps extends React.ComponentProps<typeof Button> {
39
53
  /** Default "New conversation". */
40
54
  label?: string;
@@ -48,4 +62,4 @@ export interface NewConversationButtonProps extends React.ComponentProps<typeof
48
62
  * tooltip; on mobile it closes the sidebar sheet after the click.
49
63
  */
50
64
  declare function NewConversationButton({ label, icon, shortcut, variant, onClick, className, ...props }: NewConversationButtonProps): React.JSX.Element;
51
- export { ConversationList, NewConversationButton, groupByDate };
65
+ export { ConversationList, NewConversationButton, formatWhen, groupByDate };