@alpic-ai/ui 1.160.0 → 1.162.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 (58) hide show
  1. package/dist/components/accordion-card.d.mts +1 -5
  2. package/dist/components/accordion-card.mjs +1 -5
  3. package/dist/components/accordion.d.mts +1 -5
  4. package/dist/components/accordion.mjs +1 -5
  5. package/dist/components/attachment-tile.d.mts +0 -5
  6. package/dist/components/chart-legend.d.mts +4 -1
  7. package/dist/components/chart-legend.mjs +21 -7
  8. package/dist/components/chart-primitives.d.mts +3 -8
  9. package/dist/components/chart-primitives.mjs +3 -8
  10. package/dist/components/combobox.d.mts +0 -1
  11. package/dist/components/line-chart.d.mts +3 -1
  12. package/dist/components/line-chart.mjs +7 -4
  13. package/dist/components/select-trigger-variants.d.mts +1 -5
  14. package/dist/components/select-trigger-variants.mjs +1 -5
  15. package/dist/components/table.d.mts +1 -5
  16. package/dist/components/task-progress.d.mts +1 -4
  17. package/dist/lib/chart-palette.d.mts +1 -20
  18. package/dist/lib/chart-palette.mjs +1 -20
  19. package/dist/lib/chart.d.mts +1 -4
  20. package/dist/lib/chart.mjs +2 -11
  21. package/package.json +5 -5
  22. package/src/components/accordion-card.tsx +1 -5
  23. package/src/components/accordion.tsx +1 -5
  24. package/src/components/attachment-tile.tsx +0 -5
  25. package/src/components/avatar.tsx +0 -2
  26. package/src/components/bar-chart.tsx +1 -2
  27. package/src/components/bar-list.tsx +1 -2
  28. package/src/components/chart-legend.tsx +47 -10
  29. package/src/components/chart-primitives.tsx +3 -9
  30. package/src/components/combobox.tsx +1 -29
  31. package/src/components/dropdown-menu.tsx +0 -12
  32. package/src/components/form.tsx +0 -4
  33. package/src/components/heatmap-chart.tsx +4 -10
  34. package/src/components/line-chart.tsx +16 -3
  35. package/src/components/select-trigger-variants.ts +1 -5
  36. package/src/components/select.tsx +0 -10
  37. package/src/components/sidebar.tsx +0 -2
  38. package/src/components/table.tsx +1 -5
  39. package/src/components/tabs.tsx +1 -18
  40. package/src/components/tag.tsx +0 -6
  41. package/src/components/task-progress.tsx +2 -14
  42. package/src/components/wizard.tsx +0 -6
  43. package/src/lib/chart-palette.ts +3 -22
  44. package/src/lib/chart.ts +3 -15
  45. package/src/stories/button.stories.tsx +1 -15
  46. package/src/stories/combobox.stories.tsx +0 -12
  47. package/src/stories/dropdown-menu.stories.tsx +0 -4
  48. package/src/stories/form.stories.tsx +0 -6
  49. package/src/stories/heatmap-chart.stories.tsx +0 -1
  50. package/src/stories/input-group.stories.tsx +0 -4
  51. package/src/stories/input.stories.tsx +0 -8
  52. package/src/stories/line-chart.stories.tsx +120 -80
  53. package/src/stories/sidebar.stories.tsx +1 -4
  54. package/src/stories/skeleton.stories.tsx +0 -2
  55. package/src/stories/table.stories.tsx +0 -5
  56. package/src/stories/tabs.stories.tsx +0 -11
  57. package/src/stories/toggle-group.stories.tsx +0 -5
  58. package/src/stories/wizard.stories.tsx +0 -4
@@ -10,8 +10,6 @@ import { Popover, PopoverContent, PopoverTrigger } from "./popover";
10
10
  import { selectTriggerVariants } from "./select-trigger-variants";
11
11
  import { TagDismissible } from "./tag";
12
12
 
