@robr0/design-system 0.4.0 → 0.6.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 +21 -10
- 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 +310 -0
- package/components/ChatMessage/ChatMessage.d.ts +74 -0
- package/components/ChatMessage/ChatMessage.js +71 -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 +163 -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/Timeline/Timeline.css +9 -1
- 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 +13 -0
- package/tokens/registry.json.d.ts +13 -0
- package/tokens/registry.json.js +1 -1
- package/tokens/tokens-dark.css +13 -0
- package/tokens/tokens-light.css +43 -0
- package/tokens/tokens-primitives.css +5 -0
- package/tokens/tokens-typography.css +24 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
PROSE COMPONENT
|
|
3
|
+
Token-styled typography for rendered markdown
|
|
4
|
+
============================================ */
|
|
5
|
+
|
|
6
|
+
.ds-prose {
|
|
7
|
+
font-family: var(--font-paragraph-family);
|
|
8
|
+
font-size: var(--font-paragraph-size);
|
|
9
|
+
font-weight: var(--font-paragraph-weight);
|
|
10
|
+
line-height: var(--font-paragraph-line-height);
|
|
11
|
+
letter-spacing: var(--font-paragraph-letter-spacing);
|
|
12
|
+
color: var(--color-text-primary);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Block rhythm — every block owns its bottom margin; the edges are
|
|
16
|
+
trimmed so Prose slots into bubbles and cards cleanly */
|
|
17
|
+
.ds-prose p,
|
|
18
|
+
.ds-prose ul,
|
|
19
|
+
.ds-prose ol,
|
|
20
|
+
.ds-prose pre,
|
|
21
|
+
.ds-prose blockquote,
|
|
22
|
+
.ds-prose table,
|
|
23
|
+
.ds-prose hr,
|
|
24
|
+
.ds-prose img {
|
|
25
|
+
margin: 0;
|
|
26
|
+
margin-block-end: var(--gap-md);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ds-prose > :first-child {
|
|
30
|
+
margin-block-start: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ds-prose > :last-child {
|
|
34
|
+
margin-block-end: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ============================================
|
|
38
|
+
PARAGRAPHS
|
|
39
|
+
============================================ */
|
|
40
|
+
|
|
41
|
+
.ds-prose p {
|
|
42
|
+
font-family: var(--font-paragraph-family);
|
|
43
|
+
font-size: var(--font-paragraph-size);
|
|
44
|
+
font-weight: var(--font-paragraph-weight);
|
|
45
|
+
line-height: var(--font-paragraph-line-height);
|
|
46
|
+
letter-spacing: var(--font-paragraph-letter-spacing);
|
|
47
|
+
color: var(--color-text-primary);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* ============================================
|
|
51
|
+
HEADINGS
|
|
52
|
+
Hierarchy carried by the token scale; the
|
|
53
|
+
weight contrast is encoded in the tokens
|
|
54
|
+
============================================ */
|
|
55
|
+
|
|
56
|
+
.ds-prose h1,
|
|
57
|
+
.ds-prose h2,
|
|
58
|
+
.ds-prose h3,
|
|
59
|
+
.ds-prose h4 {
|
|
60
|
+
margin: 0;
|
|
61
|
+
margin-block-start: var(--gap-lg);
|
|
62
|
+
margin-block-end: var(--gap-sm-md);
|
|
63
|
+
color: var(--color-text-primary);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.ds-prose h1 {
|
|
67
|
+
font-family: var(--font-heading-1-family);
|
|
68
|
+
font-size: var(--font-heading-1-size);
|
|
69
|
+
font-weight: var(--font-heading-1-weight);
|
|
70
|
+
line-height: var(--font-heading-1-line-height);
|
|
71
|
+
letter-spacing: var(--font-heading-1-letter-spacing);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ds-prose h2 {
|
|
75
|
+
font-family: var(--font-heading-2-family);
|
|
76
|
+
font-size: var(--font-heading-2-size);
|
|
77
|
+
font-weight: var(--font-heading-2-weight);
|
|
78
|
+
line-height: var(--font-heading-2-line-height);
|
|
79
|
+
letter-spacing: var(--font-heading-2-letter-spacing);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.ds-prose h3 {
|
|
83
|
+
font-family: var(--font-heading-3-family);
|
|
84
|
+
font-size: var(--font-heading-3-size);
|
|
85
|
+
font-weight: var(--font-heading-3-weight);
|
|
86
|
+
line-height: var(--font-heading-3-line-height);
|
|
87
|
+
letter-spacing: var(--font-heading-3-letter-spacing);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ds-prose h4 {
|
|
91
|
+
font-family: var(--font-title-body-family);
|
|
92
|
+
font-size: var(--font-title-body-size);
|
|
93
|
+
font-weight: var(--font-title-body-weight);
|
|
94
|
+
line-height: var(--font-title-body-line-height);
|
|
95
|
+
letter-spacing: var(--font-title-body-letter-spacing);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ============================================
|
|
99
|
+
LISTS
|
|
100
|
+
============================================ */
|
|
101
|
+
|
|
102
|
+
.ds-prose ul,
|
|
103
|
+
.ds-prose ol {
|
|
104
|
+
padding-inline-start: var(--padding-md);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ds-prose li {
|
|
108
|
+
font-family: var(--font-paragraph-family);
|
|
109
|
+
font-size: var(--font-paragraph-size);
|
|
110
|
+
font-weight: var(--font-paragraph-weight);
|
|
111
|
+
line-height: var(--font-paragraph-line-height);
|
|
112
|
+
letter-spacing: var(--font-paragraph-letter-spacing);
|
|
113
|
+
color: var(--color-text-primary);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ds-prose li + li {
|
|
117
|
+
margin-block-start: var(--gap-xs);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Nested lists sit tight under their parent item */
|
|
121
|
+
.ds-prose li > ul,
|
|
122
|
+
.ds-prose li > ol {
|
|
123
|
+
margin-block-start: var(--gap-xs);
|
|
124
|
+
margin-block-end: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ============================================
|
|
128
|
+
INLINE ELEMENTS
|
|
129
|
+
============================================ */
|
|
130
|
+
|
|
131
|
+
.ds-prose a {
|
|
132
|
+
color: var(--color-action-primary-text-tertiary);
|
|
133
|
+
text-decoration: underline;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.ds-prose strong {
|
|
137
|
+
font-weight: var(--font-paragraph-em-weight);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.ds-prose em {
|
|
141
|
+
font-style: italic;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.ds-prose code {
|
|
145
|
+
font-family: var(--font-family-code);
|
|
146
|
+
font-size: var(--font-paragraph-sm-size);
|
|
147
|
+
background-color: var(--color-bg-container-secondary);
|
|
148
|
+
border-radius: var(--radius-xxs);
|
|
149
|
+
padding-inline: var(--padding-xxxs);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ============================================
|
|
153
|
+
CODE BLOCKS
|
|
154
|
+
The plain fallback — CodeBlock is the richer
|
|
155
|
+
choice for standalone code
|
|
156
|
+
============================================ */
|
|
157
|
+
|
|
158
|
+
.ds-prose pre {
|
|
159
|
+
font-family: var(--font-family-code);
|
|
160
|
+
font-size: var(--font-paragraph-sm-size);
|
|
161
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
162
|
+
background-color: var(--color-bg-container-primary);
|
|
163
|
+
border: var(--border-xs) solid var(--color-bg-container-border);
|
|
164
|
+
border-radius: var(--radius-md);
|
|
165
|
+
padding: var(--padding-md);
|
|
166
|
+
overflow-x: auto;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.ds-prose pre code {
|
|
170
|
+
background-color: transparent;
|
|
171
|
+
border-radius: 0;
|
|
172
|
+
padding-inline: 0;
|
|
173
|
+
font-size: inherit;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ============================================
|
|
177
|
+
BLOCKQUOTES
|
|
178
|
+
Mirrors Reasoning's trace rail
|
|
179
|
+
============================================ */
|
|
180
|
+
|
|
181
|
+
.ds-prose blockquote {
|
|
182
|
+
border-inline-start: var(--border-md) solid var(--color-divider);
|
|
183
|
+
padding-inline-start: var(--padding-md);
|
|
184
|
+
color: var(--color-text-secondary);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.ds-prose blockquote p {
|
|
188
|
+
color: var(--color-text-secondary);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* ============================================
|
|
192
|
+
TABLES
|
|
193
|
+
============================================ */
|
|
194
|
+
|
|
195
|
+
.ds-prose table {
|
|
196
|
+
width: 100%;
|
|
197
|
+
border-collapse: collapse;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ds-prose th {
|
|
201
|
+
font-family: var(--font-title-body-family);
|
|
202
|
+
font-size: var(--font-paragraph-sm-size);
|
|
203
|
+
font-weight: var(--font-title-body-weight);
|
|
204
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
205
|
+
letter-spacing: var(--font-title-body-letter-spacing);
|
|
206
|
+
background-color: var(--color-bg-container-secondary);
|
|
207
|
+
text-align: start;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.ds-prose th,
|
|
211
|
+
.ds-prose td {
|
|
212
|
+
padding: var(--padding-xs) var(--padding-sm-md);
|
|
213
|
+
border-block-end: var(--border-xs) solid var(--color-divider);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ============================================
|
|
217
|
+
RULES AND MEDIA
|
|
218
|
+
============================================ */
|
|
219
|
+
|
|
220
|
+
.ds-prose hr {
|
|
221
|
+
border: none;
|
|
222
|
+
border-block-start: var(--border-xs) solid var(--color-divider);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.ds-prose img {
|
|
226
|
+
max-width: 100%;
|
|
227
|
+
border-radius: var(--radius-md);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ============================================
|
|
231
|
+
SMALL SIZE
|
|
232
|
+
Dense chat contexts — body scale drops to the
|
|
233
|
+
small paragraph tokens; headings keep their tiers
|
|
234
|
+
============================================ */
|
|
235
|
+
|
|
236
|
+
.ds-prose--sm,
|
|
237
|
+
.ds-prose--sm p,
|
|
238
|
+
.ds-prose--sm li {
|
|
239
|
+
font-family: var(--font-paragraph-sm-family);
|
|
240
|
+
font-size: var(--font-paragraph-sm-size);
|
|
241
|
+
font-weight: var(--font-paragraph-sm-weight);
|
|
242
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
243
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.ds-prose--sm strong {
|
|
247
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.ds-prose--sm code {
|
|
251
|
+
font-size: var(--font-paragraph-sm-size);
|
|
252
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by Prose itself — everything else falls through to the root element. */
|
|
3
|
+
type ProseOwnProps = {
|
|
4
|
+
/** Body scale. `sm` maps paragraphs, lists and code to the small paragraph tokens for dense chat contexts. */
|
|
5
|
+
size?: 'default' | 'sm';
|
|
6
|
+
/** Additional CSS classes */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** The rendered markup to style — the output of whatever markdown renderer the consumer uses. */
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export interface ProseProps extends ProseOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof ProseOwnProps> {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Prose is the typography kit for rendered markdown and rich agent output.
|
|
15
|
+
* The package ships no markdown renderer: consumers render markdown with
|
|
16
|
+
* whatever library they already use and wrap the result in Prose, which
|
|
17
|
+
* styles the descendant elements — headings, paragraphs, lists, links,
|
|
18
|
+
* code, quotes, tables — with the token scale.
|
|
19
|
+
*/
|
|
20
|
+
export declare const Prose: React.ForwardRefExoticComponent<ProseProps & React.RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import "./Prose.css";
|
|
4
|
+
const Prose = React.forwardRef(
|
|
5
|
+
({ size = "default", className = "", children, ...rest }, ref) => {
|
|
6
|
+
const baseClass = "ds-prose";
|
|
7
|
+
const classes = [baseClass, size === "sm" ? `${baseClass}--sm` : "", className].filter(Boolean).join(" ");
|
|
8
|
+
return /* @__PURE__ */ jsx("div", { ...rest, ref, className: classes, children });
|
|
9
|
+
}
|
|
10
|
+
);
|
|
11
|
+
Prose.displayName = "Prose";
|
|
12
|
+
export {
|
|
13
|
+
Prose
|
|
14
|
+
};
|
|
@@ -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
|
}
|