@orbytes/astrolab 0.3.0 → 0.4.0-next.2
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 +261 -116
- package/bin/pin-gallery.mjs +53 -19
- package/defaults.mjs +72 -20
- package/dist/core/virtual-module/virtual-routes.js +12 -1
- package/docs/PIN-CONTRACT.md +84 -10
- package/docs/PIN.md +117 -37
- package/index.d.ts +41 -23
- package/index.mjs +46 -91
- package/package.json +7 -3
- package/src/Home.astro +166 -264
- package/src/LabHead.astro +37 -1047
- package/src/chrome/ActionsMenu.astro +97 -0
- package/src/chrome/ComponentCard.astro +76 -0
- package/src/chrome/Icon.astro +21 -0
- package/src/chrome/LICENSE-icons +43 -0
- package/src/chrome/Nav.astro +131 -0
- package/src/chrome/Panel.astro +117 -0
- package/src/chrome/Properties.astro +104 -0
- package/src/chrome/SectionsTree.astro +128 -0
- package/src/chrome/Shell.astro +120 -0
- package/src/chrome/Sprite.astro +23 -0
- package/src/chrome/StoryView.astro +192 -0
- package/src/chrome/Tree.astro +86 -0
- package/src/chrome/ViewportControls.astro +173 -0
- package/src/chrome/ViewportStage.astro +55 -0
- package/src/chrome/fonts/OFL.txt +93 -0
- package/src/chrome/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/src/chrome/icons.ts +68 -0
- package/src/chrome/marks-client.ts +75 -0
- package/src/chrome/model.ts +156 -0
- package/src/chrome/navbar-client.ts +324 -0
- package/src/chrome/params-client.ts +434 -0
- package/src/chrome/pins-data.ts +63 -0
- package/src/chrome/shell-client.ts +468 -0
- package/src/chrome/site-data.ts +230 -0
- package/src/chrome/trees.ts +257 -0
- package/src/chrome/viewport-client.ts +405 -0
- package/src/chrome/views/Assets.astro +125 -0
- package/src/chrome/views/Pages.astro +214 -0
- package/src/chrome/views/Placeholder.astro +37 -0
- package/src/chrome/views/Tasks.astro +79 -0
- package/src/core/LICENSE-astrobook +21 -0
- package/src/core/lib/components/home.astro +4 -2
- package/src/core/lib/pages/story.astro +12 -10
- package/src/core/utils/kebab-case.ts +2 -2
- package/src/core/virtual-module/virtual-routes.ts +20 -4
- package/src/pin/board.mjs +414 -190
- package/src/pin/index.mjs +67 -22
- package/src/pin/tickets.mjs +6 -5
- package/src/pin/toolbar.js +82 -4
- package/src/shell/Browse.astro +131 -353
- package/src/shell/Viewport.astro +22 -1315
- package/src/shell/lab-index.ts +28 -18
- package/src/shell/lab-params.ts +113 -6
- package/src/shell/live-files.mjs +212 -10
- package/src/shell/marks.mjs +17 -41
- package/src/ui/components/app.astro +5 -7
- package/src/ui/components/preview-layout.astro +17 -0
- package/src/ui/components/theme-script.astro +17 -5
- package/src/ui/lab.css +3754 -371
- package/virtual.d.ts +13 -4
- package/bin/lab-cull.mjs +0 -401
- package/src/shell/CardGrid.astro +0 -297
- package/src/ui/components/build-path.ts +0 -13
- package/src/ui/components/build-tree.ts +0 -108
- package/src/ui/components/collapse-duration.ts +0 -28
- package/src/ui/components/compress-terms.ts +0 -10
- package/src/ui/components/dashboard-layout.astro +0 -39
- package/src/ui/components/home.astro +0 -65
- package/src/ui/components/layout.astro +0 -110
- package/src/ui/components/sidebar-button-fullscreen.astro +0 -38
- package/src/ui/components/sidebar-button-search.astro +0 -23
- package/src/ui/components/sidebar-button-theme.astro +0 -9
- package/src/ui/components/sidebar-button.astro +0 -24
- package/src/ui/components/sidebar-resize-handle.astro +0 -74
- package/src/ui/components/sidebar-search-panel.astro +0 -41
- package/src/ui/components/sidebar-search-script.ts +0 -103
- package/src/ui/components/sidebar-title.astro +0 -17
- package/src/ui/components/sidebar-tree-node.astro +0 -143
- package/src/ui/components/sidebar-tree.astro +0 -84
- package/src/ui/components/sidebar.astro +0 -29
- package/src/ui/components/theme-toggle.astro +0 -63
package/src/Home.astro
CHANGED
|
@@ -1,47 +1,69 @@
|
|
|
1
1
|
---
|
|
2
|
-
// Lab home — Astrobook's `home` component, given to it by the integration (../index.mjs)
|
|
3
|
-
//
|
|
4
|
-
// at <subpath>/browse/<tier>), then every story as a live thumbnail card grouped by tier, each
|
|
5
|
-
// card carrying its derived pill — live slot, used-by, or nothing.
|
|
2
|
+
// Lab home — Astrobook's `home` component, given to it by the integration (../index.mjs) and
|
|
3
|
+
// rendered as the whole `/lab` document (./ui/components/app.astro).
|
|
6
4
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
5
|
+
// A summary, not a gallery (redesigned 2026-09-24). The previous home drew every story in the lab
|
|
6
|
+
// as a live <iframe> thumbnail — forty-odd full renders of the site, each loading its fonts and
|
|
7
|
+
// stylesheets, before you could click anything. Those cards now live one tier at a time on the
|
|
8
|
+
// tier pages, lazily; the home page is the counts and the one thing only this lab can tell you —
|
|
9
|
+
// what is mounted on which page, in which slot, right now.
|
|
11
10
|
import { execSync } from "node:child_process";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
sortTiers,
|
|
19
|
-
STAGING_URL,
|
|
20
|
-
tierLabel,
|
|
21
|
-
} from "./shell/lab-index";
|
|
11
|
+
import Shell from "./chrome/Shell.astro";
|
|
12
|
+
import Icon from "./chrome/Icon.astro";
|
|
13
|
+
import { hrefs, labConfig, tasksBase } from "./chrome/model";
|
|
14
|
+
import { componentsOf } from "./chrome/trees";
|
|
15
|
+
import { rootDir, siteAssetsModel, sitePagesModel } from "./chrome/site-data";
|
|
16
|
+
import { buildLabIndex, STAGING_URL, sortTiers, tierLabel } from "./shell/lab-index";
|
|
22
17
|
|
|
23
18
|
const index = buildLabIndex();
|
|
24
|
-
const
|
|
25
|
-
|
|
19
|
+
const components = componentsOf(index);
|
|
20
|
+
const pages = sitePagesModel();
|
|
21
|
+
const assets = siteAssetsModel();
|
|
22
|
+
|
|
23
|
+
const tiers = sortTiers([...new Set(components.map((c) => c.tier))]).map((tier) => {
|
|
24
|
+
const mods = components.filter((c) => c.tier === tier);
|
|
26
25
|
return {
|
|
27
26
|
tier,
|
|
28
27
|
label: tierLabel(tier),
|
|
29
|
-
href: tier
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
href: hrefs.tier(tier),
|
|
29
|
+
components: mods.length,
|
|
30
|
+
stories: mods.reduce((n, m) => n + m.stories.length, 0),
|
|
31
|
+
live: mods.filter((m) => m.live).length,
|
|
32
|
+
used: mods.filter((m) => !m.live && m.usedBy.length).length,
|
|
33
|
+
unused: mods.filter((m) => !m.live && !m.usedBy.length).length,
|
|
33
34
|
};
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
// The pin board's counts, in dev only (tasksBase is null in every build).
|
|
38
|
+
let tasks: { open: number; review: number } | null = null;
|
|
39
|
+
if (tasksBase && labConfig.tasks) {
|
|
40
|
+
const { collectTickets, OPEN_STATUSES, READY_FOR_REVIEW } = await import("./pin/board.mjs");
|
|
41
|
+
try {
|
|
42
|
+
const { tickets } = collectTickets(labConfig.tasks.repoRoot, { backlogDir: labConfig.tasks.backlogDir });
|
|
43
|
+
tasks = {
|
|
44
|
+
open: tickets.filter((t: { status: string }) => OPEN_STATUSES.includes(t.status)).length,
|
|
45
|
+
review: tickets.filter((t: { status: string }) => t.status === READY_FOR_REVIEW).length,
|
|
46
|
+
};
|
|
47
|
+
} catch {
|
|
48
|
+
tasks = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// What is on the site: every page, and in slot order what it mounts, with the lab component it is.
|
|
53
|
+
const byComponentFile = new Map(components.filter((c) => c.componentFile).map((c) => [c.componentFile!, c]));
|
|
54
|
+
const onSite = pages
|
|
55
|
+
.filter((page) => page.mounts.length > 0)
|
|
56
|
+
.map((page) => ({
|
|
57
|
+
page,
|
|
58
|
+
slots: page.mounts.map((file, i) => ({ slot: i + 1, file, component: byComponentFile.get(file) ?? null })),
|
|
59
|
+
}));
|
|
60
|
+
|
|
36
61
|
// Local branches → per-branch preview builds, when the consumer configured a URL template.
|
|
37
|
-
// `previewUrlTemplate` is a string with a {branch} placeholder rather than a function because the
|
|
38
|
-
// config crosses a virtual module and has to stay JSON-serialisable. No template, no branch list:
|
|
39
|
-
// a host that does not build branches has nothing to link to.
|
|
40
62
|
const previewTemplate = labConfig.previewUrlTemplate;
|
|
41
63
|
let branches: string[] = [];
|
|
42
64
|
if (previewTemplate) {
|
|
43
65
|
try {
|
|
44
|
-
branches = execSync('git branch --format="%(refname:short)"', { encoding: "utf8" })
|
|
66
|
+
branches = execSync('git branch --format="%(refname:short)"', { encoding: "utf8", cwd: rootDir })
|
|
45
67
|
.split("\n")
|
|
46
68
|
.map((line) => line.trim())
|
|
47
69
|
.filter(Boolean);
|
|
@@ -52,247 +74,127 @@ if (previewTemplate) {
|
|
|
52
74
|
const previewAlias = (branch: string) =>
|
|
53
75
|
(previewTemplate ?? "").replace(
|
|
54
76
|
/\{branch\}/g,
|
|
55
|
-
branch
|
|
56
|
-
.toLowerCase()
|
|
57
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
58
|
-
.replace(/^-+|-+$/g, ""),
|
|
77
|
+
branch.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""),
|
|
59
78
|
);
|
|
79
|
+
const unusedAssets = assets.filter((a) => a.usedBy.length === 0).length;
|
|
60
80
|
---
|
|
61
81
|
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
<Shell title="Home" active="home" navbarClass="lab-navbar--listing">
|
|
83
|
+
<Fragment slot="navbar">
|
|
84
|
+
<div class="lab-listing-bar">
|
|
85
|
+
<h1 class="lab-listing-bar__title">Home</h1>
|
|
86
|
+
<p class="lab-listing-bar__counts">{labConfig.title}</p>
|
|
87
|
+
{
|
|
88
|
+
STAGING_URL && (
|
|
89
|
+
<a class="lab-btn lab-btn--sm lab-listing-bar__end" href={STAGING_URL} target="_blank" rel="noopener">
|
|
90
|
+
<Icon name="external" />
|
|
91
|
+
Staging
|
|
92
|
+
</a>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
</div>
|
|
96
|
+
</Fragment>
|
|
97
|
+
|
|
98
|
+
<div class="lab-page">
|
|
99
|
+
<header class="lab-page__head">
|
|
100
|
+
<h2 class="lab-page__title">What is on this site</h2>
|
|
101
|
+
<p class="lab-page__lede">
|
|
102
|
+
Every page, section and component in the build, rendered live — and which of them the site
|
|
103
|
+
actually mounts right now, read from <code>src/pages/</code> rather than declared.
|
|
104
|
+
</p>
|
|
105
|
+
</header>
|
|
106
|
+
|
|
107
|
+
<div class="lab-stats">
|
|
108
|
+
<a class="lab-stat" href={hrefs.pages}>
|
|
109
|
+
<span class="lab-stat__head"><Icon name="pages" />Pages</span>
|
|
110
|
+
<span class="lab-stat__value">{pages.length}</span>
|
|
111
|
+
<span class="lab-stat__foot">
|
|
112
|
+
<span class="lab-pill">{onSite.length} with components</span>
|
|
113
|
+
{pages.some((p) => p.dynamic) && <span class="lab-pill">{pages.filter((p) => p.dynamic).length} dynamic</span>}
|
|
114
|
+
</span>
|
|
115
|
+
</a>
|
|
116
|
+
{
|
|
117
|
+
tiers.map((tier) => (
|
|
118
|
+
<a class="lab-stat" href={tier.href}>
|
|
119
|
+
<span class="lab-stat__head">
|
|
120
|
+
<Icon name={tier.tier === labConfig.sectionsTier ? "sections" : tier.tier === "components" ? "components" : "component"} />
|
|
121
|
+
{tier.label}
|
|
122
|
+
</span>
|
|
123
|
+
<span class="lab-stat__value">{tier.components}</span>
|
|
124
|
+
<span class="lab-stat__foot">
|
|
125
|
+
{tier.live > 0 && <span class="lab-pill lab-pill--live">{tier.live} live</span>}
|
|
126
|
+
{tier.used > 0 && <span class="lab-pill lab-pill--used">{tier.used} used</span>}
|
|
127
|
+
{tier.unused > 0 && <span class="lab-pill">{tier.unused} unused</span>}
|
|
128
|
+
<span class="lab-pill">{tier.stories} variant{tier.stories === 1 ? "" : "s"}</span>
|
|
129
|
+
</span>
|
|
130
|
+
</a>
|
|
131
|
+
))
|
|
132
|
+
}
|
|
133
|
+
<a class="lab-stat" href={hrefs.assets}>
|
|
134
|
+
<span class="lab-stat__head"><Icon name="assets" />Assets</span>
|
|
135
|
+
<span class="lab-stat__value">{assets.length}</span>
|
|
136
|
+
<span class="lab-stat__foot">
|
|
137
|
+
{unusedAssets > 0 ? <span class="lab-pill lab-pill--unresponsive">{unusedAssets} unused</span> : <span class="lab-pill">all referenced</span>}
|
|
138
|
+
</span>
|
|
139
|
+
</a>
|
|
140
|
+
{
|
|
141
|
+
tasks && tasksBase && (
|
|
142
|
+
<a class="lab-stat" href={tasksBase}>
|
|
143
|
+
<span class="lab-stat__head"><Icon name="tasks" />Tasks</span>
|
|
144
|
+
<span class="lab-stat__value">{tasks.open}</span>
|
|
145
|
+
<span class="lab-stat__foot">
|
|
146
|
+
{tasks.review > 0 ? <span class="lab-pill lab-pill--brand">{tasks.review} ready for review</span> : <span class="lab-pill">open</span>}
|
|
147
|
+
</span>
|
|
148
|
+
</a>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
</div>
|
|
84
152
|
|
|
85
|
-
<section class="lab-home__tiers">
|
|
86
153
|
{
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
onSite.map(({ page, slots }) => (
|
|
155
|
+
<section>
|
|
156
|
+
<h3 class="lab-section-title">
|
|
157
|
+
<a href={page.href}>{page.label}</a>
|
|
158
|
+
<small>{page.route} · {slots.length} mounted</small>
|
|
159
|
+
</h3>
|
|
160
|
+
<ol class="lab-slots">
|
|
161
|
+
{slots.map(({ slot, file, component }) => (
|
|
162
|
+
<li class="lab-slots__row">
|
|
163
|
+
<span class="lab-slots__n">{slot}</span>
|
|
164
|
+
{component ? (
|
|
165
|
+
<a class="lab-slots__name" href={component.stories[0]!.dashboardUrl}>
|
|
166
|
+
{component.moduleName}
|
|
167
|
+
{component.version && <span class="lab-card__version">{component.version}</span>}
|
|
168
|
+
</a>
|
|
169
|
+
) : (
|
|
170
|
+
<span class="lab-slots__name lab-slots__name--bare" title="Not in the lab — no *.stories.ts file for it">
|
|
171
|
+
{file.split("/").pop()}
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
<span class="lab-slots__file lab-mono">{file}</span>
|
|
175
|
+
</li>
|
|
176
|
+
))}
|
|
177
|
+
</ol>
|
|
178
|
+
</section>
|
|
98
179
|
))
|
|
99
180
|
}
|
|
100
|
-
</section>
|
|
101
|
-
|
|
102
|
-
{
|
|
103
|
-
branches.length > 0 && (
|
|
104
|
-
<section class="lab-home__branches">
|
|
105
|
-
<h2 class="lab-home__group-title">Branches</h2>
|
|
106
|
-
<ul>
|
|
107
|
-
{branches.map((branch) => (
|
|
108
|
-
<li>
|
|
109
|
-
<code>{branch}</code>
|
|
110
|
-
<a href={previewAlias(branch)} target="_blank" rel="noopener">
|
|
111
|
-
branch preview
|
|
112
|
-
</a>
|
|
113
|
-
</li>
|
|
114
|
-
))}
|
|
115
|
-
</ul>
|
|
116
|
-
</section>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
{
|
|
121
|
-
tiers.map((tier) => (
|
|
122
|
-
<section class="lab-home__group">
|
|
123
|
-
<h2 class="lab-home__group-title">
|
|
124
|
-
<a href={tier.href}>{tier.label}</a>
|
|
125
|
-
</h2>
|
|
126
|
-
<CardGrid items={tier.items} />
|
|
127
|
-
</section>
|
|
128
|
-
))
|
|
129
|
-
}
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<style>
|
|
133
|
-
.lab-home {
|
|
134
|
-
background-color: var(--lab-color-bg);
|
|
135
|
-
color: var(--lab-color-text);
|
|
136
|
-
font-family: var(--lab-font-body);
|
|
137
|
-
min-height: 100%;
|
|
138
|
-
padding: 3em 2.5em 5em;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.lab-home__eyebrow {
|
|
142
|
-
font-family: var(--lab-font-mono);
|
|
143
|
-
font-weight: 500;
|
|
144
|
-
color: var(--lab-color-text-muted);
|
|
145
|
-
margin-bottom: 0.75em;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.lab-home__title {
|
|
149
|
-
font-family: var(--lab-font-heading);
|
|
150
|
-
color: var(--lab-color-text-strong);
|
|
151
|
-
font-size: 2.25em;
|
|
152
|
-
font-weight: 500;
|
|
153
|
-
letter-spacing: -0.5px;
|
|
154
|
-
margin-bottom: 0.4em;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.lab-home__intro {
|
|
158
|
-
max-width: 42em;
|
|
159
|
-
color: var(--lab-color-text-faint);
|
|
160
|
-
line-height: 1.55;
|
|
161
|
-
margin-bottom: 1.5em;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.lab-home__staging {
|
|
165
|
-
display: inline-block;
|
|
166
|
-
font-family: var(--lab-font-mono);
|
|
167
|
-
font-weight: 500;
|
|
168
|
-
color: var(--lab-color-text-strong);
|
|
169
|
-
text-decoration: none;
|
|
170
|
-
border: 1px solid var(--lab-color-border);
|
|
171
|
-
border-left: 1px solid var(--lab-color-text-strong);
|
|
172
|
-
padding: 0.75em 1.25em;
|
|
173
|
-
transition: background-color var(--lab-transition), color var(--lab-transition);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.lab-home__staging:hover,
|
|
177
|
-
.lab-home__staging:focus-visible {
|
|
178
|
-
background-color: var(--lab-color-text-strong);
|
|
179
|
-
color: var(--lab-color-accent-text);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.lab-home__tiers {
|
|
183
|
-
display: grid;
|
|
184
|
-
grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));
|
|
185
|
-
gap: 1em;
|
|
186
|
-
margin-top: 2.5em;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.tier-card {
|
|
190
|
-
display: flex;
|
|
191
|
-
flex-direction: column;
|
|
192
|
-
gap: 0.5em;
|
|
193
|
-
text-decoration: none;
|
|
194
|
-
border: 1px solid var(--lab-color-border);
|
|
195
|
-
background-color: var(--lab-color-surface);
|
|
196
|
-
padding: 1.25em 1.5em;
|
|
197
|
-
transition: border-color var(--lab-transition);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.tier-card:hover,
|
|
201
|
-
.tier-card:focus-visible {
|
|
202
|
-
border-color: var(--lab-color-border-strong);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.tier-card__eyebrow {
|
|
206
|
-
font-family: var(--lab-font-mono);
|
|
207
|
-
font-weight: 500;
|
|
208
|
-
font-size: 0.75em;
|
|
209
|
-
color: var(--lab-color-text-faint);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.tier-card__name {
|
|
213
|
-
font-family: var(--lab-font-heading);
|
|
214
|
-
font-size: 1.4em;
|
|
215
|
-
font-weight: 500;
|
|
216
|
-
color: var(--lab-color-text-strong);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.tier-card__meta {
|
|
220
|
-
display: flex;
|
|
221
|
-
align-items: baseline;
|
|
222
|
-
justify-content: space-between;
|
|
223
|
-
gap: 1em;
|
|
224
|
-
font-family: var(--lab-font-mono);
|
|
225
|
-
font-size: 0.8em;
|
|
226
|
-
color: var(--lab-color-text-faint);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.tier-card__live {
|
|
230
|
-
font-weight: 500;
|
|
231
|
-
line-height: 1;
|
|
232
|
-
padding: 0.45em 0.85em;
|
|
233
|
-
border-radius: var(--lab-radius-pill);
|
|
234
|
-
background-color: var(--lab-color-accent);
|
|
235
|
-
color: var(--lab-color-accent-text);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.lab-home__branches {
|
|
239
|
-
margin-top: 2.5em;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.lab-home__branches ul {
|
|
243
|
-
list-style: none;
|
|
244
|
-
padding: 0;
|
|
245
|
-
margin: 0;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.lab-home__branches li {
|
|
249
|
-
display: flex;
|
|
250
|
-
align-items: baseline;
|
|
251
|
-
gap: 1em;
|
|
252
|
-
padding: 0.5em 0;
|
|
253
|
-
border-bottom: 1px solid var(--lab-color-border);
|
|
254
|
-
}
|
|
255
181
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
margin-top: 3em;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
.lab-home__group-title {
|
|
279
|
-
font-family: var(--lab-font-mono);
|
|
280
|
-
font-weight: 500;
|
|
281
|
-
font-size: 1em;
|
|
282
|
-
color: var(--lab-color-text-muted);
|
|
283
|
-
margin-bottom: 1em;
|
|
284
|
-
padding-bottom: 0.5em;
|
|
285
|
-
border-bottom: 1px solid var(--lab-color-border);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.lab-home__group-title a {
|
|
289
|
-
color: inherit;
|
|
290
|
-
text-decoration: none;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
.lab-home__group-title a:hover,
|
|
294
|
-
.lab-home__group-title a:focus-visible {
|
|
295
|
-
color: var(--lab-color-text-strong);
|
|
296
|
-
text-decoration: underline;
|
|
297
|
-
}
|
|
298
|
-
</style>
|
|
182
|
+
{
|
|
183
|
+
branches.length > 0 && (
|
|
184
|
+
<section>
|
|
185
|
+
<h3 class="lab-section-title">Branches</h3>
|
|
186
|
+
<ol class="lab-slots">
|
|
187
|
+
{branches.map((branch) => (
|
|
188
|
+
<li class="lab-slots__row">
|
|
189
|
+
<span class="lab-slots__name lab-mono">{branch}</span>
|
|
190
|
+
<a class="lab-slots__file" href={previewAlias(branch)} target="_blank" rel="noopener">
|
|
191
|
+
branch preview ↗
|
|
192
|
+
</a>
|
|
193
|
+
</li>
|
|
194
|
+
))}
|
|
195
|
+
</ol>
|
|
196
|
+
</section>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
</div>
|
|
200
|
+
</Shell>
|