@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.
- package/README.md +30 -27
- package/components/AgentStatus/AgentStatus.css +496 -0
- package/components/AgentStatus/AgentStatus.d.ts +41 -0
- package/components/AgentStatus/AgentStatus.js +85 -0
- package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
- package/components/AgentStatus/AgentStatusPatterns.js +20 -0
- package/components/AiButton/AiButton.css +163 -0
- package/components/AiButton/AiButton.d.ts +36 -0
- package/components/AiButton/AiButton.js +58 -0
- package/components/AlertDialog/AlertDialog.css +5 -5
- package/components/Button/Button.d.ts +14 -2
- package/components/ButtonGroup/ButtonGroup.js +2 -9
- 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/Checkbox/Checkbox.d.ts +17 -3
- package/components/Chip/Chip.css +27 -0
- package/components/Chip/Chip.d.ts +2 -2
- package/components/CircularButton/CircularButton.d.ts +31 -10
- package/components/CircularButton/CircularButton.js +54 -50
- package/components/CodeBlock/CodeBlock.css +2 -2
- package/components/Combobox/Combobox.d.ts +10 -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/ContextMenu/ContextMenu.css +0 -4
- package/components/DateInput/DateInput.d.ts +5 -1
- package/components/Dialog/Dialog.css +5 -5
- package/components/DocumentChip/DocumentChip.css +181 -0
- package/components/DocumentChip/DocumentChip.d.ts +41 -0
- package/components/DocumentChip/DocumentChip.js +78 -0
- package/components/Dropdown/Dropdown.css +2 -4
- package/components/Dropdown/Dropdown.d.ts +10 -2
- package/components/DropdownMenu/DropdownMenu.css +2 -4
- package/components/FileInput/FileInput.d.ts +5 -1
- package/components/Input/Input.d.ts +5 -1
- package/components/InterruptCard/InterruptCard.css +164 -0
- package/components/InterruptCard/InterruptCard.d.ts +55 -0
- package/components/InterruptCard/InterruptCard.js +57 -0
- package/components/LinkList/LinkList.d.ts +1 -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/RadioButton/RadioButton.d.ts +17 -3
- package/components/Reasoning/Reasoning.css +276 -0
- package/components/Reasoning/Reasoning.d.ts +55 -0
- package/components/Reasoning/Reasoning.js +98 -0
- package/components/SelectionCard/SelectionCard.d.ts +5 -1
- package/components/Slider/Slider.d.ts +5 -1
- 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/Textarea/Textarea.d.ts +5 -1
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
- package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
- package/components/ToolCall/ToolCall.css +213 -0
- package/components/ToolCall/ToolCall.d.ts +43 -0
- package/components/ToolCall/ToolCall.js +89 -0
- package/components/registry.json +145 -8
- package/components/registry.json.d.ts +145 -8
- package/components/registry.json.js +2 -2
- package/index.d.ts +18 -0
- package/index.js +36 -0
- package/package.json +8 -4
- 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 +42 -0
- package/tokens/tokens-motion.css +3 -1
- 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-
|
|
85
|
-
font-size: var(--font-heading-
|
|
86
|
-
font-weight: var(--font-heading-
|
|
87
|
-
line-height: var(--font-heading-
|
|
88
|
-
letter-spacing: var(--font-heading-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
21
|
-
|
|
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
|
+
};
|