@robr0/design-system 0.3.0 → 0.5.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 (94) hide show
  1. package/README.md +30 -27
  2. package/components/AgentStatus/AgentStatus.css +496 -0
  3. package/components/AgentStatus/AgentStatus.d.ts +41 -0
  4. package/components/AgentStatus/AgentStatus.js +85 -0
  5. package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
  6. package/components/AgentStatus/AgentStatusPatterns.js +20 -0
  7. package/components/AiButton/AiButton.css +163 -0
  8. package/components/AiButton/AiButton.d.ts +36 -0
  9. package/components/AiButton/AiButton.js +58 -0
  10. package/components/AlertDialog/AlertDialog.css +5 -5
  11. package/components/Button/Button.d.ts +14 -2
  12. package/components/ButtonGroup/ButtonGroup.js +2 -9
  13. package/components/ChatHeader/ChatHeader.css +35 -0
  14. package/components/ChatHeader/ChatHeader.d.ts +30 -0
  15. package/components/ChatHeader/ChatHeader.js +17 -0
  16. package/components/ChatMarker/ChatMarker.css +52 -0
  17. package/components/ChatMarker/ChatMarker.d.ts +22 -0
  18. package/components/ChatMarker/ChatMarker.js +18 -0
  19. package/components/ChatMessage/ChatMessage.css +304 -0
  20. package/components/ChatMessage/ChatMessage.d.ts +68 -0
  21. package/components/ChatMessage/ChatMessage.js +69 -0
  22. package/components/ChatThread/ChatThread.css +171 -0
  23. package/components/ChatThread/ChatThread.d.ts +41 -0
  24. package/components/ChatThread/ChatThread.js +174 -0
  25. package/components/Checkbox/Checkbox.d.ts +17 -3
  26. package/components/Chip/Chip.css +27 -0
  27. package/components/Chip/Chip.d.ts +2 -2
  28. package/components/CircularButton/CircularButton.d.ts +31 -10
  29. package/components/CircularButton/CircularButton.js +54 -50
  30. package/components/CodeBlock/CodeBlock.css +2 -2
  31. package/components/Combobox/Combobox.d.ts +10 -2
  32. package/components/Composer/Composer.css +164 -0
  33. package/components/Composer/Composer.d.ts +67 -0
  34. package/components/Composer/Composer.js +120 -0
  35. package/components/ContextMenu/ContextMenu.css +0 -4
  36. package/components/DateInput/DateInput.d.ts +5 -1
  37. package/components/Dialog/Dialog.css +5 -5
  38. package/components/DocumentChip/DocumentChip.css +181 -0
  39. package/components/DocumentChip/DocumentChip.d.ts +41 -0
  40. package/components/DocumentChip/DocumentChip.js +78 -0
  41. package/components/Dropdown/Dropdown.css +2 -4
  42. package/components/Dropdown/Dropdown.d.ts +10 -2
  43. package/components/DropdownMenu/DropdownMenu.css +2 -4
  44. package/components/FileInput/FileInput.d.ts +5 -1
  45. package/components/Input/Input.d.ts +5 -1
  46. package/components/InterruptCard/InterruptCard.css +164 -0
  47. package/components/InterruptCard/InterruptCard.d.ts +55 -0
  48. package/components/InterruptCard/InterruptCard.js +57 -0
  49. package/components/LinkList/LinkList.d.ts +1 -0
  50. package/components/MessageActions/MessageActions.css +76 -0
  51. package/components/MessageActions/MessageActions.d.ts +38 -0
  52. package/components/MessageActions/MessageActions.js +33 -0
  53. package/components/MessageCard/MessageCard.css +112 -0
  54. package/components/MessageCard/MessageCard.d.ts +35 -0
  55. package/components/MessageCard/MessageCard.js +28 -0
  56. package/components/NavList/NavList.css +155 -0
  57. package/components/NavList/NavList.d.ts +56 -0
  58. package/components/NavList/NavList.js +99 -0
  59. package/components/PromptSuggestions/PromptSuggestions.css +87 -0
  60. package/components/PromptSuggestions/PromptSuggestions.d.ts +51 -0
  61. package/components/PromptSuggestions/PromptSuggestions.js +34 -0
  62. package/components/Prose/Prose.css +252 -0
  63. package/components/Prose/Prose.d.ts +21 -0
  64. package/components/Prose/Prose.js +14 -0
  65. package/components/RadioButton/RadioButton.d.ts +17 -3
  66. package/components/Reasoning/Reasoning.css +276 -0
  67. package/components/Reasoning/Reasoning.d.ts +55 -0
  68. package/components/Reasoning/Reasoning.js +98 -0
  69. package/components/SelectionCard/SelectionCard.d.ts +5 -1
  70. package/components/Slider/Slider.d.ts +5 -1
  71. package/components/SourceChip/SourceChip.css +102 -0
  72. package/components/SourceChip/SourceChip.d.ts +32 -0
  73. package/components/SourceChip/SourceChip.js +31 -0
  74. package/components/Stat/Stat.css +2 -2
  75. package/components/Textarea/Textarea.d.ts +5 -1
  76. package/components/Timeline/Timeline.d.ts +2 -0
  77. package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
  78. package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
  79. package/components/ToolCall/ToolCall.css +213 -0
  80. package/components/ToolCall/ToolCall.d.ts +43 -0
  81. package/components/ToolCall/ToolCall.js +89 -0
  82. package/components/registry.json +145 -8
  83. package/components/registry.json.d.ts +145 -8
  84. package/components/registry.json.js +2 -2
  85. package/index.d.ts +18 -0
  86. package/index.js +36 -0
  87. package/package.json +8 -4
  88. package/tokens/registry.json +13 -0
  89. package/tokens/registry.json.d.ts +13 -0
  90. package/tokens/registry.json.js +1 -1
  91. package/tokens/tokens-dark.css +13 -0
  92. package/tokens/tokens-light.css +42 -0
  93. package/tokens/tokens-motion.css +3 -1
  94. package/tokens/tokens-typography.css +24 -0
