@orbytes/astrolab 0.3.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/LICENSE +37 -0
- package/README.md +410 -0
- package/bin/lab-cull.mjs +401 -0
- package/bin/pin-gallery.mjs +121 -0
- package/defaults.mjs +120 -0
- package/dist/core/astro-integration.js +130 -0
- package/dist/core/index.js +6 -0
- package/dist/core/lib-paths.js +29 -0
- package/dist/core/options.js +173 -0
- package/dist/core/utils/get-exports.js +52 -0
- package/dist/core/utils/invariant.js +12 -0
- package/dist/core/utils/kebab-case.js +15 -0
- package/dist/core/utils/path-builder.js +22 -0
- package/dist/core/utils/path.js +53 -0
- package/dist/core/virtual-module/get-story-modules.js +60 -0
- package/dist/core/virtual-module/story-modules.js +8 -0
- package/dist/core/virtual-module/virtual-module-ids.js +22 -0
- package/dist/core/virtual-module/virtual-routes.js +83 -0
- package/dist/core/virtual-module/vite-plugin.js +98 -0
- package/docs/PIN-CONTRACT.md +263 -0
- package/docs/PIN.md +429 -0
- package/index.d.ts +279 -0
- package/index.mjs +347 -0
- package/package.json +93 -0
- package/src/Empty.astro +4 -0
- package/src/Home.astro +298 -0
- package/src/LabHead.astro +1102 -0
- package/src/core/LICENSE-astrobook +166 -0
- package/src/core/astro-integration.ts +166 -0
- package/src/core/client.ts +89 -0
- package/src/core/index.ts +7 -0
- package/src/core/lib/components/empty.astro +1 -0
- package/src/core/lib/components/head.astro +1 -0
- package/src/core/lib/components/home.astro +8 -0
- package/src/core/lib/components/with-decorators.astro +22 -0
- package/src/core/lib/pages/app.astro +19 -0
- package/src/core/lib/pages/preview.astro +17 -0
- package/src/core/lib/pages/story.astro +16 -0
- package/src/core/lib-paths.ts +72 -0
- package/src/core/options.ts +262 -0
- package/src/core/utils/get-exports.ts +59 -0
- package/src/core/utils/invariant.ts +13 -0
- package/src/core/utils/kebab-case.ts +30 -0
- package/src/core/utils/path-builder.ts +45 -0
- package/src/core/utils/path.ts +80 -0
- package/src/core/virtual-module/get-story-modules.ts +110 -0
- package/src/core/virtual-module/story-modules.ts +9 -0
- package/src/core/virtual-module/virtual-module-ids.ts +17 -0
- package/src/core/virtual-module/virtual-routes.ts +130 -0
- package/src/core/virtual-module/vite-plugin.ts +125 -0
- package/src/pin/board.mjs +1521 -0
- package/src/pin/index.mjs +666 -0
- package/src/pin/shot.mjs +427 -0
- package/src/pin/source-stamp.mjs +159 -0
- package/src/pin/tickets.mjs +697 -0
- package/src/pin/toolbar.js +3181 -0
- package/src/shell/Browse.astro +371 -0
- package/src/shell/CardGrid.astro +297 -0
- package/src/shell/Viewport.astro +1330 -0
- package/src/shell/index.json.ts +12 -0
- package/src/shell/lab-index.ts +344 -0
- package/src/shell/lab-params.ts +245 -0
- package/src/shell/live-files.mjs +164 -0
- package/src/shell/marks.mjs +136 -0
- package/src/types/index.ts +6 -0
- package/src/types/types.ts +239 -0
- package/src/types/virtual.d.ts +29 -0
- package/src/ui/components/app.astro +13 -0
- package/src/ui/components/build-path.ts +13 -0
- package/src/ui/components/build-tree.ts +108 -0
- package/src/ui/components/collapse-duration.ts +28 -0
- package/src/ui/components/compress-terms.ts +10 -0
- package/src/ui/components/dashboard-layout.astro +39 -0
- package/src/ui/components/home.astro +65 -0
- package/src/ui/components/layout.astro +110 -0
- package/src/ui/components/preview-layout.astro +109 -0
- package/src/ui/components/sidebar-button-fullscreen.astro +38 -0
- package/src/ui/components/sidebar-button-search.astro +23 -0
- package/src/ui/components/sidebar-button-theme.astro +9 -0
- package/src/ui/components/sidebar-button.astro +24 -0
- package/src/ui/components/sidebar-resize-handle.astro +74 -0
- package/src/ui/components/sidebar-search-panel.astro +41 -0
- package/src/ui/components/sidebar-search-script.ts +103 -0
- package/src/ui/components/sidebar-title.astro +17 -0
- package/src/ui/components/sidebar-tree-node.astro +143 -0
- package/src/ui/components/sidebar-tree.astro +84 -0
- package/src/ui/components/sidebar.astro +29 -0
- package/src/ui/components/theme-message.ts +26 -0
- package/src/ui/components/theme-script.astro +71 -0
- package/src/ui/components/theme-toggle.astro +63 -0
- package/src/ui/components/theme.ts +32 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/lab.css +549 -0
- package/virtual.d.ts +42 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Which lab components are LIVE on the site — derived from the pages under src/pages/, never declared.
|
|
2
|
+
//
|
|
3
|
+
// Dependency-free Node (fs + path only) on purpose: it is imported by three consumers that cannot
|
|
4
|
+
// share a runtime — the build-time index (./lab-index.ts, inside Vite), the dev-only mark APIs
|
|
5
|
+
// (../../index.mjs, a Vite middleware) and the cull binary (../../bin/lab-cull.mjs, plain Node).
|
|
6
|
+
// One parser, so the three can never disagree about what "live" means.
|
|
7
|
+
//
|
|
8
|
+
// The rule: a component file is live when a page imports it AND mounts it as a tag. Slot is its
|
|
9
|
+
// 1-based position among the mounted tags in that page's document order (the Footer sits outside
|
|
10
|
+
// <main>, so the whole template is scanned, not just <main>). The page layout (src/layouts/) is a
|
|
11
|
+
// wrapper, not a section, and is excluded — everything else the page mounts counts.
|
|
12
|
+
//
|
|
13
|
+
// EVERY page is walked, not just the home page (2026-09-06). orbytes.io is one page, but the lab
|
|
14
|
+
// is a shared package every orbytes Astro site installs, and on a site with an /about page a
|
|
15
|
+
// home-page-only reader reports nothing, or the wrong slot, for everything that is not on the
|
|
16
|
+
// home page. Pages come home first, then alphabetically, so a component mounted on
|
|
17
|
+
// several pages reports the home page's slot as its primary one.
|
|
18
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
|
|
21
|
+
export const PAGES_DIR = "src/pages";
|
|
22
|
+
export const HOME_PAGE = "src/pages/index.astro";
|
|
23
|
+
|
|
24
|
+
/** @param {string} rootDir @param {string} absFile */
|
|
25
|
+
export const toRepoRelative = (rootDir, absFile) =>
|
|
26
|
+
path.relative(rootDir, absFile).split(path.sep).join("/");
|
|
27
|
+
|
|
28
|
+
/** The `---` fenced frontmatter of an .astro source, or "" when there is none. @param {string} source */
|
|
29
|
+
export const frontmatterOf = (source) => {
|
|
30
|
+
const m = source.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
31
|
+
return m ? m[1] : "";
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Everything after the frontmatter fence (the template). @param {string} source */
|
|
35
|
+
export const templateOf = (source) => {
|
|
36
|
+
const m = source.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);
|
|
37
|
+
return m ? m[1] : source;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Every `import Name from "<relative>"` in a source file, resolved to repo-relative paths.
|
|
42
|
+
* For .astro files only the frontmatter is read; for .ts/.mjs the whole file. Package and
|
|
43
|
+
* virtual-module imports are skipped — they are not files in this repo.
|
|
44
|
+
* @param {string} rootDir absolute repo root
|
|
45
|
+
* @param {string} fileRel repo-relative path of the file to read
|
|
46
|
+
* @returns {{ name: string; file: string }[]}
|
|
47
|
+
*/
|
|
48
|
+
export function defaultImports(rootDir, fileRel) {
|
|
49
|
+
const abs = path.join(rootDir, fileRel);
|
|
50
|
+
if (!existsSync(abs)) return [];
|
|
51
|
+
const source = readFileSync(abs, "utf8");
|
|
52
|
+
const code = fileRel.endsWith(".astro") ? frontmatterOf(source) : source;
|
|
53
|
+
/** @type {{ name: string; file: string }[]} */
|
|
54
|
+
const out = [];
|
|
55
|
+
const re = /^\s*import\s+([A-Za-z_$][\w$]*)\s+from\s+["']([^"']+)["']/gm;
|
|
56
|
+
let m;
|
|
57
|
+
while ((m = re.exec(code))) {
|
|
58
|
+
const spec = m[2];
|
|
59
|
+
if (!spec.startsWith(".")) continue;
|
|
60
|
+
out.push({ name: m[1], file: toRepoRelative(rootDir, path.resolve(path.dirname(abs), spec)) });
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Every routable page under src/pages, repo-relative, home page first then alphabetical.
|
|
67
|
+
* A name starting with `_` (or `.`) is skipped, file or directory, exactly as Astro's own router
|
|
68
|
+
* skips it — `src/pages/_partials/Card.astro` is not a route and must never claim a slot.
|
|
69
|
+
* @param {string} rootDir absolute repo root
|
|
70
|
+
* @returns {string[]}
|
|
71
|
+
*/
|
|
72
|
+
export function sitePages(rootDir) {
|
|
73
|
+
/** @type {string[]} */
|
|
74
|
+
const out = [];
|
|
75
|
+
/** @param {string} rel */
|
|
76
|
+
const walk = (rel) => {
|
|
77
|
+
const abs = path.join(rootDir, rel);
|
|
78
|
+
if (!existsSync(abs)) return;
|
|
79
|
+
for (const entry of readdirSync(abs, { withFileTypes: true })) {
|
|
80
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
81
|
+
const child = `${rel}/${entry.name}`;
|
|
82
|
+
if (entry.isDirectory()) walk(child);
|
|
83
|
+
else if (entry.name.endsWith(".astro")) out.push(child);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
walk(PAGES_DIR);
|
|
87
|
+
return out.sort((a, b) => (a === HOME_PAGE ? -1 : b === HOME_PAGE ? 1 : a.localeCompare(b)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The route a page file serves: index.astro → "/", about.astro → "/about", blog/index.astro →
|
|
92
|
+
* "/blog". A dynamic route keeps its brackets ("/[slug]") — this is a label for a pill, not a URL.
|
|
93
|
+
* @param {string} pageRel repo-relative path under src/pages
|
|
94
|
+
*/
|
|
95
|
+
export const pageRoute = (pageRel) => {
|
|
96
|
+
const rel = pageRel.slice(PAGES_DIR.length + 1).replace(/\.astro$/, "");
|
|
97
|
+
const route = rel.replace(/(^|\/)index$/, "");
|
|
98
|
+
return `/${route}`.replace(/\/{2,}/g, "/");
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The component files one page mounts, in document order.
|
|
103
|
+
* @param {string} rootDir absolute repo root
|
|
104
|
+
* @param {string} pageRel repo-relative path of the page
|
|
105
|
+
* @returns {string[]}
|
|
106
|
+
*/
|
|
107
|
+
export function pageComponentFiles(rootDir, pageRel) {
|
|
108
|
+
const abs = path.join(rootDir, pageRel);
|
|
109
|
+
if (!existsSync(abs)) return [];
|
|
110
|
+
const source = readFileSync(abs, "utf8");
|
|
111
|
+
const byName = new Map(
|
|
112
|
+
defaultImports(rootDir, pageRel)
|
|
113
|
+
.filter((i) => i.file.endsWith(".astro") && !i.file.startsWith("src/layouts/"))
|
|
114
|
+
.map((i) => [i.name, i.file]),
|
|
115
|
+
);
|
|
116
|
+
// HTML and JSX comments name components in prose (the order table does) — strip them first.
|
|
117
|
+
const template = templateOf(source)
|
|
118
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
119
|
+
.replace(/\{\/\*[\s\S]*?\*\/\}/g, "");
|
|
120
|
+
/** @type {string[]} */
|
|
121
|
+
const mounted = [];
|
|
122
|
+
const re = /<([A-Z][\w$]*)(?=[\s/>])/g;
|
|
123
|
+
let m;
|
|
124
|
+
while ((m = re.exec(template))) {
|
|
125
|
+
const file = byName.get(m[1]);
|
|
126
|
+
if (file && !mounted.includes(file)) mounted.push(file);
|
|
127
|
+
}
|
|
128
|
+
return mounted;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Every live mount on the site: one entry per (page, component), pages in `sitePages` order and
|
|
133
|
+
* components in each page's document order. `of` is how many components that page mounts, so a
|
|
134
|
+
* slot reads "slot 3 of 12" without a second pass, and a two-page site never mixes the two counts.
|
|
135
|
+
* @param {string} rootDir absolute repo root
|
|
136
|
+
* @returns {{ file: string; page: string; slot: number; of: number }[]}
|
|
137
|
+
*/
|
|
138
|
+
export function liveComponentFiles(rootDir) {
|
|
139
|
+
/** @type {{ file: string; page: string; slot: number; of: number }[]} */
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const pageRel of sitePages(rootDir)) {
|
|
142
|
+
const files = pageComponentFiles(rootDir, pageRel);
|
|
143
|
+
const page = pageRoute(pageRel);
|
|
144
|
+
files.forEach((file, i) => out.push({ file, page, slot: i + 1, of: files.length }));
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The .astro file a stories module renders — the identifier on `component:` in its default
|
|
151
|
+
* export, followed back to its import. Null when the file is missing or the shape is unfamiliar.
|
|
152
|
+
* @param {string} rootDir absolute repo root
|
|
153
|
+
* @param {string} storiesRel repo-relative path of the *.stories.ts file
|
|
154
|
+
* @returns {string | null}
|
|
155
|
+
*/
|
|
156
|
+
export function storyComponentFile(rootDir, storiesRel) {
|
|
157
|
+
const abs = path.join(rootDir, storiesRel);
|
|
158
|
+
if (!existsSync(abs)) return null;
|
|
159
|
+
const source = readFileSync(abs, "utf8");
|
|
160
|
+
const m = source.match(/export\s+default\s*\{[\s\S]*?\bcomponent\s*:\s*([A-Za-z_$][\w$]*)/);
|
|
161
|
+
if (!m) return null;
|
|
162
|
+
const hit = defaultImports(rootDir, storiesRel).find((i) => i.name === m[1]);
|
|
163
|
+
return hit ? hit.file : null;
|
|
164
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// The lab's hand-set marks — everything a person ticks in the sidebar rather than something the
|
|
2
|
+
// lab derives, plus the one path rule both mark files obey.
|
|
3
|
+
//
|
|
4
|
+
// <directory>/responsive.json { "done": string[], "approved": string[], "updated": string|null }
|
|
5
|
+
// <directory>/cull.json { "marked": string[], "updated": string|null }
|
|
6
|
+
//
|
|
7
|
+
// Both are keyed by the story module's repo-relative path
|
|
8
|
+
// (`<directory>/<tier>/…/<Name>.stories.ts`) — one row means one key everywhere — and both are
|
|
9
|
+
// validated through `pathOffence` below, so they can never disagree about what a markable row is.
|
|
10
|
+
//
|
|
11
|
+
// Why the responsive state is marked and not measured: responsive work is approval-gated and
|
|
12
|
+
// final-versions-only (ruled 2026-09-06) — a version is worked on only once it has been approved
|
|
13
|
+
// for responsiveness, approval is NOT the same as the version being final, and that approval can
|
|
14
|
+
// be withdrawn later. Neither fact is readable from the code, so both are ticked here.
|
|
15
|
+
//
|
|
16
|
+
// Dependency-free Node (fs + path only) on purpose: this module is imported inside Vite
|
|
17
|
+
// (./lab-index.ts, at build), by a plain Vite middleware (../../index.mjs) and by the cull binary
|
|
18
|
+
// (../../bin/lab-cull.mjs, plain Node). One parser, three runtimes.
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
|
|
22
|
+
/** @typedef {import("../../defaults.mjs").LabConfig} LabConfig */
|
|
23
|
+
|
|
24
|
+
/** A story module inside the lab directory: the only thing any mark may name. */
|
|
25
|
+
export const storiesPattern = (directory) =>
|
|
26
|
+
new RegExp(`^${directory.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/(?:[\\w.-]+/)*[\\w.-]+\\.stories\\.ts$`);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Why `entry` is not a markable story module under `tier`, or null when it is.
|
|
30
|
+
* @param {string} rootDir absolute repo root
|
|
31
|
+
* @param {LabConfig} config
|
|
32
|
+
* @param {unknown} entry
|
|
33
|
+
* @param {string | null} tier repo-relative directory prefix the mark is restricted to
|
|
34
|
+
* @param {string} tierReason what to say when the entry is outside that tier
|
|
35
|
+
* @returns {string | null}
|
|
36
|
+
*/
|
|
37
|
+
export const pathOffence = (rootDir, config, entry, tier, tierReason) => {
|
|
38
|
+
if (typeof entry !== "string") return "not a string";
|
|
39
|
+
if (!storiesPattern(config.directory).test(entry) || entry.includes(".."))
|
|
40
|
+
return `not a ${config.directory}/**/*.stories.ts path`;
|
|
41
|
+
if (!tier) return tierReason;
|
|
42
|
+
if (!entry.startsWith(tier)) return tierReason;
|
|
43
|
+
if (!existsSync(path.join(rootDir, entry))) return "no such file";
|
|
44
|
+
return null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** @param {string} rootDir @param {LabConfig} config @param {unknown} entry */
|
|
48
|
+
export const responsiveOffence = (rootDir, config, entry) =>
|
|
49
|
+
pathOffence(
|
|
50
|
+
rootDir,
|
|
51
|
+
config,
|
|
52
|
+
entry,
|
|
53
|
+
config.responsiveDir,
|
|
54
|
+
"only section versions can be marked responsive",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/** @param {string} rootDir @param {LabConfig} config @param {unknown} entry */
|
|
58
|
+
export const cullOffence = (rootDir, config, entry) =>
|
|
59
|
+
pathOffence(rootDir, config, entry, config.cullDir, "only the explorations tier can be marked");
|
|
60
|
+
|
|
61
|
+
/** @param {unknown} value */
|
|
62
|
+
const stringList = (value) =>
|
|
63
|
+
Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
64
|
+
|
|
65
|
+
/** @param {string} rootDir @param {string} file */
|
|
66
|
+
const readJson = (rootDir, file) => {
|
|
67
|
+
const abs = path.join(rootDir, file);
|
|
68
|
+
if (!existsSync(abs)) return null;
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(readFileSync(abs, "utf8"));
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The responsive marks as written. Missing or unreadable file → empty, never a throw: the lab
|
|
78
|
+
* renders the same either way, it just shows everything as not responsive.
|
|
79
|
+
* @param {string} rootDir absolute repo root
|
|
80
|
+
* @param {LabConfig} config
|
|
81
|
+
* @returns {{ done: string[]; approved: string[]; updated: string | null }}
|
|
82
|
+
*/
|
|
83
|
+
export function readResponsive(rootDir, config) {
|
|
84
|
+
const data = readJson(rootDir, config.responsiveFile);
|
|
85
|
+
if (!data) return { done: [], approved: [], updated: null };
|
|
86
|
+
return {
|
|
87
|
+
done: stringList(data.done),
|
|
88
|
+
approved: stringList(data.approved),
|
|
89
|
+
updated: typeof data.updated === "string" ? data.updated : null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Replace the responsive marks. Both lists are de-duplicated and sorted so the tracked file's diff
|
|
95
|
+
* is the change and nothing else.
|
|
96
|
+
* @param {string} rootDir absolute repo root
|
|
97
|
+
* @param {LabConfig} config
|
|
98
|
+
* @param {string[]} done
|
|
99
|
+
* @param {string[]} approved
|
|
100
|
+
*/
|
|
101
|
+
export function writeResponsive(rootDir, config, done, approved) {
|
|
102
|
+
const data = {
|
|
103
|
+
done: [...new Set(done)].sort(),
|
|
104
|
+
approved: [...new Set(approved)].sort(),
|
|
105
|
+
updated: new Date().toISOString(),
|
|
106
|
+
};
|
|
107
|
+
writeFileSync(path.join(rootDir, config.responsiveFile), JSON.stringify(data, null, 2) + "\n");
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The cull marks as written.
|
|
113
|
+
* @param {string} rootDir absolute repo root
|
|
114
|
+
* @param {LabConfig} config
|
|
115
|
+
* @returns {{ marked: string[]; updated: string | null }}
|
|
116
|
+
*/
|
|
117
|
+
export function readCull(rootDir, config) {
|
|
118
|
+
const data = readJson(rootDir, config.cullFile);
|
|
119
|
+
if (!data) return { marked: [], updated: null };
|
|
120
|
+
return {
|
|
121
|
+
marked: stringList(data.marked),
|
|
122
|
+
updated: typeof data.updated === "string" ? data.updated : null,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Replace the cull marks.
|
|
128
|
+
* @param {string} rootDir absolute repo root
|
|
129
|
+
* @param {LabConfig} config
|
|
130
|
+
* @param {string[]} marked
|
|
131
|
+
*/
|
|
132
|
+
export function writeCull(rootDir, config, marked) {
|
|
133
|
+
const data = { marked: [...new Set(marked)].sort(), updated: new Date().toISOString() };
|
|
134
|
+
writeFileSync(path.join(rootDir, config.cullFile), JSON.stringify(data, null, 2) + "\n");
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Vendored from astrobook 0.13.3 (packages/types/lib/index.d.ts + index.js) — see
|
|
2
|
+
// ../core/LICENSE-astrobook. Upstream splits this into a `.d.ts` that re-exports and an
|
|
3
|
+
// `index.js` that is `export {}`; one `.ts` does both jobs here, because nothing bundles.
|
|
4
|
+
/// <reference path="./virtual.d.ts" />
|
|
5
|
+
|
|
6
|
+
export type * from './types.ts'
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Vendored from astrobook 0.13.3 (packages/types/lib/types.d.ts) — see ../core/LICENSE-astrobook.
|
|
2
|
+
// Divergence: `.d.ts` → `.ts`, so the vendored core can import these by relative path without
|
|
3
|
+
// `allowImportingTsExtensions` gymnastics. The body is interfaces and one `declare global` only,
|
|
4
|
+
// so it emits nothing at runtime and every importer uses `import type`.
|
|
5
|
+
// Divergence: the option doc comments upstream carries for options this package does not expose
|
|
6
|
+
// are kept verbatim — they document the vendored core's own behaviour, which is unchanged.
|
|
7
|
+
|
|
8
|
+
export interface IntegrationOptions {
|
|
9
|
+
/**
|
|
10
|
+
* The directory to scan for stories.
|
|
11
|
+
*
|
|
12
|
+
* @default '.'
|
|
13
|
+
*/
|
|
14
|
+
directory?: string
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The subpath to deploy Astrobook to relative to the [base
|
|
18
|
+
* URL](https://docs.astro.build/en/reference/configuration-reference/#base)
|
|
19
|
+
* of your Astro project.
|
|
20
|
+
*/
|
|
21
|
+
subpath?: string
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The subpath for dashboard pages relative to the main subpath.
|
|
25
|
+
* A dashboard page has a sidebar for navigation between components.
|
|
26
|
+
*
|
|
27
|
+
* @default '/dashboard/'
|
|
28
|
+
*/
|
|
29
|
+
dashboardSubpath?: string
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The subpath for preview pages relative to the main subpath.
|
|
33
|
+
* A preview page displays an individual component without the sidebar.
|
|
34
|
+
*
|
|
35
|
+
* @default '/stories/'
|
|
36
|
+
*/
|
|
37
|
+
previewSubpath?: string
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set the title for your website. Will be used in metadata and in the browser tab title.
|
|
41
|
+
*
|
|
42
|
+
* @default 'Astrobook'
|
|
43
|
+
*/
|
|
44
|
+
title?: string
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Provide CSS files to customize the look and feel of your Astrobook project.
|
|
48
|
+
*
|
|
49
|
+
* Supports local CSS files relative to the root of your project,
|
|
50
|
+
* e.g. `'./src/custom.css'`, and CSS you installed as an npm
|
|
51
|
+
* module, e.g. `'@fontsource/roboto'`.
|
|
52
|
+
*
|
|
53
|
+
* In this package these files reach the PREVIEW only — the story render — never the lab's own
|
|
54
|
+
* chrome (decided 2026-09-22). See src/ui/lab.css.
|
|
55
|
+
*/
|
|
56
|
+
css?: string[]
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The path to an Astro component to provide custom tags in the `<head>`.
|
|
60
|
+
* It can be an `.astro` or `.html` file.
|
|
61
|
+
*/
|
|
62
|
+
head?: string
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Controls which Astro component is rendered on the dashboard homepage.
|
|
66
|
+
*
|
|
67
|
+
* - Pass a string (relative path or package identifier) to fully replace
|
|
68
|
+
* the built-in home page with a custom Astro component.
|
|
69
|
+
* - Pass `false` to render an empty home page.
|
|
70
|
+
* - Omit the option to use Astrobook's built-in home.
|
|
71
|
+
*
|
|
72
|
+
* @default 'astrobook/components/home.astro'
|
|
73
|
+
*/
|
|
74
|
+
home?: string | false
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Customize the content of Astrobook's built-in home page (title, subtitle,
|
|
78
|
+
* version badge, GitHub badge).
|
|
79
|
+
*/
|
|
80
|
+
homeContent?: HomeContentOptions
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for customizing the content of the built-in home page.
|
|
85
|
+
*/
|
|
86
|
+
export interface HomeContentOptions {
|
|
87
|
+
/**
|
|
88
|
+
* The main title shown on the home page. Set to `false` to hide.
|
|
89
|
+
* @default 'Astrobook'
|
|
90
|
+
*/
|
|
91
|
+
title?: string | false
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The subtitle shown beneath the title. Set to `false` to hide.
|
|
95
|
+
* @default 'The minimal UI component playground'
|
|
96
|
+
*/
|
|
97
|
+
subtitle?: string | false
|
|
98
|
+
|
|
99
|
+
/** Configuration for the version badge. Set to `false` to hide. */
|
|
100
|
+
version?: HomeVersionOptions | false
|
|
101
|
+
|
|
102
|
+
/** Configuration for the GitHub repository badge. Set to `false` to hide. */
|
|
103
|
+
repo?: HomeRepoOptions | false
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface HomeVersionOptions {
|
|
107
|
+
/** The URL the version badge links to. */
|
|
108
|
+
href?: string
|
|
109
|
+
|
|
110
|
+
/** The label rendered inside the version badge. */
|
|
111
|
+
label?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface HomeRepoOptions {
|
|
115
|
+
/** The URL the repository badge links to. */
|
|
116
|
+
href?: string
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The label rendered inside the repository badge.
|
|
120
|
+
* @default 'Star on GitHub'
|
|
121
|
+
*/
|
|
122
|
+
label?: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface StoryModule {
|
|
126
|
+
/**
|
|
127
|
+
* The id of the story module.
|
|
128
|
+
*
|
|
129
|
+
* @example 'components/ui/button'
|
|
130
|
+
*/
|
|
131
|
+
id: string
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The name of the story module.
|
|
135
|
+
*
|
|
136
|
+
* @example 'Button'
|
|
137
|
+
*/
|
|
138
|
+
name: string
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The directory of the story module. It might be an empty string.
|
|
142
|
+
*
|
|
143
|
+
* @example 'components/ui'
|
|
144
|
+
*/
|
|
145
|
+
directory: string
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The import path of the story module.
|
|
149
|
+
*
|
|
150
|
+
* @example '/Users/john/projects/my-project/components/ui/Button.stories.js'
|
|
151
|
+
* @example '../ui/Button.stories.ts'
|
|
152
|
+
*/
|
|
153
|
+
importPath: string
|
|
154
|
+
|
|
155
|
+
/** All stories in the story module. */
|
|
156
|
+
stories: Story[]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface Story {
|
|
160
|
+
/**
|
|
161
|
+
* The id of the story.
|
|
162
|
+
*
|
|
163
|
+
* @example 'components/ui/button/primary-button'
|
|
164
|
+
*/
|
|
165
|
+
id: string
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The name of the story.
|
|
169
|
+
*
|
|
170
|
+
* @example 'PrimaryButton'
|
|
171
|
+
*/
|
|
172
|
+
name: string
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface GlobalConfig {
|
|
176
|
+
/**
|
|
177
|
+
* The computed base URL path for Astrobook, combining the Astro base URL with
|
|
178
|
+
* the optional subpath.
|
|
179
|
+
*/
|
|
180
|
+
astrobookBase: string
|
|
181
|
+
dashboardBase: string
|
|
182
|
+
storyBase: string
|
|
183
|
+
|
|
184
|
+
/** The path to an Astro component to provide custom tags in the `<head>`. */
|
|
185
|
+
head: string
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The path to an Astro component to render on dashboard homepage.
|
|
189
|
+
* @default astrobook/components/home.astro
|
|
190
|
+
*/
|
|
191
|
+
home: string
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The fully resolved content for the built-in home page. When the user
|
|
195
|
+
* provides a custom `home` component path or `home: false`, this still
|
|
196
|
+
* holds the default values but is not consumed by the rendered component.
|
|
197
|
+
*/
|
|
198
|
+
homeContent: {
|
|
199
|
+
title: string | false
|
|
200
|
+
subtitle: string | false
|
|
201
|
+
version: { href: string; label: string } | false
|
|
202
|
+
repo: { href: string; label: string } | false
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Array of CSS file paths to import into the PREVIEW. */
|
|
206
|
+
css: string[]
|
|
207
|
+
|
|
208
|
+
/** The title for the Astrobook website. */
|
|
209
|
+
title: string
|
|
210
|
+
|
|
211
|
+
trailingSlash: import('astro').AstroConfig['trailingSlash']
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** @internal */
|
|
215
|
+
export interface StoryDecorator {
|
|
216
|
+
/** The component to use as a decorator. */
|
|
217
|
+
component: unknown
|
|
218
|
+
|
|
219
|
+
/** The props to pass to the decorator. */
|
|
220
|
+
props?: object | null | undefined
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** @internal */
|
|
224
|
+
export interface StoryNamedExport {
|
|
225
|
+
/** The decorators to apply to the story. */
|
|
226
|
+
decorators?: StoryDecorator[] | null | undefined
|
|
227
|
+
|
|
228
|
+
/** The arguments to pass to the story. */
|
|
229
|
+
args?: object | null | undefined
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
declare global {
|
|
233
|
+
interface Window {
|
|
234
|
+
astrobook?: {
|
|
235
|
+
setTheme?: (theme: 'dark' | 'light') => void
|
|
236
|
+
getTheme?: () => 'dark' | 'light'
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Vendored from astrobook 0.13.3 (packages/types/lib/virtual.d.ts) — see ../core/LICENSE-astrobook.
|
|
2
|
+
// Divergence: `./types` → `./types.js`, which is how TypeScript's node16/bundler resolution names
|
|
3
|
+
// the sibling `types.ts` module.
|
|
4
|
+
//
|
|
5
|
+
// THE IDS BELOW ARE UPSTREAM'S, BYTE FOR BYTE, AND MUST STAY THAT WAY. The vendored core emits
|
|
6
|
+
// them (src/core/virtual-module/virtual-module-ids.ts) and the vendored UI imports them by name;
|
|
7
|
+
// renaming both sides is possible now that we own both, but it is pure churn with silent-breakage
|
|
8
|
+
// risk, so it is later cleanup and not part of this pass.
|
|
9
|
+
declare module 'virtual:astrobook/story-modules.mjs' {
|
|
10
|
+
const entries: import('./types.js').StoryModule[]
|
|
11
|
+
export default entries
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module 'virtual:astrobook/global-config.mjs' {
|
|
15
|
+
const config: import('./types.js').GlobalConfig
|
|
16
|
+
export default config
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare module 'virtual:astrobook/components/head.mjs' {
|
|
20
|
+
const Head: (props: unknown) => unknown
|
|
21
|
+
export default Head
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare module 'virtual:astrobook/components/home.mjs' {
|
|
25
|
+
const Home: (props: unknown) => unknown
|
|
26
|
+
export default Home
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare module 'virtual:astrobook/user-css.mjs' {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Vendored from astrobook 0.13.3 (packages/ui/src/components/app.astro) — see
|
|
3
|
+
// ../../core/LICENSE-astrobook. Unchanged apart from the .astro extension on the relative import.
|
|
4
|
+
//
|
|
5
|
+
// The `/lab` home page: pure chrome, so it renders through ./layout.astro and gets no consumer
|
|
6
|
+
// CSS at all (ruled 2026-09-22 — ../lab.css).
|
|
7
|
+
import Layout from './layout.astro'
|
|
8
|
+
import Home from 'virtual:astrobook/components/home.mjs'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<Layout story={undefined} hasSidebar={true}>
|
|
12
|
+
<Home />
|
|
13
|
+
</Layout>
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
})
|