@robr0/design-system 0.4.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 (68) hide show
  1. package/README.md +2 -2
  2. package/components/AgentStatus/AgentStatus.css +82 -22
  3. package/components/AgentStatus/AgentStatus.d.ts +6 -2
  4. package/components/AgentStatus/AgentStatus.js +38 -6
  5. package/components/AiButton/AiButton.css +163 -0
  6. package/components/AiButton/AiButton.d.ts +36 -0
  7. package/components/AiButton/AiButton.js +58 -0
  8. package/components/ChatHeader/ChatHeader.css +35 -0
  9. package/components/ChatHeader/ChatHeader.d.ts +30 -0
  10. package/components/ChatHeader/ChatHeader.js +17 -0
  11. package/components/ChatMarker/ChatMarker.css +52 -0
  12. package/components/ChatMarker/ChatMarker.d.ts +22 -0
  13. package/components/ChatMarker/ChatMarker.js +18 -0
  14. package/components/ChatMessage/ChatMessage.css +304 -0
  15. package/components/ChatMessage/ChatMessage.d.ts +68 -0
  16. package/components/ChatMessage/ChatMessage.js +69 -0
  17. package/components/ChatThread/ChatThread.css +171 -0
  18. package/components/ChatThread/ChatThread.d.ts +41 -0
  19. package/components/ChatThread/ChatThread.js +174 -0
  20. package/components/Chip/Chip.css +27 -0
  21. package/components/Chip/Chip.d.ts +2 -2
  22. package/components/CircularButton/CircularButton.d.ts +23 -8
  23. package/components/CircularButton/CircularButton.js +54 -52
  24. package/components/CodeBlock/CodeBlock.css +2 -2
  25. package/components/Composer/Composer.css +164 -0
  26. package/components/Composer/Composer.d.ts +67 -0
  27. package/components/Composer/Composer.js +120 -0
  28. package/components/DocumentChip/DocumentChip.css +181 -0
  29. package/components/DocumentChip/DocumentChip.d.ts +41 -0
  30. package/components/DocumentChip/DocumentChip.js +78 -0
  31. package/components/InterruptCard/InterruptCard.css +164 -0
  32. package/components/InterruptCard/InterruptCard.d.ts +55 -0
  33. package/components/InterruptCard/InterruptCard.js +57 -0
  34. package/components/MessageActions/MessageActions.css +76 -0
  35. package/components/MessageActions/MessageActions.d.ts +38 -0
  36. package/components/MessageActions/MessageActions.js +33 -0
  37. package/components/MessageCard/MessageCard.css +112 -0
  38. package/components/MessageCard/MessageCard.d.ts +35 -0
  39. package/components/MessageCard/MessageCard.js +28 -0
  40. package/components/NavList/NavList.css +155 -0
  41. package/components/NavList/NavList.d.ts +56 -0
  42. package/components/NavList/NavList.js +99 -0
  43. package/components/PromptSuggestions/PromptSuggestions.css +87 -0
  44. package/components/PromptSuggestions/PromptSuggestions.d.ts +51 -0
  45. package/components/PromptSuggestions/PromptSuggestions.js +34 -0
  46. package/components/Prose/Prose.css +252 -0
  47. package/components/Prose/Prose.d.ts +21 -0
  48. package/components/Prose/Prose.js +14 -0
  49. package/components/Reasoning/Reasoning.css +149 -25
  50. package/components/Reasoning/Reasoning.d.ts +19 -0
  51. package/components/Reasoning/Reasoning.js +32 -3
  52. package/components/SourceChip/SourceChip.css +102 -0
  53. package/components/SourceChip/SourceChip.d.ts +32 -0
  54. package/components/SourceChip/SourceChip.js +31 -0
  55. package/components/Stat/Stat.css +2 -2
  56. package/components/ToolCall/ToolCall.css +3 -3
  57. package/components/registry.json +113 -1
  58. package/components/registry.json.d.ts +113 -1
  59. package/components/registry.json.js +1 -1
  60. package/index.d.ts +14 -0
  61. package/index.js +28 -0
  62. package/package.json +1 -1
  63. package/tokens/registry.json +12 -0
  64. package/tokens/registry.json.d.ts +12 -0
  65. package/tokens/registry.json.js +1 -1
  66. package/tokens/tokens-dark.css +13 -0
  67. package/tokens/tokens-light.css +42 -0
  68. package/tokens/tokens-typography.css +24 -0
