@nadicodeai/ui 0.5.2 → 0.6.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 (92) hide show
  1. package/AGENTS.md +12 -3
  2. package/README.md +18 -1
  3. package/dist/components/accordion.js +1 -1
  4. package/dist/components/alert-dialog.js +5 -5
  5. package/dist/components/alert.js +1 -1
  6. package/dist/components/badge.js +4 -4
  7. package/dist/components/button-group.js +4 -4
  8. package/dist/components/button.d.ts.map +1 -1
  9. package/dist/components/button.js +12 -12
  10. package/dist/components/calendar.js +2 -2
  11. package/dist/components/card.js +3 -3
  12. package/dist/components/chart-breakdown-table.d.ts +39 -0
  13. package/dist/components/chart-breakdown-table.d.ts.map +1 -0
  14. package/dist/components/chart-breakdown-table.js +29 -0
  15. package/dist/components/chart-bullet-bar.d.ts +36 -0
  16. package/dist/components/chart-bullet-bar.d.ts.map +1 -0
  17. package/dist/components/chart-bullet-bar.js +30 -0
  18. package/dist/components/chart-date-range-control.d.ts +12 -0
  19. package/dist/components/chart-date-range-control.d.ts.map +1 -0
  20. package/dist/components/chart-date-range-control.js +59 -0
  21. package/dist/components/chart-date-range.d.ts +38 -0
  22. package/dist/components/chart-date-range.d.ts.map +1 -0
  23. package/dist/components/chart-date-range.js +63 -0
  24. package/dist/components/chart-delta.d.ts +28 -0
  25. package/dist/components/chart-delta.d.ts.map +1 -0
  26. package/dist/components/chart-delta.js +32 -0
  27. package/dist/components/chart-entity-colors.d.ts +37 -0
  28. package/dist/components/chart-entity-colors.d.ts.map +1 -0
  29. package/dist/components/chart-entity-colors.js +68 -0
  30. package/dist/components/chart-kpi-stat.d.ts +33 -0
  31. package/dist/components/chart-kpi-stat.d.ts.map +1 -0
  32. package/dist/components/chart-kpi-stat.js +7 -0
  33. package/dist/components/chart-sparkline.d.ts +45 -0
  34. package/dist/components/chart-sparkline.d.ts.map +1 -0
  35. package/dist/components/chart-sparkline.js +44 -0
  36. package/dist/components/chart-time-series.d.ts +33 -0
  37. package/dist/components/chart-time-series.d.ts.map +1 -0
  38. package/dist/components/chart-time-series.js +136 -0
  39. package/dist/components/chart.js +1 -1
  40. package/dist/components/checkbox.js +1 -1
  41. package/dist/components/combobox.js +5 -5
  42. package/dist/components/command.js +4 -4
  43. package/dist/components/context-menu.js +6 -6
  44. package/dist/components/dialog.d.ts +8 -4
  45. package/dist/components/dialog.d.ts.map +1 -1
  46. package/dist/components/dialog.js +6 -6
  47. package/dist/components/drawer.d.ts.map +1 -1
  48. package/dist/components/drawer.js +4 -4
  49. package/dist/components/dropdown-menu.js +7 -7
  50. package/dist/components/empty.js +2 -2
  51. package/dist/components/field.js +3 -3
  52. package/dist/components/hover-card.js +1 -1
  53. package/dist/components/input-group.js +5 -5
  54. package/dist/components/input-otp.d.ts.map +1 -1
  55. package/dist/components/input-otp.js +8 -3
  56. package/dist/components/input.js +1 -1
  57. package/dist/components/item.d.ts.map +1 -1
  58. package/dist/components/item.js +5 -5
  59. package/dist/components/menubar.js +8 -8
  60. package/dist/components/native-select.js +1 -1
  61. package/dist/components/navigation-menu.js +5 -5
  62. package/dist/components/popover.js +1 -1
  63. package/dist/components/radio-group.js +1 -1
  64. package/dist/components/resizable.js +1 -1
  65. package/dist/components/scroll-area.js +1 -1
  66. package/dist/components/select.js +4 -4
  67. package/dist/components/sheet.d.ts +4 -2
  68. package/dist/components/sheet.d.ts.map +1 -1
  69. package/dist/components/sheet.js +5 -5
  70. package/dist/components/sidebar.d.ts +7 -3
  71. package/dist/components/sidebar.d.ts.map +1 -1
  72. package/dist/components/sidebar.js +16 -16
  73. package/dist/components/slider.js +1 -1
  74. package/dist/components/spinner.js +1 -1
  75. package/dist/components/switch.js +1 -1
  76. package/dist/components/tabs.js +2 -2
  77. package/dist/components/textarea.js +1 -1
  78. package/dist/components/theme-mode-switcher.js +1 -1
  79. package/dist/components/toggle-group.js +2 -2
  80. package/dist/components/toggle.js +5 -5
  81. package/dist/components/tooltip.js +1 -1
  82. package/dist/index.d.ts +9 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +9 -0
  85. package/dist/styles/globals.css +25 -3
  86. package/dist/styles/shadcn.tailwind.css +629 -0
  87. package/docs/agents/nadicodeai-ui.md +24 -1
  88. package/docs/component-classification.md +3 -3
  89. package/docs/product-compositions.md +2 -2
  90. package/docs/product-sections.md +2 -2
  91. package/llms.txt +11 -1
  92. package/package.json +5 -3
package/AGENTS.md CHANGED
@@ -16,8 +16,8 @@ skills.
16
16
 
17
17
  ## Invariants
18
18
 
