@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.
- package/README.md +2 -2
- package/components/AgentStatus/AgentStatus.css +82 -22
- package/components/AgentStatus/AgentStatus.d.ts +6 -2
- package/components/AgentStatus/AgentStatus.js +38 -6
- package/components/AiButton/AiButton.css +163 -0
- package/components/AiButton/AiButton.d.ts +36 -0
- package/components/AiButton/AiButton.js +58 -0
- package/components/ChatHeader/ChatHeader.css +35 -0
- package/components/ChatHeader/ChatHeader.d.ts +30 -0
- package/components/ChatHeader/ChatHeader.js +17 -0
- package/components/ChatMarker/ChatMarker.css +52 -0
- package/components/ChatMarker/ChatMarker.d.ts +22 -0
- package/components/ChatMarker/ChatMarker.js +18 -0
- package/components/ChatMessage/ChatMessage.css +304 -0
- package/components/ChatMessage/ChatMessage.d.ts +68 -0
- package/components/ChatMessage/ChatMessage.js +69 -0
- package/components/ChatThread/ChatThread.css +171 -0
- package/components/ChatThread/ChatThread.d.ts +41 -0
- package/components/ChatThread/ChatThread.js +174 -0
- package/components/Chip/Chip.css +27 -0
- package/components/Chip/Chip.d.ts +2 -2
- package/components/CircularButton/CircularButton.d.ts +23 -8
- package/components/CircularButton/CircularButton.js +54 -52
- package/components/CodeBlock/CodeBlock.css +2 -2
- package/components/Composer/Composer.css +164 -0
- package/components/Composer/Composer.d.ts +67 -0
- package/components/Composer/Composer.js +120 -0
- package/components/DocumentChip/DocumentChip.css +181 -0
- package/components/DocumentChip/DocumentChip.d.ts +41 -0
- package/components/DocumentChip/DocumentChip.js +78 -0
- package/components/InterruptCard/InterruptCard.css +164 -0
- package/components/InterruptCard/InterruptCard.d.ts +55 -0
- package/components/InterruptCard/InterruptCard.js +57 -0
- package/components/MessageActions/MessageActions.css +76 -0
- package/components/MessageActions/MessageActions.d.ts +38 -0
- package/components/MessageActions/MessageActions.js +33 -0
- package/components/MessageCard/MessageCard.css +112 -0
- package/components/MessageCard/MessageCard.d.ts +35 -0
- package/components/MessageCard/MessageCard.js +28 -0
- package/components/NavList/NavList.css +155 -0
- package/components/NavList/NavList.d.ts +56 -0
- package/components/NavList/NavList.js +99 -0
- package/components/PromptSuggestions/PromptSuggestions.css +87 -0
- package/components/PromptSuggestions/PromptSuggestions.d.ts +51 -0
- package/components/PromptSuggestions/PromptSuggestions.js +34 -0
- package/components/Prose/Prose.css +252 -0
- package/components/Prose/Prose.d.ts +21 -0
- package/components/Prose/Prose.js +14 -0
- package/components/Reasoning/Reasoning.css +149 -25
- package/components/Reasoning/Reasoning.d.ts +19 -0
- package/components/Reasoning/Reasoning.js +32 -3
- package/components/SourceChip/SourceChip.css +102 -0
- package/components/SourceChip/SourceChip.d.ts +32 -0
- package/components/SourceChip/SourceChip.js +31 -0
- package/components/Stat/Stat.css +2 -2
- package/components/ToolCall/ToolCall.css +3 -3
- package/components/registry.json +113 -1
- package/components/registry.json.d.ts +113 -1
- package/components/registry.json.js +1 -1
- package/index.d.ts +14 -0
- package/index.js +28 -0
- package/package.json +1 -1
- package/tokens/registry.json +12 -0
- package/tokens/registry.json.d.ts +12 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +13 -0
- package/tokens/tokens-light.css +42 -0
- 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-
|
|
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-
|
|
30
|
-
font-family: var(--font-paragraph-
|
|
31
|
-
font-size: var(--font-paragraph-
|
|
32
|
-
font-weight: var(--font-paragraph-
|
|
33
|
-
line-height: var(--font-paragraph-
|
|
34
|
-
letter-spacing: var(--font-paragraph-
|
|
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(
|
|
75
|
+
transform: rotate(0deg);
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
/* ============================================
|
|
60
79
|
SUMMARY
|
|
61
|
-
While the trace streams, a
|
|
62
|
-
|
|
63
|
-
AgentStatus uses
|
|
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
|
-
|
|
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-
|
|
75
|
-
var(--color-text-
|
|
76
|
-
var(--color-text-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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-
|
|
124
|
-
font-size: var(--font-paragraph-
|
|
125
|
-
font-weight: var(--font-paragraph-
|
|
126
|
-
line-height: var(--font-paragraph-
|
|
127
|
-
letter-spacing: var(--font-paragraph-
|
|
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-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
+
};
|
package/components/Stat/Stat.css
CHANGED
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
.ds-stat__delta--up {
|
|
69
|
-
color: var(--color-
|
|
69
|
+
color: var(--color-core-accent-mint);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.ds-stat__delta--down {
|
|
73
|
-
color: var(--color-
|
|
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
|
|
76
|
-
|
|
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:
|
|
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);
|