@robr0/design-system 0.7.0 → 0.9.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 +4 -4
- package/components/Accordion/Accordion.js +1 -0
- package/components/AgentPlan/AgentPlan.css +231 -0
- package/components/AgentPlan/AgentPlan.d.ts +41 -0
- package/components/AgentPlan/AgentPlan.js +100 -0
- package/components/AgentStatus/AgentStatus.js +1 -0
- package/components/AiButton/AiButton.js +1 -0
- package/components/AlertDialog/AlertDialog.js +1 -0
- package/components/AnchorNav/AnchorNav.css +63 -0
- package/components/AnchorNav/AnchorNav.d.ts +33 -0
- package/components/AnchorNav/AnchorNav.js +82 -0
- package/components/AppLayout/AppLayout.js +1 -0
- package/components/AppSidebar/AppSidebar.js +1 -0
- package/components/Avatar/Avatar.js +1 -0
- package/components/Button/Button.js +1 -0
- package/components/Card/Card.js +1 -0
- package/components/Carousel/Carousel.js +1 -0
- package/components/ChatThread/ChatThread.js +1 -0
- package/components/Checkbox/Checkbox.js +1 -0
- package/components/CodeBlock/CodeBlock.js +1 -0
- package/components/CodeDiff/CodeDiff.css +175 -0
- package/components/CodeDiff/CodeDiff.d.ts +58 -0
- package/components/CodeDiff/CodeDiff.js +114 -0
- package/components/ColorPicker/ColorPicker.js +1 -0
- package/components/Combobox/Combobox.js +1 -0
- package/components/CommandPalette/CommandPalette.js +1 -0
- package/components/Composer/Composer.js +1 -0
- package/components/ContactCard/ContactCard.js +1 -0
- package/components/ContextMenu/ContextMenu.js +1 -0
- package/components/DataTable/DataTable.css +105 -0
- package/components/DataTable/DataTable.d.ts +81 -0
- package/components/DataTable/DataTable.js +236 -0
- package/components/DateInput/DateInput.js +1 -0
- package/components/DatePicker/DatePicker.js +1 -0
- package/components/Dialog/Dialog.js +1 -0
- package/components/Drawer/Drawer.js +1 -0
- package/components/Dropdown/Dropdown.js +1 -0
- package/components/DropdownMenu/DropdownMenu.css +1 -1
- package/components/DropdownMenu/DropdownMenu.js +1 -0
- package/components/EventCalendar/EventCalendar.css +328 -0
- package/components/EventCalendar/EventCalendar.d.ts +51 -0
- package/components/EventCalendar/EventCalendar.js +214 -0
- package/components/Field/Field.js +1 -0
- package/components/Field/FieldContext.js +1 -0
- package/components/FileInput/FileInput.js +1 -0
- package/components/Input/Input.js +1 -0
- package/components/InterruptCard/InterruptCard.css +11 -21
- package/components/InterruptCard/InterruptCard.d.ts +5 -6
- package/components/MessageCard/MessageCard.css +1 -2
- package/components/ModelPicker/ModelPicker.css +240 -0
- package/components/ModelPicker/ModelPicker.d.ts +60 -0
- package/components/ModelPicker/ModelPicker.js +218 -0
- package/components/NavList/NavList.js +1 -0
- package/components/NotificationCenter/NotificationCenter.css +262 -0
- package/components/NotificationCenter/NotificationCenter.d.ts +72 -0
- package/components/NotificationCenter/NotificationCenter.js +129 -0
- package/components/NumberInput/NumberInput.css +154 -0
- package/components/NumberInput/NumberInput.d.ts +44 -0
- package/components/NumberInput/NumberInput.js +147 -0
- package/components/Pagination/Pagination.js +1 -0
- package/components/PinInput/PinInput.css +92 -0
- package/components/PinInput/PinInput.d.ts +46 -0
- package/components/PinInput/PinInput.js +161 -0
- package/components/Popover/Popover.css +5 -6
- package/components/Popover/Popover.js +1 -0
- package/components/RadioButton/RadioButton.js +1 -0
- package/components/Reasoning/Reasoning.js +1 -0
- package/components/SegmentedControl/SegmentedControl.js +1 -0
- package/components/SelectionCard/SelectionCard.js +1 -0
- package/components/ShaderField/ShaderField.js +1 -0
- package/components/ShaderField/field.glsl.d.ts +11 -5
- package/components/ShaderField/field.glsl.js +4 -3
- package/components/ShaderField/useShaderField.d.ts +19 -2
- package/components/ShaderField/useShaderField.js +18 -3
- package/components/Slider/Slider.js +1 -0
- package/components/Sparkline/Sparkline.css +52 -0
- package/components/Sparkline/Sparkline.d.ts +49 -0
- package/components/Sparkline/Sparkline.js +86 -0
- package/components/Stepper/Stepper.css +207 -0
- package/components/Stepper/Stepper.d.ts +41 -0
- package/components/Stepper/Stepper.js +61 -0
- package/components/Tabs/Tabs.js +1 -0
- package/components/TagInput/TagInput.css +160 -0
- package/components/TagInput/TagInput.d.ts +43 -0
- package/components/TagInput/TagInput.js +137 -0
- package/components/Textarea/Textarea.js +1 -0
- package/components/TimePicker/TimePicker.css +230 -0
- package/components/TimePicker/TimePicker.d.ts +46 -0
- package/components/TimePicker/TimePicker.js +246 -0
- package/components/Timeline/Timeline.css +17 -0
- package/components/Timeline/Timeline.d.ts +2 -0
- package/components/Timeline/Timeline.js +1 -0
- package/components/Toast/Toast.js +1 -0
- package/components/ToggleGroup/ToggleGroup.js +1 -0
- package/components/ToggleSwitch/ToggleSwitch.js +1 -0
- package/components/ToolCall/ToolCall.css +7 -11
- package/components/ToolCall/ToolCall.js +1 -0
- package/components/Tooltip/Tooltip.js +1 -0
- package/components/TreeView/TreeView.css +119 -0
- package/components/TreeView/TreeView.d.ts +45 -0
- package/components/TreeView/TreeView.js +232 -0
- package/components/registry.d.ts +22 -6
- package/components/registry.js +3 -1
- package/components/registry.json +246 -20
- package/components/registry.json.d.ts +246 -20
- package/components/registry.json.js +2 -2
- package/index.d.ts +16 -2
- package/index.js +32 -1
- package/package.json +1 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useMemo, useState } from "react";
|
|
4
|
+
import "./EventCalendar.css";
|
|
5
|
+
import "../../fonts/material-symbols.css";
|
|
6
|
+
const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
7
|
+
const MONTHS = [
|
|
8
|
+
"January",
|
|
9
|
+
"February",
|
|
10
|
+
"March",
|
|
11
|
+
"April",
|
|
12
|
+
"May",
|
|
13
|
+
"June",
|
|
14
|
+
"July",
|
|
15
|
+
"August",
|
|
16
|
+
"September",
|
|
17
|
+
"October",
|
|
18
|
+
"November",
|
|
19
|
+
"December"
|
|
20
|
+
];
|
|
21
|
+
function parseMonth(str) {
|
|
22
|
+
const [y, m] = str.split("-").map(Number);
|
|
23
|
+
return [y, m - 1];
|
|
24
|
+
}
|
|
25
|
+
function formatMonth(year, monthIndex) {
|
|
26
|
+
return `${year}-${String(monthIndex + 1).padStart(2, "0")}`;
|
|
27
|
+
}
|
|
28
|
+
function formatDate(date) {
|
|
29
|
+
const y = date.getFullYear();
|
|
30
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
31
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
32
|
+
return `${y}-${m}-${d}`;
|
|
33
|
+
}
|
|
34
|
+
const EventCalendar = React.forwardRef(
|
|
35
|
+
({
|
|
36
|
+
events = [],
|
|
37
|
+
month,
|
|
38
|
+
defaultMonth,
|
|
39
|
+
onMonthChange,
|
|
40
|
+
maxEventsPerDay = 3,
|
|
41
|
+
onEventClick,
|
|
42
|
+
onDateClick,
|
|
43
|
+
actions,
|
|
44
|
+
className = "",
|
|
45
|
+
...rest
|
|
46
|
+
}, ref) => {
|
|
47
|
+
const baseClass = "ds-event-calendar";
|
|
48
|
+
const today = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
49
|
+
const isControlled = month !== void 0;
|
|
50
|
+
const [uncontrolledMonth, setUncontrolledMonth] = useState(
|
|
51
|
+
defaultMonth ?? formatMonth(today.getFullYear(), today.getMonth())
|
|
52
|
+
);
|
|
53
|
+
const currentMonth = isControlled ? month : uncontrolledMonth;
|
|
54
|
+
const [viewYear, viewMonth] = parseMonth(currentMonth);
|
|
55
|
+
const navigate = (offset) => {
|
|
56
|
+
const next = new Date(viewYear, viewMonth + offset, 1);
|
|
57
|
+
const value = formatMonth(next.getFullYear(), next.getMonth());
|
|
58
|
+
if (!isControlled) setUncontrolledMonth(value);
|
|
59
|
+
onMonthChange?.(value);
|
|
60
|
+
};
|
|
61
|
+
const eventsByDate = useMemo(() => {
|
|
62
|
+
const map = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const event of events) {
|
|
64
|
+
const list = map.get(event.date) ?? [];
|
|
65
|
+
list.push(event);
|
|
66
|
+
map.set(event.date, list);
|
|
67
|
+
}
|
|
68
|
+
for (const list of map.values()) {
|
|
69
|
+
list.sort((a, b) => {
|
|
70
|
+
if (!a.time && !b.time) return 0;
|
|
71
|
+
if (!a.time) return -1;
|
|
72
|
+
if (!b.time) return 1;
|
|
73
|
+
return a.time.localeCompare(b.time);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return map;
|
|
77
|
+
}, [events]);
|
|
78
|
+
const firstDay = new Date(viewYear, viewMonth, 1).getDay();
|
|
79
|
+
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
|
|
80
|
+
const weekCount = Math.ceil((firstDay + daysInMonth) / 7);
|
|
81
|
+
const cells = [];
|
|
82
|
+
for (let i = 0; i < weekCount * 7; i++) {
|
|
83
|
+
const date = new Date(viewYear, viewMonth, i - firstDay + 1);
|
|
84
|
+
cells.push({ date, inMonth: date.getMonth() === viewMonth });
|
|
85
|
+
}
|
|
86
|
+
const classes = [baseClass, className].filter(Boolean).join(" ");
|
|
87
|
+
return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, children: [
|
|
88
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("span", { className: `${baseClass}__month`, children: [
|
|
90
|
+
MONTHS[viewMonth],
|
|
91
|
+
" ",
|
|
92
|
+
viewYear
|
|
93
|
+
] }),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__nav`, children: [
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "button",
|
|
99
|
+
className: `${baseClass}__nav-btn material-symbols-rounded`,
|
|
100
|
+
onClick: () => navigate(-1),
|
|
101
|
+
"aria-label": "Previous month",
|
|
102
|
+
children: "chevron_left"
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
/* @__PURE__ */ jsx(
|
|
106
|
+
"button",
|
|
107
|
+
{
|
|
108
|
+
type: "button",
|
|
109
|
+
className: `${baseClass}__nav-btn material-symbols-rounded`,
|
|
110
|
+
onClick: () => navigate(1),
|
|
111
|
+
"aria-label": "Next month",
|
|
112
|
+
children: "chevron_right"
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
] }),
|
|
116
|
+
actions && /* @__PURE__ */ jsx("div", { className: `${baseClass}__actions`, children: actions })
|
|
117
|
+
] }),
|
|
118
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__weekdays`, "aria-hidden": "true", children: DAYS.map((day) => /* @__PURE__ */ jsx("div", { className: `${baseClass}__weekday`, children: day }, day)) }),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
"div",
|
|
121
|
+
{
|
|
122
|
+
className: `${baseClass}__grid`,
|
|
123
|
+
role: "group",
|
|
124
|
+
"aria-label": `${MONTHS[viewMonth]} ${viewYear}`,
|
|
125
|
+
children: cells.map(({ date, inMonth }) => {
|
|
126
|
+
const dateStr = formatDate(date);
|
|
127
|
+
const dayEvents = eventsByDate.get(dateStr) ?? [];
|
|
128
|
+
const visible = dayEvents.slice(0, maxEventsPerDay);
|
|
129
|
+
const overflow = dayEvents.length - visible.length;
|
|
130
|
+
const isToday = date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate();
|
|
131
|
+
const dayLabel = date.toLocaleDateString(void 0, {
|
|
132
|
+
weekday: "long",
|
|
133
|
+
year: "numeric",
|
|
134
|
+
month: "long",
|
|
135
|
+
day: "numeric"
|
|
136
|
+
});
|
|
137
|
+
const cellClasses = [
|
|
138
|
+
`${baseClass}__cell`,
|
|
139
|
+
!inMonth ? `${baseClass}__cell--outside` : "",
|
|
140
|
+
isToday ? `${baseClass}__cell--today` : ""
|
|
141
|
+
].filter(Boolean).join(" ");
|
|
142
|
+
return /* @__PURE__ */ jsxs("div", { className: cellClasses, children: [
|
|
143
|
+
onDateClick ? /* @__PURE__ */ jsx(
|
|
144
|
+
"button",
|
|
145
|
+
{
|
|
146
|
+
type: "button",
|
|
147
|
+
className: `${baseClass}__day`,
|
|
148
|
+
onClick: () => onDateClick(dateStr),
|
|
149
|
+
"aria-label": dayLabel,
|
|
150
|
+
"aria-current": isToday ? "date" : void 0,
|
|
151
|
+
children: date.getDate()
|
|
152
|
+
}
|
|
153
|
+
) : /* @__PURE__ */ jsx(
|
|
154
|
+
"span",
|
|
155
|
+
{
|
|
156
|
+
className: `${baseClass}__day`,
|
|
157
|
+
"aria-label": dayLabel,
|
|
158
|
+
"aria-current": isToday ? "date" : void 0,
|
|
159
|
+
children: date.getDate()
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__events`, children: [
|
|
163
|
+
visible.map((event) => {
|
|
164
|
+
const pillClasses = [
|
|
165
|
+
`${baseClass}__event`,
|
|
166
|
+
`${baseClass}__event--${event.color ?? "neutral"}`
|
|
167
|
+
].join(" ");
|
|
168
|
+
const pillContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
169
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__event-dot`, "aria-hidden": "true" }),
|
|
170
|
+
/* @__PURE__ */ jsx("span", { className: `${baseClass}__event-title`, children: event.title }),
|
|
171
|
+
event.time && /* @__PURE__ */ jsx("span", { className: `${baseClass}__event-time`, children: event.time })
|
|
172
|
+
] });
|
|
173
|
+
return onEventClick ? /* @__PURE__ */ jsx(
|
|
174
|
+
"button",
|
|
175
|
+
{
|
|
176
|
+
type: "button",
|
|
177
|
+
className: pillClasses,
|
|
178
|
+
onClick: () => onEventClick(event),
|
|
179
|
+
"aria-label": `${event.title}${event.time ? `, ${event.time}` : ""}, ${dayLabel}`,
|
|
180
|
+
children: pillContent
|
|
181
|
+
},
|
|
182
|
+
event.id
|
|
183
|
+
) : /* @__PURE__ */ jsx("span", { className: pillClasses, children: pillContent }, event.id);
|
|
184
|
+
}),
|
|
185
|
+
overflow > 0 && (onDateClick ? /* @__PURE__ */ jsxs(
|
|
186
|
+
"button",
|
|
187
|
+
{
|
|
188
|
+
type: "button",
|
|
189
|
+
className: `${baseClass}__more`,
|
|
190
|
+
onClick: () => onDateClick(dateStr),
|
|
191
|
+
"aria-label": `${overflow} more event${overflow === 1 ? "" : "s"}, ${dayLabel}`,
|
|
192
|
+
children: [
|
|
193
|
+
"+",
|
|
194
|
+
overflow,
|
|
195
|
+
" more"
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
) : /* @__PURE__ */ jsxs("span", { className: `${baseClass}__more`, children: [
|
|
199
|
+
"+",
|
|
200
|
+
overflow,
|
|
201
|
+
" more"
|
|
202
|
+
] }))
|
|
203
|
+
] })
|
|
204
|
+
] }, dateStr);
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] });
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
EventCalendar.displayName = "EventCalendar";
|
|
212
|
+
export {
|
|
213
|
+
EventCalendar
|
|
214
|
+
};
|
|
@@ -4,33 +4,25 @@
|
|
|
4
4
|
from the agent, a detail slot, and option
|
|
5
5
|
buttons in a footer.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
card drops to monochrome.
|
|
7
|
+
The card is deliberately quiet — one tone,
|
|
8
|
+
the standard container border, no status
|
|
9
|
+
colour. The question and its buttons are
|
|
10
|
+
the signal; the visually hidden status
|
|
11
|
+
announcement carries the waiting state.
|
|
13
12
|
============================================ */
|
|
14
13
|
|
|
15
14
|
.ds-interrupt-card {
|
|
16
|
-
--ds-interrupt-card-color: var(--color-
|
|
17
|
-
--ds-interrupt-card-border: var(--color-status-warning-border);
|
|
15
|
+
--ds-interrupt-card-color: var(--color-icon-secondary);
|
|
18
16
|
|
|
19
17
|
display: flex;
|
|
20
18
|
flex-direction: column;
|
|
21
19
|
width: 100%;
|
|
22
20
|
background-color: var(--color-bg-container-primary);
|
|
23
|
-
border: var(--border-xs) solid var(--
|
|
21
|
+
border: var(--border-xs) solid var(--color-bg-container-border);
|
|
24
22
|
border-radius: var(--radius-md);
|
|
25
23
|
overflow: hidden;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
/* Decision made — no signal needed */
|
|
29
|
-
.ds-interrupt-card--answered {
|
|
30
|
-
--ds-interrupt-card-color: var(--color-text-secondary);
|
|
31
|
-
--ds-interrupt-card-border: var(--color-bg-container-border);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
26
|
/* ============================================
|
|
35
27
|
HEADER
|
|
36
28
|
============================================ */
|
|
@@ -39,7 +31,7 @@
|
|
|
39
31
|
display: flex;
|
|
40
32
|
align-items: flex-start;
|
|
41
33
|
gap: var(--gap-sm);
|
|
42
|
-
padding: var(--padding-sm) var(--padding-
|
|
34
|
+
padding: var(--padding-sm-md) var(--padding-md);
|
|
43
35
|
}
|
|
44
36
|
|
|
45
37
|
.ds-interrupt-card__icon {
|
|
@@ -88,7 +80,7 @@
|
|
|
88
80
|
============================================ */
|
|
89
81
|
|
|
90
82
|
.ds-interrupt-card__detail {
|
|
91
|
-
padding: 0 var(--padding-
|
|
83
|
+
padding: 0 var(--padding-md) var(--padding-sm-md);
|
|
92
84
|
}
|
|
93
85
|
|
|
94
86
|
.ds-interrupt-card__detail > :first-child {
|
|
@@ -111,9 +103,8 @@
|
|
|
111
103
|
align-items: center;
|
|
112
104
|
justify-content: flex-end;
|
|
113
105
|
gap: var(--gap-sm);
|
|
114
|
-
padding: var(--padding-sm) var(--padding-
|
|
106
|
+
padding: var(--padding-sm-md) var(--padding-md);
|
|
115
107
|
border-top: var(--border-xs) solid var(--color-divider);
|
|
116
|
-
background-color: var(--color-bg-container-secondary);
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
/* ============================================
|
|
@@ -127,9 +118,8 @@
|
|
|
127
118
|
align-items: center;
|
|
128
119
|
justify-content: flex-end;
|
|
129
120
|
gap: var(--gap-xs);
|
|
130
|
-
padding: var(--padding-sm) var(--padding-
|
|
121
|
+
padding: var(--padding-sm-md) var(--padding-md);
|
|
131
122
|
border-top: var(--border-xs) solid var(--color-divider);
|
|
132
|
-
background-color: var(--color-bg-container-secondary);
|
|
133
123
|
font-family: var(--font-paragraph-sm-family);
|
|
134
124
|
font-size: var(--font-paragraph-sm-size);
|
|
135
125
|
font-weight: var(--font-paragraph-sm-weight);
|
|
@@ -22,8 +22,7 @@ type InterruptCardOwnProps = {
|
|
|
22
22
|
onValueChange?: (value: string) => void;
|
|
23
23
|
/**
|
|
24
24
|
* The already-chosen value. When set, the card renders its answered state:
|
|
25
|
-
* the
|
|
26
|
-
* echo of the chosen label.
|
|
25
|
+
* the options are replaced by a quiet echo of the chosen label.
|
|
27
26
|
*/
|
|
28
27
|
value?: string;
|
|
29
28
|
/** Override the echoed text in the answered state (default: the chosen option's label). */
|
|
@@ -43,10 +42,10 @@ export interface InterruptCardProps extends InterruptCardOwnProps, Omit<React.Co
|
|
|
43
42
|
* with option buttons for the person to decide. "Allow this file edit?" —
|
|
44
43
|
* allow once, always allow, deny.
|
|
45
44
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
45
|
+
* The card stays quiet in both states: one tone, the standard container
|
|
46
|
+
* border, options in a footer outside the content. Once `value` is set the
|
|
47
|
+
* decision is made and the footer echoes the chosen label; a visually hidden
|
|
48
|
+
* status announcement carries the waiting state for screen readers.
|
|
50
49
|
*
|
|
51
50
|
* Fully controlled: the card never stores the answer itself — `onValueChange`
|
|
52
51
|
* reports the choice and `value` renders it.
|
|
@@ -106,7 +106,6 @@
|
|
|
106
106
|
align-items: center;
|
|
107
107
|
justify-content: flex-end;
|
|
108
108
|
gap: var(--gap-sm);
|
|
109
|
-
padding: var(--padding-sm) var(--padding-
|
|
109
|
+
padding: var(--padding-sm-md) var(--padding-md);
|
|
110
110
|
border-top: var(--border-xs) solid var(--color-divider);
|
|
111
|
-
background-color: var(--color-bg-container-secondary);
|
|
112
111
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
MODEL PICKER COMPONENT
|
|
3
|
+
A quiet pill trigger showing the current
|
|
4
|
+
model, and a floating panel of models with
|
|
5
|
+
descriptions — plus an optional effort row.
|
|
6
|
+
|
|
7
|
+
Sized for Composer's action bar: the trigger
|
|
8
|
+
reads as chrome, not as a call to action, so
|
|
9
|
+
the send button keeps the only teal.
|
|
10
|
+
============================================ */
|
|
11
|
+
|
|
12
|
+
.ds-model-picker {
|
|
13
|
+
position: relative;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* ============================================
|
|
18
|
+
TRIGGER
|
|
19
|
+
============================================ */
|
|
20
|
+
|
|
21
|
+
.ds-model-picker__trigger {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: var(--gap-xxs);
|
|
25
|
+
padding: var(--padding-xxs) var(--padding-sm);
|
|
26
|
+
background-color: var(--color-action-passive-bg);
|
|
27
|
+
border: none;
|
|
28
|
+
border-radius: var(--radius-full);
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
color: var(--color-text-secondary);
|
|
31
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
32
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
33
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
34
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
35
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
36
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ds-model-picker__trigger:hover {
|
|
40
|
+
background-color: var(--color-action-passive-bg-hover);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ds-model-picker__trigger:focus-visible {
|
|
44
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
45
|
+
outline-offset: 2px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ds-model-picker__trigger:disabled {
|
|
49
|
+
opacity: 0.4;
|
|
50
|
+
cursor: not-allowed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ds-model-picker__trigger-label {
|
|
54
|
+
white-space: nowrap;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.ds-model-picker__chevron {
|
|
58
|
+
--icon-size: var(--icon-size-sm);
|
|
59
|
+
|
|
60
|
+
color: var(--color-icon-secondary);
|
|
61
|
+
transition: transform var(--motion-duration-slow) var(--motion-ease-standard);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.ds-model-picker--open .ds-model-picker__chevron {
|
|
65
|
+
transform: rotate(180deg);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ============================================
|
|
69
|
+
PANEL
|
|
70
|
+
============================================ */
|
|
71
|
+
|
|
72
|
+
.ds-model-picker__panel {
|
|
73
|
+
position: absolute;
|
|
74
|
+
left: 0;
|
|
75
|
+
z-index: 10;
|
|
76
|
+
min-width: 260px;
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
/* Page background, not container: the container fill is semi-transparent
|
|
80
|
+
in dark mode, and a floating panel must not show text through itself */
|
|
81
|
+
background-color: var(--color-bg-page-primary);
|
|
82
|
+
border: var(--border-xs) solid var(--color-bg-container-border);
|
|
83
|
+
border-radius: var(--radius-md);
|
|
84
|
+
box-shadow: var(--shadow-floating);
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ds-model-picker--bottom .ds-model-picker__panel {
|
|
89
|
+
top: calc(100% + var(--gap-xxs));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.ds-model-picker--top .ds-model-picker__panel {
|
|
93
|
+
bottom: calc(100% + var(--gap-xxs));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ============================================
|
|
97
|
+
MODEL LIST
|
|
98
|
+
============================================ */
|
|
99
|
+
|
|
100
|
+
.ds-model-picker__list {
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
gap: var(--gap-xxs);
|
|
104
|
+
margin: 0;
|
|
105
|
+
padding: var(--padding-xxs);
|
|
106
|
+
list-style: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.ds-model-picker__option {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: var(--gap-sm);
|
|
113
|
+
padding: var(--padding-xxs) var(--padding-sm);
|
|
114
|
+
border-radius: var(--radius-sm);
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ds-model-picker__option:hover,
|
|
120
|
+
.ds-model-picker__option--focused {
|
|
121
|
+
background-color: var(--color-bg-container-secondary);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.ds-model-picker__option--disabled {
|
|
125
|
+
opacity: 0.4;
|
|
126
|
+
cursor: not-allowed;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.ds-model-picker__option--disabled:hover {
|
|
130
|
+
background-color: transparent;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.ds-model-picker__option-main {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
min-width: 0;
|
|
137
|
+
flex: 1 1 auto;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.ds-model-picker__option-label {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: var(--gap-xs);
|
|
144
|
+
color: var(--color-text-primary);
|
|
145
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
146
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
147
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
148
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
149
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.ds-model-picker__option-badge {
|
|
153
|
+
padding: 0 var(--padding-xs);
|
|
154
|
+
background-color: var(--color-status-info-bg);
|
|
155
|
+
border-radius: var(--radius-full);
|
|
156
|
+
color: var(--color-status-info-text);
|
|
157
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
158
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
159
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
160
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
161
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ds-model-picker__option-description {
|
|
165
|
+
color: var(--color-text-tertiary);
|
|
166
|
+
font-family: var(--font-paragraph-sm-family);
|
|
167
|
+
font-size: var(--font-paragraph-sm-size);
|
|
168
|
+
font-weight: var(--font-paragraph-sm-weight);
|
|
169
|
+
line-height: var(--font-paragraph-sm-line-height);
|
|
170
|
+
letter-spacing: var(--font-paragraph-sm-letter-spacing);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.ds-model-picker__check {
|
|
174
|
+
--icon-size: var(--icon-size-sm);
|
|
175
|
+
|
|
176
|
+
flex: none;
|
|
177
|
+
color: var(--color-action-primary-text-tertiary);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ============================================
|
|
181
|
+
EFFORT ROW
|
|
182
|
+
============================================ */
|
|
183
|
+
|
|
184
|
+
.ds-model-picker__effort {
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
gap: var(--gap-sm);
|
|
188
|
+
padding: var(--padding-xs) var(--padding-sm);
|
|
189
|
+
border-top: var(--border-xs) solid var(--color-divider);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.ds-model-picker__effort-label {
|
|
193
|
+
flex: 1 1 auto;
|
|
194
|
+
color: var(--color-text-secondary);
|
|
195
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
196
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
197
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
198
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
199
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.ds-model-picker__effort-options {
|
|
203
|
+
display: inline-flex;
|
|
204
|
+
gap: var(--gap-xxs);
|
|
205
|
+
padding: var(--padding-xxxs);
|
|
206
|
+
background-color: var(--color-bg-container-secondary);
|
|
207
|
+
border-radius: var(--radius-full);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.ds-model-picker__effort-option {
|
|
211
|
+
padding: var(--padding-xxxs) var(--padding-sm);
|
|
212
|
+
background: none;
|
|
213
|
+
border: none;
|
|
214
|
+
border-radius: var(--radius-full);
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
color: var(--color-text-secondary);
|
|
217
|
+
font-family: var(--font-paragraph-sm-em-family);
|
|
218
|
+
font-size: var(--font-paragraph-sm-em-size);
|
|
219
|
+
font-weight: var(--font-paragraph-sm-em-weight);
|
|
220
|
+
line-height: var(--font-paragraph-sm-em-line-height);
|
|
221
|
+
letter-spacing: var(--font-paragraph-sm-em-letter-spacing);
|
|
222
|
+
transition:
|
|
223
|
+
background-color var(--motion-duration-fast) var(--motion-ease-standard),
|
|
224
|
+
color var(--motion-duration-fast) var(--motion-ease-standard);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.ds-model-picker__effort-option:hover {
|
|
228
|
+
color: var(--color-text-primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.ds-model-picker__effort-option:focus-visible {
|
|
232
|
+
outline: 2px solid var(--color-action-primary-bg);
|
|
233
|
+
outline-offset: -2px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.ds-model-picker__effort-option--selected {
|
|
237
|
+
background-color: var(--color-bg-container-primary);
|
|
238
|
+
color: var(--color-text-primary);
|
|
239
|
+
box-shadow: var(--shadow-floating);
|
|
240
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export interface ModelPickerModel {
|
|
3
|
+
/** Display name, e.g. "Fable 5". */
|
|
4
|
+
label: string;
|
|
5
|
+
/** Stable identifier reported through `onValueChange`. */
|
|
6
|
+
value: string;
|
|
7
|
+
/** One line on what the model is good at or how fast it is. */
|
|
8
|
+
description?: string;
|
|
9
|
+
/** Small trailing tag, e.g. "New". */
|
|
10
|
+
badge?: string;
|
|
11
|
+
/** Whether this model can be chosen. */
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ModelPickerEffortOption {
|
|
15
|
+
/** Display label, e.g. "Medium". */
|
|
16
|
+
label: string;
|
|
17
|
+
/** Stable identifier reported through `onEffortChange`. */
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
/** Props owned by ModelPicker itself — everything else falls through to the root element. */
|
|
21
|
+
type ModelPickerOwnProps = {
|
|
22
|
+
/** The models on offer, in display order. */
|
|
23
|
+
models: ModelPickerModel[];
|
|
24
|
+
/** Selected model value for controlled use. Pair with `onValueChange`. */
|
|
25
|
+
value?: string;
|
|
26
|
+
/** Initially selected model value for uncontrolled use. Defaults to the first model. */
|
|
27
|
+
defaultValue?: string;
|
|
28
|
+
/** Fires with the newly selected model's value. */
|
|
29
|
+
onValueChange?: (value: string) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Selected effort level for controlled use. Setting this (or `defaultEffort`)
|
|
32
|
+
* is what makes the effort row appear at all.
|
|
33
|
+
*/
|
|
34
|
+
effort?: string;
|
|
35
|
+
/** Initially selected effort level for uncontrolled use. */
|
|
36
|
+
defaultEffort?: string;
|
|
37
|
+
/** Fires with the newly selected effort value. */
|
|
38
|
+
onEffortChange?: (effort: string) => void;
|
|
39
|
+
/** The effort levels on offer. Defaults to low, medium and high. */
|
|
40
|
+
effortOptions?: ModelPickerEffortOption[];
|
|
41
|
+
/** Which side of the trigger the panel opens on. In a composer pinned to the bottom of the screen, use `top`. */
|
|
42
|
+
placement?: 'bottom' | 'top';
|
|
43
|
+
/** Whether the whole control is disabled. */
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
/** Additional CSS classes */
|
|
46
|
+
className?: string;
|
|
47
|
+
};
|
|
48
|
+
export interface ModelPickerProps extends ModelPickerOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof ModelPickerOwnProps> {
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* ModelPicker is the model selector for a chat surface: a quiet pill trigger
|
|
52
|
+
* showing the current model, and a floating panel listing every model with a
|
|
53
|
+
* one-line description — plus, when an effort value is provided, a row of
|
|
54
|
+
* effort levels below a divider.
|
|
55
|
+
*
|
|
56
|
+
* Built for Composer's `actions` slot, but freestanding anywhere. Forwards a
|
|
57
|
+
* ref to the root element and spreads unrecognised props onto it.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ModelPicker: React.ForwardRefExoticComponent<ModelPickerProps & React.RefAttributes<HTMLDivElement>>;
|
|
60
|
+
export {};
|