13
- /* ── Context ──────────────────────────────────────────────────────────────── */
14
-
15
13
  interface ComboboxContextValue {
16
14
  multiple: boolean;
17
15
  value: string | null;
@@ -35,8 +33,6 @@ function useComboboxContext() {
35
33
  return context;
36
34
  }
37
35
 
38
- /* ── Root ─────────────────────────────────────────────────────────────────── */
39
-
40
36
  interface ComboboxBaseProps {
41
37
  children: ReactNode;
42
38
  open?: boolean;
@@ -72,12 +68,10 @@ function Combobox(props: ComboboxProps) {
72
68
  getOptionLabel,
73
69
  } = props;
74
70
 
75
- // Single mode state
76
71
  const [uncontrolledSingleValue, setUncontrolledSingleValue] = useState<string | null>(
77
72
  !multiple ? ((props as ComboboxSingleProps).defaultValue ?? null) : null,
78
73
  );
79
74
 
80
- // Multi mode state
81
75
  const [uncontrolledMultiValue, setUncontrolledMultiValue] = useState<string[]>(
82
76
  multiple ? ((props as ComboboxMultipleProps).defaultValue ?? []) : [],
83
77
  );
@@ -97,7 +91,6 @@ function Combobox(props: ComboboxProps) {
97
91
  [isOpenControlled, controlledOnOpenChange],
98
92
  );
99
93
 
100
- // Resolve current values
101
94
  const singleValue = multiple
102
95
  ? null
103
96
  : (((props as ComboboxSingleProps).value !== undefined
@@ -136,7 +129,7 @@ function Combobox(props: ComboboxProps) {
136
129
  setUncontrolledMultiValue(next);
137
130
  }
138
131
  onValueChangeMulti?.(next);
139
- // Stay open in multi mode
132
+ // Intentionally stays open in multi mode so several items can be picked.
140
133
  } else {
141
134
  if (!isControlledSingle) {
142
135
  setUncontrolledSingleValue(itemValue);
@@ -194,8 +187,6 @@ function Combobox(props: ComboboxProps) {
194
187
  );
195
188
  }
196
189
 
197
- /* ── Trigger ──────────────────────────────────────────────────────────────── */
198
-
199
190
  interface ComboboxTriggerProps
200
191
  extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "size">,
201
192
  VariantProps<typeof selectTriggerVariants> {
@@ -232,8 +223,6 @@ function ComboboxTrigger({ className, size, placeholder, children, ...props }: C
232
223
  );
233
224
  }
234
225
 
235
- /* ── Tags (internal, for multi-select trigger) ────────────────────────────── */
236
-
237
226
  function ComboboxTags({ values, onDeselect }: { values: string[]; onDeselect: (value: string) => void }) {
238
227
  const { getOptionLabel } = useComboboxContext();
239
228
  return (
@@ -251,8 +240,6 @@ function ComboboxTags({ values, onDeselect }: { values: string[]; onDeselect: (v
251
240
  );
252
241
  }
253
242
 
254
- /* ── Content ──────────────────────────────────────────────────────────────── */
255
-
256
243
  interface ComboboxContentProps extends React.ComponentProps<typeof PopoverContent> {
257
244
  className?: string;
258
245
  children?: ReactNode;
@@ -280,8 +267,6 @@ function ComboboxContent({ className, children, filter, ...props }: ComboboxCont
280
267
  );
281
268
  }
282
269
 
283
- /* ── Search ───────────────────────────────────────────────────────────────── */
284
-
285
270
  function ComboboxSearch({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
286
271
  return (
287
272
  <div data-slot="combobox-search" className="flex items-center gap-2 border-b border-border-secondary px-3 py-2.5">
@@ -300,8 +285,6 @@ function ComboboxSearch({ className, ...props }: React.ComponentProps<typeof Com
300
285
  );
301
286
  }
302
287
 
303
- /* ── List ─────────────────────────────────────────────────────────────────── */
304
-
305
288
  function ComboboxList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
306
289
  return (
307
290
  <CommandPrimitive.List
@@ -312,8 +295,6 @@ function ComboboxList({ className, ...props }: React.ComponentProps<typeof Comma
312
295
  );
313
296
  }
314
297
 
315
- /* ── Empty ────────────────────────────────────────────────────────────────── */
316
-
317
298
  function ComboboxEmpty({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
318
299
  return (
319
300
  <CommandPrimitive.Empty
@@ -324,8 +305,6 @@ function ComboboxEmpty({ className, ...props }: React.ComponentProps<typeof Comm
324
305
  );
325
306
  }
326
307
 
327
- /* ── Group ────────────────────────────────────────────────────────────────── */
328
-
329
308
  function ComboboxGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
330
309
  return (
331
310
  <CommandPrimitive.Group
@@ -339,8 +318,6 @@ function ComboboxGroup({ className, ...props }: React.ComponentProps<typeof Comm
339
318
  );
340
319
  }
341
320
 
342
- /* ── Separator ────────────────────────────────────────────────────────────── */
343
-
344
321
  function ComboboxSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
345
322
  return (
346
323
  <CommandPrimitive.Separator
@@ -351,10 +328,7 @@ function ComboboxSeparator({ className, ...props }: React.ComponentProps<typeof
351
328
  );
352
329
  }
353
330
 
354
- /* ── Item ─────────────────────────────────────────────────────────────────── */
355
-
356
331
  interface ComboboxItemProps extends Omit<React.ComponentProps<typeof CommandPrimitive.Item>, "onSelect"> {
357
- /** The value stored when this item is selected */
358
332
  itemValue: string;
359
333
  }
360
334
 
@@ -384,8 +358,6 @@ function ComboboxItem({ className, children, itemValue, ...props }: ComboboxItem
384
358
  );
385
359
  }
386
360
 
387
- /* ── Item supporting text ─────────────────────────────────────────────────── */
388
-
389
361
  function ComboboxItemText({ className, children, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
390
362
  return (
391
363
  <span
@@ -51,8 +51,6 @@ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMen
51
51
  return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
52
52
  }
53
53
 
54
- /* ── Item ─────────────────────────────────────────────────────────────────── */
55
-
56
54
  const dropdownMenuItemVariants = cva(
57
55
  [
58
56
  "relative flex cursor-default items-center gap-2 rounded-sm px-2.5 py-2 outline-hidden select-none",
@@ -103,8 +101,6 @@ function DropdownMenuItem({
103
101
  );
104
102
  }
105
103
 
106
- /* ── Label ────────────────────────────────────────────────────────────────── */
107
-
108
104
  function DropdownMenuLabel({
109
105
  className,
110
106
  inset,
@@ -121,8 +117,6 @@ function DropdownMenuLabel({
121
117
  );
122
118
  }
123
119
 
124
- /* ── Header (avatar group) ────────────────────────────────────────────────── */
125
-
126
120
  function DropdownMenuHeader({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
127
121
  return (
128
122
  <div
@@ -135,8 +129,6 @@ function DropdownMenuHeader({ className, children, ...props }: React.HTMLAttribu
135
129
  );
136
130
  }
137
131
 
138
- /* ── Separator ────────────────────────────────────────────────────────────── */
139
-
140
132
  function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
141
133
  return (
142
134
  <DropdownMenuPrimitive.Separator
@@ -147,8 +139,6 @@ function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typ
147
139
  );
148
140
  }
149
141
 
150
- /* ── Shortcut ─────────────────────────────────────────────────────────────── */
151
-
152
142
  function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
153
143
  return (
154
144
  <span
@@ -162,8 +152,6 @@ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"spa
162
152
  );
163
153
  }
164
154
 
165
- /* ── Sub menu ─────────────────────────────────────────────────────────────── */
166
-
167
155
  function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
168
156
  return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
169
157
  }
@@ -166,8 +166,6 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
166
166
  );
167
167
  }
168
168
 
169
- /* ── Layout components ───────────────────────────────────────────────────── */
170
-
171
169
  interface FormHeaderProps extends React.ComponentProps<"div"> {
172
170
  title: string;
173
171
  description?: string;
@@ -186,8 +184,6 @@ function FormFields({ className, ...props }: React.ComponentProps<"div">) {
186
184
  return <div className={cn("flex flex-col gap-6", className)} {...props} />;
187
185
  }
188
186
 
189
- /* ── Convenience field wrappers ─────────────────────────────────────────── */
190
-
191
187
  interface FormFieldBaseProps<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> {
192
188
  control: ControllerProps<TFieldValues, TName>["control"];
193
189
  name: TName;
@@ -129,16 +129,13 @@ function HeatmapChart({
129
129
  const { columns, rows, cells, maxValue, peakKey } = layout;
130
130
  const isEmpty = columns.length === 0 || rows.length === 0 || maxValue <= 0;
131
131
 
132
- // "rank" spreads clustered values across the whole ramp by colouring each cell
133
- // by its position among the distinct non-zero values rather than its raw ratio
134
- // to the max — the right read for skewed activity counts (à la GitHub).
132
+ // "rank" colours each cell by its position among distinct non-zero values, spreading skewed activity counts across the whole ramp la GitHub).
135
133
  const colorFractionOf = React.useMemo(() => {
136
134
  if (colorScale === "rank") {
137
135
  const sorted = [...new Set(cells.map((cell) => cell.value).filter((value) => value > 0))].sort(
138
136
  (lower, upper) => lower - upper,
139
137
  );
140
- // Floor non-zero ranks above the empty colour so the quietest active day
141
- // still reads as coloured, never mistaken for no data.
138
+ // Floor non-zero ranks above the empty colour so the quietest active day is never mistaken for no data.
142
139
  const RANK_FLOOR = 0.18;
143
140
  const fractionByValue = new Map(
144
141
  sorted.map((value, index) => [
@@ -189,9 +186,7 @@ function HeatmapChart({
189
186
  const formatX = (label: string) => (xTickFormatter ? xTickFormatter(label) : label);
190
187
  const formatY = (label: string) => (yTickFormatter ? yTickFormatter(label) : label);
191
188
 
192
- // The tooltip follows the cursor; rendered through a portal so a transformed/
193
- // clipped ancestor (e.g. the card's mount animation + overflow-hidden) can't
194
- // shift or trim it. Flip near the viewport edges.
189
+ // The tooltip renders through a portal so a transformed/clipped ancestor (e.g. the card's mount animation + overflow-hidden) can't shift or trim it.
195
190
  const tooltipLeft =
196
191
  hovered && hovered.x + TOOLTIP_OFFSET + TOOLTIP_WIDTH > window.innerWidth
197
192
  ? hovered.x - TOOLTIP_OFFSET - TOOLTIP_WIDTH
@@ -268,8 +263,7 @@ function HeatmapChart({
268
263
  />
269
264
  );
270
265
  })}
271
- {/* Transparent hit layer over the full pitch so every cell — and the gaps
272
- between dots — registers hover uniformly. */}
266
+ {/* Transparent hit layer over the full pitch so cells and the gaps between dots register hover uniformly. */}
273
267
  {cells.map((cell) => (
274
268
  // biome-ignore lint/a11y/noStaticElementInteractions: decorative hover tooltip; all values are visible on the cells
275
269
  <rect
@@ -48,6 +48,8 @@ export interface LineChartProps {
48
48
  markers?: ChartMarker[];
49
49
  lastValueLabel?: boolean;
50
50
  loading?: boolean;
51
+ hiddenSeries?: ReadonlySet<string>;
52
+ onLegendItemClick?: (key: string) => void;
51
53
  valueFormatter?: (value: number) => string;
52
54
  labelFormatter?: (label: string | number) => string;
53
55
  className?: string;
@@ -69,6 +71,8 @@ function LineChart({
69
71
  markers,
70
72
  lastValueLabel = false,
71
73
  loading = false,
74
+ hiddenSeries,
75
+ onLegendItemClick,
72
76
  valueFormatter = (value) => value.toLocaleString("en-US"),
73
77
  labelFormatter,
74
78
  className,
@@ -76,8 +80,9 @@ function LineChart({
76
80
  const { palette: paletteColors, theme } = useChartContext(palette);
77
81
 
78
82
  const resolved = resolveSeries(series, paletteColors, theme);
83
+ const visible = resolved.filter((entry) => !hiddenSeries?.has(entry.key));
79
84
 
80
- const numericMax = React.useMemo(() => computeNumericMax(data, series, false), [data, series]);
85
+ const numericMax = React.useMemo(() => computeNumericMax(data, visible, false), [data, visible]);
81
86
 
82
87
  const curveType = CURVE_TYPE[curve];
83
88
  const margin = { top: markers?.length ? 18 : 8, right: lastValueLabel ? 56 : 8, bottom: 2, left: 0 };
@@ -129,7 +134,7 @@ function LineChart({
129
134
  />
130
135
  {renderReferenceLine({ referenceLine, numericMax, theme })}
131
136
 
132
- {resolved.map((entry) => (
137
+ {visible.map((entry) => (
133
138
  <Line
134
139
  key={entry.key}
135
140
  type={curveType}
@@ -159,7 +164,15 @@ function LineChart({
159
164
  )}
160
165
  </div>
161
166
 
162
- {legend && !isEmpty && <ChartLegend items={legendItems} align={legendAlign} insetLeft={yAxisWidth} />}
167
+ {legend && !isEmpty && (
168
+ <ChartLegend
169
+ items={legendItems}
170
+ align={legendAlign}
171
+ insetLeft={yAxisWidth}
172
+ hiddenKeys={hiddenSeries}
173
+ onItemClick={onLegendItemClick}
174
+ />
175
+ )}
163
176
  </div>
164
177
  );
165
178
  }
@@ -1,10 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
2
 
3
- /**
4
- * Shared trigger styles for Select and Combobox.
5
- * Both components use the same visual treatment for their trigger buttons.
6
- * Edit here to keep them in sync.
7
- */
3
+ /** Shared by Select and Combobox so their trigger buttons stay visually in sync. */
8
4
  export const selectTriggerVariants = cva(
9
5
  [
10
6
  "flex w-full items-center gap-2 rounded-md border bg-background text-foreground shadow-xs outline-hidden",
@@ -20,8 +20,6 @@ function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.V
20
20
  return <SelectPrimitive.Value data-slot="select-value" {...props} />;
21
21
  }
22
22
 
23
- /* ── Trigger ──────────────────────────────────────────────────────────────── */
24
-
25
23
  function SelectTrigger({
26
24
  className,
27
25
  size = "md",
@@ -47,8 +45,6 @@ function SelectTrigger({
47
45
  );
48
46
  }
49
47
 
50
- /* ── Content ──────────────────────────────────────────────────────────────── */
51
-
52
48
  function SelectContent({
53
49
  className,
54
50
  children,
@@ -89,8 +85,6 @@ function SelectContent({
89
85
  );
90
86
  }
91
87
 
92
- /* ── Label ────────────────────────────────────────────────────────────────── */
93
-
94
88
  function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>) {
95
89
  return (
96
90
  <SelectPrimitive.Label
@@ -101,8 +95,6 @@ function SelectLabel({ className, ...props }: React.ComponentProps<typeof Select
101
95
  );
102
96
  }
103
97
 
104
- /* ── Item ─────────────────────────────────────────────────────────────────── */
105
-
106
98
  function SelectItem({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Item>) {
107
99
  return (
108
100
  <SelectPrimitive.Item
@@ -128,8 +120,6 @@ function SelectItem({ className, children, ...props }: React.ComponentProps<type
128
120
  );
129
121
  }
130
122
 
131
- /* ── Separator ────────────────────────────────────────────────────────────── */
132
-
133
123
  function SelectSeparator({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
134
124
  return (
135
125
  <SelectPrimitive.Separator
@@ -88,7 +88,6 @@ function SidebarProvider({
88
88
  [setOpenProp, open],
89
89
  );
90
90
 
91
- // Helper to toggle the sidebar.
92
91
  const toggleSidebar = React.useCallback(() => {
93
92
  return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
94
93
  }, [isMobile, setOpen]);
@@ -240,7 +239,6 @@ function Sidebar({
240
239
  side === "left"
241
240
  ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
242
241
  : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
243
- // Adjust the padding for floating and inset variants.
244
242
  variant === "floating" || variant === "inset"
245
243
  ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
246
244
  : "border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
@@ -75,11 +75,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
75
75
  }
76
76
 
77
77
  interface TableCellProps extends React.ComponentProps<"td"> {
78
- /**
79
- * When true, the cell renders edge-to-edge so the child can act as the
80
- * interactive surface (e.g. a button or popover trigger filling the cell).
81
- * Defaults to false (standard padded cell).
82
- */
78
+ /** Renders the cell edge-to-edge so the child (e.g. a button or popover trigger) can fill it as the interactive surface. */
83
79
  interactive?: boolean;
84
80
  }
85
81
 
@@ -1,17 +1,6 @@
1
1
  "use client";
2
2
 
3
- /*
4
- * Two tab families, one visual system:
5
- *
6
- * - Tabs / TabsList / TabsTrigger / TabsContent — state-managed.
7
- * Use when tab content lives in the same page (e.g. "Code" vs "Preview").
8
- *
9
- * - TabsNav / TabsNavList / TabsNavTrigger — semantic <nav>/<ul>/<a>, URL-based.
10
- * Use when each tab is a route (e.g. settings pages). Pair with <Link> via asChild.
11
- *
12
- * Both share `tabsTriggerVariants` for visual styles (colors, spacing, active state).
13
- * Layout concerns (flex-1, orientation group selectors) stay component-specific.
14
- */
3
+ /* Two families sharing `tabsTriggerVariants`: Tabs for in-page state-switched content, TabsNav (semantic <nav>/<ul>/<a>) for URL-routed tabs. */
15
4
 
16
5
  import { Slot } from "@radix-ui/react-slot";
17
6
  import * as TabsPrimitive from "@radix-ui/react-tabs";
@@ -21,8 +10,6 @@ import { createContext, use } from "react";
21
10
 
22
11
  import { cn } from "../lib/cn";
23
12
 
24
- /* ─── Shared trigger styles ──────────────────────────────────────────────── */
25
-
26
13
  const tabsTriggerVariants = cva(
27
14
  [
28
15
  "type-text-sm items-center gap-1.5 font-medium",
@@ -57,8 +44,6 @@ const tabsTriggerVariants = cva(
57
44
  },
58
45
  );
59
46
 
60
- /* ─── Tabs (state-based, in-page content switching) ────────────────── */
61
-
62
47
  function Tabs({ className, orientation = "horizontal", ...props }: React.ComponentProps<typeof TabsPrimitive.Root>) {
63
48
  return (
64
49
  <TabsPrimitive.Root
@@ -134,8 +119,6 @@ function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPr
134
119
  return <TabsPrimitive.Content data-slot="tabs-content" className={cn("flex-1 outline-none", className)} {...props} />;
135
120
  }
136
121
 
137
- /* ─── Navigation tabs (semantic <nav>/<ul>/<a>, for URL-based routing) ──── */
138
-
139
122
  type TabsNavVariant = "line" | "pill";
140
123
  type TabsNavOrientation = "horizontal" | "vertical";
141
124
 
@@ -9,8 +9,6 @@ interface TagBaseProps {
9
9
  icon?: React.ReactNode;
10
10
  }
11
11
 
12
- /* ── Tag (text only) ──────────────────────────────────────────── */
13
-
14
12
  interface TagProps extends React.ComponentProps<"span">, TagBaseProps {}
15
13
 
16
14
  function Tag({ className, icon, children, ...props }: TagProps) {
@@ -22,8 +20,6 @@ function Tag({ className, icon, children, ...props }: TagProps) {
22
20
  );
23
21
  }
24
22
 
25
- /* ── TagDismissible (with × button) ───────────────────────────── */
26
-
27
23
  interface TagDismissibleProps extends React.ComponentProps<"span">, TagBaseProps {
28
24
  onDismiss?: () => void;
29
25
  }
@@ -61,8 +57,6 @@ function TagDismissible({ className, icon, children, onDismiss, ...props }: TagD
61
57
  );
62
58
  }
63
59
 
64
- /* ── TagCount (with count badge) ──────────────────────────────── */
65
-
66
60
  interface TagCountProps extends React.ComponentProps<"span">, TagBaseProps {
67
61
  count: number;
68
62
  }
@@ -1,15 +1,6 @@
1
1
  "use client";
2
2
 
3
- /*
4
- * TaskProgress — multi-step async progress primitive.
5
- *
6
- * Dumb component: the consumer owns orchestration (timer, websocket, polling) and
7
- * passes `steps` with each step's status. Renders a percentage bar at the top
8
- * and one row per step with done/running/pending state badges.
9
- *
10
- * `trailingLabel` is for "still working on something not in the step list" —
11
- * shows after the last step as a running row that isn't counted in the percent.
12
- */
3
+ /* Dumb component: the consumer owns orchestration (timer, websocket, polling) and passes each step's status. */
13
4
 
14
5
  import { CheckIcon } from "lucide-react";
15
6
 
@@ -27,10 +18,7 @@ interface TaskProgressStep {
27
18
  interface TaskProgressProps extends Omit<React.ComponentProps<"div">, "children"> {
28
19
  steps: readonly TaskProgressStep[];
29
20
  trailingLabel?: string;
30
- /**
31
- * Optional mount-time stagger: each row fades + slides in with `animation-delay = index × stepRevealDelayMs`.
32
- * Pure CSS, runs once on mount. Omit for no animation.
33
- */
21
+ /** Mount-time stagger: each row fades in after `index × stepRevealDelayMs`; omit for no animation. */
34
22
  stepRevealDelayMs?: number;
35
23
  }
36
24
 
@@ -1,11 +1,5 @@
1
1
  "use client";
2
2
 
3
- /*
4
- * Wizard family — primitives for multi-step flows.
5
- *
6
- * - WizardProgress — step counter + progress bar
7
- */
8
-
9
3
  import type * as React from "react";
10
4
 
11
5
  import { cn } from "../lib/cn";
@@ -1,18 +1,4 @@
1
- /**
2
- * Chart palettes — the locked "paired system" from the analytics-v2 design.
3
- *
4
- * These are charting-API color values (consumed by Recharts at draw time, which
5
- * cannot read CSS custom properties), so per the design-system rules they live
6
- * as documented hex constants rather than `--color-*` tokens. They are
7
- * theme-independent: the same series colors read on light and dark canvases.
8
- * Only the chrome (grid, axis text, tooltip surface, semantic colors) is
9
- * theme-aware — see `useChartTheme`.
10
- *
11
- * Rules baked in:
12
- * - Pink leads (index 0) — never buried, fixing the V1 "pink last" bug.
13
- * - Adjacent dashboard cards alternate lead palette (magenta / cyan) — the
14
- * page decides which to pass; the component just receives one.
15
- */
1
+ // Theme-independent hex constants (Recharts can't read CSS custom properties); pink leads at index 0 and adjacent cards alternate magenta/cyan lead palettes.
16
2
 
17
3
  export const MAGENTA_PALETTE = [
18
4
  "#da1b6a",
@@ -46,8 +32,7 @@ export const CHART_PALETTES: Record<ChartPaletteName, readonly string[]> = {
46
32
  export const heatRampMagenta = (empty: string) => [empty, "#5b0a31", "#a3034a", "#e90060", "#ff7eb6"];
47
33
  export const heatRampMint = (empty: string) => [empty, "#0c4a52", "#17b8cf", "#41ddc9", "#6eece7"];
48
34
 
49
- // Empty heat cell — the only theme-dependent charting value, so it travels as a
50
- // light/dark pair rather than a `--color-*` token (heatmaps draw to SVG fills).
35
+ // The only theme-dependent charting value; a light/dark pair rather than a `--color-*` token because heatmaps draw to SVG fills.
51
36
  export const HEAT_EMPTY = { light: "#eef3f3", dark: "#102222" } as const;
52
37
 
53
38
  const HEAT_RAMPS: Record<ChartPaletteName, (empty: string) => string[]> = {
@@ -99,11 +84,7 @@ export const rampColor = (palette: ChartPaletteName, fraction: number) => {
99
84
  return mixHex(from, to, fraction);
100
85
  };
101
86
 
102
- /**
103
- * Interpolate continuously across a multi-stop heat ramp (e.g. `heatRampMagenta`),
104
- * so a normalized 0..1 value reads as a smooth single-hue gradient rather than
105
- * the five discrete bands.
106
- */
87
+ /** Interpolates continuously across the ramp so a 0..1 value reads as a smooth gradient rather than five discrete bands. */
107
88
  export const heatColor = (stops: readonly string[], fraction: number) => {
108
89
  const clamped = Math.min(1, Math.max(0, fraction));
109
90
  const segment = (stops.length - 1) * clamped;
package/src/lib/chart.ts CHANGED
@@ -2,10 +2,7 @@ import * as React from "react";
2
2
  import type { ChartTheme } from "../hooks/use-chart-theme";
3
3
  import { luminance, paletteColor } from "./chart-palette";
4
4
 
5
- /**
6
- * Declarative description of one plotted series. The same shape drives area,
7
- * line and bar charts — what differs is how each chart renders it.
8
- */
5
+ /** One plotted series; the same shape drives area, line and bar charts. */
9
6
  export interface ChartSeries {
10
7
  key: string;
11
8
  name?: string;
@@ -31,11 +28,7 @@ const semanticColor = (theme: ChartTheme, semantic: NonNullable<ChartSeries["sem
31
28
  return theme.success;
32
29
  };
33
30
 
34
- /**
35
- * Resolve each series to a concrete color and name. Color precedence:
36
- * explicit `color` → `semantic` token → palette slot by declared index (so the
37
- * lead series keeps the lead color even after the bands are reordered).
38
- */
31
+ /** Palette fallback uses the declared index so the lead series keeps the lead color even after reordering. */
39
32
  export const resolveSeries = (
40
33
  series: readonly ChartSeries[],
41
34
  palette: readonly string[],
@@ -47,12 +40,7 @@ export const resolveSeries = (
47
40
  color: entry.color ?? (entry.semantic ? semanticColor(theme, entry.semantic) : paletteColor(palette, index)),
48
41
  }));
49
42
 
50
- /**
51
- * Stacked bands read cleanest as a vertical gradient: darkest at the baseline,
52
- * lightest at the top edge. Sorting by luminance enforces that for any palette
53
- * (this is what fixed the "muddy cyan" stack in the lab). Render order maps to
54
- * stack order in Recharts — first entry sits at the bottom.
55
- */
43
+ /** Stacked bands read cleanest darkest-at-baseline to lightest-on-top; sorting by luminance enforces that for any palette. */
56
44
  export const orderByLuminance = (series: ResolvedSeries[]) =>
57
45
  [...series].sort((lower, upper) => luminance(lower.color) - luminance(upper.color));
58
46