@@ -7,6 +7,10 @@
7
7
  ============================================ */
8
8
 
9
9
  .ds-reasoning {
10
+ /* Where an AgentStatus summary's label starts: matrix plus its gap.
11
+ The rail geometry and the handoff slide both derive from it. */
12
+ --ds-reasoning-align: calc(var(--icon-size-md) + var(--gap-sm-md));
13
+
10
14
  display: flex;
11
15
  flex-direction: column;
12
16
  width: 100%;
@@ -19,24 +23,34 @@
19
23
  .ds-reasoning__trigger {
20
24
  display: inline-flex;
21
25
  align-items: center;
22
- gap: var(--gap-xs);
26
+ gap: var(--gap-sm);
23
27
  align-self: flex-start;
24
28
  padding: var(--padding-xxs) var(--padding-xs);
25
29
  margin-left: calc(var(--padding-xs) * -1);
26
30
  background: none;
27
31
  border: none;
28
32
  border-radius: var(--radius-sm);
29
- color: var(--color-text-tertiary);
30
- font-family: var(--font-paragraph-sm-family);
31
- font-size: var(--font-paragraph-sm-size);
32
- font-weight: var(--font-paragraph-sm-weight);
33
- line-height: var(--font-paragraph-sm-line-height);
34
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
33
+ color: var(--color-text-secondary);
34
+ font-family: var(--font-paragraph-family);
35
+ font-size: var(--font-paragraph-size);
36
+ font-weight: var(--font-paragraph-weight);
37
+ line-height: var(--font-paragraph-line-height);
38
+ letter-spacing: var(--font-paragraph-letter-spacing);
35
39
  cursor: pointer;
36
40
  transition: color var(--motion-duration-fast) var(--motion-ease-standard);
37
41
  }
38
42
 
39
43
  .ds-reasoning__trigger:hover {
44
+ color: var(--color-text-primary);
45
+ }
46
+
47
+ /* Summary-only: the same line, minus every affordance that would promise a
48
+ panel behind it. Not a button, so there is nothing to focus or press. */
49
+ .ds-reasoning__trigger--static {
50
+ cursor: default;
51
+ }
52
+
53
+ .ds-reasoning__trigger--static:hover {
40
54
  color: var(--color-text-secondary);
41
55
  }
42
56
 
@@ -45,35 +59,81 @@
45
59
  outline-offset: -2px;
46
60
  }
47
61
 
62
+ /* Closed the chevron points at the collapsed trace; open it points down the
63
+ way the panel expands. The glyph itself points down, so closed is the
64
+ rotated state — and the quarter turn to open runs clockwise, following
65
+ the panel as it opens. */
48
66
  .ds-reasoning__chevron {
49
67
  --icon-size: var(--icon-size-sm);
50
68
 
51
69
  flex: none;
70
+ transform: rotate(-90deg);
52
71
  transition: transform var(--motion-duration-slow) var(--motion-ease-standard);
53
72
  }
54
73
 
55
74
  .ds-reasoning--open .ds-reasoning__chevron {
56
- transform: rotate(180deg);
75
+ transform: rotate(0deg);
57
76
  }
58
77
 
59
78
  /* ============================================
60
79
  SUMMARY
61
- While the trace streams, a highlight sweeps
62
- the summary left to right — the same treatment
63
- AgentStatus uses, so "the model is working"
80
+ While the trace streams, a sweep walks the
81
+ summary left to right — the same gradient
82
+ AgentStatus uses (primary base, tertiary
83
+ band, same stops), so "the model is working"
64
84
  looks the same wherever it appears.
65
85
  ============================================ */