19
- - **Token-only, no raw literals.** Components style through brand tokens only — the `nadicode.theme.css` role vars bridged into Tailwind utilities (`bg-background`, `text-foreground`, `border-input`, …) plus UI-local product aliases defined in `globals.css` such as `--nc-app-surface`, `--nc-chat-tool-surface`, and `--nc-terminal-bg`. No raw `#hex` / `rgb()` / `hsl()` in any `.tsx`; a new source value goes to `design-system/DESIGN.md` first and flows through generated tokens. Enforced by `tests/no-raw-literal.test.ts` and `tests/token-reference-integrity.test.ts`.
20
- - **Dark-ready roles, no component `dark:` styling.** The generated design-system Tailwind theme owns the standard shadcn `:root` and `.dark` role maps; `globals.css` owns the Tailwind `@theme inline` bridge and UI-local product aliases. Components consume semantic utilities or product aliases such as `var(--nc-app-surface)`; they must not ship `dark:` Tailwind utilities, OS dark-mode branching, or raw dark palette utilities such as `dark:bg-slate-950` / `dark:text-white`. Enforced by `tests/no-dark.test.ts`, `tests/dark-semantic-roles.test.ts`, and `tests/token-reference-integrity.test.ts`.
19
+ - **Token-only, no raw literals.** Components style through brand tokens only — the `nadicode.theme.css` role vars bridged into Tailwind utilities (`bg-background`, `text-foreground`, `border-input`, …) plus UI-local product aliases defined in `globals.css` such as `--nc-app-surface`, `--nc-chat-tool-surface`, and `--nc-terminal-bg`. No raw `#hex` / `rgb()` / `hsl()` in any `.tsx`; a new source value goes to `design-system/DESIGN.md` first and flows through generated tokens. Enforced by `tests/guards/no-raw-literal.test.ts` and `tests/guards/token-reference-integrity.test.ts`.
20
+ - **Dark-ready roles, no component `dark:` styling.** The generated design-system Tailwind theme owns the standard shadcn `:root` and `.dark` role maps; `globals.css` owns the Tailwind `@theme inline` bridge and UI-local product aliases. Components consume semantic utilities or product aliases such as `var(--nc-app-surface)`; they must not ship `dark:` Tailwind utilities, OS dark-mode branching, or raw dark palette utilities such as `dark:bg-slate-950` / `dark:text-white`. Enforced by `tests/guards/no-dark.test.ts`, `tests/guards/dark-semantic-roles.test.ts`, and `tests/guards/token-reference-integrity.test.ts`.
21
21
  - **Components inherit the brand theme.** Consumers import `@nadicodeai/ui/globals.css` (Tailwind + the generated brand `@theme` + semantic role/alias maps) and `@nadicodeai/design-system/css` (kept `nc-*` geometry, type scale, chat CSS components, motion, and display components). Components carry no theme of their own — they resolve every color/radius from bridged brand tokens, UI-local aliases, and design-system CSS.
22
22
  - **Source-authored, built package.** Components are authored as shadcn-style source in this workspace, then emitted to `dist/` for consumption by other apps. Package exports must point at built JS/declarations/CSS, not raw `src/*.tsx`, and `npm pack --dry-run` must include only intentional consumer artifacts.
23
23
  - **Public surface changes require a release decision.** Any change to exported components, `src/index.ts`, `src/ai-elements.ts`, package `exports`, `globals.css`, UI-local semantic aliases, theme behavior, package docs, package skills, or design-system token/CSS/theme output consumed by this package must end with an explicit npm release decision before handoff. If the change should reach external consumers, bump the relevant package version(s), run `npm pack --dry-run -w @nadicodeai/ui`, and follow `../docs/agents/npm-publishing.md`. When the UI change depends on modified `@nadicodeai/design-system` output, bump/publish `@nadicodeai/design-system` first, then update `@nadicodeai/ui`'s dependency and publish UI. Never claim a UI package change is shipped to npm until `npm view @nadicodeai/ui version` reports the intended published version.
@@ -26,7 +26,7 @@ skills.
26
26
  - **Named shadcn blocks stay named.** When a user, plan, or issue selects a shadcn block/example such as `login-02`, `signup-02`, `dashboard-01`, or `sidebar-*`, use that registry item as the source of truth through shadcn CLI/MCP. Do not replace it with a package-authored abstraction unless the plan explicitly records the substitution, the reason, and the true consumer route that proves it.
27
27
  - **Package surface is source-backed.** `src/components/` is intentionally flat shadcn-style source. Do not document folders or package namespaces that do not exist. Do not add top-level exports for recipes, blocks, agent, or terminal groupings unless a later package API decision adds concrete source, built files, docs, and tests in the same change.
28
28
  - **Auth routes are consumer-owned shadcn block work.** Login, signup, OTP, two-factor, magic-link, and provider-button flows start from the selected shadcn block/example in the consuming app, with app-owned Better Auth behavior. Do not ship generic auth, signup, OTP, federated-login, or provider abstractions from this package unless a later explicit boundary decision replaces the selected shadcn block.
29
- - **Standard components, not app-owned product features.** This package ships primitives, standard UI components, AI Elements, brand adapters, React adapters, reusable sections, and presentational product components such as settings, shells, plugin boundaries, installer states, agent chat, artifacts, files, diffs, and redacted terminal summaries. It must not ship app-specific feature/use-case screens or names such as operator-login, portal activation, customer credential control, customer management, or other product-domain use cases. Generic data tables, command palettes, and chart cards are app-owned shadcn registry adaptations, not NadicodeAI package abstractions. Those screens live in the consuming app and are built from `@nadicodeai/ui` components and first-party shadcn blocks/examples.
29
+ - **Standard components, not app-owned product features.** This package ships primitives, standard UI components, AI Elements, brand adapters, React adapters, reusable sections, and presentational product components such as settings, shells, plugin boundaries, installer states, agent chat, artifacts, files, diffs, and redacted terminal summaries. It must not ship app-specific feature/use-case screens or names such as operator-login, portal activation, customer credential control, customer management, or other product-domain use cases. Generic data tables, command palettes, and chart cards are app-owned shadcn registry adaptations, not NadicodeAI package abstractions. Those screens live in the consuming app and are built from `@nadicodeai/ui` components and first-party shadcn blocks/examples. The console chart kit (`chart-kpi-stat`, `chart-sparkline`, `chart-time-series`, `chart-bullet-bar`, `chart-breakdown-table`, `chart-delta`, `chart-entity-colors`, `chart-date-range`, `chart-date-range-control`) is plan-approved package surface per issue #166 and ADR 0010; chart *cards* — the app screens that arrange these widgets — remain app-owned. Every data-shaped chart names a data alternative (a visually hidden `<table>` twin for the time-series charts, a real `<table>` for `chart-breakdown-table`, an sr-only value list for a standalone `chart-sparkline`); `chart-bullet-bar` (data in `aria-valuetext` plus the visible value) and `chart-kpi-stat` (data in visible text) carry no separate table twin by design.
30
30
  - **Design-system geometry remains the foundation.** React components compose the kept `nc-*` page grammar and display CSS components; they do not redefine frame math, marker placement, token values, or raw CSS geometry. Use parent-paints-plus-gap for bordered cell grids and keep every visible seam single-owner.
