@dreamtree-org/twreact-ui 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/DESIGN.md +118 -0
  2. package/README.md +52 -34
  3. package/ai-skills/dreamtree-ui.md +27 -0
  4. package/dist/components/blocks/AppShell.d.ts +4 -0
  5. package/dist/components/blocks/AppShell.d.ts.map +1 -0
  6. package/dist/components/blocks/ConfirmDialog.d.ts +4 -0
  7. package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -0
  8. package/dist/components/blocks/EmptyState.d.ts +4 -0
  9. package/dist/components/blocks/EmptyState.d.ts.map +1 -0
  10. package/dist/components/blocks/ErrorState.d.ts +4 -0
  11. package/dist/components/blocks/ErrorState.d.ts.map +1 -0
  12. package/dist/components/blocks/LoadingState.d.ts +9 -0
  13. package/dist/components/blocks/LoadingState.d.ts.map +1 -0
  14. package/dist/components/blocks/PageHeader.d.ts +4 -0
  15. package/dist/components/blocks/PageHeader.d.ts.map +1 -0
  16. package/dist/components/blocks/StatCard.d.ts +4 -0
  17. package/dist/components/blocks/StatCard.d.ts.map +1 -0
  18. package/dist/components/blocks/StatusPill.d.ts +5 -0
  19. package/dist/components/blocks/StatusPill.d.ts.map +1 -0
  20. package/dist/index.css +1 -1
  21. package/dist/index.d.ts +48 -38
  22. package/dist/index.esm.css +1 -1
  23. package/dist/index.esm.js +958 -268
  24. package/dist/index.js +965 -267
  25. package/dist/tokens/brands/default.css +1 -0
  26. package/dist/tokens/brands/default.json +5 -0
  27. package/dist/tokens/brands/practicepro.css +17 -0
  28. package/dist/tokens/brands/practicepro.json +27 -0
  29. package/dist/tokens/tailwind3-preset.cjs +214 -0
  30. package/dist/tokens/theme.css +193 -0
  31. package/dist/tokens/tokens.json +373 -0
  32. package/dist/tokens/vars.css +98 -0
  33. package/dist/types/blocks.d.ts +109 -0
  34. package/dist/types/blocks.d.ts.map +1 -0
  35. package/dist/types/index.d.ts +1 -0
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/doc/README.md +19 -0
  38. package/mcp/catalog.snapshot.json +398 -3
  39. package/package.json +11 -5
  40. package/tokens/brands/default.json +5 -0
  41. package/tokens/brands/practicepro.json +27 -0
  42. package/tokens/lib.cjs +128 -0
  43. package/tokens/tokens.json +133 -0
@@ -3113,6 +3113,392 @@
3113
3113
  }
3114
3114
  ],
3115
3115
  "examples": "```jsx\n<Carousel autoPlay interval={5000} showDots>\n <div className=\"h-64 bg-gray-100 flex items-center justify-center\">Slide 1</div>\n <div className=\"h-64 bg-gray-200 flex items-center justify-center\">Slide 2</div>\n <div className=\"h-64 bg-gray-300 flex items-center justify-center\">Slide 3</div>\n</Carousel>\n```\n\n[← Component overview](README.md)"
3116
+ },
3117
+ {
3118
+ "name": "AppShell",
3119
+ "group": "blocks",
3120
+ "familyExports": [],
3121
+ "hasDoc": true,
3122
+ "description": "Page-level block: the product app shell — a sidebar on large screens and a top bar with a navigation drawer below `lg`. Replaces the shell 12 of 13 products hand-rolled (extracted from procurementpro `components/app-shell.tsx`). Colours come from the `shell` role tokens, so a brand file restyles it.",
3123
+ "props": [
3124
+ {
3125
+ "prop": "brand",
3126
+ "type": "`ReactNode`",
3127
+ "default": "",
3128
+ "description": "Product mark at the top of the sidebar"
3129
+ },
3130
+ {
3131
+ "prop": "nav",
3132
+ "type": "`AppShellNavItem[]`",
3133
+ "default": "",
3134
+ "description": "`{ id, label, href?, icon?, active?, count?, group?, onClick? }`. Items with `href` render as links; `active` sets `aria-current=\"page\"`"
3135
+ },
3136
+ {
3137
+ "prop": "sidebarFooter",
3138
+ "type": "`ReactNode`",
3139
+ "default": "",
3140
+ "description": "Pinned to the bottom of the sidebar (user menu, workspace switcher)"
3141
+ },
3142
+ {
3143
+ "prop": "topbar",
3144
+ "type": "`ReactNode`",
3145
+ "default": "",
3146
+ "description": "Right side of the top bar (search, notifications, avatar)"
3147
+ },
3148
+ {
3149
+ "prop": "pageTitle",
3150
+ "type": "`string`",
3151
+ "default": "",
3152
+ "description": "Title shown in the mobile top bar"
3153
+ },
3154
+ {
3155
+ "prop": "linkAs",
3156
+ "type": "`ElementType`",
3157
+ "default": "\"a\"",
3158
+ "description": "Link element for nav items, e.g. Next.js `Link`"
3159
+ },
3160
+ {
3161
+ "prop": "className",
3162
+ "type": "`string`",
3163
+ "default": "",
3164
+ "description": "Extra CSS classes"
3165
+ },
3166
+ {
3167
+ "prop": "children",
3168
+ "type": "`ReactNode`",
3169
+ "default": "",
3170
+ "description": "Page content, rendered in `<main id=\"main\">`"
3171
+ }
3172
+ ],
3173
+ "examples": "```jsx\n<AppShell\n brand={<span className=\"text-lg font-bold\">PracticePro</span>}\n nav={[\n { id: 'overview', label: 'Overview', href: '/overview', icon: <Home className=\"h-5 w-5\" /> },\n { id: 'compliance', label: 'Compliance', href: '/compliance', active: true },\n { id: 'approvals', label: 'Approvals', href: '/approvals', count: 4 },\n ]}\n topbar={<Avatar name=\"Ananya Rao\" size=\"sm\" />}\n linkAs={Link}\n>\n <PageHeader title=\"Compliance calendar\" />\n</AppShell>\n```\n\n[← Component overview](README.md)"
3174
+ },
3175
+ {
3176
+ "name": "PageHeader",
3177
+ "group": "blocks",
3178
+ "familyExports": [],
3179
+ "hasDoc": true,
3180
+ "description": "Page-level block: the page title (`h1`), a muted subtitle, the page's actions (one primary action) and an optional back link. Extracted from taskpro `features/shared/PageHeader.jsx`.",
3181
+ "props": [
3182
+ {
3183
+ "prop": "title",
3184
+ "type": "`ReactNode`",
3185
+ "default": "",
3186
+ "description": "Page title, rendered as the page's only `h1`"
3187
+ },
3188
+ {
3189
+ "prop": "subtitle",
3190
+ "type": "`ReactNode`",
3191
+ "default": "",
3192
+ "description": "One line under the title"
3193
+ },
3194
+ {
3195
+ "prop": "actions",
3196
+ "type": "`ReactNode`",
3197
+ "default": "",
3198
+ "description": "Buttons on the right; keep one primary action"
3199
+ },
3200
+ {
3201
+ "prop": "back",
3202
+ "type": "`{ href: string; label: string }`",
3203
+ "default": "",
3204
+ "description": "Back link for detail pages"
3205
+ },
3206
+ {
3207
+ "prop": "linkAs",
3208
+ "type": "`ElementType`",
3209
+ "default": "\"a\"",
3210
+ "description": "Link element for the back link"
3211
+ },
3212
+ {
3213
+ "prop": "className",
3214
+ "type": "`string`",
3215
+ "default": "",
3216
+ "description": "Extra CSS classes"
3217
+ }
3218
+ ],
3219
+ "examples": "```jsx\n<PageHeader\n title=\"Compliance calendar\"\n subtitle=\"Track deadlines, ownership and filing readiness across clients\"\n actions={<Button leftIcon={<Plus className=\"h-4 w-4\" />}>Add obligation</Button>}\n/>\n```\n\n[← Component overview](README.md)"
3220
+ },
3221
+ {
3222
+ "name": "StatCard",
3223
+ "group": "blocks",
3224
+ "familyExports": [],
3225
+ "hasDoc": true,
3226
+ "description": "Page-level block: a KPI tile with an icon, a label, a big tabular number and an optional change vs a previous period. Extracted from PracticePro `components/ui/StatCard.tsx`.",
3227
+ "props": [
3228
+ {
3229
+ "prop": "label",
3230
+ "type": "`ReactNode`",
3231
+ "default": "",
3232
+ "description": "What the number counts"
3233
+ },
3234
+ {
3235
+ "prop": "value",
3236
+ "type": "`ReactNode`",
3237
+ "default": "",
3238
+ "description": "The figure"
3239
+ },
3240
+ {
3241
+ "prop": "icon",
3242
+ "type": "`ReactNode`",
3243
+ "default": "",
3244
+ "description": "Lucide icon shown in a tinted chip"
3245
+ },
3246
+ {
3247
+ "prop": "tone",
3248
+ "type": "`primary` ",
3249
+ "default": "secondary \\",
3250
+ "description": "`success` \\"
3251
+ },
3252
+ {
3253
+ "prop": "change",
3254
+ "type": "`{ value: number; period?: string; unit?: '%' ",
3255
+ "default": "''; goodWhenUp?: boolean }",
3256
+ "description": "—"
3257
+ },
3258
+ {
3259
+ "prop": "loading",
3260
+ "type": "`boolean`",
3261
+ "default": "false",
3262
+ "description": "Shows a skeleton instead of the value"
3263
+ },
3264
+ {
3265
+ "prop": "className",
3266
+ "type": "`string`",
3267
+ "default": "",
3268
+ "description": "Extra CSS classes"
3269
+ }
3270
+ ],
3271
+ "examples": "```jsx\n<StatCard label=\"Due this week\" value={18} tone=\"primary\" icon={<CalendarDays className=\"h-6 w-6\" />} />\n<StatCard label=\"Overdue\" value={3} tone=\"error\" change={{ value: 2, unit: '', period: 'vs last week', goodWhenUp: false }} />\n```\n\n[← Component overview](README.md)"
3272
+ },
3273
+ {
3274
+ "name": "StatusPill",
3275
+ "group": "blocks",
3276
+ "familyExports": [],
3277
+ "hasDoc": true,
3278
+ "description": "Page-level block: one status badge for every domain state. Map each state to a tone and a human label once; unknown states are humanized (`in_review` → \"In review\"), never shown raw. Extracted from billpro `components/StatusPill.jsx`.",
3279
+ "props": [
3280
+ {
3281
+ "prop": "status",
3282
+ "type": "`string`",
3283
+ "default": "",
3284
+ "description": "Domain state to look up in `map`"
3285
+ },
3286
+ {
3287
+ "prop": "map",
3288
+ "type": "`Record<string, { label; tone; icon? }>`",
3289
+ "default": "",
3290
+ "description": "State → label and tone (`neutral`, `info`, `success`, `warning`, `danger`, `muted`)"
3291
+ },
3292
+ {
3293
+ "prop": "tone",
3294
+ "type": "`string`",
3295
+ "default": "\"neutral\"",
3296
+ "description": "Tone when not using `map`; also accepts semantic tones"
3297
+ },
3298
+ {
3299
+ "prop": "icon",
3300
+ "type": "`ReactNode`",
3301
+ "default": "",
3302
+ "description": "Small leading icon"
3303
+ },
3304
+ {
3305
+ "prop": "children",
3306
+ "type": "`ReactNode`",
3307
+ "default": "",
3308
+ "description": "Label override"
3309
+ },
3310
+ {
3311
+ "prop": "className",
3312
+ "type": "`string`",
3313
+ "default": "",
3314
+ "description": "Extra CSS classes"
3315
+ }
3316
+ ],
3317
+ "examples": "```jsx\nconst OBLIGATION_STATUS = {\n overdue: { label: 'Overdue', tone: 'danger' },\n docs_missing: { label: 'Missing documents', tone: 'warning' },\n ready: { label: 'Ready for review', tone: 'info' },\n filed: { label: 'Filed', tone: 'success' },\n};\n\n<StatusPill status=\"docs_missing\" map={OBLIGATION_STATUS} />\n<StatusPill tone=\"success\">Filed</StatusPill>\n```\n\n[← Component overview](README.md)"
3318
+ },
3319
+ {
3320
+ "name": "EmptyState",
3321
+ "group": "blocks",
3322
+ "familyExports": [],
3323
+ "hasDoc": true,
3324
+ "description": "Page-level block: what a card shows when it has no data — an icon, a title, one line saying what to do, and the action that fixes it. Extracted from procurementpro `components/common/empty-state.tsx`.",
3325
+ "props": [
3326
+ {
3327
+ "prop": "title",
3328
+ "type": "`ReactNode`",
3329
+ "default": "",
3330
+ "description": "What is empty"
3331
+ },
3332
+ {
3333
+ "prop": "description",
3334
+ "type": "`ReactNode`",
3335
+ "default": "",
3336
+ "description": "One line of guidance"
3337
+ },
3338
+ {
3339
+ "prop": "icon",
3340
+ "type": "`ReactNode`",
3341
+ "default": "Inbox icon",
3342
+ "description": "Muted icon"
3343
+ },
3344
+ {
3345
+ "prop": "action",
3346
+ "type": "`ReactNode`",
3347
+ "default": "",
3348
+ "description": "Primary or outlined button"
3349
+ },
3350
+ {
3351
+ "prop": "compact",
3352
+ "type": "`boolean`",
3353
+ "default": "false",
3354
+ "description": "Less vertical padding"
3355
+ },
3356
+ {
3357
+ "prop": "className",
3358
+ "type": "`string`",
3359
+ "default": "",
3360
+ "description": "Extra CSS classes"
3361
+ }
3362
+ ],
3363
+ "examples": "```jsx\n<EmptyState\n title=\"No obligations this month\"\n description=\"Add the first obligation for a client, or import your compliance register.\"\n action={<Button>Add obligation</Button>}\n/>\n```\n\n[← Component overview](README.md)"
3364
+ },
3365
+ {
3366
+ "name": "ErrorState",
3367
+ "group": "blocks",
3368
+ "familyExports": [],
3369
+ "hasDoc": true,
3370
+ "description": "Page-level block: what a card shows when loading failed — neutral copy and a retry, rendered with `role=\"alert\"`. Never shows a raw `error.message`. Extracted from CampaignPro `components/app/page-error.tsx`.",
3371
+ "props": [
3372
+ {
3373
+ "prop": "title",
3374
+ "type": "`ReactNode`",
3375
+ "default": "\"We couldn't load this\"",
3376
+ "description": "What failed"
3377
+ },
3378
+ {
3379
+ "prop": "description",
3380
+ "type": "`ReactNode`",
3381
+ "default": "Reassurance line",
3382
+ "description": "What the user can do"
3383
+ },
3384
+ {
3385
+ "prop": "onRetry",
3386
+ "type": "`() => void`",
3387
+ "default": "",
3388
+ "description": "Shows a Try again button"
3389
+ },
3390
+ {
3391
+ "prop": "retryLabel",
3392
+ "type": "`string`",
3393
+ "default": "\"Try again\"",
3394
+ "description": "Retry button label"
3395
+ },
3396
+ {
3397
+ "prop": "className",
3398
+ "type": "`string`",
3399
+ "default": "",
3400
+ "description": "Extra CSS classes"
3401
+ }
3402
+ ],
3403
+ "examples": "```jsx\n<ErrorState title=\"We couldn't load the compliance calendar\" onRetry={refetch} />\n```\n\n[← Component overview](README.md)"
3404
+ },
3405
+ {
3406
+ "name": "LoadingState",
3407
+ "group": "blocks",
3408
+ "familyExports": [],
3409
+ "hasDoc": true,
3410
+ "description": "Page-level block: a skeleton shaped like the content that will replace it (table rows, cards, KPI tiles or a key–value panel). One `role=\"status\"` region with screen-reader text; no page-level spinners.",
3411
+ "props": [
3412
+ {
3413
+ "prop": "variant",
3414
+ "type": "`table` ",
3415
+ "default": "cards \\",
3416
+ "description": "`stats` \\"
3417
+ },
3418
+ {
3419
+ "prop": "rows",
3420
+ "type": "`number`",
3421
+ "default": "5 (4 for stats, 3 for cards)",
3422
+ "description": "How many rows or tiles"
3423
+ },
3424
+ {
3425
+ "prop": "label",
3426
+ "type": "`string`",
3427
+ "default": "\"Loading…\"",
3428
+ "description": "Screen-reader text"
3429
+ },
3430
+ {
3431
+ "prop": "className",
3432
+ "type": "`string`",
3433
+ "default": "",
3434
+ "description": "Extra CSS classes"
3435
+ }
3436
+ ],
3437
+ "examples": "```jsx\n{isLoading ? <LoadingState variant=\"table\" rows={5} label=\"Loading obligations\" /> : <Table … />}\n```\n\n[← Component overview](README.md)"
3438
+ },
3439
+ {
3440
+ "name": "ConfirmDialog",
3441
+ "group": "blocks",
3442
+ "familyExports": [],
3443
+ "hasDoc": true,
3444
+ "description": "Page-level block: the confirmation step for delete, remove, archive or cancel — replaces native `confirm()`. The title asks the question, one line says what happens, and the action button is named with the verb. Extracted from firepro `components/ui/confirm-dialog.tsx`.",
3445
+ "props": [
3446
+ {
3447
+ "prop": "isOpen",
3448
+ "type": "`boolean`",
3449
+ "default": "",
3450
+ "description": "Whether the dialog is open"
3451
+ },
3452
+ {
3453
+ "prop": "title",
3454
+ "type": "`string`",
3455
+ "default": "",
3456
+ "description": "The action as a question"
3457
+ },
3458
+ {
3459
+ "prop": "description",
3460
+ "type": "`ReactNode`",
3461
+ "default": "",
3462
+ "description": "One line saying what will happen"
3463
+ },
3464
+ {
3465
+ "prop": "confirmLabel",
3466
+ "type": "`string`",
3467
+ "default": "",
3468
+ "description": "The verb, e.g. \"Remove\" (never \"OK\")"
3469
+ },
3470
+ {
3471
+ "prop": "cancelLabel",
3472
+ "type": "`string`",
3473
+ "default": "\"Cancel\"",
3474
+ "description": "Cancel button label"
3475
+ },
3476
+ {
3477
+ "prop": "destructive",
3478
+ "type": "`boolean`",
3479
+ "default": "false",
3480
+ "description": "Red confirm button for actions that can't be undone"
3481
+ },
3482
+ {
3483
+ "prop": "loading",
3484
+ "type": "`boolean`",
3485
+ "default": "false",
3486
+ "description": "Busy confirm button; the dialog can't be dismissed"
3487
+ },
3488
+ {
3489
+ "prop": "onConfirm",
3490
+ "type": "`() => void`",
3491
+ "default": "",
3492
+ "description": "Called on confirm"
3493
+ },
3494
+ {
3495
+ "prop": "onCancel",
3496
+ "type": "`() => void`",
3497
+ "default": "",
3498
+ "description": "Called on cancel, Esc or backdrop click"
3499
+ }
3500
+ ],
3501
+ "examples": "```jsx\n<ConfirmDialog\n isOpen={open}\n title=\"Remove Kiran M from Diwali buyers?\"\n description=\"They stop receiving this list's campaigns.\"\n confirmLabel=\"Remove\"\n destructive\n onConfirm={remove}\n onCancel={() => setOpen(false)}\n/>\n```\n\n[← Component overview](README.md)"
3116
3502
  }
