@annondeveloper/ui-kit 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -1,215 +1,1551 @@
1
1
  # @annondeveloper/ui-kit
2
2
 
3
- Production-grade React UI component library with dark/light theme support, built on Tailwind CSS v4, Framer Motion, and Radix UI primitives.
3
+ > The React component library for monitoring dashboards, infrastructure tools, and professional applications.
4
4
 
5
- ## Features
5
+ 53 components • Dark/Light theme • Accessible • AI-ready • Real-time primitives • Tree-shakeable
6
6
 
7
- - **17 components** — Button, DataTable, Select, Badge, ConfirmDialog, and more
8
- - **Dark/light theme** — CSS custom properties, toggleable via `.light` class on `<html>`
9
- - **Accessible** — Radix UI primitives for Select, Dialog, Tooltip
10
- - **Animated** — Framer Motion with `useReducedMotion()` respect
11
- - **Responsive** — Desktop-first, mobile-capable
12
- - **Tree-shakeable** — ESM-only, import only what you use
13
- - **Type-safe** — Full TypeScript with exported types
7
+ [![npm](https://img.shields.io/npm/v/@annondeveloper/ui-kit)](https://www.npmjs.com/package/@annondeveloper/ui-kit)
8
+ &nbsp;
9
+ [GitHub](https://github.com/annondeveloper/ui-kit)
10
+ &nbsp;&bull;&nbsp;
11
+ [JSR](https://jsr.io/@annondeveloper/ui-kit)
12
+ &nbsp;&bull;&nbsp;
13
+ [Demo](https://annondeveloper.github.io/ui-kit)
14
14
 
15
- ## Install
15
+ ---
16
+
17
+ ## Quick Start
18
+
19
+ ### Install
16
20
 
17
21
  ```bash
22
+ # npm
18
23
  npm install @annondeveloper/ui-kit
24
+
25
+ # pnpm
26
+ pnpm add @annondeveloper/ui-kit
27
+
28
+ # yarn
29
+ yarn add @annondeveloper/ui-kit
30
+
31
+ # bun
32
+ bun add @annondeveloper/ui-kit
33
+
34
+ # jsr
35
+ npx jsr add @annondeveloper/ui-kit
19
36
  ```
20
37
 
21
38
  ### Peer Dependencies
22
39
 
23
40
  ```bash
24
41
  npm install react react-dom framer-motion lucide-react clsx tailwind-merge sonner \
25
- @radix-ui/react-select @radix-ui/react-alert-dialog @radix-ui/react-tooltip
42
+ @radix-ui/react-select @radix-ui/react-alert-dialog @radix-ui/react-tooltip \
43
+ @radix-ui/react-popover @radix-ui/react-dropdown-menu
26
44
  ```
27
45
 
28
- ## Setup
46
+ `react-hook-form` is an optional peer dependency -- only needed if you use the `@annondeveloper/ui-kit/form` entry point.
29
47
 
30
- ### 1. Import the theme CSS
48
+ ### Theme Setup
49
+
50
+ Import the theme CSS once in your app's root layout:
31
51
 
32
52
  ```tsx
33
- // app/layout.tsx or main entry
34
- import '@annondeveloper/ui-kit/src/theme.css'
53
+ import '@annondeveloper/ui-kit/theme.css'
35
54
  ```
36
55
 
37
- Or copy the CSS custom properties into your own `globals.css`.
56
+ Or copy the CSS custom properties into your own stylesheet. See [Theme System](#theme-system) below.
38
57
 
39
- ### 2. Use components
58
+ ### Minimal Example
40
59
 
41
60
  ```tsx
42
- import { Button, DataTable, Badge, Select, AnimatedCounter } from '@annondeveloper/ui-kit'
61
+ import { Button, MetricCard, StatusBadge, Toaster } from '@annondeveloper/ui-kit'
62
+ import { Activity } from 'lucide-react'
43
63
 
44
64
  function App() {
45
65
  return (
46
- <Button variant="primary" loading={isPending}>
47
- Save Changes
48
- </Button>
66
+ <>
67
+ <MetricCard label="CPU Usage" value={72.4} format={n => `${n.toFixed(1)}%`} icon={Activity} status="warning" />
68
+ <StatusBadge status="active" pulse />
69
+ <Button variant="primary" loading={isPending}>Deploy</Button>
70
+ <Toaster />
71
+ </>
49
72
  )
50
73
  }
51
74
  ```
52
75
 
53
- ## Components
54
-
55
- ### Layout & Feedback
76
+ ---
56
77
 
57
- | Component | Description |
58
- |-----------|-------------|
59
- | `Button` | 5 variants (primary/secondary/danger/outline/ghost), 4 sizes, loading state |
60
- | `Badge` | Color-coded label with 10 color presets |
61
- | `EmptyState` | Placeholder with icon, title, description, optional actions |
62
- | `Skeleton` / `SkeletonText` / `SkeletonCard` | Loading placeholders with shimmer animation |
63
- | `SuccessCheckmark` | Animated SVG checkmark for success states |
64
- | `StatusBadge` | Configurable status indicator (accepts custom status map) |
65
- | `StatusPulse` | Animated status dot with configurable pulse behavior |
78
+ ## Why This Library?
66
79
 
67
- ### Forms
80
+ Most component libraries are built for CRUD apps. This one is built for **ops dashboards, infrastructure tools, and AI-powered interfaces** -- where data density, real-time updates, and status visualization are first-class concerns.
68
81
 
69
- | Component | Description |
70
- |-----------|-------------|
71
- | `Select` | Radix UI select with theme-safe styling |
72
- | `Checkbox` | Forwarded ref checkbox with indeterminate support |
73
- | `ToggleSwitch` | Icon-based boolean toggle |
74
- | `FormInput` | Labeled input with hint text + shared class constants |
75
- | `FilterPill` | Rounded filter toggle pill with optional count |
82
+ - **Smart Components** -- SmartTable auto-generates filter suggestions (outliers, top-N, patterns). CommandBar provides fuzzy search with recent history.
83
+ - **AI-Ready** -- StreamingText renders LLM output with a blinking cursor. TypingIndicator, ConfidenceBar, and copy-on-complete are built in.
84
+ - **Real-Time** -- RealtimeValue tracks data freshness with connection state indicators and stale-data dimming. LiveFeed auto-scrolls with pause controls. NotificationStack supports auto-dismiss with progress bars.
85
+ - **Monitoring** -- MetricCard with trend arrows and sparklines. ThresholdGauge with color-coded zones. UtilizationBar, UptimeTracker, PortStatusGrid, and PipelineStage for infrastructure dashboards.
86
+ - **Data-Dense** -- DataTable with 10+ features in one import (search, column filters, sort, pagination, density control, column picker, CSV export, row animations). HeatmapCalendar, SeverityTimeline, LogViewer for high-density displays.
87
+ - **Developer Experience** -- CLI for scaffolding (`npx @annondeveloper/ui-kit add button`). react-hook-form adapters. Zero config theming. Full TypeScript coverage with exported types.
76
88
 
77
- ### Data Display
89
+ ---
78
90
 
79
- | Component | Description |
80
- |-----------|-------------|
81
- | `DataTable` | Full-featured table — sort, filter, search, paginate, density, column picker, CSV export |
82
- | `AnimatedCounter` | Smooth number animation with rAF easing |
83
- | `TruncatedText` | Auto-truncation with Radix tooltip + copy-to-clipboard |
91
+ ## Component Reference
84
92
 
85
- ### Overlays
93
+ ### Core
86
94
 
87
- | Component | Description |
88
- |-----------|-------------|
89
- | `ConfirmDialog` | Radix AlertDialog with Framer Motion animations, danger variant |
90
- | `Toaster` / `toast` | Sonner wrapper with theme-aware styling |
95
+ #### Button
91
96
 
92
- ## Utilities
97
+ A themed button with variant, size, and loading support.
93
98
 
94
99
  ```tsx
95
- import { cn, fmtBytes, fmtRelative, fmtBps, fmtPct } from '@annondeveloper/ui-kit'
100
+ import { Button } from '@annondeveloper/ui-kit'
96
101
 
97
- // Tailwind class merging
98
- cn('px-4 py-2', isActive && 'bg-blue-500', className)
99
-
100
- // Formatters
101
- fmtBytes(1073741824) // "1.0 GB"
102
- fmtRelative('2024-01-01') // "3 months ago"
103
- fmtBps(1000000000) // "1.0 Gbps"
104
- fmtPct(0.956) // "95.6%"
102
+ <Button variant="primary" size="md" loading={isPending}>Save</Button>
105
103
  ```
106
104
 
107
- ## Theme System
105
+ | Prop | Type | Default | Description |
106
+ |------|------|---------|-------------|
107
+ | `variant` | `'primary' \| 'secondary' \| 'danger' \| 'outline' \| 'ghost'` | `'primary'` | Visual variant |
108
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'icon'` | `'md'` | Size preset |
109
+ | `loading` | `boolean` | `false` | Show spinner and disable interaction |
108
110
 
109
- The library uses CSS custom properties for theming. Dark mode is the default; add `.light` class to `<html>` for light mode.
111
+ Extends all native `<button>` attributes. Supports `ref` forwarding.
110
112
 
111
- ```css
112
- /* Dark mode (default) */
113
- :root {
114
- --bg-base: 220 15% 8%;
115
- --bg-surface: 220 14% 11%;
116
- --text-primary: 220 10% 93%;
117
- --brand-primary: 217 91% 60%;
118
- --status-ok: 142 71% 45%;
119
- --status-critical: 0 84% 60%;
120
- }
113
+ ---
121
114
 
122
- /* Light mode */
123
- .light {
124
- --bg-base: 220 14% 96%;
125
- --bg-surface: 0 0% 100%;
126
- --text-primary: 220 14% 10%;
127
- }
115
+ #### Badge
116
+
117
+ A pill-shaped label with 10 color presets and optional icon.
118
+
119
+ ```tsx
120
+ import { Badge, createBadgeVariant } from '@annondeveloper/ui-kit'
121
+ import { Shield } from 'lucide-react'
122
+
123
+ <Badge color="green" icon={Shield} size="sm">Secure</Badge>
128
124
  ```
129
125
 
130
- All components use `hsl(var(--token))` syntax never hardcoded colors.
126
+ | Prop | Type | Default | Description |
127
+ |------|------|---------|-------------|
128
+ | `children` | `ReactNode` | -- | Badge content |
129
+ | `color` | `'brand' \| 'blue' \| 'green' \| 'yellow' \| 'red' \| 'orange' \| 'purple' \| 'pink' \| 'teal' \| 'gray'` | `'gray'` | Color preset |
130
+ | `icon` | `LucideIcon` | -- | Optional icon before the label |
131
+ | `size` | `'xs' \| 'sm' \| 'md'` | `'sm'` | Size variant |
131
132
 
132
- ## Badge Factory
133
+ ##### Badge Factory
133
134
 
134
- Create domain-specific badge variants without modifying the library:
135
+ Create domain-specific badge components without modifying the library:
135
136
 
136
137
  ```tsx
137
- import { createBadgeVariant } from '@annondeveloper/ui-kit'
138
-
139
138
  const SeverityBadge = createBadgeVariant({
140
139
  colorMap: { critical: 'red', warning: 'yellow', info: 'blue' },
141
140
  labelMap: { critical: 'Critical', warning: 'Warning', info: 'Info' },
142
141
  })
143
142
 
144
- // Usage: <SeverityBadge value="critical" />
143
+ <SeverityBadge value="critical" />
145
144
  ```
146
145
 
147
- ## StatusBadge Configuration
146
+ ---
147
+
148
+ #### Card
149
+
150
+ A styled card container with subcomponents for semantic structure.
148
151
 
149
152
  ```tsx
150
- import { StatusBadge, defaultStatusMap } from '@annondeveloper/ui-kit'
153
+ import { Card, CardHeader, CardTitle, CardContent } from '@annondeveloper/ui-kit'
154
+
155
+ <Card variant="interactive" padding="lg">
156
+ <CardHeader><CardTitle>Server Health</CardTitle></CardHeader>
157
+ <CardContent>...</CardContent>
158
+ </Card>
159
+ ```
151
160
 
152
- // Use defaults
153
- <StatusBadge status="ok" />
161
+ | Prop | Type | Default | Description |
162
+ |------|------|---------|-------------|
163
+ | `variant` | `'default' \| 'elevated' \| 'outlined' \| 'interactive'` | `'default'` | Visual variant (`interactive` adds hover effect) |
164
+ | `padding` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'` | Padding preset |
154
165
 
155
- // Or provide custom status map
156
- const myStatuses = {
157
- healthy: { label: 'Healthy', color: 'hsl(var(--status-ok))', dot: 'bg-green-500' },
158
- degraded: { label: 'Degraded', color: 'hsl(var(--status-warning))', dot: 'bg-yellow-500' },
159
- }
160
- <StatusBadge status="healthy" statusMap={myStatuses} />
166
+ Subcomponents: `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`. All support `ref` forwarding.
167
+
168
+ ---
169
+
170
+ #### Tabs
171
+
172
+ Accessible tabbed interface with three visual variants and animated indicator.
173
+
174
+ ```tsx
175
+ import { Tabs } from '@annondeveloper/ui-kit'
176
+
177
+ <Tabs tabs={[{ value: 'metrics', label: 'Metrics' }, { value: 'logs', label: 'Logs' }]}
178
+ value={tab} onChange={setTab} variant="pills" />
161
179
  ```
162
180
 
163
- ## DataTable
181
+ | Prop | Type | Default | Description |
182
+ |------|------|---------|-------------|
183
+ | `tabs` | `Tab[]` | -- | Array of `{ value, label, icon?, disabled? }` |
184
+ | `value` | `string` | -- | Currently selected tab value |
185
+ | `onChange` | `(value: string) => void` | -- | Selection callback |
186
+ | `variant` | `'underline' \| 'pills' \| 'enclosed'` | `'underline'` | Visual variant |
187
+ | `size` | `'sm' \| 'md'` | `'md'` | Size preset |
164
188
 
165
- The most feature-rich component a full data grid built on TanStack Table:
189
+ Supports keyboard navigation (arrow keys, Home, End) and ARIA roles. Animated indicator uses Framer Motion `layoutId`.
190
+
191
+ ---
192
+
193
+ #### Sheet
194
+
195
+ A slide-over drawer panel from any screen edge.
166
196
 
167
197
  ```tsx
168
- import { DataTable } from '@annondeveloper/ui-kit'
169
- import type { ColumnDef } from '@tanstack/react-table'
198
+ import { Sheet } from '@annondeveloper/ui-kit'
199
+
200
+ <Sheet open={isOpen} onClose={() => setOpen(false)} title="Settings" side="right" width="max-w-lg">
201
+ {children}
202
+ </Sheet>
203
+ ```
204
+
205
+ | Prop | Type | Default | Description |
206
+ |------|------|---------|-------------|
207
+ | `open` | `boolean` | -- | Whether the sheet is open |
208
+ | `onClose` | `() => void` | -- | Close callback |
209
+ | `side` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | Slide-in edge |
210
+ | `title` | `string` | -- | Header title |
211
+ | `description` | `string` | -- | Header description |
212
+ | `width` | `string` | `'max-w-md'` | Width/height class |
213
+
214
+ Features: backdrop blur, spring animation, Escape to close, body scroll lock, auto-focus.
215
+
216
+ ---
217
+
218
+ #### Tooltip
219
+
220
+ Simple tooltip wrapper built on Radix UI with theme-styled content and arrow pointer.
221
+
222
+ ```tsx
223
+ import { Tooltip } from '@annondeveloper/ui-kit'
224
+
225
+ <Tooltip content="Copy to clipboard" side="top"><button>...</button></Tooltip>
226
+ ```
227
+
228
+ | Prop | Type | Default | Description |
229
+ |------|------|---------|-------------|
230
+ | `content` | `ReactNode` | -- | Tooltip content |
231
+ | `children` | `ReactNode` | -- | Trigger element |
232
+ | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | Display side |
233
+ | `delay` | `number` | `200` | Delay in ms |
234
+
235
+ ---
236
+
237
+ #### Popover
238
+
239
+ Popover wrapper built on Radix Popover with Framer Motion entry animation.
240
+
241
+ ```tsx
242
+ import { Popover } from '@annondeveloper/ui-kit'
243
+
244
+ <Popover trigger={<button>Options</button>} side="bottom" align="end">
245
+ <p>Popover content</p>
246
+ </Popover>
247
+ ```
248
+
249
+ | Prop | Type | Default | Description |
250
+ |------|------|---------|-------------|
251
+ | `trigger` | `ReactNode` | -- | Element that opens the popover |
252
+ | `children` | `ReactNode` | -- | Popover content |
253
+ | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | Display side |
254
+ | `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment |
255
+
256
+ ---
257
+
258
+ #### DropdownMenu
259
+
260
+ Action/context dropdown menu built on Radix DropdownMenu with danger variant support.
261
+
262
+ ```tsx
263
+ import { DropdownMenu } from '@annondeveloper/ui-kit'
264
+ import { Edit, Trash2 } from 'lucide-react'
265
+
266
+ <DropdownMenu
267
+ trigger={<button>Actions</button>}
268
+ items={[
269
+ { label: 'Edit', icon: Edit, onClick: handleEdit },
270
+ { label: 'Delete', icon: Trash2, onClick: handleDelete, variant: 'danger' },
271
+ ]}
272
+ />
273
+ ```
274
+
275
+ | Prop | Type | Default | Description |
276
+ |------|------|---------|-------------|
277
+ | `trigger` | `ReactNode` | -- | Element that opens the menu |
278
+ | `items` | `MenuItem[]` | -- | `{ label, icon?, onClick, variant?, disabled? }` |
279
+ | `align` | `'start' \| 'center' \| 'end'` | `'end'` | Menu alignment |
280
+
281
+ ---
282
+
283
+ #### ConfirmDialog
284
+
285
+ Confirmation modal built on Radix AlertDialog with animated enter/exit.
286
+
287
+ ```tsx
288
+ import { ConfirmDialog } from '@annondeveloper/ui-kit'
289
+
290
+ <ConfirmDialog
291
+ open={showDelete} onOpenChange={setShowDelete}
292
+ title="Delete server?" description="This action cannot be undone."
293
+ variant="danger" confirmLabel="Delete" loading={isDeleting}
294
+ onConfirm={handleDelete}
295
+ />
296
+ ```
297
+
298
+ | Prop | Type | Default | Description |
299
+ |------|------|---------|-------------|
300
+ | `open` | `boolean` | -- | Control open state |
301
+ | `onOpenChange` | `(open: boolean) => void` | -- | Open state callback |
302
+ | `title` | `string` | -- | Dialog title |
303
+ | `description` | `string` | -- | Dialog description |
304
+ | `confirmLabel` | `string` | `'Confirm'` | Confirm button text |
305
+ | `cancelLabel` | `string` | `'Cancel'` | Cancel button text |
306
+ | `variant` | `'danger' \| 'warning' \| 'default'` | `'danger'` | Icon and button color |
307
+ | `loading` | `boolean` | `false` | Show spinner on confirm button |
308
+ | `onConfirm` | `() => void` | -- | Confirm callback |
309
+
310
+ ---
311
+
312
+ ### Forms
313
+
314
+ #### FormInput
315
+
316
+ Themed form input with label, required indicator, and optional hint text.
317
+
318
+ ```tsx
319
+ import { FormInput } from '@annondeveloper/ui-kit'
320
+
321
+ <FormInput label="Hostname" value={host} onChange={setHost} placeholder="10.0.0.1" required hint="IPv4 or FQDN" />
322
+ ```
323
+
324
+ | Prop | Type | Default | Description |
325
+ |------|------|---------|-------------|
326
+ | `label` | `string` | -- | Label text |
327
+ | `value` | `string` | -- | Input value |
328
+ | `onChange` | `(value: string) => void` | -- | Value change callback |
329
+ | `type` | `string` | `'text'` | HTML input type |
330
+ | `placeholder` | `string` | -- | Placeholder text |
331
+ | `required` | `boolean` | -- | Show required indicator |
332
+ | `disabled` | `boolean` | -- | Disable input |
333
+ | `hint` | `string` | -- | Help text below input |
334
+ | `autoComplete` | `string` | -- | Autocomplete attribute |
335
+
336
+ Also exports `INPUT_CLS`, `LABEL_CLS`, and `TEXTAREA_CLS` constants for building custom inputs with consistent styling.
337
+
338
+ ---
339
+
340
+ #### Select
341
+
342
+ Themed dropdown built on Radix UI Select.
343
+
344
+ ```tsx
345
+ import { Select } from '@annondeveloper/ui-kit'
346
+
347
+ <Select value={protocol} onValueChange={setProtocol}
348
+ options={[{ value: 'snmp', label: 'SNMP' }, { value: 'ssh', label: 'SSH' }]}
349
+ placeholder="Select protocol" />
350
+ ```
351
+
352
+ | Prop | Type | Default | Description |
353
+ |------|------|---------|-------------|
354
+ | `value` | `string` | -- | Selected value |
355
+ | `onValueChange` | `(v: string) => void` | -- | Selection callback |
356
+ | `options` | `SelectOption[]` | -- | `{ value, label }` |
357
+ | `placeholder` | `string` | -- | Placeholder text |
358
+ | `disabled` | `boolean` | -- | Disable select |
359
+
360
+ ---
361
+
362
+ #### Checkbox
363
+
364
+ Themed checkbox with indeterminate state support. Forwards ref.
365
+
366
+ ```tsx
367
+ import { Checkbox } from '@annondeveloper/ui-kit'
368
+
369
+ <Checkbox checked={selected} onChange={handleChange} indeterminate={isPartial} size="sm" />
370
+ ```
371
+
372
+ | Prop | Type | Default | Description |
373
+ |------|------|---------|-------------|
374
+ | `indeterminate` | `boolean` | -- | Show minus indicator |
375
+ | `size` | `'sm' \| 'md'` | `'md'` | Size variant |
376
+
377
+ Extends native `<input>` attributes (except `type` and `size`).
378
+
379
+ ---
380
+
381
+ #### ToggleSwitch
382
+
383
+ Icon-based boolean toggle using lucide icons.
384
+
385
+ ```tsx
386
+ import { ToggleSwitch } from '@annondeveloper/ui-kit'
387
+
388
+ <ToggleSwitch enabled={isActive} onChange={setIsActive} label="Auto-refresh" />
389
+ ```
390
+
391
+ | Prop | Type | Default | Description |
392
+ |------|------|---------|-------------|
393
+ | `enabled` | `boolean` | -- | Toggle state |
394
+ | `onChange` | `(enabled: boolean) => void` | -- | Change callback |
395
+ | `size` | `'sm' \| 'md'` | `'md'` | Size variant |
396
+ | `disabled` | `boolean` | -- | Disable toggle |
397
+ | `label` | `string` | -- | Accessible label |
398
+
399
+ ---
400
+
401
+ #### RadioGroup
402
+
403
+ Custom-styled radio button group with animated selection indicator.
404
+
405
+ ```tsx
406
+ import { RadioGroup } from '@annondeveloper/ui-kit'
407
+
408
+ <RadioGroup
409
+ options={[
410
+ { value: 'v2c', label: 'SNMPv2c' },
411
+ { value: 'v3', label: 'SNMPv3', description: 'Recommended for production' },
412
+ ]}
413
+ value={version} onChange={setVersion} orientation="vertical"
414
+ />
415
+ ```
416
+
417
+ | Prop | Type | Default | Description |
418
+ |------|------|---------|-------------|
419
+ | `options` | `RadioOption[]` | -- | `{ value, label, description?, disabled? }` |
420
+ | `value` | `string` | -- | Selected value |
421
+ | `onChange` | `(value: string) => void` | -- | Selection callback |
422
+ | `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Layout direction |
423
+
424
+ Supports keyboard navigation (arrow keys, Home, End) and ARIA roles.
425
+
426
+ ---
427
+
428
+ #### Slider
429
+
430
+ Custom range slider with keyboard accessibility and hover tooltip.
431
+
432
+ ```tsx
433
+ import { Slider } from '@annondeveloper/ui-kit'
434
+
435
+ <Slider value={threshold} onChange={setThreshold} min={0} max={100} step={5} label="Alert threshold" showValue />
436
+ ```
437
+
438
+ | Prop | Type | Default | Description |
439
+ |------|------|---------|-------------|
440
+ | `value` | `number` | -- | Current value |
441
+ | `onChange` | `(value: number) => void` | -- | Change callback |
442
+ | `min` | `number` | `0` | Minimum value |
443
+ | `max` | `number` | `100` | Maximum value |
444
+ | `step` | `number` | `1` | Step increment |
445
+ | `label` | `string` | -- | Label text |
446
+ | `showValue` | `boolean` | `false` | Show value display |
447
+
448
+ Supports keyboard (arrow keys, Home, End), mouse drag, and touch.
449
+
450
+ ---
451
+
452
+ #### ColorInput
453
+
454
+ Compact color picker with expandable panel, hue/saturation area, format switching, and presets.
455
+
456
+ ```tsx
457
+ import { ColorInput } from '@annondeveloper/ui-kit'
458
+
459
+ <ColorInput value={color} onChange={setColor} label="Accent" format="hex"
460
+ presets={['#3b82f6', '#10b981', '#f59e0b', '#ef4444']} />
461
+ ```
462
+
463
+ | Prop | Type | Default | Description |
464
+ |------|------|---------|-------------|
465
+ | `value` | `string` | -- | Hex color string |
466
+ | `onChange` | `(color: string) => void` | -- | Change callback |
467
+ | `label` | `string` | -- | Optional label |
468
+ | `presets` | `string[]` | -- | Preset color swatches |
469
+ | `showAlpha` | `boolean` | `false` | Show alpha slider |
470
+ | `format` | `'hex' \| 'rgb' \| 'hsl'` | `'hex'` | Display format |
471
+
472
+ Features: HSL picker area, hue slider, format switching, clipboard copy, recent color history (localStorage).
473
+
474
+ ---
475
+
476
+ #### StepWizard
477
+
478
+ Multi-step form wizard with animated slide transitions and per-step validation.
479
+
480
+ ```tsx
481
+ import { StepWizard } from '@annondeveloper/ui-kit'
482
+
483
+ <StepWizard
484
+ steps={[
485
+ { id: 'creds', title: 'Credentials', content: <CredentialsForm /> },
486
+ { id: 'targets', title: 'Targets', content: <TargetForm />, validate: () => targets.length > 0 },
487
+ { id: 'review', title: 'Review', content: <ReviewSummary /> },
488
+ ]}
489
+ onComplete={handleDeploy} showSummary
490
+ />
491
+ ```
492
+
493
+ | Prop | Type | Default | Description |
494
+ |------|------|---------|-------------|
495
+ | `steps` | `WizardStep[]` | -- | `{ id, title, description?, icon?, content, validate? }` |
496
+ | `onComplete` | `() => void` | -- | Final step completion callback |
497
+ | `onStepChange` | `(step: number) => void` | -- | Step change callback |
498
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Step indicator layout |
499
+ | `allowSkip` | `boolean` | `false` | Allow skipping to uncompleted steps |
500
+ | `showSummary` | `boolean` | `false` | Show completion state after last step |
501
+
502
+ Features: animated step transitions, progress bar, keyboard navigation (Enter to advance), session storage auto-save, async validation.
503
+
504
+ ---
505
+
506
+ #### FilterPill
507
+
508
+ Rounded pill-style filter toggle with optional count.
509
+
510
+ ```tsx
511
+ import { FilterPill } from '@annondeveloper/ui-kit'
512
+
513
+ <FilterPill label="Critical" count={12} active={filter === 'critical'} onClick={() => setFilter('critical')} />
514
+ ```
515
+
516
+ | Prop | Type | Default | Description |
517
+ |------|------|---------|-------------|
518
+ | `label` | `string` | -- | Pill text |
519
+ | `count` | `number` | -- | Optional count badge |
520
+ | `active` | `boolean` | -- | Active/selected state |
521
+ | `onClick` | `() => void` | -- | Click handler |
170
522
 
171
- const columns: ColumnDef<User>[] = [
172
- { id: 'name', header: 'Name', accessorKey: 'name' },
173
- { id: 'email', header: 'Email', accessorKey: 'email' },
174
- { id: 'role', header: 'Role', accessorKey: 'role', size: 100 },
175
- ]
523
+ ---
524
+
525
+ ### Data Display
526
+
527
+ #### DataTable
528
+
529
+ Full-featured data grid built on TanStack Table v8.
530
+
531
+ ```tsx
532
+ import { DataTable } from '@annondeveloper/ui-kit'
176
533
 
177
534
  <DataTable
178
- columns={columns}
179
- data={users}
180
- isLoading={isLoading}
181
- searchPlaceholder="Search users..."
182
- defaultPageSize={25}
183
- exportFilename="users"
184
- onRowClick={(row) => router.push(`/users/${row.id}`)}
185
- emptyState={{ icon: Users, title: 'No users', description: 'Get started by inviting team members.' }}
535
+ columns={columns} data={devices} isLoading={isLoading}
536
+ searchPlaceholder="Search devices..."
537
+ defaultPageSize={25} exportFilename="devices"
538
+ onRowClick={(row) => router.push(`/devices/${row.id}`)}
539
+ emptyState={{ icon: Server, title: 'No devices', description: 'Run a discovery scan to get started.' }}
186
540
  />
187
541
  ```
188
542
 
189
- Features: global search, per-column filters (text/enum/number), multi-column sort, pagination, density control (compact/comfortable/spacious), column visibility picker, CSV export, row click handler, loading skeleton, empty state, Framer Motion row animations.
543
+ | Prop | Type | Default | Description |
544
+ |------|------|---------|-------------|
545
+ | `columns` | `ColumnDef<T>[]` | -- | TanStack column definitions |
546
+ | `data` | `T[]` | -- | Row data |
547
+ | `isLoading` | `boolean` | `false` | Show loading skeleton |
548
+ | `emptyState` | `{ icon, title, description }` | -- | Custom empty state |
549
+ | `searchPlaceholder` | `string` | `'Search...'` | Search input placeholder |
550
+ | `onRowClick` | `(row: T) => void` | -- | Row click handler |
551
+ | `defaultSort` | `{ id, desc }` | -- | Initial sort configuration |
552
+ | `defaultPageSize` | `number` | `25` | Rows per page |
553
+ | `exportFilename` | `string` | -- | Enables CSV export button |
554
+ | `stickyFirstColumn` | `boolean` | `false` | Sticky first column on scroll |
555
+ | `density` | `'compact' \| 'comfortable' \| 'spacious'` | `'comfortable'` | Row density |
190
556
 
191
- ## Publishing
557
+ Built-in features: global search, auto-detected column filters (text/enum/number range), multi-column sort, pagination, density control (persisted to localStorage), column visibility picker with drag reorder, CSV export, row click, loading skeleton, empty state, Framer Motion row animations.
192
558
 
193
- ### GitHub Packages
559
+ ---
194
560
 
195
- ```bash
196
- npm login --registry=https://npm.pkg.github.com --scope=@annondeveloper
197
- npm publish
561
+ #### SmartTable
562
+
563
+ Enhanced DataTable that auto-generates filter suggestions by analyzing column data.
564
+
565
+ ```tsx
566
+ import { SmartTable } from '@annondeveloper/ui-kit'
567
+
568
+ <SmartTable columns={columns} data={data} maxSuggestions={4} onFilterSuggestion={console.log} />
198
569
  ```
199
570
 
200
- ### npm
571
+ | Prop | Type | Default | Description |
572
+ |------|------|---------|-------------|
573
+ | ...all DataTable props | | | Inherits all DataTable props |
574
+ | `onFilterSuggestion` | `(suggestion: FilterSuggestion) => void` | -- | Suggestion click callback |
575
+ | `maxSuggestions` | `number` | `6` | Max visible suggestions |
201
576
 
202
- ```bash
203
- npm login
204
- npm publish --access public
577
+ Auto-detects: statistical outliers (>2 std dev), top-N frequent values, dominant pattern detection, minority value highlighting.
578
+
579
+ ---
580
+
581
+ #### AnimatedCounter
582
+
583
+ Smooth number animation using requestAnimationFrame with cubic easing.
584
+
585
+ ```tsx
586
+ import { AnimatedCounter } from '@annondeveloper/ui-kit'
587
+
588
+ <AnimatedCounter value={deviceCount} duration={400} format={n => n.toLocaleString()} />
205
589
  ```
206
590
 
207
- ### JSR
591
+ | Prop | Type | Default | Description |
592
+ |------|------|---------|-------------|
593
+ | `value` | `number` | -- | Target value |
594
+ | `duration` | `number` | `400` | Animation duration (ms) |
595
+ | `format` | `(n: number) => string` | -- | Custom number formatter |
208
596
 
209
- ```bash
210
- npx jsr publish
597
+ ---
598
+
599
+ #### Sparkline
600
+
601
+ Tiny inline SVG sparkline chart with gradient fill.
602
+
603
+ ```tsx
604
+ import { Sparkline } from '@annondeveloper/ui-kit'
605
+
606
+ <Sparkline data={[10, 25, 18, 30, 22]} width={80} height={24} showDots />
607
+ ```
608
+
609
+ | Prop | Type | Default | Description |
610
+ |------|------|---------|-------------|
611
+ | `data` | `number[]` | -- | Values to plot (min 2) |
612
+ | `width` | `number` | `80` | SVG width |
613
+ | `height` | `number` | `24` | SVG height |
614
+ | `color` | `string` | `'hsl(var(--brand-primary))'` | Line color |
615
+ | `fillOpacity` | `number` | `0.1` | Gradient fill opacity (0 to disable) |
616
+ | `showDots` | `boolean` | `false` | Dots on first/last points |
617
+
618
+ ---
619
+
620
+ #### TruncatedText
621
+
622
+ Auto-truncating text with tooltip and copy-to-clipboard on hover.
623
+
624
+ ```tsx
625
+ import { TruncatedText } from '@annondeveloper/ui-kit'
626
+
627
+ <TruncatedText text="very-long-hostname-that-overflows.example.com" maxWidth={200} />
628
+ ```
629
+
630
+ | Prop | Type | Default | Description |
631
+ |------|------|---------|-------------|
632
+ | `text` | `string` | -- | Full text content |
633
+ | `maxWidth` | `string \| number` | `'100%'` | Max width for truncation |
634
+
635
+ ---
636
+
637
+ #### CopyBlock
638
+
639
+ Monospace code/text display with one-click copy and collapsible overflow.
640
+
641
+ ```tsx
642
+ import { CopyBlock } from '@annondeveloper/ui-kit'
643
+
644
+ <CopyBlock content="curl -X GET https://api.example.com/health" language="bash" label="Health check" />
645
+ ```
646
+
647
+ | Prop | Type | Default | Description |
648
+ |------|------|---------|-------------|
649
+ | `content` | `string` | -- | Text content |
650
+ | `language` | `string` | -- | Language hint (e.g. "json", "bash") |
651
+ | `showLineNumbers` | `boolean` | `false` | Show line number gutter |
652
+ | `maxHeight` | `number` | -- | Max height before "Show more" toggle |
653
+ | `label` | `string` | -- | Label above the block |
654
+
655
+ ---
656
+
657
+ #### DiffViewer
658
+
659
+ Line-by-line diff viewer using LCS algorithm with collapsible unchanged sections.
660
+
661
+ ```tsx
662
+ import { DiffViewer } from '@annondeveloper/ui-kit'
663
+
664
+ <DiffViewer oldValue={previousConfig} newValue={currentConfig} mode="side-by-side" showLineNumbers />
665
+ ```
666
+
667
+ | Prop | Type | Default | Description |
668
+ |------|------|---------|-------------|
669
+ | `oldValue` | `string` | -- | Original text |
670
+ | `newValue` | `string` | -- | Modified text |
671
+ | `mode` | `'inline' \| 'side-by-side'` | `'inline'` | Display mode |
672
+ | `language` | `string` | -- | Language hint |
673
+ | `showLineNumbers` | `boolean` | `true` | Show line numbers |
674
+
675
+ ---
676
+
677
+ ### Status & Monitoring
678
+
679
+ #### StatusBadge
680
+
681
+ Configurable status indicator with colored dot and label.
682
+
683
+ ```tsx
684
+ import { StatusBadge, defaultStatusMap } from '@annondeveloper/ui-kit'
685
+
686
+ <StatusBadge status="active" pulse />
687
+ <StatusBadge status="healthy" statusMap={myCustomStatuses} />
688
+ ```
689
+
690
+ | Prop | Type | Default | Description |
691
+ |------|------|---------|-------------|
692
+ | `status` | `string` | -- | Status key |
693
+ | `label` | `string` | -- | Override display label |
694
+ | `size` | `'sm' \| 'md'` | `'md'` | Size variant |
695
+ | `pulse` | `boolean` | `false` | Pulse animation on the dot |
696
+ | `statusMap` | `Record<string, StatusConfig>` | `defaultStatusMap` | Custom status definitions |
697
+
698
+ Built-in statuses: `ok`, `active`, `warning`, `critical`, `unknown`, `maintenance`, `stale`, `inactive`, `decommissioned`, `pending`.
699
+
700
+ ---
701
+
702
+ #### StatusPulse
703
+
704
+ Animated status dot with optional pulse ring and label.
705
+
706
+ ```tsx
707
+ import { StatusPulse } from '@annondeveloper/ui-kit'
708
+
709
+ <StatusPulse status="online" />
710
+ <StatusPulse status="degraded" label={false} />
711
+ ```
712
+
713
+ | Prop | Type | Default | Description |
714
+ |------|------|---------|-------------|
715
+ | `status` | `string` | -- | Status key |
716
+ | `label` | `boolean` | `true` | Show status text |
717
+ | `configMap` | `Record<string, PulseConfig>` | `defaultPulseConfigMap` | Custom pulse configs |
718
+
719
+ Built-in statuses: `online` (green pulse), `degraded` (yellow fast pulse), `offline` (red, no pulse), `unknown` (gray, no pulse).
720
+
721
+ ---
722
+
723
+ #### MetricCard
724
+
725
+ Dashboard stat tile with animated counter, trend indicator, and optional sparkline.
726
+
727
+ ```tsx
728
+ import { MetricCard } from '@annondeveloper/ui-kit'
729
+ import { HardDrive } from 'lucide-react'
730
+
731
+ <MetricCard
732
+ label="Disk Usage" value={78.3} format={n => `${n.toFixed(1)}%`}
733
+ previousValue={72.1} trendDirection="up-bad"
734
+ icon={HardDrive} status="warning"
735
+ sparklineData={[65, 68, 70, 72, 75, 78]}
736
+ />
737
+ ```
738
+
739
+ | Prop | Type | Default | Description |
740
+ |------|------|---------|-------------|
741
+ | `label` | `string` | -- | Metric name |
742
+ | `value` | `number` | -- | Current value |
743
+ | `format` | `(n: number) => string` | -- | Custom formatter |
744
+ | `previousValue` | `number` | -- | Previous value for trend |
745
+ | `trendDirection` | `'up-good' \| 'up-bad' \| 'down-good' \| 'down-bad'` | -- | Color interpretation |
746
+ | `icon` | `ElementType` | -- | Lucide icon component |
747
+ | `status` | `'ok' \| 'warning' \| 'critical'` | -- | Left border accent color |
748
+ | `sparklineData` | `number[]` | -- | Recent values for inline chart |
749
+
750
+ ---
751
+
752
+ #### ThresholdGauge
753
+
754
+ Semicircular SVG gauge with color-coded threshold zones.
755
+
756
+ ```tsx
757
+ import { ThresholdGauge } from '@annondeveloper/ui-kit'
758
+
759
+ <ThresholdGauge value={85} label="CPU" thresholds={{ warning: 70, critical: 90 }} size={120} />
760
+ ```
761
+
762
+ | Prop | Type | Default | Description |
763
+ |------|------|---------|-------------|
764
+ | `value` | `number` | -- | Gauge value (0-100) |
765
+ | `label` | `string` | -- | Label below gauge |
766
+ | `thresholds` | `{ warning, critical }` | `{ warning: 70, critical: 90 }` | Color zone boundaries |
767
+ | `size` | `number` | `120` | Diameter in pixels |
768
+ | `showValue` | `boolean` | `true` | Show value in center |
769
+ | `format` | `(n: number) => string` | -- | Custom value formatter |
770
+
771
+ ---
772
+
773
+ #### UtilizationBar
774
+
775
+ Horizontal bar with color-coded thresholds. Animates fill on mount.
776
+
777
+ ```tsx
778
+ import { UtilizationBar } from '@annondeveloper/ui-kit'
779
+
780
+ <UtilizationBar value={92} label="Memory" thresholds={{ warning: 70, critical: 90 }} size="md" />
211
781
  ```
212
782
 
783
+ | Prop | Type | Default | Description |
784
+ |------|------|---------|-------------|
785
+ | `value` | `number` | -- | Utilization percentage (0-100) |
786
+ | `thresholds` | `{ warning, critical }` | `{ warning: 70, critical: 90 }` | Color zone boundaries |
787
+ | `label` | `string` | -- | Label to the left |
788
+ | `showValue` | `boolean` | `true` | Show percentage text |
789
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Bar height |
790
+ | `animated` | `boolean` | `true` | Animate fill width |
791
+
792
+ ---
793
+
794
+ #### UptimeTracker
795
+
796
+ GitHub/Statuspage-style daily uptime bar strip.
797
+
798
+ ```tsx
799
+ import { UptimeTracker } from '@annondeveloper/ui-kit'
800
+
801
+ <UptimeTracker
802
+ days={last90Days} label="API Server" showPercentage
803
+ onDayClick={(day) => showIncidents(day.date)}
804
+ />
805
+ ```
806
+
807
+ | Prop | Type | Default | Description |
808
+ |------|------|---------|-------------|
809
+ | `days` | `DayStatus[]` | -- | `{ date, status: 'ok'\|'degraded'\|'outage'\|'no-data', uptime? }` |
810
+ | `showPercentage` | `boolean` | `true` | Show overall uptime % |
811
+ | `label` | `string` | -- | Header label |
812
+ | `onDayClick` | `(day: DayStatus) => void` | -- | Day click callback |
813
+
814
+ ---
815
+
816
+ #### PortStatusGrid
817
+
818
+ Grid of colored indicators for network ports/interfaces.
819
+
820
+ ```tsx
821
+ import { PortStatusGrid } from '@annondeveloper/ui-kit'
822
+
823
+ <PortStatusGrid
824
+ ports={switchPorts} columns={24} size="md"
825
+ onPortClick={(port) => showPortDetail(port.id)}
826
+ />
827
+ ```
828
+
829
+ | Prop | Type | Default | Description |
830
+ |------|------|---------|-------------|
831
+ | `ports` | `PortStatus[]` | -- | `{ id, label, status: 'up'\|'down'\|'disabled'\|'error', speed?, utilization? }` |
832
+ | `columns` | `number` | auto-fit | Grid columns |
833
+ | `size` | `'sm' \| 'md'` | `'sm'` | Indicator size |
834
+ | `onPortClick` | `(port: PortStatus) => void` | -- | Port click callback |
835
+
836
+ ---
837
+
838
+ #### PipelineStage
839
+
840
+ Horizontal data pipeline visualization with status dots and chevron connectors.
841
+
842
+ ```tsx
843
+ import { PipelineStage } from '@annondeveloper/ui-kit'
844
+
845
+ <PipelineStage
846
+ stages={[
847
+ { name: 'Collector', status: 'active', metric: { label: 'msg/s', value: '1.2K' } },
848
+ { name: 'Normalizer', status: 'active' },
849
+ { name: 'Writer', status: 'error' },
850
+ ]}
851
+ onStageClick={(stage) => showStageDetail(stage)}
852
+ />
853
+ ```
854
+
855
+ | Prop | Type | Default | Description |
856
+ |------|------|---------|-------------|
857
+ | `stages` | `StageInfo[]` | -- | `{ name, status: 'active'\|'idle'\|'error'\|'disabled', metric?, icon? }` |
858
+ | `onStageClick` | `(stage, index) => void` | -- | Stage click callback |
859
+
860
+ ---
861
+
862
+ #### SeverityTimeline
863
+
864
+ Horizontal scrollable event timeline with severity-colored dots.
865
+
866
+ ```tsx
867
+ import { SeverityTimeline } from '@annondeveloper/ui-kit'
868
+
869
+ <SeverityTimeline events={recentAlerts} maxVisible={20} onEventClick={showAlertDetail} />
870
+ ```
871
+
872
+ | Prop | Type | Default | Description |
873
+ |------|------|---------|-------------|
874
+ | `events` | `TimelineEvent[]` | -- | `{ time, label, severity: 'critical'\|'warning'\|'info'\|'ok', detail? }` |
875
+ | `maxVisible` | `number` | `20` | Max visible events |
876
+ | `onEventClick` | `(event) => void` | -- | Event click callback |
877
+
878
+ ---
879
+
880
+ #### TimeRangeSelector
881
+
882
+ Compact horizontal pill-group time range selector.
883
+
884
+ ```tsx
885
+ import { TimeRangeSelector } from '@annondeveloper/ui-kit'
886
+
887
+ <TimeRangeSelector value={range} onChange={(v, r) => setRange(v)} />
888
+ ```
889
+
890
+ | Prop | Type | Default | Description |
891
+ |------|------|---------|-------------|
892
+ | `value` | `string` | -- | Selected range value |
893
+ | `onChange` | `(value, range) => void` | -- | Selection callback |
894
+ | `ranges` | `TimeRange[]` | `[1h, 6h, 24h, 7d, 30d]` | Custom range options (each `{ label, value, seconds }`) |
895
+
896
+ ---
897
+
898
+ #### LogViewer
899
+
900
+ Real-time log stream viewer with severity-colored borders and auto-scroll.
901
+
902
+ ```tsx
903
+ import { LogViewer } from '@annondeveloper/ui-kit'
904
+
905
+ <LogViewer entries={logs} maxHeight={400} autoScroll showTimestamps onEntryClick={showDetail} />
906
+ ```
907
+
908
+ | Prop | Type | Default | Description |
909
+ |------|------|---------|-------------|
910
+ | `entries` | `LogEntry[]` | -- | `{ time, level: 'error'\|'warn'\|'info'\|'debug'\|'trace', message, source? }` |
911
+ | `maxHeight` | `number` | `400` | Container max height |
912
+ | `autoScroll` | `boolean` | `true` | Auto-scroll on new entries |
913
+ | `showTimestamps` | `boolean` | `true` | Show timestamps column |
914
+ | `showLevel` | `boolean` | `true` | Show level badge |
915
+ | `onEntryClick` | `(entry) => void` | -- | Entry click callback |
916
+
917
+ Features: search filtering, "N new entries" floating badge when scrolled up.
918
+
919
+ ---
920
+
921
+ ### Real-Time & AI
922
+
923
+ #### RealtimeValue
924
+
925
+ Live data display with freshness tracking, connection state, and delta arrows.
926
+
927
+ ```tsx
928
+ import { RealtimeValue } from '@annondeveloper/ui-kit'
929
+ import { fmtBps } from '@annondeveloper/ui-kit'
930
+
931
+ <RealtimeValue
932
+ value={throughput} format={fmtBps}
933
+ previousValue={prevThroughput} lastUpdated={lastTs}
934
+ staleAfterMs={30000} connectionState="connected" size="lg"
935
+ />
936
+ ```
937
+
938
+ | Prop | Type | Default | Description |
939
+ |------|------|---------|-------------|
940
+ | `value` | `number \| string` | -- | Current value |
941
+ | `label` | `string` | -- | Label text |
942
+ | `format` | `(v: number) => string` | -- | Custom formatter |
943
+ | `lastUpdated` | `string \| Date` | -- | Last data timestamp |
944
+ | `staleAfterMs` | `number` | `30000` | Staleness threshold (ms) |
945
+ | `connectionState` | `'connected' \| 'reconnecting' \| 'disconnected'` | `'connected'` | Connection indicator |
946
+ | `previousValue` | `number` | -- | Previous value for delta |
947
+ | `animate` | `boolean` | `true` | Animate value changes |
948
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Display size |
949
+
950
+ Features: animated number transitions, freshness dot (green/yellow/red), ping animation when fresh, connection state icon (reconnecting spinner, disconnected icon), auto-updating relative timestamp.
951
+
952
+ ---
953
+
954
+ #### StreamingText
955
+
956
+ AI/LLM streaming text display with blinking cursor and inline markdown formatting.
957
+
958
+ ```tsx
959
+ import { StreamingText } from '@annondeveloper/ui-kit'
960
+
961
+ <StreamingText text={response} isStreaming={isGenerating} onComplete={() => logCompletion()} />
962
+ ```
963
+
964
+ | Prop | Type | Default | Description |
965
+ |------|------|---------|-------------|
966
+ | `text` | `string` | -- | Text content (grows as tokens arrive) |
967
+ | `isStreaming` | `boolean` | -- | Whether tokens are still arriving |
968
+ | `speed` | `number` | `500` | Cursor blink speed (ms) |
969
+ | `showCursor` | `boolean` | `true` | Show blinking cursor |
970
+ | `onComplete` | `() => void` | -- | Streaming complete callback |
971
+
972
+ Features: auto-scroll during streaming, inline `**bold**` and `` `code` `` formatting, copy button appears on completion.
973
+
974
+ ---
975
+
976
+ #### TypingIndicator
977
+
978
+ "Someone is typing" indicator with three animation variants.
979
+
980
+ ```tsx
981
+ import { TypingIndicator } from '@annondeveloper/ui-kit'
982
+
983
+ <TypingIndicator variant="dots" label="AI is thinking" size="md" />
984
+ ```
985
+
986
+ | Prop | Type | Default | Description |
987
+ |------|------|---------|-------------|
988
+ | `label` | `string` | -- | Status text |
989
+ | `variant` | `'dots' \| 'pulse' \| 'text'` | `'dots'` | Animation variant |
990
+ | `size` | `'sm' \| 'md'` | `'md'` | Size preset |
991
+
992
+ ---
993
+
994
+ #### ConfidenceBar
995
+
996
+ Horizontal probability bar with color-coded threshold zones.
997
+
998
+ ```tsx
999
+ import { ConfidenceBar } from '@annondeveloper/ui-kit'
1000
+
1001
+ <ConfidenceBar value={0.87} label="Match confidence" thresholds={{ low: 0.3, medium: 0.7 }} />
1002
+ ```
1003
+
1004
+ | Prop | Type | Default | Description |
1005
+ |------|------|---------|-------------|
1006
+ | `value` | `number` | -- | Confidence (0-1) |
1007
+ | `label` | `string` | -- | Label text |
1008
+ | `showPercentage` | `boolean` | `true` | Show percentage |
1009
+ | `thresholds` | `{ low, medium }` | `{ low: 0.3, medium: 0.7 }` | Color zone boundaries |
1010
+ | `size` | `'sm' \| 'md'` | `'md'` | Bar height |
1011
+
1012
+ ---
1013
+
1014
+ #### CommandBar
1015
+
1016
+ Universal command palette activated by Cmd+K / Ctrl+K.
1017
+
1018
+ ```tsx
1019
+ import { CommandBar } from '@annondeveloper/ui-kit'
1020
+
1021
+ <CommandBar
1022
+ items={[
1023
+ { id: 'devices', label: 'Go to Devices', shortcut: 'G D', onSelect: () => navigate('/devices') },
1024
+ { id: 'search', label: 'Search entities', icon: Search, onSelect: openSearch, keywords: ['find'] },
1025
+ ]}
1026
+ placeholder="Type a command..."
1027
+ onSearch={async (q) => fetchResults(q)}
1028
+ />
1029
+ ```
1030
+
1031
+ | Prop | Type | Default | Description |
1032
+ |------|------|---------|-------------|
1033
+ | `items` | `CommandItem[]` | -- | `{ id, label, description?, icon?, shortcut?, group?, onSelect, keywords? }` |
1034
+ | `placeholder` | `string` | `'Type a command...'` | Search placeholder |
1035
+ | `hotkey` | `string` | `'k'` | Hotkey letter (Cmd/Ctrl+key) |
1036
+ | `onSearch` | `(query) => Promise<CommandItem[]>` | -- | Async search function |
1037
+ | `recentKey` | `string` | `'ui-kit-command-recent'` | localStorage key for recents |
1038
+ | `maxRecent` | `number` | `5` | Max recent items |
1039
+
1040
+ Features: fuzzy search scoring, grouped sections, recent selections (localStorage), async search with debounce, keyboard navigation (arrows + Enter), keyboard shortcut hints in footer.
1041
+
1042
+ ---
1043
+
1044
+ #### LiveFeed
1045
+
1046
+ Real-time event feed with animated entry, pause/resume, and type-colored borders.
1047
+
1048
+ ```tsx
1049
+ import { LiveFeed } from '@annondeveloper/ui-kit'
1050
+
1051
+ <LiveFeed
1052
+ items={events} maxVisible={50} showTimestamps autoScroll
1053
+ onItemClick={(item) => showDetail(item.id)}
1054
+ emptyMessage="Waiting for events..."
1055
+ />
1056
+ ```
1057
+
1058
+ | Prop | Type | Default | Description |
1059
+ |------|------|---------|-------------|
1060
+ | `items` | `FeedItem[]` | -- | `{ id, content: ReactNode, timestamp, type?: 'info'\|'success'\|'warning'\|'error' }` |
1061
+ | `maxVisible` | `number` | `50` | Max visible items |
1062
+ | `showTimestamps` | `boolean` | `true` | Show relative timestamps |
1063
+ | `autoScroll` | `boolean` | `true` | Auto-scroll to newest |
1064
+ | `onItemClick` | `(item) => void` | -- | Item click callback |
1065
+ | `emptyMessage` | `string` | `'No events yet'` | Empty state text |
1066
+
1067
+ Features: pause/resume button, "N new items" floating badge when scrolled, auto-updating relative timestamps.
1068
+
1069
+ ---
1070
+
1071
+ #### NotificationStack
1072
+
1073
+ Fixed-position notification cards with auto-dismiss progress bars.
1074
+
1075
+ ```tsx
1076
+ import { NotificationStack } from '@annondeveloper/ui-kit'
1077
+
1078
+ <NotificationStack
1079
+ notifications={notifications}
1080
+ onDismiss={(id) => removeNotification(id)}
1081
+ position="top-right" maxVisible={5}
1082
+ />
1083
+ ```
1084
+
1085
+ | Prop | Type | Default | Description |
1086
+ |------|------|---------|-------------|
1087
+ | `notifications` | `Notification[]` | -- | `{ id, title, message?, type, action?, dismissible?, duration? }` |
1088
+ | `onDismiss` | `(id: string) => void` | -- | Dismiss callback |
1089
+ | `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` | Screen position |
1090
+ | `maxVisible` | `number` | `5` | Max visible before stacking |
1091
+
1092
+ Features: type-specific icons and colors, action buttons, auto-dismiss with progress bar, overflow indicator, spring entry/exit animations.
1093
+
1094
+ ---
1095
+
1096
+ ### Layout & Feedback
1097
+
1098
+ #### EmptyState
1099
+
1100
+ Decorative placeholder with icon, title, description, and optional actions.
1101
+
1102
+ ```tsx
1103
+ import { EmptyState, Button } from '@annondeveloper/ui-kit'
1104
+ import { Inbox } from 'lucide-react'
1105
+
1106
+ <EmptyState icon={Inbox} title="No alerts" description="Your infrastructure is healthy."
1107
+ actions={<Button size="sm">Configure Rules</Button>} />
1108
+ ```
1109
+
1110
+ | Prop | Type | Default | Description |
1111
+ |------|------|---------|-------------|
1112
+ | `icon` | `LucideIcon` | -- | Center icon |
1113
+ | `title` | `string` | -- | Title text |
1114
+ | `description` | `string` | -- | Description text |
1115
+ | `actions` | `ReactNode` | -- | Action buttons |
1116
+
1117
+ ---
1118
+
1119
+ #### Skeleton / SkeletonText / SkeletonCard
1120
+
1121
+ Shimmer loading placeholders.
1122
+
1123
+ ```tsx
1124
+ import { Skeleton, SkeletonText, SkeletonCard } from '@annondeveloper/ui-kit'
1125
+
1126
+ <Skeleton className="h-8 w-32" />
1127
+ <SkeletonText lines={3} />
1128
+ <SkeletonCard />
1129
+ ```
1130
+
1131
+ `SkeletonText` props: `lines?: number` (default 1).
1132
+
1133
+ ---
1134
+
1135
+ #### Progress
1136
+
1137
+ Progress bar with optional label, animated fill, and indeterminate mode.
1138
+
1139
+ ```tsx
1140
+ import { Progress } from '@annondeveloper/ui-kit'
1141
+
1142
+ <Progress value={65} max={100} label="Upload" showValue variant="success" />
1143
+ <Progress indeterminate label="Processing..." />
1144
+ ```
1145
+
1146
+ | Prop | Type | Default | Description |
1147
+ |------|------|---------|-------------|
1148
+ | `value` | `number` | -- | Current value |
1149
+ | `max` | `number` | `100` | Maximum value |
1150
+ | `label` | `string` | -- | Label text |
1151
+ | `showValue` | `boolean` | `false` | Show percentage |
1152
+ | `variant` | `'default' \| 'success' \| 'warning' \| 'danger'` | `'default'` | Color variant |
1153
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Bar height |
1154
+ | `animated` | `boolean` | `true` | Animate fill changes |
1155
+ | `indeterminate` | `boolean` | `false` | Shimmer animation |
1156
+
1157
+ ---
1158
+
1159
+ #### Avatar
1160
+
1161
+ User/entity avatar with image support, initials fallback, and status dot.
1162
+
1163
+ ```tsx
1164
+ import { Avatar } from '@annondeveloper/ui-kit'
1165
+
1166
+ <Avatar src="/avatar.jpg" alt="Jane Doe" size="md" status="online" />
1167
+ <Avatar alt="John Smith" size="sm" /> {/* Shows "JS" initials */}
1168
+ ```
1169
+
1170
+ | Prop | Type | Default | Description |
1171
+ |------|------|---------|-------------|
1172
+ | `src` | `string` | -- | Image URL |
1173
+ | `alt` | `string` | -- | Alt text (used for initials derivation) |
1174
+ | `fallback` | `string` | -- | Override initials |
1175
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Size preset |
1176
+ | `status` | `'online' \| 'offline' \| 'busy' \| 'away'` | -- | Status dot overlay |
1177
+
1178
+ ---
1179
+
1180
+ #### InfiniteScroll
1181
+
1182
+ Virtualized infinite-scroll list using IntersectionObserver.
1183
+
1184
+ ```tsx
1185
+ import { InfiniteScroll } from '@annondeveloper/ui-kit'
1186
+
1187
+ <InfiniteScroll
1188
+ items={logs} hasMore={hasNextPage} isLoading={isFetching}
1189
+ loadMore={fetchNextPage} itemHeight={48}
1190
+ renderItem={(item, i) => <LogRow key={item.id} entry={item} />}
1191
+ />
1192
+ ```
1193
+
1194
+ | Prop | Type | Default | Description |
1195
+ |------|------|---------|-------------|
1196
+ | `items` | `T[]` | -- | Loaded items |
1197
+ | `renderItem` | `(item, index) => JSX.Element` | -- | Item renderer |
1198
+ | `loadMore` | `() => void \| Promise<void>` | -- | Load more callback |
1199
+ | `hasMore` | `boolean` | -- | Whether more items exist |
1200
+ | `isLoading` | `boolean` | `false` | Loading state |
1201
+ | `threshold` | `number` | `200` | Trigger distance from bottom (px) |
1202
+ | `itemHeight` | `number` | -- | Fixed height for virtualization |
1203
+ | `emptyState` | `ReactNode` | -- | Empty content |
1204
+
1205
+ Features: optional height-based virtualization, scroll-to-top button, skeleton placeholders.
1206
+
1207
+ ---
1208
+
1209
+ #### SortableList
1210
+
1211
+ Drag-and-drop reorderable list with smooth layout animations.
1212
+
1213
+ ```tsx
1214
+ import { SortableList, DragHandle } from '@annondeveloper/ui-kit'
1215
+
1216
+ <SortableList
1217
+ items={rules} onReorder={setRules}
1218
+ renderItem={(item, i, dragProps) => (
1219
+ <div className="flex items-center gap-2 p-3">
1220
+ <DragHandle {...dragProps} />
1221
+ <span>{item.name}</span>
1222
+ </div>
1223
+ )}
1224
+ />
1225
+ ```
1226
+
1227
+ | Prop | Type | Default | Description |
1228
+ |------|------|---------|-------------|
1229
+ | `items` | `T[]` (each needs `id: string`) | -- | Items to display |
1230
+ | `onReorder` | `(items: T[]) => void` | -- | Reorder callback |
1231
+ | `renderItem` | `(item, index, dragHandleProps) => JSX.Element` | -- | Item renderer |
1232
+ | `direction` | `'vertical' \| 'horizontal'` | `'vertical'` | Layout direction |
1233
+
1234
+ Features: pointer-based drag (no external DnD library), keyboard reordering (Space to pick, arrows to move, Enter to drop), touch support, drop indicator line.
1235
+
1236
+ ---
1237
+
1238
+ #### KanbanColumn
1239
+
1240
+ Kanban board column with title, count badge, and scrollable card list.
1241
+
1242
+ ```tsx
1243
+ import { KanbanColumn } from '@annondeveloper/ui-kit'
1244
+
1245
+ <KanbanColumn
1246
+ title="In Progress" color="hsl(var(--status-warning))"
1247
+ items={tasks} onItemClick={showTask} onAddItem={createTask}
1248
+ />
1249
+ ```
1250
+
1251
+ | Prop | Type | Default | Description |
1252
+ |------|------|---------|-------------|
1253
+ | `title` | `string` | -- | Column title |
1254
+ | `items` | `KanbanItem[]` | -- | `{ id, title, description?, tags?, assignee? }` |
1255
+ | `count` | `number` | -- | Override count badge |
1256
+ | `color` | `string` | -- | Header accent color |
1257
+ | `onItemClick` | `(item) => void` | -- | Card click callback |
1258
+ | `onAddItem` | `() => void` | -- | Add button callback |
1259
+
1260
+ ---
1261
+
1262
+ #### HeatmapCalendar
1263
+
1264
+ GitHub-style contribution heatmap calendar with configurable color scale.
1265
+
1266
+ ```tsx
1267
+ import { HeatmapCalendar } from '@annondeveloper/ui-kit'
1268
+
1269
+ <HeatmapCalendar data={dailyMetrics} onDayClick={(d) => showDayDetail(d.date)} showMonthLabels showDayLabels />
1270
+ ```
1271
+
1272
+ | Prop | Type | Default | Description |
1273
+ |------|------|---------|-------------|
1274
+ | `data` | `DayValue[]` | -- | `{ date: 'YYYY-MM-DD', value: number }` |
1275
+ | `startDate` | `string` | 365 days ago | Start date |
1276
+ | `endDate` | `string` | Today | End date |
1277
+ | `colorScale` | `string[]` | Green 5-step scale | 5 colors lightest to darkest |
1278
+ | `onDayClick` | `(day) => void` | -- | Day click callback |
1279
+ | `showMonthLabels` | `boolean` | `true` | Month labels at top |
1280
+ | `showDayLabels` | `boolean` | `true` | Day-of-week labels |
1281
+ | `tooltipFormat` | `(day) => string` | -- | Custom tooltip format |
1282
+
1283
+ ---
1284
+
1285
+ #### Toaster / toast
1286
+
1287
+ Pre-themed Sonner toast container. Import once, use `toast()` anywhere.
1288
+
1289
+ ```tsx
1290
+ // In layout:
1291
+ import { Toaster } from '@annondeveloper/ui-kit'
1292
+ <Toaster theme="dark" position="bottom-right" />
1293
+
1294
+ // Anywhere:
1295
+ import { toast } from '@annondeveloper/ui-kit'
1296
+ toast.success('Deployment complete')
1297
+ toast.error('Connection failed')
1298
+ ```
1299
+
1300
+ | Prop | Type | Default | Description |
1301
+ |------|------|---------|-------------|
1302
+ | `theme` | `'dark' \| 'light'` | `'dark'` | Toast theme |
1303
+ | `position` | Sonner positions | `'bottom-right'` | Screen position |
1304
+ | `duration` | `number` | `4000` | Auto-dismiss (ms) |
1305
+
1306
+ ---
1307
+
1308
+ #### SuccessCheckmark
1309
+
1310
+ Animated SVG checkmark with circle and path draw animations.
1311
+
1312
+ ```tsx
1313
+ import { SuccessCheckmark } from '@annondeveloper/ui-kit'
1314
+
1315
+ <SuccessCheckmark size={24} />
1316
+ ```
1317
+
1318
+ | Prop | Type | Default | Description |
1319
+ |------|------|---------|-------------|
1320
+ | `size` | `number` | `20` | SVG size in pixels |
1321
+
1322
+ ---
1323
+
1324
+ ## Utilities
1325
+
1326
+ ### Class Merging
1327
+
1328
+ ```tsx
1329
+ import { cn } from '@annondeveloper/ui-kit'
1330
+
1331
+ cn('px-4 py-2', isActive && 'bg-blue-500', className)
1332
+ ```
1333
+
1334
+ ### Formatters
1335
+
1336
+ | Function | Example | Output |
1337
+ |----------|---------|--------|
1338
+ | `fmtBps(n)` | `fmtBps(1_000_000_000)` | `"1.0 Gbps"` |
1339
+ | `fmtSpeed(n)` | `fmtSpeed(10_000_000_000)` | `"10G"` |
1340
+ | `fmtBytes(n)` | `fmtBytes(1_073_741_824)` | `"1.0 GB"` |
1341
+ | `fmtPct(n, d?)` | `fmtPct(95.6)` | `"95.6%"` |
1342
+ | `fmtUptime(secs)` | `fmtUptime(90061)` | `"1d 1h"` |
1343
+ | `fmtRelative(iso)` | `fmtRelative('2024-01-01')` | `"3d ago"` |
1344
+ | `fmtCompact(n)` | `fmtCompact(480_933_305)` | `"480.9M"` |
1345
+ | `fmtDuration(secs)` | `fmtDuration(0.003)` | `"3ms"` |
1346
+ | `fmtUtil(bps, speed)` | `fmtUtil(500_000_000, 1_000_000_000)` | `"50%"` |
1347
+ | `stripCidr(ip)` | `stripCidr('10.0.0.1/32')` | `"10.0.0.1"` |
1348
+ | `clamp(v, min, max)` | `clamp(150, 0, 100)` | `100` |
1349
+
1350
+ ### utilColor
1351
+
1352
+ Returns a Tailwind class based on utilization percentage:
1353
+
1354
+ ```tsx
1355
+ import { utilColor, defaultUtilColorMap } from '@annondeveloper/ui-kit'
1356
+
1357
+ utilColor(85) // "text-[hsl(var(--status-critical))]"
1358
+ utilColor(45) // "text-[hsl(var(--status-ok))]"
1359
+ utilColor(75, customColorMap) // uses your thresholds
1360
+ ```
1361
+
1362
+ The `defaultUtilColorMap` thresholds: >= 80% critical (red), >= 60% warning (yellow), < 60% ok (green).
1363
+
1364
+ ### createBadgeVariant
1365
+
1366
+ Factory for domain-specific badge components. See [Badge](#badge) section above.
1367
+
1368
+ ---
1369
+
1370
+ ## Theme System
1371
+
1372
+ The library uses CSS custom properties for theming. Dark mode is the default; add the `light` class to `<html>` for light mode.
1373
+
1374
+ ### Token Reference
1375
+
1376
+ | Token | Purpose | Dark Default | Light Default |
1377
+ |-------|---------|--------------|---------------|
1378
+ | `--bg-base` | Page background | `222 47% 7%` | `210 40% 98%` |
1379
+ | `--bg-surface` | Card/panel background | `222 40% 10%` | `0 0% 100%` |
1380
+ | `--bg-elevated` | Elevated surface (dropdowns, popovers) | `222 35% 14%` | `210 40% 96%` |
1381
+ | `--bg-overlay` | Overlay/hover background | `222 30% 18%` | `210 30% 92%` |
1382
+ | `--border-subtle` | Subtle borders | `222 30% 20%` | `210 20% 88%` |
1383
+ | `--border-default` | Default borders | `222 25% 25%` | `210 20% 80%` |
1384
+ | `--border-strong` | Strong/focus borders | `222 20% 35%` | `210 20% 65%` |
1385
+ | `--text-primary` | Primary text | `210 40% 95%` | `222 47% 11%` |
1386
+ | `--text-secondary` | Secondary text | `210 20% 70%` | `222 25% 35%` |
1387
+ | `--text-tertiary` | Tertiary text | `210 15% 50%` | `222 15% 55%` |
1388
+ | `--text-disabled` | Disabled text | `210 10% 35%` | `222 10% 75%` |
1389
+ | `--brand-primary` | Brand/accent color | `217 91% 60%` | `217 91% 50%` |
1390
+ | `--brand-secondary` | Secondary accent | `258 80% 65%` | `258 80% 58%` |
1391
+ | `--text-on-brand` | Text on brand backgrounds | `0 0% 100%` | `0 0% 100%` |
1392
+ | `--status-ok` | Success/online | `142 71% 45%` | `142 71% 38%` |
1393
+ | `--status-warning` | Warning/degraded | `38 92% 50%` | `38 92% 42%` |
1394
+ | `--status-critical` | Critical/error | `0 84% 60%` | `0 84% 52%` |
1395
+ | `--status-unknown` | Unknown status | `220 15% 50%` | `220 15% 55%` |
1396
+ | `--status-maintenance` | Maintenance mode | `258 60% 65%` | `258 60% 58%` |
1397
+ | `--chart-1` through `--chart-8` | Chart color series | Various | Various |
1398
+
1399
+ ### Customization
1400
+
1401
+ Override any token in your own CSS:
1402
+
1403
+ ```css
1404
+ :root {
1405
+ --brand-primary: 160 84% 39%; /* teal accent */
1406
+ --status-ok: 160 84% 39%;
1407
+ }
1408
+ ```
1409
+
1410
+ ### Theme Toggle
1411
+
1412
+ ```tsx
1413
+ function ThemeToggle() {
1414
+ const toggle = () => document.documentElement.classList.toggle('light')
1415
+ return <button onClick={toggle}>Toggle theme</button>
1416
+ }
1417
+ ```
1418
+
1419
+ ### Typography Scale
1420
+
1421
+ The theme CSS includes utility classes: `.text-display`, `.text-heading-1`, `.text-heading-2`, `.text-heading-3`, `.text-body`, `.text-small`, `.text-label`.
1422
+
1423
+ ---
1424
+
1425
+ ## CLI
1426
+
1427
+ Scaffold components directly into your project -- shadcn/ui-style.
1428
+
1429
+ ### Initialize
1430
+
1431
+ ```bash
1432
+ npx @annondeveloper/ui-kit init
1433
+ ```
1434
+
1435
+ Copies `theme.css` and `utils.ts` into your target directory (default `./components/ui`).
1436
+
1437
+ ### Add a Component
1438
+
1439
+ ```bash
1440
+ npx @annondeveloper/ui-kit add button
1441
+ npx @annondeveloper/ui-kit add data-table
1442
+ npx @annondeveloper/ui-kit add metric-card
1443
+ ```
1444
+
1445
+ Copies the component source file and resolves internal dependencies automatically.
1446
+
1447
+ ### List Available Components
1448
+
1449
+ ```bash
1450
+ npx @annondeveloper/ui-kit list
1451
+ ```
1452
+
1453
+ ### Options
1454
+
1455
+ | Flag | Description |
1456
+ |------|-------------|
1457
+ | `--dir <path>` | Target directory (default `./components/ui`) |
1458
+ | `--overwrite` | Overwrite existing files |
1459
+
1460
+ ---
1461
+
1462
+ ## Form Integration
1463
+
1464
+ Install the optional peer dependency:
1465
+
1466
+ ```bash
1467
+ npm install react-hook-form
1468
+ ```
1469
+
1470
+ Import from the dedicated entry point:
1471
+
1472
+ ```tsx
1473
+ import { RHFFormInput, RHFSelect, RHFCheckbox, RHFToggleSwitch } from '@annondeveloper/ui-kit/form'
1474
+ ```
1475
+
1476
+ ### Example with Validation
1477
+
1478
+ ```tsx
1479
+ import { useForm } from 'react-hook-form'
1480
+ import { RHFFormInput, RHFSelect } from '@annondeveloper/ui-kit/form'
1481
+ import { Button } from '@annondeveloper/ui-kit'
1482
+
1483
+ function CredentialForm() {
1484
+ const { control, handleSubmit } = useForm({
1485
+ defaultValues: { hostname: '', protocol: '' },
1486
+ })
1487
+
1488
+ return (
1489
+ <form onSubmit={handleSubmit(onSubmit)}>
1490
+ <RHFFormInput
1491
+ control={control} name="hostname" label="Hostname"
1492
+ rules={{ required: 'Hostname is required' }} placeholder="10.0.0.1"
1493
+ />
1494
+ <RHFSelect
1495
+ control={control} name="protocol" label="Protocol"
1496
+ options={[{ value: 'snmp', label: 'SNMP' }, { value: 'ssh', label: 'SSH' }]}
1497
+ rules={{ required: 'Select a protocol' }}
1498
+ />
1499
+ <Button type="submit" variant="primary">Save</Button>
1500
+ </form>
1501
+ )
1502
+ }
1503
+ ```
1504
+
1505
+ All RHF wrappers automatically display validation errors from `fieldState.error`.
1506
+
1507
+ ---
1508
+
1509
+ ## Contributing
1510
+
1511
+ ### Development Setup
1512
+
1513
+ ```bash
1514
+ git clone https://github.com/annondeveloper/ui-kit.git
1515
+ cd ui-kit
1516
+ npm install
1517
+ npm run storybook # Component explorer at localhost:6006
1518
+ npm run test:watch # Vitest in watch mode
1519
+ npm run typecheck # TypeScript check
1520
+ ```
1521
+
1522
+ ### Running Tests
1523
+
1524
+ ```bash
1525
+ npm test # Run all tests once
1526
+ npm run test:watch # Watch mode
1527
+ ```
1528
+
1529
+ Tests use Vitest + Testing Library + jest-axe for accessibility.
1530
+
1531
+ ### Adding a Component
1532
+
1533
+ 1. Create `src/components/my-component.tsx` with the `'use client'` directive
1534
+ 2. Export from `src/index.ts`
1535
+ 3. Add to `src/cli/registry.ts` for CLI support
1536
+ 4. Add a Storybook story in `src/components/my-component.stories.tsx`
1537
+ 5. Add tests in `src/__tests__/my-component.test.tsx`
1538
+ 6. Run `npm run typecheck && npm test` to verify
1539
+
1540
+ ### Conventions
1541
+
1542
+ - All colors use `hsl(var(--token))` syntax -- never hardcoded hex values
1543
+ - All animations respect `useReducedMotion()` from Framer Motion
1544
+ - Components use `'use client'` directive for React Server Component compatibility
1545
+ - Types are exported alongside components for consumer use
1546
+
1547
+ ---
1548
+
213
1549
  ## License
214
1550
 
215
- MIT
1551
+ [MIT](./LICENSE)