31
31
  - **Reusable sections and compositions live here after proof.** Bento grids, media mounts, demo surfaces, page frames, section intros, CTA bands, proof slots, benefits grids, generic shells, and homepage/use-case sections belong in `@nadicodeai/ui` when a second concrete consumer, a plan-approved shared component, or a named validation slice proves the package boundary. Website, portal, and sibling Nadia routes should pass data and media children into existing compositions instead of rebuilding layout locally.
32
32
  - **Brand renderers live here for React consumers.** `BrandMark` is the logo mark, and `BrandWordmark` is the text-only wordmark. A full lockup is exactly one `BrandMark` followed by one `BrandWordmark`; compact mark-only usage uses `BrandMark` alone. These adapters are the only place React code imports the raw `@nadicodeai/design-system/assets/*` logo SVG exports. Website, portal, and sibling routes use these adapters instead of importing logo assets directly, drawing brand SVG inline, masking/recoloring assets, or embedding the mark inside the wordmark.
@@ -94,6 +94,15 @@ Use public component subpaths for product UI:
94
94
  - `@nadicodeai/ui/components/agent-tool-approval`
95
95
  - `@nadicodeai/ui/components/artifact-panel`
96
96
  - `@nadicodeai/ui/components/automation-section`
97
+ - `@nadicodeai/ui/components/chart-breakdown-table`
98
+ - `@nadicodeai/ui/components/chart-bullet-bar`
99
+ - `@nadicodeai/ui/components/chart-date-range`
100
+ - `@nadicodeai/ui/components/chart-date-range-control`
101
+ - `@nadicodeai/ui/components/chart-delta`
102
+ - `@nadicodeai/ui/components/chart-entity-colors`
103
+ - `@nadicodeai/ui/components/chart-kpi-stat`
104
+ - `@nadicodeai/ui/components/chart-sparkline`
105
+ - `@nadicodeai/ui/components/chart-time-series`
97
106
  - `@nadicodeai/ui/components/dashboard-shell`
98
107
  - `@nadicodeai/ui/components/desktop-shell`
99
108
  - `@nadicodeai/ui/components/feedback-state`
package/README.md CHANGED
@@ -114,7 +114,15 @@ runtime adapters, analytics, and product-specific copy.
114
114
  Generic product widgets that shadcn already ships as first-party registry
115
115
  source, such as data tables, command palettes, and chart cards, are app-owned
116
116
  shadcn block/example adaptations. Do not add a second package abstraction over
117
- those registry items.
117
+ those registry items. The console chart kit subpaths (`chart-*`) are the
118
+ plan-approved exception (issue #166, ADR 0010): they mechanize the
119
+ design-system dataviz grammar on the Recharts chart foundation, while chart
120
+ cards — the app screens that arrange those widgets — stay app-owned. Every
121
+ data-shaped chart names a data alternative (a visually hidden `<table>` twin for
122
+ the time-series charts, a real `<table>` for `BreakdownTable`, an sr-only value
123
+ list for a standalone `Sparkline`); `BulletBar` and `KpiStat` carry their data
124
+ fully in `aria-valuetext`/visible text and so have no separate table twin by
125
+ design.
118
126
 
119
127
  Current product subpaths:
120
128
 
@@ -125,6 +133,15 @@ Current product subpaths:
125
133
  - `@nadicodeai/ui/components/agent-tool-approval`
126
134
  - `@nadicodeai/ui/components/artifact-panel`
127
135
  - `@nadicodeai/ui/components/automation-section`
136
+ - `@nadicodeai/ui/components/chart-breakdown-table`
137
+ - `@nadicodeai/ui/components/chart-bullet-bar`
138
+ - `@nadicodeai/ui/components/chart-date-range`
139
+ - `@nadicodeai/ui/components/chart-date-range-control`
140
+ - `@nadicodeai/ui/components/chart-delta`
141
+ - `@nadicodeai/ui/components/chart-entity-colors`
142
+ - `@nadicodeai/ui/components/chart-kpi-stat`
143
+ - `@nadicodeai/ui/components/chart-sparkline`
144
+ - `@nadicodeai/ui/components/chart-time-series`
128
145
  - `@nadicodeai/ui/components/dashboard-shell`
129
146
  - `@nadicodeai/ui/components/desktop-shell`
130
147
  - `@nadicodeai/ui/components/feedback-state`
@@ -9,7 +9,7 @@ function AccordionItem({ className, ...props }) {
9
9
  return (_jsx(AccordionPrimitive.Item, { "data-slot": "accordion-item", className: cn("not-last:border-b", className), ...props }));
10
10
  }
11
11
  function AccordionTrigger({ className, children, ...props }) {
12
- return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn("group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground", className), ...props, children: [children, _jsx(ChevronDownIcon, { "data-slot": "accordion-trigger-icon", className: "pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" }), _jsx(ChevronUpIcon, { "data-slot": "accordion-trigger-icon", className: "pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" })] }) }));
12
+ return (_jsx(AccordionPrimitive.Header, { className: "flex", children: _jsxs(AccordionPrimitive.Trigger, { "data-slot": "accordion-trigger", className: cn("group/accordion-trigger relative flex min-h-touch-target flex-1 items-start justify-between rounded-md border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-focus focus-visible:ring-ring focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 [@media(hover:hover)_and_(pointer:fine)]:min-h-0 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground", className), ...props, children: [children, _jsx(ChevronDownIcon, { "data-slot": "accordion-trigger-icon", className: "pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" }), _jsx(ChevronUpIcon, { "data-slot": "accordion-trigger-icon", className: "pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" })] }) }));
13
13
  }
