@robr0/design-system 0.8.0 → 0.10.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 (109) hide show
  1. package/README.md +12 -7
  2. package/components/Accordion/Accordion.js +1 -0
  3. package/components/AgentPlan/AgentPlan.css +2 -2
  4. package/components/AgentPlan/AgentPlan.js +1 -0
  5. package/components/AgentStatus/AgentStatus.js +1 -0
  6. package/components/AiButton/AiButton.js +1 -0
  7. package/components/AlertDialog/AlertDialog.js +1 -0
  8. package/components/AnchorNav/AnchorNav.css +63 -0
  9. package/components/AnchorNav/AnchorNav.d.ts +33 -0
  10. package/components/AnchorNav/AnchorNav.js +82 -0
  11. package/components/AppLayout/AppLayout.js +1 -0
  12. package/components/AppSidebar/AppSidebar.css +13 -3
  13. package/components/AppSidebar/AppSidebar.d.ts +6 -2
  14. package/components/AppSidebar/AppSidebar.js +68 -36
  15. package/components/Avatar/Avatar.js +1 -0
  16. package/components/Button/Button.js +1 -0
  17. package/components/Card/Card.js +1 -0
  18. package/components/Carousel/Carousel.js +1 -0
  19. package/components/Chart/AreaChart.js +1 -1
  20. package/components/Chart/BarChart.js +1 -1
  21. package/components/Chart/LineChart.js +1 -1
  22. package/components/Chart/PieChart.js +2 -2
  23. package/components/Chart/RadarChart.js +1 -1
  24. package/components/Chart/RadialChart.js +2 -2
  25. package/components/Chart/StackedBarChart.js +2 -2
  26. package/components/ChatThread/ChatThread.js +1 -0
  27. package/components/Checkbox/Checkbox.js +1 -0
  28. package/components/CodeBlock/CodeBlock.js +1 -0
  29. package/components/CodeDiff/CodeDiff.css +175 -0
  30. package/components/CodeDiff/CodeDiff.d.ts +58 -0
  31. package/components/CodeDiff/CodeDiff.js +114 -0
  32. package/components/ColorPicker/ColorPicker.js +1 -0
  33. package/components/Combobox/Combobox.js +1 -0
  34. package/components/CommandPalette/CommandPalette.js +1 -0
  35. package/components/Composer/Composer.js +1 -0
  36. package/components/ContactCard/ContactCard.js +1 -0
  37. package/components/ContextMenu/ContextMenu.js +1 -0
  38. package/components/DataTable/DataTable.js +1 -0
  39. package/components/DateInput/DateInput.js +1 -0
  40. package/components/DatePicker/DatePicker.js +1 -0
  41. package/components/Dialog/Dialog.js +1 -0
  42. package/components/Drawer/Drawer.js +1 -0
  43. package/components/Dropdown/Dropdown.js +1 -0
  44. package/components/DropdownMenu/DropdownMenu.css +1 -1
  45. package/components/DropdownMenu/DropdownMenu.js +1 -0
  46. package/components/EventCalendar/EventCalendar.js +1 -0
  47. package/components/Field/Field.js +1 -0
  48. package/components/Field/FieldContext.js +1 -0
  49. package/components/FileInput/FileInput.js +1 -0
  50. package/components/Input/Input.js +1 -0
  51. package/components/InterruptCard/InterruptCard.css +11 -21
  52. package/components/InterruptCard/InterruptCard.d.ts +5 -6
  53. package/components/MessageCard/MessageCard.css +1 -2
  54. package/components/ModelPicker/ModelPicker.css +3 -1
  55. package/components/ModelPicker/ModelPicker.js +1 -0
  56. package/components/NavList/NavList.js +1 -0
  57. package/components/NotificationCenter/NotificationCenter.js +1 -0
  58. package/components/NumberInput/NumberInput.css +154 -0
  59. package/components/NumberInput/NumberInput.d.ts +44 -0
  60. package/components/NumberInput/NumberInput.js +147 -0
  61. package/components/Pagination/Pagination.js +1 -0
  62. package/components/PinInput/PinInput.css +92 -0
  63. package/components/PinInput/PinInput.d.ts +46 -0
  64. package/components/PinInput/PinInput.js +161 -0
  65. package/components/Popover/Popover.css +5 -6
  66. package/components/Popover/Popover.js +1 -0
  67. package/components/RadioButton/RadioButton.js +1 -0
  68. package/components/Reasoning/Reasoning.js +1 -0
  69. package/components/SectionTitle/SectionTitle.css +8 -0
  70. package/components/SectionTitle/SectionTitle.d.ts +3 -1
  71. package/components/SectionTitle/SectionTitle.js +6 -1
  72. package/components/SegmentedControl/SegmentedControl.js +1 -0
  73. package/components/SelectionCard/SelectionCard.js +1 -0
  74. package/components/ShaderField/ShaderField.js +1 -0
  75. package/components/ShaderField/useShaderField.js +1 -0
  76. package/components/Slider/Slider.js +1 -0
  77. package/components/Sparkline/Sparkline.css +52 -0
  78. package/components/Sparkline/Sparkline.d.ts +49 -0
  79. package/components/Sparkline/Sparkline.js +86 -0
  80. package/components/Stepper/Stepper.css +207 -0
  81. package/components/Stepper/Stepper.d.ts +41 -0
  82. package/components/Stepper/Stepper.js +61 -0
  83. package/components/Tabs/Tabs.js +1 -0
  84. package/components/TagInput/TagInput.css +160 -0
  85. package/components/TagInput/TagInput.d.ts +43 -0
  86. package/components/TagInput/TagInput.js +137 -0
  87. package/components/Textarea/Textarea.js +1 -0
  88. package/components/TimePicker/TimePicker.css +230 -0
  89. package/components/TimePicker/TimePicker.d.ts +46 -0
  90. package/components/TimePicker/TimePicker.js +246 -0
  91. package/components/Toast/Toast.js +1 -0
  92. package/components/ToggleGroup/ToggleGroup.js +1 -0
  93. package/components/ToggleSwitch/ToggleSwitch.js +1 -0
  94. package/components/ToolCall/ToolCall.css +7 -11
  95. package/components/ToolCall/ToolCall.js +1 -0
  96. package/components/Tooltip/Tooltip.js +1 -0
  97. package/components/TreeView/TreeView.css +119 -0
  98. package/components/TreeView/TreeView.d.ts +45 -0
  99. package/components/TreeView/TreeView.js +232 -0
  100. package/components/registry.d.ts +22 -6
  101. package/components/registry.js +3 -1
  102. package/components/registry.json +206 -20
  103. package/components/registry.json.d.ts +206 -20
  104. package/components/registry.json.js +2 -2
  105. package/index.d.ts +11 -2
  106. package/index.js +21 -1
  107. package/package.json +1 -1
  108. package/tokens/tokens-dark.css +17 -13
  109. package/tokens/tokens-light.css +15 -12
