blume 1.6.6 → 1.7.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/CHANGELOG.md +17 -0
- package/dist/cli/{chunk-nyqzjdhj.js → chunk-0qhq7b8q.js} +5 -5
- package/dist/cli/{chunk-cnvm6k3e.js → chunk-18tjv4f7.js} +11 -11
- package/dist/cli/{chunk-62qsssnh.js → chunk-5d4q7121.js} +401 -145
- package/dist/cli/chunk-5d4q7121.js.map +40 -0
- package/dist/cli/{chunk-ag1zyr5x.js → chunk-9qs6acpw.js} +11 -11
- package/dist/cli/{chunk-aerwpe14.js → chunk-agy5rzxy.js} +98 -15
- package/dist/cli/chunk-agy5rzxy.js.map +15 -0
- package/dist/cli/{chunk-x1vrdjyk.js → chunk-cfw6x4rm.js} +5 -5
- package/dist/cli/{chunk-bawgnt8x.js → chunk-ckh3a410.js} +3 -3
- package/dist/cli/{chunk-j00ezcg5.js → chunk-drke6t0h.js} +9 -9
- package/dist/cli/{chunk-3k0kzs6d.js → chunk-j6pxe0dt.js} +2 -2
- package/dist/cli/{chunk-n0y172hf.js → chunk-jk1zwka1.js} +4 -4
- package/dist/cli/{chunk-f75cqye8.js → chunk-jxkxjsc1.js} +10 -10
- package/dist/cli/{chunk-s4k1pnvf.js → chunk-kwx90v78.js} +11 -11
- package/dist/cli/{chunk-9sh49q0h.js → chunk-n0nyat6g.js} +2 -2
- package/dist/cli/{chunk-wkq5tbtq.js → chunk-qq9nm3qd.js} +3 -3
- package/dist/cli/{chunk-etsqspj6.js → chunk-s102bysw.js} +2 -2
- package/dist/cli/{chunk-wb067mv3.js → chunk-s5dsk8bj.js} +18 -7
- package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
- package/dist/cli/{chunk-m3p3wahd.js → chunk-tnskyrej.js} +4 -4
- package/dist/cli/{chunk-vv237fp3.js → chunk-v2ymm99c.js} +26 -12
- package/dist/cli/{chunk-vv237fp3.js.map → chunk-v2ymm99c.js.map} +3 -3
- package/dist/cli/{chunk-5yvt556e.js → chunk-v5mm027v.js} +2 -2
- package/dist/cli/{chunk-vv3f8mb6.js → chunk-xv91q4nm.js} +24 -24
- package/dist/cli/{chunk-vv3f8mb6.js.map → chunk-xv91q4nm.js.map} +3 -3
- package/dist/cli/{chunk-0ewz4trd.js → chunk-y3g15rvv.js} +6 -6
- package/dist/cli/{chunk-tc89yh2r.js → chunk-ye9zdkgv.js} +2 -2
- package/dist/cli/{chunk-n4qjabmt.js → chunk-ynacq3ev.js} +4 -4
- package/dist/cli/{chunk-s4jn7f1q.js → chunk-zr3ygrq3.js} +2 -2
- package/dist/cli/index.js +13 -13
- package/dist/types/components/layout/nav-utils.d.ts +33 -1
- package/dist/types/theme/fonts.d.ts +22 -22
- package/docs/02-deployment.mdx +21 -0
- package/docs/content/navigation.mdx +2 -0
- package/docs/content/syntax.mdx +1 -1
- package/docs/discoverability/open-graph.mdx +4 -0
- package/package.json +1 -1
- package/src/astro/generate.ts +141 -5
- package/src/astro/integration.ts +12 -1
- package/src/astro/module-types.ts +9 -0
- package/src/astro/templates.ts +171 -11
- package/src/cli/commands/build.ts +28 -0
- package/src/components/Icon.astro +24 -0
- package/src/components/icon-sprite-middleware.ts +41 -0
- package/src/components/icon-sprite.ts +93 -0
- package/src/components/layout/IconSprite.astro +11 -0
- package/src/components/layout/NavTree.astro +156 -188
- package/src/components/layout/NavTreeCache.astro +45 -0
- package/src/components/layout/NavTreeScript.astro +256 -0
- package/src/components/layout/PageActions.astro +11 -5
- package/src/components/layout/PageLayout.astro +7 -0
- package/src/components/layout/ReferenceLayout.astro +7 -0
- package/src/components/layout/RootLayout.astro +30 -2
- package/src/components/layout/nav-cache.ts +49 -0
- package/src/components/layout/nav-utils.ts +69 -1
- package/src/markdown/language-icon.ts +64 -20
- package/src/markdown/mermaid.ts +11 -0
- package/src/og/cache.ts +236 -0
- package/src/og/card.ts +12 -4
- package/src/og/index.ts +8 -1
- package/src/registry/eject.ts +23 -8
- package/src/theme/entry.ts +41 -7
- package/src/theme/fonts.ts +30 -23
- package/dist/cli/chunk-62qsssnh.js.map +0 -36
- package/dist/cli/chunk-aerwpe14.js.map +0 -15
- package/dist/cli/chunk-wb067mv3.js.map +0 -13
- /package/dist/cli/{chunk-nyqzjdhj.js.map → chunk-0qhq7b8q.js.map} +0 -0
- /package/dist/cli/{chunk-cnvm6k3e.js.map → chunk-18tjv4f7.js.map} +0 -0
- /package/dist/cli/{chunk-ag1zyr5x.js.map → chunk-9qs6acpw.js.map} +0 -0
- /package/dist/cli/{chunk-x1vrdjyk.js.map → chunk-cfw6x4rm.js.map} +0 -0
- /package/dist/cli/{chunk-bawgnt8x.js.map → chunk-ckh3a410.js.map} +0 -0
- /package/dist/cli/{chunk-j00ezcg5.js.map → chunk-drke6t0h.js.map} +0 -0
- /package/dist/cli/{chunk-3k0kzs6d.js.map → chunk-j6pxe0dt.js.map} +0 -0
- /package/dist/cli/{chunk-n0y172hf.js.map → chunk-jk1zwka1.js.map} +0 -0
- /package/dist/cli/{chunk-f75cqye8.js.map → chunk-jxkxjsc1.js.map} +0 -0
- /package/dist/cli/{chunk-s4k1pnvf.js.map → chunk-kwx90v78.js.map} +0 -0
- /package/dist/cli/{chunk-9sh49q0h.js.map → chunk-n0nyat6g.js.map} +0 -0
- /package/dist/cli/{chunk-wkq5tbtq.js.map → chunk-qq9nm3qd.js.map} +0 -0
- /package/dist/cli/{chunk-etsqspj6.js.map → chunk-s102bysw.js.map} +0 -0
- /package/dist/cli/{chunk-m3p3wahd.js.map → chunk-tnskyrej.js.map} +0 -0
- /package/dist/cli/{chunk-5yvt556e.js.map → chunk-v5mm027v.js.map} +0 -0
- /package/dist/cli/{chunk-0ewz4trd.js.map → chunk-y3g15rvv.js.map} +0 -0
- /package/dist/cli/{chunk-tc89yh2r.js.map → chunk-ye9zdkgv.js.map} +0 -0
- /package/dist/cli/{chunk-n4qjabmt.js.map → chunk-ynacq3ev.js.map} +0 -0
- /package/dist/cli/{chunk-s4jn7f1q.js.map → chunk-zr3ygrq3.js.map} +0 -0
|
@@ -18,15 +18,15 @@ import {
|
|
|
18
18
|
} from "./chunk-btfr9yvw.js";
|
|
19
19
|
import {
|
|
20
20
|
buildMcpData
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import"./chunk-
|
|
23
|
-
import"./chunk-
|
|
21
|
+
} from "./chunk-ynacq3ev.js";
|
|
22
|
+
import"./chunk-v5mm027v.js";
|
|
23
|
+
import"./chunk-j6pxe0dt.js";
|
|
24
24
|
import"./chunk-vxv4x1n8.js";
|
|
25
25
|
import {
|
|
26
26
|
YAML_SCHEMA,
|
|
27
27
|
scanProject
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import"./chunk-
|
|
28
|
+
} from "./chunk-s102bysw.js";
|
|
29
|
+
import"./chunk-xv91q4nm.js";
|
|
30
30
|
import"./chunk-27gtm2ym.js";
|
|
31
31
|
import"./chunk-4xyggvgf.js";
|
|
32
32
|
import"./chunk-6kzzpsx8.js";
|
|
@@ -676,4 +676,4 @@ export {
|
|
|
676
676
|
};
|
|
677
677
|
|
|
678
678
|
//# debugId=D2F08D9122CA47D464756E2164756E21
|
|
679
|
-
//# sourceMappingURL=chunk-
|
|
679
|
+
//# sourceMappingURL=chunk-y3g15rvv.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveRuntimeDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-xv91q4nm.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-ey89bjj1.js";
|
|
@@ -133,4 +133,4 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
133
133
|
export { readDevLock, DevLockHeldError, acquireDevLock, updateDevLockPort, describeDevLock, refuseIfDevRunning };
|
|
134
134
|
|
|
135
135
|
//# debugId=1007843429DAE30664756E2164756E21
|
|
136
|
-
//# sourceMappingURL=chunk-
|
|
136
|
+
//# sourceMappingURL=chunk-ye9zdkgv.js.map
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
readExpandedEntryText,
|
|
4
4
|
rewriteRelativeImages
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-v5mm027v.js";
|
|
6
6
|
import {
|
|
7
7
|
API_CATALOG_PATH,
|
|
8
8
|
API_PAGES_PATH,
|
|
9
9
|
OPENAPI_PATH,
|
|
10
10
|
hasApiCatalog
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-j6pxe0dt.js";
|
|
12
12
|
import {
|
|
13
13
|
absoluteUrl,
|
|
14
14
|
siteRoot
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
parseHeadingMarkers,
|
|
24
24
|
specAddresses,
|
|
25
25
|
specOf
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-s102bysw.js";
|
|
27
27
|
import {
|
|
28
28
|
normalizeBasePath,
|
|
29
29
|
withBasePath
|
|
@@ -1059,4 +1059,4 @@ var buildMcpData = async (project) => {
|
|
|
1059
1059
|
export { buildRssFeeds, renderRssFeed, agentMarkdown, markdownTokenCount, buildRawMarkdown, markdownRoutePaths, buildSearchDocuments, buildMcpData };
|
|
1060
1060
|
|
|
1061
1061
|
//# debugId=8258B61E89F8EE8E64756E2164756E21
|
|
1062
|
-
//# sourceMappingURL=chunk-
|
|
1062
|
+
//# sourceMappingURL=chunk-ynacq3ev.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
loadConfig,
|
|
10
10
|
resolveProjectContext
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-xv91q4nm.js";
|
|
12
12
|
import"./chunk-27gtm2ym.js";
|
|
13
13
|
import {
|
|
14
14
|
logger
|
|
@@ -51,4 +51,4 @@ export {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
//# debugId=A0AB802DBFFA2DAA64756E2164756E21
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-zr3ygrq3.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -57,20 +57,20 @@ var main = defineCommand({
|
|
|
57
57
|
},
|
|
58
58
|
subCommands: {
|
|
59
59
|
add: lazyCommand(commandMeta.add, () => import("./chunk-4trphnvy.js"), "addCommand"),
|
|
60
|
-
audit: lazyCommand(commandMeta.audit, () => import("./chunk-
|
|
61
|
-
build: lazyCommand(commandMeta.build, () => import("./chunk-
|
|
62
|
-
check: lazyCommand(commandMeta.check, () => import("./chunk-
|
|
63
|
-
dev: lazyCommand(commandMeta.dev, () => import("./chunk-
|
|
64
|
-
doctor: lazyCommand(commandMeta.doctor, () => import("./chunk-
|
|
65
|
-
eject: lazyCommand(commandMeta.eject, () => import("./chunk-
|
|
66
|
-
eval: lazyCommand(commandMeta.eval, () => import("./chunk-
|
|
67
|
-
init: lazyCommand(commandMeta.init, () => import("./chunk-
|
|
60
|
+
audit: lazyCommand(commandMeta.audit, () => import("./chunk-cfw6x4rm.js"), "auditCommand"),
|
|
61
|
+
build: lazyCommand(commandMeta.build, () => import("./chunk-v2ymm99c.js"), "buildCommand"),
|
|
62
|
+
check: lazyCommand(commandMeta.check, () => import("./chunk-18tjv4f7.js"), "checkCommand"),
|
|
63
|
+
dev: lazyCommand(commandMeta.dev, () => import("./chunk-9qs6acpw.js"), "devCommand"),
|
|
64
|
+
doctor: lazyCommand(commandMeta.doctor, () => import("./chunk-tnskyrej.js"), "doctorCommand"),
|
|
65
|
+
eject: lazyCommand(commandMeta.eject, () => import("./chunk-jxkxjsc1.js"), "ejectCommand"),
|
|
66
|
+
eval: lazyCommand(commandMeta.eval, () => import("./chunk-y3g15rvv.js"), "evalCommand"),
|
|
67
|
+
init: lazyCommand(commandMeta.init, () => import("./chunk-drke6t0h.js"), "initCommand"),
|
|
68
68
|
"mcp-stdio": lazyCommand(commandMeta["mcp-stdio"], () => import("./chunk-jtb45atp.js"), "mcpStdioCommand"),
|
|
69
|
-
preview: lazyCommand(commandMeta.preview, () => import("./chunk-
|
|
70
|
-
sync: lazyCommand(commandMeta.sync, () => import("./chunk-
|
|
71
|
-
translate: lazyCommand(commandMeta.translate, () => import("./chunk-
|
|
72
|
-
validate: lazyCommand(commandMeta.validate, () => import("./chunk-
|
|
73
|
-
version: lazyCommand(commandMeta.version, () => import("./chunk-
|
|
69
|
+
preview: lazyCommand(commandMeta.preview, () => import("./chunk-zr3ygrq3.js"), "previewCommand"),
|
|
70
|
+
sync: lazyCommand(commandMeta.sync, () => import("./chunk-kwx90v78.js"), "syncCommand"),
|
|
71
|
+
translate: lazyCommand(commandMeta.translate, () => import("./chunk-qq9nm3qd.js"), "translateCommand"),
|
|
72
|
+
validate: lazyCommand(commandMeta.validate, () => import("./chunk-jk1zwka1.js"), "validateCommand"),
|
|
73
|
+
version: lazyCommand(commandMeta.version, () => import("./chunk-ckh3a410.js"), "versionCommand")
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
loadEnvFiles(process.cwd());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NavNode, NavTab } from "../../core/types.ts";
|
|
1
|
+
import type { NavNode, NavTab, Navigation } from "../../core/types.ts";
|
|
2
2
|
/** A flat, ordered page reference used for previous/next pagination. */
|
|
3
3
|
export interface FlatPage {
|
|
4
4
|
route: string;
|
|
@@ -58,3 +58,35 @@ export declare const getPagination: (flat: FlatPage[], route: string) => {
|
|
|
58
58
|
next: FlatPage | null;
|
|
59
59
|
prev: FlatPage | null;
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* A stable id for every group in a sidebar — `g<n>` by pre-order position in
|
|
63
|
+
* the full tree. The layout hands `NavTree` a scoped view of that tree (a
|
|
64
|
+
* tab's section, or the sidebar minus the tab sections), so positions within
|
|
65
|
+
* the rendered slice differ from page to page; these ids name the same group
|
|
66
|
+
* everywhere, which the drill-in panels and the deferred-section fragments
|
|
67
|
+
* (`/blume-nav/…`) rely on. Keyed by node identity: the scoped views reuse
|
|
68
|
+
* the full tree's node objects.
|
|
69
|
+
*/
|
|
70
|
+
export declare const navGroupIds: (sidebar: NavNode[]) => Map<NavNode, string>;
|
|
71
|
+
/** Whether any group in a sidebar renders as a disclosure or a drill-in panel. */
|
|
72
|
+
export declare const hasDeferrableGroups: (sidebar: NavNode[]) => boolean;
|
|
73
|
+
/** One of the navigation trees a site renders, by URL segment. */
|
|
74
|
+
export interface NavVariant {
|
|
75
|
+
/** `current`, or an archived version id. */
|
|
76
|
+
version: string;
|
|
77
|
+
/** `default`, or a locale code. */
|
|
78
|
+
locale: string;
|
|
79
|
+
navigation: Navigation;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Every navigation tree the runtime data holds — the default, each locale's,
|
|
83
|
+
* and each archived version's per locale — keyed the way the deferred
|
|
84
|
+
* sidebar fragments' URLs are (`/blume-nav/<version>/<locale>/…`). An
|
|
85
|
+
* unlocalized version tree is keyed by `""` in the data; it maps to
|
|
86
|
+
* `default` here.
|
|
87
|
+
*/
|
|
88
|
+
export declare const navVariants: (data: {
|
|
89
|
+
navigation: Navigation;
|
|
90
|
+
navigationByLocale: Record<string, Navigation>;
|
|
91
|
+
navigationByVersion: Record<string, Record<string, Navigation>>;
|
|
92
|
+
}) => NavVariant[];
|
|
@@ -71,27 +71,27 @@ export declare const GOOGLE_FONTS: {
|
|
|
71
71
|
"dm-sans": {
|
|
72
72
|
category: "sans";
|
|
73
73
|
family: string;
|
|
74
|
-
weights:
|
|
74
|
+
weights: string[];
|
|
75
75
|
};
|
|
76
76
|
figtree: {
|
|
77
77
|
category: "sans";
|
|
78
78
|
family: string;
|
|
79
|
-
weights:
|
|
79
|
+
weights: string[];
|
|
80
80
|
};
|
|
81
81
|
"fira-code": {
|
|
82
82
|
category: "mono";
|
|
83
83
|
family: string;
|
|
84
|
-
weights:
|
|
84
|
+
weights: string[];
|
|
85
85
|
};
|
|
86
86
|
geist: {
|
|
87
87
|
category: "sans";
|
|
88
88
|
family: string;
|
|
89
|
-
weights:
|
|
89
|
+
weights: string[];
|
|
90
90
|
};
|
|
91
91
|
"geist-mono": {
|
|
92
92
|
category: "mono";
|
|
93
93
|
family: string;
|
|
94
|
-
weights:
|
|
94
|
+
weights: string[];
|
|
95
95
|
};
|
|
96
96
|
"ibm-plex-mono": {
|
|
97
97
|
category: "mono";
|
|
@@ -101,7 +101,7 @@ export declare const GOOGLE_FONTS: {
|
|
|
101
101
|
"ibm-plex-sans": {
|
|
102
102
|
category: "sans";
|
|
103
103
|
family: string;
|
|
104
|
-
weights:
|
|
104
|
+
weights: string[];
|
|
105
105
|
};
|
|
106
106
|
"ibm-plex-serif": {
|
|
107
107
|
category: "serif";
|
|
@@ -111,77 +111,77 @@ export declare const GOOGLE_FONTS: {
|
|
|
111
111
|
inter: {
|
|
112
112
|
category: "sans";
|
|
113
113
|
family: string;
|
|
114
|
-
weights:
|
|
114
|
+
weights: string[];
|
|
115
115
|
};
|
|
116
116
|
"inter-tight": {
|
|
117
117
|
category: "sans";
|
|
118
118
|
family: string;
|
|
119
|
-
weights:
|
|
119
|
+
weights: string[];
|
|
120
120
|
};
|
|
121
121
|
"jetbrains-mono": {
|
|
122
122
|
category: "mono";
|
|
123
123
|
family: string;
|
|
124
|
-
weights:
|
|
124
|
+
weights: string[];
|
|
125
125
|
};
|
|
126
126
|
lora: {
|
|
127
127
|
category: "serif";
|
|
128
128
|
family: string;
|
|
129
|
-
weights:
|
|
129
|
+
weights: string[];
|
|
130
130
|
};
|
|
131
131
|
manrope: {
|
|
132
132
|
category: "sans";
|
|
133
133
|
family: string;
|
|
134
|
-
weights:
|
|
134
|
+
weights: string[];
|
|
135
135
|
};
|
|
136
136
|
merriweather: {
|
|
137
137
|
category: "serif";
|
|
138
138
|
family: string;
|
|
139
|
-
weights:
|
|
139
|
+
weights: string[];
|
|
140
140
|
};
|
|
141
141
|
"open-sans": {
|
|
142
142
|
category: "sans";
|
|
143
143
|
family: string;
|
|
144
|
-
weights:
|
|
144
|
+
weights: string[];
|
|
145
145
|
};
|
|
146
146
|
"playfair-display": {
|
|
147
147
|
category: "serif";
|
|
148
148
|
family: string;
|
|
149
|
-
weights:
|
|
149
|
+
weights: string[];
|
|
150
150
|
};
|
|
151
151
|
"plus-jakarta-sans": {
|
|
152
152
|
category: "sans";
|
|
153
153
|
family: string;
|
|
154
|
-
weights:
|
|
154
|
+
weights: string[];
|
|
155
155
|
};
|
|
156
156
|
roboto: {
|
|
157
157
|
category: "sans";
|
|
158
158
|
family: string;
|
|
159
|
-
weights:
|
|
159
|
+
weights: string[];
|
|
160
160
|
};
|
|
161
161
|
"roboto-mono": {
|
|
162
162
|
category: "mono";
|
|
163
163
|
family: string;
|
|
164
|
-
weights:
|
|
164
|
+
weights: string[];
|
|
165
165
|
};
|
|
166
166
|
"source-code-pro": {
|
|
167
167
|
category: "mono";
|
|
168
168
|
family: string;
|
|
169
|
-
weights:
|
|
169
|
+
weights: string[];
|
|
170
170
|
};
|
|
171
171
|
"source-sans-3": {
|
|
172
172
|
category: "sans";
|
|
173
173
|
family: string;
|
|
174
|
-
weights:
|
|
174
|
+
weights: string[];
|
|
175
175
|
};
|
|
176
176
|
"source-serif-4": {
|
|
177
177
|
category: "serif";
|
|
178
178
|
family: string;
|
|
179
|
-
weights:
|
|
179
|
+
weights: string[];
|
|
180
180
|
};
|
|
181
181
|
"space-grotesk": {
|
|
182
182
|
category: "sans";
|
|
183
183
|
family: string;
|
|
184
|
-
weights:
|
|
184
|
+
weights: string[];
|
|
185
185
|
};
|
|
186
186
|
"space-mono": {
|
|
187
187
|
category: "mono";
|
|
@@ -191,7 +191,7 @@ export declare const GOOGLE_FONTS: {
|
|
|
191
191
|
"work-sans": {
|
|
192
192
|
category: "sans";
|
|
193
193
|
family: string;
|
|
194
|
-
weights:
|
|
194
|
+
weights: string[];
|
|
195
195
|
};
|
|
196
196
|
};
|
|
197
197
|
export type FontSlug = keyof typeof GOOGLE_FONTS;
|
package/docs/02-deployment.mdx
CHANGED
|
@@ -141,6 +141,27 @@ When a feature needs a runtime secret, Blume warns at `blume dev`/`build` if it'
|
|
|
141
141
|
|
|
142
142
|
Set them in `.env.local` for local dev and in your host's environment for production. Build-time secrets for search-index sync (Algolia, Orama Cloud, Typesense) are warned about separately during the sync step.
|
|
143
143
|
|
|
144
|
+
## Build cache
|
|
145
|
+
|
|
146
|
+
Blume keeps two caches a build can reuse. Astro's and Vite's caches live under `.blume/.cache/` (the content store and image transforms among them). Rendered [OG cards](/docs/discoverability/open-graph#card-cache) live in `node_modules/.cache/blume/og`, so a rebuild renders only the cards whose title, description, or branding changed. Whether a platform keeps that directory between deploys varies:
|
|
147
|
+
|
|
148
|
+
- **Vercel** restores `node_modules/**` from its build cache, so cards carry over (the cache is 1 GB, retained for a month, and keyed per branch — a new branch starts from the production cache).
|
|
149
|
+
- **Netlify** restores `node_modules`, so cards carry over.
|
|
150
|
+
- **Cloudflare Workers Builds** persists only package-manager caches and, for a detected Astro project, `node_modules/.astro` — never `node_modules/.cache` — so every deploy renders every card there.
|
|
151
|
+
- **GitHub Actions** and other runners you manage keep nothing unless you cache the directory yourself:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
- uses: actions/cache@v4
|
|
155
|
+
with:
|
|
156
|
+
path: node_modules/.cache/blume/og
|
|
157
|
+
key: blume-og-${{ runner.os }}-${{ hashFiles('**/bun.lock', '**/package-lock.json', '**/pnpm-lock.yaml') }}
|
|
158
|
+
restore-keys: blume-og-${{ runner.os }}-
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Cards are keyed by content, so an imprecise key is fine: a restored cache only ever saves renders, never serves a wrong card.
|
|
162
|
+
|
|
163
|
+
One install command discards the cache on every platform: `npm ci` deletes `node_modules` before installing. Keep `npm install`, `bun install`, or `pnpm install` as the install command to get the reuse.
|
|
164
|
+
|
|
144
165
|
## Build summary
|
|
145
166
|
|
|
146
167
|
Every build prints a summary — output mode, adapter, resolved site URL, search provider, redirect count, sitemap and `llms.txt` status, and any enabled server features — so you can confirm what shipped (including anything auto-detected) before you deploy.
|
|
@@ -69,6 +69,8 @@ navigation: {
|
|
|
69
69
|
`page` mode keeps deep sections tidy — reach for it when groups have many children and you'd rather drill into them than scroll past them.
|
|
70
70
|
:::
|
|
71
71
|
|
|
72
|
+
In both `group` and `page` mode, a section that isn't open on the current page is left out of that page's HTML and fetched the first time it's opened (it's prefetched as soon as the pointer or focus reaches its row, so the open is usually instant, and a section fetched once is kept for the rest of the visit). On a large site this is most of a page's weight: only the open section's rows ship with the page. The rows are prerendered fragments under `/blume-nav/`, so they need no server. Readers without JavaScript see the open section and the group rows; the sitemap, the previous/next links, and the open section keep every page reachable for crawlers.
|
|
73
|
+
|
|
72
74
|
### Per-group overrides
|
|
73
75
|
|
|
74
76
|
Any generated group can opt out of the global mode — no explicit sidebar required. Set `display` in the folder's [`meta.ts`](/docs/content/meta), or — when the folder has an `index` page — under `sidebar` in that page's frontmatter, and only that group changes:
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -384,7 +384,7 @@ flowchart LR
|
|
|
384
384
|
```
|
|
385
385
|
````
|
|
386
386
|
|
|
387
|
-
Diagrams render on the client, so this is an MDX-only feature, and the Mermaid library loads only on pages that include one. Diagrams use Mermaid's dagre layout and classic look by default; opt a single diagram into another layout or look through Mermaid front matter (a `config:` block with `layout: elk` or `look: neo`), and the ELK engine loads only for diagrams that ask for it. The rest of this section is a gallery of common types — see the [Mermaid docs](https://mermaid.js.org/intro/) for the full list.
|
|
387
|
+
Diagrams render on the client, so this is an MDX-only feature, and the Mermaid library loads only on pages that include one; a site with no diagram doesn't ship it at all. Diagrams use Mermaid's dagre layout and classic look by default; opt a single diagram into another layout or look through Mermaid front matter (a `config:` block with `layout: elk` or `look: neo`), and the ELK engine loads only for diagrams that ask for it. The rest of this section is a gallery of common types — see the [Mermaid docs](https://mermaid.js.org/intro/) for the full list.
|
|
388
388
|
|
|
389
389
|
### Flowchart
|
|
390
390
|
|
|
@@ -94,6 +94,10 @@ Google families are fetched at build — so a build that uses them needs network
|
|
|
94
94
|
|
|
95
95
|
An explicit `og.fonts: []` opts out entirely: cards keep the built-in font even when `theme.fonts` is set.
|
|
96
96
|
|
|
97
|
+
## Card cache
|
|
98
|
+
|
|
99
|
+
Rendered cards are cached on disk between builds, keyed by everything that decides their pixels: the page title and description, the brand text, logo, palette, footer text, and fonts (a local font file by its contents), plus the Blume version. A rebuild renders only the cards whose inputs changed and reads the rest back; the build log reports how many were reused. The cache lives in `node_modules/.cache/blume/og`; on a platform that keeps `node_modules` between builds (Vercel and Netlify do, Cloudflare does not) or a CI runner that caches that directory (see [Build cache](/docs/deployment#build-cache)), a deploy that touches a handful of pages re-renders a handful of cards. Cards that no page asked for are removed after each build, so the directory only ever holds the current site's cards.
|
|
100
|
+
|
|
97
101
|
## Custom page titles
|
|
98
102
|
|
|
99
103
|
A custom [`.astro` page](/docs/advanced/custom-pages) has no frontmatter to read, so its generated card is titled by humanizing the last URL segment of its route — `/getting-started` becomes "Getting Started", but `/cli` becomes "Cli". Name those cards explicitly with `og.titles`, keyed by route (`"/"` addresses the home, whose card otherwise carries the site title):
|
package/package.json
CHANGED
package/src/astro/generate.ts
CHANGED
|
@@ -30,9 +30,14 @@ import { buildAskData } from "../ai/ask-data.ts";
|
|
|
30
30
|
import { askBackendRuntimeDep, resolveAskBackend } from "../ai/ask.ts";
|
|
31
31
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
32
32
|
import { buildRawMarkdown, markdownRoutePaths } from "../ai/markdown.ts";
|
|
33
|
+
import type { RawMarkdownEntry } from "../ai/markdown.ts";
|
|
33
34
|
import { buildMcpData } from "../ai/mcp/data.ts";
|
|
34
35
|
import type { McpData } from "../ai/mcp/data.ts";
|
|
35
36
|
import { buildMcpDiscovery, buildMcpServerCard } from "../ai/mcp/discovery.ts";
|
|
37
|
+
import {
|
|
38
|
+
hasDeferrableGroups,
|
|
39
|
+
navVariants,
|
|
40
|
+
} from "../components/layout/nav-utils.ts";
|
|
36
41
|
import { normalizeBasePath } from "../core/base-path.ts";
|
|
37
42
|
import { validateUsedComponents } from "../core/component-diagnostics.ts";
|
|
38
43
|
import { analyzeComponentOverrides } from "../core/component-overrides.ts";
|
|
@@ -68,7 +73,14 @@ import { svgDimensions } from "../core/svg-dimensions.ts";
|
|
|
68
73
|
import { trimChar } from "../core/trim.ts";
|
|
69
74
|
import { resolveTsconfigAliases } from "../core/tsconfig-aliases.ts";
|
|
70
75
|
import type { Diagnostic, Navigation } from "../core/types.ts";
|
|
76
|
+
import { getBlumeVersion } from "../core/version.ts";
|
|
71
77
|
import { buildRssFeeds, renderRssFeed } from "../deploy/rss.ts";
|
|
78
|
+
import {
|
|
79
|
+
languageIconCss,
|
|
80
|
+
languageIconSlugsIn,
|
|
81
|
+
} from "../markdown/language-icon.ts";
|
|
82
|
+
import { hasMermaidFence } from "../markdown/mermaid.ts";
|
|
83
|
+
import { ogCacheDir } from "../og/cache.ts";
|
|
72
84
|
import { missingFontFiles, resolveOgFonts } from "../og/derive.ts";
|
|
73
85
|
import type { DerivedOgFonts } from "../og/derive.ts";
|
|
74
86
|
import { resolveOgLogo } from "../og/logo.ts";
|
|
@@ -148,10 +160,13 @@ import {
|
|
|
148
160
|
runtimeDependencies,
|
|
149
161
|
runtimePackageTemplate,
|
|
150
162
|
runtimeTsconfigTemplate,
|
|
163
|
+
featuresTemplate,
|
|
164
|
+
navFragmentTemplate,
|
|
151
165
|
searchClientTemplate,
|
|
152
166
|
searchEndpointTemplate,
|
|
153
167
|
stagedContentDir,
|
|
154
168
|
} from "./templates.ts";
|
|
169
|
+
import type { ClientFeatures } from "./templates.ts";
|
|
155
170
|
|
|
156
171
|
/** Absolute path to the Blume package `src` directory. */
|
|
157
172
|
const BLUME_SRC = join(packageRoot(), "src");
|
|
@@ -322,10 +337,35 @@ export const blumeDepsDir = (pkgDir: string = packageRoot()): string | null => {
|
|
|
322
337
|
};
|
|
323
338
|
|
|
324
339
|
/**
|
|
325
|
-
*
|
|
326
|
-
* replacing a stale junction we own and leaving a real directory untouched.
|
|
340
|
+
* Create `link` as a directory symlink to `target`, falling back to a junction.
|
|
327
341
|
*
|
|
328
|
-
*
|
|
342
|
+
* The type only matters on Windows, and there a junction is the wrong first
|
|
343
|
+
* choice: Windows can't follow a *relative* symlink reached through a
|
|
344
|
+
* junction (it resolves the relative target against the junction-side path,
|
|
345
|
+
* so it lands outside the store and every lookup is ENOENT), and Bun's
|
|
346
|
+
* isolated linker writes exactly those into Blume's dependency directory
|
|
347
|
+
* whenever it holds symlink privilege. A directory symlink traverses them
|
|
348
|
+
* fine. Without that privilege (no Developer Mode, non-admin shell) creating
|
|
349
|
+
* one fails, so fall back to a junction — and in that session the installer
|
|
350
|
+
* had no privilege either, so the deps are absolute junctions a junction can
|
|
351
|
+
* follow. Exported for testing.
|
|
352
|
+
*/
|
|
353
|
+
export const symlinkDir = async (
|
|
354
|
+
target: string,
|
|
355
|
+
link: string
|
|
356
|
+
): Promise<void> => {
|
|
357
|
+
try {
|
|
358
|
+
await symlink(target, link, "dir");
|
|
359
|
+
} catch {
|
|
360
|
+
await symlink(target, link, "junction");
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Point `link` at Blume's dependency directory via a `node_modules` link,
|
|
366
|
+
* replacing a stale link we own and leaving a real directory untouched.
|
|
367
|
+
*
|
|
368
|
+
* `lstat`, not `existsSync`, so a broken link (target since moved) is still
|
|
329
369
|
* detected — `existsSync` follows the link and reports a dangling one as absent.
|
|
330
370
|
*/
|
|
331
371
|
const linkDepsJunction = async (
|
|
@@ -356,7 +396,7 @@ const linkDepsJunction = async (
|
|
|
356
396
|
await rm(link, { force: true });
|
|
357
397
|
}
|
|
358
398
|
await mkdir(dirname(link), { recursive: true });
|
|
359
|
-
await
|
|
399
|
+
await symlinkDir(depsDir, link);
|
|
360
400
|
};
|
|
361
401
|
|
|
362
402
|
/** Read the `version` field of a `package.json`, or null when unreadable. */
|
|
@@ -1894,6 +1934,84 @@ const assertFontFilesExist = (project: BlumeProject): void => {
|
|
|
1894
1934
|
}
|
|
1895
1935
|
};
|
|
1896
1936
|
|
|
1937
|
+
/**
|
|
1938
|
+
* The client libraries a site needs (see `featuresTemplate`): the EPUB
|
|
1939
|
+
* generator when `export.epub` is on, and the Mermaid element when any page's
|
|
1940
|
+
* source has a mermaid fence — read from the raw-Markdown mirrors (every
|
|
1941
|
+
* route's verbatim source) and, for sources that carry their text on the
|
|
1942
|
+
* page record, the record itself.
|
|
1943
|
+
*/
|
|
1944
|
+
export const clientFeaturesFrom = (
|
|
1945
|
+
project: BlumeProject,
|
|
1946
|
+
rawMarkdown: Record<string, RawMarkdownEntry>
|
|
1947
|
+
): ClientFeatures => ({
|
|
1948
|
+
epub: project.config.export.epub,
|
|
1949
|
+
mermaid:
|
|
1950
|
+
Object.values(rawMarkdown).some((entry) =>
|
|
1951
|
+
hasMermaidFence(entry.mdx ?? entry.md ?? "")
|
|
1952
|
+
) ||
|
|
1953
|
+
project.graph.pages.some(
|
|
1954
|
+
(page) => page.body !== undefined && hasMermaidFence(page.body.text)
|
|
1955
|
+
),
|
|
1956
|
+
});
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* The theme's code-block icon rules for the languages the site's Markdown
|
|
1960
|
+
* uses (see `languageIconCss`), read from the raw-Markdown mirrors and the
|
|
1961
|
+
* page records that carry their text.
|
|
1962
|
+
*/
|
|
1963
|
+
export const languageIconCssFrom = (
|
|
1964
|
+
project: BlumeProject,
|
|
1965
|
+
rawMarkdown: Record<string, RawMarkdownEntry>
|
|
1966
|
+
): string =>
|
|
1967
|
+
languageIconCss(
|
|
1968
|
+
languageIconSlugsIn(
|
|
1969
|
+
[
|
|
1970
|
+
...Object.values(rawMarkdown).map(
|
|
1971
|
+
(entry) => entry.mdx ?? entry.md ?? ""
|
|
1972
|
+
),
|
|
1973
|
+
...project.graph.pages.map((page) => page.body?.text ?? ""),
|
|
1974
|
+
].join("\n")
|
|
1975
|
+
)
|
|
1976
|
+
);
|
|
1977
|
+
|
|
1978
|
+
/** {@link languageIconCssFrom} over a fresh read of the raw Markdown (eject). */
|
|
1979
|
+
export const languageIconCssFor = async (
|
|
1980
|
+
project: BlumeProject
|
|
1981
|
+
): Promise<string> =>
|
|
1982
|
+
languageIconCssFrom(project, await buildRawMarkdown(project));
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* The deferred sidebar fragments page, only when some group is a disclosure
|
|
1986
|
+
* or a drill-in panel — a flat sidebar renders every row on every page, so
|
|
1987
|
+
* there is nothing to fetch. A previous pass's page is an orphan the
|
|
1988
|
+
* generator removes when the sidebar goes flat again.
|
|
1989
|
+
*/
|
|
1990
|
+
const writeNavFragments = (
|
|
1991
|
+
write: (path: string, content: string) => Promise<boolean>,
|
|
1992
|
+
srcDir: string,
|
|
1993
|
+
navFragments: boolean
|
|
1994
|
+
): Promise<boolean> =>
|
|
1995
|
+
navFragments
|
|
1996
|
+
? write(
|
|
1997
|
+
join(
|
|
1998
|
+
srcDir,
|
|
1999
|
+
"pages",
|
|
2000
|
+
"blume-nav",
|
|
2001
|
+
"[version]",
|
|
2002
|
+
"[locale]",
|
|
2003
|
+
"[id].astro"
|
|
2004
|
+
),
|
|
2005
|
+
navFragmentTemplate()
|
|
2006
|
+
)
|
|
2007
|
+
: Promise.resolve(false);
|
|
2008
|
+
|
|
2009
|
+
/** {@link clientFeaturesFrom} over a fresh read of the raw Markdown (eject). */
|
|
2010
|
+
export const clientFeaturesFor = async (
|
|
2011
|
+
project: BlumeProject
|
|
2012
|
+
): Promise<ClientFeatures> =>
|
|
2013
|
+
clientFeaturesFrom(project, await buildRawMarkdown(project));
|
|
2014
|
+
|
|
1897
2015
|
/**
|
|
1898
2016
|
* Write (or update) the generated `.blume/` Astro runtime for a project.
|
|
1899
2017
|
* Only files whose content changed are rewritten so Vite HMR stays fast.
|
|
@@ -1909,6 +2027,7 @@ export const generateRuntime = async (
|
|
|
1909
2027
|
const askPath = join(srcDir, "generated", "Ask.astro");
|
|
1910
2028
|
const themePath = join(srcDir, "generated", "app.css");
|
|
1911
2029
|
const searchClientPath = join(srcDir, "generated", "search-client.ts");
|
|
2030
|
+
const featuresPath = join(srcDir, "generated", "features.ts");
|
|
1912
2031
|
const examplesPath = join(srcDir, "generated", "examples.ts");
|
|
1913
2032
|
const examplesThemePath = join(srcDir, "generated", "examples.css");
|
|
1914
2033
|
|
|
@@ -1937,6 +2056,14 @@ export const generateRuntime = async (
|
|
|
1937
2056
|
const askEnabled = config.ai.ask?.enabled ?? false;
|
|
1938
2057
|
const exportPdf = config.export.pdf;
|
|
1939
2058
|
const exportEpub = config.export.epub;
|
|
2059
|
+
// Every route's source Markdown: published as `blume:raw-markdown` below,
|
|
2060
|
+
// and inspected here for the client features the site needs.
|
|
2061
|
+
const rawMarkdown = await buildRawMarkdown(project);
|
|
2062
|
+
const clientFeatures = clientFeaturesFrom(project, rawMarkdown);
|
|
2063
|
+
const navFragments = navVariants(project.graph).some(({ navigation }) =>
|
|
2064
|
+
hasDeferrableGroups(navigation.sidebar)
|
|
2065
|
+
);
|
|
2066
|
+
const languageIcons = languageIconCssFrom(project, rawMarkdown);
|
|
1940
2067
|
// Staged (non-filesystem) sources materialize into `.blume/content`; keyed by
|
|
1941
2068
|
// entryId so i18n duplicates of one entry write a single file. Collected here
|
|
1942
2069
|
// so math detection also sees staged bodies (they never live under root).
|
|
@@ -2060,6 +2187,8 @@ export const generateRuntime = async (
|
|
|
2060
2187
|
context,
|
|
2061
2188
|
examplesPath,
|
|
2062
2189
|
examplesThemePath,
|
|
2190
|
+
features: clientFeatures,
|
|
2191
|
+
featuresPath,
|
|
2063
2192
|
integrationBridge,
|
|
2064
2193
|
needsReact,
|
|
2065
2194
|
needsSvelte,
|
|
@@ -2093,9 +2222,11 @@ export const generateRuntime = async (
|
|
|
2093
2222
|
exportEpub,
|
|
2094
2223
|
exportPdf,
|
|
2095
2224
|
mathEnabled: usesMath,
|
|
2225
|
+
navFragments,
|
|
2096
2226
|
needsReact,
|
|
2097
2227
|
})
|
|
2098
2228
|
),
|
|
2229
|
+
writeNavFragments(write, srcDir, navFragments),
|
|
2099
2230
|
// The header's Ask trigger, behind the `blume:ask` alias. Always written
|
|
2100
2231
|
// (even when Ask is off, as a component that renders nothing) so the alias
|
|
2101
2232
|
// resolves — the same contract as `blume:search-client`.
|
|
@@ -2127,6 +2258,7 @@ export const generateRuntime = async (
|
|
|
2127
2258
|
themePath,
|
|
2128
2259
|
tailwindEntryTemplate({
|
|
2129
2260
|
configTokens: `${buildThemeCss(config.theme)}${buildFontsCss(config.theme.fonts)}`,
|
|
2261
|
+
languageIcons,
|
|
2130
2262
|
sources: [
|
|
2131
2263
|
`${BLUME_SRC}/**/*.{astro,ts,tsx}`,
|
|
2132
2264
|
`${context.root}/**/*.{astro,mdx,ts,tsx}`,
|
|
@@ -2189,6 +2321,10 @@ export const generateRuntime = async (
|
|
|
2189
2321
|
ogRoutes,
|
|
2190
2322
|
{
|
|
2191
2323
|
...projectOgFonts(project),
|
|
2324
|
+
cache: {
|
|
2325
|
+
dir: ogCacheDir(project.context),
|
|
2326
|
+
version: getBlumeVersion(),
|
|
2327
|
+
},
|
|
2192
2328
|
pageDescriptions: config.seo.og.description !== false,
|
|
2193
2329
|
},
|
|
2194
2330
|
changelogIndex
|
|
@@ -2225,6 +2361,7 @@ export const generateRuntime = async (
|
|
|
2225
2361
|
}),
|
|
2226
2362
|
writeNotFoundPage(write, srcDir, pages, project.graph.pages),
|
|
2227
2363
|
write(searchClientPath, searchClientTemplate(config)),
|
|
2364
|
+
write(featuresPath, featuresTemplate(clientFeatures)),
|
|
2228
2365
|
]);
|
|
2229
2366
|
|
|
2230
2367
|
// Client-loaded providers (orama, flexsearch) ship a static index + endpoint.
|
|
@@ -2253,7 +2390,6 @@ export const generateRuntime = async (
|
|
|
2253
2390
|
`${JSON.stringify(buildIncludeGraph(project.graph.pages))}\n`
|
|
2254
2391
|
);
|
|
2255
2392
|
|
|
2256
|
-
const rawMarkdown = await buildRawMarkdown(project);
|
|
2257
2393
|
modules.set("blume:raw-markdown", JSON.stringify(rawMarkdown));
|
|
2258
2394
|
// The originals behind the rewritten `/blume-assets/content/…` references in
|
|
2259
2395
|
// the agent-facing Markdown, plus the endpoint that serves them (and the
|
package/src/astro/integration.ts
CHANGED
|
@@ -374,8 +374,19 @@ export const blumeIntegration = (
|
|
|
374
374
|
filename: MODULE_TYPES_FILE,
|
|
375
375
|
});
|
|
376
376
|
},
|
|
377
|
-
"astro:config:setup": ({
|
|
377
|
+
"astro:config:setup": ({
|
|
378
|
+
addMiddleware,
|
|
379
|
+
createCodegenDir,
|
|
380
|
+
injectRoute,
|
|
381
|
+
}) => {
|
|
378
382
|
codegenDir = createCodegenDir();
|
|
383
|
+
// Splices each page's icon sprite in once the page has rendered (see
|
|
384
|
+
// components/icon-sprite-middleware.ts). Innermost, so a project's
|
|
385
|
+
// own middleware sees the finished HTML.
|
|
386
|
+
addMiddleware({
|
|
387
|
+
entrypoint: "blume/components/icon-sprite-middleware.ts",
|
|
388
|
+
order: "post",
|
|
389
|
+
});
|
|
379
390
|
for (const page of options.pages) {
|
|
380
391
|
injectRoute({
|
|
381
392
|
entrypoint: page.entrypoint,
|