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.
- package/AGENTS.md +19 -2
- package/README.md +108 -36
- package/bin/harebear-ui.mjs +66 -3
- package/dist/components/admin/status.d.ts +3 -1
- package/dist/components/admin/status.js +5 -2
- package/dist/components/ai/approval.d.ts +3 -1
- package/dist/components/ai/approval.js +6 -3
- package/dist/components/ai/chat.d.ts +5 -2
- package/dist/components/ai/chat.js +1 -1
- package/dist/components/ai/code-block.d.ts +5 -1
- package/dist/components/ai/code-block.js +5 -3
- package/dist/components/ai/conversation-list.d.ts +16 -2
- package/dist/components/ai/conversation-list.js +88 -37
- package/dist/components/ai/model-picker.d.ts +6 -1
- package/dist/components/ai/model-picker.js +43 -23
- package/dist/components/layout/media-item.d.ts +13 -0
- package/dist/components/layout/media-item.js +54 -0
- package/dist/components/layout/metric.d.ts +15 -0
- package/dist/components/layout/metric.js +51 -0
- package/dist/components/layout/show-more.d.ts +17 -0
- package/dist/components/layout/show-more.js +30 -0
- package/dist/components/layout/stack.d.ts +39 -0
- package/dist/components/layout/stack.js +114 -0
- package/dist/components/layout/text.d.ts +37 -0
- package/dist/components/layout/text.js +52 -0
- package/dist/components/theme/theme-toggle.d.ts +4 -1
- package/dist/components/theme/theme-toggle.js +11 -6
- package/dist/components/ui/badge.d.ts +4 -1
- package/dist/components/ui/badge.js +10 -1
- package/dist/components/ui/button.d.ts +3 -1
- package/dist/components/ui/button.js +13 -3
- package/dist/components/ui/empty.d.ts +4 -1
- package/dist/components/ui/empty.js +3 -2
- package/dist/components/ui/item.d.ts +4 -1
- package/dist/components/ui/item.js +7 -2
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7 -2
- package/docs/COMPONENTS.md +96 -71
- package/docs/guides/agents.md +75 -0
- package/docs/guides/getting-started.md +217 -0
- package/docs/guides/theming.md +98 -0
- package/llms.txt +6 -1
- package/package.json +2 -1
- 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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
## Use
|
|
86
|
+
And build:
|
|
45
87
|
|
|
46
88
|
```tsx
|
|
47
|
-
import { AppShell,
|
|
89
|
+
import { AppShell, Button, DataTable, PageHeader, StatCard } from "harebear-ui"
|
|
48
90
|
```
|
|
49
91
|
|
|
50
|
-
Everything imports from the package root.
|
|
92
|
+
Everything imports from the package root. Geist and Geist Mono are bundled.
|
|
51
93
|
|
|
52
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
117
|
+
## Built for AI agents
|
|
72
118
|
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/bin/harebear-ui.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
}),
|
|
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__ */
|
|
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
|
-
/**
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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 };
|