package/README.md CHANGED
@@ -18,7 +18,7 @@ The **live site** is the portfolio built on the design system. **Storybook** is
18
18
  | Part | Description |
19
19
  |---|---|
20
20
  | **Portfolio website** (`/website`) | Next.js app with case studies, work history, writing, and about pages, all built exclusively with the design system components below. **[Live site →](https://robertritacca.com/)** |
21
- | **Design system** (`/src`) | <!-- component-count -->84<!-- /component-count --> React components, a three-tier token architecture, dark mode, a WebGL2 ambient background that themes itself from your tokens, and a full documentation site. Built to production standards. **[Storybook →](https://design-system-iota-one.vercel.app/?path=/docs/robr0-ds--docs)** |
21
+ | **Design system** (`/src`) | <!-- component-count -->101<!-- /component-count --> React components, a three-tier token architecture, dark mode, a WebGL2 ambient background that themes itself from your tokens, and a full documentation site. Built to production standards. **[Storybook →](https://design-system-iota-one.vercel.app/?path=/docs/robr0-ds--docs)** |
22
22
  | **AI layer** (`ai` components in `/src`, chat in `/website`, `/evals`) | A site-wide chat that answers questions about the work: built from the library's own `ai` components, grounded in a corpus generated from the site's published content, and scored by a golden-set eval. |
23
23
 
24
24
  ---
@@ -28,7 +28,7 @@ The **live site** is the portfolio built on the design system. **Storybook** is
28
28
  ### Components
29
29
 
30
30
  <!-- component-list:start -->
31
- Accordion · Agent plan · Agent status · AI button · Alert · Alert dialog · App layout · App sidebar · Avatar · Badge · Breadcrumb · Button · Button group · Card · Carousel · Chart · Chat header · Chat marker · Chat message · Chat thread · Checkbox · Chip · Circular button · Code block · Colour picker · Combobox · Command palette · Composer · Contact card · Context menu · Contribution graph · Data table · Date input · Date picker · Dialog · Divider · Document chip · Drawer · Dropdown · Dropdown menu · Empty state · Entity card · Event calendar · Field · Figure · File input · Input · Instructions · Interrupt card · Kbd · Link list · Message actions · Message card · Model picker · Navigation · Nav list · Notification centre · Pagination · Popover · Progress bar · Prompt suggestions · Prose · Quote · Radio button · Reasoning · Section title · Segmented control · Selection card · Shader field · Skeleton · Slider · Source chip · Spinner · Stat · Swatch · Table · Tabs · Textarea · Timeline · Toast · Toggle group · Toggle switch · Tool call · Tooltip
31
+ Accordion · Agent plan · Agent status · AI button · Alert · Alert dialog · Anchor nav · App layout · App sidebar · Area chart · Avatar · Badge · Bar chart · Breadcrumb · Button · Button group · Card · Carousel · Chat header · Chat marker · Chat message · Chat thread · Checkbox · Chip · Circular button · Code block · Code diff · Colour picker · Combobox · Command palette · Composer · Contact card · Context menu · Contribution graph · Data table · Date input · Date picker · Dialog · Divider · Document chip · Drawer · Dropdown · Dropdown menu · Empty state · Entity card · Event calendar · Field · Figure · File input · Input · Instructions · Interrupt card · Kbd · Line chart · Link list · Message actions · Message card · Model picker · Nav · Nav list · Notification centre · Number input · Pagination · Pie chart · Pin input · Popover · Progress bar · Prompt suggestions · Prose · Quote · Radar chart · Radial chart · Radio button · Reasoning · Scatter chart · Section title · Segmented control · Selection card · Shader field · Skeleton · Slider · Source chip · Sparkline · Spinner · Stacked bar chart · Stat · Stepper · Swatch · Table · Tabs · Tag input · Textarea · Time picker · Timeline · Toast · Toggle group · Toggle switch · Tool call · Tooltip · Tree view · Treemap
32
32
  <!-- component-list:end -->
33
33
 
34
34
  ### Ambient background
@@ -71,7 +71,7 @@ import '@robr0/design-system/tokens/tokens.css';
71
71
  import { Button, Card, Badge } from '@robr0/design-system';
72
72
  ```
73
73
 
74
- Deep imports work too (`@robr0/design-system/components/Button/Button`). Chart components live behind a separate entry so the optional `recharts` peer dependency is only needed if you use them:
74
+ Deep imports work too (`@robr0/design-system/components/Button/Button`). The Recharts-backed charts live behind a separate entry so the optional `recharts` peer dependency is only needed if you use them; Sparkline and ContributionGraph are dependency-free and export from the main barrel:
75
75
 
76
76
  ```tsx
77
77
  import { BarChart, LineChart } from '@robr0/design-system/charts';
@@ -84,10 +84,15 @@ import { BarChart, LineChart } from '@robr0/design-system/charts';
84
84
  ```css
85
85
  :root { --font-family-primary: 'Inter', sans-serif; }
86
86
  ```
87
- - **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it:
87
+ - **Colours, radius, spacing**: every semantic token chains to a primitive, so overriding a primitive re-themes everything built on it. The action colour is theme-split by design (light fills run teal-08/09/10, dark inverts to teal-05/04/03), so rebranding it means re-keying those steps:
88
88
  ```css
89
89
  :root {
90
- --primitive-teal-07: #7C3AED; /* your brand color becomes the action color */
90
+ --primitive-teal-08: #6D31D3; /* light fill */
91
+ --primitive-teal-09: #4C2293; /* light hover */
92
+ --primitive-teal-10: #2E1560; /* light active, dark label */
93
+ --primitive-teal-05: #A78BFA; /* dark fill */
94
+ --primitive-teal-04: #C4B5FD; /* dark hover */
95
+ --primitive-teal-03: #DDD6FE; /* dark active */
91
96
  --primitive-radius-full: 12px; /* pill buttons become rounded rectangles */
92
97
  }
93
98
  ```
@@ -99,9 +104,9 @@ Icons use a bundled Material Symbols Rounded variable font (woff2), with no extr
99
104
  Tokens flow in one direction, and primitives are never referenced directly in components:
100
105
 
101
106
  ```
102
- tokens-primitives.css --primitive-teal-07: #118AB2
107
+ tokens-primitives.css --primitive-teal-08: #0E6E8F
103
108
 
104
- tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-07)
109
+ tokens-light/dark.css --color-action-primary-bg: var(--primitive-teal-08)
105
110
 
106
111
  Component CSS background-color: var(--color-action-primary-bg)
107
112
  ```
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import { useState, useCallback } from "react";
3
4
  import "./Accordion.css";
@@ -29,7 +29,7 @@
29
29
  align-items: center;
30
30
  gap: var(--gap-sm);
31
31
  width: 100%;
32
- padding: var(--padding-xs) var(--padding-sm-md);
32
+ padding: var(--padding-sm) var(--padding-md);
33
33
  background: none;
34
34
  border: none;
35
35
  text-align: left;
@@ -121,7 +121,7 @@
121
121
  display: flex;
122
122
  flex-direction: column;
123
123
  margin: 0;
124
- padding: var(--padding-sm) var(--padding-sm-md);
124
+ padding: var(--padding-sm-md) var(--padding-md);
125
125
  list-style: none;
126
126
  }
127
127
 
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import React, { useId, useState, useCallback } from "react";
3
4
  import { Spinner } from "../Spinner/Spinner.js";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
3
  import React, { useState, useRef, useEffect } from "react";
3
4
  import "./AgentStatus.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./AiButton.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import { useRef, useId, useSyncExternalStore, useCallback, useEffect } from "react";
3
4
  import ReactDOM from "react-dom";
@@ -0,0 +1,63 @@
1
+ /* ============================================
2
+ ANCHOR NAV
3
+ "On this page" list of anchor links with a
4
+ position indicator riding the left rail.
5
+ ============================================ */
6
+ .ds-anchor-nav {
7
+ font-family: var(--font-family-primary);
8
+ }
9
+
10
+ .ds-anchor-nav__header {
11
+ display: flex;
12
+ align-items: center;
13
+ gap: var(--gap-xs);
14
+ margin: 0 0 var(--gap-sm);
15
+ font-size: var(--font-paragraph-em-size);
16
+ font-weight: var(--font-paragraph-em-weight);
17
+ line-height: var(--font-paragraph-em-line-height);
18
+ color: var(--color-text-primary);
19
+ }
20
+
21
+ .ds-anchor-nav__icon {
22
+ --icon-size: var(--icon-size-sm);
23
+ color: var(--color-icon-primary);
24
+ }
25
+
26
+ .ds-anchor-nav__list {
27
+ list-style: none;
28
+ margin: 0;
29
+ padding: 0;
30
+ border-left: var(--border-xs) solid var(--color-divider);
31
+ }
32
+
33
+ .ds-anchor-nav__link {
34
+ display: block;
35
+ padding: var(--padding-xs) 0 var(--padding-xs) var(--padding-md);
36
+ /* Sits over the list rail; turns into the indicator when active */
37
+ margin-left: calc(var(--border-xs) * -1);
38
+ border-left: var(--border-md) solid transparent;
39
+ font-size: var(--font-paragraph-size);
40
+ font-weight: var(--font-paragraph-weight);
41
+ line-height: var(--font-paragraph-line-height);
42
+ color: var(--color-text-tertiary);
43
+ text-decoration: none;
44
+ transition:
45
+ color var(--motion-duration-fast) var(--motion-ease-standard),
46
+ border-color var(--motion-duration-fast) var(--motion-ease-standard);
47
+ }
48
+
49
+ .ds-anchor-nav__link:hover {
50
+ color: var(--color-text-primary);
51
+ }
52
+
53
+ .ds-anchor-nav__link:focus-visible {
54
+ outline: 2px solid var(--color-action-primary-bg);
55
+ outline-offset: -2px;
56
+ border-radius: var(--radius-xxs);
57
+ }
58
+
59
+ .ds-anchor-nav__link--active {
60
+ color: var(--color-text-primary);
61
+ font-weight: var(--font-paragraph-em-weight);
62
+ border-left-color: var(--color-text-primary);
63
+ }
@@ -0,0 +1,33 @@
1
+ import { default as React } from 'react';
2
+ export interface AnchorNavItem {
3
+ /** The id of the element the item jumps to (rendered as `href="#id"`) */
4
+ id: string;
5
+ /** Link text */
6
+ label: string;
7
+ }
8
+ type AnchorNavOwnProps = {
9
+ /** The on-page sections to list, in document order */
10
+ items: AnchorNavItem[];
11
+ /** Header text above the list; pass an empty string to render no header */
12
+ title?: string;
13
+ /** Material Symbols icon name beside the header; pass an empty string for none */
14
+ icon?: string;
15
+ /** Controlled active item id — set it to drive the highlight yourself and skip scroll tracking */
16
+ activeId?: string;
17
+ /** Fires when the tracked (or clicked) active item changes */
18
+ onActiveChange?: (id: string) => void;
19
+ /** Distance in px from the viewport top at which a section counts as current, e.g. a sticky header's height */
20
+ offset?: number;
21
+ };
22
+ export interface AnchorNavProps extends AnchorNavOwnProps, Omit<React.ComponentPropsWithoutRef<'nav'>, keyof AnchorNavOwnProps> {
23
+ }
24
+ /**
25
+ * An "On this page" list of anchor links that tracks the reader's position.
26
+ *
27
+ * Uncontrolled by default: a scroll listener marks the last section whose top
28
+ * has passed `offset` as current. Pass `activeId` to control the highlight
29
+ * instead (no listener is attached). The active link carries
30
+ * `aria-current="location"`.
31
+ */
32
+ export declare const AnchorNav: React.ForwardRefExoticComponent<AnchorNavProps & React.RefAttributes<HTMLElement>>;
33
+ export {};
@@ -0,0 +1,82 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import React, { useRef, useImperativeHandle, useState, useCallback, useEffect } from "react";
4
+ import "./AnchorNav.css";
5
+ const AnchorNav = React.forwardRef(
6
+ ({
7
+ items,
8
+ title = "On this page",
9
+ icon = "toc",
10
+ activeId,
11
+ onActiveChange,
12
+ offset = 96,
13
+ className,
14
+ ...rest
15
+ }, ref) => {
16
+ const navRef = useRef(null);
17
+ useImperativeHandle(ref, () => navRef.current);
18
+ const isControlled = activeId !== void 0;
19
+ const [trackedId, setTrackedId] = useState(void 0);
20
+ const currentId = isControlled ? activeId : trackedId;
21
+ const onActiveChangeRef = useRef(onActiveChange);
22
+ onActiveChangeRef.current = onActiveChange;
23
+ const setActive = useCallback(
24
+ (id) => {
25
+ setTrackedId((prev) => {
26
+ if (id !== void 0 && id !== prev) onActiveChangeRef.current?.(id);
27
+ return id;
28
+ });
29
+ },
30
+ []
31
+ );
32
+ useEffect(() => {
33
+ if (isControlled || items.length === 0) return;
34
+ let frame = 0;
35
+ const measure = () => {
36
+ frame = 0;
37
+ let current = items[0]?.id;
38
+ for (const item of items) {
39
+ const el = document.getElementById(item.id);
40
+ if (!el) continue;
41
+ if (el.getBoundingClientRect().top <= offset + 1) current = item.id;
42
+ }
43
+ setActive(current);
44
+ };
45
+ const schedule = () => {
46
+ if (frame === 0) frame = window.requestAnimationFrame(measure);
47
+ };
48
+ measure();
49
+ window.addEventListener("scroll", schedule, { passive: true });
50
+ window.addEventListener("resize", schedule);
51
+ return () => {
52
+ window.removeEventListener("scroll", schedule);
53
+ window.removeEventListener("resize", schedule);
54
+ if (frame !== 0) window.cancelAnimationFrame(frame);
55
+ };
56
+ }, [isControlled, items, offset, setActive]);
57
+ const classes = ["ds-anchor-nav", className].filter(Boolean).join(" ");
58
+ return /* @__PURE__ */ jsxs("nav", { ref: navRef, "aria-label": title || "On this page", className: classes, ...rest, children: [
59
+ title && /* @__PURE__ */ jsxs("p", { className: "ds-anchor-nav__header", children: [
60
+ icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded ds-anchor-nav__icon", "aria-hidden": "true", children: icon }),
61
+ title
62
+ ] }),
63
+ /* @__PURE__ */ jsx("ul", { className: "ds-anchor-nav__list", children: items.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
64
+ "a",
65
+ {
66
+ href: `#${item.id}`,
67
+ className: item.id === currentId ? "ds-anchor-nav__link ds-anchor-nav__link--active" : "ds-anchor-nav__link",
68
+ "aria-current": item.id === currentId ? "location" : void 0,
69
+ onClick: () => {
70
+ if (!isControlled) setActive(item.id);
71
+ else onActiveChangeRef.current?.(item.id);
72
+ },
73
+ children: item.label
74
+ }
75
+ ) }, item.id)) })
76
+ ] });
77
+ }
78
+ );
79
+ AnchorNav.displayName = "AnchorNav";
80
+ export {
81
+ AnchorNav
82
+ };
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import { useState, useCallback } from "react";
3
4
  import { AppSidebar } from "../AppSidebar/AppSidebar.js";
