@orbytes/astrolab 0.3.0 → 0.4.0-next.1
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 +109 -64
- package/defaults.mjs +65 -0
- package/dist/core/virtual-module/virtual-routes.js +12 -1
- package/docs/PIN-CONTRACT.md +8 -0
- package/docs/PIN.md +29 -19
- package/index.d.ts +40 -16
- package/index.mjs +32 -10
- package/package.json +6 -2
- package/src/Home.astro +167 -264
- package/src/LabHead.astro +37 -1047
- package/src/chrome/ComponentCard.astro +69 -0
- package/src/chrome/Icon.astro +21 -0
- package/src/chrome/LICENSE-icons +43 -0
- package/src/chrome/Nav.astro +105 -0
- package/src/chrome/Panel.astro +104 -0
- package/src/chrome/Shell.astro +106 -0
- package/src/chrome/Sprite.astro +23 -0
- package/src/chrome/StoryView.astro +251 -0
- package/src/chrome/Tree.astro +83 -0
- package/src/chrome/ViewportControls.astro +98 -0
- package/src/chrome/ViewportStage.astro +32 -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 +59 -0
- package/src/chrome/marks-client.ts +102 -0
- package/src/chrome/model.ts +142 -0
- package/src/chrome/pins-data.ts +30 -0
- package/src/chrome/shell-client.ts +372 -0
- package/src/chrome/site-data.ts +230 -0
- package/src/chrome/trees.ts +152 -0
- package/src/chrome/viewport-client.ts +579 -0
- package/src/chrome/views/Assets.astro +110 -0
- package/src/chrome/views/Pages.astro +178 -0
- package/src/chrome/views/Placeholder.astro +37 -0
- package/src/chrome/views/Tasks.astro +107 -0
- package/src/core/LICENSE-astrobook +16 -0
- package/src/core/lib/components/home.astro +4 -2
- package/src/core/lib/pages/story.astro +12 -10
- package/src/core/virtual-module/virtual-routes.ts +20 -4
- package/src/pin/board.mjs +389 -175
- package/src/pin/index.mjs +33 -2
- package/src/pin/toolbar.js +1 -1
- package/src/shell/Browse.astro +106 -353
- package/src/shell/Viewport.astro +22 -1315
- package/src/shell/lab-index.ts +23 -14
- package/src/ui/components/app.astro +5 -7
- package/src/ui/components/theme-script.astro +13 -2
- package/src/ui/lab.css +2152 -370
- package/virtual.d.ts +13 -0
- 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/virtual.d.ts
CHANGED
|
@@ -23,6 +23,19 @@ declare module "virtual:orbytes-lab/config.mjs" {
|
|
|
23
23
|
cullDir: string | null;
|
|
24
24
|
responsiveFile: string;
|
|
25
25
|
cullFile: string;
|
|
26
|
+
/**
|
|
27
|
+
* The pin board, when it is running: dev only, and only when the board is on and found a git
|
|
28
|
+
* repository. Null in every build. Set by the pin half through the integration's shared state
|
|
29
|
+
* (../index.mjs), read when this module loads — after every integration's setup has run.
|
|
30
|
+
* `repoRoot` is an absolute local path, so this module must stay server-side only.
|
|
31
|
+
*/
|
|
32
|
+
tasks: { base: string; api: string; assets: string; repoRoot: string; backlogDir: string } | null;
|
|
33
|
+
/** The viewport's screen sizes — defaults.mjs › DEFAULT_VIEWPORTS. */
|
|
34
|
+
viewports: {
|
|
35
|
+
design: { label: string; width: number; icon: string }[];
|
|
36
|
+
breakpoints: { name: string; min: number }[];
|
|
37
|
+
devices: { label: string; width: number; height: number }[];
|
|
38
|
+
};
|
|
26
39
|
}
|
|
27
40
|
const config: LabConfig;
|
|
28
41
|
export default config;
|
package/src/shell/CardGrid.astro
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// One card per story: a scaled-down live <iframe> of the bare render, the module and story
|
|
3
|
-
// names, the pills derived from the index — `live · slot N of M` when a page mounts the component
|
|
4
|
-
// (`live · /about, slot 3` when that page is not the home page), `used by N` when a live component
|
|
5
|
-
// imports it, plus the responsive mark on section versions — and a row of links: the viewport
|
|
6
|
-
// configurator, the same story on staging, and (dev only) the component in VS Code. Factored out
|
|
7
|
-
// of Home.astro so the folder pages (Browse.astro) draw the identical card.
|
|
8
|
-
//
|
|
9
|
-
// Thumbnails are the bare story routes themselves (/lab/stories/<id>) rendered in a virtual
|
|
10
|
-
// viewport: sections at 1440×900 (page width), everything else at 960×540.
|
|
11
|
-
//
|
|
12
|
-
// The card is a <div> holding several links, not one <a>: the main link (thumb + label) opens the
|
|
13
|
-
// dashboard and the link row sits under it. An <a> cannot nest in an <a>.
|
|
14
|
-
import { isSectionLike, labBase, livePillText, liveTitle, type LabItem } from "./lab-index";
|
|
15
|
-
|
|
16
|
-
interface Props {
|
|
17
|
-
items: LabItem[];
|
|
18
|
-
/** Shown when there is nothing to draw. */
|
|
19
|
-
empty?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const { items, empty = "No stories here." } = Astro.props;
|
|
23
|
-
|
|
24
|
-
type Pill = { text: string; kind: string; title?: string };
|
|
25
|
-
|
|
26
|
-
const pillsOf = (item: LabItem): Pill[] => {
|
|
27
|
-
const pills: Pill[] = [];
|
|
28
|
-
const live = livePillText(item);
|
|
29
|
-
if (live) pills.push({ text: live, kind: "live", title: liveTitle(item) });
|
|
30
|
-
else if (item.usedBy.length > 0)
|
|
31
|
-
pills.push({ text: `used by ${item.usedBy.length}`, kind: "used", title: item.usedBy.join("\n") });
|
|
32
|
-
if (item.responsive) {
|
|
33
|
-
pills.push({
|
|
34
|
-
text: item.responsive.done ? "responsive" : "not responsive",
|
|
35
|
-
kind: item.responsive.done ? "responsive" : "unresponsive",
|
|
36
|
-
title: item.responsive.approved
|
|
37
|
-
? "approved for responsive work"
|
|
38
|
-
: "not approved for responsive work yet",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return pills;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// What the page-level filter box matches against.
|
|
45
|
-
const searchText = (item: LabItem) =>
|
|
46
|
-
[
|
|
47
|
-
item.moduleName,
|
|
48
|
-
item.storyName,
|
|
49
|
-
item.section,
|
|
50
|
-
item.version,
|
|
51
|
-
item.tier,
|
|
52
|
-
item.livePage,
|
|
53
|
-
item.live ? "live" : "",
|
|
54
|
-
// "unresponsive", not "not responsive": the filter is a substring match per term, so a card
|
|
55
|
-
// that said "not responsive" would still be a hit for "responsive". Same on the sidebar rows.
|
|
56
|
-
item.responsive ? (item.responsive.done ? "responsive" : "unresponsive") : "",
|
|
57
|
-
...item.tags,
|
|
58
|
-
]
|
|
59
|
-
.filter(Boolean)
|
|
60
|
-
.join(" ")
|
|
61
|
-
.toLowerCase();
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
items.length === 0 ? (
|
|
66
|
-
<p class="lab-grid__empty">{empty}</p>
|
|
67
|
-
) : (
|
|
68
|
-
<div class="lab-grid">
|
|
69
|
-
{items.map((item) => {
|
|
70
|
-
const section = isSectionLike(item);
|
|
71
|
-
const pills = pillsOf(item);
|
|
72
|
-
return (
|
|
73
|
-
<div class="lab-card" data-search={searchText(item)}>
|
|
74
|
-
<a class="lab-card__main" href={item.dashboardUrl}>
|
|
75
|
-
<span
|
|
76
|
-
class="lab-card__thumb"
|
|
77
|
-
data-frame-w={section ? 1440 : 960}
|
|
78
|
-
data-frame-h={section ? 900 : 540}
|
|
79
|
-
>
|
|
80
|
-
<iframe
|
|
81
|
-
src={item.storyUrl}
|
|
82
|
-
title={`${item.moduleName} — ${item.storyName}`}
|
|
83
|
-
loading="lazy"
|
|
84
|
-
tabindex="-1"
|
|
85
|
-
/>
|
|
86
|
-
{pills.length > 0 && (
|
|
87
|
-
<span class="lab-card__pills">
|
|
88
|
-
{pills.map((pill) => (
|
|
89
|
-
<span class:list={["lab-card__pill", `lab-card__pill--${pill.kind}`]} title={pill.title}>
|
|
90
|
-
{pill.text}
|
|
91
|
-
</span>
|
|
92
|
-
))}
|
|
93
|
-
</span>
|
|
94
|
-
)}
|
|
95
|
-
</span>
|
|
96
|
-
<span class="lab-card__label">
|
|
97
|
-
<span class="lab-card__name">
|
|
98
|
-
{item.moduleName}
|
|
99
|
-
{item.version && <span class="lab-card__version">{item.version}</span>}
|
|
100
|
-
</span>
|
|
101
|
-
<span class="lab-card__variant">{item.storyName}</span>
|
|
102
|
-
</span>
|
|
103
|
-
</a>
|
|
104
|
-
<span class="lab-card__links">
|
|
105
|
-
<a
|
|
106
|
-
href={`${labBase}/viewport/${item.storyId}`}
|
|
107
|
-
title="Open in the viewport configurator"
|
|
108
|
-
data-astro-reload
|
|
109
|
-
>
|
|
110
|
-
viewport <span aria-hidden="true">⇲</span>
|
|
111
|
-
</a>
|
|
112
|
-
{item.stagingUrl && (
|
|
113
|
-
<a href={item.stagingUrl} title="The same story on the deployed lab" target="_blank" rel="noopener">
|
|
114
|
-
staging <span aria-hidden="true">↗</span>
|
|
115
|
-
</a>
|
|
116
|
-
)}
|
|
117
|
-
{item.editorUrl && (
|
|
118
|
-
<a href={item.editorUrl} title="Open the component in VS Code">
|
|
119
|
-
code <span aria-hidden="true">↗</span>
|
|
120
|
-
</a>
|
|
121
|
-
)}
|
|
122
|
-
</span>
|
|
123
|
-
</div>
|
|
124
|
-
);
|
|
125
|
-
})}
|
|
126
|
-
</div>
|
|
127
|
-
)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
<script>
|
|
131
|
-
// Scale each thumbnail iframe so its virtual viewport (data-frame-w × data-frame-h) fits the
|
|
132
|
-
// card width exactly. Re-run on resize and after every Astro view-transition swap (the lab home
|
|
133
|
-
// sits inside Astrobook's ClientRouter, where module scripts do not re-execute on navigation).
|
|
134
|
-
const fit = () => {
|
|
135
|
-
for (const thumb of document.querySelectorAll<HTMLElement>(".lab-card__thumb")) {
|
|
136
|
-
const iframe = thumb.querySelector("iframe");
|
|
137
|
-
if (!iframe) continue;
|
|
138
|
-
const frameW = Number(thumb.dataset.frameW) || 1440;
|
|
139
|
-
const frameH = Number(thumb.dataset.frameH) || 900;
|
|
140
|
-
const scale = thumb.clientWidth / frameW;
|
|
141
|
-
iframe.style.width = `${frameW}px`;
|
|
142
|
-
iframe.style.height = `${frameH}px`;
|
|
143
|
-
iframe.style.transform = `scale(${scale})`;
|
|
144
|
-
thumb.style.height = `${Math.round(frameH * scale)}px`;
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
fit();
|
|
148
|
-
window.addEventListener("resize", fit);
|
|
149
|
-
document.addEventListener("astro:page-load", fit);
|
|
150
|
-
</script>
|
|
151
|
-
|
|
152
|
-
<style>
|
|
153
|
-
.lab-grid {
|
|
154
|
-
display: grid;
|
|
155
|
-
grid-template-columns: repeat(auto-fill, minmax(20em, 1fr));
|
|
156
|
-
gap: 1.5em;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.lab-grid__empty {
|
|
160
|
-
font-family: var(--lab-font-mono);
|
|
161
|
-
font-size: 0.85em;
|
|
162
|
-
color: var(--lab-color-text-faint);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.lab-card {
|
|
166
|
-
position: relative;
|
|
167
|
-
display: block;
|
|
168
|
-
border: 1px solid var(--lab-color-border);
|
|
169
|
-
background-color: var(--lab-color-surface);
|
|
170
|
-
transition: border-color var(--lab-transition);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.lab-card:hover,
|
|
174
|
-
.lab-card:focus-within {
|
|
175
|
-
border-color: var(--lab-color-border-strong);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.lab-card__main {
|
|
179
|
-
display: block;
|
|
180
|
-
text-decoration: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.lab-card__main:focus-visible {
|
|
184
|
-
outline: none;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/* The link row under the label. Three links no longer fit over the label's right end, so they
|
|
188
|
-
get their own row rather than a cramped overlay. */
|
|
189
|
-
.lab-card__links {
|
|
190
|
-
display: flex;
|
|
191
|
-
flex-wrap: wrap;
|
|
192
|
-
gap: 1.25em;
|
|
193
|
-
padding: 0 1em 0.85em;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.lab-card__links a {
|
|
197
|
-
font-family: var(--lab-font-mono);
|
|
198
|
-
font-weight: 500;
|
|
199
|
-
font-size: 0.75em;
|
|
200
|
-
line-height: 1.5;
|
|
201
|
-
color: var(--lab-color-text-faint);
|
|
202
|
-
text-decoration: none;
|
|
203
|
-
border-bottom: 1px solid transparent;
|
|
204
|
-
transition: color var(--lab-transition), border-color var(--lab-transition);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
.lab-card__links a:hover,
|
|
208
|
-
.lab-card__links a:focus-visible {
|
|
209
|
-
color: var(--lab-color-text-strong);
|
|
210
|
-
border-bottom-color: var(--lab-color-text-strong);
|
|
211
|
-
outline: none;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.lab-card__thumb {
|
|
215
|
-
display: block;
|
|
216
|
-
position: relative;
|
|
217
|
-
overflow: hidden;
|
|
218
|
-
background-color: var(--lab-color-bg);
|
|
219
|
-
border-bottom: 1px solid var(--lab-color-border);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.lab-card__thumb iframe {
|
|
223
|
-
border: 0;
|
|
224
|
-
transform-origin: 0 0;
|
|
225
|
-
pointer-events: none; /* the card is the link; the live render is display-only */
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.lab-card__pills {
|
|
229
|
-
position: absolute;
|
|
230
|
-
top: 0.6em;
|
|
231
|
-
left: 0.6em;
|
|
232
|
-
display: flex;
|
|
233
|
-
flex-wrap: wrap;
|
|
234
|
-
gap: 0.4em;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.lab-card__pill {
|
|
238
|
-
font-family: var(--lab-font-mono);
|
|
239
|
-
font-weight: 500;
|
|
240
|
-
font-size: 0.7em;
|
|
241
|
-
line-height: 1;
|
|
242
|
-
padding: 0.5em 0.85em;
|
|
243
|
-
border-radius: var(--lab-radius-pill);
|
|
244
|
-
border: 1px solid var(--lab-color-border);
|
|
245
|
-
background-color: var(--lab-color-surface-raised);
|
|
246
|
-
color: var(--lab-color-text-muted);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.lab-card__pill--live {
|
|
250
|
-
background-color: var(--lab-color-accent);
|
|
251
|
-
border-color: var(--lab-color-accent);
|
|
252
|
-
color: var(--lab-color-accent-text);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/* The responsive mark: solid border when the version has been made responsive, dashed when it
|
|
256
|
-
has not — the same two-state reading as the sidebar's pill. */
|
|
257
|
-
.lab-card__pill--responsive {
|
|
258
|
-
border-color: var(--lab-color-border-strong);
|
|
259
|
-
color: var(--lab-color-text-strong);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.lab-card__pill--unresponsive {
|
|
263
|
-
border-style: dashed;
|
|
264
|
-
color: var(--lab-color-text-faint);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
.lab-card__label {
|
|
268
|
-
display: flex;
|
|
269
|
-
align-items: baseline;
|
|
270
|
-
justify-content: space-between;
|
|
271
|
-
gap: 1em;
|
|
272
|
-
padding: 0.75em 1em 0.5em;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.lab-card__name {
|
|
276
|
-
display: inline-flex;
|
|
277
|
-
align-items: baseline;
|
|
278
|
-
gap: 0.6em;
|
|
279
|
-
color: var(--lab-color-text-strong);
|
|
280
|
-
font-weight: 500;
|
|
281
|
-
font-size: 0.95em;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.lab-card__version {
|
|
285
|
-
font-family: var(--lab-font-mono);
|
|
286
|
-
font-size: 0.75em;
|
|
287
|
-
color: var(--lab-color-text-faint);
|
|
288
|
-
border: 1px solid var(--lab-color-border);
|
|
289
|
-
padding: 0.1em 0.5em;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.lab-card__variant {
|
|
293
|
-
font-family: var(--lab-font-mono);
|
|
294
|
-
font-size: 0.8em;
|
|
295
|
-
color: var(--lab-color-text-faint);
|
|
296
|
-
}
|
|
297
|
-
</style>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Vendored from astrobook 0.13.3 (packages/ui/src/components/build-path.ts) — see
|
|
2
|
-
// ../../core/LICENSE-astrobook.
|
|
3
|
-
// Divergence: `@astrobook/core/client` → the vendored core's own path-builder, by relative path
|
|
4
|
-
// with the .ts extension. The `import '@astrobook/types'` side-effect line is gone: the ambient
|
|
5
|
-
// declarations it pulled in now live in ../../types/virtual.d.ts, referenced from the package's
|
|
6
|
-
// own virtual.d.ts.
|
|
7
|
-
import config from 'virtual:astrobook/global-config.mjs'
|
|
8
|
-
|
|
9
|
-
import { createPathBuilder } from '../../core/utils/path-builder.ts'
|
|
10
|
-
|
|
11
|
-
export const buildPath: (...parts: string[]) => string = createPathBuilder({
|
|
12
|
-
trailingSlash: config.trailingSlash,
|
|
13
|
-
})
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
// Vendored from astrobook 0.13.3 (packages/ui/src/components/build-tree.ts) — see
|
|
2
|
-
// ../../core/LICENSE-astrobook.
|
|
3
|
-
// Divergence: relative imports carry the .ts extension, and the StoryModule type comes from the
|
|
4
|
-
// vendored types (../../types/) rather than the `@astrobook/types` package.
|
|
5
|
-
import type { StoryModule } from '../../types/types.ts'
|
|
6
|
-
|
|
7
|
-
import { compressTerms } from './compress-terms.ts'
|
|
8
|
-
|
|
9
|
-
export interface DirectoryNode {
|
|
10
|
-
type: 'directory'
|
|
11
|
-
name: string
|
|
12
|
-
searchText: string
|
|
13
|
-
children: TreeNode[]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ModuleNode {
|
|
17
|
-
type: 'module'
|
|
18
|
-
id: string
|
|
19
|
-
name: string
|
|
20
|
-
searchText: string
|
|
21
|
-
children: StoryNode[]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface StoryNode {
|
|
25
|
-
type: 'story'
|
|
26
|
-
id: string
|
|
27
|
-
name: string
|
|
28
|
-
searchText: string
|
|
29
|
-
children?: undefined
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type TreeNode = DirectoryNode | ModuleNode | StoryNode
|
|
33
|
-
|
|
34
|
-
export function isSingleStory(module: StoryModule): boolean {
|
|
35
|
-
return module.stories.length === 1 && module.stories[0].name === module.name
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function findOrCreateDirectory(
|
|
39
|
-
children: TreeNode[],
|
|
40
|
-
segments: string[],
|
|
41
|
-
): TreeNode[] {
|
|
42
|
-
let current = children
|
|
43
|
-
for (const segment of segments) {
|
|
44
|
-
let dir: DirectoryNode | undefined
|
|
45
|
-
for (const node of current) {
|
|
46
|
-
if (node.type === 'directory' && node.name === segment) {
|
|
47
|
-
dir = node
|
|
48
|
-
break
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (!dir) {
|
|
52
|
-
dir = { type: 'directory', name: segment, children: [], searchText: '' }
|
|
53
|
-
current.push(dir)
|
|
54
|
-
}
|
|
55
|
-
current = dir.children
|
|
56
|
-
}
|
|
57
|
-
return current
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function buildTree(modules: StoryModule[]): TreeNode[] {
|
|
61
|
-
const root: TreeNode[] = []
|
|
62
|
-
|
|
63
|
-
for (const module of modules) {
|
|
64
|
-
const segments = module.directory ? module.directory.split('/') : []
|
|
65
|
-
const children = findOrCreateDirectory(root, segments)
|
|
66
|
-
|
|
67
|
-
const storyNodes: StoryNode[] = module.stories.map((story) => ({
|
|
68
|
-
type: 'story',
|
|
69
|
-
id: story.id,
|
|
70
|
-
name: story.name,
|
|
71
|
-
searchText: '',
|
|
72
|
-
}))
|
|
73
|
-
|
|
74
|
-
if (isSingleStory(module)) {
|
|
75
|
-
children.push(storyNodes[0])
|
|
76
|
-
} else {
|
|
77
|
-
const moduleNode: ModuleNode = {
|
|
78
|
-
type: 'module',
|
|
79
|
-
id: module.id,
|
|
80
|
-
name: module.name,
|
|
81
|
-
searchText: '',
|
|
82
|
-
children: storyNodes,
|
|
83
|
-
}
|
|
84
|
-
children.push(moduleNode)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const node of root) {
|
|
89
|
-
assignSearchText(node, [])
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return root
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function assignSearchText(
|
|
96
|
-
node: TreeNode,
|
|
97
|
-
ancestorTerms: readonly string[],
|
|
98
|
-
): readonly string[] {
|
|
99
|
-
const currTerms = [...ancestorTerms, node.name]
|
|
100
|
-
const subTreeTerms: Array<readonly string[]> = Array.from(
|
|
101
|
-
node.children || [],
|
|
102
|
-
(child) => assignSearchText(child, currTerms),
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
const mergedTerms = compressTerms([...currTerms, ...subTreeTerms.flat()])
|
|
106
|
-
node.searchText = mergedTerms.join(' ')
|
|
107
|
-
return mergedTerms
|
|
108
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Vendored from astrobook 0.13.3 (packages/ui/src/components/collapse-duration.ts) — see
|
|
2
|
-
// ../../core/LICENSE-astrobook.
|
|
3
|
-
// Divergence: the relative import carries the .ts extension.
|
|
4
|
-
import type { TreeNode } from './build-tree.ts'
|
|
5
|
-
|
|
6
|
-
function countDescendantRows(node: TreeNode): number {
|
|
7
|
-
if (node.type === 'story') return 0
|
|
8
|
-
let rows = node.children.length
|
|
9
|
-
for (const child of node.children) {
|
|
10
|
-
rows += countDescendantRows(child)
|
|
11
|
-
}
|
|
12
|
-
return rows
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function getCollapseDuration(node: TreeNode): number {
|
|
16
|
-
if (node.type === 'story') return 0
|
|
17
|
-
return computeCollapseDuration(countDescendantRows(node))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function computeCollapseDuration(rows: number): number {
|
|
21
|
-
const minMs = 100
|
|
22
|
-
const maxMs = 500
|
|
23
|
-
const scale = 35
|
|
24
|
-
return Math.min(
|
|
25
|
-
maxMs,
|
|
26
|
-
Math.round(minMs + scale * Math.log(Math.max(1, rows))),
|
|
27
|
-
)
|
|
28
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Vendored from astrobook 0.13.3 (packages/ui/src/components/compress-terms.ts) — see
|
|
2
|
-
// ../../core/LICENSE-astrobook. Unchanged.
|
|
3
|
-
export function compressTerms(terms: readonly string[]): readonly string[] {
|
|
4
|
-
const unique = Array.from(new Set(terms.map((k) => k.toLowerCase())))
|
|
5
|
-
return unique
|
|
6
|
-
.filter(
|
|
7
|
-
(term) => !unique.some((other) => other !== term && other.includes(term)),
|
|
8
|
-
)
|
|
9
|
-
.sort()
|
|
10
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// The DASHBOARD document: the lab's chrome with the story rendered inside it. New in this
|
|
3
|
-
// package — upstream had one layout for the dashboard, the bare story and the home page alike,
|
|
4
|
-
// and gave all three the consumer's CSS.
|
|
5
|
-
//
|
|
6
|
-
// This is the one document that legitimately carries BOTH stylesheets, because it is both things
|
|
7
|
-
// at once (ruled 2026-09-22):
|
|
8
|
-
//
|
|
9
|
-
// · the chrome — sidebar, search, toolbar — styled by ../lab.css and by nothing else;
|
|
10
|
-
// · the preview — the story in <main> — which must be rendered exactly as the real site
|
|
11
|
-
// renders it, so it gets the consumer's `css` list and the consumer's head component.
|
|
12
|
-
//
|
|
13
|
-
// Nothing crosses. The chrome reads only `--lab-*` tokens and is anchored on chrome classes and
|
|
14
|
-
// ids, so the consumer's `body`/`a`/`*` rules cannot reach it; the chrome sets no font, colour or
|
|
15
|
-
// size on `body`, so nothing of ours inherits down into the story. The full argument, and what it
|
|
16
|
-
// costs, is at the top of ../lab.css.
|
|
17
|
-
//
|
|
18
|
-
// The consumer's head arrives via `virtual:orbytes-lab/user-head.mjs` — the lab shell's own
|
|
19
|
-
// virtual module (../../../index.mjs), which is how it reached this package before too: the
|
|
20
|
-
// package's LabHead used to mount it on EVERY lab page, which is what carried the site's fonts
|
|
21
|
-
// into the chrome.
|
|
22
|
-
import 'virtual:astrobook/user-css.mjs'
|
|
23
|
-
|
|
24
|
-
import UserHead from 'virtual:orbytes-lab/user-head.mjs'
|
|
25
|
-
|
|
26
|
-
import Layout from './layout.astro'
|
|
27
|
-
|
|
28
|
-
interface Props {
|
|
29
|
-
story: string | undefined
|
|
30
|
-
hasSidebar: boolean
|
|
31
|
-
}
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
<Layout story={Astro.props.story} hasSidebar={Astro.props.hasSidebar}>
|
|
35
|
-
<Fragment slot="head">
|
|
36
|
-
<UserHead />
|
|
37
|
-
</Fragment>
|
|
38
|
-
<slot />
|
|
39
|
-
</Layout>
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Vendored from astrobook 0.13.3 (packages/ui/src/components/home.astro) — see
|
|
3
|
-
// ../../core/LICENSE-astrobook.
|
|
4
|
-
// Divergence: the UnoCSS utility strings (including the `@container` queries, which had no
|
|
5
|
-
// container to query once the chrome stopped shipping Uno's preflight) are the `.lab-splash*`
|
|
6
|
-
// classes in ../lab.css, and the star glyph is an inline SVG.
|
|
7
|
-
//
|
|
8
|
-
// This is Astrobook's BUILT-IN splash. The orbytes lab always passes its own `home`
|
|
9
|
-
// (src/Home.astro — the tier cards and live thumbnails), so this renders only for a consumer
|
|
10
|
-
// that leaves the option at its default. Kept working, not designed.
|
|
11
|
-
import config from 'virtual:astrobook/global-config.mjs'
|
|
12
|
-
|
|
13
|
-
const content = config.homeContent
|
|
14
|
-
const showBadges = !!(content.version || content.repo)
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
<div class="lab-splash lab-chrome">
|
|
18
|
-
{
|
|
19
|
-
content.title == null ? null : (
|
|
20
|
-
<h1 class="lab-splash__title">{content.title}</h1>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
{
|
|
24
|
-
content.subtitle == null ? null : (
|
|
25
|
-
<p class="lab-splash__subtitle">{content.subtitle}</p>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
showBadges && (
|
|
31
|
-
<div class="lab-splash__badges">
|
|
32
|
-
{content.version && (
|
|
33
|
-
<a
|
|
34
|
-
href={content.version.href}
|
|
35
|
-
target="_blank"
|
|
36
|
-
rel="noopener noreferrer"
|
|
37
|
-
class="lab-splash__badge"
|
|
38
|
-
>
|
|
39
|
-
{content.version.label}
|
|
40
|
-
</a>
|
|
41
|
-
)}
|
|
42
|
-
{content.repo && (
|
|
43
|
-
<a
|
|
44
|
-
href={content.repo.href}
|
|
45
|
-
target="_blank"
|
|
46
|
-
rel="noopener noreferrer"
|
|
47
|
-
class="lab-splash__badge"
|
|
48
|
-
>
|
|
49
|
-
<svg
|
|
50
|
-
viewBox="0 0 16 16"
|
|
51
|
-
fill="none"
|
|
52
|
-
stroke="currentColor"
|
|
53
|
-
stroke-width="1.5"
|
|
54
|
-
stroke-linejoin="round"
|
|
55
|
-
aria-hidden="true"
|
|
56
|
-
>
|
|
57
|
-
<path d="M8 1.75 10 6l4.25.5-3.1 2.9.85 4.35L8 11.6l-4 2.15.85-4.35L1.75 6.5 6 6z" />
|
|
58
|
-
</svg>
|
|
59
|
-
<span>{content.repo.label}</span>
|
|
60
|
-
</a>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
</div>
|