66
86
 
67
87
  .ds-reasoning__summary {
88
+ position: relative;
89
+ white-space: nowrap;
90
+ }
91
+
92
+ /* The live→done handoff. The departing node summary (an AgentStatus)
93
+ overlays the incoming text, fading out in place, while the text summary
94
+ slides left from where its label sat into the flush position. The
95
+ modifier comes off once the animation ends, so nothing stays composited. */
96
+ .ds-reasoning__summary--entering {
97
+ animation: ds-reasoning-summary-in var(--motion-duration-slow) var(--motion-ease-entrance) both;
98
+ }
99
+
100
+ .ds-reasoning__summary-leaving {
101
+ position: absolute;
102
+ left: 0;
103
+ top: 50%;
104
+ transform: translateY(-50%);
68
105
  white-space: nowrap;
106
+ animation: ds-reasoning-summary-out var(--motion-duration-slow) var(--motion-ease-standard) both;
107
+ }
108
+
109
+ @keyframes ds-reasoning-summary-in {
110
+ from {
111
+ opacity: 0;
112
+ transform: translateX(var(--ds-reasoning-align));
113
+ }
69
114
  }
70
115
 
71
- .ds-reasoning--streaming .ds-reasoning__summary {
116
+ @keyframes ds-reasoning-summary-out {
117
+ to {
118
+ opacity: 0;
119
+ }
120
+ }
121
+
122
+ /* A node summary (an AgentStatus) owns its own appearance — the text
123
+ shimmer below never applies to it. Inline-flex keeps the span exactly
124
+ the node's height: left inline, the node sits on the text baseline and
125
+ the leftover descender space pushes it off the chevron's centreline. */
126
+ .ds-reasoning__summary--node {
127
+ display: inline-flex;
128
+ align-items: center;
129
+ }
130
+
131
+ .ds-reasoning--streaming .ds-reasoning__summary:not(.ds-reasoning__summary--node) {
72
132
  background-image: linear-gradient(
73
133
  100deg,
74
- var(--color-text-tertiary) 40%,
75
- var(--color-text-primary) 50%,
76
- var(--color-text-tertiary) 60%
134
+ var(--color-text-primary) 35%,
135
+ var(--color-text-tertiary) 50%,
136
+ var(--color-text-primary) 65%
77
137
  );
78
138
  background-size: 250% 100%;
79
139
  background-clip: text;
@@ -115,20 +175,28 @@
115
175
  overflow: hidden;
116
176
  min-height: 0;
117
177
  transition: padding-block var(--motion-duration-slow) var(--motion-ease-standard);
118
- padding-left: var(--padding-sm-md);
119
- /* The rail sits flush with the summary's left edge the trigger's own
120
- padding is cancelled by its negative margin, so both start at zero */
178
+ /* The rail and trace align to an AgentStatus summary's geometry: the
179
+ rail runs down the centre of the matrix (half an icon step, corrected
180
+ by half the rail's own width) and the trace text starts exactly where
181
+ the status label's first letter starts (matrix + its gap). A plain
182
+ text summary shares the same geometry, so swapping a live status for
183
+ the thought-for line never shifts the trace. */
184
+ margin-left: calc(var(--icon-size-md) / 2 - var(--border-md) / 2);
185
+ padding-left: calc(var(--icon-size-md) / 2 + var(--gap-sm-md) - var(--border-md) / 2);
121
186
  border-left: var(--border-md) solid var(--color-divider);
122
187
  color: var(--color-text-secondary);
123
- font-family: var(--font-paragraph-sm-family);
124
- font-size: var(--font-paragraph-sm-size);
125
- font-weight: var(--font-paragraph-sm-weight);
126
- line-height: var(--font-paragraph-sm-line-height);
127
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
188
+ font-family: var(--font-paragraph-family);
189
+ font-size: var(--font-paragraph-size);
190
+ font-weight: var(--font-paragraph-weight);
191
+ line-height: var(--font-paragraph-line-height);
192
+ letter-spacing: var(--font-paragraph-letter-spacing);
128
193
  }
129
194
 
195
+ /* With the trigger's own bottom padding, the panel opens one --gap-lg
196
+ below the summary row — the same step a --gap-lg trace list uses
197
+ between nodes, so the first node isn't tighter than the rest. */
130
198
  .ds-reasoning--open .ds-reasoning__content {
131
- padding-block: var(--padding-xs);
199
+ padding-block: var(--padding-md);
132
200
  }
133
201
 
134
202
  .ds-reasoning__content > :first-child {
@@ -139,12 +207,68 @@
139
207
  margin-bottom: 0;
140
208
  }
141
209
 
210
+ /* ============================================
211
+ TRACE NODES
212
+ Inside Reasoning, a list is the trace's node
213
+ list: one thinking step per item, no bullets
214
+ (the rail is the marker), on the same
215
+ --gap-lg beat the panel opens with. New
216
+ nodes rise in as the trace builds.
217
+ ============================================ */
218
+
219
+ .ds-reasoning__content ul,
220
+ .ds-reasoning__content ol {
221
+ list-style: none;
222
+ margin: 0;
223
+ padding: 0;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: var(--gap-lg);
227
+ }
228
+
229
+ .ds-reasoning__content li {
230
+ animation: ds-reasoning-node-in var(--motion-duration-slow) var(--motion-ease-entrance);
231
+ }
232
+
233
+ @keyframes ds-reasoning-node-in {
234
+ from {
235
+ opacity: 0;
236
+ transform: translateY(6px);
237
+ }
238
+ }
239
+
240
+ /* ============================================
241
+ SIZES
242
+ The scale pairs with ChatMessage: default
243
+ matches default message text, compact
244
+ matches compact message text.
245
+ ============================================ */
246
+
247
+ .ds-reasoning--compact .ds-reasoning__trigger,
248
+ .ds-reasoning--compact .ds-reasoning__content {
249
+ font-family: var(--font-paragraph-sm-family);
250
+ font-size: var(--font-paragraph-sm-size);
251
+ font-weight: var(--font-paragraph-sm-weight);
252
+ line-height: var(--font-paragraph-sm-line-height);
253
+ letter-spacing: var(--font-paragraph-sm-letter-spacing);
254
+ }
255
+
256
+ /* Compact pairs with a compact AgentStatus: sm matrix, sm gap. */
257
+ .ds-reasoning--compact {
258
+ --ds-reasoning-align: calc(var(--icon-size-sm) + var(--gap-sm));
259
+ }
260
+
261
+ .ds-reasoning--compact .ds-reasoning__content {
262
+ margin-left: calc(var(--icon-size-sm) / 2 - var(--border-md) / 2);
263
+ padding-left: calc(var(--icon-size-sm) / 2 + var(--gap-sm) - var(--border-md) / 2);
264
+ }
265
+
142
266
  /* ============================================
143
267
  REDUCED MOTION
144
268
  ============================================ */
145
269
 
146
270
  @media (prefers-reduced-motion: reduce) {
147
- .ds-reasoning--streaming .ds-reasoning__summary {
271
+ .ds-reasoning--streaming .ds-reasoning__summary:not(.ds-reasoning__summary--node) {
148
272
  background-image: none;
149
273
  color: var(--color-text-tertiary);
150
274
  animation: none;
@@ -7,6 +7,25 @@ type ReasoningOwnProps = {
7
7
  duration?: number;
8
8
  /** Override the summary line. By default it reports the streaming state and the duration. */
9
9
  label?: string;
10
+ /**
11
+ * Replace the summary text with custom content — typically an AgentStatus,
12
+ * pairing the live indicator with the trace disclosure. The node owns its
13
+ * own appearance and announcement, so Reasoning's shimmer and live region
14
+ * stand down.
15
+ */
16
+ summary?: React.ReactNode;
17
+ /**
18
+ * Render the summary line alone, with no chevron, no trigger, and no
19
+ * panel — for a model that reports what it is doing but produces no trace
20
+ * to read. A disclosure that opens onto nothing is a promise the component
21
+ * cannot keep, so it stops being one.
22
+ */
23
+ summaryOnly?: boolean;
24
+ /**
25
+ * Text scale, paired with ChatMessage's sizes: `default` matches default
26
+ * message text, `compact` matches compact message text.
27
+ */
28
+ size?: 'default' | 'compact';
10
29
  /** Open state for controlled use. Pair with `onOpenChange`. */
11
30
  open?: boolean;
12
31
  /** Open state for uncontrolled use. Defaults to open while `streaming`. */
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import React, { useId, useState, useRef, useEffect, useCallback } from "react";
3
3
  import "./Reasoning.css";
4
4
  import "../../fonts/material-symbols.css";
@@ -7,6 +7,9 @@ const Reasoning = React.forwardRef(
7
7
  streaming = false,
8
8
  duration,
9
9
  label,
10
+ summary,
11
+ summaryOnly = false,
12
+ size = "default",
10
13
  open,
11
14
  defaultOpen,
12
15
  onOpenChange,
@@ -21,6 +24,14 @@ const Reasoning = React.forwardRef(
21
24
  const isControlled = open !== void 0;
22
25
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen ?? streaming);
23
26
  const isOpen = isControlled ? open : uncontrolledOpen;
27
+ const [leaving, setLeaving] = useState(null);
28
+ const prevSummary = useRef(summary);
29
+ useEffect(() => {
30
+ if (prevSummary.current != null && summary == null) {
31
+ setLeaving(prevSummary.current);
32
+ }
33
+ prevSummary.current = summary;
34
+ }, [summary]);
24
35
  const readerDecided = useRef(false);
25
36
  const wasStreaming = useRef(streaming);
26
37
  useEffect(() => {
@@ -36,13 +47,31 @@ const Reasoning = React.forwardRef(
36
47
  if (!isControlled) setUncontrolledOpen(next);
37
48
  onOpenChange?.(next);
38
49
  }, [isOpen, isControlled, onOpenChange]);
39
- const summary = label ?? (streaming ? "Thinking" : duration !== void 0 ? `Thought for ${duration}s` : "Thought process");
50
+ const summaryText = label ?? (streaming ? "Thinking" : duration !== void 0 ? `Thought for ${duration}s` : "Thought process");
40
51
  const classes = [
41
52
  baseClass,
53
+ `${baseClass}--${size}`,
42
54
  isOpen ? `${baseClass}--open` : "",
43
55
  streaming ? `${baseClass}--streaming` : "",
44
56
  className
45
57
  ].filter(Boolean).join(" ");
58
+ const summaryLine = /* @__PURE__ */ jsxs(
59
+ "span",
60
+ {
61
+ className: `${baseClass}__summary${summary ? ` ${baseClass}__summary--node` : ""}${leaving != null && summary == null ? ` ${baseClass}__summary--entering` : ""}`,
62
+ role: summary ? void 0 : "status",
63
+ onAnimationEnd: (e) => {
64
+ if (e.target === e.currentTarget) setLeaving(null);
65
+ },
66
+ children: [
67
+ leaving != null && summary == null && /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary-leaving`, "aria-hidden": "true", children: leaving }),
68
+ summary ?? summaryText
69
+ ]
70
+ }
71
+ );
72
+ if (summaryOnly) {
73
+ return /* @__PURE__ */ jsx("div", { ...rest, ref, className: classes, children: /* @__PURE__ */ jsx("span", { className: `${baseClass}__trigger ${baseClass}__trigger--static`, children: summaryLine }) });
74
+ }
46
75
  return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, children: [
47
76
  /* @__PURE__ */ jsxs(
48
77
  "button",
@@ -54,7 +83,7 @@ const Reasoning = React.forwardRef(
54
83
  "aria-controls": panelId,
55
84
  onClick: toggle,
56
85
  children: [
57
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__summary`, role: "status", children: summary }),
86
+ summaryLine,
58
87
  /* @__PURE__ */ jsx("span", { className: `${baseClass}__chevron material-symbols-rounded`, "aria-hidden": "true", children: "expand_more" })
59
88
  ]
60
89
  }
@@ -0,0 +1,102 @@
1
+ /* ============================================
2
+ SOURCE CHIP COMPONENT
3
+ Numbered citation pill linking a claim to
4
+ its source. Anchor when href is set, plain
5
+ span otherwise.
6
+ ============================================ */
7
+
8
+ .ds-source-chip {
9
+ /* Layout */
10
+ display: inline-flex;
11
+ align-items: center;
12
+ gap: var(--gap-xs); /* 4px */
13
+
14
+ /* Spacing */
15
+ padding: var(--padding-xxxs) var(--padding-sm); /* 2px 8px */
16
+
17
+ /* Shape — borderless pill on the secondary container */
18
+ border-radius: var(--radius-full);
19
+ background-color: var(--color-bg-container-secondary);
20
+
21
+ /* Typography */
22
+ font-family: var(--font-paragraph-sm-family);
23
+ font-size: var(--font-paragraph-sm-size);
24
+ font-weight: var(--font-paragraph-sm-weight);
25
+ line-height: var(--font-paragraph-sm-line-height);
26
+ letter-spacing: var(--font-paragraph-sm-letter-spacing);
27
+ color: var(--color-text-secondary);
28
+
29
+ /* The pill shape is the affordance — never an underline */
30
+ text-decoration: none;
31
+ cursor: default;
32
+
33
+ transition: background-color var(--motion-duration-fast) var(--motion-ease-standard),
34
+ color var(--motion-duration-fast) var(--motion-ease-standard);
35
+ }
36
+
37
+ /* ============================================
38
+ INDEX — citation numeral in its own circle
39
+ ============================================ */
40
+
41
+ .ds-source-chip__index {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ flex-shrink: 0;
46
+ min-width: var(--icon-size-sm);
47
+ height: var(--icon-size-sm);
48
+ border-radius: var(--radius-full);
49
+ background-color: var(--color-bg-container-tertiary);
50
+
51
+ font-family: var(--font-paragraph-sm-em-family);
52
+ font-size: var(--font-paragraph-sm-em-size);
53
+ font-weight: var(--font-paragraph-sm-em-weight);
54
+ letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
55
+ line-height: 1;
56
+ font-variant-numeric: tabular-nums;
57
+ color: var(--color-text-tertiary);
58
+ }
59
+
60
+ /* ============================================
61
+ ICON (leading, when no index)
62
+ ============================================ */
63
+
64
+ .ds-source-chip__icon {
65
+ flex-shrink: 0;
66
+ --icon-size: var(--icon-size-sm);
67
+ line-height: 1;
68
+ color: var(--color-icon-primary);
69
+ }
70
+
71
+ /* ============================================
72
+ TITLE — truncates with an ellipsis
73
+ ============================================ */
74
+
75
+ .ds-source-chip__title {
76
+ max-width: 24ch;
77
+ overflow: hidden;
78
+ white-space: nowrap;
79
+ text-overflow: ellipsis;
80
+ }
81
+
82
+ /* ============================================
83
+ LINK (rendered as <a>)
84
+ ============================================ */
85
+
86
+ .ds-source-chip--link {
87
+ cursor: pointer;
88
+ }
89
+
90
+ .ds-source-chip--link:hover {
91
+ background-color: var(--color-bg-container-tertiary);
92
+ color: var(--color-text-primary);
93
+ }
94
+
95
+ /* ============================================
96
+ FOCUS
97
+ ============================================ */
98
+
99
+ .ds-source-chip--link:focus-visible {
100
+ outline: 2px solid var(--color-action-primary-bg);
101
+ outline-offset: 2px;
102
+ }
@@ -0,0 +1,32 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by SourceChip itself — everything else falls through to the root element. */
3
+ type SourceChipOwnProps = {
4
+ /** The source name, e.g. "Design tokens quarterly". Truncates with an ellipsis when long. */
5
+ title: string;
6
+ /** Citation number, rendered as a leading numeral in its own small badge circle. */
7
+ index?: number;
8
+ /**
9
+ * Leading icon — a Material Symbol name (string) or custom element (ReactNode).
10
+ * The icon and the index share the leading slot: when both are passed, `index` wins
11
+ * and the icon is not rendered.
12
+ */
13
+ icon?: string | React.ReactNode;
14
+ /** Optional href — renders as an `<a>` instead of a `<span>`. */
15
+ href?: string;
16
+ /** Additional CSS classes */
17
+ className?: string;
18
+ };
19
+ export interface SourceChipProps extends SourceChipOwnProps, Omit<React.ComponentPropsWithoutRef<'a'>, keyof SourceChipOwnProps> {
20
+ }
21
+ /**
22
+ * SourceChip is the numbered citation pill linking a claim to its source.
23
+ * It renders inline after a sentence or in a sources row under an assistant
24
+ * chat message: a leading citation number (or icon) beside a truncating
25
+ * source title.
26
+ *
27
+ * Renders an `<a>` when `href` is supplied, a plain `<span>` otherwise.
28
+ * Forwards a ref to whichever element it renders, and spreads unrecognised
29
+ * props onto it — `target` and `rel` pass through as native anchor attributes.
30
+ */
31
+ export declare const SourceChip: React.ForwardRefExoticComponent<SourceChipProps & React.RefAttributes<HTMLSpanElement | HTMLAnchorElement>>;
32
+ export {};
@@ -0,0 +1,31 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./SourceChip.css";
4
+ import "../../fonts/material-symbols.css";
5
+ const SourceChip = React.forwardRef(
6
+ ({ title, index, icon, href, className = "", ...rest }, ref) => {
7
+ const baseClass = "ds-source-chip";
8
+ const classes = [baseClass, href ? `${baseClass}--link` : "", className].filter(Boolean).join(" ");
9
+ const leading = index !== void 0 ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__index`, children: index }) : icon ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: typeof icon === "string" ? /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", children: icon }) : icon }) : null;
10
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
11
+ leading,
12
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__title`, children: title })
13
+ ] });
14
+ if (href) {
15
+ return /* @__PURE__ */ jsx("a", { ...rest, ref, className: classes, href, children: content });
16
+ }
17
+ return /* @__PURE__ */ jsx(
18
+ "span",
19
+ {
20
+ ...rest,
21
+ ref,
22
+ className: classes,
23
+ children: content
24
+ }
25
+ );
26
+ }
27
+ );
28
+ SourceChip.displayName = "SourceChip";
29
+ export {
30
+ SourceChip
31
+ };
@@ -66,11 +66,11 @@
66
66
  }
67
67
 
68
68
  .ds-stat__delta--up {
69
- color: var(--color-status-positive-text);
69
+ color: var(--color-core-accent-mint);
70
70
  }
71
71
 
72
72
  .ds-stat__delta--down {
73
- color: var(--color-status-error-text);
73
+ color: var(--color-core-accent-coral);
74
74
  }
75
75
 
76
76
  .ds-stat__delta--neutral {
@@ -72,11 +72,11 @@
72
72
  --icon-size: var(--icon-size-sm);
73
73
  }
74
74
 
75
- /* The tool name is an identifier, so it takes the same monospace
76
- context CodeBlock uses — the one place the system leaves Nunito Sans */
75
+ /* The tool name is an identifier, so it takes the code stack —
76
+ the one place the system leaves Nunito Sans */
77
77
  .ds-tool-call__name {
78
78
  flex: none;
79
- font-family: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
79
+ font-family: var(--font-family-code);
80
80
  font-size: var(--font-paragraph-sm-size);
81
81
  line-height: var(--font-paragraph-sm-line-height);
82
82
  color: var(--color-text-primary);