@@ -166,12 +166,17 @@
166
166
  cursor: pointer;
167
167
  overflow: hidden;
168
168
  text-align: left;
169
- transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), padding var(--motion-duration-slow) var(--motion-ease-emphasized);
169
+ /* The row renders as an <a> when the item carries an href. */
170
+ text-decoration: none;
171
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard), padding var(--motion-duration-slow) var(--motion-ease-emphasized), margin var(--motion-duration-slow) var(--motion-ease-emphasized);
170
172
  }
171
173
 
174
+ /* Inset via margin + reduced padding (12 + 8 = the 20px the label sat at
175
+ before), so the pill floats off the rail edges while the icon and label
176
+ keep their exact x-positions against the category labels above. */
172
177
  .ds-app-sidebar--expanded .ds-app-sidebar__btn {
173
- width: 100%;
174
- padding: var(--padding-sm) var(--padding-lg);
178
+ margin: 0 var(--padding-sm-md);
179
+ padding: var(--padding-sm);
175
180
  justify-content: space-between;
176
181
  }
177
182
 
@@ -282,6 +287,9 @@
282
287
  align-items: center;
283
288
  gap: 8px;
284
289
  height: 40px;
290
+ /* The same edge inset the parent pills get; sub-items only render while
291
+ the rail is expanded, so this needs no state selector. */
292
+ margin-right: var(--padding-sm-md);
285
293
  padding: var(--padding-sm);
