@protolabsai/ui 0.11.0 → 0.13.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/package.json +1 -1
- package/src/AppShell.full.stories.tsx +19 -2
- package/src/Changelog.stories.tsx +66 -0
- package/src/app-shell.tsx +44 -0
- package/src/marketing.tsx +67 -0
- package/src/primitives.tsx +16 -0
- package/src/styles/app-shell.css +56 -0
- package/src/styles/marketing.css +142 -0
- package/src/styles/primitives.css +7 -0
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import { Badge, Button } from "./primitives";
|
|
3
|
+
import { Badge, Button, Logo } from "./primitives";
|
|
4
4
|
import { PanelHeader } from "./navigation";
|
|
5
5
|
import { StatusDot } from "./data";
|
|
6
|
-
import { AppShell, MobileNav, SurfaceRail, UtilityBar } from "./app-shell";
|
|
6
|
+
import { AppShell, Header, MobileNav, SurfaceRail, UtilityBar } from "./app-shell";
|
|
7
7
|
import type { MobileItem, RailItem } from "./app-shell";
|
|
8
8
|
|
|
9
|
+
const LOGO =
|
|
10
|
+
"data:image/svg+xml," +
|
|
11
|
+
encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect width="24" height="24" rx="6" fill="#9b87f2"/></svg>');
|
|
12
|
+
|
|
9
13
|
const meta: Meta = { title: "Components/AppShell" };
|
|
10
14
|
export default meta;
|
|
11
15
|
type Story = StoryObj;
|
|
@@ -84,6 +88,19 @@ export const Full: Story = {
|
|
|
84
88
|
{surfaceBody(activeRight)}
|
|
85
89
|
</>
|
|
86
90
|
}
|
|
91
|
+
header={
|
|
92
|
+
<Header
|
|
93
|
+
logo={<Logo src={LOGO} alt="" />}
|
|
94
|
+
name="protoAgent"
|
|
95
|
+
org="protoLabs.studio"
|
|
96
|
+
status={<StatusDot status="success" pulse />}
|
|
97
|
+
actions={
|
|
98
|
+
<Button size="sm" variant="ghost">
|
|
99
|
+
Settings
|
|
100
|
+
</Button>
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
}
|
|
87
104
|
utilityBar={
|
|
88
105
|
<UtilityBar
|
|
89
106
|
start={<StatusDot status="success" pulse label="connected · 3 agents" />}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Changelog, ChangelogChange, ChangelogEntry, Heading, Lead } from "./marketing";
|
|
3
|
+
import { TextLink } from "./primitives";
|
|
4
|
+
|
|
5
|
+
const meta: Meta = { title: "Components/Marketing/Changelog", parameters: { layout: "fullscreen" } };
|
|
6
|
+
export default meta;
|
|
7
|
+
type Story = StoryObj;
|
|
8
|
+
|
|
9
|
+
const Page = ({ children }: { children: React.ReactNode }) => (
|
|
10
|
+
<div style={{ maxWidth: 680, margin: "0 auto", padding: "64px 24px" }}>
|
|
11
|
+
<Heading style={{ fontSize: "2rem" }}>Changelog</Heading>
|
|
12
|
+
<Lead>Every release, newest first.</Lead>
|
|
13
|
+
{children}
|
|
14
|
+
<div style={{ marginTop: 48, paddingTop: 24, borderTop: "1px solid var(--pl-color-border)" }}>
|
|
15
|
+
<p style={{ fontSize: 13, color: "var(--pl-color-fg-subtle)" }}>
|
|
16
|
+
Full release notes and source on <TextLink href="#" external>GitHub Releases</TextLink>.
|
|
17
|
+
</p>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/** User-facing — flat bullets, LLM-summarized voice. The marketing-site layout. */
|
|
23
|
+
export const UserFacing: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<Page>
|
|
26
|
+
<Changelog>
|
|
27
|
+
<ChangelogEntry version="@protolabsai/ui@0.11.0" date="Jun 9, 2026" latest href="#">
|
|
28
|
+
<ChangelogChange>Avatars and removable tags for identity and filters</ChangelogChange>
|
|
29
|
+
<ChangelogChange>A bottom utility bar in the app shell for global status and actions</ChangelogChange>
|
|
30
|
+
<ChangelogChange>Form toggles now associate with external labels</ChangelogChange>
|
|
31
|
+
</ChangelogEntry>
|
|
32
|
+
<ChangelogEntry version="@protolabsai/ui@0.10.0" date="Jun 9, 2026" href="#">
|
|
33
|
+
<ChangelogChange>Popover — anchored floating content for filters and pickers</ChangelogChange>
|
|
34
|
+
</ChangelogEntry>
|
|
35
|
+
<ChangelogEntry version="@protolabsai/ui@0.9.0" date="Jun 9, 2026" href="#">
|
|
36
|
+
<ChangelogChange>Drag a rail icon to reorder, or across to the other rail</ChangelogChange>
|
|
37
|
+
</ChangelogEntry>
|
|
38
|
+
<ChangelogEntry version="@protolabsai/design@0.5.0" date="Jun 9, 2026" href="#">
|
|
39
|
+
<ChangelogChange>Light mode, OS-driven, with a white-label token set</ChangelogChange>
|
|
40
|
+
</ChangelogEntry>
|
|
41
|
+
</Changelog>
|
|
42
|
+
</Page>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Technical — typed entries (Keep-a-Changelog style) off the same layout. */
|
|
47
|
+
export const Technical: Story = {
|
|
48
|
+
render: () => (
|
|
49
|
+
<Page>
|
|
50
|
+
<Changelog>
|
|
51
|
+
<ChangelogEntry version="ui@0.11.0" date="2026-06-09" latest href="#">
|
|
52
|
+
<ChangelogChange tag="added">Avatar, AvatarGroup, Tag (removable chip)</ChangelogChange>
|
|
53
|
+
<ChangelogChange tag="added">AppShell utilityBar slot + UtilityBar container</ChangelogChange>
|
|
54
|
+
<ChangelogChange tag="fixed">Checkbox/Switch spread ...rest for external-label association (#155)</ChangelogChange>
|
|
55
|
+
</ChangelogEntry>
|
|
56
|
+
<ChangelogEntry version="ui@0.10.0" date="2026-06-09" href="#">
|
|
57
|
+
<ChangelogChange tag="added">Popover + PopoverClose (Radix)</ChangelogChange>
|
|
58
|
+
</ChangelogEntry>
|
|
59
|
+
<ChangelogEntry version="ui@0.9.0" date="2026-06-09" href="#">
|
|
60
|
+
<ChangelogChange tag="added">AppShell rail drag-and-drop + cross-rail (dnd-kit)</ChangelogChange>
|
|
61
|
+
<ChangelogChange tag="changed">Drag overlay reuses the rail-item renderer (keeps badge/dot)</ChangelogChange>
|
|
62
|
+
</ChangelogEntry>
|
|
63
|
+
</Changelog>
|
|
64
|
+
</Page>
|
|
65
|
+
),
|
|
66
|
+
};
|
package/src/app-shell.tsx
CHANGED
|
@@ -280,6 +280,9 @@ export type AppShellProps = {
|
|
|
280
280
|
onMobileSelect?: (id: string) => void;
|
|
281
281
|
quickBarIds?: string[];
|
|
282
282
|
mobileBreakpoint?: number;
|
|
283
|
+
/** Top 48px bar — brand lockup + status/actions. Presentation only; the host
|
|
284
|
+
* fills it (compose a `Header`). Renders on both desktop and mobile. */
|
|
285
|
+
header?: ReactNode;
|
|
283
286
|
/** Bottom 40px track — global utility actions / status / tickers. Presentation
|
|
284
287
|
* only; the host fills it (compose a `UtilityBar`). Desktop only. */
|
|
285
288
|
utilityBar?: ReactNode;
|
|
@@ -311,6 +314,7 @@ export function AppShell({
|
|
|
311
314
|
onMobileSelect,
|
|
312
315
|
quickBarIds,
|
|
313
316
|
mobileBreakpoint = 768,
|
|
317
|
+
header,
|
|
314
318
|
utilityBar,
|
|
315
319
|
className,
|
|
316
320
|
}: AppShellProps) {
|
|
@@ -433,6 +437,7 @@ export function AppShell({
|
|
|
433
437
|
if (isMobile && mobileItems && onMobileSelect && quickBarIds) {
|
|
434
438
|
return (
|
|
435
439
|
<div className={cx("pl-appshell", "pl-appshell--mobile", className)}>
|
|
440
|
+
{header != null && <div className="pl-appshell__header">{header}</div>}
|
|
436
441
|
<div className="pl-appshell__mobile-stage">{leftContent}</div>
|
|
437
442
|
<MobileNav
|
|
438
443
|
items={mobileItems}
|
|
@@ -450,6 +455,7 @@ export function AppShell({
|
|
|
450
455
|
|
|
451
456
|
const renderShell = (leftRail: ReactNode, rightRail: ReactNode) => (
|
|
452
457
|
<div className={cx("pl-appshell-frame", className)}>
|
|
458
|
+
{header != null && <div className="pl-appshell__header">{header}</div>}
|
|
453
459
|
<div className="pl-appshell">
|
|
454
460
|
{leftRail}
|
|
455
461
|
<main className="pl-appshell__col pl-appshell__col--left">{leftContent}</main>
|
|
@@ -539,3 +545,41 @@ export function UtilityBar({
|
|
|
539
545
|
</div>
|
|
540
546
|
);
|
|
541
547
|
}
|
|
548
|
+
|
|
549
|
+
/** White-label top-bar chrome (48px header row) — brand lockup (logo · name ·
|
|
550
|
+
* org) on the left, status + actions on the right. Pass to AppShell's `header`
|
|
551
|
+
* slot. `dragRegion` marks it as the desktop window drag region (Tauri). */
|
|
552
|
+
export function Header({
|
|
553
|
+
logo,
|
|
554
|
+
name,
|
|
555
|
+
org,
|
|
556
|
+
status,
|
|
557
|
+
actions,
|
|
558
|
+
dragRegion,
|
|
559
|
+
className,
|
|
560
|
+
}: {
|
|
561
|
+
logo?: ReactNode;
|
|
562
|
+
name?: ReactNode;
|
|
563
|
+
org?: ReactNode;
|
|
564
|
+
status?: ReactNode;
|
|
565
|
+
actions?: ReactNode;
|
|
566
|
+
dragRegion?: boolean;
|
|
567
|
+
className?: string;
|
|
568
|
+
}) {
|
|
569
|
+
return (
|
|
570
|
+
<div className={cx("pl-header", className)} {...(dragRegion ? { "data-tauri-drag-region": "" } : {})}>
|
|
571
|
+
<div className="pl-header__brand">
|
|
572
|
+
{logo}
|
|
573
|
+
{(name != null || org != null) && (
|
|
574
|
+
<div className="pl-header__lockup">
|
|
575
|
+
{name != null && <span className="pl-header__name">{name}</span>}
|
|
576
|
+
{org != null && <span className="pl-header__org">{org}</span>}
|
|
577
|
+
</div>
|
|
578
|
+
)}
|
|
579
|
+
</div>
|
|
580
|
+
<div className="pl-header__spacer" />
|
|
581
|
+
{status != null && <div className="pl-header__status">{status}</div>}
|
|
582
|
+
{actions != null && <div className="pl-header__actions">{actions}</div>}
|
|
583
|
+
</div>
|
|
584
|
+
);
|
|
585
|
+
}
|
package/src/marketing.tsx
CHANGED
|
@@ -93,3 +93,70 @@ export function PostItem({ meta, title, excerpt, href }: PostItemProps) {
|
|
|
93
93
|
export function Prose({ className, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
|
94
94
|
return <div className={cx("pl-prose", className)} {...rest} />;
|
|
95
95
|
}
|
|
96
|
+
|
|
97
|
+
// ── Changelog (release timeline) — the standard marketing changelog layout ────
|
|
98
|
+
|
|
99
|
+
/** Changelog timeline — newest first. Wrap ChangelogEntry children. */
|
|
100
|
+
export function Changelog({ className, ...rest }: HTMLAttributes<HTMLOListElement>) {
|
|
101
|
+
return <ol className={cx("pl-changelog", className)} {...rest} />;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** One release on the timeline. `latest` accents the dot + version pill. Put
|
|
105
|
+
* ChangelogChange children inside. */
|
|
106
|
+
export function ChangelogEntry({
|
|
107
|
+
version,
|
|
108
|
+
date,
|
|
109
|
+
latest,
|
|
110
|
+
href,
|
|
111
|
+
children,
|
|
112
|
+
}: {
|
|
113
|
+
version: ReactNode;
|
|
114
|
+
date?: ReactNode;
|
|
115
|
+
latest?: boolean;
|
|
116
|
+
/** Links the version pill (e.g. to the GitHub release / download). */
|
|
117
|
+
href?: string;
|
|
118
|
+
children: ReactNode;
|
|
119
|
+
}) {
|
|
120
|
+
return (
|
|
121
|
+
<li className={cx("pl-changelog__entry", latest && "pl-changelog__entry--latest")}>
|
|
122
|
+
<span className="pl-changelog__dot" aria-hidden />
|
|
123
|
+
<div className="pl-changelog__body">
|
|
124
|
+
<div className="pl-changelog__meta">
|
|
125
|
+
{href ? (
|
|
126
|
+
<a className="pl-changelog__version" href={href}>
|
|
127
|
+
{version}
|
|
128
|
+
</a>
|
|
129
|
+
) : (
|
|
130
|
+
<span className="pl-changelog__version">{version}</span>
|
|
131
|
+
)}
|
|
132
|
+
{date != null && <span className="pl-changelog__date">{date}</span>}
|
|
133
|
+
{latest && <span className="pl-changelog__latest">latest</span>}
|
|
134
|
+
</div>
|
|
135
|
+
<div className="pl-changelog__changes">{children}</div>
|
|
136
|
+
</div>
|
|
137
|
+
</li>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** A single change line. Flat (em-dash) for user-facing notes, or pass a `tag`
|
|
142
|
+
* (added / changed / fixed / removed) for a technical changelog. */
|
|
143
|
+
export function ChangelogChange({
|
|
144
|
+
tag,
|
|
145
|
+
children,
|
|
146
|
+
}: {
|
|
147
|
+
tag?: "added" | "changed" | "fixed" | "removed";
|
|
148
|
+
children: ReactNode;
|
|
149
|
+
}) {
|
|
150
|
+
return (
|
|
151
|
+
<div className="pl-changelog__change">
|
|
152
|
+
{tag ? (
|
|
153
|
+
<span className={cx("pl-changelog__tag", `pl-changelog__tag--${tag}`)}>{tag}</span>
|
|
154
|
+
) : (
|
|
155
|
+
<span className="pl-changelog__bullet" aria-hidden>
|
|
156
|
+
—
|
|
157
|
+
</span>
|
|
158
|
+
)}
|
|
159
|
+
<span>{children}</span>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
package/src/primitives.tsx
CHANGED
|
@@ -154,3 +154,19 @@ export function Tag({
|
|
|
154
154
|
</span>
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
/** Brand mark — a token-sized, contained `<img>` (square by default). The
|
|
159
|
+
* white-label logo primitive (vs Avatar, which is a person/initials affordance). */
|
|
160
|
+
export function Logo({
|
|
161
|
+
src,
|
|
162
|
+
alt = "",
|
|
163
|
+
size = 22,
|
|
164
|
+
className,
|
|
165
|
+
}: {
|
|
166
|
+
src: string;
|
|
167
|
+
alt?: string;
|
|
168
|
+
size?: number;
|
|
169
|
+
className?: string;
|
|
170
|
+
}) {
|
|
171
|
+
return <img className={cx("pl-logo", className)} src={src} alt={alt} width={size} height={size} />;
|
|
172
|
+
}
|
package/src/styles/app-shell.css
CHANGED
|
@@ -274,3 +274,59 @@
|
|
|
274
274
|
box-shadow: var(--pl-shadow-popover);
|
|
275
275
|
cursor: grabbing;
|
|
276
276
|
}
|
|
277
|
+
|
|
278
|
+
/* ── Header (top bar) ─────────────────────────────────────────────────────────── */
|
|
279
|
+
.pl-appshell__header {
|
|
280
|
+
flex: 0 0 48px;
|
|
281
|
+
height: 48px;
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
border-bottom: var(--pl-border-width) solid var(--pl-color-border);
|
|
285
|
+
background: var(--pl-color-bg-raised);
|
|
286
|
+
}
|
|
287
|
+
.pl-header {
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
gap: var(--pl-space-3);
|
|
291
|
+
width: 100%;
|
|
292
|
+
height: 100%;
|
|
293
|
+
padding: 0 var(--pl-space-3);
|
|
294
|
+
}
|
|
295
|
+
.pl-header__brand {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: var(--pl-space-2);
|
|
299
|
+
min-width: 0;
|
|
300
|
+
}
|
|
301
|
+
.pl-header__lockup {
|
|
302
|
+
display: flex;
|
|
303
|
+
flex-direction: column;
|
|
304
|
+
justify-content: center;
|
|
305
|
+
min-width: 0;
|
|
306
|
+
line-height: 1.15;
|
|
307
|
+
}
|
|
308
|
+
.pl-header__name {
|
|
309
|
+
overflow: hidden;
|
|
310
|
+
font-size: 13px;
|
|
311
|
+
font-weight: var(--pl-font-weight-medium);
|
|
312
|
+
color: var(--pl-color-fg);
|
|
313
|
+
text-overflow: ellipsis;
|
|
314
|
+
white-space: nowrap;
|
|
315
|
+
}
|
|
316
|
+
.pl-header__org {
|
|
317
|
+
font-family: var(--pl-font-mono);
|
|
318
|
+
font-size: 10px;
|
|
319
|
+
color: var(--pl-color-fg-muted);
|
|
320
|
+
}
|
|
321
|
+
.pl-header__spacer {
|
|
322
|
+
flex: 1;
|
|
323
|
+
}
|
|
324
|
+
.pl-header__status {
|
|
325
|
+
display: flex;
|
|
326
|
+
align-items: center;
|
|
327
|
+
}
|
|
328
|
+
.pl-header__actions {
|
|
329
|
+
display: flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
gap: var(--pl-space-2);
|
|
332
|
+
}
|
package/src/styles/marketing.css
CHANGED
|
@@ -247,3 +247,145 @@
|
|
|
247
247
|
margin: 1.5rem 0;
|
|
248
248
|
color: var(--pl-color-fg-muted);
|
|
249
249
|
}
|
|
250
|
+
|
|
251
|
+
/* ── Changelog (release timeline) ─────────────────────────────────────────────── */
|
|
252
|
+
.pl-changelog {
|
|
253
|
+
position: relative;
|
|
254
|
+
display: grid;
|
|
255
|
+
gap: 40px;
|
|
256
|
+
margin: 0;
|
|
257
|
+
padding: 0;
|
|
258
|
+
list-style: none;
|
|
259
|
+
}
|
|
260
|
+
/* the timeline spine */
|
|
261
|
+
.pl-changelog::before {
|
|
262
|
+
content: "";
|
|
263
|
+
position: absolute;
|
|
264
|
+
left: 12px;
|
|
265
|
+
top: 8px;
|
|
266
|
+
bottom: 0;
|
|
267
|
+
width: 1px;
|
|
268
|
+
background: var(--pl-color-border);
|
|
269
|
+
}
|
|
270
|
+
.pl-changelog__entry {
|
|
271
|
+
position: relative;
|
|
272
|
+
display: flex;
|
|
273
|
+
gap: 20px;
|
|
274
|
+
}
|
|
275
|
+
.pl-changelog__dot {
|
|
276
|
+
position: relative;
|
|
277
|
+
z-index: 1;
|
|
278
|
+
flex-shrink: 0;
|
|
279
|
+
display: inline-flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
justify-content: center;
|
|
282
|
+
width: 25px;
|
|
283
|
+
height: 25px;
|
|
284
|
+
margin-top: 2px;
|
|
285
|
+
border-radius: 50%;
|
|
286
|
+
background: var(--pl-color-bg-raised);
|
|
287
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
288
|
+
}
|
|
289
|
+
.pl-changelog__dot::after {
|
|
290
|
+
content: "";
|
|
291
|
+
width: 6px;
|
|
292
|
+
height: 6px;
|
|
293
|
+
border-radius: 50%;
|
|
294
|
+
background: var(--pl-color-fg-subtle);
|
|
295
|
+
}
|
|
296
|
+
.pl-changelog__entry--latest .pl-changelog__dot {
|
|
297
|
+
background: color-mix(in oklch, var(--pl-color-accent) 15%, transparent);
|
|
298
|
+
border-color: color-mix(in oklch, var(--pl-color-accent) 50%, transparent);
|
|
299
|
+
}
|
|
300
|
+
.pl-changelog__entry--latest .pl-changelog__dot::after {
|
|
301
|
+
background: var(--pl-color-accent);
|
|
302
|
+
}
|
|
303
|
+
.pl-changelog__body {
|
|
304
|
+
flex: 1;
|
|
305
|
+
min-width: 0;
|
|
306
|
+
padding-bottom: 8px;
|
|
307
|
+
}
|
|
308
|
+
.pl-changelog__meta {
|
|
309
|
+
display: flex;
|
|
310
|
+
flex-wrap: wrap;
|
|
311
|
+
align-items: center;
|
|
312
|
+
gap: 12px;
|
|
313
|
+
margin-bottom: 12px;
|
|
314
|
+
}
|
|
315
|
+
.pl-changelog__version {
|
|
316
|
+
padding: 2px 8px;
|
|
317
|
+
font-family: var(--pl-font-mono);
|
|
318
|
+
font-size: 13px;
|
|
319
|
+
color: var(--pl-color-fg-muted);
|
|
320
|
+
background: var(--pl-color-bg-raised);
|
|
321
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
322
|
+
border-radius: var(--pl-radius);
|
|
323
|
+
text-decoration: none;
|
|
324
|
+
transition: border-color var(--pl-motion-fast) var(--pl-motion-ease);
|
|
325
|
+
}
|
|
326
|
+
a.pl-changelog__version:hover {
|
|
327
|
+
border-color: var(--pl-color-accent);
|
|
328
|
+
}
|
|
329
|
+
.pl-changelog__entry--latest .pl-changelog__version {
|
|
330
|
+
color: var(--pl-color-accent-fg);
|
|
331
|
+
background: color-mix(in oklch, var(--pl-color-accent) 10%, transparent);
|
|
332
|
+
border-color: color-mix(in oklch, var(--pl-color-accent) 35%, transparent);
|
|
333
|
+
}
|
|
334
|
+
.pl-changelog__date {
|
|
335
|
+
font-size: 13px;
|
|
336
|
+
color: var(--pl-color-fg-subtle);
|
|
337
|
+
}
|
|
338
|
+
.pl-changelog__latest {
|
|
339
|
+
padding: 1px 6px;
|
|
340
|
+
font-family: var(--pl-font-mono);
|
|
341
|
+
font-size: 11px;
|
|
342
|
+
color: var(--pl-color-accent-fg);
|
|
343
|
+
background: color-mix(in oklch, var(--pl-color-accent) 8%, transparent);
|
|
344
|
+
border: var(--pl-border-width) solid color-mix(in oklch, var(--pl-color-accent) 25%, transparent);
|
|
345
|
+
border-radius: var(--pl-radius);
|
|
346
|
+
}
|
|
347
|
+
.pl-changelog__changes {
|
|
348
|
+
display: grid;
|
|
349
|
+
gap: 6px;
|
|
350
|
+
}
|
|
351
|
+
.pl-changelog__change {
|
|
352
|
+
display: flex;
|
|
353
|
+
align-items: flex-start;
|
|
354
|
+
gap: 8px;
|
|
355
|
+
font-size: 14px;
|
|
356
|
+
line-height: 1.5;
|
|
357
|
+
color: var(--pl-color-fg);
|
|
358
|
+
}
|
|
359
|
+
.pl-changelog__bullet {
|
|
360
|
+
flex-shrink: 0;
|
|
361
|
+
margin-top: 1px;
|
|
362
|
+
color: var(--pl-color-fg-subtle);
|
|
363
|
+
}
|
|
364
|
+
.pl-changelog__tag {
|
|
365
|
+
flex-shrink: 0;
|
|
366
|
+
min-width: 52px;
|
|
367
|
+
padding: 0 6px;
|
|
368
|
+
font-family: var(--pl-font-mono);
|
|
369
|
+
font-size: 10px;
|
|
370
|
+
line-height: 18px;
|
|
371
|
+
text-align: center;
|
|
372
|
+
text-transform: lowercase;
|
|
373
|
+
border: var(--pl-border-width) solid var(--pl-color-border);
|
|
374
|
+
border-radius: var(--pl-radius);
|
|
375
|
+
}
|
|
376
|
+
.pl-changelog__tag--added {
|
|
377
|
+
color: var(--pl-color-status-success);
|
|
378
|
+
border-color: color-mix(in oklch, var(--pl-color-status-success) 35%, transparent);
|
|
379
|
+
}
|
|
380
|
+
.pl-changelog__tag--fixed {
|
|
381
|
+
color: var(--pl-color-status-info);
|
|
382
|
+
border-color: color-mix(in oklch, var(--pl-color-status-info) 35%, transparent);
|
|
383
|
+
}
|
|
384
|
+
.pl-changelog__tag--changed {
|
|
385
|
+
color: var(--pl-color-status-warning);
|
|
386
|
+
border-color: color-mix(in oklch, var(--pl-color-status-warning) 35%, transparent);
|
|
387
|
+
}
|
|
388
|
+
.pl-changelog__tag--removed {
|
|
389
|
+
color: var(--pl-color-status-error);
|
|
390
|
+
border-color: color-mix(in oklch, var(--pl-color-status-error) 35%, transparent);
|
|
391
|
+
}
|
|
@@ -285,3 +285,10 @@
|
|
|
285
285
|
color: var(--pl-color-fg);
|
|
286
286
|
background: var(--pl-color-bg-hover);
|
|
287
287
|
}
|
|
288
|
+
|
|
289
|
+
/* ── Logo ────────────────────────────────────────────────────────────────────── */
|
|
290
|
+
.pl-logo {
|
|
291
|
+
display: block;
|
|
292
|
+
flex-shrink: 0;
|
|
293
|
+
object-fit: contain;
|
|
294
|
+
}
|