14
14
  function AccordionContent({ className, children, ...props }) {
15
15
  return (_jsx(AccordionPrimitive.Panel, { "data-slot": "accordion-content", className: "overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up", ...props, children: _jsx("div", { className: cn("h-(--accordion-panel-height) pt-0 pb-2.5 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", className), children: children }) }));
@@ -13,16 +13,16 @@ function AlertDialogPortal({ ...props }) {
13
13
  return (_jsx(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props }));
14
14
  }
15
15
  function AlertDialogOverlay({ className, ...props }) {
16
- return (_jsx(AlertDialogPrimitive.Backdrop, { "data-slot": "alert-dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-foreground/50 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
16
+ return (_jsx(AlertDialogPrimitive.Backdrop, { "data-slot": "alert-dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-scrim/scrim duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
17
17
  }
18
18
  function AlertDialogContent({ className, size = "default", ...props }) {
19
- return (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Popup, { "data-slot": "alert-dialog-content", "data-size": size, className: cn("group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props })] }));
19
+ return (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Popup, { "data-slot": "alert-dialog-content", "data-size": size, className: cn("group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-nadicode-md rounded-2xl border border-border bg-popover p-nadicode-lg text-popover-foreground shadow-dialog duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props })] }));
20
20
  }
21
21
  function AlertDialogHeader({ className, ...props }) {
22
22
  return (_jsx("div", { "data-slot": "alert-dialog-header", className: cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className), ...props }));
23
23
  }
24
24
  function AlertDialogFooter({ className, ...props }) {
25
- return (_jsx("div", { "data-slot": "alert-dialog-footer", className: cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className), ...props }));
25
+ return (_jsx("div", { "data-slot": "alert-dialog-footer", className: cn("-mx-nadicode-lg -mb-nadicode-lg flex flex-col-reverse gap-2 p-nadicode-md group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className), ...props }));
26
26
  }
27
27
  function AlertDialogMedia({ className, ...props }) {
28
28
  return (_jsx("div", { "data-slot": "alert-dialog-media", className: cn("mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className), ...props }));
@@ -33,8 +33,8 @@ function AlertDialogTitle({ className, ...props }) {
33
33
  function AlertDialogDescription({ className, ...props }) {
34
34
  return (_jsx(AlertDialogPrimitive.Description, { "data-slot": "alert-dialog-description", className: cn("text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", className), ...props }));
35
35
  }
36
- // Deviates from stock base-nova (bare Button): Action stays a Close so it
37
- // auto-closes on click, preserving the contract portal consumers rely on.
36
+ // An action is also a Base UI Close so the standard alert-dialog contract
37
+ // dismisses after confirmation while preserving the Button API.
38
38
  function AlertDialogAction({ className, variant = "default", size = "default", ...props }) {
39
39
  return (_jsx(AlertDialogPrimitive.Close, { "data-slot": "alert-dialog-action", className: cn(className), render: _jsx(Button, { variant: variant, size: size }), ...props }));
40
40
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cva } from "class-variance-authority";
3
3
  import { cn } from "../lib/utils.js";
4
- const alertVariants = cva("group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", {
4
+ const alertVariants = cva("group/alert relative grid w-full gap-0.5 rounded-md border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", {
5
5
  variants: {
6
6
  variant: {
7
7
  default: "bg-card text-card-foreground",
@@ -2,12 +2,12 @@ import { mergeProps } from "@base-ui/react/merge-props";
2
2
  import { useRender } from "@base-ui/react/use-render";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { cn } from "../lib/utils.js";
5
- const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 [&>svg]:pointer-events-none [&>svg]:size-3!", {
5
+ const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-focus focus-visible:ring-ring has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-focus aria-invalid:ring-destructive [&>svg]:pointer-events-none [&>svg]:size-3!", {
6
6
  variants: {
7
7
  variant: {
8
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
9
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
10
- destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 [a]:hover:bg-destructive/20",
8
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary-hover",
9
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-accent",
10
+ destructive: "bg-destructive/10 text-destructive [a]:hover:bg-destructive/20",
11
11
  outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
12
12
  ghost: "hover:bg-muted hover:text-muted-foreground",
13
13
  link: "text-primary underline-offset-4 hover:underline",
@@ -4,11 +4,11 @@ import { useRender } from "@base-ui/react/use-render";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { cn } from "../lib/utils.js";
6
6
  import { Separator } from "./separator.js";
7
- const buttonGroupVariants = cva("flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", {
7
+ const buttonGroupVariants = cva("flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", {
8
8
  variants: {
9
9
  orientation: {
10
- horizontal: "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
11
- vertical: "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
10
+ horizontal: "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-md! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
11
+ vertical: "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
12
12
  },
13
13
  },
14
14
  defaultVariants: {
@@ -22,7 +22,7 @@ function ButtonGroupText({ className, render, ...props }) {
22
22
  return useRender({
23
23
  defaultTagName: "div",
24
24
  props: mergeProps({
25
- className: cn("flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", className),
25
+ className: cn("flex items-center gap-2 rounded-md border bg-muted px-control-padding-inline text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4", className),
26
26
  }, props),
27
27
  render,
28
28
  state: {
@@ -1 +1 @@
1
- {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,QAAA,MAAM,cAAc;;;8EAmCnB,CAAA;AAED,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,+BAQ7D;AAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,QAAA,MAAM,cAAc;;;8EAoCnB,CAAA;AAED,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,+BAQ7D;AAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
@@ -2,24 +2,24 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Button as ButtonPrimitive } from "@base-ui/react/button";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { cn } from "../lib/utils.js";
5
- const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
5
+ const buttonVariants = cva("group/button inline-flex min-h-touch-target min-w-touch-target shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-focus focus-visible:ring-ring active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-focus aria-invalid:ring-destructive [@media(hover:hover)_and_(pointer:fine)]:min-h-0 [@media(hover:hover)_and_(pointer:fine)]:min-w-0 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
6
6
  variants: {
7
7
  variant: {
8
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
9
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
10
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
8
+ default: "bg-primary text-primary-foreground hover:bg-primary-hover active:bg-primary-active",
9
+ outline: "border-input bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
10
+ secondary: "bg-secondary text-secondary-foreground hover:bg-accent aria-expanded:bg-accent aria-expanded:text-accent-foreground",
11
11
  ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
12
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20",
12
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20",
13
13
  link: "text-primary underline-offset-4 hover:underline",
14
14
  },
15
15
  size: {
16
- default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
17
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
18
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
19
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
20
- icon: "size-8",
21
- "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
22
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
16
+ default: "h-control-height gap-1.5 px-control-padding-inline has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
17
+ xs: "h-6 gap-1 px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
18
+ sm: "h-7 gap-1 px-control-padding-inline text-[0.8rem] in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
19
+ lg: "h-9 gap-1.5 px-control-padding-inline has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
20
+ icon: "size-control-height",
21
+ "icon-xs": "size-6 in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
22
+ "icon-sm": "size-7 in-data-[slot=button-group]:rounded-md",
23
23
  "icon-lg": "size-9",
24
24
  },
25
25
  },
@@ -7,7 +7,7 @@ import { Button, buttonVariants } from "./button.js";
7
7
  import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react";
8
8
  function Calendar({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", locale, formatters, components, ...props }) {
9
9
  const defaultClassNames = getDefaultClassNames();
10
- return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, className: cn("group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, locale: locale, formatters: {
10
+ return (_jsx(DayPicker, { showOutsideDays: showOutsideDays, className: cn("group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:var(--spacing-touch-target)] [@media(hover:hover)_and_(pointer:fine)]:[--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, locale: locale, formatters: {
11
11
  formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: "short" }),
12
12
  ...formatters,
13
13
  }, classNames: {
@@ -71,6 +71,6 @@ function CalendarDayButton({ className, day, modifiers, locale, ...props }) {
71
71
  return (_jsx(Button, { ref: ref, variant: "ghost", size: "icon", "data-day": day.date.toLocaleDateString(locale?.code), "data-selected-single": modifiers.selected &&
72
72
  !modifiers.range_start &&
73
73
  !modifiers.range_end &&
74
- !modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground [&>span]:text-xs [&>span]:opacity-70", defaultClassNames.day, className), ...props }));
74
+ !modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-focus group-data-[focused=true]/day:ring-ring data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground [&>span]:text-xs [&>span]:opacity-70", defaultClassNames.day, className), ...props }));
75
75
  }
76
76
  export { Calendar, CalendarDayButton };
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cn } from "../lib/utils.js";
3
3
  function Card({ className, size = "default", ...props }) {
4
- return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-md border border-border bg-card py-(--card-spacing) text-sm text-card-foreground [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-md *:[img:last-child]:rounded-b-md", className), ...props }));
4
+ return (_jsx("div", { "data-slot": "card", "data-size": size, className: cn("group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-md border border-border bg-card py-(--card-spacing) text-sm text-card-foreground [--card-spacing:var(--spacing-nadicode-lg)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:var(--spacing-nadicode-md)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-md *:[img:last-child]:rounded-b-md", className), ...props }));
5
5
  }
6
6
  function CardHeader({ className, ...props }) {
7
7
  return (_jsx("div", { "data-slot": "card-header", className: cn("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-md px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)", className), ...props }));
8
8
  }
9
9
  function CardTitle({ className, ...props }) {
10
- return (_jsx("div", { "data-slot": "card-title", className: cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className), ...props }));
10
+ return (_jsx("div", { "data-slot": "card-title", className: cn("text-base font-medium group-data-[size=sm]/card:text-sm", className), ...props }));
11
11
  }
12
12
  function CardDescription({ className, ...props }) {
13
13
  return (_jsx("div", { "data-slot": "card-description", className: cn("text-sm text-muted-foreground", className), ...props }));
@@ -19,6 +19,6 @@ function CardContent({ className, ...props }) {
19
19
  return (_jsx("div", { "data-slot": "card-content", className: cn("px-(--card-spacing)", className), ...props }));
20
20
  }
21
21
  function CardFooter({ className, ...props }) {
22
- return (_jsx("div", { "data-slot": "card-footer", className: cn("flex items-center rounded-b-md border-t bg-muted/50 p-(--card-spacing)", className), ...props }));
22
+ return (_jsx("div", { "data-slot": "card-footer", className: cn("flex items-center p-(--card-spacing)", className), ...props }));
23
23
  }
24
24
  export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
@@ -0,0 +1,39 @@
1
+ import * as React from "react";
2
+ import { Table } from "./table";
3
+ import { type EntityColorMap } from "./chart-entity-colors";
4
+ /**
5
+ * chart-breakdown-table — entity breakdown with proportion bars for the
6
+ * console chart kit (issue #166; DESIGN.md "Entity-color assignment",
7
+ * proportion-bar carve-out per ADR 0010 D1).
8
+ *
9
+ * Real `<table>` semantics: each entity row reads name + value + proportion
10
+ * bar + share of total, with the entity-color dot resolved from the shared
11
+ * page-context assignment map. `topN` folds the tail into a single
12
+ * "everything else" rollup row on the neutral register — never an extra hue.
13
+ */
14
+ type BreakdownEntity = {
15
+ /** Stable entity id — the key the entity-color map assigns by. */
16
+ id: string;
17
+ label: React.ReactNode;
18
+ value: number;
19
+ };
20
+ declare function BreakdownTable({ entities, entityColorMap, label, topN, valueFormatter, columnLabels, otherLabel, className, ...props }: Omit<React.ComponentProps<typeof Table>, "children"> & {
21
+ entities: readonly BreakdownEntity[];
22
+ /** The shared page-context entity-color assignment map. */
23
+ entityColorMap: EntityColorMap;
24
+ /** REQUIRED accessible name, rendered as a visually hidden caption. */
25
+ label: string;
26
+ /** Keep the top N entities by value; the rest fold into one rollup row. */
27
+ topN?: number;
28
+ valueFormatter?: (value: number) => string;
29
+ columnLabels?: {
30
+ entity?: string;
31
+ value?: string;
32
+ share?: string;
33
+ };
34
+ /** Label for the neutral rollup row. */
35
+ otherLabel?: React.ReactNode;
36
+ }): React.JSX.Element;
37
+ export { BreakdownTable };
38
+ export type { BreakdownEntity };
39
+ //# sourceMappingURL=chart-breakdown-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-breakdown-table.d.ts","sourceRoot":"","sources":["../../src/components/chart-breakdown-table.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EACL,KAAK,EAON,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAA;AAEzC;;;;;;;;;GASG;AAEH,KAAK,eAAe,GAAG;IACrB,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAoED,iBAAS,cAAc,CAAC,EACtB,QAAQ,EACR,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAA8B,EAC9B,YAAY,EACZ,UAA8B,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACxD,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;IACpC,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAA;IAC9B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,YAAY,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,wCAAwC;IACxC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B,qBAiDA;AAED,OAAO,EAAE,cAAc,EAAE,CAAA;AACzB,YAAY,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, } from "./table.js";
4
+ import { CHART_ENTITY_OTHER_COLOR, entityColor, } from "./chart-entity-colors.js";
5
+ function percentOf(value, total) {
6
+ return total > 0 ? (value / total) * 100 : 0;
7
+ }
8
+ function formatShare(share) {
9
+ return `${share.toFixed(1)}%`;
10
+ }
11
+ function defaultFormat(value) {
12
+ return value.toLocaleString("en-US");
13
+ }
14
+ function BreakdownRow({ label, value, share, color, valueFormatter, ...props }) {
15
+ return (_jsxs(TableRow, { ...props, children: [_jsx(TableCell, { className: "font-medium", children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { "data-slot": "breakdown-table-dot", "aria-hidden": "true", className: "size-2 shrink-0 rounded-[2px]", style: { backgroundColor: color } }), label] }) }), _jsx(TableCell, { className: "text-right font-mono tabular-nums", children: valueFormatter(value) }), _jsx(TableCell, { children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { "data-slot": "breakdown-table-bar", "aria-hidden": "true", className: "h-1.5 flex-1 overflow-hidden rounded-[2px] bg-muted", children: _jsx("span", { className: "block h-full rounded-r-[2px]", style: {
16
+ width: `${Number(share.toFixed(3))}%`,
17
+ backgroundColor: color,
18
+ } }) }), _jsx("span", { className: "w-12 text-right font-mono text-xs text-muted-foreground tabular-nums", children: formatShare(share) })] }) })] }));
19
+ }
20
+ function BreakdownTable({ entities, entityColorMap, label, topN, valueFormatter = defaultFormat, columnLabels, otherLabel = "Everything else", className, ...props }) {
21
+ const total = entities.reduce((sum, entity) => sum + entity.value, 0);
22
+ const sorted = [...entities].sort((a, b) => b.value - a.value);
23
+ const visible = topN !== undefined && topN < sorted.length ? sorted.slice(0, topN) : sorted;
24
+ const foldedValue = sorted
25
+ .slice(visible.length)
26
+ .reduce((sum, entity) => sum + entity.value, 0);
27
+ return (_jsxs(Table, { "data-slot": "breakdown-table", className: cn("text-sm", className), ...props, children: [_jsx(TableCaption, { className: "sr-only", children: label }), _jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: columnLabels?.entity ?? "Entity" }), _jsx(TableHead, { className: "text-right", children: columnLabels?.value ?? "Value" }), _jsx(TableHead, { children: columnLabels?.share ?? "Share of total" })] }) }), _jsxs(TableBody, { children: [visible.map((entity) => (_jsx(BreakdownRow, { label: entity.label, value: entity.value, share: percentOf(entity.value, total), color: entityColor(entityColorMap, entity.id), valueFormatter: valueFormatter }, entity.id))), visible.length < sorted.length ? (_jsx(BreakdownRow, { "data-slot": "breakdown-table-other", label: otherLabel, value: foldedValue, share: percentOf(foldedValue, total), color: CHART_ENTITY_OTHER_COLOR, valueFormatter: valueFormatter })) : null] })] }));
28
+ }
29
+ export { BreakdownTable };
@@ -0,0 +1,36 @@
1
+ import * as React from "react";
2
+ /**
3
+ * chart-bullet-bar — the cap / included-usage meter for the console chart kit
4
+ * (issue #166; DESIGN.md "Meter and bullet grammar").
5
+ *
6
+ * The bar carries a positional threshold tick at the product-configured
7
+ * warning fraction (`thresholdFraction` is a REQUIRED consumer input, never a
8
+ * constant of this component — 0.8 is the common product default) and a cap
9
+ * tick at 100%. Overflow renders as a distinct segment continuing past the
10
+ * cap tick, never clamped away. Severity state tokens reinforce position,
11
+ * never act as the sole signal. The current value reads at the bar tip.
12
+ *
13
+ * Accessibility: `role="meter"` with a REQUIRED accessible name (`label`);
14
+ * `aria-valuenow` clamps to the meter maximum while `aria-valuetext` conveys
15
+ * the true value including overflow.
16
+ */
17
+ type BulletBarSeverity = "ok" | "warn" | "over";
18
+ declare function BulletBar({ value, max, thresholdFraction, label, valueFormatter, valueText, className, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
19
+ value: number;
20
+ /** The cap (included usage / limit) the meter measures against. */
21
+ max: number;
22
+ /**
23
+ * REQUIRED product-configured warning fraction of the cap (0–1) for the
24
+ * positional threshold tick. 0.8 is the common product default, but the
25
+ * fraction is a consumer input, never a constant of this contract.
26
+ */
27
+ thresholdFraction: number;
28
+ /** REQUIRED accessible name for the meter. */
29
+ label: string;
30
+ valueFormatter?: (value: number) => string;
31
+ /** Optional override for the generated `aria-valuetext`. */
32
+ valueText?: string;
33
+ }): React.JSX.Element;
34
+ export { BulletBar };
35
+ export type { BulletBarSeverity };
36
+ //# sourceMappingURL=chart-bullet-bar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-bullet-bar.d.ts","sourceRoot":"","sources":["../../src/components/chart-bullet-bar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B;;;;;;;;;;;;;;GAcG;AAEH,KAAK,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAgB/C,iBAAS,SAAS,CAAC,EACjB,KAAK,EACL,GAAG,EACH,iBAAiB,EACjB,KAAK,EACL,cAA8B,EAC9B,SAAS,EACT,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAA;IACzB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBAsFA;AAED,OAAO,EAAE,SAAS,EAAE,CAAA;AACpB,YAAY,EAAE,iBAAiB,EAAE,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ const BULLET_BAR_VALUE_TONE = {
4
+ ok: "text-foreground",
5
+ warn: "text-(--nc-state-review)",
6
+ over: "text-(--nc-state-blocked)",
7
+ };
8
+ function percent(fraction) {
9
+ return `${Number((fraction * 100).toFixed(3))}%`;
10
+ }
11
+ function defaultFormat(value) {
12
+ return value.toLocaleString("en-US");
13
+ }
14
+ function BulletBar({ value, max, thresholdFraction, label, valueFormatter = defaultFormat, valueText, className, ...props }) {
15
+ const overflow = Math.max(value - max, 0);
16
+ const total = Math.max(value, max);
17
+ const fillFraction = total > 0 ? Math.min(value, max) / total : 0;
18
+ const overflowFraction = total > 0 ? overflow / total : 0;
19
+ const capFraction = total > 0 ? max / total : 1;
20
+ const thresholdTickFraction = capFraction * thresholdFraction;
21
+ const tipFraction = fillFraction + overflowFraction;
22
+ const severity = overflow > 0 ? "over" : value >= thresholdFraction * max ? "warn" : "ok";
23
+ const meterValueText = valueText ??
24
+ `${valueFormatter(value)} of ${valueFormatter(max)}${overflow > 0 ? ` (${valueFormatter(overflow)} over the cap)` : ""}`;
25
+ return (_jsxs("div", { "data-slot": "bullet-bar", "data-severity": severity, className: cn("flex flex-col gap-1", className), ...props, children: [_jsxs("div", { role: "meter", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuenow": Math.min(value, max), "aria-valuetext": meterValueText, className: "relative h-3 w-full", children: [_jsx("div", { "data-slot": "bullet-bar-track", className: "absolute inset-y-0 rounded-r-[4px] bg-(--nc-chart-seq-1)", style: { left: 0, width: percent(capFraction) } }), _jsx("div", { "data-slot": "bullet-bar-fill", className: cn("absolute inset-y-0 bg-(--nc-chart-seq-3)", overflow === 0 && "rounded-r-[4px]"), style: { left: 0, width: percent(fillFraction) } }), overflow > 0 ? (_jsx("div", { "data-slot": "bullet-bar-overflow", className: "absolute inset-y-0 rounded-r-[4px] bg-(--nc-state-blocked)", style: {
26
+ left: percent(fillFraction),
27
+ width: percent(overflowFraction),
28
+ } })) : null, _jsx("div", { "data-slot": "bullet-bar-threshold-tick", "aria-hidden": "true", className: "absolute -inset-y-0.5 w-px bg-foreground", style: { left: percent(thresholdTickFraction) } }), _jsx("div", { "data-slot": "bullet-bar-cap-tick", "aria-hidden": "true", className: "absolute -inset-y-0.5 w-px bg-foreground", style: { left: percent(capFraction) } })] }), _jsx("div", { className: "relative h-4 w-full text-xs", "aria-hidden": "true", children: _jsx("span", { "data-slot": "bullet-bar-value", className: cn("absolute font-mono whitespace-nowrap tabular-nums", tipFraction > 0.6 && "-translate-x-full", BULLET_BAR_VALUE_TONE[severity]), style: { left: percent(tipFraction) }, children: valueFormatter(value) }) })] }));
29
+ }
30
+ export { BulletBar };
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import { type DateRangePreset, type DateRangeValue } from "./chart-date-range";
3
+ declare function DateRangeControl({ value, onChange, label, presetLabels, customLabel, className, ...props }: Omit<React.ComponentProps<"div">, "children" | "onChange"> & {
4
+ value: DateRangeValue;
5
+ onChange: (value: DateRangeValue) => void;
6
+ /** Accessible name for the control group. */
7
+ label?: string;
8
+ presetLabels?: Partial<Record<DateRangePreset, string>>;
9
+ customLabel?: string;
10
+ }): React.JSX.Element;
11
+ export { DateRangeControl };
12
+ //# sourceMappingURL=chart-date-range-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-date-range-control.d.ts","sourceRoot":"","sources":["../../src/components/chart-date-range-control.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAY9B,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAA;AAqBtC,iBAAS,gBAAgB,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,KAAoB,EACpB,YAAY,EACZ,WAAsB,EACtB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG;IAC9D,KAAK,EAAE,cAAc,CAAA;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAA;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAmFA;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { CalendarIcon } from "lucide-react";
5
+ import { cn } from "../lib/utils.js";
6
+ import { Button } from "./button.js";
7
+ import { Calendar } from "./calendar.js";
8
+ import { Popover, PopoverContent, PopoverTrigger, } from "./popover.js";
9
+ import { DATE_RANGE_PRESETS, formatIsoDay, parseIsoDay, } from "./chart-date-range.js";
10
+ /**
11
+ * chart-date-range-control — the console date-range picker for the chart kit
12
+ * (issue #166): relative presets (7d / 14d / 30d / this-cycle) plus a custom
13
+ * calendar range.
14
+ *
15
+ * Fully controlled: the consumer owns the value and typically round-trips it
16
+ * through URL state with `encodeDateRange` / `decodeDateRange` from
17
+ * `chart-date-range` (kept in a separate server-safe module because the
18
+ * package owns no routing). Preset buttons expose their pressed state via
19
+ * `aria-pressed`.
20
+ */
21
+ const DEFAULT_PRESET_LABELS = {
22
+ "7d": "7d",
23
+ "14d": "14d",
24
+ "30d": "30d",
25
+ cycle: "This cycle",
26
+ };
27
+ function DateRangeControl({ value, onChange, label = "Date range", presetLabels, customLabel = "Custom", className, ...props }) {
28
+ const [open, setOpen] = React.useState(false);
29
+ const [pending, setPending] = React.useState(undefined);
30
+ const isCustom = value.kind === "custom";
31
+ const selected = pending ??
32
+ (isCustom
33
+ ? { from: parseIsoDay(value.from), to: parseIsoDay(value.to) }
34
+ : undefined);
35
+ function handleOpenChange(nextOpen) {
36
+ setOpen(nextOpen);
37
+ if (!nextOpen) {
38
+ setPending(undefined);
39
+ }
40
+ }
41
+ function handleSelect(range) {
42
+ if (range?.from && range?.to) {
43
+ setPending(undefined);
44
+ setOpen(false);
45
+ onChange({
46
+ kind: "custom",
47
+ from: formatIsoDay(range.from),
48
+ to: formatIsoDay(range.to),
49
+ });
50
+ return;
51
+ }
52
+ setPending(range);
53
+ }
54
+ return (_jsxs("div", { "data-slot": "date-range-control", role: "group", "aria-label": label, className: cn("flex flex-wrap items-center gap-1", className), ...props, children: [DATE_RANGE_PRESETS.map((preset) => {
55
+ const active = value.kind === "preset" && value.preset === preset;
56
+ return (_jsx(Button, { size: "sm", variant: active ? "secondary" : "ghost", "aria-pressed": active, onClick: () => onChange({ kind: "preset", preset }), children: presetLabels?.[preset] ?? DEFAULT_PRESET_LABELS[preset] }, preset));
57
+ }), _jsxs(Popover, { open: open, onOpenChange: handleOpenChange, children: [_jsx(PopoverTrigger, { render: _jsxs(Button, { size: "sm", variant: isCustom ? "secondary" : "ghost", "aria-pressed": isCustom, children: [_jsx(CalendarIcon, { "data-icon": "inline-start" }), isCustom ? `${value.from} – ${value.to}` : customLabel] }) }), _jsx(PopoverContent, { align: "start", className: "w-auto p-0", children: _jsx(Calendar, { mode: "range", numberOfMonths: 2, defaultMonth: selected?.from, selected: selected, onSelect: handleSelect }) })] })] }));
58
+ }
59
+ export { DateRangeControl };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * chart-date-range — the date-range value model and URL-state codec for the
3
+ * console chart kit (issue #166).
4
+ *
5
+ * This module is intentionally server-safe (no "use client" directive): the
6
+ * package owns no routing, so consumers wire `encodeDateRange` /
7
+ * `decodeDateRange` to their own searchParams — often in a Server Component,
8
+ * where importing a pure function from a client module would break. The
9
+ * interactive picker lives in `chart-date-range-control`.
10
+ */
11
+ declare const DATE_RANGE_PRESETS: readonly ["7d", "14d", "30d", "cycle"];
12
+ type DateRangePreset = (typeof DATE_RANGE_PRESETS)[number];
13
+ type DateRangeValue = {
14
+ kind: "preset";
15
+ preset: DateRangePreset;
16
+ } | {
17
+ kind: "custom";
18
+ from: string;
19
+ to: string;
20
+ };
21
+ /**
22
+ * Encodes a date-range value for URL state: a preset encodes as its token
23
+ * ("7d", "cycle", …) and a custom range as "yyyy-mm-dd..yyyy-mm-dd".
24
+ */
25
+ declare function encodeDateRange(value: DateRangeValue): string;
26
+ /**
27
+ * Decodes a URL-state string back into a date-range value. Returns null for
28
+ * anything malformed (unknown preset, bad date shape, inverted range) so
29
+ * consumers fall back to their own default.
30
+ */
31
+ declare function decodeDateRange(raw: string | null | undefined): DateRangeValue | null;
32
+ /** Parses a "yyyy-mm-dd" day as a LOCAL date (no timezone shifting). */
33
+ declare function parseIsoDay(iso: string): Date;
34
+ /** Formats a local date as "yyyy-mm-dd". */
35
+ declare function formatIsoDay(date: Date): string;
36
+ export { DATE_RANGE_PRESETS, decodeDateRange, encodeDateRange, formatIsoDay, parseIsoDay, };
37
+ export type { DateRangePreset, DateRangeValue };
38
+ //# sourceMappingURL=chart-date-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-date-range.d.ts","sourceRoot":"","sources":["../../src/components/chart-date-range.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,QAAA,MAAM,kBAAkB,wCAAyC,CAAA;AAEjE,KAAK,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,KAAK,cAAc,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAKhD;;;GAGG;AACH,iBAAS,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAItD;AAED;;;;GAIG;AACH,iBAAS,eAAe,CACtB,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,cAAc,GAAG,IAAI,CAwBvB;AAED,wEAAwE;AACxE,iBAAS,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGtC;AAED,4CAA4C;AAC5C,iBAAS,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAKxC;AAMD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,WAAW,GACZ,CAAA;AACD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,CAAA"}