3117
3503
  ],
3118
3504
  "hooks": [
@@ -3190,10 +3576,11 @@
3190
3576
  "core",
3191
3577
  "feedback",
3192
3578
  "navigation",
3193
- "utility"
3579
+ "utility",
3580
+ "blocks"
3194
3581
  ],
3195
3582
  "counts": {
3196
- "components": 39,
3583
+ "components": 47,
3197
3584
  "hooks": 3,
3198
3585
  "utils": 3,
3199
3586
  "store": 1
@@ -3202,6 +3589,7 @@
3202
3589
  "docs": {
3203
3590
  "Accordion": "# Accordion\n\nExpandable/collapsible content panels. Only one panel open at a time by default, or multiple when `allowMultiple` is true. Supports keyboard navigation (Arrow keys, Home, End, Enter, Space).\n\n## Import\n\n```jsx\nimport { Accordion } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `items` | `Array` | `[]` | Array of `{ id?, title, subtitle?, content, disabled?, leftIcon? }`. `leftIcon` can be ReactNode or `(index, item) => ReactNode`. |\n| `allowMultiple` | `boolean` | `false` | If true, multiple panels can be open at once. |\n| `defaultOpenIndexes` | `number[]` | `[]` | Initial open panel index(es). |\n| `onChange` | `(openIndexes: number[]) => void` | — | Called when open panels change. |\n| `className` | `string` | `\"\"` | Wrapper CSS classes. |\n| `headerClassName` | `string` | `\"\"` | Header button CSS classes. |\n| `contentClassName` | `string` | `\"\"` | Panel content CSS classes. |\n| `chevronPosition` | `\"left\"` \\| `\"right\"` | `\"right\"` | Position of chevron icon. |\n| `chevronIcon` | `ReactNode` | default SVG | Custom chevron icon. |\n| `bordered` | `boolean` | `true` | Whether to show border/background. |\n| `rounded` | `\"none\"` \\| `\"sm\"` \\| `\"md\"` \\| `\"lg\"` \\| `\"xl\"` | `\"md\"` | Border radius of wrapper. |\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | `\"md\"` | Header and content padding/text size. |\n| `transitionDuration` | `number` | `220` | Max-height transition duration in ms. |\n\n## Example\n\n```jsx\n<Accordion\n items={[\n { id: '1', title: 'Section 1', content: <p>Content for section 1</p> },\n { id: '2', title: 'Section 2', subtitle: 'Optional', content: <p>Content 2</p> },\n { title: 'Disabled', content: <p>Content</p>, disabled: true },\n ]}\n allowMultiple={false}\n defaultOpenIndexes={[0]}\n onChange={(indexes) => console.log('Open:', indexes)}\n size=\"md\"\n chevronPosition=\"right\"\n/>\n```\n\n[← Component overview](README.md)\n",
3204
3591
  "Alert": "# Alert\n\nInline alert box for success, error, warning, or info messages. Optional title and dismiss button.\n\n## Import\n\n```jsx\nimport { Alert } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `variant` | `string` | `\"info\"` | `success`, `error`, `warning`, `info` |\n| `title` | `string` | — | Optional title |\n| `children` | `ReactNode` | — | Alert body content |\n| `dismissible` | `boolean` | `false` | Show close button |\n| `onDismiss` | `function` | — | Called when dismissed |\n| `className` | `string` | — | Extra CSS classes |\n\n## Examples\n\n```jsx\n<Alert variant=\"info\" title=\"Note\">Your session will expire in 5 minutes.</Alert>\n<Alert variant=\"success\">Saved successfully.</Alert>\n<Alert variant=\"error\" title=\"Error\">Something went wrong.</Alert>\n<Alert variant=\"warning\" dismissible onDismiss={() => {}}>\n Please review your input.\n</Alert>\n```\n\n[← Component overview](README.md)\n",
3592
+ "AppShell": "# AppShell\n\nPage-level block: the product app shell — a sidebar on large screens and a top bar with a navigation drawer below `lg`. Replaces the shell 12 of 13 products hand-rolled (extracted from procurementpro `components/app-shell.tsx`). Colours come from the `shell` role tokens, so a brand file restyles it.\n\n## Import\n\n```jsx\nimport { AppShell } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `brand` | `ReactNode` | — | Product mark at the top of the sidebar |\n| `nav` | `AppShellNavItem[]` | — | `{ id, label, href?, icon?, active?, count?, group?, onClick? }`. Items with `href` render as links; `active` sets `aria-current=\"page\"` |\n| `sidebarFooter` | `ReactNode` | — | Pinned to the bottom of the sidebar (user menu, workspace switcher) |\n| `topbar` | `ReactNode` | — | Right side of the top bar (search, notifications, avatar) |\n| `pageTitle` | `string` | — | Title shown in the mobile top bar |\n| `linkAs` | `ElementType` | `\"a\"` | Link element for nav items, e.g. Next.js `Link` |\n| `className` | `string` | — | Extra CSS classes |\n| `children` | `ReactNode` | — | Page content, rendered in `<main id=\"main\">` |\n\n## Example\n\n```jsx\n<AppShell\n brand={<span className=\"text-lg font-bold\">PracticePro</span>}\n nav={[\n { id: 'overview', label: 'Overview', href: '/overview', icon: <Home className=\"h-5 w-5\" /> },\n { id: 'compliance', label: 'Compliance', href: '/compliance', active: true },\n { id: 'approvals', label: 'Approvals', href: '/approvals', count: 4 },\n ]}\n topbar={<Avatar name=\"Ananya Rao\" size=\"sm\" />}\n linkAs={Link}\n>\n <PageHeader title=\"Compliance calendar\" />\n</AppShell>\n```\n\n[← Component overview](README.md)\n",
3205
3593
  "Avatar": "# Avatar\n\nUser avatar: image or initials fallback. Optional status indicator.\n\n## Import\n\n```jsx\nimport { Avatar } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `src` | `string` | — | Image URL |\n| `alt` | `string` | — | Image alt |\n| `name` | `string` | — | Used for initials when no src |\n| `size` | `string` | `\"md\"` | `xs`, `sm`, `md`, `lg`, `xl`, `2xl` |\n| `shape` | `string` | `\"circle\"` | `circle`, `rounded`, `square` |\n| `status` | `string` | — | `online`, `offline`, `away`, `busy` |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<Avatar name=\"Alice Smith\" />\n<Avatar src=\"/photo.jpg\" alt=\"User\" />\n<Avatar name=\"Bob\" status=\"online\" size=\"lg\" />\n```\n\n[← Component overview](README.md)\n",
3206
3594
  "Badge": "# Badge\n\nBadge or pill for counts, labels, and status.\n\n## Import\n\n```jsx\nimport { Badge } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `variant` | `string` | `\"default\"` | `default`, `primary`, `secondary`, `success`, `warning`, `error`, `outline` |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `rounded` | `boolean` | `true` | Rounded pill when true |\n| `className` | `string` | — | Extra CSS classes |\n| `children` | `ReactNode` | — | Badge content |\n\n## Example\n\n```jsx\n<Badge>New</Badge>\n<Badge variant=\"success\">Active</Badge>\n<Badge variant=\"error\" size=\"sm\">3</Badge>\n```\n\n[← Component overview](README.md)\n",
3207
3595
  "Breadcrumbs": "# Breadcrumbs\n\nBreadcrumb navigation trail. Optional home link and custom separator.\n\n## Import\n\n```jsx\nimport { Breadcrumbs } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `items` | `Array` | `[]` | `[{ id, label, href?, onClick? }]` |\n| `separator` | `ReactNode` | `<ChevronRight />` | Separator between items |\n| `showHome` | `boolean` | `true` | Prepend Home link |\n| `homeIcon` | `ReactNode` | — | Icon for Home |\n| `onHomeClick` | `function` | — | Home click handler |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<Breadcrumbs\n items={[\n { id: '1', label: 'Products', href: '/products' },\n { id: '2', label: 'Category', href: '/products/cat' },\n { id: '3', label: 'Current Page' },\n ]}\n showHome\n/>\n```\n\n[← Component overview](README.md)\n",
@@ -3211,16 +3599,21 @@
3211
3599
  "Checkbox": "# Checkbox\n\nCheckbox with label, sizes, and optional indeterminate state.\n\n## Import\n\n```jsx\nimport { Checkbox } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `id` | `string` | — | Input id |\n| `name` | `string` | — | Form name |\n| `label` | `string` | — | Label text |\n| `checked` | `boolean` | — | Controlled checked |\n| `defaultChecked` | `boolean` | `false` | Uncontrolled default |\n| `onChange` | `function` | — | Change handler |\n| `disabled` | `boolean` | `false` | Disable |\n| `required` | `boolean` | `false` | Required |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `labelPosition` | `string` | `\"right\"` | `left`, `right` |\n| `value` | `any` | — | Value when checked |\n| `indeterminate` | `boolean` | `false` | Indeterminate state |\n| `borderColor` | `string` | `\"#ccc\"` | Box border color |\n| `color` | `string` | `\"primary\"` | Color preset (`primary`, `secondary`, `success`, `error`, `warning`, `info`, etc.) or custom hex/RGB color string |\n| `iconClass` | `string` | — | Extra classes for the check icon |\n| `checkedClass` | `string` | — | Extra classes applied when checked |\n| `wrapperProps` | `object` | — | Extra props spread onto the outer wrapper element |\n\n## Example\n\n```jsx\n<Checkbox label=\"Accept terms\" checked={checked} onChange={e => setChecked(e.target.checked)} />\n<Checkbox label=\"Remember me\" defaultChecked />\n```\n\n[← Component overview](README.md)\n",
3212
3600
  "ColorPicker": "# ColorPicker\n\nColor picker with optional swatches, hex input, HSL sliders, and alpha. Value can be hex or rgba string; `onChange` receives normalized `rgba(r,g,b,a)` string.\n\n## Import\n\n```jsx\nimport { ColorPicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `string` | — | Controlled value (hex or rgba string). |\n| `defaultValue` | `string` | — | Uncontrolled initial value. |\n| `onChange` | `(rgbaString: string) => void` | — | Called with normalized `rgba(r,g,b,a)` string. |\n| `swatches` | `string[]` | — | Array of hex/rgba strings for preset swatches. |\n| `showAlpha` | `boolean` | `false` | Show alpha slider. When off (or when the alpha channel is fully opaque), the displayed hex is the 6-char `#rrggbb` form; the 8-char `#rrggbbff` form is shown only when `showAlpha` is on **and** alpha < 1. |\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | — | Picker size. |\n| `disabled` | `boolean` | — | Disable the picker. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `label` | `string` | — | Label text. |\n| `id` | `string` | — | Input id. |\n\n## Example\n\n```jsx\nconst [color, setColor] = useState('#3b82f6');\n<ColorPicker\n value={color}\n onChange={setColor}\n swatches={['#ef4444', '#22c55e', '#3b82f6']}\n showAlpha\n label=\"Background\"\n/>\n```\n\n## Notes\n\n- Forwards `ref` to the root wrapper element and spreads unknown props (`...rest`,\n e.g. `data-*`) onto it. `className` is merged via `cn()` so caller classes win\n on Tailwind conflicts.\n- The default render (no `showAlpha`) displays the 6-char `#rrggbb` value.\n\n[← Component overview](README.md)\n",
3213
3601
  "Condition": "# Condition\n\nRenders `children` when a condition is true, otherwise renders `fallback`. Simple conditional wrapper with no extra DOM when not needed.\n\n## Import\n\n```jsx\nimport { Condition } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `condition` | `boolean` | — | When true, render `children`; when false, render `fallback`. |\n| `children` | `ReactNode` | — | Content when condition is true. |\n| `fallback` | `ReactNode` | `null` | Content when condition is false. |\n\n## Example\n\n```jsx\n<Condition condition={isLoggedIn}>\n <Dashboard />\n</Condition>\n\n<Condition condition={loading} fallback={<Loader />}>\n <Content />\n</Condition>\n\n<Condition condition={hasError} fallback={<Alert variant=\"error\">Error</Alert>}>\n <Data />\n</Condition>\n```\n\n[← Component overview](README.md)\n",
3602
+ "ConfirmDialog": "# ConfirmDialog\n\nPage-level block: the confirmation step for delete, remove, archive or cancel — replaces native `confirm()`. The title asks the question, one line says what happens, and the action button is named with the verb. Extracted from firepro `components/ui/confirm-dialog.tsx`.\n\n## Import\n\n```jsx\nimport { ConfirmDialog } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `isOpen` | `boolean` | — | Whether the dialog is open |\n| `title` | `string` | — | The action as a question |\n| `description` | `ReactNode` | — | One line saying what will happen |\n| `confirmLabel` | `string` | — | The verb, e.g. \"Remove\" (never \"OK\") |\n| `cancelLabel` | `string` | `\"Cancel\"` | Cancel button label |\n| `destructive` | `boolean` | `false` | Red confirm button for actions that can't be undone |\n| `loading` | `boolean` | `false` | Busy confirm button; the dialog can't be dismissed |\n| `onConfirm` | `() => void` | — | Called on confirm |\n| `onCancel` | `() => void` | — | Called on cancel, Esc or backdrop click |\n\n## Example\n\n```jsx\n<ConfirmDialog\n isOpen={open}\n title=\"Remove Kiran M from Diwali buyers?\"\n description=\"They stop receiving this list's campaigns.\"\n confirmLabel=\"Remove\"\n destructive\n onConfirm={remove}\n onCancel={() => setOpen(false)}\n/>\n```\n\n[← Component overview](README.md)\n",
3214
3603
  "DatePicker": "# DatePicker\n\nSingle date picker with calendar popup. Accepts `Date` or ISO date string; supports min/max, clear button, and optional portal rendering.\n\n## Import\n\n```jsx\nimport { DatePicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `Date` \\| `string` | — | Selected date (Date or ISO string). |\n| `onChange` | `(date: Date \\| null) => void` | — | Called when date changes. |\n| `placeholder` | `string` | `\"Select date...\"` | Input placeholder. |\n| `label` | `string` | — | Label text. |\n| `error` | `string` | — | Error message. |\n| `disabled` | `boolean` | — | Disable the picker. |\n| `required` | `boolean` | — | Required field. |\n| `minDate` | `Date` | — | Minimum selectable date. |\n| `maxDate` | `Date` | — | Maximum selectable date. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `weekStartsOn` | `0` \\| `1` | `0` | 0 = Sunday, 1 = Monday. |\n| `portal` | `boolean` | `false` | Render calendar in `document.body`. |\n| `displayFormat` | `string` | `\"MMM dd, yyyy\"` | date-fns format for display. |\n| `locale` | `Locale` | — | date-fns locale. |\n| `showClear` | `boolean` | `true` | Show clear button. |\n| `closeOnSelect` | `boolean` | `true` | Close popup on date select. |\n\n## Example\n\n```jsx\nconst [date, setDate] = useState(null);\n<DatePicker\n value={date}\n onChange={setDate}\n placeholder=\"Select date\"\n minDate={new Date()}\n label=\"Start date\"\n/>\n```\n\n[← Component overview](README.md)\n",
3215
3604
  "DateRangePicker": "# DateRangePicker\n\nDate range picker: user selects start then end. Value is `{ start: Date, end: Date }`. Popup stays open until both are selected (or user clears). Supports min/max, clear, and optional portal.\n\n## Import\n\n```jsx\nimport { DateRangePicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `{ start: Date\\|string, end: Date\\|string }` | — | Selected range. |\n| `onChange` | `(range: { start, end } \\| null) => void` | — | Called when range changes. |\n| `placeholder` | `string` | `\"Select date range...\"` | Input placeholder. |\n| `label` | `string` | — | Label text. |\n| `error` | `string` | — | Error message. |\n| `disabled` | `boolean` | — | Disable. |\n| `required` | `boolean` | — | Required. |\n| `minDate` | `Date` | — | Minimum selectable date. |\n| `maxDate` | `Date` | — | Maximum selectable date. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `weekStartsOn` | `0` \\| `1` | `0` | 0 = Sunday, 1 = Monday. |\n| `portal` | `boolean` | `false` | Render popup in document.body. |\n| `displayFormat` | `string` | `\"MMM dd, yyyy\"` | date-fns format for display. |\n| `locale` | `Locale` | — | date-fns locale. |\n| `showClear` | `boolean` | `true` | Show clear button. |\n| `closeOnSelect` | `boolean` | `true` | Close when both dates selected. |\n\n## Example\n\n```jsx\nconst [range, setRange] = useState({ start: null, end: null });\n<DateRangePicker value={range} onChange={setRange} label=\"Period\" />\n```\n\n[← Component overview](README.md)\n",
3216
3605
  "Dialog": "# Dialog\n\nModal dialog with title, body, optional footer, and configurable size. Supports escape key and backdrop click to close when dismissible.\n\n## Import\n\n```jsx\nimport { Dialog } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `isOpen` | `boolean` | — | Controls visibility |\n| `onClose` | `function` | — | Called when dialog should close |\n| `title` | `string` | — | Dialog title |\n| `children` | `ReactNode` | — | Body content |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg`, `xl`, `full` |\n| `dismissible` | `boolean` | `true` | Allow close via backdrop or Escape |\n| `showCloseButton` | `boolean` | `true` | Show X button |\n| `footer` | `ReactNode` | — | Footer content (e.g. buttons) |\n| `className` | `string` | — | Extra CSS classes |\n| `onOpen` | `function` | — | Called when dialog opens (e.g. with dialogRef) |\n\n## Examples\n\n### Basic\n\n```jsx\nconst [open, setOpen] = useState(false);\n\n<Button onClick={() => setOpen(true)}>Open</Button>\n<Dialog\n isOpen={open}\n onClose={() => setOpen(false)}\n title=\"Confirm\"\n>\n Are you sure you want to continue?\n</Dialog>\n```\n\n### With footer\n\n```jsx\n<Dialog\n isOpen={open}\n onClose={() => setOpen(false)}\n title=\"Edit\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={() => setOpen(false)}>Cancel</Button>\n <Button onClick={handleSave}>Save</Button>\n </>\n }\n>\n <Input label=\"Name\" value={name} onChange={e => setName(e.target.value)} />\n</Dialog>\n```\n\n## Accessibility & keyboard\n\n`Dialog` renders `role=\"dialog\"` + `aria-modal=\"true\"` and backs that promise\nwith real focus management (no extra dependency — a built-in focus trap):\n\n| Behavior | Detail |\n|----------|--------|\n| Focus on open | Focus moves to the first focusable element inside the panel (or the panel itself when there is none). |\n| Focus trap | `Tab` / `Shift`+`Tab` cycle only through focusable elements inside the panel; focus cannot reach the page behind the modal. |\n| Focus restore | On close, focus returns to the element that had it when the dialog opened (typically the trigger). |\n| `Escape` | Closes the dialog when `dismissible` (default); ignored when `dismissible={false}`. |\n| Backdrop click | Closes the dialog when `dismissible`. |\n| Reduced motion | The scale-in entrance is suppressed under `prefers-reduced-motion` (`motion-reduce:animate-none`). |\n| Labelling | When `title` is set, the panel is `aria-labelledby` the title heading. |\n\n[← Component overview](README.md)\n",
3606
+ "EmptyState": "# EmptyState\n\nPage-level block: what a card shows when it has no data — an icon, a title, one line saying what to do, and the action that fixes it. Extracted from procurementpro `components/common/empty-state.tsx`.\n\n## Import\n\n```jsx\nimport { EmptyState } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `title` | `ReactNode` | — | What is empty |\n| `description` | `ReactNode` | — | One line of guidance |\n| `icon` | `ReactNode` | Inbox icon | Muted icon |\n| `action` | `ReactNode` | — | Primary or outlined button |\n| `compact` | `boolean` | `false` | Less vertical padding |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<EmptyState\n title=\"No obligations this month\"\n description=\"Add the first obligation for a client, or import your compliance register.\"\n action={<Button>Add obligation</Button>}\n/>\n```\n\n[← Component overview](README.md)\n",
3607
+ "ErrorState": "# ErrorState\n\nPage-level block: what a card shows when loading failed — neutral copy and a retry, rendered with `role=\"alert\"`. Never shows a raw `error.message`. Extracted from CampaignPro `components/app/page-error.tsx`.\n\n## Import\n\n```jsx\nimport { ErrorState } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `title` | `ReactNode` | `\"We couldn't load this\"` | What failed |\n| `description` | `ReactNode` | Reassurance line | What the user can do |\n| `onRetry` | `() => void` | — | Shows a Try again button |\n| `retryLabel` | `string` | `\"Try again\"` | Retry button label |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<ErrorState title=\"We couldn't load the compliance calendar\" onRetry={refetch} />\n```\n\n[← Component overview](README.md)\n",
3217
3608
  "FileUpload": "# FileUpload\n\nFile upload zone: drag-and-drop or click to open file dialog. Supports single/multiple, `accept`, `maxSize` validation, and list of selected files with remove. Calls `onFileSelect` with valid files and `onFileRemove` with index.\n\n## Import\n\n```jsx\nimport { FileUpload } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `accept` | `string` | — | Input accept (e.g. `\"image/*\"`, `\".pdf\"`). |\n| `multiple` | `boolean` | `false` | Allow multiple files. |\n| `maxSize` | `number` | — | Max file size in bytes; larger files trigger error and are not added. |\n| `onFileSelect` | `(files: File[]) => void` | — | Called with valid selected/dropped files. |\n| `onFileRemove` | `(fileIndex: number) => void` | — | Called when user removes a file from the list. |\n| `files` | `File[]` | `[]` | List of files to display (e.g. controlled from parent). |\n| `disabled` | `boolean` | `false` | Disable the zone and remove buttons. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| ...rest | — | — | Passed to wrapper div. |\n\n## Behavior\n\n- Validates each file: if `maxSize` is set and file.size > maxSize, shows error and does not add that file.\n- File type icons: image, text/document, or generic file.\n- Displays file size in human-readable form (Bytes, KB, MB, GB).\n\n## Example\n\n```jsx\nconst [files, setFiles] = useState([]);\n<FileUpload\n accept=\"image/*\"\n multiple\n maxSize={5 * 1024 * 1024}\n files={files}\n onFileSelect={(newFiles) => setFiles(prev => [...prev, ...newFiles])}\n onFileRemove={(index) => setFiles(prev => prev.filter((_, i) => i !== index))}\n/>\n```\n\n[← Component overview](README.md)\n",
3218
3609
  "FootNav": "# FootNav\n\nFixed bottom navigation for mobile (hidden on `md` and up via `md:hidden`). Renders a row of items with icon and label; optional badge per item. Items support `onClick`.\n\n## Import\n\n```jsx\nimport { FootNav } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `items` | `Array` | `[]` | Each item: `{ id, label, icon, onClick?, active?, badge? }`. |\n| `className` | `string` | — | Nav wrapper CSS classes. |\n| ...rest | — | — | Passed to `<nav>`. |\n\n## Item shape\n\n- `id`: string or number (key).\n- `label`: string.\n- `icon`: ReactNode (e.g. Lucide icon).\n- `onClick(item)`: optional; called when item is clicked.\n- `active`: boolean; highlights as primary.\n- `badge`: optional number or node; shown as small badge on the icon.\n\n## Example\n\n```jsx\n<FootNav\n items={[\n { id: 'home', label: 'Home', icon: <Home />, onClick: () => navigate('/'), active: path === '/' },\n { id: 'search', label: 'Search', icon: <Search />, onClick: () => openSearch() },\n { id: 'notifications', label: 'Alerts', icon: <Bell />, badge: 3, onClick: () => openNotifications() },\n { id: 'profile', label: 'Profile', icon: <User />, onClick: () => navigate('/profile') },\n ]}\n/>\n```\n\n[← Component overview](README.md)\n",
3219
3610
  "Form": "# Form\n\nForm built on **react-hook-form** with a `fields` config array. Renders Input, Select, DatePicker, Checkbox, Switch, textarea, etc., and handles submit/reset. Use `validationSchema` (e.g. from `@hookform/resolvers` with yup) for validation.\n\n## Import\n\n```jsx\nimport { Form } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `fields` | `Array` | `[]` | Field configs (see Field shape below). |\n| `onSubmit` | `(data: object) => void` | — | Submit handler; receives form data. |\n| `defaultValues` | `object` | `{}` | Default values for each field name. |\n| `validationSchema` | `object` | — | Resolver from e.g. `yupResolver(schema)`. |\n| `className` | `string` | — | Form wrapper CSS classes. |\n| `submitText` | `string` | `\"Submit\"` | Submit button text. |\n| `resetText` | `string` | `\"Reset\"` | Reset button text. |\n| `submitButtonClass` | `string` | — | Submit button CSS classes. |\n| `resetButtonClass` | `string` | — | Reset button CSS classes. |\n| `showReset` | `boolean` | `true` | Show reset button. |\n| `submitDisabled` | `boolean` | `false` | Disable submit button. |\n| ...rest | — | — | Passed to react-hook-form `useForm` (e.g. `mode`). |\n\n## Field shape\n\nEach item in `fields` can have:\n\n- `type`: `\"text\"` \\| `\"email\"` \\| `\"password\"` \\| `\"number\"` \\| `\"tel\"` \\| `\"url\"` \\| `\"select\"` \\| `\"date\"` \\| `\"textarea\"` \\| `\"checkbox\"` \\| `\"switch\"` (and possibly others; check component for full list).\n- `name`: string (required).\n- `label`: string.\n- `required`: boolean.\n- `placeholder`: string.\n- `options`: for select, array of `{ value, label }`; other options passed to the underlying component.\n- `rows`: for textarea.\n- Any other prop passed through to the rendered component (e.g. `disabled`).\n\n## Example\n\n```jsx\nimport { Form } from '@dreamtree-org/twreact-ui';\nimport { yupResolver } from '@hookform/resolvers/yup';\nimport * as yup from 'yup';\n\nconst schema = yup.object({\n email: yup.string().email().required(),\n password: yup.string().min(6).required(),\n});\n\n<Form\n fields={[\n { type: 'email', name: 'email', label: 'Email', required: true, placeholder: 'you@example.com' },\n { type: 'password', name: 'password', label: 'Password', required: true },\n { type: 'select', name: 'role', label: 'Role', options: [{ value: 'user', label: 'User' }, { value: 'admin', label: 'Admin' }] },\n ]}\n defaultValues={{ email: '', password: '', role: 'user' }}\n validationSchema={yupResolver(schema)}\n onSubmit={(data) => console.log(data)}\n submitText=\"Sign in\"\n showReset\n/>\n```\n\n[← Component overview](README.md)\n",
3220
3611
  "Input": "# Input\n\nText input with optional label, error/success states, clear button, character count, and password visibility toggle.\n\n## Import\n\n```jsx\nimport { Input } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `id` | `string` | — | Input id (auto-generated if not set) |\n| `className` | `string` | — | Extra CSS classes |\n| `type` | `string` | `\"text\"` | `text`, `search`, `password`, etc. |\n| `label` | `string` | — | Label text |\n| `placeholder` | `string` | — | Placeholder |\n| `error` | `string` | — | Error message (shows error state) |\n| `success` | `boolean` | — | Success state |\n| `disabled` | `boolean` | `false` | Disable input |\n| `required` | `boolean` | `false` | Required field |\n| `clearable` | `boolean` | `false` | Show clear button when has value |\n| `icon` | `ReactNode` | — | Left or right icon |\n| `iconPosition` | `\"left\"` \\| `\"right\"` | `\"left\"` | Icon position |\n| `onClear` | `function` | — | Called when clear is clicked |\n| `showCount` | `boolean` | `false` | Show character count |\n| `maxLength` | `number` | — | Max length (used with showCount) |\n| `readOnly` | `boolean` | `false` | Read-only |\n| `value` | `string` | — | Controlled value |\n| `defaultValue` | `string` | — | Uncontrolled default |\n| `onChange` | `function` | — | Change handler |\n| ...rest | — | — | Passed to native `<input>` |\n\n## Examples\n\n### Basic\n\n```jsx\n<Input label=\"Email\" placeholder=\"you@example.com\" />\n```\n\n### With error\n\n```jsx\n<Input label=\"Email\" error=\"Invalid email address\" />\n```\n\n### Password with toggle\n\n```jsx\n<Input type=\"password\" label=\"Password\" placeholder=\"Enter password\" />\n```\n\n### Clearable and character count\n\n```jsx\n<Input\n label=\"Bio\"\n clearable\n showCount\n maxLength={200}\n placeholder=\"Tell us about yourself\"\n/>\n```\n\n[← Component overview](README.md)\n",
3221
3612
  "Loader": "# Loader\n\nLoading indicator in three variants: spinner (default), dots, or bar. Configurable size, speed, colors, and optional label text.\n\n## Import\n\n```jsx\nimport { Loader } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` \\| `number` | `\"md\"` | Size: sm=14px, md=20px, lg=36px, or numeric px. |\n| `speed` | `number` | `0.9` | Animation duration in seconds. |\n| `thickness` | `number` | `2.5` | Stroke thickness (spinner). |\n| `color` | `string` | `\"currentColor\"` | Main color (spinner/dots/bar). |\n| `secondaryColor` | `string` | `\"rgba(0,0,0,0.08)\"` | Track/background color. |\n| `variant` | `\"spinner\"` \\| `\"dots\"` \\| `\"bar\"` | `\"spinner\"` | Visual style. |\n| `className` | `string` | `\"\"` | Wrapper CSS classes. |\n| `inline` | `boolean` | `false` | If true, wrapper is inline-flex. |\n| `ariaLabel` | `string` | `\"Loading\"` | Accessibility label. |\n| `text` | `string` | — | Optional label shown next to loader. |\n| `reverse` | `boolean` | `false` | Reverse spinner rotation direction. |\n| `style` | `object` | — | Inline style for wrapper. |\n\n## Examples\n\n```jsx\n<Loader />\n<Loader variant=\"dots\" size=\"sm\" />\n<Loader variant=\"bar\" text=\"Loading...\" />\n<Loader size={32} color=\"#2563eb\" />\n```\n\n[← Component overview](README.md)\n",
3613
+ "LoadingState": "# LoadingState\n\nPage-level block: a skeleton shaped like the content that will replace it (table rows, cards, KPI tiles or a key–value panel). One `role=\"status\"` region with screen-reader text; no page-level spinners.\n\n## Import\n\n```jsx\nimport { LoadingState } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `variant` | `table` \\| `cards` \\| `stats` \\| `detail` | `\"table\"` | Shape of the skeleton |\n| `rows` | `number` | 5 (4 for stats, 3 for cards) | How many rows or tiles |\n| `label` | `string` | `\"Loading…\"` | Screen-reader text |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n{isLoading ? <LoadingState variant=\"table\" rows={5} label=\"Loading obligations\" /> : <Table … />}\n```\n\n[← Component overview](README.md)\n",
3222
3614
  "LocationPicker": "# LocationPicker\n\nInput for latitude/longitude. User can type `lat, lng` (e.g. `12.34, 56.78`) or click the icon to use browser geolocation. Value is `{ lat, lng }` or `null`.\n\n## Import\n\n```jsx\nimport { LocationPicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `{ lat: number, lng: number }` \\| `null` | `null` | Current coordinates. |\n| `onChange` | `(coords: { lat, lng } \\| null) => void` | — | Called when value changes (typed or from geolocation). |\n| `inputProps` | `object` | `{}` | Props spread to the underlying input (e.g. `placeholder`). A supplied `inputProps.onChange` is forwarded **in addition to** (not instead of) the component's coord-parsing handler — both fire. `inputProps.className` is merged via `cn()`. |\n| `className` | `string` | — | Classes merged (via `cn()`) onto the root wrapper. |\n| `icon` | `ReactNode` | default pin SVG | Icon for \"use my location\" button. |\n| `iconProps` | `object` | `{}` | Props for the icon button (e.g. `className`, `onClick`). |\n| `iconPosition` | `\"left\"` \\| `\"right\"` | `\"right\"` | Position of the icon button. |\n| `placeholder` | `string` | `\"lat, lng\"` | Input placeholder. |\n\n## Behavior\n\n- Display format is `lat.toFixed(6), lng.toFixed(6)`.\n- Typing a valid \"lat, lng\" string calls `onChange` with `{ lat, lng }` (e.g.\n typing `\"1.5, 2.5\"` calls `onChange({ lat: 1.5, lng: 2.5 })`). A\n consumer-supplied `inputProps.onChange` still fires alongside it.\n- Clearing the input calls `onChange(null)`.\n- Clicking the icon calls `navigator.geolocation.getCurrentPosition` and passes the result to `onChange`. Requires HTTPS in production.\n- Forwards `ref` to the underlying `<input>` and spreads unknown props (`...rest`) onto the root wrapper.\n\n## Example\n\n```jsx\nconst [location, setLocation] = useState(null);\n<LocationPicker\n value={location}\n onChange={setLocation}\n placeholder=\"Enter lat, lng or use location\"\n/>\n```\n\n[← Component overview](README.md)\n",
3223
3615
  "Navbar": "# Navbar\n\nTop navigation bar with logo, nav items (links or buttons), optional search, notifications dropdown, and login/logout. Uses an **emitter** for notification actions (view, markAsRead, markAsUnread, clearAll, delete). Responsive: left icon and hamburger on mobile; desktop nav and search. You can replace the right area with a custom component via `rightMenuContent`.\n\n## Import\n\n```jsx\nimport { Navbar } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `emitter` | `object` | **required** | Event emitter for notifications (e.g. `notification:view`, `notification:markAsRead`, etc.). |\n| `logo` | `ReactNode` | — | Logo (e.g. image or link). |\n| `items` | `Array` | `[]` | Nav items: `{ id, label, href?, active?, onClick?(item) }`. If `href` is set, renders `<a>`; else `<button>` with `onClick`. |\n| `user` | `object` \\| `null` | — | If set, shows logout button; if null/undefined, shows login button. |\n| `notifications` | `Array` | `[]` | List of notifications for the dropdown. Each: `{ id, title, message?, read? }`. |\n| `searchable` | `boolean` | `false` | Show search input (desktop). |\n| `onSearch` | `(term: string) => void` | — | Called on search form submit with current search term. |\n| `leftIcon` | `ReactNode` \\| `false` | `<ChevronLeft />` | Icon shown on mobile (e.g. back). Set `false` to hide. |\n| `onLeftIconClick` | `() => void` | — | Called when left icon (mobile) is clicked. |\n| `rightMenuContent` | `(props) => ReactNode` | — | Custom right menu. Receives `{ notifications }` and any Navbar `...props` (e.g. `emitter`, `onFetch`). If not provided, default renders notifications dropdown. |\n| `onLoginClick` | `() => void` | — | Called when login button is clicked. |\n| `onLogoutClick` | `() => void` | — | Called when logout button is clicked. |\n| `onFetch` | `({ setNotifications }) => void` | — | Refresh handler for the default notifications dropdown. Called on mount and when `notifications` changes. Consumed by the right menu — it is **not** forwarded onto `<nav>`. |\n| `className` | `string` | — | Wrapper CSS classes. |\n| ...rest | — | — | Only DOM-valid attributes (e.g. `id`, `aria-*`, `data-*`, event handlers) are forwarded to the root `<nav>`. The component's own control props (`isMobileOpen`, `setIsMobileOpen`, `onFetch`, …) are consumed and never leak to the DOM. |\n\n`Navbar` forwards `ref` to its root `<nav>` element.\n\n## Item shape (items)\n\n- `id`: string or number (key).\n- `label`: string.\n- `href`: optional; if set, item is an `<a href={href}>`.\n- `active`: optional boolean; applies primary styling.\n- `onClick(item)`: optional; used when no `href` (button click).\n\n## Notification shape (notifications)\n\n- `id`: string or number.\n- `title`: string.\n- `message`: optional string.\n- `read`: optional boolean; unread count uses `!n.read`.\n\n## Default notifications dropdown\n\nIf `rightMenuContent` is not provided, the right menu shows a bell icon and dropdown with:\n\n- Unread count badge.\n- \"Clear all\" that clears the list and calls emitter `notification:clearAll`.\n- Per notification: title, message, \"Mark as Read\" / \"Mark as Unread\", \"Delete\" (emitter events).\n- Clicking a notification calls `emitter.emit('notification:view', [n])`.\n\nTo refresh the list from the server, pass `onFetch({ setNotifications })`; the default dropdown uses it on mount and when `notifications` prop changes.\n\n## Example\n\n```jsx\nimport { Navbar } from '@dreamtree-org/twreact-ui';\nimport MyEmitter from './emitter';\n\n<Navbar\n emitter={MyEmitter}\n logo={<a href=\"/\"><img src=\"/logo.svg\" alt=\"Home\" /></a>}\n items={[\n { id: '1', label: 'Home', href: '/', active: pathname === '/' },\n { id: '2', label: 'About', href: '/about' },\n { id: '3', label: 'Settings', onClick: () => openSettings() },\n ]}\n user={user}\n notifications={notifications}\n searchable\n onSearch={(term) => doSearch(term)}\n onLoginClick={() => navigate('/login')}\n onLogoutClick={logout}\n onFetch={async ({ setNotifications }) => {\n const res = await fetch('/api/notifications');\n const data = await res.json();\n setNotifications(data.items);\n }}\n/>\n```\n\n[← Component overview](README.md)\n",
3616
+ "PageHeader": "# PageHeader\n\nPage-level block: the page title (`h1`), a muted subtitle, the page's actions (one primary action) and an optional back link. Extracted from taskpro `features/shared/PageHeader.jsx`.\n\n## Import\n\n```jsx\nimport { PageHeader } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `title` | `ReactNode` | — | Page title, rendered as the page's only `h1` |\n| `subtitle` | `ReactNode` | — | One line under the title |\n| `actions` | `ReactNode` | — | Buttons on the right; keep one primary action |\n| `back` | `{ href: string; label: string }` | — | Back link for detail pages |\n| `linkAs` | `ElementType` | `\"a\"` | Link element for the back link |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<PageHeader\n title=\"Compliance calendar\"\n subtitle=\"Track deadlines, ownership and filing readiness across clients\"\n actions={<Button leftIcon={<Plus className=\"h-4 w-4\" />}>Add obligation</Button>}\n/>\n```\n\n[← Component overview](README.md)\n",
3224
3617
  "Pagination": "# Pagination\n\nPage navigation: first, previous, page numbers, next, last. Renders only when `totalPages > 1`. Optional `theme` for styling (e.g. from Table).\n\n## Import\n\n```jsx\nimport { Pagination } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `currentPage` | `number` | `1` | Current page (1-based). |\n| `totalPages` | `number` | `1` | Total pages. If ≤ 1, component returns null. |\n| `onPageChange` | `(page: number) => void` | — | Called when user selects a page. |\n| `showFirstLast` | `boolean` | `true` | Show first/last page buttons. |\n| `showPrevNext` | `boolean` | `true` | Show previous/next buttons. |\n| `maxVisiblePages` | `number` | `5` | Max page number buttons to show. |\n| `className` | `string` | — | Nav wrapper CSS classes. |\n| ...rest | — | — | Passed to `<nav>`. |\n\n## Example\n\n```jsx\n<Pagination\n currentPage={page}\n totalPages={Math.ceil(total / pageSize)}\n onPageChange={setPage}\n maxVisiblePages={5}\n/>\n```\n\n[← Component overview](README.md)\n",
3225
3618
  "PriceRangePicker": "# PriceRangePicker\n\nDual-thumb range slider for min/max values (e.g. price or any number). Optional number inputs and preset buttons. Controlled via `value` or uncontrolled via `defaultValue`.\n\n## Import\n\n```jsx\nimport { PriceRangePicker } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `min` | `number` | `0` | Minimum value. |\n| `max` | `number` | `1000` | Maximum value. |\n| `step` | `number` | `1` | Step for thumbs and inputs. |\n| `minGap` | `number` | `5` | Minimum gap between min and max. |\n| `value` | `{ min, max }` \\| `null` | `null` | Controlled value. |\n| `defaultValue` | `{ min, max }` | `{ min: 0, max: 1000 }` | Uncontrolled initial value. |\n| `onChange` | `(value: { min, max }) => void` | `() => {}` | Called on thumb/input change. |\n| `onFinalChange` | `(value: { min, max }) => void` | `() => {}` | Called on mouseup/touchend or Enter in inputs. |\n| `showInputs` | `boolean` | `true` | Show min/max number inputs. |\n| `showTooltips` | `boolean` | `true` | Show tooltips on thumbs. |\n| `disabled` | `boolean` | `false` | Disable the control. |\n| `presets` | `Array<{ id, label, value: { min, max } }>` | `[]` | Preset buttons. |\n| `formatValue` | `(n: number) => string` | `(n) => String(n)` | Format display (e.g. currency). |\n| `className` | `string` | `\"\"` | Container CSS classes. |\n| `size` | `\"sm\"` \\| `\"md\"` \\| `\"lg\"` | `\"md\"` | Thumb and spacing size. |\n| `colorClass` | `string` | `\"bg-pink-500\"` | Tailwind class for active track/thumb. |\n| `ariaLabel` | `string` | `\"Price range\"` | Accessibility label. |\n\n## Example\n\n```jsx\nconst [range, setRange] = useState({ min: 100, max: 500 });\n<PriceRangePicker\n min={0}\n max={1000}\n value={range}\n onChange={setRange}\n formatValue={(n) => `$${n}`}\n presets={[\n { id: '1', label: 'Under $200', value: { min: 0, max: 200 } },\n ]}\n/>\n```\n\n[← Component overview](README.md)\n",
3226
3619
  "ProgressBar": "# ProgressBar\n\nProgress bar showing a value between 0 and max. Supports custom HEX colors, optional label, value text inside the bar, striped and animated styles.\n\n## Import\n\n```jsx\nimport { ProgressBar } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `value` | `number` | `0` | Current value. |\n| `max` | `number` | `100` | Maximum value. |\n| `size` | `\"xs\"` \\| `\"sm\"` \\| `\"md\"` \\| `\"lg\"` \\| `number` | `\"md\"` | Height: xs/sm/md/lg or px. |\n| `showValue` | `boolean` | `false` | Show percentage text inside the fill. |\n| `rounded` | `boolean` | `true` | Rounded corners. |\n| `color` | `string` (HEX) | `\"#2563eb\"` | Fill color (HEX only). |\n| `bgColor` | `string` (HEX) | `\"#e6eefc\"` | Track background (HEX only). |\n| `valueColor` | `string` (HEX) | auto | Text color inside fill; auto from luminance if omitted. |\n| `striped` | `boolean` | `false` | Striped fill pattern. |\n| `animated` | `boolean` | `false` | Animate stripe movement. |\n| `label` | `string` | `\"\"` | Label above the bar. |\n| `ariaLabel` | `string` | `\"Progress\"` | Accessibility label. |\n| `className` | `string` | `\"\"` | Wrapper CSS classes. |\n| `style` | `object` | `{}` | Wrapper inline style. |\n\n## Example\n\n```jsx\n<ProgressBar value={60} max={100} />\n<ProgressBar value={75} showValue label=\"Upload\" color=\"#16a34a\" />\n<ProgressBar value={40} striped animated size=\"lg\" />\n```\n\n[← Component overview](README.md)\n",
@@ -3231,6 +3624,8 @@
3231
3624
  "Sidebar": "# Sidebar\n\nCollapsible side navigation with optional nested items, logo, and user block. Supports desktop collapsed state and mobile drawer. Tooltips show when collapsed; optional `drawerPosition` for mobile.\n\n## Import\n\n```jsx\nimport { Sidebar } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `items` | `Array` | `[]` | Nav items: `{ id, label, icon, children?, onClick?, active? }`. `children` = nested items. |\n| `collapsed` | `boolean` | `false` | Desktop collapsed state (icon-only). |\n| `onToggle` | `() => void` | — | Toggle collapse (e.g. chevron click). |\n| `className` | `string` | — | Wrapper CSS classes. |\n| `logo` | `ReactNode` | — | Logo at top. |\n| `user` | `object` \\| `ReactNode` | — | User block at bottom. |\n| `onUserClick` | `function` | — | User block click. |\n| `drawerPosition` | `\"left\"` \\| `\"right\"` | `\"left\"` | Mobile drawer side. |\n| `isMobileOpen` | `boolean` | — | Controlled mobile open. |\n| `setIsMobileOpen` | `(open: boolean) => void` | — | Set mobile open (when controlled). |\n| `showCollapsedTooltips` | `boolean` | `true` | Show tooltips when collapsed. |\n| `tooltipOptions` | `object` | — | Options for internal Tooltip. |\n\n## Item shape\n\n- `id`: string (required for expand/active).\n- `label`: string.\n- `icon`: ReactNode.\n- `children`: optional array of same shape (nested items).\n- `onClick`: optional handler.\n- `active`: optional boolean for current page.\n\n## Example\n\n```jsx\nconst [collapsed, setCollapsed] = useState(false);\n<Sidebar\n items={[\n { id: 'dash', label: 'Dashboard', icon: <Home /> },\n { id: 'settings', label: 'Settings', icon: <Settings />, children: [\n { id: 'profile', label: 'Profile' },\n { id: 'security', label: 'Security' },\n ]},\n ]}\n collapsed={collapsed}\n onToggle={() => setCollapsed(!collapsed)}\n logo={<Logo />}\n user={{ name: 'Jane', avatar: '/jane.jpg' }}\n/>\n```\n\n[← Component overview](README.md)\n",
3232
3625
  "Skeleton": "# Skeleton\n\nPlaceholder blocks for loading states. Renders shimmer bars (or circles) when `active` is true; otherwise renders `children`.\n\n## Import\n\n```jsx\nimport { Skeleton } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `count` | `number` | `1` | Number of skeleton items. |\n| `circle` | `boolean` | `false` | Render items as circles. |\n| `height` | `number` \\| `string` | `16` | Height in px or CSS unit (e.g. `\"2rem\"`). |\n| `width` | `number` \\| `string` | — | Width; default 100% (column) or height (inline/circle). |\n| `rounded` | `boolean` | `true` | Rounded corners (when not circle). |\n| `animated` | `boolean` | `true` | Shimmer animation. |\n| `active` | `boolean` | `true` | If true show skeletons; if false render children. |\n| `gap` | `string` | `\"8px\"` | Spacing between items. |\n| `inline` | `boolean` | `false` | Layout items in a row. |\n| `className` | `string` | `\"\"` | Wrapper CSS classes. |\n| `style` | `object` | `{}` | Wrapper inline style. |\n| `children` | `ReactNode` | `null` | Rendered when `active` is false. |\n\n## Examples\n\n```jsx\n<Skeleton />\n<Skeleton count={3} height={20} gap=\"12px\" />\n<Skeleton circle height={40} />\n<Skeleton active={loading} count={2}>\n <RealContent />\n</Skeleton>\n```\n\n[← Component overview](README.md)\n",
3233
3626
  "SpeechToText": "# SpeechToText\n\nVoice-to-text using the Web Speech API. Headless by default — render a custom\ncontrol via `renderButton`, or drive it imperatively through the ref. The\nbuilt-in fallback button is `className`-extensible and forwards arbitrary props.\n\n## Import\n\n```jsx\nimport { SpeechToText } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `lang` | `string` | `\"en-US\"` | Recognition language |\n| `continuous` | `boolean` | `true` | Keep listening until stopped (forced `false` on mobile) |\n| `interimResults` | `boolean` | `true` | Return interim results |\n| `onSpeechComplete` | `function` | — | Final transcript chunk callback `(text) => void` |\n| `onSpeaking` | `function` | — | Interim transcript callback `(text) => void` |\n| `onError` | `function` | — | Fatal error callback `(Error) => void`. Routine events (`no-speech`, `aborted`) are **not** surfaced |\n| `onStart` | `function` | — | Start callback |\n| `onStop` | `function` | — | Stop callback |\n| `renderButton` | `function` | — | Custom control render-prop (see signature below) |\n| `autoStart` | `boolean` | `false` | Start on mount (once, after support is detected) |\n| `disabled` | `boolean` | `false` | Disable |\n| `resetOnStart` | `boolean` | `false` | Clear the accumulated transcript when a new session starts |\n| `className` | `string` | — | Merged (via `cn`) onto the default button |\n| `...rest` | — | — | Forwarded to the default `<button>` |\n\nThe default button is keyboard-accessible (`aria-pressed`, `aria-label`) and\ntoken-driven. When `renderButton` is supplied, `className`/`...rest` are not\napplied (you own the rendered control).\n\n## `renderButton` signature\n\n```js\nrenderButton({ isListening, isSupported, error, start, stop, toggle, disabled })\n```\n\n## Ref methods\n\n`start()`, `stop()`, `toggle()`, `isListening()`, `isSupported()`,\n`getTranscript()`, `clearTranscript()`, `getError()`.\n\n## Example\n\n```jsx\nconst [transcript, setTranscript] = useState('');\n<SpeechToText\n onSpeechComplete={(chunk) => setTranscript((t) => `${t} ${chunk}`.trim())}\n onError={(e) => console.error(e)}\n/>\n<div>Transcript: {transcript}</div>\n```\n\n[← Component overview](README.md)\n",
3627
+ "StatCard": "# StatCard\n\nPage-level block: a KPI tile with an icon, a label, a big tabular number and an optional change vs a previous period. Extracted from PracticePro `components/ui/StatCard.tsx`.\n\n## Import\n\n```jsx\nimport { StatCard } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `label` | `ReactNode` | — | What the number counts |\n| `value` | `ReactNode` | — | The figure |\n| `icon` | `ReactNode` | — | Lucide icon shown in a tinted chip |\n| `tone` | `primary` \\| `secondary` \\| `success` \\| `warning` \\| `error` \\| `info` | `\"primary\"` | Chip colour |\n| `change` | `{ value: number; period?: string; unit?: '%' \\| ''; goodWhenUp?: boolean }` | — | Change indicator; `goodWhenUp: false` makes a rise red |\n| `loading` | `boolean` | `false` | Shows a skeleton instead of the value |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\n<StatCard label=\"Due this week\" value={18} tone=\"primary\" icon={<CalendarDays className=\"h-6 w-6\" />} />\n<StatCard label=\"Overdue\" value={3} tone=\"error\" change={{ value: 2, unit: '', period: 'vs last week', goodWhenUp: false }} />\n```\n\n[← Component overview](README.md)\n",
3628
+ "StatusPill": "# StatusPill\n\nPage-level block: one status badge for every domain state. Map each state to a tone and a human label once; unknown states are humanized (`in_review` → \"In review\"), never shown raw. Extracted from billpro `components/StatusPill.jsx`.\n\n## Import\n\n```jsx\nimport { StatusPill } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `status` | `string` | — | Domain state to look up in `map` |\n| `map` | `Record<string, { label; tone; icon? }>` | — | State → label and tone (`neutral`, `info`, `success`, `warning`, `danger`, `muted`) |\n| `tone` | `string` | `\"neutral\"` | Tone when not using `map`; also accepts semantic tones |\n| `icon` | `ReactNode` | — | Small leading icon |\n| `children` | `ReactNode` | — | Label override |\n| `className` | `string` | — | Extra CSS classes |\n\n## Example\n\n```jsx\nconst OBLIGATION_STATUS = {\n overdue: { label: 'Overdue', tone: 'danger' },\n docs_missing: { label: 'Missing documents', tone: 'warning' },\n ready: { label: 'Ready for review', tone: 'info' },\n filed: { label: 'Filed', tone: 'success' },\n};\n\n<StatusPill status=\"docs_missing\" map={OBLIGATION_STATUS} />\n<StatusPill tone=\"success\">Filed</StatusPill>\n```\n\n[← Component overview](README.md)\n",
3234
3629
  "Stepper": "# Stepper\n\nHorizontal or vertical step indicator. Each step has circle (with check for completed), optional line, and label. Optional `onStepClick` for clickable steps.\n\n## Import\n\n```jsx\nimport { Stepper } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `steps` | `Array` | `[]` | Each step: `{ id?, label?, description? }`. |\n| `currentStep` | `number` | `0` | Zero-based index of current step. |\n| `orientation` | `\"horizontal\"` \\| `\"vertical\"` | `\"horizontal\"` | Layout. |\n| `variant` | `string` | `\"default\"` | Visual variant. |\n| `onStepClick` | `(step, stepIndex) => void` | — | Called when a step is clicked (e.g. for completed/current). |\n| `className` | `string` | — | Nav wrapper CSS classes. |\n| ...rest | — | — | Passed to `<nav>`. |\n\n## Step status\n\n- **completed**: `stepIndex < currentStep` (check icon, primary color).\n- **current**: `stepIndex === currentStep`.\n- **upcoming**: `stepIndex > currentStep` (gray).\n\n## Example\n\n```jsx\n<Stepper\n steps={[\n { id: '1', label: 'Details' },\n { id: '2', label: 'Payment' },\n { id: '3', label: 'Confirm' },\n ]}\n currentStep={1}\n orientation=\"horizontal\"\n onStepClick={(step, idx) => setStep(idx)}\n/>\n```\n\n[← Component overview](README.md)\n",
3235
3630
  "Switch": "# Switch\n\nToggle switch (on/off). Controlled or uncontrolled.\n\n## Import\n\n```jsx\nimport { Switch } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `checked` | `boolean` | — | Controlled checked |\n| `defaultChecked` | `boolean` | `false` | Uncontrolled default |\n| `onChange` | `(checked: boolean) => void` | — | Receives the new boolean state (not a DOM event) |\n| `disabled` | `boolean` | `false` | Disable |\n| `size` | `string` | `\"md\"` | `sm`, `md`, `lg` |\n| `color` | `string` | `\"primary\"` | Color preset (`primary`, `secondary`, `success`, `error`, `warning`, `info`, etc.) or custom hex/RGB color string |\n| `name` | `string` | — | Form name |\n| `topLabel` | `string` | — | Label above |\n| `bottomLabel` | `string` | — | Label below |\n| `leftLabel` | `string` | — | Label left |\n| `rightLabel` | `string` | — | Label right |\n\n## Example\n\n```jsx\n<Switch checked={enabled} onChange={checked => setEnabled(checked)} />\n<Switch defaultChecked rightLabel=\"Enable notifications\" />\n```\n\n[← Component overview](README.md)\n",
3236
3631
  "Table": "# Table\n\nData table with sorting, filtering, pagination, row selection, expandable details, and responsive mobile card view.\n\n## Import\n\n```jsx\nimport { Table } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `Array` | `[]` | Row data (array of objects) |\n| `columns` | `Array` | `[]` | Column definitions `[{ key, label, sortable?, isVisible?, editable?, type?, options?, required?, validate?, onRender?, render? }]` |\n| `interactive` | `boolean` | `false` | Enable interactive mode with inline cell editing (double-click cell), keyboard shortcuts (`Tab`/`Shift+Tab` cell navigation, `Enter` to save modified changes, `Arrows`, `Escape`), primary Save button, outline Clear Changes button, constraint validation before save, and instant new row addition. |\n| `onSave` | `function` | — | Save callback in interactive mode `({ updatedRows, newRows, allData, tableData }) => Promise<allData | { data: allData } | void> | void`. Returning a dataset array or `{ data }`/`{ allData }` updates internal state with returned records. |\n| `onEdit` | `function` | — | Live edit callback fired when a cell value changes or row edit action is clicked `({ rowKey, colKey, newValue, row, action, isNew }) => void` |\n| `sortable` | `boolean` | `true` | Enable column sort |\n| `filterable` | `boolean` | `false` | Enable column filters |\n| `selectable` | `boolean` | `false` | Row selection checkboxes |\n| `pagination` | `boolean` | `false` | Enable pagination |\n| `pageSize` | `number` | `25` | Rows per page |\n| `onSort` | `function` | — | Sort callback |\n| `onFilter` | `function` | — | Filter callback |\n| `onFetch` | `function` | — | Server-side fetch (setData, setLoading, filters, page, limit, sort) |\n| `onFilterChange` | `function` | — | Filter change callback |\n| `onSelectionChange` | `function` | — | Selected rows callback |\n| `onRowClick` | `function` | — | Row click callback |\n| `hasDetails` | `boolean` or `(row, index) => boolean` | `false` | Expandable row details. Pass a predicate to give only matching rows an expand toggle; non-matching rows render an empty placeholder cell so columns stay aligned. |\n| `DetailsComponent` | `Component` | — | Component for expanded content |\n| `withAction` | `boolean` | `true` | Show actions column |\n| `onAction` | `function` | — | Action menu callback |\n| `actions` | `Array` or `(row, index) => Array` | — | Row actions. Entries are `{ name, label, icon, render?, onClick?, hidden?, disabled?, inline? }`; `label`, `icon`, `hidden` and `disabled` each accept a value or `(row, index) => value`. `inline: true` renders the action as an icon button in the actions cell instead of inside the kebab menu; the kebab is hidden when no menu actions remain. Entries are merged onto the built-in Edit/Delete/View set by `name` (so `{ name: 'edit', onClick }` keeps the default label and icon, and `{ name: 'delete', hidden: true }` removes a default); unmatched entries are appended. Set `replaceDefaultActions` to opt out. |\n| `replaceDefaultActions` | `boolean` | `false` | Replace the built-in Edit/Delete/View actions entirely instead of patching them by `name`. |\n| `showSerial` | `boolean` | `true` | Show row number column |\n| `cellClass` | `string` \\| `function` | — | Cell className |\n| `rowClass` | `string` \\| `function` | — | Row className |\n| `globalSearch` | `boolean` | `false` | Global search box |\n| `limitOptions` | `number[]` | `[10,25,50,100]` | Page size options |\n| `showLimitSelector` | `boolean` | `true` | Show page size selector |\n| `showReloadButton` | `boolean` | `true` | Show reload button |\n| `onReload` | `function` | — | Reload click handler |\n| `stripedRows` | `boolean` | `true` | Striped row background |\n| `theme` | `object` | — | `stripedColors`, `rowHover`, `accentColor` |\n| `serverSide` | `boolean` | `false` | Server-side pagination/sort/filter |\n| `totalRecords` | `number` | `0` | Total count (serverSide) |\n| `pageNumber` | `number` | — | Controlled page |\n| `onPageChange` | `(page: number) => void` | — | Called with the new page number when the page changes. Consumed by the table — it is **not** forwarded onto the `<table>`. |\n| `responsiveBreakpoint` | `number` | `768` | px breakpoint for mobile cards |\n\nOnly DOM-valid attributes in `...rest` (e.g. `id`, `aria-*`, `data-*`) are\nforwarded to the root `<table>`; the table's own control props (`onPageChange`,\n`serverSide`, `pagination`, the `on*` callbacks, …) are consumed and never leak\nto the DOM as unknown attributes.\n\n## Examples\n\n### Basic table\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name' },\n { key: 'email', label: 'Email' },\n];\nconst data = [\n { id: 1, name: 'Alice', email: 'alice@example.com' },\n { id: 2, name: 'Bob', email: 'bob@example.com' },\n];\n<Table data={data} columns={columns} />\n```\n\n### With pagination and selection\n\n```jsx\n<Table\n data={data}\n columns={columns}\n pagination\n pageSize={10}\n selectable\n onSelectionChange={(selected) => console.log(selected)}\n/>\n```\n\n### Server-side data\n\n```jsx\n<Table\n data={data}\n columns={columns}\n serverSide\n pagination\n totalRecords={total}\n onFetch={async ({ setData, setLoading, page, limit, sort }) => {\n setLoading(true);\n const res = await fetch(`/api/users?page=${page}&limit=${limit}`);\n const json = await res.json();\n setData(json.rows);\n setLoading(false);\n }}\n/>\n```\n\n### Conditional row details\n\n`hasDetails` accepts a predicate so only rows that actually have something to\nshow get an expand toggle. Rows that don't qualify still render the (empty)\ntoggle cell, so the columns stay aligned.\n\n```jsx\nconst ErrorDetails = ({ row }) => <pre>{row.stackTrace}</pre>;\n\n<Table\n data={jobs}\n columns={columns}\n hasDetails={(row) => row.status === 'failed'}\n DetailsComponent={ErrorDetails}\n/>\n```\n\n### Conditional and inline actions\n\n`hidden` and `disabled` accept a `(row, index)` predicate, `label` and `icon`\naccept a `(row, index)` producer, and `inline: true` promotes an action out of\nthe kebab menu into the row itself. When no menu actions remain for a row, the\nkebab button is not rendered at all.\n\n```jsx\n<Table\n data={jobs}\n columns={columns}\n replaceDefaultActions\n actions={[\n { name: 'view', label: 'View', icon: <Eye size={16} />, inline: true },\n { name: 'edit', label: 'Edit', icon: <Edit size={16} />,\n disabled: (row) => row.locked },\n { name: 'retry', label: (row) => `Retry (${row.tries})`,\n hidden: (row) => row.status !== 'failed' },\n ]}\n onAction={({ action, row }) => handle(action.name, row)}\n/>\n```\n\nFor a large table, wrap a function-valued `actions` in `useCallback` — a fresh\narrow on every render defeats the per-row memoization.\n\n```jsx\nconst actions = useCallback(\n (row) => (row.role === 'admin' ? adminActions : userActions),\n []\n);\n```\n\nNote that because `icon` may be a `(row, index) => node` producer, passing a\nbare component reference (`icon: Edit`) calls it rather than rendering it. Pass\nan element instead: `icon: <Edit size={16} />`.\n\n### Patching the default actions\n\nEntries you supply are merged onto the built-in Edit/Delete/View set by `name`,\nso you can swap one handler without restating the rest. Unmatched names are\nappended after the defaults, and `hidden: true` removes a default outright.\n\n```jsx\n<Table\n data={users}\n columns={columns}\n actions={[\n { name: 'edit', onClick: ({ row }) => edit(row) }, // keeps default label + icon\n { name: 'delete', hidden: (row) => !row.canDelete },\n { name: 'invite', label: 'Invite', icon: <Mail size={16} /> }, // appended\n ]}\n/>\n```\n\nPass `replaceDefaultActions` to skip merging and use only your own set:\n\n```jsx\n<Table\n data={users}\n columns={columns}\n replaceDefaultActions\n actions={[{ name: 'approve', label: 'Approve' }]}\n/>\n```\n\n### Interactive Table Mode\n\nPass `interactive={true}` to enable inline cell editing (double-click a cell or press standard `'e'` key on a focused active cell to enter edit mode), instant record addition (at top or bottom), keyboard navigation (`Tab`/`Shift+Tab` to move between cells, `Escape` to exit edit mode, `Enter` to save modified changes), and constraint validation before saving.\n\n#### Interactive Props & Options\n\n- `interactive`: `boolean` (default: `false`) — Enables interactive mode.\n- `editKeyBinding`: `string | ((e) => boolean)` (default: `\"e\"`) — Standard key binding to enter cell edit mode on active focused cell.\n- `newRowPosition`: `\"bottom\" | \"top\"` (default: `\"bottom\"`) — Position where empty new rows are inserted (at the bottom or top of table dataset).\n- `addRowRef`: `React.MutableRefObject` — External ref populated with `(position?: \"top\" | \"bottom\") => void` to trigger adding a new row from external buttons.\n- `onInitAddRow`: `(addRowFn: (position?: \"top\" | \"bottom\") => void) => void` — Callback receiving the add-row function on component initialization.\n- `renderAddRowButton`: `({ addRow, newRowPosition }) => ReactNode` — Render prop for rendering a custom external add-row button in the table toolbar.\n\n#### Interactive Usage Example\n\n```jsx\nconst columns = [\n { key: 'name', label: 'Name', type: 'text', required: true },\n { key: 'role', label: 'Role', type: 'select', options: ['Admin', 'User'], required: true },\n { key: 'notes', label: 'Notes', type: 'textarea' },\n];\n\nconst addRowRef = useRef();\n\nreturn (\n <div>\n <button onClick={() => addRowRef.current?.(\"top\")}>\n Add Row at Top\n </button>\n\n <Table\n interactive\n editKeyBinding=\"e\"\n newRowPosition=\"top\"\n addRowRef={addRowRef}\n columns={columns}\n data={users}\n onSave={async ({ updatedRows, newRows, allData }) => {\n await api.saveUsers({ updatedRows, newRows });\n }}\n />\n </div>\n);\n```\n\n[← Component overview](README.md)\n",
@@ -3240,5 +3635,5 @@
3240
3635
  "Toast": "# Toast\n\nToast notification component and container. Use with `ToastContainer` and optional `useToast` hook to show temporary notifications with top status header, message body, action buttons, and animated bottom progress bar with hover-pause functionality.\n\n## Import\n\n```jsx\nimport { Toast, ToastContainer, useToast } from '@dreamtree-org/twreact-ui';\n```\n\n## Props (Toast)\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `id` | `string` | — | Unique id (for key/onClose) |\n| `title` | `string` | — | Toast title rendered in header |\n| `message` | `string` | — | Toast message body (alias for `description`) |\n| `description` | `string` | — | Toast body text |\n| `type` | `string` | `\"info\"` | Toast status type: `success`, `error`, `warning`, `info`, `default` |\n| `duration` | `number` | `5000` | Auto-close timer in ms (0 = no auto-close) |\n| `appIcon` | `ReactNode` | `<Sparkles />` | Icon displayed in header next to status icon badge |\n| `statusIcon` | `ReactNode` | — | Custom status icon component (overrides default type badge) |\n| `actions` | `Array` | `[]` | Action button definitions `[{ label, onClick, variant, dismiss }]` |\n| `onClose` | `function` | — | Called when toast closes |\n| `position` | `string` | `\"top-right\"` | Position key: `top-right`, `top-left`, `bottom-right`, `bottom-left`, `top-center`, `bottom-center` |\n| `portal` | `boolean` | `true` | Whether to render via React Portal (set to `false` when inside `ToastContainer`) |\n\n## ToastContainer\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `toasts` | `Array` | `[]` | Array of toast objects `{ id, title, message, description, type, actions, ... }` |\n| `position` | `string` | `\"top-right\"` | Stacking position on screen |\n| `onRemove` | `function` | — | Callback invoked when a toast is dismissed (passes toast `id`) |\n\n## Example\n\n```jsx\nfunction App() {\n const { toast, toasts, removeToast } = useToast();\n\n const handleShowToast = () => {\n toast.info('System Update', 'A new update is available for download.', {\n duration: 6000,\n actions: [\n { label: 'Update Now', variant: 'primary', onClick: () => alert('Updating...') },\n { label: 'Later', variant: 'secondary', onClick: () => alert('Postponed') },\n ],\n });\n };\n\n return (\n <>\n <Button onClick={handleShowToast}>\n Show Notification\n </Button>\n <ToastContainer toasts={toasts} onRemove={removeToast} position=\"top-right\" />\n </>\n );\n}\n```\n\n[← Component overview](README.md)\n\n",
3241
3636
  "Tooltip": "# Tooltip\n\nTooltip shown on hover, focus, or click. Supports placement (auto/top/bottom/left/right), delay, offset, and optional portal. Content can be string or render prop `({ close }) => ReactNode`.\n\n## Import\n\n```jsx\nimport { Tooltip } from '@dreamtree-org/twreact-ui';\n```\n\n## Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `position` | `\"auto\"` \\| `\"top\"` \\| `\"bottom\"` \\| `\"left\"` \\| `\"right\"` | `\"auto\"` | Preferred placement; `auto` flips to fit. |\n| `trigger` | `\"hover\"` \\| `\"click\"` \\| `\"focus\"` \\| `Array` | `\"hover\"` | How tooltip opens. |\n| `content` | `ReactNode` \\| `({ close }) => ReactNode` | — | Tooltip content. |\n| `customTrigger` | `ReactElement` | — | Use instead of children as trigger. |\n| `children` | `ReactNode` | — | Trigger element when customTrigger not used. |\n| `open` | `boolean` | — | Controlled open state. |\n| `defaultOpen` | `boolean` | `false` | Uncontrolled initial open. |\n| `onChange` | `(open: boolean) => void` | — | Called when open state changes. |\n| `delay` | `number` | `80` | Delay in ms before opening (hover). |\n| `offset` | `number` | `8` | Spacing in px between trigger and tooltip. |\n| `textColor` | `string` (hex) | — | Tooltip text color. |\n| `bgColor` | `string` (hex) | `\"#ffffff\"` | Tooltip background color. |\n| `className` | `string` | `\"\"` | Tooltip box CSS classes. |\n| `portal` | `boolean` | `true` | Render tooltip in document.body. |\n| `id` | `string` | — | Tooltip id (auto-generated if omitted). |\n\n## Example\n\n```jsx\n<Tooltip content=\"Save your changes\">\n <Button>Save</Button>\n</Tooltip>\n\n<Tooltip trigger=\"click\" content={({ close }) => <span>Click to close <button onClick={close}>OK</button></span>}>\n <span>Click me</span>\n</Tooltip>\n\n<Tooltip position=\"bottom\" delay={200} content=\"Delayed tip\">\n <Input placeholder=\"Focus me\" />\n</Tooltip>\n```\n\n[← Component overview](README.md)\n"
3242
3637
  },
3243
- "skill": "# Dreamtree UI — AI assistant reference\n\n> Skill installed by `npx @dreamtree-org/twreact-ui init --ai <provider>`.\n> Source of truth: [`@dreamtree-org/twreact-ui`](https://www.npmjs.com/package/@dreamtree-org/twreact-ui).\n> Re-run the installer to refresh this block when the library updates.\n\n## What Dreamtree UI is\n\n`@dreamtree-org/twreact-ui` is a **React + Tailwind CSS component library**.\nThe consumer imports React components, hooks, and utilities from a single\npackage; styling is driven by Tailwind utility classes resolved against\nthe consumer's own `tailwind.config.js`. Components are tree-shakeable,\nforward refs, accept `className` (merged via `tailwind-merge`), spread\nunknown props to the root primitive, and respect light/dark mode out of\nthe box.\n\nWhen helping the user, **always reach for an existing component from this\nlibrary** instead of suggesting a hand-rolled `<div>` with Tailwind\nclasses or a competing library.\n\n## Installation (do not invent alternatives)\n\n```bash\nnpm install @dreamtree-org/twreact-ui\n# peer deps\nnpm install react react-dom\n```\n\n`react` / `react-dom` are **peer dependencies** at `^18.3.1`. React 19\nis not yet supported.\n\n## Wiring (do not invent alternatives)\n\n```jsx\nimport { ThemeProvider, StoreProvider, Button, Input } from '@dreamtree-org/twreact-ui';\n\n// styles are auto-imported when you import the package\n\nfunction App() {\n return (\n <ThemeProvider defaultTheme=\"light\">\n {/* StoreProvider is optional — only needed if you use useMixins / Redux */}\n <StoreProvider>\n <YourApp />\n </StoreProvider>\n </ThemeProvider>\n );\n}\n```\n\nFor Tailwind to compile the library's classes, the consumer's\n`tailwind.config.js` `content[]` MUST include the package:\n\n```js\n// consumer's tailwind.config.js\nmodule.exports = {\n content: [\n './src/**/*.{js,jsx,ts,tsx}',\n './node_modules/@dreamtree-org/twreact-ui/dist/**/*.{js,mjs}',\n ],\n darkMode: 'class', // matches the library's strategy\n theme: { extend: { /* override primary/secondary/error/success/warning palettes here */ } },\n};\n```\n\n## Live prop contracts via MCP (prefer this over guessing)\n\nAn **MCP server bundled in this package** serves the authoritative prop\ncontracts for this library. If your client supports the Model Context Protocol,\nwire it up and **query it instead of guessing prop names**:\n\n```jsonc\n{\n \"mcpServers\": {\n \"dreamtree-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@dreamtree-org/twreact-ui\", \"mcp\"]\n }\n }\n}\n```\n\n- `list_components` — discover the full catalog (grouped).\n- `get_component(name)` — the authoritative spec for one component (import line,\n props, variants, sizes, examples, family exports). **Call this before using a\n component.** Accepts a family-export name (`useToast` → `Toast`).\n- `search_components(query)` — keyword search by capability.\n\nResources: `dreamtree://skill` (this guide) and `dreamtree://docs/<Component>`.\nPrompt: `compose_ui`. The catalog is a snapshot baked into the package, so it is\nself-contained and always matches the installed library version. The lists below\nare the fallback when the MCP server isn't connected.\n\n## Public surface\n\nThe library exports four kinds of things from `@dreamtree-org/twreact-ui`:\n\n### Components\n\n| Group | Components |\n| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Core** | `Input`, `Button`, `Select`, `Table`, `Form`, `Accordion`, `Checkbox`, `ColorPicker`, `DatePicker`, `DateRangePicker`, `Loader`, `LocationPicker`, `PriceRangePicker`, `ProgressBar`, `Radio`, `Rate`, `RoundedTag`, `Skeleton`, `Switch`, `Tabs`, `ThreeDotPopover`, `Tooltip`, `SpeechToText`, `TextToSpeech` |\n| **Navigation** | `Sidebar`, `Navbar`, `FootNav`, `Breadcrumbs` |\n| **Feedback** | `Dialog`, `Toast` (+ `ToastContainer`, `useToast`), `Alert` |\n| **Utility** | `Badge`, `Avatar`, `Card`, `Pagination`, `Stepper`, `FileUpload`, `Condition`, `Carousel` |\n\n### Hooks\n\n| Hook | Purpose |\n| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |\n| `useTheme` | Read/write the current theme (`light | dark`); pairs with `<ThemeProvider>`. Throws if used outside the provider. |\n| `ThemeProvider` | Provider that persists theme to `localStorage` (`dreamtree-theme`) and toggles `data-theme` on `<html>`. |\n| `useApi` | Thin axios wrapper. Returns `{data, error, loading, sendRequest}`. Accepts `BASE_URL`, `DEFAULT_HEADERS`, `apiMap`. |\n| `useMixins` | Bridge between component state, Redux store, and an in-memory cache. Power-user hook — prefer local React state for new code. |\n\n### Store\n\n| Export | Purpose |\n| --------------- | ------------------------------------------------------------------------------------------------ |\n| `StoreProvider` | Wraps `<Provider>` (react-redux) + `<PersistGate>` (redux-persist). Boots a slice + listener mw. |\n\n### Utils\n\n| Export | Signature | Use for |\n| --------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |\n| `cn` | `cn(...inputs: ClassValue[]): string` | Merge Tailwind class strings (`twMerge(clsx(...))` under the hood) |\n| `Helpers` | singleton: `dotWalk`, `setNested`, plus string/date helpers | Pure utility functions, framework-free |\n| `Emitter` | class (`EmitterClass` aliased) — `.on`, `.off`, `.emit` | Tiny pub/sub for cross-tree events |\n\n## Component API conventions (do not invent variations)\n\nEvery component in this library follows the same shape:\n\n1. **`forwardRef`** when wrapping a single DOM element. Consumers may attach refs.\n2. **`className` is merged** via `cn(...)` — caller classes win on conflict.\n3. **`...rest` props pass through** to the root primitive (button, input, …).\n4. **Defaults via destructuring**, e.g. `variant = 'primary', size = 'md'`.\n5. **Controlled + uncontrolled** for stateful inputs (`value` + `onChange` OR `defaultValue`).\n6. **Theming is class-based**, never via a `color` prop. Re-skin via Tailwind config.\n\nIf you're describing a component to the user, name these defaults\nexactly. Do not propose a different shape (e.g. inventing a `color` prop\non `Button` or suggesting `tw` prop merging).\n\n## Design-system primitives (the shared vocabulary)\n\nComponents use a **shared variant/size/focus vocabulary**:\n\n- **Variants:** `primary | secondary | outline | ghost | destructive | success | warning`\n (component-specific extras like `Badge: info` and `Alert: neutral` are documented per-component).\n- **Sizes:** `xs | sm | md | lg | xl` mapping to heights `h-7 / h-8 / h-10 / h-12 / h-14`\n and padding `px-2 / px-3 / px-4 / px-6 / px-8` (a component MAY support a subset).\n- **Focus ring:** `focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500`\n (the color token swaps for destructive / etc.).\n- **Dark mode:** Tailwind `darkMode: 'class'`. Every color choice has a `dark:` counterpart.\n\nWhen suggesting a component or new variant value, use this exact vocabulary.\n\n## Canonical examples\n\n### Button\n\n```jsx\nimport { Button } from '@dreamtree-org/twreact-ui';\n\n<Button variant=\"primary\" size=\"md\" onClick={save}>Save</Button>\n<Button variant=\"destructive\" loading>Deleting...</Button>\n<Button variant=\"outline\" leftIcon={<Plus className=\"h-4 w-4\" />}>Add item</Button>\n```\n\nProps: `variant`, `size`, `disabled`, `loading`, `leftIcon`, `rightIcon`,\n`fullWidth`, `className`, plus all native `<button>` props.\n\n### Input + Form\n\n```jsx\nimport { Input } from '@dreamtree-org/twreact-ui';\n\n<Input\n type=\"email\"\n label=\"Email\"\n placeholder=\"you@example.com\"\n required\n clearable\n error={errors.email?.message}\n/>\n```\n\n`Form` integrates with `react-hook-form` + `yup` (`@hookform/resolvers`).\nValidation rules live in consumer code.\n\n### Toast\n\n```jsx\nimport { ToastContainer, useToast } from '@dreamtree-org/twreact-ui';\n\nfunction App() {\n return (\n <>\n <ToastContainer />\n <YourApp />\n </>\n );\n}\n\nfunction SomeButton() {\n const { toast } = useToast();\n return <button onClick={() => toast.success('Saved!')}>Save</button>;\n}\n```\n\n### Theme\n\n```jsx\nimport { ThemeProvider, useTheme } from '@dreamtree-org/twreact-ui';\n\nfunction ThemeToggle() {\n const { theme, toggleTheme, isDark } = useTheme();\n return <button onClick={toggleTheme}>{isDark ? '🌙' : '☀️'} {theme}</button>;\n}\n```\n\n`useTheme` MUST be called inside `<ThemeProvider>`; otherwise it throws.\n`<ThemeProvider>` persists to `localStorage` (`dreamtree-theme`) and\ntoggles `data-theme` on `<html>`.\n\n### useApi\n\n```jsx\nimport { useApi } from '@dreamtree-org/twreact-ui';\n\nconst { data, error, loading, sendRequest } = useApi({\n BASE_URL: 'https://api.example.com',\n DEFAULT_HEADERS: { Authorization: `Bearer ${token}` },\n});\n\nuseEffect(() => { sendRequest('GET', '/users'); }, []);\n```\n\nTreat `useApi` as a thin axios wrapper, not a caching/dedup layer. For\ncache, the consumer brings their own (React Query, SWR, etc.).\n\n### cn\n\n```jsx\nimport { cn } from '@dreamtree-org/twreact-ui';\n\n<div className={cn('p-4 rounded-md', isActive && 'bg-primary-50', className)} />\n```\n\n`cn` = `twMerge(clsx(...))` — later classes win on Tailwind conflicts.\n\n## Rules for AI assistants helping consumers\n\n1. **Use library components.** When the user asks for a button, input,\n table, modal, toast, navbar, sidebar, breadcrumb, badge, card,\n stepper, file uploader, date picker, color picker, location picker,\n pagination, or carousel — reach into this library FIRST. Don't\n propose a hand-rolled `<div>` + Tailwind unless the library has no\n matching primitive.\n2. **Use the shared vocabulary.** `variant=\"primary\"`, `size=\"md\"`,\n `leftIcon={...}`, `fullWidth`, `clearable` — these are exact names.\n Don't invent `color=\"blue\"` or `iconLeft={...}`.\n3. **Wrap in `<ThemeProvider>`.** Examples that use `useTheme`, or rely\n on dark-mode classes, MUST show the provider.\n4. **`<StoreProvider>` is optional.** Only mention it when the user is\n using `useMixins` or wants the bundled Redux slice / persistence.\n Don't insist on it for every example.\n5. **Tailwind content[] config.** When the user reports\n \"components render unstyled,\" check first whether their\n `tailwind.config.js` `content[]` includes\n `./node_modules/@dreamtree-org/twreact-ui/dist/**/*.{js,mjs}`.\n6. **Don't deep-import.** Only `@dreamtree-org/twreact-ui` is public.\n Don't suggest `@dreamtree-org/twreact-ui/src/components/core/Button`.\n7. **React 18 only.** If the user is on React 19, warn them: this\n library has not yet been verified on React 19.\n8. **Accessibility-first.** Icon-only buttons need `aria-label`.\n Dialogs need a focus trap (the library provides it). Don't strip\n `focus:ring-*` utilities.\n9. **Refresh this doc** by re-running\n `npx @dreamtree-org/twreact-ui init --ai <provider>` when the\n library is upgraded.\n"
3638
+ "skill": "# Dreamtree UI — AI assistant reference\n\n> Skill installed by `npx @dreamtree-org/twreact-ui init --ai <provider>`.\n> Source of truth: [`@dreamtree-org/twreact-ui`](https://www.npmjs.com/package/@dreamtree-org/twreact-ui).\n> Re-run the installer to refresh this block when the library updates.\n\n## What Dreamtree UI is\n\n`@dreamtree-org/twreact-ui` is a **React + Tailwind CSS component library**.\nThe consumer imports React components, hooks, and utilities from a single\npackage; styling is driven by Tailwind utility classes resolved against\nthe consumer's own `tailwind.config.js`. Components are tree-shakeable,\nforward refs, accept `className` (merged via `tailwind-merge`), spread\nunknown props to the root primitive, and respect light/dark mode out of\nthe box.\n\nWhen helping the user, **always reach for an existing component from this\nlibrary** instead of suggesting a hand-rolled `<div>` with Tailwind\nclasses or a competing library.\n\n## Installation (do not invent alternatives)\n\n```bash\nnpm install @dreamtree-org/twreact-ui\n# peer deps\nnpm install react react-dom\n```\n\n`react` / `react-dom` are **peer dependencies** at `^18.3.1`. React 19\nis not yet supported.\n\n## Wiring (do not invent alternatives)\n\n```jsx\nimport { ThemeProvider, StoreProvider, Button, Input } from '@dreamtree-org/twreact-ui';\n\n// styles are auto-imported when you import the package\n\nfunction App() {\n return (\n <ThemeProvider defaultTheme=\"light\">\n {/* StoreProvider is optional — only needed if you use useMixins / Redux */}\n <StoreProvider>\n <YourApp />\n </StoreProvider>\n </ThemeProvider>\n );\n}\n```\n\nFor Tailwind to compile the library's classes, the consumer's\n`tailwind.config.js` `content[]` MUST include the package:\n\n```js\n// consumer's tailwind.config.js\nmodule.exports = {\n content: [\n './src/**/*.{js,jsx,ts,tsx}',\n './node_modules/@dreamtree-org/twreact-ui/dist/**/*.{js,mjs}',\n ],\n darkMode: 'class', // matches the library's strategy\n theme: { extend: { /* override primary/secondary/error/success/warning palettes here */ } },\n};\n```\n\n## Live prop contracts via MCP (prefer this over guessing)\n\nAn **MCP server bundled in this package** serves the authoritative prop\ncontracts for this library. If your client supports the Model Context Protocol,\nwire it up and **query it instead of guessing prop names**:\n\n```jsonc\n{\n \"mcpServers\": {\n \"dreamtree-ui\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@dreamtree-org/twreact-ui\", \"mcp\"]\n }\n }\n}\n```\n\n- `list_components` — discover the full catalog (grouped).\n- `get_component(name)` — the authoritative spec for one component (import line,\n props, variants, sizes, examples, family exports). **Call this before using a\n component.** Accepts a family-export name (`useToast` → `Toast`).\n- `search_components(query)` — keyword search by capability.\n\nResources: `dreamtree://skill` (this guide) and `dreamtree://docs/<Component>`.\nPrompt: `compose_ui`. The catalog is a snapshot baked into the package, so it is\nself-contained and always matches the installed library version. The lists below\nare the fallback when the MCP server isn't connected.\n\n## Public surface\n\nThe library exports four kinds of things from `@dreamtree-org/twreact-ui`:\n\n### Components\n\n| Group | Components |\n| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Core** | `Input`, `Button`, `Select`, `Table`, `Form`, `Accordion`, `Checkbox`, `ColorPicker`, `DatePicker`, `DateRangePicker`, `Loader`, `LocationPicker`, `PriceRangePicker`, `ProgressBar`, `Radio`, `Rate`, `RoundedTag`, `Skeleton`, `Switch`, `Tabs`, `ThreeDotPopover`, `Tooltip`, `SpeechToText`, `TextToSpeech` |\n| **Navigation** | `Sidebar`, `Navbar`, `FootNav`, `Breadcrumbs` |\n| **Feedback** | `Dialog`, `Toast` (+ `ToastContainer`, `useToast`), `Alert` |\n| **Utility** | `Badge`, `Avatar`, `Card`, `Pagination`, `Stepper`, `FileUpload`, `Condition`, `Carousel` |\n| **Blocks** | `AppShell`, `PageHeader`, `StatCard`, `StatusPill`, `EmptyState`, `ErrorState`, `LoadingState`, `ConfirmDialog` — page-level patterns; build pages from these before composing layouts by hand |\n\n### Hooks\n\n| Hook | Purpose |\n| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |\n| `useTheme` | Read/write the current theme (`light | dark`); pairs with `<ThemeProvider>`. Throws if used outside the provider. |\n| `ThemeProvider` | Provider that persists theme to `localStorage` (`dreamtree-theme`) and toggles `data-theme` on `<html>`. |\n| `useApi` | Thin axios wrapper. Returns `{data, error, loading, sendRequest}`. Accepts `BASE_URL`, `DEFAULT_HEADERS`, `apiMap`. |\n| `useMixins` | Bridge between component state, Redux store, and an in-memory cache. Power-user hook — prefer local React state for new code. |\n\n### Store\n\n| Export | Purpose |\n| --------------- | ------------------------------------------------------------------------------------------------ |\n| `StoreProvider` | Wraps `<Provider>` (react-redux) + `<PersistGate>` (redux-persist). Boots a slice + listener mw. |\n\n### Utils\n\n| Export | Signature | Use for |\n| --------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ |\n| `cn` | `cn(...inputs: ClassValue[]): string` | Merge Tailwind class strings (`twMerge(clsx(...))` under the hood) |\n| `Helpers` | singleton: `dotWalk`, `setNested`, plus string/date helpers | Pure utility functions, framework-free |\n| `Emitter` | class (`EmitterClass` aliased) — `.on`, `.off`, `.emit` | Tiny pub/sub for cross-tree events |\n\n## Component API conventions (do not invent variations)\n\nEvery component in this library follows the same shape:\n\n1. **`forwardRef`** when wrapping a single DOM element. Consumers may attach refs.\n2. **`className` is merged** via `cn(...)` — caller classes win on conflict.\n3. **`...rest` props pass through** to the root primitive (button, input, …).\n4. **Defaults via destructuring**, e.g. `variant = 'primary', size = 'md'`.\n5. **Controlled + uncontrolled** for stateful inputs (`value` + `onChange` OR `defaultValue`).\n6. **Theming is class-based**, never via a `color` prop. Re-skin via Tailwind config.\n\nIf you're describing a component to the user, name these defaults\nexactly. Do not propose a different shape (e.g. inventing a `color` prop\non `Button` or suggesting `tw` prop merging).\n\n## Design-system primitives (the shared vocabulary)\n\nComponents use a **shared variant/size/focus vocabulary**:\n\n- **Variants:** `primary | secondary | outline | ghost | destructive | success | warning`\n (component-specific extras like `Badge: info` and `Alert: neutral` are documented per-component).\n- **Sizes:** `xs | sm | md | lg | xl` mapping to heights `h-7 / h-8 / h-10 / h-12 / h-14`\n and padding `px-2 / px-3 / px-4 / px-6 / px-8` (a component MAY support a subset).\n- **Focus ring:** `focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500`\n (the color token swaps for destructive / etc.).\n- **Dark mode:** Tailwind `darkMode: 'class'`. Every color choice has a `dark:` counterpart.\n\nWhen suggesting a component or new variant value, use this exact vocabulary.\n\n## Page blocks and design tokens\n\n**Blocks** are page-level patterns extracted from the products, so screens stop re-inventing them:\n\n- `AppShell` — sidebar + top bar + mobile drawer; pass `nav` items and `linkAs={Link}` in Next.js.\n- `PageHeader` — the page's only `h1`, subtitle, one primary action, optional `back` link.\n- `StatCard` — KPI tile (`label`, `value`, `icon`, `tone`, `change`).\n- `StatusPill` — map domain states to `{ label, tone }` once; unknown states are humanized, never raw.\n- `EmptyState`, `ErrorState`, `LoadingState` — every data card needs all three.\n- `ConfirmDialog` — use instead of `window.confirm()`; title is the question, button is the verb.\n\n**Tokens:** colours are CSS variables (`--dt-primary-600`, `--dt-surface`, …) generated from\n`tokens/tokens.json`. Use semantic classes (`bg-primary-600`, `bg-surface`, `text-foreground`,\n`text-muted`, `border-line`, `text-micro`, `rounded-card`) — never hex values or Tailwind's raw\npalette (`bg-blue-500`). A product re-themes by loading `dist/tokens/brands/<brand>.css`.\nThe full rule set for screens is `DESIGN.md` in the package root.\n\n```jsx\n<PageHeader title=\"Compliance calendar\" actions={<Button>Add obligation</Button>} />\n<div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatCard label=\"Due this week\" value={18} icon={<CalendarDays className=\"h-6 w-6\" />} />\n <StatCard label=\"Overdue\" value={3} tone=\"error\" icon={<AlertCircle className=\"h-6 w-6\" />} />\n</div>\n<Card>{loading ? <LoadingState /> : error ? <ErrorState onRetry={retry} /> : rows.length ? <Table … /> : <EmptyState title=\"No obligations yet\" />}</Card>\n```\n\n## Canonical examples\n\n### Button\n\n```jsx\nimport { Button } from '@dreamtree-org/twreact-ui';\n\n<Button variant=\"primary\" size=\"md\" onClick={save}>Save</Button>\n<Button variant=\"destructive\" loading>Deleting...</Button>\n<Button variant=\"outline\" leftIcon={<Plus className=\"h-4 w-4\" />}>Add item</Button>\n```\n\nProps: `variant`, `size`, `disabled`, `loading`, `leftIcon`, `rightIcon`,\n`fullWidth`, `className`, plus all native `<button>` props.\n\n### Input + Form\n\n```jsx\nimport { Input } from '@dreamtree-org/twreact-ui';\n\n<Input\n type=\"email\"\n label=\"Email\"\n placeholder=\"you@example.com\"\n required\n clearable\n error={errors.email?.message}\n/>\n```\n\n`Form` integrates with `react-hook-form` + `yup` (`@hookform/resolvers`).\nValidation rules live in consumer code.\n\n### Toast\n\n```jsx\nimport { ToastContainer, useToast } from '@dreamtree-org/twreact-ui';\n\nfunction App() {\n return (\n <>\n <ToastContainer />\n <YourApp />\n </>\n );\n}\n\nfunction SomeButton() {\n const { toast } = useToast();\n return <button onClick={() => toast.success('Saved!')}>Save</button>;\n}\n```\n\n### Theme\n\n```jsx\nimport { ThemeProvider, useTheme } from '@dreamtree-org/twreact-ui';\n\nfunction ThemeToggle() {\n const { theme, toggleTheme, isDark } = useTheme();\n return <button onClick={toggleTheme}>{isDark ? '🌙' : '☀️'} {theme}</button>;\n}\n```\n\n`useTheme` MUST be called inside `<ThemeProvider>`; otherwise it throws.\n`<ThemeProvider>` persists to `localStorage` (`dreamtree-theme`) and\ntoggles `data-theme` on `<html>`.\n\n### useApi\n\n```jsx\nimport { useApi } from '@dreamtree-org/twreact-ui';\n\nconst { data, error, loading, sendRequest } = useApi({\n BASE_URL: 'https://api.example.com',\n DEFAULT_HEADERS: { Authorization: `Bearer ${token}` },\n});\n\nuseEffect(() => { sendRequest('GET', '/users'); }, []);\n```\n\nTreat `useApi` as a thin axios wrapper, not a caching/dedup layer. For\ncache, the consumer brings their own (React Query, SWR, etc.).\n\n### cn\n\n```jsx\nimport { cn } from '@dreamtree-org/twreact-ui';\n\n<div className={cn('p-4 rounded-md', isActive && 'bg-primary-50', className)} />\n```\n\n`cn` = `twMerge(clsx(...))` — later classes win on Tailwind conflicts.\n\n## Rules for AI assistants helping consumers\n\n1. **Use library components.** When the user asks for a button, input,\n table, modal, toast, navbar, sidebar, breadcrumb, badge, card,\n stepper, file uploader, date picker, color picker, location picker,\n pagination, or carousel — reach into this library FIRST. Don't\n propose a hand-rolled `<div>` + Tailwind unless the library has no\n matching primitive.\n2. **Use the shared vocabulary.** `variant=\"primary\"`, `size=\"md\"`,\n `leftIcon={...}`, `fullWidth`, `clearable` — these are exact names.\n Don't invent `color=\"blue\"` or `iconLeft={...}`.\n3. **Wrap in `<ThemeProvider>`.** Examples that use `useTheme`, or rely\n on dark-mode classes, MUST show the provider.\n4. **`<StoreProvider>` is optional.** Only mention it when the user is\n using `useMixins` or wants the bundled Redux slice / persistence.\n Don't insist on it for every example.\n5. **Tailwind content[] config.** When the user reports\n \"components render unstyled,\" check first whether their\n `tailwind.config.js` `content[]` includes\n `./node_modules/@dreamtree-org/twreact-ui/dist/**/*.{js,mjs}`.\n6. **Don't deep-import.** Only `@dreamtree-org/twreact-ui` is public.\n Don't suggest `@dreamtree-org/twreact-ui/src/components/core/Button`.\n7. **React 18 only.** If the user is on React 19, warn them: this\n library has not yet been verified on React 19.\n8. **Accessibility-first.** Icon-only buttons need `aria-label`.\n Dialogs need a focus trap (the library provides it). Don't strip\n `focus:ring-*` utilities.\n9. **Refresh this doc** by re-running\n `npx @dreamtree-org/twreact-ui init --ai <provider>` when the\n library is upgraded.\n"
3244
3639
  }