286
294
  border-radius: var(--radius-full);
287
295
  background: transparent;
@@ -292,6 +300,8 @@
292
300
  text-align: left;
293
301
  overflow: hidden;
294
302
  white-space: nowrap;
303
+ /* The row renders as an <a> when the sub-item carries an href. */
304
+ text-decoration: none;
295
305
  transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
296
306
  }
297
307
 
@@ -4,8 +4,10 @@ export interface AppSidebarSubItem {
4
4
  key: string;
5
5
  /** Display label */
6
6
  label: string;
7
- /** Click handler */
7
+ /** Click handler — also fires on a link sub-item, so a consumer can route client-side */
8
8
  onClick?: () => void;
9
+ /** Renders the sub-item as a real link to this URL instead of a button */
10
+ href?: string;
9
11
  }
10
12
  export interface AppSidebarItem {
11
13
  /** Unique key for this item */
@@ -14,8 +16,10 @@ export interface AppSidebarItem {
14
16
  icon: string;
15
17
  /** Display label (shown when expanded) */
16
18
  label: string;
17
- /** Click handler */
19
+ /** Click handler — also fires on a link item, so a consumer can route client-side */
18
20
  onClick?: () => void;
21
+ /** Renders the item as a real link to this URL instead of a button. Ignored when the item has children, whose row is the accordion toggle */
22
+ href?: string;
19
23
  /** Sub-items — turns this into an accordion */
20
24
  children?: AppSidebarSubItem[];
21
25
  }
@@ -1,4 +1,5 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ "use client";
2
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
3
  import React, { useState, useCallback } from "react";
3
4
  import "./AppSidebar.css";
4
5
  import "../../fonts/material-symbols.css";
@@ -186,14 +187,43 @@ const AppSidebar = ({
186
187
  const hasChildren = item.children && item.children.length > 0;
187
188
  const isOpen = openKeys.has(item.key);
188
189
  const isActive = activeKey === item.key;
189
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
190
- /* @__PURE__ */ jsxs(
191
- "button",
190
+ const isLink = Boolean(item.href) && !hasChildren;
191
+ const itemClasses = [
192
+ `${baseClass}__btn`,
193
+ isActive ? `${baseClass}__btn--active` : ""
194
+ ].filter(Boolean).join(" ");
195
+ const itemContent = /* @__PURE__ */ jsxs(Fragment, { children: [
196
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
197
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
198
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
199
+ ] }),
200
+ hasChildren && /* @__PURE__ */ jsx(
201
+ "span",
192
202
  {
193
203
  className: [
194
- `${baseClass}__btn`,
195
- isActive ? `${baseClass}__btn--active` : ""
204
+ `${baseClass}__btn-chevron material-symbols-rounded`,
205
+ isOpen ? `${baseClass}__btn-chevron--open` : ""
196
206
  ].filter(Boolean).join(" "),
207
+ children: "chevron_right"
208
+ }
209
+ )
210
+ ] });
211
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
212
+ isLink ? /* @__PURE__ */ jsx(
213
+ "a",
214
+ {
215
+ className: itemClasses,
216
+ href: item.href,
217
+ onClick: () => item.onClick?.(),
218
+ "aria-current": isActive ? "page" : void 0,
219
+ title: !isExpanded ? item.label : void 0,
220
+ children: itemContent
221
+ }
222
+ ) : /* @__PURE__ */ jsx(
223
+ "button",
224
+ {
225
+ type: "button",
226
+ className: itemClasses,
197
227
  onClick: () => {
198
228
  if (hasChildren && isExpanded) {
199
229
  toggleAccordion(item.key);
@@ -201,23 +231,9 @@ const AppSidebar = ({
201
231
  item.onClick?.();
202
232
  },
203
233
  "aria-expanded": hasChildren ? isOpen : void 0,
234
+ "aria-current": isActive ? "page" : void 0,
204
235
  title: !isExpanded ? item.label : void 0,
205
- children: [
206
- /* @__PURE__ */ jsxs("span", { className: `${baseClass}__btn-left`, children: [
207
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-icon material-symbols-rounded`, children: item.icon }),
208
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__btn-label`, children: item.label })
209
- ] }),
210
- hasChildren && /* @__PURE__ */ jsx(
211
- "span",
212
- {
213
- className: [
214
- `${baseClass}__btn-chevron material-symbols-rounded`,
215
- isOpen ? `${baseClass}__btn-chevron--open` : ""
216
- ].filter(Boolean).join(" "),
217
- children: "chevron_right"
218
- }
219
- )
220
- ]
236
+ children: itemContent
221
237
  }
222
238
  ),
223
239
  hasChildren && /* @__PURE__ */ jsx(
@@ -227,20 +243,36 @@ const AppSidebar = ({
227
243
  `${baseClass}__sub-items`,
228
244
  isOpen && isExpanded ? `${baseClass}__sub-items--open` : ""
229
245
  ].filter(Boolean).join(" "),
230
- children: item.children.map((sub) => /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
231
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
232
- /* @__PURE__ */ jsx(
233
- "button",
234
- {
235
- className: [
236
- `${baseClass}__sub-btn`,
237
- activeSubKey === sub.key ? `${baseClass}__sub-btn--active` : ""
238
- ].filter(Boolean).join(" "),
239
- onClick: sub.onClick,
240
- children: /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label })
241
- }
242
- )
243
- ] }, sub.key))
246
+ children: item.children.map((sub) => {
247
+ const subActive = activeSubKey === sub.key;
248
+ const subClasses = [
249
+ `${baseClass}__sub-btn`,
250
+ subActive ? `${baseClass}__sub-btn--active` : ""
251
+ ].filter(Boolean).join(" ");
252
+ const subLabel = /* @__PURE__ */ jsx("span", { className: `${baseClass}__sub-label`, children: sub.label });
253
+ return /* @__PURE__ */ jsxs("div", { className: `${baseClass}__sub-item`, children: [
254
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__sub-line` }),
255
+ sub.href ? /* @__PURE__ */ jsx(
256
+ "a",
257
+ {
258
+ className: subClasses,
259
+ href: sub.href,
260
+ onClick: sub.onClick,
261
+ "aria-current": subActive ? "page" : void 0,
262
+ children: subLabel
263
+ }
264
+ ) : /* @__PURE__ */ jsx(
265
+ "button",
266
+ {
267
+ type: "button",
268
+ className: subClasses,
269
+ onClick: sub.onClick,
270
+ "aria-current": subActive ? "page" : void 0,
271
+ children: subLabel
272
+ }
273
+ )
274
+ ] }, sub.key);
275
+ })
244
276
  }
245
277
  )
246
278
  ] }, item.key);
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import { useState } from "react";
3
4
  import "./Avatar.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import { Spinner } from "../Spinner/Spinner.js";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./Card.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
3
  import React, { useState, useRef, useCallback, useEffect } from "react";
3
4
  import "./Carousel.css";
@@ -32,7 +32,7 @@ const AreaChart = ({
32
32
  const classes = [baseClass, className].filter(Boolean).join(" ");
33
33
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
34
34
  const gridColor = getCSSVar("--color-divider", "#232323");
35
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
35
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
36
36
  const renderTooltip = useCallback(
37
37
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
38
38
  (props) => /* @__PURE__ */ jsx(AreaTooltip, { ...props }),
@@ -37,7 +37,7 @@ const BarChart = ({
37
37
  }) => {
38
38
  const baseClass = "ds-chart";
39
39
  const classes = [baseClass, className].filter(Boolean).join(" ");
40
- const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#118AB2");
40
+ const resolvedBarColor = barColor || getCSSVar("--color-action-primary-bg", "#0E6E8F");
41
41
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
42
42
  const gridColor = getCSSVar("--color-divider", "#232323");
43
43
  const cursorColor = getCSSVar("--color-bg-container-secondary", "#303030");
@@ -31,7 +31,7 @@ const LineChart = ({
31
31
  const classes = [baseClass, className].filter(Boolean).join(" ");
32
32
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
33
33
  const gridColor = getCSSVar("--color-divider", "#232323");
34
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
34
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
35
35
  const renderTooltip = useCallback(
36
36
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
37
37
  (props) => /* @__PURE__ */ jsx(LineChartTooltip, { ...props }),
@@ -4,11 +4,11 @@ import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip, Legend
4
4
  import "./Chart.css";
5
5
  function getDefaultColors() {
6
6
  if (typeof window === "undefined") {
7
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
7
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
8
8
  }
9
9
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
10
10
  return [
11
- get("--color-action-primary-bg", "#118AB2"),
11
+ get("--color-action-primary-bg", "#0E6E8F"),
12
12
  get("--color-core-accent-mint", "#06D6A0"),
13
13
  get("--color-core-accent-gold", "#FFD166"),
14
14
  get("--color-core-accent-coral", "#EF476F"),
@@ -31,7 +31,7 @@ const RadarChart = ({
31
31
  const classes = [baseClass, className].filter(Boolean).join(" ");
32
32
  const textSecondary = getCSSVar("--color-text-secondary", "#A2A2A2");
33
33
  const gridColor = getCSSVar("--color-divider", "#232323");
34
- const defaultColor = getCSSVar("--color-action-primary-bg", "#118AB2");
34
+ const defaultColor = getCSSVar("--color-action-primary-bg", "#0E6E8F");
35
35
  const renderTooltip = useCallback(
36
36
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
37
37
  (props) => /* @__PURE__ */ jsx(RadarTooltip, { ...props }),
@@ -4,11 +4,11 @@ import { ResponsiveContainer, RadialBarChart, PolarAngleAxis, RadialBar, Tooltip
4
4
  import "./Chart.css";
5
5
  function getDefaultColors() {
6
6
  if (typeof window === "undefined") {
7
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
7
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
8
8
  }
9
9
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
10
10
  return [
11
- get("--color-action-primary-bg", "#118AB2"),
11
+ get("--color-action-primary-bg", "#0E6E8F"),
12
12
  get("--color-core-accent-mint", "#06D6A0"),
13
13
  get("--color-core-accent-gold", "#FFD166"),
14
14
  get("--color-core-accent-coral", "#EF476F"),
@@ -8,11 +8,11 @@ function getCSSVar(name, fallback) {
8
8
  }
9
9
  function getDefaultColors() {
10
10
  if (typeof window === "undefined") {
11
- return ["#118AB2", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
11
+ return ["#0E6E8F", "#06D6A0", "#FFD166", "#EF476F", "#9E47EF", "#EF8247"];
12
12
  }
13
13
  const get = (v, fb) => getComputedStyle(document.documentElement).getPropertyValue(v).trim() || fb;
14
14
  return [
15
- get("--color-action-primary-bg", "#118AB2"),
15
+ get("--color-action-primary-bg", "#0E6E8F"),
16
16
  get("--color-core-accent-mint", "#06D6A0"),
17
17
  get("--color-core-accent-gold", "#FFD166"),
18
18
  get("--color-core-accent-coral", "#EF476F"),
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import React, { useRef, useEffect, useState, useLayoutEffect } from "react";
3
4
  import "./ChatThread.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./Checkbox.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import { useState, useId } from "react";
3
4
  import "./CodeBlock.css";