blume 1.6.5 → 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 +39 -0
- package/bin/blume.mjs +3 -2
- package/dist/cli/chunk-0qhq7b8q.js +111 -0
- package/dist/cli/chunk-0qhq7b8q.js.map +11 -0
- package/dist/cli/chunk-18tjv4f7.js +96 -0
- package/dist/cli/chunk-18tjv4f7.js.map +10 -0
- package/dist/cli/chunk-27gtm2ym.js +69 -0
- package/dist/cli/chunk-27gtm2ym.js.map +11 -0
- package/dist/cli/chunk-2aj8ddew.js +72 -0
- package/dist/cli/chunk-2aj8ddew.js.map +10 -0
- package/dist/cli/chunk-3r94j3tc.js +221 -0
- package/dist/cli/chunk-3r94j3tc.js.map +10 -0
- package/dist/cli/chunk-4trphnvy.js +102 -0
- package/dist/cli/chunk-4trphnvy.js.map +11 -0
- package/dist/cli/chunk-4xyggvgf.js +21 -0
- package/dist/cli/chunk-4xyggvgf.js.map +10 -0
- package/dist/cli/chunk-5d4q7121.js +4064 -0
- package/dist/cli/chunk-5d4q7121.js.map +40 -0
- package/dist/cli/chunk-5hs6gb7n.js +32 -0
- package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
- package/dist/cli/chunk-6kzzpsx8.js +26 -0
- package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
- package/dist/cli/chunk-8gnpdsn1.js +952 -0
- package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
- package/dist/cli/chunk-9qs6acpw.js +176 -0
- package/dist/cli/chunk-9qs6acpw.js.map +10 -0
- package/dist/cli/chunk-agy5rzxy.js +2453 -0
- package/dist/cli/chunk-agy5rzxy.js.map +15 -0
- package/dist/cli/chunk-bcy492zc.js +16 -0
- package/dist/cli/chunk-bcy492zc.js.map +10 -0
- package/dist/cli/chunk-btfr9yvw.js +41 -0
- package/dist/cli/chunk-btfr9yvw.js.map +10 -0
- package/dist/cli/chunk-cbjnx4s8.js +73 -0
- package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
- package/dist/cli/chunk-cfw6x4rm.js +1967 -0
- package/dist/cli/chunk-cfw6x4rm.js.map +34 -0
- package/dist/cli/chunk-ckh3a410.js +277 -0
- package/dist/cli/chunk-ckh3a410.js.map +11 -0
- package/dist/cli/chunk-drke6t0h.js +259 -0
- package/dist/cli/chunk-drke6t0h.js.map +11 -0
- package/dist/cli/chunk-ev67ycx0.js +15 -0
- package/dist/cli/chunk-ev67ycx0.js.map +10 -0
- package/dist/cli/chunk-ey89bjj1.js +209 -0
- package/dist/cli/chunk-ey89bjj1.js.map +11 -0
- package/dist/cli/chunk-j6pxe0dt.js +69 -0
- package/dist/cli/chunk-j6pxe0dt.js.map +11 -0
- package/dist/cli/chunk-jk1zwka1.js +387 -0
- package/dist/cli/chunk-jk1zwka1.js.map +12 -0
- package/dist/cli/chunk-jtb45atp.js +467 -0
- package/dist/cli/chunk-jtb45atp.js.map +14 -0
- package/dist/cli/chunk-jxkxjsc1.js +76 -0
- package/dist/cli/chunk-jxkxjsc1.js.map +10 -0
- package/dist/cli/chunk-kwx90v78.js +81 -0
- package/dist/cli/chunk-kwx90v78.js.map +10 -0
- package/dist/cli/chunk-n0nyat6g.js +30 -0
- package/dist/cli/chunk-n0nyat6g.js.map +10 -0
- package/dist/cli/chunk-pxj10x8y.js +35 -0
- package/dist/cli/chunk-pxj10x8y.js.map +10 -0
- package/dist/cli/chunk-qq9nm3qd.js +1141 -0
- package/dist/cli/chunk-qq9nm3qd.js.map +19 -0
- package/dist/cli/chunk-s102bysw.js +5170 -0
- package/dist/cli/chunk-s102bysw.js.map +47 -0
- package/dist/cli/chunk-s5dsk8bj.js +769 -0
- package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
- package/dist/cli/chunk-s5e5jt53.js +227 -0
- package/dist/cli/chunk-s5e5jt53.js.map +11 -0
- package/dist/cli/chunk-sbdqrjbb.js +81 -0
- package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
- package/dist/cli/chunk-tnskyrej.js +117 -0
- package/dist/cli/chunk-tnskyrej.js.map +10 -0
- package/dist/cli/chunk-v2ymm99c.js +1016 -0
- package/dist/cli/chunk-v2ymm99c.js.map +13 -0
- package/dist/cli/chunk-v5mm027v.js +185 -0
- package/dist/cli/chunk-v5mm027v.js.map +11 -0
- package/dist/cli/chunk-vt8fgygt.js +23 -0
- package/dist/cli/chunk-vt8fgygt.js.map +10 -0
- package/dist/cli/chunk-vxv4x1n8.js +17 -0
- package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
- package/dist/cli/chunk-wd27zjcz.js +60 -0
- package/dist/cli/chunk-wd27zjcz.js.map +10 -0
- package/dist/cli/chunk-x66c5yjn.js +23 -0
- package/dist/cli/chunk-x66c5yjn.js.map +10 -0
- package/dist/cli/chunk-xv91q4nm.js +5314 -0
- package/dist/cli/chunk-xv91q4nm.js.map +58 -0
- package/dist/cli/chunk-y3g15rvv.js +679 -0
- package/dist/cli/chunk-y3g15rvv.js.map +15 -0
- package/dist/cli/chunk-ye9zdkgv.js +136 -0
- package/dist/cli/chunk-ye9zdkgv.js.map +10 -0
- package/dist/cli/chunk-ynacq3ev.js +1062 -0
- package/dist/cli/chunk-ynacq3ev.js.map +25 -0
- package/dist/cli/chunk-zr3ygrq3.js +54 -0
- package/dist/cli/chunk-zr3ygrq3.js.map +10 -0
- package/dist/cli/index.js +55 -27597
- package/dist/cli/index.js.map +5 -243
- package/dist/types/ai/ask-context.d.ts +26 -0
- package/dist/types/components/layout/nav-utils.d.ts +33 -1
- package/dist/types/core/code-fences.d.ts +11 -0
- package/dist/types/core/package-root.d.ts +1 -1
- package/dist/types/core/schema.d.ts +70 -0
- package/dist/types/theme/fonts.d.ts +22 -22
- package/docs/02-deployment.mdx +22 -1
- package/docs/configuration/ask-ai.mdx +1 -1
- package/docs/configuration/customization.mdx +2 -9
- package/docs/content/navigation.mdx +2 -0
- package/docs/content/syntax.mdx +1 -1
- package/docs/discoverability/open-graph.mdx +4 -0
- package/docs/reference/cli.mdx +1 -1
- package/package.json +4 -2
- package/src/ai/api/handlers.ts +4 -7
- package/src/ai/api/paths.ts +8 -0
- package/src/ai/api/spec.ts +2 -1
- package/src/ai/ask-context.ts +378 -22
- package/src/astro/generate.ts +161 -28
- package/src/astro/include-hmr.ts +10 -13
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +6 -1
- package/src/astro/integration.ts +280 -53
- package/src/astro/module-types.ts +83 -0
- package/src/astro/templates.ts +256 -108
- package/src/audit/image-size.ts +10 -8
- package/src/cli/command-meta.ts +77 -0
- package/src/cli/commands/add.ts +2 -4
- package/src/cli/commands/audit.ts +2 -4
- package/src/cli/commands/build.ts +70 -346
- package/src/cli/commands/check.ts +2 -4
- package/src/cli/commands/dev.ts +31 -42
- package/src/cli/commands/doctor.ts +2 -4
- package/src/cli/commands/eject.ts +3 -41
- package/src/cli/commands/eval.ts +2 -5
- package/src/cli/commands/init.ts +2 -4
- package/src/cli/commands/mcp-stdio.ts +2 -5
- package/src/cli/commands/preview.ts +3 -5
- package/src/cli/commands/sync.ts +2 -4
- package/src/cli/commands/translate.ts +2 -5
- package/src/cli/commands/validate.ts +2 -4
- package/src/cli/commands/version.ts +2 -4
- package/src/cli/eject-scripts.ts +0 -45
- package/src/cli/host-args.ts +16 -0
- package/src/cli/index.ts +84 -35
- package/src/cli/lazy-command.ts +47 -0
- package/src/components/Icon.astro +24 -0
- package/src/components/content/GithubInfo.astro +4 -1
- 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 +21 -3
- package/src/components/layout/ReferenceLayout.astro +21 -4
- package/src/components/layout/RootLayout.astro +44 -6
- package/src/components/layout/nav-cache.ts +49 -0
- package/src/components/layout/nav-utils.ts +69 -1
- package/src/components/layout/page-locale.ts +29 -0
- package/src/core/api-name.ts +18 -0
- package/src/core/code-fences.ts +48 -0
- package/src/core/content-assets.ts +3 -7
- package/src/core/includes.ts +3 -7
- package/src/core/package-root.ts +1 -1
- package/src/core/schema.ts +19 -0
- package/src/core/sources/normalize.ts +2 -37
- package/src/core/sources/obsidian.ts +3 -2
- package/src/core/svg-dimensions.ts +97 -0
- package/src/core/version-cut.ts +2 -2
- package/src/deploy/artifacts.ts +370 -0
- package/src/deploy/cloudflare-negotiation.ts +97 -32
- package/src/deploy/function-bundle.ts +66 -20
- package/src/deploy/sitemap.ts +6 -0
- package/src/deploy/vercel-negotiation.ts +8 -30
- 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 +18 -16
- package/src/og/index.ts +8 -1
- package/src/openapi/render-mdx.ts +9 -5
- package/src/registry/eject.ts +23 -10
- package/src/theme/entry.ts +41 -7
- package/src/theme/fonts.ts +30 -23
|
@@ -55,6 +55,31 @@ export interface AskRetrievalOptions {
|
|
|
55
55
|
* Exported for testing; {@link createAskContext} is the runtime entry point.
|
|
56
56
|
*/
|
|
57
57
|
export declare const relevantExcerpt: (content: string, query: string, max: number) => string;
|
|
58
|
+
interface PageSection {
|
|
59
|
+
/** Word tokens of the section's heading line, or none when it has no heading. */
|
|
60
|
+
headingWords: string[];
|
|
61
|
+
/** Position in the page, for source-order output and omission markers. */
|
|
62
|
+
index: number;
|
|
63
|
+
text: string;
|
|
64
|
+
/** The section's word tokens, cut once so scoring is a prefix test. */
|
|
65
|
+
words: string[];
|
|
66
|
+
}
|
|
67
|
+
/** A page split into sections once, so per-request scoring never re-tokenizes. */
|
|
68
|
+
export interface ParsedPage {
|
|
69
|
+
sections: PageSection[];
|
|
70
|
+
/** NFC-normalized, LF-only, trimmed page text; excerpts slice from it. */
|
|
71
|
+
text: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Split a page at its `##`+ headings (outside code fences). The text above the
|
|
75
|
+
* first heading is the page's own lead-in and is a section like any other.
|
|
76
|
+
* Line endings are folded to LF first so a CRLF checkout splits and matches the
|
|
77
|
+
* same way as an LF one. Exported for testing; {@link createAskContext}
|
|
78
|
+
* parses each page once and caches it across requests.
|
|
79
|
+
*/
|
|
80
|
+
export declare const parsePage: (content: string) => ParsedPage;
|
|
81
|
+
/** {@link excerptPage} over a page parsed on the spot. Exported for testing. */
|
|
82
|
+
export declare const sectionExcerpt: (content: string, query: string, max: number) => string;
|
|
58
83
|
/**
|
|
59
84
|
* Build the request-time grounding function for the Ask AI endpoint.
|
|
60
85
|
*
|
|
@@ -76,3 +101,4 @@ export declare const createAskContext: (data: AskData, options?: {
|
|
|
76
101
|
instructions?: string;
|
|
77
102
|
retrieval?: AskRetrievalOptions;
|
|
78
103
|
}) => ((messages: AskMessage[], page?: AskPage) => Promise<string | undefined>);
|
|
104
|
+
export {};
|
|
@@ -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[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The open fence's delimiter char and run length, or null outside one. */
|
|
2
|
+
export type FenceState = {
|
|
3
|
+
delimiter: "`" | "~";
|
|
4
|
+
length: number;
|
|
5
|
+
} | null;
|
|
6
|
+
/**
|
|
7
|
+
* Advance the fenced-code state for one line: an opening fence records its
|
|
8
|
+
* delimiter and run length, only a bare run of the same character at least as
|
|
9
|
+
* long closes it (CommonMark), and any other line leaves the state untouched.
|
|
10
|
+
*/
|
|
11
|
+
export declare const nextFenceState: (line: string, fence: FenceState) => FenceState;
|
|
@@ -11,7 +11,7 @@ export declare const findPackageRoot: (start: string) => string;
|
|
|
11
11
|
* Anchoring here — rather than at a fixed offset from `import.meta` — keeps the
|
|
12
12
|
* package's own `src/`, assets, and `node_modules` locatable whether the code
|
|
13
13
|
* runs from source under Bun (`src/...`) or from the published, bundled CLI
|
|
14
|
-
* (`dist/cli
|
|
14
|
+
* (`dist/cli/*.js`). The two layouts sit at different depths, so a relative
|
|
15
15
|
* `../..` resolves to different places; locating `package.json` does not.
|
|
16
16
|
*/
|
|
17
17
|
export declare const packageRoot: () => string;
|
|
@@ -146,6 +146,76 @@ export declare const pageMetaSchema: z.ZodObject<{
|
|
|
146
146
|
}, z.core.$strict>;
|
|
147
147
|
export type PageMeta = z.infer<typeof pageMetaBaseSchema>;
|
|
148
148
|
export type PageMetaInput = z.input<typeof pageMetaBaseSchema>;
|
|
149
|
+
/**
|
|
150
|
+
* The page schema as the generated content collections declare it, so
|
|
151
|
+
* `entry.data` is typed and normalized (dates as ISO strings, X handles with
|
|
152
|
+
* their `@`) the same way the scan's `PageMeta` is. Two deliberate loosenings
|
|
153
|
+
* over {@link pageMetaSchema}: custom keys (`frontmatter.extend`, per-type
|
|
154
|
+
* maps) pass through instead of failing the strict parse, and a page the
|
|
155
|
+
* strict parse rejects resolves to the empty defaults instead of throwing.
|
|
156
|
+
* The scan has already dropped such a page with a `BLUME_FRONTMATTER_INVALID`
|
|
157
|
+
* diagnostic — Blume continues without it unless `--strict` — so the
|
|
158
|
+
* collection must not turn that dropped page into a failed content sync.
|
|
159
|
+
*/
|
|
160
|
+
export declare const pageCollectionSchema: z.ZodCatch<z.ZodObject<{
|
|
161
|
+
ai: z.ZodPrefault<z.ZodObject<{
|
|
162
|
+
exclude: z.ZodDefault<z.ZodBoolean>;
|
|
163
|
+
}, z.core.$strict>>;
|
|
164
|
+
authors: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
165
|
+
avatar: z.ZodOptional<z.ZodString>;
|
|
166
|
+
image: z.ZodOptional<z.ZodString>;
|
|
167
|
+
name: z.ZodString;
|
|
168
|
+
url: z.ZodOptional<z.ZodString>;
|
|
169
|
+
}, z.core.$catchall<z.ZodUnknown>>]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
170
|
+
avatar: z.ZodOptional<z.ZodString>;
|
|
171
|
+
image: z.ZodOptional<z.ZodString>;
|
|
172
|
+
name: z.ZodString;
|
|
173
|
+
url: z.ZodOptional<z.ZodString>;
|
|
174
|
+
}, z.core.$catchall<z.ZodUnknown>>]>>]>>;
|
|
175
|
+
changelog: z.ZodOptional<z.ZodObject<{
|
|
176
|
+
category: z.ZodOptional<z.ZodString>;
|
|
177
|
+
date: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>, z.ZodTransform<string, string | Date>>>;
|
|
178
|
+
version: z.ZodOptional<z.ZodString>;
|
|
179
|
+
}, z.core.$strict>>;
|
|
180
|
+
date: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>, z.ZodTransform<string, string | Date>>>;
|
|
181
|
+
deprecated: z.ZodDefault<z.ZodBoolean>;
|
|
182
|
+
description: z.ZodOptional<z.ZodString>;
|
|
183
|
+
draft: z.ZodDefault<z.ZodBoolean>;
|
|
184
|
+
hidden: z.ZodDefault<z.ZodBoolean>;
|
|
185
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
186
|
+
lastModified: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>, z.ZodTransform<string, string | Date>>>;
|
|
187
|
+
noindex: z.ZodDefault<z.ZodBoolean>;
|
|
188
|
+
search: z.ZodPrefault<z.ZodObject<{
|
|
189
|
+
boost: z.ZodOptional<z.ZodNumber>;
|
|
190
|
+
exclude: z.ZodDefault<z.ZodBoolean>;
|
|
191
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
192
|
+
}, z.core.$strict>>;
|
|
193
|
+
seo: z.ZodPrefault<z.ZodObject<{
|
|
194
|
+
canonical: z.ZodOptional<z.ZodURL>;
|
|
195
|
+
description: z.ZodOptional<z.ZodString>;
|
|
196
|
+
image: z.ZodOptional<z.ZodString>;
|
|
197
|
+
noindex: z.ZodDefault<z.ZodBoolean>;
|
|
198
|
+
title: z.ZodOptional<z.ZodString>;
|
|
199
|
+
x: z.ZodOptional<z.ZodObject<{
|
|
200
|
+
creator: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
201
|
+
}, z.core.$strict>>;
|
|
202
|
+
}, z.core.$strict>>;
|
|
203
|
+
sidebar: z.ZodPrefault<z.ZodObject<{
|
|
204
|
+
badge: z.ZodOptional<z.ZodString>;
|
|
205
|
+
display: z.ZodOptional<z.ZodEnum<{
|
|
206
|
+
flat: "flat";
|
|
207
|
+
group: "group";
|
|
208
|
+
page: "page";
|
|
209
|
+
}>>;
|
|
210
|
+
hidden: z.ZodDefault<z.ZodBoolean>;
|
|
211
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
212
|
+
label: z.ZodOptional<z.ZodString>;
|
|
213
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
214
|
+
}, z.core.$strict>>;
|
|
215
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
216
|
+
title: z.ZodOptional<z.ZodString>;
|
|
217
|
+
type: z.ZodOptional<z.ZodString>;
|
|
218
|
+
}, z.core.$loose>>;
|
|
149
219
|
export declare const folderMetaSchema: z.ZodObject<{
|
|
150
220
|
collapsed: z.ZodOptional<z.ZodBoolean>;
|
|
151
221
|
display: z.ZodOptional<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
|
@@ -101,7 +101,7 @@ On **Vercel**, **Netlify**, and **Cloudflare Pages**, Blume picks the matching a
|
|
|
101
101
|
|
|
102
102
|
A server build includes everything a static build does, plus any Astro endpoints or middleware you add. The `node` adapter produces a standalone server you can run directly.
|
|
103
103
|
|
|
104
|
-
On Vercel and Cloudflare, a server build also turns on [`Accept: text/markdown` content negotiation](/docs/discoverability/markdown#content-negotiation), so an agent requesting any content page with that header receives its raw-Markdown mirror at the same URL. On Vercel, Blume splices header-conditional rewrites into the deploy's routing config; on Cloudflare, it generates a small Worker in front of the Astro one and scopes `assets.run_worker_first` to the content routes, since the platform would otherwise serve the prerendered pages before any server code runs — other assets keep their zero-Worker fast path.
|
|
104
|
+
On Vercel and Cloudflare, a server build also turns on [`Accept: text/markdown` content negotiation](/docs/discoverability/markdown#content-negotiation), so an agent requesting any content page with that header receives its raw-Markdown mirror at the same URL. On Vercel, Blume splices header-conditional rewrites into the deploy's routing config; on Cloudflare, it generates a small Worker in front of the Astro one and scopes `assets.run_worker_first` to the content routes, since the platform would otherwise serve the prerendered pages before any server code runs — other assets keep their zero-Worker fast path. That Worker also answers the prerendered per-page JSON documents (`/api/docs/pages/{route}.json`) from the asset binding when a request for one reaches it, because Astro would otherwise route it to the `/api/` catch-all.
|
|
105
105
|
|
|
106
106
|
:::note
|
|
107
107
|
Server features have their own configuration — for example, Ask AI needs a model API key. See the [Ask AI guide](/docs/configuration/ask-ai) for setup.
|
|
@@ -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.
|
|
@@ -170,7 +170,7 @@ Set `apiKeyEnv` (and, for the named providers, `baseUrl`) on any backend to poin
|
|
|
170
170
|
**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
171
|
:::
|
|
172
172
|
|
|
173
|
-
Keys are read
|
|
173
|
+
Keys are read through Astro's [`getSecret()`](https://docs.astro.build/en/guides/environment-variables/#retrieving-secrets-programmatically), so each adapter supplies them its own way: environment variables on Node, Vercel, and Netlify, and the Worker's [bindings](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#environment-variables-and-secrets) on Cloudflare. Enabling Ask AI also turns on React for the in-page island — see [Customization](/docs/configuration/customization#interactive-islands).
|
|
174
174
|
|
|
175
175
|
## Rate limiting
|
|
176
176
|
|
|
@@ -182,13 +182,6 @@ blume eject --yes
|
|
|
182
182
|
|
|
183
183
|
Eject is a one-way step: the hidden `.blume/` runtime becomes a normal Astro app you own and can modify directly. The `blume` package stays importable, so you keep its components, theme, and Markdown processors.
|
|
184
184
|
|
|
185
|
-
### What eject
|
|
185
|
+
### What eject keeps
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
- **Pagefind search index** — with `search.provider: "pagefind"`, the search UI loads the index from the built site, so search breaks in production until you index it yourself. Install `pagefind` as a devDependency and index after each build: `"build": "astro build && pagefind --site dist"`.
|
|
190
|
-
- **Hosted search sync** — a hosted provider's index is no longer pushed on build; re-upload your search records after each build with the provider's API or CLI.
|
|
191
|
-
- **sitemap.xml** — recreate it with the standard [@astrojs/sitemap](https://docs.astro.build/en/guides/integrations-guide/sitemap/) integration.
|
|
192
|
-
- **robots.txt** — ship your own as `public/robots.txt`.
|
|
193
|
-
- **llms.txt / llms-full.txt and agent-readability.json** — write them by hand (or generate them in a build step of your own) and serve them from `public/`.
|
|
194
|
-
- **Platform redirect files** — `_redirects` and `vercel.json` are no longer emitted for static builds. Your redirects still work as Astro-generated meta-refresh pages, or you can move them into your host's own config.
|
|
187
|
+
The ejected app's `build` script runs plain `astro build`, and the artifacts `blume build` layers on top — the search index (and a hosted provider's index sync), `llms.txt` and `llms-full.txt`, `sitemap.xml`, `robots.txt`, `agent-readability.json`, the `.well-known` discovery files, Agent Skills, and the platform `_redirects`/`_headers` files — are still produced: the Blume integration in the ejected `astro.config.mjs` writes them from Astro's `astro:build:done` hook, scanning the project (your `blume.config.ts` and content) the way the CLI did. What the ejected build does not do is the CLI's adapter post-processing: the Vercel and Cloudflare `Accept: text/markdown` routing splices, the Vercel function-bundle audit, and the `--analyze`/`--budget-*` gate.
|
|
@@ -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/docs/reference/cli.mdx
CHANGED
|
@@ -111,7 +111,7 @@ Add a `tsconfig.json` extending Astro's config to your project root so authored
|
|
|
111
111
|
```json title="tsconfig.json"
|
|
112
112
|
{
|
|
113
113
|
"extends": "astro/tsconfigs/strict",
|
|
114
|
-
"include": [".blume/.astro/types.d.ts", "
|
|
114
|
+
"include": [".blume/.astro/types.d.ts", "**/*"]
|
|
115
115
|
}
|
|
116
116
|
```
|
|
117
117
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blume",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Documentation that's fast, AI-ready, and zero-config.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "bun run scripts/build.ts",
|
|
62
|
+
"bench": "bun bench/run.ts",
|
|
62
63
|
"bundle-docs": "node scripts/bundle-docs.mjs",
|
|
63
64
|
"prepack": "bun run scripts/build.ts && node scripts/bundle-docs.mjs",
|
|
64
65
|
"test": "bun test",
|
|
@@ -99,12 +100,12 @@
|
|
|
99
100
|
"dompurify": "^3.4.15",
|
|
100
101
|
"dotenv": "^17.4.2",
|
|
101
102
|
"epub-gen-memory": "^1.1.2",
|
|
103
|
+
"es-module-lexer": "^3.0.2",
|
|
102
104
|
"fast-xml-parser": "^5.11.1",
|
|
103
105
|
"github-slugger": "^2.0.0",
|
|
104
106
|
"graphql": "^17.0.2",
|
|
105
107
|
"gray-matter": "^4.0.3",
|
|
106
108
|
"html-escaper": "^3.0.3",
|
|
107
|
-
"image-size": "^2.0.2",
|
|
108
109
|
"jiti": "^2.7.0",
|
|
109
110
|
"js-yaml": "^5.4.1",
|
|
110
111
|
"katex": "^0.18.7",
|
|
@@ -166,6 +167,7 @@
|
|
|
166
167
|
"algoliasearch": "^5.59.0",
|
|
167
168
|
"bun-types": "^1.4.2",
|
|
168
169
|
"flexsearch": "^0.8.212",
|
|
170
|
+
"mitata": "1.0.34",
|
|
169
171
|
"typesense": "^3.0.6"
|
|
170
172
|
},
|
|
171
173
|
"peerDependencies": {
|
package/src/ai/api/handlers.ts
CHANGED
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
} from "../mcp/query.ts";
|
|
12
12
|
import type { SearchHitPayload } from "../mcp/query.ts";
|
|
13
13
|
import {
|
|
14
|
-
API_BASE,
|
|
15
14
|
API_PAGES_PATH,
|
|
16
15
|
API_SEARCH_PATH,
|
|
17
16
|
OPENAPI_PATH,
|
|
17
|
+
pageJsonPath,
|
|
18
|
+
pageParam,
|
|
18
19
|
} from "./paths.ts";
|
|
19
20
|
import { problemResponse } from "./problem.ts";
|
|
20
21
|
|
|
@@ -85,10 +86,6 @@ export const jsonResponse = (payload: ApiPayload, status = 200): Response =>
|
|
|
85
86
|
status,
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
/** The `pages/{route}.json` path segment for a route (`index` for home). */
|
|
89
|
-
export const pageParam = (route: string): string =>
|
|
90
|
-
route === "/" ? "index" : route.slice(1);
|
|
91
|
-
|
|
92
89
|
/** The absolute (or root-relative) URL for a base-less path. */
|
|
93
90
|
const siteUrl = (path: string, context: ApiSiteContext): string => {
|
|
94
91
|
const based = withBasePath(context.base, path);
|
|
@@ -98,7 +95,7 @@ const siteUrl = (path: string, context: ApiSiteContext): string => {
|
|
|
98
95
|
const summarize = (route: McpRoute, data: McpData): ApiPageSummary => {
|
|
99
96
|
const summary: ApiPageSummary = {
|
|
100
97
|
contentType: route.contentType,
|
|
101
|
-
json: siteUrl(
|
|
98
|
+
json: siteUrl(pageJsonPath(route.route), data),
|
|
102
99
|
lastModified: route.lastModified,
|
|
103
100
|
locale: route.locale,
|
|
104
101
|
markdownUrl: siteUrl(`/${pageParam(route.route)}.md`, data),
|
|
@@ -163,7 +160,7 @@ export const pageResponse = (data: McpData, route: string): Response => {
|
|
|
163
160
|
return problemResponse({
|
|
164
161
|
code: "PAGE_NOT_FOUND",
|
|
165
162
|
detail: `No documentation page has the route "${route}".`,
|
|
166
|
-
instance: siteUrl(
|
|
163
|
+
instance: siteUrl(pageJsonPath(route), data),
|
|
167
164
|
resolution: `List every page at ${siteUrl(API_PAGES_PATH, data)}, or discover the API through ${siteUrl(OPENAPI_PATH, data)}.`,
|
|
168
165
|
status: 404,
|
|
169
166
|
title: "Page not found",
|
package/src/ai/api/paths.ts
CHANGED
|
@@ -12,3 +12,11 @@ export const API_PAGES_PATH = `${API_BASE}/pages.json`;
|
|
|
12
12
|
export const API_PAGE_PATH = `${API_BASE}/pages/{route}.json`;
|
|
13
13
|
export const API_NAVIGATION_PATH = `${API_BASE}/navigation.json`;
|
|
14
14
|
export const API_SEARCH_PATH = `${API_BASE}/search`;
|
|
15
|
+
|
|
16
|
+
/** The `pages/{route}.json` path segment for a route (`index` for home). */
|
|
17
|
+
export const pageParam = (route: string): string =>
|
|
18
|
+
route === "/" ? "index" : route.slice(1);
|
|
19
|
+
|
|
20
|
+
/** The base-less served path of a route's per-page JSON document. */
|
|
21
|
+
export const pageJsonPath = (route: string): string =>
|
|
22
|
+
`${API_BASE}/pages/${pageParam(route)}.json`;
|
package/src/ai/api/spec.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { apiNamePhrase } from "../../core/api-name.ts";
|
|
1
2
|
import { withBasePath } from "../../core/base-path.ts";
|
|
2
3
|
import { absoluteUrl, siteRoot } from "../../core/site-url.ts";
|
|
3
4
|
import {
|
|
@@ -658,7 +659,7 @@ export const buildApiSpec = (input: ApiSpecInput): ApiSpecDocument => {
|
|
|
658
659
|
`Read-only JSON API over the ${input.name} documentation${input.description ? `: ${input.description}` : "."}`,
|
|
659
660
|
"Every operation is public and needs no authentication. Errors are RFC 9457 problem details (`application/problem+json`) with a stable `code`, a `detail`, and a `resolution` hint.",
|
|
660
661
|
].join("\n\n"),
|
|
661
|
-
title:
|
|
662
|
+
title: apiNamePhrase(input.name),
|
|
662
663
|
version: input.version,
|
|
663
664
|
"x-generator": `blume@${input.version}`,
|
|
664
665
|
};
|