@@ -0,0 +1,41 @@
1
+ import { default as React } from 'react';
2
+ import { AgentStatusPattern, AgentStatusState } from './AgentStatusPatterns';
3
+ /** Props owned by AgentStatus itself — everything else falls through to the root element. */
4
+ type AgentStatusOwnProps = {
5
+ /** What the agent is doing. Drives the colour, the default label, and whether the matrix animates. */
6
+ state?: AgentStatusState;
7
+ /** Status text. Falls back to a default for the state. Ignored when `children` are given. */
8
+ label?: string;
9
+ /** Which dot-matrix choreography to run. */
10
+ pattern?: AgentStatusPattern;
11
+ /**
12
+ * Sweep a highlight across the label while the agent is active. Defaults to
13
+ * on for `thinking`, `working` and `waiting`, off for the resting states.
14
+ */
15
+ shimmer?: boolean;
16
+ /**
17
+ * Indicator and text scale, paired with ChatMessage's sizes: `default`
18
+ * matches default message text, `compact` matches compact message text.
19
+ * `sm` and `md` are legacy aliases for `compact` and `default`.
20
+ */
21
+ size?: 'default' | 'compact' | 'sm' | 'md';
22
+ /** `inline` sits in a line of content; `bar` is a full-width row for the top of a panel. */
23
+ variant?: 'inline' | 'bar';
24
+ /** Additional CSS classes */
25
+ className?: string;
26
+ /** Status text, when it needs markup the `label` string cannot express. */
27
+ children?: React.ReactNode;
28
+ };
29
+ export interface AgentStatusProps extends AgentStatusOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof AgentStatusOwnProps> {
30
+ }
31
+ /**
32
+ * AgentStatus reports what an agent is doing right now: a dot-matrix indicator
33
+ * beside a line of status text.
34
+ *
35
+ * Colour carries meaning rather than decoration — the three working states are
36
+ * monochrome, and only `waiting`, `done` and `error` take a status colour.
37
+ * The whole thing is one polite live region, so a screen reader hears the state
38
+ * change without the caller wiring anything up.
39
+ */
40
+ export declare const AgentStatus: React.ForwardRefExoticComponent<AgentStatusProps & React.RefAttributes<HTMLDivElement>>;
41
+ export {};
@@ -0,0 +1,85 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import React, { useState, useRef, useEffect } from "react";
3
+ import "./AgentStatus.css";
4
+ const DOT_COUNT = 12;
5
+ const DEFAULT_LABELS = {
6
+ idle: "Idle",
7
+ thinking: "Thinking",
8
+ working: "Working",
9
+ waiting: "Waiting for you",
10
+ done: "Done",
11
+ error: "Failed"
12
+ };
13
+ const ACTIVE_STATES = ["thinking", "working", "waiting"];
14
+ const AgentStatus = React.forwardRef(
15
+ ({
16
+ state = "thinking",
17
+ label,
18
+ pattern = "orbit",
19
+ shimmer,
20
+ size = "default",
21
+ variant = "inline",
22
+ className = "",
23
+ children,
24
+ ...rest
25
+ }, ref) => {
26
+ const baseClass = "ds-agent-status";
27
+ const isActive = ACTIVE_STATES.includes(state);
28
+ const isShimmering = shimmer ?? isActive;
29
+ const resolvedSize = size === "sm" ? "compact" : size === "md" ? "default" : size;
30
+ const resolvedLabel = label ?? DEFAULT_LABELS[state];
31
+ const [outgoing, setOutgoing] = useState(null);
32
+ const lastLabel = useRef(resolvedLabel);
33
+ useEffect(() => {
34
+ if (children != null) return;
35
+ if (lastLabel.current !== resolvedLabel) {
36
+ setOutgoing(lastLabel.current);
37
+ lastLabel.current = resolvedLabel;
38
+ }
39
+ }, [resolvedLabel, children]);
40
+ const isSwapping = outgoing !== null;
41
+ const classes = [
42
+ baseClass,
43
+ `${baseClass}--${state}`,
44
+ `${baseClass}--${pattern}`,
45
+ `${baseClass}--${resolvedSize}`,
46
+ `${baseClass}--${variant}`,
47
+ isActive ? `${baseClass}--animated` : "",
48
+ className
49
+ ].filter(Boolean).join(" ");
50
+ return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, role: "status", children: [
51
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__matrix`, "aria-hidden": "true", children: Array.from({ length: DOT_COUNT }, (_, i) => /* @__PURE__ */ jsx("span", { className: `${baseClass}__dot` }, i)) }),
52
+ /* @__PURE__ */ jsx(
53
+ "span",
54
+ {
55
+ className: `${baseClass}__label${isShimmering ? ` ${baseClass}__label--shimmer` : ""}${isSwapping ? ` ${baseClass}__label--swapping` : ""}`,
56
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
57
+ isSwapping && /* @__PURE__ */ jsx(
58
+ "span",
59
+ {
60
+ className: `${baseClass}__label-text ${baseClass}__label-text--out`,
61
+ "aria-hidden": "true",
62
+ children: outgoing
63
+ }
64
+ ),
65
+ /* @__PURE__ */ jsx(
66
+ "span",
67
+ {
68
+ className: `${baseClass}__label-text${isSwapping ? ` ${baseClass}__label-text--in` : ""}`,
69
+ onAnimationEnd: (e) => {
70
+ if (e.target === e.currentTarget) setOutgoing(null);
71
+ },
72
+ children: resolvedLabel
73
+ },
74
+ resolvedLabel
75
+ )
76
+ ] })
77
+ }
78
+ )
79
+ ] });
80
+ }
81
+ );
82
+ AgentStatus.displayName = "AgentStatus";
83
+ export {
84
+ AgentStatus
85
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * AgentStatus's vocabulary, kept out of the component file so that module
3
+ * exports only a component (react-refresh) — and exported rather than kept
4
+ * private so a consumer building a pattern picker, and this repo's own docs
5
+ * gallery, can iterate the list instead of restating it.
6
+ *
7
+ * `.tsx` despite holding no JSX: the barrel generator and the
8
+ * `./components/*` export subpath both address `.tsx`, so that extension is
9
+ * what makes this reachable as `@robr0/design-system/components/AgentStatus/AgentStatusPatterns`.
10
+ */
11
+ /**
12
+ * The dot-matrix animations. Each pattern is a named choreography over the
13
+ * same 4x3 grid, so switching pattern never changes the indicator's footprint.
14
+ */
15
+ export declare const agentStatusPatterns: readonly ["braille", "orbit", "breathe", "snake", "fill-sweep", "pulse", "columns", "checkerboard", "scan", "rain", "cascade", "sparkle", "wave-rows", "helix", "diagonal-swipe"];
16
+ export type AgentStatusPattern = (typeof agentStatusPatterns)[number];
17
+ export type AgentStatusState = 'idle' | 'thinking' | 'working' | 'waiting' | 'done' | 'error';
@@ -0,0 +1,20 @@
1
+ const agentStatusPatterns = [
2
+ "braille",
3
+ "orbit",
4
+ "breathe",
5
+ "snake",
6
+ "fill-sweep",
7
+ "pulse",
8
+ "columns",
9
+ "checkerboard",
10
+ "scan",
11
+ "rain",
12
+ "cascade",
13
+ "sparkle",
14
+ "wave-rows",
15
+ "helix",
16
+ "diagonal-swipe"
17
+ ];
18
+ export {
19
+ agentStatusPatterns
20
+ };
@@ -0,0 +1,163 @@
1
+ /* ============================================
2
+ AI BUTTON
3
+ The AI entry point: transparent field, slowly
4
+ rotating gradient ring, soft glow of the same
5
+ gradient. The ring is the system's "a model
6
+ answers here" signal — ordinary actions keep
7
+ the flat action teal.
8
+ ============================================ */
9
+
10
+ /* The rotation animates a registered custom property: a conic gradient's
11
+ angle cannot tween otherwise. Browsers without @property render the ring
12
+ at its initial angle, static — a correct, quieter fallback. */
13
+ @property --ds-ai-button-angle {
14
+ syntax: '<angle>';
15
+ initial-value: 0deg;
16
+ inherits: false;
17
+ }
18
+
19
+ /* Base */
20
+ .ds-ai-button {
21
+ /* Layout */
22
+ position: relative;
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ gap: var(--gap-sm);
27
+
28
+ /* Spacing — matches Button so the two sit on one control scale */
29
+ padding: var(--padding-sm) var(--padding-lg);
30
+
31
+ /* Buttons are always pill-shaped (design.md invariant) */
32
+ border-radius: var(--radius-full);
33
+
34
+ /* Typography — the clickable-control face, same as Button */
35
+ font-family: var(--font-paragraph-em-family);
36
+ font-weight: var(--font-paragraph-em-weight);
37
+ font-size: var(--font-paragraph-em-size);
38
+ line-height: var(--font-paragraph-em-line-height);
39
+ letter-spacing: var(--font-paragraph-em-letter-spacing);
40
+ text-align: center;
41
+
42
+ /* The field is transparent by design: the ring and glow are the button */
43
+ background: transparent;
44
+ color: var(--color-text-primary);
45
+
46
+ transition: opacity var(--motion-duration-base) var(--motion-ease-standard);
47
+
48
+ /* Reset */
49
+ cursor: pointer;
50
+ border: none;
51
+ text-decoration: none;
52
+ }
53
+
54
+ /* The gradient ring: a conic gradient painted across the whole button, then
55
+ masked to the border band (two fills, exclude-composited, inset by the
56
+ border width). The gradient repeats back to its start colour so the seam
57
+ at 360° is invisible while it turns. */
58
+ .ds-ai-button::before {
59
+ content: '';
60
+ position: absolute;
61
+ inset: 0;
62
+ border-radius: inherit;
63
+ padding: var(--border-xs);
64
+ background: conic-gradient(
65
+ from var(--ds-ai-button-angle),
66
+ var(--color-ai-gradient-start),
67
+ var(--color-ai-gradient-mid),
68
+ var(--color-ai-gradient-end),
69
+ var(--color-ai-gradient-mid),
70
+ var(--color-ai-gradient-start)
71
+ );
72
+ /* Standard properties only — the build pipeline prefixes per its browser
73
+ targets, and hand-written -webkit- duplicates confuse its merging. */
74
+ mask:
75
+ linear-gradient(#fff 0 0) content-box,
76
+ linear-gradient(#fff 0 0);
77
+ mask-composite: exclude;
78
+ pointer-events: none;
79
+ /* ds-allow(motion): ambient signature rotation, tuned by eye — deliberately
80
+ far slower than the interaction-scale motion tokens */
81
+ animation: ds-ai-button-turn 7s linear infinite;
82
+ }
83
+
84
+ /* The glow: the same gradient, blurred behind the button at low opacity.
85
+ A colour effect, not an elevation shadow — depth stays token-owned. */
86
+ .ds-ai-button::after {
87
+ content: '';
88
+ position: absolute;
89
+ inset: 0;
90
+ border-radius: inherit;
91
+ background: conic-gradient(
92
+ from var(--ds-ai-button-angle),
93
+ var(--color-ai-gradient-start),
94
+ var(--color-ai-gradient-mid),
95
+ var(--color-ai-gradient-end),
96
+ var(--color-ai-gradient-mid),
97
+ var(--color-ai-gradient-start)
98
+ );
99
+ /* The glow's spread; a length with no token family, chosen by eye. */
100
+ --ds-ai-button-glow: 14px;
101
+ filter: blur(var(--ds-ai-button-glow));
102
+ opacity: 0.3;
103
+ z-index: -1;
104
+ pointer-events: none;
105
+ transition: opacity var(--motion-duration-base) var(--motion-ease-standard);
106
+ /* ds-allow(motion): same ambient rotation as the ring, kept in phase */
107
+ animation: ds-ai-button-turn 7s linear infinite;
108
+ }
109
+
110
+ @keyframes ds-ai-button-turn {
111
+ to {
112
+ --ds-ai-button-angle: 360deg;
113
+ }
114
+ }
115
+
116
+ /* Icon rides the icon-size scale */
117
+ .ds-ai-button__icon {
118
+ --icon-size: var(--icon-size-md);
119
+ line-height: 1;
120
+ }
121
+
122
+ /* States */
123
+ .ds-ai-button:hover::after {
124
+ opacity: 0.55;
125
+ }
126
+
127
+ .ds-ai-button:focus-visible {
128
+ outline: var(--border-md) solid var(--color-action-primary-bg);
129
+ outline-offset: 2px;
130
+ }
131
+
132
+ .ds-ai-button--disabled,
133
+ .ds-ai-button:disabled {
134
+ opacity: 0.4;
135
+ cursor: not-allowed;
136
+ }
137
+
138
+ .ds-ai-button--disabled::before,
139
+ .ds-ai-button--disabled::after,
140
+ .ds-ai-button:disabled::before,
141
+ .ds-ai-button:disabled::after {
142
+ animation-play-state: paused;
143
+ }
144
+
145
+ /* Sizes */
146
+ .ds-ai-button--compact {
147
+ padding: var(--padding-xs) var(--padding-sm-md);
148
+ font-size: var(--font-paragraph-sm-em-size);
149
+ line-height: var(--font-paragraph-sm-em-line-height);
150
+ letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
151
+ }
152
+
153
+ .ds-ai-button--compact .ds-ai-button__icon {
154
+ --icon-size: var(--icon-size-sm);
155
+ }
156
+
157
+ /* Reduced motion: the ring and glow stay, the turning stops. */
158
+ @media (prefers-reduced-motion: reduce) {
159
+ .ds-ai-button::before,
160
+ .ds-ai-button::after {
161
+ animation: none;
162
+ }
163
+ }
@@ -0,0 +1,36 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by AiButton itself — everything else falls through to the DOM node. */
3
+ type AiButtonOwnProps = {
4
+ /** Button text content */
5
+ label?: string;
6
+ /** Leading icon — Material Symbol name (string) or custom element (ReactNode) */
7
+ icon?: string | React.ReactNode;
8
+ /** Button size */
9
+ size?: 'default' | 'compact';
10
+ /** Whether the button is disabled */
11
+ disabled?: boolean;
12
+ /** Optional href — renders as <a> instead of <button> */
13
+ href?: string;
14
+ /** Optional target attribute for links */
15
+ target?: string;
16
+ /** Optional rel attribute for links */
17
+ rel?: string;
18
+ /** Additional CSS classes */
19
+ className?: string;
20
+ };
21
+ export interface AiButtonProps extends AiButtonOwnProps, Omit<React.ComponentPropsWithoutRef<'button'>, keyof AiButtonOwnProps | 'type'> {
22
+ }
23
+ /**
24
+ * The AI entry point. Icon and label on a transparent field, ringed by the
25
+ * AI gradient (`--color-ai-gradient-*`) rotating slowly, with a soft glow of
26
+ * the same gradient behind it. The treatment is the design system's signal
27
+ * for "a model answers here": ordinary actions keep the flat action teal,
28
+ * and this ring is reserved for AI surfaces so neither affordance dilutes
29
+ * the other.
30
+ *
31
+ * Renders a `<button>`, or an `<a>` when `href` is supplied. Forwards a ref
32
+ * to whichever element it renders, and spreads unrecognised props onto it.
33
+ * The rotation pauses under `prefers-reduced-motion`; the ring and glow stay.
34
+ */
35
+ export declare const AiButton: React.ForwardRefExoticComponent<AiButtonProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
36
+ export {};
@@ -0,0 +1,58 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./AiButton.css";
4
+ import "../../fonts/material-symbols.css";
5
+ const AiButton = React.forwardRef(
6
+ ({
7
+ label = "Ask AI",
8
+ icon = "auto_awesome",
9
+ size = "default",
10
+ disabled,
11
+ href,
12
+ target,
13
+ rel,
14
+ className = "",
15
+ ...rest
16
+ }, ref) => {
17
+ const baseClass = "ds-ai-button";
18
+ const classes = [
19
+ baseClass,
20
+ `${baseClass}--${size}`,
21
+ disabled && `${baseClass}--disabled`,
22
+ className
23
+ ].filter(Boolean).join(" ");
24
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
25
+ icon && (typeof icon === "string" ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: icon })),
26
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__text`, children: label })
27
+ ] });
28
+ if (href && !disabled) {
29
+ return /* @__PURE__ */ jsx(
30
+ "a",
31
+ {
32
+ ...rest,
33
+ ref,
34
+ className: classes,
35
+ href,
36
+ target,
37
+ rel,
38
+ children: content
39
+ }
40
+ );
41
+ }
42
+ return /* @__PURE__ */ jsx(
43
+ "button",
44
+ {
45
+ ...rest,
46
+ ref,
47
+ type: "button",
48
+ className: classes,
49
+ disabled,
50
+ children: content
51
+ }
52
+ );
53
+ }
54
+ );
55
+ AiButton.displayName = "AiButton";
56
+ export {
57
+ AiButton
58
+ };
@@ -81,11 +81,11 @@
81
81
  ============================================ */
82
82
 
83
83
  .ds-alert-dialog__title {
84
- font-family: var(--font-heading-6-family);
85
- font-size: var(--font-heading-6-size);
86
- font-weight: var(--font-heading-6-weight);
87
- line-height: var(--font-heading-6-line-height);
88
- letter-spacing: var(--font-heading-6-letter-spacing);
84
+ font-family: var(--font-heading-3-family);
85
+ font-size: var(--font-heading-3-size);
86
+ font-weight: var(--font-heading-3-weight);
87
+ line-height: var(--font-heading-3-line-height);
88
+ letter-spacing: var(--font-heading-3-letter-spacing);
89
89
  color: var(--color-text-primary);
90
90
  margin: 0;
91
91
  }
@@ -29,9 +29,15 @@ type ButtonOwnProps = {
29
29
  ariaCurrent?: boolean;
30
30
  /** Additional CSS classes */
31
31
  className?: string;
32
- /** @deprecated Use `variant` instead. */
32
+ /**
33
+ * Legacy alias for `variant`.
34
+ *
35
+ * @deprecated Use `variant` instead.
36
+ */
33
37
  priority?: 'primary' | 'secondary' | 'tertiary' | 'destructive';
34
38
  /**
39
+ * Documentation-only interaction state.
40
+ *
35
41
  * @deprecated Use `disabled` for the disabled state.
36
42
  *
37
43
  * Documentation-only affordance for rendering a *static* interaction state in
@@ -39,9 +45,15 @@ type ButtonOwnProps = {
39
45
  * pseudo-classes and needs no prop — for docs, prefer `className="ds-button--hover"`.
40
46
  */
41
47
  state?: 'default' | 'hover' | 'active' | 'disabled';
42
- /** @deprecated Use `iconLeft` instead. */
48
+ /**
49
+ * Legacy alias for `iconLeft`.
50
+ *
51
+ * @deprecated Use `iconLeft` instead.
52
+ */
43
53
  icon?: string;
44
54
  /**
55
+ * Legacy toggle for showing the text label.
56
+ *
45
57
  * @deprecated Will be removed once `label` loses its default in the next major;
46
58
  * an icon-only button will simply omit `label`.
47
59
  */
@@ -17,15 +17,8 @@ const ButtonGroup = ({
17
17
  children: buttons.map((button, index) => /* @__PURE__ */ jsx(
18
18
  Button,
19
19
  {
20
- priority: button.priority || "tertiary",
21
- state: button.state || "default",
22
- label: button.label,
23
- iconLeft: button.iconLeft,
24
- iconRight: button.iconRight,
25
- icon: button.icon,
26
- onClick: button.onClick,
27
- href: button.href,
28
- ariaCurrent: button.ariaCurrent
20
+ ...button,
21
+ variant: button.variant ?? button.priority ?? "tertiary"
29
22
  },
30
23
  index
31
24
  ))
@@ -0,0 +1,35 @@
1
+ /* ============================================
2
+ CHAT HEADER COMPONENT
3
+ Top row of a chat surface: title on the
4
+ left, controls on the right. No divider —
5
+ it floats free of the transcript.
6
+ ============================================ */
7
+
8
+ /* Base */
9
+ .ds-chat-header {
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ gap: var(--gap-md);
14
+ padding: var(--padding-xs) var(--padding-md);
15
+ }
16
+
17
+ /* Title */
18
+ .ds-chat-header__title {
19
+ font-family: var(--font-title-body-family);
20
+ font-size: var(--font-title-body-size);
21
+ font-weight: var(--font-title-body-weight);
22
+ line-height: var(--font-title-body-line-height);
23
+ color: var(--color-text-primary);
24
+ overflow: hidden;
25
+ text-overflow: ellipsis;
26
+ white-space: nowrap;
27
+ }
28
+
29
+ /* Actions */
30
+ .ds-chat-header__actions {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--gap-xs);
34
+ flex-shrink: 0;
35
+ }
@@ -0,0 +1,30 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by ChatHeader itself — everything else falls through to the root element. */
3
+ type ChatHeaderOwnProps = {
4
+ /**
5
+ * The chat or assistant name. Deliberately shadows the native `title`
6
+ * tooltip attribute — a header's title is content, not a tooltip. Pass a
7
+ * string for the default treatment, or your own heading element.
8
+ */
9
+ title?: React.ReactNode;
10
+ /**
11
+ * Trailing controls: a new-chat CircularButton, a view switcher, a close
12
+ * button. The slot only lays them out — each control owns its behaviour.
13
+ */
14
+ actions?: React.ReactNode;
15
+ /** Additional CSS classes */
16
+ className?: string;
17
+ };
18
+ export interface ChatHeaderProps extends ChatHeaderOwnProps, Omit<React.ComponentPropsWithoutRef<'header'>, keyof ChatHeaderOwnProps> {
19
+ }
20
+ /**
21
+ * ChatHeader is the top row of a chat surface: the conversation's name on the
22
+ * left, its controls on the right. It draws no bottom border — like the
23
+ * composer below, it floats free of the transcript, and the thread's own
24
+ * padding provides the separation.
25
+ *
26
+ * Purely presentational, so it renders from a Server Component; the controls
27
+ * passed into `actions` bring their own interactivity.
28
+ */
29
+ export declare const ChatHeader: React.ForwardRefExoticComponent<ChatHeaderProps & React.RefAttributes<HTMLElement>>;
30
+ export {};
@@ -0,0 +1,17 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./ChatHeader.css";
4
+ const ChatHeader = React.forwardRef(
5
+ ({ title, actions, className = "", ...rest }, ref) => {
6
+ const baseClass = "ds-chat-header";
7
+ const classes = [baseClass, className].filter(Boolean).join(" ");
8
+ return /* @__PURE__ */ jsxs("header", { ...rest, ref, className: classes, children: [
9
+ typeof title === "string" ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__title`, children: title }) : title,
10
+ actions && /* @__PURE__ */ jsx("div", { className: `${baseClass}__actions`, children: actions })
11
+ ] });
12
+ }
13
+ );
14
+ ChatHeader.displayName = "ChatHeader";
15
+ export {
16
+ ChatHeader
17
+ };
@@ -0,0 +1,52 @@
1
+ /* ============================================
2
+ CHAT MARKER COMPONENT
3
+ Inline conversation separator for dates and system notes
4
+ ============================================ */
5
+
6
+ .ds-chat-marker {
7
+ display: flex;
8
+ align-items: center;
9
+ gap: var(--gap-sm-md);
10
+ width: 100%;
11
+ }
12
+
13
+ /* Flanking lines — kept as flex spacers when bare, so the label
14
+ stays centred either way */
15
+ .ds-chat-marker::before,
16
+ .ds-chat-marker::after {
17
+ content: '';
18
+ flex: 1;
19
+ height: var(--border-xs);
20
+ background-color: var(--color-divider);
21
+ }
22
+
23
+ .ds-chat-marker--bare::before,
24
+ .ds-chat-marker--bare::after {
25
+ background-color: transparent;
26
+ }
27
+
28
+ /* ============================================
29
+ LABEL
30
+ ============================================ */
31
+
32
+ .ds-chat-marker__label {
33
+ flex-shrink: 0;
34
+ display: inline-flex;
35
+ align-items: center;
36
+ gap: var(--gap-xs);
37
+ font-family: var(--font-paragraph-sm-family);
38
+ font-size: var(--font-paragraph-sm-size);
39
+ font-weight: var(--font-paragraph-sm-weight);
40
+ line-height: var(--font-paragraph-sm-line-height);
41
+ letter-spacing: var(--font-paragraph-sm-letter-spacing);
42
+ color: var(--color-text-tertiary);
43
+ }
44
+
45
+ .ds-chat-marker__icon {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ }
49
+
50
+ .ds-chat-marker__icon .material-symbols-rounded {
51
+ --icon-size: var(--icon-size-sm);
52
+ }
@@ -0,0 +1,22 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by ChatMarker itself — everything else falls through to the root element. */
3
+ type ChatMarkerOwnProps = {
4
+ /** Leading icon — a Material Symbol name, or any custom element. */
5
+ icon?: string | React.ReactNode;
6
+ /** Draw the flanking divider lines. Turn off for a bare centred note. */
7
+ line?: boolean;
8
+ /** Additional CSS classes */
9
+ className?: string;
10
+ /** The marker text, e.g. "Today" or "Chat renamed". */
11
+ children?: React.ReactNode;
12
+ };
13
+ export interface ChatMarkerProps extends ChatMarkerOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof ChatMarkerOwnProps> {
14
+ }
15
+ /**
16
+ * ChatMarker is the inline separator a conversation uses for anything that
17
+ * is not a turn: date breaks, joins, mode changes, system notes. It reads
18
+ * as furniture rather than as a message — quiet tertiary text between two
19
+ * divider lines, so the eye skips it while scanning turns.
20
+ */
21
+ export declare const ChatMarker: React.ForwardRefExoticComponent<ChatMarkerProps & React.RefAttributes<HTMLDivElement>>;
22
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./ChatMarker.css";
4
+ import "../../fonts/material-symbols.css";
5
+ const ChatMarker = React.forwardRef(
6
+ ({ icon, line = true, className = "", children, ...rest }, ref) => {
7
+ const baseClass = "ds-chat-marker";
8
+ const classes = [baseClass, line ? "" : `${baseClass}--bare`, className].filter(Boolean).join(" ");
9
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: classes, role: "separator", children: /* @__PURE__ */ jsxs("span", { className: `${baseClass}__label`, children: [
10
+ icon && /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: typeof icon === "string" ? /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", children: icon }) : icon }),
11
+ children
12
+ ] }) });
13
+ }
14
+ );
15
+ ChatMarker.displayName = "ChatMarker";
16
+ export {
17
+ ChatMarker
18
+ };