blume 1.6.6 → 1.7.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/CHANGELOG.md +27 -0
- package/dist/cli/{chunk-etsqspj6.js → chunk-12dzsn9b.js} +140 -23
- package/dist/cli/{chunk-etsqspj6.js.map → chunk-12dzsn9b.js.map} +6 -5
- package/dist/cli/{chunk-aerwpe14.js → chunk-4ae4f395.js} +164 -51
- package/dist/cli/chunk-4ae4f395.js.map +15 -0
- package/dist/cli/{chunk-m3p3wahd.js → chunk-52cwcqvp.js} +4 -4
- package/dist/cli/{chunk-j00ezcg5.js → chunk-5gfw0q4j.js} +9 -9
- package/dist/cli/{chunk-bawgnt8x.js → chunk-6mq7qkve.js} +3 -3
- package/dist/cli/{chunk-nyqzjdhj.js → chunk-82atea4k.js} +5 -5
- package/dist/cli/{chunk-tc89yh2r.js → chunk-8p3xe5jv.js} +2 -2
- package/dist/cli/{chunk-s4k1pnvf.js → chunk-90pdhkpm.js} +11 -11
- package/dist/cli/{chunk-n0y172hf.js → chunk-aqjvpd03.js} +4 -4
- package/dist/cli/{chunk-x1vrdjyk.js → chunk-h9ekmtz7.js} +5 -5
- package/dist/cli/{chunk-f75cqye8.js → chunk-he2zfgah.js} +10 -10
- package/dist/cli/{chunk-s4jn7f1q.js → chunk-j5f2wrj5.js} +2 -2
- package/dist/cli/{chunk-wkq5tbtq.js → chunk-k0v1f8bb.js} +3 -3
- package/dist/cli/{chunk-n4qjabmt.js → chunk-ka5k7cz9.js} +6 -19
- package/dist/cli/{chunk-n4qjabmt.js.map → chunk-ka5k7cz9.js.map} +3 -4
- package/dist/cli/{chunk-5yvt556e.js → chunk-kmx2mydj.js} +2 -2
- package/dist/cli/{chunk-ag1zyr5x.js → chunk-mfm4sjwx.js} +11 -11
- package/dist/cli/{chunk-0ewz4trd.js → chunk-np8dmfb0.js} +6 -6
- package/dist/cli/{chunk-cnvm6k3e.js → chunk-pdwg3q9g.js} +11 -11
- package/dist/cli/{chunk-vv237fp3.js → chunk-q56730e0.js} +26 -12
- package/dist/cli/{chunk-vv237fp3.js.map → chunk-q56730e0.js.map} +3 -3
- package/dist/cli/{chunk-3k0kzs6d.js → chunk-qvvpnwaz.js} +2 -2
- package/dist/cli/{chunk-vv3f8mb6.js → chunk-r99hynxh.js} +25 -24
- package/dist/cli/{chunk-vv3f8mb6.js.map → chunk-r99hynxh.js.map} +4 -4
- package/dist/cli/{chunk-wb067mv3.js → chunk-vyqj481z.js} +19 -7
- package/dist/cli/chunk-vyqj481z.js.map +13 -0
- package/dist/cli/{chunk-62qsssnh.js → chunk-x1wvw7a8.js} +411 -145
- package/dist/cli/chunk-x1wvw7a8.js.map +40 -0
- package/dist/cli/{chunk-9sh49q0h.js → chunk-ywn7t0pb.js} +2 -2
- package/dist/cli/index.js +13 -13
- package/dist/types/components/layout/nav-utils.d.ts +46 -1
- package/dist/types/core/config-input.d.ts +7 -0
- package/dist/types/core/schema.d.ts +2 -0
- package/dist/types/theme/fonts.d.ts +22 -22
- package/docs/02-deployment.mdx +21 -0
- package/docs/08-faq.mdx +21 -0
- package/docs/configuration/ask-ai.mdx +16 -0
- package/docs/content/navigation.mdx +2 -0
- package/docs/content/sources.mdx +2 -2
- package/docs/content/syntax.mdx +1 -1
- package/docs/discoverability/open-graph.mdx +4 -0
- package/package.json +1 -1
- package/src/ai/ask.ts +31 -4
- package/src/astro/generate.ts +142 -5
- package/src/astro/integration.ts +12 -1
- package/src/astro/module-types.ts +9 -0
- package/src/astro/templates.ts +260 -56
- package/src/cli/commands/build.ts +28 -0
- package/src/components/Icon.astro +24 -0
- package/src/components/content/YouTube.astro +1 -1
- package/src/components/icon-sprite-middleware.ts +41 -0
- package/src/components/icon-sprite.ts +93 -0
- package/src/components/layout/Header.astro +1 -1
- 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/Search.astro +11 -0
- package/src/components/layout/nav-cache.ts +49 -0
- package/src/components/layout/nav-utils.ts +87 -1
- package/src/core/config-input.ts +7 -0
- package/src/core/schema.ts +5 -0
- package/src/core/sources/assets.ts +162 -26
- package/src/core/sources/notion.ts +60 -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 +24 -8
- package/src/theme/entry.ts +50 -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-m3p3wahd.js.map → chunk-52cwcqvp.js.map} +0 -0
- /package/dist/cli/{chunk-j00ezcg5.js.map → chunk-5gfw0q4j.js.map} +0 -0
- /package/dist/cli/{chunk-bawgnt8x.js.map → chunk-6mq7qkve.js.map} +0 -0
- /package/dist/cli/{chunk-nyqzjdhj.js.map → chunk-82atea4k.js.map} +0 -0
- /package/dist/cli/{chunk-tc89yh2r.js.map → chunk-8p3xe5jv.js.map} +0 -0
- /package/dist/cli/{chunk-s4k1pnvf.js.map → chunk-90pdhkpm.js.map} +0 -0
- /package/dist/cli/{chunk-n0y172hf.js.map → chunk-aqjvpd03.js.map} +0 -0
- /package/dist/cli/{chunk-x1vrdjyk.js.map → chunk-h9ekmtz7.js.map} +0 -0
- /package/dist/cli/{chunk-f75cqye8.js.map → chunk-he2zfgah.js.map} +0 -0
- /package/dist/cli/{chunk-s4jn7f1q.js.map → chunk-j5f2wrj5.js.map} +0 -0
- /package/dist/cli/{chunk-wkq5tbtq.js.map → chunk-k0v1f8bb.js.map} +0 -0
- /package/dist/cli/{chunk-5yvt556e.js.map → chunk-kmx2mydj.js.map} +0 -0
- /package/dist/cli/{chunk-ag1zyr5x.js.map → chunk-mfm4sjwx.js.map} +0 -0
- /package/dist/cli/{chunk-0ewz4trd.js.map → chunk-np8dmfb0.js.map} +0 -0
- /package/dist/cli/{chunk-cnvm6k3e.js.map → chunk-pdwg3q9g.js.map} +0 -0
- /package/dist/cli/{chunk-3k0kzs6d.js.map → chunk-qvvpnwaz.js.map} +0 -0
- /package/dist/cli/{chunk-9sh49q0h.js.map → chunk-ywn7t0pb.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-wd27zjcz.js";
|
|
5
5
|
import {
|
|
6
6
|
needsPlaygroundProxy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-r99hynxh.js";
|
|
8
8
|
|
|
9
9
|
// src/core/server-features.ts
|
|
10
10
|
var serverFeatures = (config) => {
|
|
@@ -27,4 +27,4 @@ var serverFeatures = (config) => {
|
|
|
27
27
|
export { serverFeatures };
|
|
28
28
|
|
|
29
29
|
//# debugId=0DFA9D31AEDBECCC64756E2164756E21
|
|
30
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-ywn7t0pb.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-h9ekmtz7.js"), "auditCommand"),
|
|
61
|
+
build: lazyCommand(commandMeta.build, () => import("./chunk-q56730e0.js"), "buildCommand"),
|
|
62
|
+
check: lazyCommand(commandMeta.check, () => import("./chunk-pdwg3q9g.js"), "checkCommand"),
|
|
63
|
+
dev: lazyCommand(commandMeta.dev, () => import("./chunk-mfm4sjwx.js"), "devCommand"),
|
|
64
|
+
doctor: lazyCommand(commandMeta.doctor, () => import("./chunk-52cwcqvp.js"), "doctorCommand"),
|
|
65
|
+
eject: lazyCommand(commandMeta.eject, () => import("./chunk-he2zfgah.js"), "ejectCommand"),
|
|
66
|
+
eval: lazyCommand(commandMeta.eval, () => import("./chunk-np8dmfb0.js"), "evalCommand"),
|
|
67
|
+
init: lazyCommand(commandMeta.init, () => import("./chunk-5gfw0q4j.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-j5f2wrj5.js"), "previewCommand"),
|
|
70
|
+
sync: lazyCommand(commandMeta.sync, () => import("./chunk-90pdhkpm.js"), "syncCommand"),
|
|
71
|
+
translate: lazyCommand(commandMeta.translate, () => import("./chunk-k0v1f8bb.js"), "translateCommand"),
|
|
72
|
+
validate: lazyCommand(commandMeta.validate, () => import("./chunk-aqjvpd03.js"), "validateCommand"),
|
|
73
|
+
version: lazyCommand(commandMeta.version, () => import("./chunk-6mq7qkve.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,48 @@ 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
|
+
* The locale whose code must not appear as a fragment URL segment: the
|
|
83
|
+
* default locale while its URL prefix is hidden. Astro's i18n routing 404s any
|
|
84
|
+
* page URL carrying that locale's code as a segment (it expects the default
|
|
85
|
+
* locale to be unprefixed), so its trees are keyed `default` instead, the same
|
|
86
|
+
* way its page routes drop the prefix. `null` when every locale is prefixed
|
|
87
|
+
* or the site is single-locale.
|
|
88
|
+
*/
|
|
89
|
+
export declare const hiddenDefaultLocale: (i18n: {
|
|
90
|
+
defaultLocale: string;
|
|
91
|
+
hideDefaultLocalePrefix: boolean;
|
|
92
|
+
} | null) => string | null;
|
|
93
|
+
/**
|
|
94
|
+
* Every navigation tree the runtime data holds — the default, each locale's,
|
|
95
|
+
* and each archived version's per locale — keyed the way the deferred
|
|
96
|
+
* sidebar fragments' URLs are (`/blume-nav/<version>/<locale>/…`). An
|
|
97
|
+
* unlocalized version tree is keyed by `""` in the data; it maps to
|
|
98
|
+
* `default` here, as does the hidden-prefix default locale's (see
|
|
99
|
+
* `hiddenDefaultLocale`), whose current tree is `data.navigation` already.
|
|
100
|
+
*/
|
|
101
|
+
export declare const navVariants: (data: {
|
|
102
|
+
navigation: Navigation;
|
|
103
|
+
navigationByLocale: Record<string, Navigation>;
|
|
104
|
+
navigationByVersion: Record<string, Record<string, Navigation>>;
|
|
105
|
+
}, hiddenDefault?: string | null) => NavVariant[];
|
|
@@ -628,6 +628,13 @@ export interface AskConfig {
|
|
|
628
628
|
* limiting, and streaming. Accepts an absolute URL or root-relative path.
|
|
629
629
|
*/
|
|
630
630
|
endpoint?: string;
|
|
631
|
+
/**
|
|
632
|
+
* Static request headers sent to the provider on every call — a
|
|
633
|
+
* caller-identifying header for a shared backend, for example. Values are
|
|
634
|
+
* written into the generated route as literals, so keep secrets in
|
|
635
|
+
* `apiKeyEnv` rather than here.
|
|
636
|
+
*/
|
|
637
|
+
headers?: Record<string, string>;
|
|
631
638
|
/**
|
|
632
639
|
* Extra system-prompt text appended to the built-in instructions — use it
|
|
633
640
|
* for identity, language, or tone. The built-in grounding behavior (answer
|
|
@@ -336,6 +336,7 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
336
336
|
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
337
337
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
338
|
endpoint: z.ZodOptional<z.ZodString>;
|
|
339
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
339
340
|
instructions: z.ZodOptional<z.ZodString>;
|
|
340
341
|
model: z.ZodDefault<z.ZodString>;
|
|
341
342
|
provider: z.ZodDefault<z.ZodEnum<{
|
|
@@ -577,6 +578,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
577
578
|
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
578
579
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
579
580
|
endpoint: z.ZodOptional<z.ZodString>;
|
|
581
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
580
582
|
instructions: z.ZodOptional<z.ZodString>;
|
|
581
583
|
model: z.ZodDefault<z.ZodString>;
|
|
582
584
|
provider: z.ZodDefault<z.ZodEnum<{
|
|
@@ -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.
|
package/docs/08-faq.mdx
CHANGED
|
@@ -160,3 +160,24 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
160
160
|
:::warning[Version-pinned]
|
|
161
161
|
The patch targets a specific oxfmt build — its diff references a file whose name is hashed per release (`dist/markdown-*.js`). When you bump oxfmt, regenerate the patch (e.g. `bun patch oxfmt`) or check whether the upstream fix has landed and the patch is no longer needed.
|
|
162
162
|
:::
|
|
163
|
+
|
|
164
|
+
## Why does Knip report my `blume.config.ts` dependencies as unused?
|
|
165
|
+
|
|
166
|
+
[Knip](https://knip.dev) only follows imports from files it knows are entry points, and it learns those from its built-in plugins. There is no Blume plugin yet, and Knip's Astro plugin doesn't switch on either: it looks for `astro` in your own `package.json`, but a Blume project depends on `blume`, and the generated `.blume/` Astro project is gitignored, so Knip never sees it. Nothing references `blume.config.ts`, so any package it imports gets reported as unused.
|
|
167
|
+
|
|
168
|
+
Register the files Blume loads from your project root as entries. In `knip.json`:
|
|
169
|
+
|
|
170
|
+
```json knip.json
|
|
171
|
+
{
|
|
172
|
+
"entry": [
|
|
173
|
+
"blume.config.{ts,mjs,js}",
|
|
174
|
+
"components.{ts,tsx}",
|
|
175
|
+
"islands/**/*.{ts,tsx}",
|
|
176
|
+
"pages/**/*"
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
In a monorepo, put the same `entry` list under the docs workspace in `workspaces` instead. Drop any line for a convention you don't use — `components.ts` for [component overrides](/docs/configuration/customization#component-overrides), `islands/` for [interactive islands](/docs/configuration/customization#interactive-islands), and `pages/` for [custom pages](/docs/configuration/customization#custom-pages) (adjust the last one if you've changed `content.pages`).
|
|
182
|
+
|
|
183
|
+
Knip can only follow real imports. A package that's only named inside a string — say, an [Astro integration](/docs/configuration/customization#astro-integrations) that calls `injectScript("page", "import('some-package')")` — still needs an `ignoreDependencies` entry.
|
|
@@ -166,6 +166,22 @@ ai: {
|
|
|
166
166
|
|
|
167
167
|
Set `apiKeyEnv` (and, for the named providers, `baseUrl`) on any backend to point at a different env var or proxy.
|
|
168
168
|
|
|
169
|
+
To send static request headers with every call — a caller-identifying header for a shared backend, say, so its own observability or rate limiting can tell your docs apart from other traffic — set `headers`. It works on every backend, including the gateway:
|
|
170
|
+
|
|
171
|
+
```ts blume.config.ts lineNumbers
|
|
172
|
+
ai: {
|
|
173
|
+
ask: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
provider: "openai-compatible",
|
|
176
|
+
baseUrl: "https://llm.internal.example.com/v1",
|
|
177
|
+
apiKeyEnv: "INTERNAL_LLM_API_KEY",
|
|
178
|
+
headers: { "X-Caller-Id": "docs" },
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The values are written into the generated route as-is, so keep secrets in `apiKeyEnv` rather than in `headers`. The API key's `Authorization` header is applied first, so a custom header can't displace it.
|
|
184
|
+
|
|
169
185
|
:::note
|
|
170
186
|
**Inkeep** answers from the content you've indexed in the Inkeep dashboard — it runs its own retrieval — so Blume leaves it ungrounded. Every other backend is [grounded](#grounding) in this site's pages.
|
|
171
187
|
:::
|
|
@@ -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/sources.mdx
CHANGED
|
@@ -163,7 +163,7 @@ A read token for a private dataset comes from the `SANITY_TOKEN` environment var
|
|
|
163
163
|
|
|
164
164
|
## Notion
|
|
165
165
|
|
|
166
|
-
The built-in `notion` source turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components. `@notionhq/client` (v5 or later) is an optional peer dependency; Blume reads the database through its first data source.
|
|
166
|
+
The built-in `notion` source turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components. Video blocks become a `<YouTube>` embed when they hold a YouTube link and a `<video>` player otherwise, with the block's caption as a `<Frame>` caption either way; a link to a video page rather than a media file (a Vimeo or Loom URL, say) is reported as a warning instead of embedded. `@notionhq/client` (v5 or later) is an optional peer dependency; Blume reads the database through its first data source.
|
|
167
167
|
|
|
168
168
|
```ts blume.config.ts
|
|
169
169
|
import { defineConfig } from "blume";
|
|
@@ -185,7 +185,7 @@ export default defineConfig({
|
|
|
185
185
|
});
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration). By default every page is imported; set `publishedValue` to make the `Status` property a publish gate — any other value then maps to `draft: true`, which production builds drop. **Notion image URLs are signed and expire**, so the adapter downloads them at build time into the site's assets and rewrites the references — a CMS
|
|
188
|
+
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration). By default every page is imported; set `publishedValue` to make the `Status` property a publish gate — any other value then maps to `draft: true`, which production builds drop. **Notion image and video URLs are signed and expire**, so the adapter downloads them at build time into the site's assets and rewrites the references — a CMS asset never rots a static build. API calls are paced through a small request pool (3 at a time, matching Notion's per-integration rate limit) so databases with hundreds of pages import without tripping `429` responses; set `concurrency` on the source to tune it.
|
|
189
189
|
|
|
190
190
|
## Preview and sync
|
|
191
191
|
|
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/ai/ask.ts
CHANGED
|
@@ -7,16 +7,31 @@ import type { AskAiConfig } from "../core/schema.ts";
|
|
|
7
7
|
* AI SDK's OpenAI-compatible provider.
|
|
8
8
|
*/
|
|
9
9
|
export type AskBackend =
|
|
10
|
-
| { kind: "gateway"; model: string }
|
|
11
|
-
| {
|
|
10
|
+
| { headers?: AskHeaders; kind: "gateway"; model: string }
|
|
11
|
+
| {
|
|
12
|
+
apiKeyEnv: string;
|
|
13
|
+
headers?: AskHeaders;
|
|
14
|
+
kind: "openrouter";
|
|
15
|
+
model: string;
|
|
16
|
+
}
|
|
12
17
|
| {
|
|
13
18
|
apiKeyEnv: string;
|
|
14
19
|
baseUrl: string;
|
|
20
|
+
headers?: AskHeaders;
|
|
15
21
|
kind: "openai-compatible";
|
|
16
22
|
model: string;
|
|
17
23
|
name: string;
|
|
18
24
|
};
|
|
19
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Static request headers (`ai.ask.headers`) every backend forwards to its
|
|
28
|
+
* provider factory. Every provider Blume generates against accepts the same
|
|
29
|
+
* `headers` option, so the map travels unchanged; the OpenAI-compatible
|
|
30
|
+
* provider applies them after the `Authorization` header it derives from the
|
|
31
|
+
* API key, so a custom header can't displace auth.
|
|
32
|
+
*/
|
|
33
|
+
export type AskHeaders = Record<string, string>;
|
|
34
|
+
|
|
20
35
|
interface AskPreset {
|
|
21
36
|
apiKeyEnv: string;
|
|
22
37
|
baseUrl?: string;
|
|
@@ -69,17 +84,28 @@ const ASK_PRESETS: AskPresetRegistry = {
|
|
|
69
84
|
|
|
70
85
|
const DEFAULT_MODEL = "openai/gpt-5.5";
|
|
71
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The `headers` field every backend variant shares. An empty map is dropped so
|
|
89
|
+
* the generated route only carries a `headers` option when there is something
|
|
90
|
+
* to send.
|
|
91
|
+
*/
|
|
92
|
+
const askHeadersField = (ask?: AskAiConfig): { headers?: AskHeaders } =>
|
|
93
|
+
ask?.headers && Object.keys(ask.headers).length > 0
|
|
94
|
+
? { headers: ask.headers }
|
|
95
|
+
: {};
|
|
96
|
+
|
|
72
97
|
/** Resolve the `ai.ask` config into the backend the endpoint is built against. */
|
|
73
98
|
export const resolveAskBackend = (ask?: AskAiConfig): AskBackend => {
|
|
74
99
|
const provider = ask?.provider ?? "gateway";
|
|
75
100
|
const model = ask?.model ?? DEFAULT_MODEL;
|
|
101
|
+
const headers = askHeadersField(ask);
|
|
76
102
|
if (provider === "gateway") {
|
|
77
|
-
return { kind: "gateway", model };
|
|
103
|
+
return { ...headers, kind: "gateway", model };
|
|
78
104
|
}
|
|
79
105
|
const preset = ASK_PRESETS[provider];
|
|
80
106
|
const apiKeyEnv = ask?.apiKeyEnv ?? preset?.apiKeyEnv ?? "API_KEY";
|
|
81
107
|
if (provider === "openrouter") {
|
|
82
|
-
return { apiKeyEnv, kind: "openrouter", model };
|
|
108
|
+
return { apiKeyEnv, ...headers, kind: "openrouter", model };
|
|
83
109
|
}
|
|
84
110
|
// `llmgateway`, `inkeep`, and the generic `openai-compatible` provider all
|
|
85
111
|
// stream through the AI SDK's OpenAI-compatible provider. The schema requires
|
|
@@ -87,6 +113,7 @@ export const resolveAskBackend = (ask?: AskAiConfig): AskBackend => {
|
|
|
87
113
|
return {
|
|
88
114
|
apiKeyEnv,
|
|
89
115
|
baseUrl: ask?.baseUrl ?? preset?.baseUrl ?? "",
|
|
116
|
+
...headers,
|
|
90
117
|
kind: OPENAI_COMPATIBLE,
|
|
91
118
|
model,
|
|
92
119
|
name: preset?.name ?? OPENAI_COMPATIBLE,
|