@protolabsai/ui 0.22.1 → 0.23.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 +80 -124
- package/package.json +1 -1
- package/src/EditableText.stories.tsx +41 -0
- package/src/app-shell.tsx +1 -1
- package/src/forms.tsx +103 -1
- package/src/navigation.tsx +18 -37
- package/src/overlays.tsx +3 -3
- package/src/styles/app-shell.css +3 -8
- package/src/styles/forms.css +27 -0
- package/src/styles/navigation.css +6 -73
- package/src/styles/overlays.css +2 -22
- package/src/styles/primitives.css +39 -0
- package/src/styles/tool-card.css +3 -21
- package/src/tool-card.tsx +1 -1
package/dist/plugin-kit.css
CHANGED
|
@@ -372,6 +372,45 @@ a:hover {
|
|
|
372
372
|
margin-top: var(--pl-space-2);
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
/* ── count badge (round numeric pill) — shared by Tabs / TabBar / SurfaceRail.
|
|
376
|
+
Distinct from .pl-badge (a lowercase text/label pill). ── */
|
|
377
|
+
.pl-count {
|
|
378
|
+
display: inline-flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
justify-content: center;
|
|
381
|
+
min-width: 16px;
|
|
382
|
+
height: 16px;
|
|
383
|
+
padding: 0 5px;
|
|
384
|
+
font-family: var(--pl-font-mono);
|
|
385
|
+
font-size: 10px;
|
|
386
|
+
line-height: 1;
|
|
387
|
+
color: var(--pl-color-fg-muted);
|
|
388
|
+
background: var(--pl-color-bg-subtle);
|
|
389
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
390
|
+
border-radius: 999px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* ── icon button (bare clickable glyph) — shared base for close / add / copy /
|
|
394
|
+
dialog + toast dismiss. Each use adds its own size/margin delta. ── */
|
|
395
|
+
.pl-iconbtn {
|
|
396
|
+
display: inline-flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
justify-content: center;
|
|
399
|
+
padding: 0;
|
|
400
|
+
color: var(--pl-color-fg-muted);
|
|
401
|
+
background: none;
|
|
402
|
+
border: none;
|
|
403
|
+
border-radius: var(--pl-radius);
|
|
404
|
+
cursor: pointer;
|
|
405
|
+
transition:
|
|
406
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
407
|
+
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
408
|
+
}
|
|
409
|
+
.pl-iconbtn:hover {
|
|
410
|
+
color: var(--pl-color-fg);
|
|
411
|
+
background: var(--pl-color-bg-hover);
|
|
412
|
+
}
|
|
413
|
+
|
|
375
414
|
/* ── divider ── */
|
|
376
415
|
.pl-divider {
|
|
377
416
|
border: 0;
|
|
@@ -1137,23 +1176,8 @@ a.pl-changelog__version:hover {
|
|
|
1137
1176
|
height: 15px;
|
|
1138
1177
|
}
|
|
1139
1178
|
|
|
1140
|
-
.pl-
|
|
1141
|
-
|
|
1142
|
-
align-items: center;
|
|
1143
|
-
justify-content: center;
|
|
1144
|
-
min-width: 16px;
|
|
1145
|
-
height: 16px;
|
|
1146
|
-
padding: 0 5px;
|
|
1147
|
-
font-family: var(--pl-font-mono);
|
|
1148
|
-
font-size: 10px;
|
|
1149
|
-
line-height: 1;
|
|
1150
|
-
color: var(--pl-color-fg-muted);
|
|
1151
|
-
background: var(--pl-color-bg-subtle);
|
|
1152
|
-
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
1153
|
-
border-radius: 999px;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
.pl-tab--active .pl-tab__badge {
|
|
1179
|
+
/* count badge is the shared .pl-count (primitives.css) */
|
|
1180
|
+
.pl-tab--active .pl-count {
|
|
1157
1181
|
color: var(--pl-color-fg);
|
|
1158
1182
|
}
|
|
1159
1183
|
|
|
@@ -1420,76 +1444,24 @@ a.pl-changelog__version:hover {
|
|
|
1420
1444
|
text-overflow: ellipsis;
|
|
1421
1445
|
white-space: nowrap;
|
|
1422
1446
|
}
|
|
1423
|
-
.pl-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
padding: 1px 4px;
|
|
1427
|
-
font: inherit;
|
|
1428
|
-
font-size: 13px;
|
|
1429
|
-
color: var(--pl-color-fg);
|
|
1430
|
-
background: var(--pl-color-bg-inset);
|
|
1431
|
-
border: var(--pl-border-width) solid var(--pl-color-accent);
|
|
1432
|
-
border-radius: calc(var(--pl-radius) - 2px);
|
|
1433
|
-
outline: none;
|
|
1434
|
-
}
|
|
1435
|
-
.pl-tabbar__badge {
|
|
1436
|
-
display: inline-flex;
|
|
1437
|
-
align-items: center;
|
|
1438
|
-
justify-content: center;
|
|
1439
|
-
min-width: 16px;
|
|
1440
|
-
height: 16px;
|
|
1441
|
-
padding: 0 5px;
|
|
1442
|
-
font-family: var(--pl-font-mono);
|
|
1443
|
-
font-size: 10px;
|
|
1444
|
-
line-height: 1;
|
|
1445
|
-
color: var(--pl-color-fg-muted);
|
|
1446
|
-
background: var(--pl-color-bg-subtle);
|
|
1447
|
-
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
1448
|
-
border-radius: 999px;
|
|
1449
|
-
}
|
|
1447
|
+
/* inline rename uses the shared EditableText control (.pl-editable__input) */
|
|
1448
|
+
/* count badge is the shared .pl-count (primitives.css) */
|
|
1449
|
+
/* close / add extend the shared .pl-iconbtn (primitives.css) — deltas only */
|
|
1450
1450
|
.pl-tabbar__close {
|
|
1451
|
-
display: inline-flex;
|
|
1452
|
-
align-items: center;
|
|
1453
|
-
justify-content: center;
|
|
1454
1451
|
width: 18px;
|
|
1455
1452
|
height: 18px;
|
|
1456
1453
|
margin-right: -3px;
|
|
1457
|
-
padding: 0;
|
|
1458
1454
|
color: var(--pl-color-fg-subtle);
|
|
1459
|
-
background: none;
|
|
1460
|
-
border: none;
|
|
1461
|
-
border-radius: var(--pl-radius);
|
|
1462
|
-
cursor: pointer;
|
|
1463
1455
|
opacity: 0.7;
|
|
1464
|
-
transition:
|
|
1465
|
-
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
1466
|
-
color var(--pl-motion-fast) var(--pl-motion-ease),
|
|
1467
|
-
opacity var(--pl-motion-fast) var(--pl-motion-ease);
|
|
1456
|
+
transition: opacity var(--pl-motion-fast) var(--pl-motion-ease);
|
|
1468
1457
|
}
|
|
1469
1458
|
.pl-tabbar__close:hover {
|
|
1470
|
-
color: var(--pl-color-fg);
|
|
1471
|
-
background: var(--pl-color-bg-hover);
|
|
1472
1459
|
opacity: 1;
|
|
1473
1460
|
}
|
|
1474
1461
|
.pl-tabbar__add {
|
|
1475
|
-
display: inline-flex;
|
|
1476
|
-
align-items: center;
|
|
1477
|
-
justify-content: center;
|
|
1478
1462
|
width: 28px;
|
|
1479
1463
|
flex-shrink: 0;
|
|
1480
1464
|
margin-left: 2px;
|
|
1481
|
-
color: var(--pl-color-fg-muted);
|
|
1482
|
-
background: none;
|
|
1483
|
-
border: none;
|
|
1484
|
-
cursor: pointer;
|
|
1485
|
-
border-radius: var(--pl-radius);
|
|
1486
|
-
transition:
|
|
1487
|
-
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
1488
|
-
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
1489
|
-
}
|
|
1490
|
-
.pl-tabbar__add:hover {
|
|
1491
|
-
color: var(--pl-color-fg);
|
|
1492
|
-
background: var(--pl-color-bg-hover);
|
|
1493
1465
|
}
|
|
1494
1466
|
|
|
1495
1467
|
/* ── ui component: forms.css ───────────────────────────────────────────────── */
|
|
@@ -1748,6 +1720,33 @@ a.pl-changelog__version:hover {
|
|
|
1748
1720
|
color: var(--pl-color-fg);
|
|
1749
1721
|
}
|
|
1750
1722
|
|
|
1723
|
+
/* ── EditableText (inline rename) ── */
|
|
1724
|
+
/* Both states are font:inherit so the label↔input flip is seamless in any context. */
|
|
1725
|
+
.pl-editable {
|
|
1726
|
+
cursor: text;
|
|
1727
|
+
border-radius: calc(var(--pl-radius) - 2px);
|
|
1728
|
+
}
|
|
1729
|
+
.pl-editable:hover {
|
|
1730
|
+
background: var(--pl-color-bg-hover);
|
|
1731
|
+
}
|
|
1732
|
+
.pl-editable:focus-visible {
|
|
1733
|
+
outline: 2px solid var(--pl-color-accent);
|
|
1734
|
+
outline-offset: 2px;
|
|
1735
|
+
}
|
|
1736
|
+
.pl-editable__input {
|
|
1737
|
+
min-width: 60px;
|
|
1738
|
+
padding: 1px 4px;
|
|
1739
|
+
font: inherit;
|
|
1740
|
+
color: var(--pl-color-fg);
|
|
1741
|
+
background: var(--pl-color-bg-inset);
|
|
1742
|
+
border: var(--pl-border-width) solid var(--pl-color-accent);
|
|
1743
|
+
border-radius: calc(var(--pl-radius) - 2px);
|
|
1744
|
+
outline: none;
|
|
1745
|
+
}
|
|
1746
|
+
.pl-editable__input--invalid {
|
|
1747
|
+
border-color: var(--pl-color-status-error);
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1751
1750
|
/* ── ui component: overlays.css ────────────────────────────────────────────── */
|
|
1752
1751
|
/* @protolabsai/ui — overlays styles (over @protolabsai/design --pl-* tokens). */
|
|
1753
1752
|
|
|
@@ -1799,28 +1798,14 @@ a.pl-changelog__version:hover {
|
|
|
1799
1798
|
color: var(--pl-color-fg);
|
|
1800
1799
|
}
|
|
1801
1800
|
|
|
1801
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
1802
1802
|
.pl-dialog__close {
|
|
1803
|
-
display: inline-flex;
|
|
1804
|
-
align-items: center;
|
|
1805
|
-
justify-content: center;
|
|
1806
1803
|
width: 24px;
|
|
1807
1804
|
height: 24px;
|
|
1808
1805
|
font-size: 18px;
|
|
1809
1806
|
line-height: 1;
|
|
1810
|
-
color: var(--pl-color-fg-muted);
|
|
1811
|
-
background: transparent;
|
|
1812
|
-
border: none;
|
|
1813
|
-
border-radius: var(--pl-radius);
|
|
1814
|
-
cursor: pointer;
|
|
1815
|
-
transition:
|
|
1816
|
-
color var(--pl-motion-fast) var(--pl-motion-ease),
|
|
1817
|
-
background var(--pl-motion-fast) var(--pl-motion-ease);
|
|
1818
1807
|
}
|
|
1819
1808
|
|
|
1820
|
-
.pl-dialog__close:hover {
|
|
1821
|
-
color: var(--pl-color-fg);
|
|
1822
|
-
background: var(--pl-color-bg-hover);
|
|
1823
|
-
}
|
|
1824
1809
|
|
|
1825
1810
|
.pl-dialog__body {
|
|
1826
1811
|
padding: var(--pl-space-4);
|
|
@@ -1979,19 +1964,13 @@ a.pl-changelog__version:hover {
|
|
|
1979
1964
|
color: var(--pl-color-fg-muted);
|
|
1980
1965
|
}
|
|
1981
1966
|
|
|
1967
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
1982
1968
|
.pl-toast__close {
|
|
1983
1969
|
flex-shrink: 0;
|
|
1984
1970
|
font-size: 16px;
|
|
1985
1971
|
line-height: 1;
|
|
1986
|
-
color: var(--pl-color-fg-muted);
|
|
1987
|
-
background: transparent;
|
|
1988
|
-
border: none;
|
|
1989
|
-
cursor: pointer;
|
|
1990
1972
|
}
|
|
1991
1973
|
|
|
1992
|
-
.pl-toast__close:hover {
|
|
1993
|
-
color: var(--pl-color-fg);
|
|
1994
|
-
}
|
|
1995
1974
|
|
|
1996
1975
|
/* ── Tooltip (CSS-only) ──────────────────────────────────────────────────────── */
|
|
1997
1976
|
.pl-tip-wrap {
|
|
@@ -2546,22 +2525,17 @@ a.pl-changelog__version:hover {
|
|
|
2546
2525
|
white-space: nowrap;
|
|
2547
2526
|
}
|
|
2548
2527
|
|
|
2549
|
-
.pl-
|
|
2528
|
+
/* rail count badge = shared .pl-count (primitives.css) + absolute positioning */
|
|
2529
|
+
.pl-count--rail {
|
|
2550
2530
|
position: absolute;
|
|
2551
2531
|
top: 4px;
|
|
2552
2532
|
right: 9px;
|
|
2553
|
-
display: inline-flex;
|
|
2554
|
-
align-items: center;
|
|
2555
|
-
justify-content: center;
|
|
2556
2533
|
min-width: 15px;
|
|
2557
2534
|
height: 15px;
|
|
2558
2535
|
padding: 0 4px;
|
|
2559
|
-
font-family: var(--pl-font-mono);
|
|
2560
2536
|
font-size: 9px;
|
|
2561
2537
|
color: var(--pl-color-fg);
|
|
2562
|
-
|
|
2563
|
-
border: var(--pl-border-width) solid var(--pl-color-border-strong);
|
|
2564
|
-
border-radius: 999px;
|
|
2538
|
+
border-color: var(--pl-color-border-strong);
|
|
2565
2539
|
}
|
|
2566
2540
|
|
|
2567
2541
|
.pl-rail__dot {
|
|
@@ -2984,13 +2958,9 @@ a.pl-changelog__version:hover {
|
|
|
2984
2958
|
.pl-toolcard__status--running {
|
|
2985
2959
|
color: var(--pl-color-status-warning);
|
|
2986
2960
|
}
|
|
2961
|
+
/* reuses the shared `pl-spin` keyframe (data.css) */
|
|
2987
2962
|
.pl-toolcard__spin {
|
|
2988
|
-
animation: pl-
|
|
2989
|
-
}
|
|
2990
|
-
@keyframes pl-toolcard-spin {
|
|
2991
|
-
to {
|
|
2992
|
-
transform: rotate(360deg);
|
|
2993
|
-
}
|
|
2963
|
+
animation: pl-spin 0.8s linear infinite;
|
|
2994
2964
|
}
|
|
2995
2965
|
|
|
2996
2966
|
.pl-toolcard__body {
|
|
@@ -3032,23 +3002,9 @@ a.pl-changelog__version:hover {
|
|
|
3032
3002
|
letter-spacing: 0.04em;
|
|
3033
3003
|
color: var(--pl-color-fg-subtle);
|
|
3034
3004
|
}
|
|
3005
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
3035
3006
|
.pl-toolcard__copy {
|
|
3036
|
-
display: inline-flex;
|
|
3037
|
-
align-items: center;
|
|
3038
|
-
justify-content: center;
|
|
3039
3007
|
padding: 2px;
|
|
3040
|
-
background: none;
|
|
3041
|
-
border: none;
|
|
3042
|
-
border-radius: var(--pl-radius);
|
|
3043
|
-
color: var(--pl-color-fg-muted);
|
|
3044
|
-
cursor: pointer;
|
|
3045
|
-
transition:
|
|
3046
|
-
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
3047
|
-
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
3048
|
-
}
|
|
3049
|
-
.pl-toolcard__copy:hover {
|
|
3050
|
-
color: var(--pl-color-fg);
|
|
3051
|
-
background: var(--pl-color-bg-hover);
|
|
3052
3008
|
}
|
|
3053
3009
|
|
|
3054
3010
|
/* ── ui component: theming.css ─────────────────────────────────────────────── */
|
package/package.json
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { EditableText } from "./forms";
|
|
4
|
+
|
|
5
|
+
const meta: Meta = { title: "Components/Forms/EditableText" };
|
|
6
|
+
export default meta;
|
|
7
|
+
type Story = StoryObj;
|
|
8
|
+
|
|
9
|
+
/** Uncontrolled: double-click (or focus + Enter) to edit; Enter commits, Esc cancels.
|
|
10
|
+
* The input is `font: inherit`, so it flips in place seamlessly. */
|
|
11
|
+
export const Inline: Story = {
|
|
12
|
+
render: () => {
|
|
13
|
+
function Demo() {
|
|
14
|
+
const [name, setName] = useState("untitled-agent");
|
|
15
|
+
return (
|
|
16
|
+
<div style={{ fontSize: 16, fontWeight: 600, color: "var(--pl-color-fg)" }}>
|
|
17
|
+
<EditableText value={name} onCommit={setName} aria-label="Agent name" />
|
|
18
|
+
<p style={{ fontFamily: "var(--pl-font-mono)", fontSize: 12, color: "var(--pl-color-fg-muted)", fontWeight: 400 }}>
|
|
19
|
+
value: {name} · double-click to rename
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return <Demo />;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** With inline validation — slugs only; commit is blocked while invalid (red border). */
|
|
29
|
+
export const Validated: Story = {
|
|
30
|
+
render: () => {
|
|
31
|
+
function Demo() {
|
|
32
|
+
const [slug, setSlug] = useState("my-project");
|
|
33
|
+
return (
|
|
34
|
+
<div style={{ fontFamily: "var(--pl-font-mono)", fontSize: 14, color: "var(--pl-color-fg)" }}>
|
|
35
|
+
<EditableText value={slug} onCommit={setSlug} validate={(v) => /^[a-z0-9-]+$/.test(v)} aria-label="Slug" />
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return <Demo />;
|
|
40
|
+
},
|
|
41
|
+
};
|
package/src/app-shell.tsx
CHANGED
|
@@ -50,7 +50,7 @@ function RailItemInner({ item }: { item: RailItem }) {
|
|
|
50
50
|
</span>
|
|
51
51
|
<span className="pl-rail__label">{item.label}</span>
|
|
52
52
|
{item.badge ? (
|
|
53
|
-
<span className="pl-
|
|
53
|
+
<span className="pl-count pl-count--rail">{item.badge > 9 ? "9+" : item.badge}</span>
|
|
54
54
|
) : item.dot ? (
|
|
55
55
|
<span className="pl-rail__dot" aria-label="active" />
|
|
56
56
|
) : null}
|
package/src/forms.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InputHTMLAttributes, ReactNode, SelectHTMLAttributes, TextareaHTMLAttributes } from "react";
|
|
2
|
-
import { createContext, useContext } from "react";
|
|
2
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
3
3
|
import { cx } from "./internal";
|
|
4
4
|
|
|
5
5
|
/** A labeled input/textarea bound to a string value (form fields, editors). */
|
|
@@ -171,3 +171,105 @@ export function Radio({
|
|
|
171
171
|
</label>
|
|
172
172
|
);
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
/** Inline rename: a text label that flips to an input in place — Enter commits,
|
|
176
|
+
* Esc cancels, blur commits (or cancels via `commitOnBlur={false}`). Uncontrolled
|
|
177
|
+
* by default (double-click / Enter to edit); pass `editing` + `onEditingChange` to
|
|
178
|
+
* drive it from a host (e.g. a paired trigger). The input is `font: inherit`, so the
|
|
179
|
+
* flip is visually seamless. The canonical inline-edit control — TabBar renders this. */
|
|
180
|
+
export function EditableText({
|
|
181
|
+
value,
|
|
182
|
+
onCommit,
|
|
183
|
+
onCancel,
|
|
184
|
+
validate,
|
|
185
|
+
commitOnBlur = true,
|
|
186
|
+
editing: editingProp,
|
|
187
|
+
onEditingChange,
|
|
188
|
+
placeholder,
|
|
189
|
+
className,
|
|
190
|
+
inputClassName,
|
|
191
|
+
"aria-label": ariaLabel,
|
|
192
|
+
}: {
|
|
193
|
+
value: string;
|
|
194
|
+
onCommit: (next: string) => void;
|
|
195
|
+
onCancel?: () => void;
|
|
196
|
+
/** Optional inline validity — a falsy result blocks commit (stays in edit). */
|
|
197
|
+
validate?: (v: string) => boolean;
|
|
198
|
+
/** Blur commits (default) or cancels. */
|
|
199
|
+
commitOnBlur?: boolean;
|
|
200
|
+
/** Controlled edit state. Omit for uncontrolled (double-click to edit). */
|
|
201
|
+
editing?: boolean;
|
|
202
|
+
onEditingChange?: (editing: boolean) => void;
|
|
203
|
+
placeholder?: string;
|
|
204
|
+
className?: string;
|
|
205
|
+
inputClassName?: string;
|
|
206
|
+
"aria-label"?: string;
|
|
207
|
+
}) {
|
|
208
|
+
const [uncontrolled, setUncontrolled] = useState(false);
|
|
209
|
+
const editing = editingProp ?? uncontrolled;
|
|
210
|
+
const setEditing = (v: boolean) => {
|
|
211
|
+
if (editingProp === undefined) setUncontrolled(v);
|
|
212
|
+
onEditingChange?.(v);
|
|
213
|
+
};
|
|
214
|
+
const [draft, setDraft] = useState(value);
|
|
215
|
+
// Reset the draft to the live value each time editing (re)starts.
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (editing) setDraft(value);
|
|
218
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
219
|
+
}, [editing]);
|
|
220
|
+
|
|
221
|
+
const invalid = validate ? !validate(draft.trim()) : false;
|
|
222
|
+
const commit = () => {
|
|
223
|
+
const next = draft.trim();
|
|
224
|
+
if (validate && !validate(next)) return; // stay in edit while invalid
|
|
225
|
+
if (next && next !== value) onCommit(next);
|
|
226
|
+
setEditing(false);
|
|
227
|
+
};
|
|
228
|
+
const cancel = () => {
|
|
229
|
+
setDraft(value);
|
|
230
|
+
onCancel?.();
|
|
231
|
+
setEditing(false);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (!editing) {
|
|
235
|
+
return (
|
|
236
|
+
<span
|
|
237
|
+
className={cx("pl-editable", className)}
|
|
238
|
+
tabIndex={0}
|
|
239
|
+
role="button"
|
|
240
|
+
aria-label={ariaLabel}
|
|
241
|
+
onDoubleClick={() => setEditing(true)}
|
|
242
|
+
onKeyDown={(e) => {
|
|
243
|
+
if (e.key === "Enter" || e.key === "F2") {
|
|
244
|
+
e.preventDefault();
|
|
245
|
+
setEditing(true);
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
{value || placeholder}
|
|
250
|
+
</span>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return (
|
|
254
|
+
<input
|
|
255
|
+
className={cx("pl-editable__input", invalid && "pl-editable__input--invalid", inputClassName)}
|
|
256
|
+
autoFocus
|
|
257
|
+
value={draft}
|
|
258
|
+
placeholder={placeholder}
|
|
259
|
+
aria-label={ariaLabel}
|
|
260
|
+
aria-invalid={invalid || undefined}
|
|
261
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
262
|
+
onClick={(e) => e.stopPropagation()}
|
|
263
|
+
onBlur={() => (commitOnBlur ? commit() : cancel())}
|
|
264
|
+
onKeyDown={(e) => {
|
|
265
|
+
if (e.key === "Enter") {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
commit();
|
|
268
|
+
} else if (e.key === "Escape") {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
cancel();
|
|
271
|
+
}
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
);
|
|
275
|
+
}
|
package/src/navigation.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
|
|
2
2
|
import { useId, useState } from "react";
|
|
3
3
|
import { cx } from "./internal";
|
|
4
|
+
import { EditableText } from "./forms";
|
|
4
5
|
|
|
5
6
|
export type TabItem = {
|
|
6
7
|
id: string;
|
|
@@ -50,7 +51,7 @@ export function Tabs({
|
|
|
50
51
|
</span>
|
|
51
52
|
)}
|
|
52
53
|
<span className="pl-tab__label">{t.label}</span>
|
|
53
|
-
{t.badge != null && <span className="pl-
|
|
54
|
+
{t.badge != null && <span className="pl-count">{t.badge}</span>}
|
|
54
55
|
{t.locked ? (
|
|
55
56
|
<span className="pl-tab__lock" aria-hidden>
|
|
56
57
|
🔒
|
|
@@ -111,20 +112,8 @@ export function TabBar({
|
|
|
111
112
|
addLabel?: string;
|
|
112
113
|
ariaLabel?: string;
|
|
113
114
|
}) {
|
|
114
|
-
|
|
115
|
-
const [
|
|
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
|
-
};
|
|
115
|
+
// Which tab is mid-rename; the inline editor itself is the shared EditableText.
|
|
116
|
+
const [editingId, setEditingId] = useState<string | null>(null);
|
|
128
117
|
return (
|
|
129
118
|
<div className="pl-tabbar" role="tablist" aria-label={ariaLabel}>
|
|
130
119
|
{items.map((t) => (
|
|
@@ -134,10 +123,10 @@ export function TabBar({
|
|
|
134
123
|
tabIndex={0}
|
|
135
124
|
aria-selected={t.id === activeId}
|
|
136
125
|
className={cx("pl-tabbar__tab", t.id === activeId && "pl-tabbar__tab--active")}
|
|
137
|
-
onClick={() =>
|
|
138
|
-
onDoubleClick={() =>
|
|
126
|
+
onClick={() => editingId !== t.id && onSelect(t.id)}
|
|
127
|
+
onDoubleClick={() => onRename && setEditingId(t.id)}
|
|
139
128
|
onKeyDown={(e) => {
|
|
140
|
-
if (
|
|
129
|
+
if (editingId === t.id) return;
|
|
141
130
|
if (e.key === "Enter" || e.key === " ") {
|
|
142
131
|
e.preventDefault();
|
|
143
132
|
onSelect(t.id);
|
|
@@ -149,32 +138,24 @@ export function TabBar({
|
|
|
149
138
|
{t.icon}
|
|
150
139
|
</span>
|
|
151
140
|
)}
|
|
152
|
-
{
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
value={draft}
|
|
141
|
+
{editingId === t.id ? (
|
|
142
|
+
<EditableText
|
|
143
|
+
editing
|
|
144
|
+
value={t.label}
|
|
157
145
|
aria-label="Rename tab"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
onKeyDown={(e) => {
|
|
162
|
-
if (e.key === "Enter") {
|
|
163
|
-
e.preventDefault();
|
|
164
|
-
commit();
|
|
165
|
-
} else if (e.key === "Escape") {
|
|
166
|
-
setEditing(null);
|
|
167
|
-
}
|
|
146
|
+
onCommit={(v) => onRename?.(t.id, v)}
|
|
147
|
+
onEditingChange={(e) => {
|
|
148
|
+
if (!e) setEditingId(null);
|
|
168
149
|
}}
|
|
169
150
|
/>
|
|
170
151
|
) : (
|
|
171
152
|
<span className="pl-tabbar__label">{t.label}</span>
|
|
172
153
|
)}
|
|
173
|
-
{t.badge != null && <span className="pl-
|
|
174
|
-
{onClose &&
|
|
154
|
+
{t.badge != null && <span className="pl-count">{t.badge}</span>}
|
|
155
|
+
{onClose && editingId !== t.id && (
|
|
175
156
|
<button
|
|
176
157
|
type="button"
|
|
177
|
-
className="pl-tabbar__close"
|
|
158
|
+
className="pl-iconbtn pl-tabbar__close"
|
|
178
159
|
aria-label={`Close ${t.label}`}
|
|
179
160
|
onClick={(e) => {
|
|
180
161
|
e.stopPropagation();
|
|
@@ -189,7 +170,7 @@ export function TabBar({
|
|
|
189
170
|
</div>
|
|
190
171
|
))}
|
|
191
172
|
{onAdd && (
|
|
192
|
-
<button type="button" className="pl-tabbar__add" aria-label={addLabel} title={addLabel} onClick={onAdd}>
|
|
173
|
+
<button type="button" className="pl-iconbtn pl-tabbar__add" aria-label={addLabel} title={addLabel} onClick={onAdd}>
|
|
193
174
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
|
|
194
175
|
<path d="M12 5v14M5 12h14" />
|
|
195
176
|
</svg>
|
package/src/overlays.tsx
CHANGED
|
@@ -100,7 +100,7 @@ export function Dialog({
|
|
|
100
100
|
{title}
|
|
101
101
|
</div>
|
|
102
102
|
{onClose && (
|
|
103
|
-
<button type="button" className="pl-dialog__close" aria-label="Close" onClick={onClose}>
|
|
103
|
+
<button type="button" className="pl-iconbtn pl-dialog__close" aria-label="Close" onClick={onClose}>
|
|
104
104
|
×
|
|
105
105
|
</button>
|
|
106
106
|
)}
|
|
@@ -200,7 +200,7 @@ export function Drawer({
|
|
|
200
200
|
{title}
|
|
201
201
|
</div>
|
|
202
202
|
{onClose && (
|
|
203
|
-
<button type="button" className="pl-dialog__close" aria-label="Close" onClick={onClose}>
|
|
203
|
+
<button type="button" className="pl-iconbtn pl-dialog__close" aria-label="Close" onClick={onClose}>
|
|
204
204
|
×
|
|
205
205
|
</button>
|
|
206
206
|
)}
|
|
@@ -265,7 +265,7 @@ function ToastView({ toast, onDismiss }: { toast: ToastItem; onDismiss: (id: str
|
|
|
265
265
|
{toast.title != null && <div className="pl-toast__title">{toast.title}</div>}
|
|
266
266
|
<div className="pl-toast__msg">{toast.message}</div>
|
|
267
267
|
</div>
|
|
268
|
-
<button type="button" className="pl-toast__close" aria-label="Dismiss" onClick={() => onDismiss(toast.id)}>
|
|
268
|
+
<button type="button" className="pl-iconbtn pl-toast__close" aria-label="Dismiss" onClick={() => onDismiss(toast.id)}>
|
|
269
269
|
×
|
|
270
270
|
</button>
|
|
271
271
|
</div>
|
package/src/styles/app-shell.css
CHANGED
|
@@ -65,22 +65,17 @@
|
|
|
65
65
|
white-space: nowrap;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
.pl-
|
|
68
|
+
/* rail count badge = shared .pl-count (primitives.css) + absolute positioning */
|
|
69
|
+
.pl-count--rail {
|
|
69
70
|
position: absolute;
|
|
70
71
|
top: 4px;
|
|
71
72
|
right: 9px;
|
|
72
|
-
display: inline-flex;
|
|
73
|
-
align-items: center;
|
|
74
|
-
justify-content: center;
|
|
75
73
|
min-width: 15px;
|
|
76
74
|
height: 15px;
|
|
77
75
|
padding: 0 4px;
|
|
78
|
-
font-family: var(--pl-font-mono);
|
|
79
76
|
font-size: 9px;
|
|
80
77
|
color: var(--pl-color-fg);
|
|
81
|
-
|
|
82
|
-
border: var(--pl-border-width) solid var(--pl-color-border-strong);
|
|
83
|
-
border-radius: 999px;
|
|
78
|
+
border-color: var(--pl-color-border-strong);
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
.pl-rail__dot {
|
package/src/styles/forms.css
CHANGED
|
@@ -252,3 +252,30 @@
|
|
|
252
252
|
font-size: 13px;
|
|
253
253
|
color: var(--pl-color-fg);
|
|
254
254
|
}
|
|
255
|
+
|
|
256
|
+
/* ── EditableText (inline rename) ── */
|
|
257
|
+
/* Both states are font:inherit so the label↔input flip is seamless in any context. */
|
|
258
|
+
.pl-editable {
|
|
259
|
+
cursor: text;
|
|
260
|
+
border-radius: calc(var(--pl-radius) - 2px);
|
|
261
|
+
}
|
|
262
|
+
.pl-editable:hover {
|
|
263
|
+
background: var(--pl-color-bg-hover);
|
|
264
|
+
}
|
|
265
|
+
.pl-editable:focus-visible {
|
|
266
|
+
outline: 2px solid var(--pl-color-accent);
|
|
267
|
+
outline-offset: 2px;
|
|
268
|
+
}
|
|
269
|
+
.pl-editable__input {
|
|
270
|
+
min-width: 60px;
|
|
271
|
+
padding: 1px 4px;
|
|
272
|
+
font: inherit;
|
|
273
|
+
color: var(--pl-color-fg);
|
|
274
|
+
background: var(--pl-color-bg-inset);
|
|
275
|
+
border: var(--pl-border-width) solid var(--pl-color-accent);
|
|
276
|
+
border-radius: calc(var(--pl-radius) - 2px);
|
|
277
|
+
outline: none;
|
|
278
|
+
}
|
|
279
|
+
.pl-editable__input--invalid {
|
|
280
|
+
border-color: var(--pl-color-status-error);
|
|
281
|
+
}
|
|
@@ -32,23 +32,8 @@
|
|
|
32
32
|
height: 15px;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
.pl-
|
|
36
|
-
|
|
37
|
-
align-items: center;
|
|
38
|
-
justify-content: center;
|
|
39
|
-
min-width: 16px;
|
|
40
|
-
height: 16px;
|
|
41
|
-
padding: 0 5px;
|
|
42
|
-
font-family: var(--pl-font-mono);
|
|
43
|
-
font-size: 10px;
|
|
44
|
-
line-height: 1;
|
|
45
|
-
color: var(--pl-color-fg-muted);
|
|
46
|
-
background: var(--pl-color-bg-subtle);
|
|
47
|
-
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
48
|
-
border-radius: 999px;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.pl-tab--active .pl-tab__badge {
|
|
35
|
+
/* count badge is the shared .pl-count (primitives.css) */
|
|
36
|
+
.pl-tab--active .pl-count {
|
|
52
37
|
color: var(--pl-color-fg);
|
|
53
38
|
}
|
|
54
39
|
|
|
@@ -315,74 +300,22 @@
|
|
|
315
300
|
text-overflow: ellipsis;
|
|
316
301
|
white-space: nowrap;
|
|
317
302
|
}
|
|
318
|
-
.pl-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
}
|
|
303
|
+
/* inline rename uses the shared EditableText control (.pl-editable__input) */
|
|
304
|
+
/* count badge is the shared .pl-count (primitives.css) */
|
|
305
|
+
/* close / add extend the shared .pl-iconbtn (primitives.css) — deltas only */
|
|
345
306
|
.pl-tabbar__close {
|
|
346
|
-
display: inline-flex;
|
|
347
|
-
align-items: center;
|
|
348
|
-
justify-content: center;
|
|
349
307
|
width: 18px;
|
|
350
308
|
height: 18px;
|
|
351
309
|
margin-right: -3px;
|
|
352
|
-
padding: 0;
|
|
353
310
|
color: var(--pl-color-fg-subtle);
|
|
354
|
-
background: none;
|
|
355
|
-
border: none;
|
|
356
|
-
border-radius: var(--pl-radius);
|
|
357
|
-
cursor: pointer;
|
|
358
311
|
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);
|
|
312
|
+
transition: opacity var(--pl-motion-fast) var(--pl-motion-ease);
|
|
363
313
|
}
|
|
364
314
|
.pl-tabbar__close:hover {
|
|
365
|
-
color: var(--pl-color-fg);
|
|
366
|
-
background: var(--pl-color-bg-hover);
|
|
367
315
|
opacity: 1;
|
|
368
316
|
}
|
|
369
317
|
.pl-tabbar__add {
|
|
370
|
-
display: inline-flex;
|
|
371
|
-
align-items: center;
|
|
372
|
-
justify-content: center;
|
|
373
318
|
width: 28px;
|
|
374
319
|
flex-shrink: 0;
|
|
375
320
|
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
321
|
}
|
package/src/styles/overlays.css
CHANGED
|
@@ -48,28 +48,14 @@
|
|
|
48
48
|
color: var(--pl-color-fg);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
51
52
|
.pl-dialog__close {
|
|
52
|
-
display: inline-flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
justify-content: center;
|
|
55
53
|
width: 24px;
|
|
56
54
|
height: 24px;
|
|
57
55
|
font-size: 18px;
|
|
58
56
|
line-height: 1;
|
|
59
|
-
color: var(--pl-color-fg-muted);
|
|
60
|
-
background: transparent;
|
|
61
|
-
border: none;
|
|
62
|
-
border-radius: var(--pl-radius);
|
|
63
|
-
cursor: pointer;
|
|
64
|
-
transition:
|
|
65
|
-
color var(--pl-motion-fast) var(--pl-motion-ease),
|
|
66
|
-
background var(--pl-motion-fast) var(--pl-motion-ease);
|
|
67
57
|
}
|
|
68
58
|
|
|
69
|
-
.pl-dialog__close:hover {
|
|
70
|
-
color: var(--pl-color-fg);
|
|
71
|
-
background: var(--pl-color-bg-hover);
|
|
72
|
-
}
|
|
73
59
|
|
|
74
60
|
.pl-dialog__body {
|
|
75
61
|
padding: var(--pl-space-4);
|
|
@@ -228,19 +214,13 @@
|
|
|
228
214
|
color: var(--pl-color-fg-muted);
|
|
229
215
|
}
|
|
230
216
|
|
|
217
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
231
218
|
.pl-toast__close {
|
|
232
219
|
flex-shrink: 0;
|
|
233
220
|
font-size: 16px;
|
|
234
221
|
line-height: 1;
|
|
235
|
-
color: var(--pl-color-fg-muted);
|
|
236
|
-
background: transparent;
|
|
237
|
-
border: none;
|
|
238
|
-
cursor: pointer;
|
|
239
222
|
}
|
|
240
223
|
|
|
241
|
-
.pl-toast__close:hover {
|
|
242
|
-
color: var(--pl-color-fg);
|
|
243
|
-
}
|
|
244
224
|
|
|
245
225
|
/* ── Tooltip (CSS-only) ──────────────────────────────────────────────────────── */
|
|
246
226
|
.pl-tip-wrap {
|
|
@@ -135,6 +135,45 @@
|
|
|
135
135
|
margin-top: var(--pl-space-2);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/* ── count badge (round numeric pill) — shared by Tabs / TabBar / SurfaceRail.
|
|
139
|
+
Distinct from .pl-badge (a lowercase text/label pill). ── */
|
|
140
|
+
.pl-count {
|
|
141
|
+
display: inline-flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
justify-content: center;
|
|
144
|
+
min-width: 16px;
|
|
145
|
+
height: 16px;
|
|
146
|
+
padding: 0 5px;
|
|
147
|
+
font-family: var(--pl-font-mono);
|
|
148
|
+
font-size: 10px;
|
|
149
|
+
line-height: 1;
|
|
150
|
+
color: var(--pl-color-fg-muted);
|
|
151
|
+
background: var(--pl-color-bg-subtle);
|
|
152
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
153
|
+
border-radius: 999px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ── icon button (bare clickable glyph) — shared base for close / add / copy /
|
|
157
|
+
dialog + toast dismiss. Each use adds its own size/margin delta. ── */
|
|
158
|
+
.pl-iconbtn {
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
padding: 0;
|
|
163
|
+
color: var(--pl-color-fg-muted);
|
|
164
|
+
background: none;
|
|
165
|
+
border: none;
|
|
166
|
+
border-radius: var(--pl-radius);
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
transition:
|
|
169
|
+
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
170
|
+
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
171
|
+
}
|
|
172
|
+
.pl-iconbtn:hover {
|
|
173
|
+
color: var(--pl-color-fg);
|
|
174
|
+
background: var(--pl-color-bg-hover);
|
|
175
|
+
}
|
|
176
|
+
|
|
138
177
|
/* ── divider ── */
|
|
139
178
|
.pl-divider {
|
|
140
179
|
border: 0;
|
package/src/styles/tool-card.css
CHANGED
|
@@ -112,13 +112,9 @@
|
|
|
112
112
|
.pl-toolcard__status--running {
|
|
113
113
|
color: var(--pl-color-status-warning);
|
|
114
114
|
}
|
|
115
|
+
/* reuses the shared `pl-spin` keyframe (data.css) */
|
|
115
116
|
.pl-toolcard__spin {
|
|
116
|
-
animation: pl-
|
|
117
|
-
}
|
|
118
|
-
@keyframes pl-toolcard-spin {
|
|
119
|
-
to {
|
|
120
|
-
transform: rotate(360deg);
|
|
121
|
-
}
|
|
117
|
+
animation: pl-spin 0.8s linear infinite;
|
|
122
118
|
}
|
|
123
119
|
|
|
124
120
|
.pl-toolcard__body {
|
|
@@ -160,21 +156,7 @@
|
|
|
160
156
|
letter-spacing: 0.04em;
|
|
161
157
|
color: var(--pl-color-fg-subtle);
|
|
162
158
|
}
|
|
159
|
+
/* extends the shared .pl-iconbtn (primitives.css) — delta only */
|
|
163
160
|
.pl-toolcard__copy {
|
|
164
|
-
display: inline-flex;
|
|
165
|
-
align-items: center;
|
|
166
|
-
justify-content: center;
|
|
167
161
|
padding: 2px;
|
|
168
|
-
background: none;
|
|
169
|
-
border: none;
|
|
170
|
-
border-radius: var(--pl-radius);
|
|
171
|
-
color: var(--pl-color-fg-muted);
|
|
172
|
-
cursor: pointer;
|
|
173
|
-
transition:
|
|
174
|
-
background var(--pl-motion-fast) var(--pl-motion-ease),
|
|
175
|
-
color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
176
|
-
}
|
|
177
|
-
.pl-toolcard__copy:hover {
|
|
178
|
-
color: var(--pl-color-fg);
|
|
179
|
-
background: var(--pl-color-bg-hover);
|
|
180
162
|
}
|
package/src/tool-card.tsx
CHANGED
|
@@ -133,7 +133,7 @@ function CopyButton({ text }: { text: string }) {
|
|
|
133
133
|
return (
|
|
134
134
|
<button
|
|
135
135
|
type="button"
|
|
136
|
-
className="pl-toolcard__copy"
|
|
136
|
+
className="pl-iconbtn pl-toolcard__copy"
|
|
137
137
|
title="Copy to clipboard"
|
|
138
138
|
aria-label={copied ? "Copied" : "Copy"}
|
|
139
139
|
onClick={async () => {
|