@protolabsai/ui 0.20.0 → 0.22.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/dist/plugin-kit.css +379 -0
- package/package.json +2 -1
- package/src/Empty.stories.tsx +32 -0
- package/src/Grid.stories.tsx +37 -0
- package/src/TabBar.stories.tsx +74 -0
- package/src/ToolCard.stories.tsx +71 -0
- package/src/layout.tsx +40 -1
- package/src/navigation.tsx +119 -0
- package/src/primitives.tsx +31 -3
- package/src/styles/layout.css +43 -0
- package/src/styles/navigation.css +131 -0
- package/src/styles/primitives.css +36 -0
- package/src/styles/tool-card.css +167 -0
- package/src/styles.css +1 -0
- package/src/tool-card.tsx +177 -0
package/src/navigation.tsx
CHANGED
|
@@ -80,6 +80,125 @@ export function Tabs({
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export type TabBarItem = {
|
|
84
|
+
id: string;
|
|
85
|
+
label: string;
|
|
86
|
+
icon?: ReactNode;
|
|
87
|
+
badge?: ReactNode;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** Browser-style session tabs: closable, inline-renamable, with an add-new "+".
|
|
91
|
+
* Every interaction callback is optional — absence hides its affordance, so with
|
|
92
|
+
* just `items`/`activeId`/`onSelect` it degrades to a plain tab strip. Pass
|
|
93
|
+
* `onClose` for per-tab ✕, `onRename` to enable double-click-to-edit, `onAdd` for
|
|
94
|
+
* the trailing "+". */
|
|
95
|
+
export function TabBar({
|
|
96
|
+
items,
|
|
97
|
+
activeId,
|
|
98
|
+
onSelect,
|
|
99
|
+
onClose,
|
|
100
|
+
onRename,
|
|
101
|
+
onAdd,
|
|
102
|
+
addLabel = "New tab",
|
|
103
|
+
ariaLabel,
|
|
104
|
+
}: {
|
|
105
|
+
items: TabBarItem[];
|
|
106
|
+
activeId: string;
|
|
107
|
+
onSelect: (id: string) => void;
|
|
108
|
+
onClose?: (id: string) => void;
|
|
109
|
+
onRename?: (id: string, label: string) => void;
|
|
110
|
+
onAdd?: () => void;
|
|
111
|
+
addLabel?: string;
|
|
112
|
+
ariaLabel?: string;
|
|
113
|
+
}) {
|
|
114
|
+
const [editing, setEditing] = useState<string | null>(null);
|
|
115
|
+
const [draft, setDraft] = useState("");
|
|
116
|
+
const startEdit = (t: TabBarItem) => {
|
|
117
|
+
if (!onRename) return;
|
|
118
|
+
setEditing(t.id);
|
|
119
|
+
setDraft(t.label);
|
|
120
|
+
};
|
|
121
|
+
const commit = () => {
|
|
122
|
+
if (editing != null && onRename) {
|
|
123
|
+
const v = draft.trim();
|
|
124
|
+
if (v) onRename(editing, v);
|
|
125
|
+
}
|
|
126
|
+
setEditing(null);
|
|
127
|
+
};
|
|
128
|
+
return (
|
|
129
|
+
<div className="pl-tabbar" role="tablist" aria-label={ariaLabel}>
|
|
130
|
+
{items.map((t) => (
|
|
131
|
+
<div
|
|
132
|
+
key={t.id}
|
|
133
|
+
role="tab"
|
|
134
|
+
tabIndex={0}
|
|
135
|
+
aria-selected={t.id === activeId}
|
|
136
|
+
className={cx("pl-tabbar__tab", t.id === activeId && "pl-tabbar__tab--active")}
|
|
137
|
+
onClick={() => editing !== t.id && onSelect(t.id)}
|
|
138
|
+
onDoubleClick={() => startEdit(t)}
|
|
139
|
+
onKeyDown={(e) => {
|
|
140
|
+
if (editing === t.id) return;
|
|
141
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
onSelect(t.id);
|
|
144
|
+
}
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{t.icon != null && (
|
|
148
|
+
<span className="pl-tabbar__icon" aria-hidden>
|
|
149
|
+
{t.icon}
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
152
|
+
{editing === t.id ? (
|
|
153
|
+
<input
|
|
154
|
+
className="pl-tabbar__edit"
|
|
155
|
+
autoFocus
|
|
156
|
+
value={draft}
|
|
157
|
+
aria-label="Rename tab"
|
|
158
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
159
|
+
onClick={(e) => e.stopPropagation()}
|
|
160
|
+
onBlur={commit}
|
|
161
|
+
onKeyDown={(e) => {
|
|
162
|
+
if (e.key === "Enter") {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
commit();
|
|
165
|
+
} else if (e.key === "Escape") {
|
|
166
|
+
setEditing(null);
|
|
167
|
+
}
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
) : (
|
|
171
|
+
<span className="pl-tabbar__label">{t.label}</span>
|
|
172
|
+
)}
|
|
173
|
+
{t.badge != null && <span className="pl-tabbar__badge">{t.badge}</span>}
|
|
174
|
+
{onClose && editing !== t.id && (
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
className="pl-tabbar__close"
|
|
178
|
+
aria-label={`Close ${t.label}`}
|
|
179
|
+
onClick={(e) => {
|
|
180
|
+
e.stopPropagation();
|
|
181
|
+
onClose(t.id);
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
184
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round">
|
|
185
|
+
<path d="M6 6l12 12M18 6L6 18" />
|
|
186
|
+
</svg>
|
|
187
|
+
</button>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
))}
|
|
191
|
+
{onAdd && (
|
|
192
|
+
<button type="button" className="pl-tabbar__add" aria-label={addLabel} title={addLabel} onClick={onAdd}>
|
|
193
|
+
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
|
|
194
|
+
<path d="M12 5v14M5 12h14" />
|
|
195
|
+
</svg>
|
|
196
|
+
</button>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
83
202
|
/** A horizontal kanban board. Wrap BoardColumn children. */
|
|
84
203
|
export function Board({ className, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
|
85
204
|
return <div className={cx("pl-board", className)} {...rest} />;
|
package/src/primitives.tsx
CHANGED
|
@@ -40,9 +40,37 @@ export function Eyebrow({ children }: { children: ReactNode }) {
|
|
|
40
40
|
return <div className="pl-eyebrow">{children}</div>;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
/** Empty state. Bare `<Empty>body</Empty>` still works; pass any of the slots
|
|
44
|
+
* (icon / title / description / action) for the standard centered scaffold so
|
|
45
|
+
* surfaces stop re-composing it around the wrapper. */
|
|
46
|
+
export function Empty({
|
|
47
|
+
icon,
|
|
48
|
+
title,
|
|
49
|
+
description,
|
|
50
|
+
action,
|
|
51
|
+
className,
|
|
52
|
+
children,
|
|
53
|
+
...rest
|
|
54
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
55
|
+
icon?: ReactNode;
|
|
56
|
+
title?: ReactNode;
|
|
57
|
+
description?: ReactNode;
|
|
58
|
+
action?: ReactNode;
|
|
59
|
+
}) {
|
|
60
|
+
const slotted = icon != null || title != null || description != null || action != null;
|
|
61
|
+
return (
|
|
62
|
+
<div className={cx("pl-empty", slotted && "pl-empty--slotted", className)} {...rest}>
|
|
63
|
+
{icon != null && (
|
|
64
|
+
<div className="pl-empty__icon" aria-hidden>
|
|
65
|
+
{icon}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
{title != null && <div className="pl-empty__title">{title}</div>}
|
|
69
|
+
{description != null && <div className="pl-empty__desc">{description}</div>}
|
|
70
|
+
{children}
|
|
71
|
+
{action != null && <div className="pl-empty__action">{action}</div>}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
46
74
|
}
|
|
47
75
|
|
|
48
76
|
/** Hairline rule. */
|
package/src/styles/layout.css
CHANGED
|
@@ -39,6 +39,49 @@
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/* ── Grid (responsive card grid) ── */
|
|
43
|
+
.pl-grid {
|
|
44
|
+
display: grid;
|
|
45
|
+
}
|
|
46
|
+
.pl-grid--gap-sm {
|
|
47
|
+
gap: var(--pl-space-2);
|
|
48
|
+
}
|
|
49
|
+
.pl-grid--gap-md {
|
|
50
|
+
gap: var(--pl-space-4);
|
|
51
|
+
}
|
|
52
|
+
.pl-grid--gap-lg {
|
|
53
|
+
gap: var(--pl-space-6);
|
|
54
|
+
}
|
|
55
|
+
/* auto-fill: as many columns of >= --pl-grid-min as fit */
|
|
56
|
+
.pl-grid--auto {
|
|
57
|
+
grid-template-columns: repeat(auto-fill, minmax(var(--pl-grid-min, 14rem), 1fr));
|
|
58
|
+
}
|
|
59
|
+
/* fixed/responsive column count — each breakpoint falls back to the nearest
|
|
60
|
+
smaller value that was set, so `cols={{ base:1, md:2, xl:3 }}` cascades cleanly */
|
|
61
|
+
.pl-grid--cols {
|
|
62
|
+
grid-template-columns: repeat(var(--pl-grid-cols, 1), minmax(0, 1fr));
|
|
63
|
+
}
|
|
64
|
+
@media (min-width: 640px) {
|
|
65
|
+
.pl-grid--cols {
|
|
66
|
+
grid-template-columns: repeat(var(--pl-grid-cols-sm, var(--pl-grid-cols, 1)), minmax(0, 1fr));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
@media (min-width: 768px) {
|
|
70
|
+
.pl-grid--cols {
|
|
71
|
+
grid-template-columns: repeat(var(--pl-grid-cols-md, var(--pl-grid-cols-sm, var(--pl-grid-cols, 1))), minmax(0, 1fr));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
@media (min-width: 1024px) {
|
|
75
|
+
.pl-grid--cols {
|
|
76
|
+
grid-template-columns: repeat(var(--pl-grid-cols-lg, var(--pl-grid-cols-md, var(--pl-grid-cols-sm, var(--pl-grid-cols, 1)))), minmax(0, 1fr));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
@media (min-width: 1280px) {
|
|
80
|
+
.pl-grid--cols {
|
|
81
|
+
grid-template-columns: repeat(var(--pl-grid-cols-xl, var(--pl-grid-cols-lg, var(--pl-grid-cols-md, var(--pl-grid-cols-sm, var(--pl-grid-cols, 1))))), minmax(0, 1fr));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
42
85
|
/* ── row (label | body [| status]) ── */
|
|
43
86
|
.pl-row {
|
|
44
87
|
display: grid;
|
|
@@ -255,3 +255,134 @@
|
|
|
255
255
|
line-height: 1.55;
|
|
256
256
|
color: var(--pl-color-fg-muted);
|
|
257
257
|
}
|
|
258
|
+
|
|
259
|
+
/* ── TabBar (browser-style session tabs) ────────────────────────────────────── */
|
|
260
|
+
/* On a narrow window session tabs scroll horizontally (browser/VS Code behaviour) —
|
|
261
|
+
they do NOT collapse to a <select> like Tabs, because a select can't host the
|
|
262
|
+
per-tab close / inline rename / add-new affordances that are the point of TabBar. */
|
|
263
|
+
.pl-tabbar {
|
|
264
|
+
display: flex;
|
|
265
|
+
align-items: stretch;
|
|
266
|
+
gap: 2px;
|
|
267
|
+
overflow-x: auto;
|
|
268
|
+
scrollbar-width: thin;
|
|
269
|
+
border-bottom: var(--pl-border-width) solid var(--pl-color-border);
|
|
270
|
+
}
|
|
271
|
+
.pl-tabbar__tab {
|
|
272
|
+
display: inline-flex;
|
|
273
|
+
align-items: center;
|
|
274
|
+
gap: 7px;
|
|
275
|
+
flex: 0 1 auto;
|
|
276
|
+
min-width: 92px;
|
|
277
|
+
max-width: 220px;
|
|
278
|
+
padding: 7px 10px 7px 12px;
|
|
279
|
+
background: none;
|
|
280
|
+
border: none;
|
|
281
|
+
border-bottom: 2px solid transparent;
|
|
282
|
+
color: var(--pl-color-fg-muted);
|
|
283
|
+
font-family: var(--pl-font-sans);
|
|
284
|
+
font-size: 13px;
|
|
285
|
+
cursor: pointer;
|
|
286
|
+
border-radius: var(--pl-radius) var(--pl-radius) 0 0;
|
|
287
|
+
transition:
|
|
288
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
289
|
+
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
290
|
+
}
|
|
291
|
+
.pl-tabbar__tab:hover {
|
|
292
|
+
background: var(--pl-color-bg-hover);
|
|
293
|
+
color: var(--pl-color-fg);
|
|
294
|
+
}
|
|
295
|
+
.pl-tabbar__tab:focus-visible {
|
|
296
|
+
outline: 2px solid var(--pl-color-accent);
|
|
297
|
+
outline-offset: -2px;
|
|
298
|
+
}
|
|
299
|
+
.pl-tabbar__tab--active {
|
|
300
|
+
color: var(--pl-color-fg);
|
|
301
|
+
background: var(--pl-color-bg-subtle);
|
|
302
|
+
border-bottom-color: var(--pl-color-accent);
|
|
303
|
+
}
|
|
304
|
+
.pl-tabbar__icon {
|
|
305
|
+
display: inline-flex;
|
|
306
|
+
align-items: center;
|
|
307
|
+
}
|
|
308
|
+
.pl-tabbar__icon svg {
|
|
309
|
+
width: 15px;
|
|
310
|
+
height: 15px;
|
|
311
|
+
}
|
|
312
|
+
.pl-tabbar__label {
|
|
313
|
+
min-width: 0;
|
|
314
|
+
overflow: hidden;
|
|
315
|
+
text-overflow: ellipsis;
|
|
316
|
+
white-space: nowrap;
|
|
317
|
+
}
|
|
318
|
+
.pl-tabbar__edit {
|
|
319
|
+
min-width: 60px;
|
|
320
|
+
max-width: 140px;
|
|
321
|
+
padding: 1px 4px;
|
|
322
|
+
font: inherit;
|
|
323
|
+
font-size: 13px;
|
|
324
|
+
color: var(--pl-color-fg);
|
|
325
|
+
background: var(--pl-color-bg-inset);
|
|
326
|
+
border: var(--pl-border-width) solid var(--pl-color-accent);
|
|
327
|
+
border-radius: calc(var(--pl-radius) - 2px);
|
|
328
|
+
outline: none;
|
|
329
|
+
}
|
|
330
|
+
.pl-tabbar__badge {
|
|
331
|
+
display: inline-flex;
|
|
332
|
+
align-items: center;
|
|
333
|
+
justify-content: center;
|
|
334
|
+
min-width: 16px;
|
|
335
|
+
height: 16px;
|
|
336
|
+
padding: 0 5px;
|
|
337
|
+
font-family: var(--pl-font-mono);
|
|
338
|
+
font-size: 10px;
|
|
339
|
+
line-height: 1;
|
|
340
|
+
color: var(--pl-color-fg-muted);
|
|
341
|
+
background: var(--pl-color-bg-subtle);
|
|
342
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
343
|
+
border-radius: 999px;
|
|
344
|
+
}
|
|
345
|
+
.pl-tabbar__close {
|
|
346
|
+
display: inline-flex;
|
|
347
|
+
align-items: center;
|
|
348
|
+
justify-content: center;
|
|
349
|
+
width: 18px;
|
|
350
|
+
height: 18px;
|
|
351
|
+
margin-right: -3px;
|
|
352
|
+
padding: 0;
|
|
353
|
+
color: var(--pl-color-fg-subtle);
|
|
354
|
+
background: none;
|
|
355
|
+
border: none;
|
|
356
|
+
border-radius: var(--pl-radius);
|
|
357
|
+
cursor: pointer;
|
|
358
|
+
opacity: 0.7;
|
|
359
|
+
transition:
|
|
360
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
361
|
+
color var(--pl-motion-fast) var(--pl-motion-ease),
|
|
362
|
+
opacity var(--pl-motion-fast) var(--pl-motion-ease);
|
|
363
|
+
}
|
|
364
|
+
.pl-tabbar__close:hover {
|
|
365
|
+
color: var(--pl-color-fg);
|
|
366
|
+
background: var(--pl-color-bg-hover);
|
|
367
|
+
opacity: 1;
|
|
368
|
+
}
|
|
369
|
+
.pl-tabbar__add {
|
|
370
|
+
display: inline-flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
justify-content: center;
|
|
373
|
+
width: 28px;
|
|
374
|
+
flex-shrink: 0;
|
|
375
|
+
margin-left: 2px;
|
|
376
|
+
color: var(--pl-color-fg-muted);
|
|
377
|
+
background: none;
|
|
378
|
+
border: none;
|
|
379
|
+
cursor: pointer;
|
|
380
|
+
border-radius: var(--pl-radius);
|
|
381
|
+
transition:
|
|
382
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
383
|
+
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
384
|
+
}
|
|
385
|
+
.pl-tabbar__add:hover {
|
|
386
|
+
color: var(--pl-color-fg);
|
|
387
|
+
background: var(--pl-color-bg-hover);
|
|
388
|
+
}
|
|
@@ -99,6 +99,42 @@
|
|
|
99
99
|
padding: 1rem 0;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/* Slotted form (icon/title/description/action) — centered scaffold. */
|
|
103
|
+
.pl-empty--slotted {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
align-items: center;
|
|
107
|
+
justify-content: center;
|
|
108
|
+
gap: var(--pl-space-2);
|
|
109
|
+
padding: var(--pl-space-6) var(--pl-space-4);
|
|
110
|
+
text-align: center;
|
|
111
|
+
}
|
|
112
|
+
.pl-empty__icon {
|
|
113
|
+
display: inline-flex;
|
|
114
|
+
margin-bottom: var(--pl-space-1);
|
|
115
|
+
color: var(--pl-color-fg-subtle);
|
|
116
|
+
}
|
|
117
|
+
.pl-empty__icon svg {
|
|
118
|
+
width: 28px;
|
|
119
|
+
height: 28px;
|
|
120
|
+
}
|
|
121
|
+
.pl-empty__title {
|
|
122
|
+
font-family: var(--pl-font-sans);
|
|
123
|
+
font-size: 14px;
|
|
124
|
+
font-weight: var(--pl-font-weight-medium);
|
|
125
|
+
color: var(--pl-color-fg);
|
|
126
|
+
}
|
|
127
|
+
.pl-empty__desc {
|
|
128
|
+
max-width: 44ch;
|
|
129
|
+
font-family: var(--pl-font-sans);
|
|
130
|
+
font-size: 13px;
|
|
131
|
+
line-height: 1.6;
|
|
132
|
+
color: var(--pl-color-fg-muted);
|
|
133
|
+
}
|
|
134
|
+
.pl-empty__action {
|
|
135
|
+
margin-top: var(--pl-space-2);
|
|
136
|
+
}
|
|
137
|
+
|
|
102
138
|
/* ── divider ── */
|
|
103
139
|
.pl-divider {
|
|
104
140
|
border: 0;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* @protolabsai/ui — ToolCard (streamed tool-call disclosure). Ported from the
|
|
2
|
+
protoAgent console's tool-card family onto --pl-* tokens. The frame only — the
|
|
3
|
+
body is a host-filled slot (per-tool value rendering stays app-side). */
|
|
4
|
+
|
|
5
|
+
.pl-toolcard-list {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--pl-space-2);
|
|
9
|
+
min-width: 0;
|
|
10
|
+
}
|
|
11
|
+
.pl-toolcard-group {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 6px;
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.pl-toolcard {
|
|
19
|
+
min-width: 0;
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
background: var(--pl-color-bg-raised);
|
|
22
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
23
|
+
border-radius: var(--pl-radius);
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.pl-toolcard__head {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: 7px;
|
|
31
|
+
width: 100%;
|
|
32
|
+
padding: 6px 9px;
|
|
33
|
+
background: none;
|
|
34
|
+
border: none;
|
|
35
|
+
color: var(--pl-color-fg-muted);
|
|
36
|
+
font: inherit;
|
|
37
|
+
font-size: 12px;
|
|
38
|
+
text-align: left;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
}
|
|
41
|
+
.pl-toolcard__head:disabled {
|
|
42
|
+
cursor: default;
|
|
43
|
+
}
|
|
44
|
+
.pl-toolcard__head:hover:not(:disabled) .pl-toolcard__name {
|
|
45
|
+
color: var(--pl-color-fg);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.pl-toolcard__caret {
|
|
49
|
+
flex: none;
|
|
50
|
+
display: inline-flex;
|
|
51
|
+
color: var(--pl-color-fg-subtle);
|
|
52
|
+
transition: transform var(--pl-motion-fast) var(--pl-motion-ease);
|
|
53
|
+
}
|
|
54
|
+
.pl-toolcard__caret--open {
|
|
55
|
+
transform: rotate(90deg);
|
|
56
|
+
}
|
|
57
|
+
.pl-toolcard__caret--hidden {
|
|
58
|
+
visibility: hidden;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.pl-toolcard__icon {
|
|
62
|
+
flex: none;
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
color: var(--pl-color-fg-muted);
|
|
65
|
+
}
|
|
66
|
+
.pl-toolcard__icon svg {
|
|
67
|
+
width: 13px;
|
|
68
|
+
height: 13px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.pl-toolcard__name {
|
|
72
|
+
flex: 1 1 auto;
|
|
73
|
+
min-width: 0;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
font-family: var(--pl-font-mono);
|
|
78
|
+
color: var(--pl-color-fg);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.pl-toolcard__dur {
|
|
82
|
+
flex: none;
|
|
83
|
+
font-family: var(--pl-font-mono);
|
|
84
|
+
font-size: 10px;
|
|
85
|
+
color: var(--pl-color-fg-muted);
|
|
86
|
+
font-variant-numeric: tabular-nums;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.pl-toolcard__status {
|
|
90
|
+
flex: none;
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
}
|
|
93
|
+
.pl-toolcard__status--done {
|
|
94
|
+
color: var(--pl-color-status-success);
|
|
95
|
+
}
|
|
96
|
+
.pl-toolcard__status--error {
|
|
97
|
+
color: var(--pl-color-status-error);
|
|
98
|
+
}
|
|
99
|
+
.pl-toolcard__status--running {
|
|
100
|
+
color: var(--pl-color-status-warning);
|
|
101
|
+
}
|
|
102
|
+
.pl-toolcard__spin {
|
|
103
|
+
animation: pl-toolcard-spin 0.8s linear infinite;
|
|
104
|
+
}
|
|
105
|
+
@keyframes pl-toolcard-spin {
|
|
106
|
+
to {
|
|
107
|
+
transform: rotate(360deg);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.pl-toolcard__body {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
gap: 8px;
|
|
115
|
+
padding: 8px 9px;
|
|
116
|
+
border-top: var(--pl-border-width) solid var(--pl-color-border);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* subagent `task` nesting — indented rail of child cards */
|
|
120
|
+
.pl-toolcard__children {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
gap: 6px;
|
|
124
|
+
min-width: 0;
|
|
125
|
+
margin-left: 14px;
|
|
126
|
+
padding-left: 10px;
|
|
127
|
+
border-left: 2px solid var(--pl-color-border);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* body section (input / result) */
|
|
131
|
+
.pl-toolcard__section {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
gap: 4px;
|
|
135
|
+
min-width: 0;
|
|
136
|
+
}
|
|
137
|
+
.pl-toolcard__section-head {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: space-between;
|
|
141
|
+
gap: 8px;
|
|
142
|
+
}
|
|
143
|
+
.pl-toolcard__label {
|
|
144
|
+
font-family: var(--pl-font-mono);
|
|
145
|
+
font-size: 10px;
|
|
146
|
+
text-transform: uppercase;
|
|
147
|
+
letter-spacing: 0.04em;
|
|
148
|
+
color: var(--pl-color-fg-subtle);
|
|
149
|
+
}
|
|
150
|
+
.pl-toolcard__copy {
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
padding: 2px;
|
|
155
|
+
background: none;
|
|
156
|
+
border: none;
|
|
157
|
+
border-radius: var(--pl-radius);
|
|
158
|
+
color: var(--pl-color-fg-muted);
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
transition:
|
|
161
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
162
|
+
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
163
|
+
}
|
|
164
|
+
.pl-toolcard__copy:hover {
|
|
165
|
+
color: var(--pl-color-fg);
|
|
166
|
+
background: var(--pl-color-bg-hover);
|
|
167
|
+
}
|