blume 1.5.3 → 1.6.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 +94 -0
- package/dist/cli/index.js +3949 -1403
- package/dist/cli/index.js.map +111 -96
- package/dist/types/ai/component-markdown.d.ts +79 -0
- package/dist/types/components/layout/nav-utils.d.ts +60 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +206 -4
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +33 -2
- package/dist/types/core/github.d.ts +35 -0
- package/dist/types/core/i18n-ui.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +69 -0
- package/dist/types/core/schema.d.ts +122 -1
- package/dist/types/core/sources/types.d.ts +31 -6
- package/dist/types/core/types.d.ts +29 -2
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/openapi/references.d.ts +26 -1
- package/dist/types/seo/jsonld.d.ts +105 -0
- package/dist/types/theme/fonts.d.ts +34 -4
- package/docs/07-faq.mdx +9 -9
- package/docs/_snippets/include-demo.mdx +7 -0
- package/docs/advanced/api-reference.mdx +13 -4
- package/docs/advanced/custom-pages.mdx +4 -2
- package/docs/advanced/graphql.mdx +84 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/configuration/ai.mdx +25 -3
- package/docs/configuration/index.mdx +24 -0
- package/docs/configuration/search.mdx +13 -1
- package/docs/configuration/seo.mdx +30 -3
- package/docs/configuration/theming.mdx +23 -0
- package/docs/content/components.mdx +15 -1
- package/docs/content/includes.mdx +68 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +25 -0
- package/docs/content/sources.mdx +42 -1
- package/docs/content/syntax.mdx +69 -1
- package/docs/content/versioning.mdx +15 -9
- package/docs/reference/cli.mdx +2 -1
- package/package.json +66 -57
- package/skills/blume-migrate/SKILL.md +16 -7
- package/skills/blume-migrate/references/docusaurus.md +5 -3
- package/skills/blume-migrate/references/fumadocs.md +10 -2
- package/skills/blume-migrate/references/mintlify.md +3 -2
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +1 -1
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/ask-data.ts +2 -1
- package/src/ai/component-markdown.ts +199 -36
- package/src/ai/llms.ts +93 -6
- package/src/ai/markdown.ts +2 -2
- package/src/ai/mcp/discovery.ts +10 -2
- package/src/ai/mcp/server.ts +74 -2
- package/src/astro/examples.ts +29 -2
- package/src/astro/generate.ts +282 -177
- package/src/astro/include-hmr.ts +81 -0
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +10 -5
- package/src/astro/markdown-negotiation.ts +1 -1
- package/src/astro/runtime-modules.ts +196 -0
- package/src/astro/templates.ts +365 -113
- package/src/cli/commands/build.ts +91 -16
- package/src/cli/commands/dev.ts +6 -3
- package/src/cli/host-args.ts +18 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/init/questions.ts +1 -0
- package/src/cli/init/scaffold.ts +27 -4
- package/src/components/colors.ts +142 -0
- package/src/components/content/Badge.astro +5 -12
- package/src/components/content/Callout.astro +19 -36
- package/src/components/content/Card.astro +15 -21
- package/src/components/content/Component.astro +10 -1
- package/src/components/content/GithubInfo.astro +28 -9
- package/src/components/content/Tabs.astro +27 -5
- package/src/components/content/github-info.ts +20 -5
- package/src/components/copy-feedback.ts +93 -9
- package/src/components/dropdown-dismiss.ts +122 -0
- package/src/components/islands/ask-ai.tsx +4 -1
- package/src/components/islands/hooks.ts +3 -1
- package/src/components/layout/Fonts.astro +15 -8
- package/src/components/layout/Header.astro +44 -0
- package/src/components/layout/LanguageSwitcher.astro +9 -1
- package/src/components/layout/NavSelector.astro +12 -3
- package/src/components/layout/NavTree.astro +6 -18
- package/src/components/layout/PageActions.astro +54 -22
- package/src/components/layout/PageLayout.astro +2 -0
- package/src/components/layout/ReferenceLayout.astro +6 -1
- package/src/components/layout/RootLayout.astro +42 -15
- package/src/components/layout/Search.astro +36 -4
- package/src/components/layout/TableOfContents.astro +8 -2
- package/src/components/layout/head-scripts.ts +30 -1
- package/src/components/openapi/ApiOverview.astro +13 -3
- package/src/components/openapi/AsyncApiOperation.astro +7 -14
- package/src/components/openapi/GraphqlChip.astro +33 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +111 -0
- package/src/components/openapi/GraphqlOperation.astro +186 -0
- package/src/components/openapi/GraphqlType.astro +154 -0
- package/src/components/openapi/MethodBadge.astro +3 -14
- package/src/components/openapi/Operation.astro +12 -5
- package/src/components/openapi/OperationPanel.astro +43 -0
- package/src/components/openapi/RequestPanel.astro +5 -10
- package/src/components/openapi/Responses.astro +1 -16
- package/src/components/openapi/graphql-helpers.ts +466 -0
- package/src/components/openapi/playground-client.ts +15 -0
- package/src/components/openapi/sample-panels.ts +45 -0
- package/src/components/openapi/snippets.ts +13 -35
- package/src/core/base-path.ts +11 -0
- package/src/core/config-input.ts +209 -2
- package/src/core/config.ts +6 -4
- package/src/core/content-assets.ts +15 -4
- package/src/core/data.ts +28 -3
- package/src/core/define-components.ts +2 -0
- package/src/core/diagnostics.ts +8 -0
- package/src/core/frontmatter.ts +20 -8
- package/src/core/github.ts +71 -0
- package/src/core/graph.ts +22 -8
- package/src/core/heading-markers.ts +96 -0
- package/src/core/i18n-ui.ts +12 -0
- package/src/core/includes.ts +633 -0
- package/src/core/last-modified.ts +36 -11
- package/src/core/links.ts +79 -13
- package/src/core/manifest.ts +10 -0
- package/src/core/meta.ts +2 -1
- package/src/core/nav-diagnostics.ts +11 -2
- package/src/core/navigation.ts +27 -6
- package/src/core/project-graph.ts +61 -9
- package/src/core/schema.ts +235 -36
- package/src/core/server-features.ts +5 -9
- package/src/core/sources/github-releases.ts +2 -2
- package/src/core/sources/normalize.ts +502 -115
- package/src/core/sources/notion.ts +43 -8
- package/src/core/sources/obsidian.ts +1038 -0
- package/src/core/sources/read.ts +36 -1
- package/src/core/sources/resolve.ts +34 -1
- package/src/core/sources/types.ts +28 -6
- package/src/core/sources/watch.ts +12 -8
- package/src/core/tsconfig-aliases.ts +48 -35
- package/src/core/types.ts +31 -2
- package/src/core/ui-packs/ar.ts +2 -0
- package/src/core/ui-packs/bg.ts +3 -0
- package/src/core/ui-packs/bn.ts +2 -0
- package/src/core/ui-packs/ca.ts +3 -0
- package/src/core/ui-packs/cs.ts +2 -0
- package/src/core/ui-packs/da.ts +2 -0
- package/src/core/ui-packs/de.ts +3 -0
- package/src/core/ui-packs/el.ts +3 -0
- package/src/core/ui-packs/es.ts +3 -0
- package/src/core/ui-packs/fa.ts +2 -0
- package/src/core/ui-packs/fi.ts +2 -0
- package/src/core/ui-packs/fr.ts +3 -0
- package/src/core/ui-packs/he.ts +2 -0
- package/src/core/ui-packs/hi.ts +2 -0
- package/src/core/ui-packs/hr.ts +3 -0
- package/src/core/ui-packs/hu.ts +3 -0
- package/src/core/ui-packs/id.ts +3 -0
- package/src/core/ui-packs/it.ts +2 -0
- package/src/core/ui-packs/ja.ts +3 -0
- package/src/core/ui-packs/ko.ts +3 -0
- package/src/core/ui-packs/nl.ts +3 -0
- package/src/core/ui-packs/no.ts +3 -0
- package/src/core/ui-packs/pl.ts +3 -0
- package/src/core/ui-packs/pt-br.ts +3 -0
- package/src/core/ui-packs/pt.ts +3 -0
- package/src/core/ui-packs/ro.ts +3 -0
- package/src/core/ui-packs/ru.ts +3 -0
- package/src/core/ui-packs/sk.ts +2 -0
- package/src/core/ui-packs/sr.ts +2 -0
- package/src/core/ui-packs/sv.ts +3 -0
- package/src/core/ui-packs/th.ts +2 -0
- package/src/core/ui-packs/tr.ts +3 -0
- package/src/core/ui-packs/uk.ts +3 -0
- package/src/core/ui-packs/vi.ts +2 -0
- package/src/core/ui-packs/zh-tw.ts +2 -0
- package/src/core/ui-packs/zh.ts +2 -0
- package/src/core/version-cut.ts +26 -6
- package/src/core/yaml.ts +26 -0
- package/src/deploy/function-bundle.ts +251 -0
- package/src/deploy/vercel-negotiation.ts +49 -6
- package/src/eval/schema.ts +3 -1
- package/src/markdown/code-title.ts +22 -16
- package/src/markdown/features.ts +21 -0
- package/src/markdown/fence-meta.ts +50 -0
- package/src/markdown/heading-anchors.ts +198 -37
- package/src/markdown/include.ts +247 -0
- package/src/markdown/index.ts +43 -34
- package/src/markdown/language-icon.ts +2 -2
- package/src/markdown/mdast.ts +7 -3
- package/src/markdown/ts2js.ts +264 -0
- package/src/og/card.ts +1 -1
- package/src/openapi/asyncapi.ts +4 -1
- package/src/openapi/graphql-build.ts +293 -0
- package/src/openapi/graphql.ts +212 -0
- package/src/openapi/model.ts +38 -5
- package/src/openapi/parse.ts +34 -0
- package/src/openapi/proxy.ts +30 -5
- package/src/openapi/references.ts +97 -13
- package/src/openapi/render-mdx.ts +66 -12
- package/src/openapi/scalar.ts +5 -16
- package/src/openapi/source.ts +91 -23
- package/src/registry/eject.ts +47 -17
- package/src/search/documents.ts +229 -37
- package/src/search/orama-index.ts +9 -5
- package/src/seo/jsonld.ts +293 -51
- package/src/theme/code-block-padding.ts +16 -0
- package/src/theme/entry.ts +67 -13
- package/src/theme/fonts.ts +189 -16
- package/src/theme/sources.ts +49 -0
- package/src/translate/prompts.ts +2 -0
- package/src/translate/run.ts +7 -0
- package/src/translate/work-list.ts +0 -0
|
@@ -20,6 +20,11 @@ export interface RemoteFontConfig {
|
|
|
20
20
|
provider?: RemoteFontProvider;
|
|
21
21
|
/** Weights (or variable ranges like `"100..900"`) to load. Defaults to `[400, 500, 600, 700]`. */
|
|
22
22
|
weights?: (number | string)[];
|
|
23
|
+
/**
|
|
24
|
+
* Character subsets to load (`"latin"`, `"vietnamese"`, `"cyrillic"`, …).
|
|
25
|
+
* Defaults to `latin` plus whatever the site's configured locales need.
|
|
26
|
+
*/
|
|
27
|
+
subsets?: string[];
|
|
23
28
|
/** Fallback stack category. Defaults to `"mono"` for the mono role, `"sans"` otherwise. */
|
|
24
29
|
fallback?: FontCategory;
|
|
25
30
|
}
|
|
@@ -52,6 +57,7 @@ export type FontEntry = {
|
|
|
52
57
|
cssVariable: string;
|
|
53
58
|
fallbacks: string[];
|
|
54
59
|
name: string;
|
|
60
|
+
subsets: string[];
|
|
55
61
|
weights: (number | string)[];
|
|
56
62
|
} | {
|
|
57
63
|
kind: "local";
|
|
@@ -193,19 +199,43 @@ export type FontSlug = keyof typeof GOOGLE_FONTS;
|
|
|
193
199
|
export declare const FONT_SLUGS: string[];
|
|
194
200
|
/** Type guard: is `value` a supported font slug? */
|
|
195
201
|
export declare const isFontSlug: (value: string) => value is FontSlug;
|
|
202
|
+
/** The shape of a config's `i18n` block the font builders read. */
|
|
203
|
+
export interface FontLocaleSource {
|
|
204
|
+
locales: {
|
|
205
|
+
code: string;
|
|
206
|
+
}[];
|
|
207
|
+
}
|
|
208
|
+
/** The locale codes an optional `i18n` block declares (none when absent). */
|
|
209
|
+
export declare const fontLocaleCodes: (i18n: FontLocaleSource | undefined) => string[];
|
|
210
|
+
/**
|
|
211
|
+
* The subsets the site's locales need: `latin` always, plus each locale's
|
|
212
|
+
* script. A site with no `i18n` block (or only Latin-1 languages) gets just
|
|
213
|
+
* `latin` — Astro's own default — so its CSS and preloads don't change.
|
|
214
|
+
*/
|
|
215
|
+
export declare const localeFontSubsets: (locales: string[]) => string[];
|
|
196
216
|
/** Kebab-case a family name into a slug (`"Noto Sans JP"` -> `"noto-sans-jp"`). */
|
|
197
217
|
export declare const slugifyFontName: (name: string) => string;
|
|
198
|
-
/**
|
|
199
|
-
|
|
218
|
+
/**
|
|
219
|
+
* The unique Astro `fonts:` entries for the configured roles (deduped).
|
|
220
|
+
* `locales` are the site's configured locale codes; they pick the subsets a
|
|
221
|
+
* remote family loads unless its config pins `subsets` itself.
|
|
222
|
+
*/
|
|
223
|
+
export declare const buildFontEntries: (fonts: FontsConfig, locales?: string[]) => FontEntry[];
|
|
200
224
|
/**
|
|
201
225
|
* The config-token CSS that points each role's `--blume-font-<role>-src` at the
|
|
202
226
|
* Astro-populated family variable. Concatenated into the generated entry's
|
|
203
227
|
* config tokens; empty when no fonts are set so defaults stay the system stacks.
|
|
204
228
|
*/
|
|
205
229
|
export declare const buildFontsCss: (fonts: FontsConfig) => string;
|
|
206
|
-
/**
|
|
230
|
+
/**
|
|
231
|
+
* One `<Font>` render in the head: its CSS variable + the weights (and, for
|
|
232
|
+
* providers that split faces by subset, the subsets) to preload. Local and
|
|
233
|
+
* Fontshare faces carry no subset, so those entries leave `preloadSubsets`
|
|
234
|
+
* unset and preload by weight alone.
|
|
235
|
+
*/
|
|
207
236
|
export interface FontHead {
|
|
208
237
|
cssVariable: string;
|
|
238
|
+
preloadSubsets?: string[];
|
|
209
239
|
preloadWeights: number[];
|
|
210
240
|
}
|
|
211
241
|
/**
|
|
@@ -213,4 +243,4 @@ export interface FontHead {
|
|
|
213
243
|
* by CSS variable with preload weights unioned across the roles that share a
|
|
214
244
|
* family (so `display` and `body` both set to Inter preload 400/500/600 once).
|
|
215
245
|
*/
|
|
216
|
-
export declare const configuredFonts: (fonts: FontsConfig) => FontHead[];
|
|
246
|
+
export declare const configuredFonts: (fonts: FontsConfig, locales?: string[]) => FontHead[];
|
package/docs/07-faq.mdx
CHANGED
|
@@ -87,14 +87,14 @@ We reported it upstream in [oxc-project/oxc#24096](https://github.com/oxc-projec
|
|
|
87
87
|
|
|
88
88
|
Patch oxfmt so it preserves the line break that sits directly against a `:::` fence. Blume ships the same fix in its own repo, and you can apply it in any project.
|
|
89
89
|
|
|
90
|
-
1. Save the patch as `patches/oxfmt@0.
|
|
91
|
-
|
|
92
|
-
```diff patches/oxfmt@0.
|
|
93
|
-
diff --git a/dist/markdown-
|
|
94
|
-
index
|
|
95
|
-
--- a/dist/markdown-
|
|
96
|
-
+++ b/dist/markdown-
|
|
97
|
-
@@ -
|
|
90
|
+
1. Save the patch as `patches/oxfmt@0.66.0.patch`:
|
|
91
|
+
|
|
92
|
+
```diff patches/oxfmt@0.66.0.patch
|
|
93
|
+
diff --git a/dist/markdown-BgZGxhM2.js b/dist/markdown-BgZGxhM2.js
|
|
94
|
+
index 859231f9387a50df16419bc22b5268c4e446ddbc..dcc0ffda6f71adb27a03e3918a3db6fc7a58ffe9 100644
|
|
95
|
+
--- a/dist/markdown-BgZGxhM2.js
|
|
96
|
+
+++ b/dist/markdown-BgZGxhM2.js
|
|
97
|
+
@@ -4872,7 +4872,43 @@ function lu(e, t, r) {
|
|
98
98
|
case "sentence": return Oh(e, r);
|
|
99
99
|
case "word": return t.parser !== "mdx" ? zh(e, t) : Uh(e);
|
|
100
100
|
case "whitespace": {
|
|
@@ -146,7 +146,7 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
146
146
|
```json package.json
|
|
147
147
|
{
|
|
148
148
|
"patchedDependencies": {
|
|
149
|
-
"oxfmt@0.
|
|
149
|
+
"oxfmt@0.66.0": "patches/oxfmt@0.66.0.patch"
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
```
|
|
@@ -12,7 +12,7 @@ openapi: {
|
|
|
12
12
|
}
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
That mounts the reference at `/reference` (an overview page) with each operation at `/reference/<tag>/<operation>`. The `spec` is either an `http(s)` URL or a path to a local file in your project. Blume parses it with [Scalar's OpenAPI parser](https://github.com/scalar/scalar) — Swagger 2.0 and OpenAPI 3.0 specs are upgraded to 3.1 automatically.
|
|
15
|
+
That mounts the reference at `/reference` (an overview page) with each operation at `/reference/<tag>/<operation>`. The `spec` is either an `http(s)` URL or a path to a local file in your project. Blume parses it with [Scalar's OpenAPI parser](https://github.com/scalar/scalar) — Swagger 2.0 and OpenAPI 3.0 specs are upgraded to 3.1 automatically. Documenting a GraphQL API instead? See the [GraphQL reference](/docs/advanced/graphql).
|
|
16
16
|
|
|
17
17
|
The reference doesn't add a header tab on its own. To surface it, point a [navigation tab](/docs/content/navigation#tabs) at its route — this also scopes the operations sidebar for the native renderer:
|
|
18
18
|
|
|
@@ -139,6 +139,15 @@ openapi: {
|
|
|
139
139
|
- `includeInLlms: false` keeps them out of both `llms.txt` files.
|
|
140
140
|
- `noindex: true` adds crawler noindex metadata and removes the pages from the sitemap.
|
|
141
141
|
|
|
142
|
+
Each operation page's meta description is the operation's own `description` (or `summary`), followed by a generated sentence naming the endpoint — "Reference for the `GET /pets` endpoint in the Petstore API." — so a spec of terse one-line summaries still ships a distinct, snippet-length description per page. That sentence is English. On a site whose spec prose is written in another language, set `seoDescriptionSuffix: false` on the source to drop it and describe each page with the authored prose alone; an operation with neither a `description` nor a `summary` falls back to its title (`GET /pets`), so no page ships an empty description:
|
|
143
|
+
|
|
144
|
+
```ts blume.config.ts lineNumbers
|
|
145
|
+
openapi: {
|
|
146
|
+
enabled: true,
|
|
147
|
+
sources: [{ spec: "./openapi.de.json", seoDescriptionSuffix: false }],
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
142
151
|
With the [Scalar renderer](#the-scalar-renderer), only `noindex` applies — a Scalar-rendered reference already sits outside Blume's search and `llms.txt`, so the two `include*` settings have nothing to act on there.
|
|
143
152
|
|
|
144
153
|
## Authorization
|
|
@@ -165,7 +174,7 @@ openapi: {
|
|
|
165
174
|
}
|
|
166
175
|
```
|
|
167
176
|
|
|
168
|
-
A Scalar-rendered reference is a self-contained embed on its own route — it doesn't weave into Blume's sidebar, search, or `llms.txt`, and Blume's [`playground`](#try-it-playground) config doesn't apply to it. Scalar brings its own request client, which calls your **target API directly from the browser** (the `playground.proxy` route isn't available here), so the API must allow cross-origin requests from the docs site (`Access-Control-Allow-Origin`). `theme` applies to the Scalar renderer only.
|
|
177
|
+
A Scalar-rendered reference is a self-contained embed on its own route — it doesn't weave into Blume's sidebar, search, or `llms.txt`, and Blume's [`playground`](#try-it-playground) config doesn't apply to it. It does follow Blume's light/dark toggle: the embed is pinned to the page's theme when it mounts and switches with it, so Scalar's own theme switch is hidden (set `scalar.forceDarkModeState` or `scalar.darkMode` to hand color mode back to Scalar). Scalar brings its own request client, which calls your **target API directly from the browser** (the `playground.proxy` route isn't available here), so the API must allow cross-origin requests from the docs site (`Access-Control-Allow-Origin`). `theme` applies to the Scalar renderer only.
|
|
169
178
|
|
|
170
179
|
### Passing Scalar options
|
|
171
180
|
|
|
@@ -202,7 +211,7 @@ AsyncAPI **2.x specs are normalized to 3.x automatically** with the official Asy
|
|
|
202
211
|
|
|
203
212
|
Code samples are **protocol-aware**, keyed off the operation's binding (or its servers' protocol): `wscat` and a browser `WebSocket` snippet for WebSockets, `kcat` for Kafka, `mosquitto_pub`/`mosquitto_sub` for MQTT. `codeSamples` filters that set, the same way it picks languages on the `openapi` block; a protocol without a supported tool renders the message payload example alone rather than a fabricated client.
|
|
204
213
|
|
|
205
|
-
Everything documented above carries over, [`playground`](#try-it-for-events) included: `route`, `sources` with `label`/`route`, `expandSchemas`, the [per-source indexing](#per-source-indexing) flags, and search indexing by operation summary and tag.
|
|
214
|
+
Everything documented above carries over, [`playground`](#try-it-for-events) included: `route`, `sources` with `label`/`route`, `expandSchemas`, the [per-source indexing](#per-source-indexing) flags (`seoDescriptionSuffix` too — the generated sentence names the channel and action instead of an endpoint), and search indexing by operation summary and tag.
|
|
206
215
|
|
|
207
216
|
Setting `renderer: "scalar"` opts back into the embedded Scalar SPA, where — as with OpenAPI — only `noindex` applies. Scalar has no AsyncAPI playground of its own; its embed auto-detects the document type and renders channels, operations, messages, and a Models section, so that swap trades the composer away.
|
|
208
217
|
|
|
@@ -225,7 +234,7 @@ asyncapi: {
|
|
|
225
234
|
```
|
|
226
235
|
|
|
227
236
|
:::note
|
|
228
|
-
`playground.proxy`
|
|
237
|
+
`playground.proxy` doesn't apply to event operations. It forwards HTTP requests, and a WebSocket connect goes straight from the browser to the server named in the URL, so there's nothing for a proxy to sit in front of.
|
|
229
238
|
:::
|
|
230
239
|
|
|
231
240
|
The event composer collects no broker credentials. Each operation page's **Authorization** section documents what the broker expects, and a WebSocket connect carries only what's already in the URL. Nothing is persisted for event operations.
|
|
@@ -74,7 +74,7 @@ The module exposes:
|
|
|
74
74
|
type: "BlumeDataConfig",
|
|
75
75
|
required: true,
|
|
76
76
|
description:
|
|
77
|
-
"Resolved site settings: title, description, logo, favicon, appleIcon, banner, theme, site, repoUrl, search, i18n, mcp, ask, og, analytics, feedback, structuredData, toc, codeThemes, codeWrap, and imageZoom.",
|
|
77
|
+
"Resolved site settings: title, description, logo, favicon, appleIcon, banner, theme, site, repoUrl, github (owner, repo, host, and the REST api base — null when unset), search, i18n, mcp, ask, og, analytics, feedback, structuredData, toc, codeThemes, codeWrap, and imageZoom.",
|
|
78
78
|
},
|
|
79
79
|
navigation: {
|
|
80
80
|
type: "Navigation",
|
|
@@ -272,7 +272,9 @@ import data from "blume:data";
|
|
|
272
272
|
|
|
273
273
|
## 404 page
|
|
274
274
|
|
|
275
|
-
Blume ships a default **not found** page out of the box: a centered "404" message wrapped in the site chrome (header, search, theme), served for any unmatched URL. `blume build` writes it to `404.html`, which static hosts serve automatically, and `blume dev` shows it for unknown routes.
|
|
275
|
+
Blume ships a default **not found** page out of the box: a centered "404" message wrapped in the site chrome (header, search, theme), served for any unmatched URL. `blume build` writes it to `404.html`, which static hosts serve automatically, and `blume dev` shows it for unknown routes. Under the message, a **Where to look next** list links every top-level section plus the `sitemap.xml` and [`llms.txt`](/docs/configuration/ai#llmstxt) indexes when they exist, so a reader — or an agent that followed a stale URL — has a way back.
|
|
276
|
+
|
|
277
|
+
The page also has a Markdown twin at `/404.md` with the same recovery links (absolute URLs once [`deployment.site`](/docs/deployment) is set). On a [Vercel server build](/docs/deployment#server-rendering), a request for a missing page that sends [`Accept: text/markdown`](/docs/configuration/ai#content-negotiation), or asks for a `.md` URL no page backs, gets that Markdown body with the `404` status instead of the HTML shell — so an agent never has to parse a page of chrome to learn where to go next.
|
|
276
278
|
|
|
277
279
|
To replace it with your own, add a `pages/404.astro`. It owns the `/404` route the same way `pages/changelog.astro` takes over the changelog — your page wins and the default is dropped. Build it like any other custom page, in `PageLayout` or `RootLayout`:
|
|
278
280
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: GraphQL
|
|
3
|
+
description: Drop in a GraphQL schema and get a native API reference — one real page per operation and per type, in your sidebar and search.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Point Blume at a GraphQL schema and it generates a native API reference: one **real page per root field** — queries, mutations, and subscriptions — plus one **page per named type** (objects, input objects, enums, interfaces, unions, and custom scalars). Every page shows arguments, defaults, deprecations, and usage backlinks, alongside a generated example operation, code samples, and an interactive [Try it](#try-it-playground) panel. Because each page is a genuine Blume page, it gets its own URL, shows up in **site search** and `llms.txt`, and gets an Open Graph image — the same as any hand-written doc.
|
|
7
|
+
|
|
8
|
+
```ts blume.config.ts lineNumbers
|
|
9
|
+
graphql: {
|
|
10
|
+
enabled: true,
|
|
11
|
+
spec: "./schema.graphql",
|
|
12
|
+
endpoint: "https://api.example.com/graphql",
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
That mounts the reference at `/graphql` (an overview page), with root fields at `/graphql/queries/<field>`, `/graphql/mutations/<field>`, and `/graphql/subscriptions/<field>`, and types grouped by kind at `/graphql/objects/<type>`, `/graphql/enums/<type>`, and so on.
|
|
17
|
+
|
|
18
|
+
The `spec` is either a path to a local file in your project or an `http(s)` URL, and accepts two formats:
|
|
19
|
+
|
|
20
|
+
- **SDL text** — a `.graphql` file with type definitions.
|
|
21
|
+
- **An introspection result** — the JSON produced by running the standard introspection query, either the raw `{ "__schema": … }` shape or the full `{ "data": { "__schema": … } }` response envelope.
|
|
22
|
+
|
|
23
|
+
The `endpoint` is the live GraphQL API URL. A schema, unlike an OpenAPI document, names no server — so the endpoint is what the Try it panel and the generated code samples target. Leave it off and the samples render with a placeholder URL readers replace.
|
|
24
|
+
|
|
25
|
+
The reference doesn't add a header tab on its own. To surface it, point a [navigation tab](/docs/content/navigation#tabs) at its route — this also scopes the reference sidebar:
|
|
26
|
+
|
|
27
|
+
```ts blume.config.ts
|
|
28
|
+
navigation: {
|
|
29
|
+
tabs: [{ label: "GraphQL", path: "/graphql" }],
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Generated examples
|
|
34
|
+
|
|
35
|
+
Every operation page carries a complete, valid example operation — one variable per argument, typed off the schema, with a bounded-depth selection set over the return type — plus matching example variables and an example response that mirrors the same selection. Code samples show the exact HTTP request (a JSON `POST` of `{ query, variables }`) in each configured language:
|
|
36
|
+
|
|
37
|
+
```ts blume.config.ts lineNumbers
|
|
38
|
+
graphql: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
spec: "./schema.graphql",
|
|
41
|
+
codeSamples: ["curl", "js"], // built in: curl, js, python
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Type pages
|
|
46
|
+
|
|
47
|
+
Named types get their own deep-linkable pages, grouped by kind in the sidebar: fields and input fields with their types linked, enum values, union members, interface implementations, and a **Used by** section listing the operations that return or accept the type and the other types that reference it. Spec-defined scalars (`String`, `Int`, …) don't get pages; custom scalars do, including their `specifiedBy` URL.
|
|
48
|
+
|
|
49
|
+
## Multiple schemas
|
|
50
|
+
|
|
51
|
+
Each entry in `sources` renders one schema on its own route. A per-source `endpoint` overrides the block-level one:
|
|
52
|
+
|
|
53
|
+
```ts blume.config.ts lineNumbers
|
|
54
|
+
graphql: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
endpoint: "https://api.example.com/graphql",
|
|
57
|
+
sources: [
|
|
58
|
+
{ label: "Public API", spec: "./schema.graphql" },
|
|
59
|
+
{
|
|
60
|
+
label: "Admin API",
|
|
61
|
+
route: "/graphql-admin",
|
|
62
|
+
spec: "./admin.graphql",
|
|
63
|
+
endpoint: "https://admin.example.com/graphql",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each source takes the same [per-source controls](/docs/advanced/api-reference#per-source-indexing) as the OpenAPI block: `includeInSearch`, `includeInLlms`, `noindex`, and `seoDescriptionSuffix` (here the generated sentence names the query, mutation, or type — "Reference for the `pets` query in the GraphQL API.").
|
|
70
|
+
|
|
71
|
+
## Try it playground
|
|
72
|
+
|
|
73
|
+
Query and mutation pages render an interactive panel: edit the request body (the query and variables), point it at your endpoint or a custom URL, and send — the code samples update live so what you copy is byte-for-byte what was sent. Disable it with `playground: false`. Subscription pages show the generated operation and an example event instead: subscriptions run over a stateful transport (WebSocket or SSE) that the playground's single HTTP `POST` can't speak.
|
|
74
|
+
|
|
75
|
+
If your GraphQL API doesn't allow cross-origin requests from the docs site, route sends through a CORS proxy — a URL of your own, or `true` for the built-in `/_api-proxy` endpoint (which requires `deployment.output: "server"`). The built-in proxy only forwards to origins your documented specs declare — each configured GraphQL `endpoint`, plus any absolute `servers[].url` from a documented [OpenAPI spec](/docs/advanced/api-reference) — so a public docs deployment can't be aimed at other hosts. That makes `endpoint` required for a working proxy: without one, the proxy has no origin to allow for this reference and refuses every send (the build warns about this).
|
|
76
|
+
|
|
77
|
+
```ts blume.config.ts lineNumbers
|
|
78
|
+
graphql: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
spec: "./schema.graphql",
|
|
81
|
+
endpoint: "https://api.example.com/graphql",
|
|
82
|
+
playground: { proxy: true },
|
|
83
|
+
}
|
|
84
|
+
```
|
package/docs/advanced/meta.ts
CHANGED
|
@@ -2,6 +2,13 @@ import { defineMeta } from "blume";
|
|
|
2
2
|
|
|
3
3
|
export default defineMeta({
|
|
4
4
|
order: 5,
|
|
5
|
-
pages: [
|
|
5
|
+
pages: [
|
|
6
|
+
"skills",
|
|
7
|
+
"custom-pages",
|
|
8
|
+
"changelog",
|
|
9
|
+
"blog",
|
|
10
|
+
"api-reference",
|
|
11
|
+
"graphql",
|
|
12
|
+
],
|
|
6
13
|
title: "Advanced",
|
|
7
14
|
});
|
|
@@ -33,6 +33,22 @@ ai: {
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
The object form also takes `details`: Markdown placed right after the title and summary in `llms.txt`, before the page sections — the [llms.txt spec](https://llmstxt.org)'s free-form "details" block. It's the place to tell agents _when_ to reach for your product and how to call it, which readiness scanners look for explicitly; an install command and the package name belong here too:
|
|
37
|
+
|
|
38
|
+
```ts blume.config.ts lineNumbers
|
|
39
|
+
ai: {
|
|
40
|
+
llmsTxt: {
|
|
41
|
+
details: [
|
|
42
|
+
"## When to use Acme",
|
|
43
|
+
"",
|
|
44
|
+
"Reach for Acme when a project needs hosted feature flags. Install the CLI with `npm install -g acme`; the API reference below covers every endpoint.",
|
|
45
|
+
].join("\n"),
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`llms.txt` closes with two generated sections that need no configuration. **Agent skills** lists each skill published through [`ai.skills`](#skills-discovery) with its description (where a skill says when to use it). **Agent resources** links every machine-readable artifact the build emits — `llms-full.txt`, the per-page [raw Markdown](#raw-markdown) mirror, the [MCP server](#mcp-server) and its discovery document, the skills index, the [API catalog](#api-catalog), [`agent-readability.json`](#agent-readability), and the [sitemap](/docs/configuration/seo#sitemap) — each only when it exists, so an agent that reads nothing but `llms.txt` still finds the whole surface.
|
|
51
|
+
|
|
36
52
|
To keep an individual page out of both files, set `ai.exclude` in its frontmatter:
|
|
37
53
|
|
|
38
54
|
```mdx
|
|
@@ -59,12 +75,14 @@ Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source — pe
|
|
|
59
75
|
|
|
60
76
|
Nested routes work the same way (`/content/syntax.md`), and the home page is served at `/index.md`.
|
|
61
77
|
|
|
62
|
-
The `.md` variant _downlevels_ components to plain Markdown for consumers that can't interpret JSX: `<TypeTable>` becomes a Markdown table, `<Callout>` a labeled blockquote, `<Steps>` an ordered list, `<Tabs>` bold-labeled sections, and `<YouTube>` a link. Props are evaluated with the page's `frontmatter` in scope, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Anything that can't be converted faithfully — a custom component, or a prop computed from an import — is left as-is, and component markup inside fenced code blocks is never touched. The same conversion applies to `llms-full.txt` and the MCP server's `get_page` tool, so every agent-facing surface reads clean Markdown. When you want the untransformed source, use the `.mdx` variant.
|
|
78
|
+
The `.md` variant _downlevels_ components to plain Markdown for consumers that can't interpret JSX: `<TypeTable>` becomes a Markdown table, `<Callout>` a labeled blockquote, `<Steps>` an ordered list, `<Tabs>` bold-labeled sections, `<Card>` its title as a link over its body (and `<CardGroup>` the cards it holds), and `<YouTube>` a link. Props are evaluated with the page's `frontmatter` in scope, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Anything that can't be converted faithfully — a custom component, or a prop computed from an import — is left as-is, and component markup inside fenced code blocks is never touched. The same conversion applies to `llms-full.txt` and the MCP server's `get_page` tool, so every agent-facing surface reads clean Markdown. When you want the untransformed source, use the `.mdx` variant.
|
|
63
79
|
|
|
64
80
|
### Content negotiation
|
|
65
81
|
|
|
66
82
|
Agents don't need to know the `.md` convention: requesting a page's own URL with an [`Accept: text/markdown`](https://acceptmarkdown.com) header serves the Markdown variant at the same address, with `Vary: Accept` so caches keep the two apart. The dev server honors the header out of the box, and a [Vercel or Cloudflare server build](/docs/deployment#server-rendering) wires the same negotiation into the deploy automatically — routing rules on Vercel, a generated Worker on Cloudflare — no configuration needed. The homepage always negotiates, even when it's a custom landing page rather than a content page: its Markdown mirror falls back to the [`llms.txt`](#llmstxt) index, so an agent asking the site root for Markdown gets the machine-readable map of the site. Markdown responses also carry an `x-markdown-tokens` header — an estimated token count (~4 characters per token), following the convention of [Cloudflare's Markdown for Agents](https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/) — on every surface where Blume controls response headers: the dev server, server-rendered responses, and the negotiated homepage on Vercel and Cloudflare. Other deploy targets serve prerendered pages from a static layer with no request-time hook, so agents there fetch the `.md` URL directly; the [agent readability manifest](#agent-readability) advertises `contentNegotiation` only on deployments that honor the header.
|
|
67
83
|
|
|
84
|
+
Missing pages negotiate too. Every build emits a Markdown [404 page](/docs/advanced/custom-pages#404-page) at `/404.md` — the not-found message followed by recovery links to every top-level section, the sitemap, and `llms.txt` — and on Vercel a request for a nonexistent URL that prefers Markdown, or any `.md` URL with no page behind it, gets that body with a real `404` status rather than the HTML shell.
|
|
85
|
+
|
|
68
86
|
### Custom component serializers
|
|
69
87
|
|
|
70
88
|
Give your own components a Markdown form with `ai.markdownComponents` — a map of JSX name to serializer. Each serializer receives the component's `props` (statically evaluated from the MDX attributes, with the page's `frontmatter` in scope), its `children` (already downleveled to Markdown), and the page's `frontmatter` data, and returns the replacement — or `null` to leave the JSX as-is:
|
|
@@ -85,7 +103,7 @@ export default defineConfig({
|
|
|
85
103
|
});
|
|
86
104
|
```
|
|
87
105
|
|
|
88
|
-
For container components, `childComponents("Name")` extracts direct children by tag — the same way the built-in `<Steps>` serializer collects its `<Step>` items. A same-name entry replaces a built-in serializer, so you can restyle how `<Callout>` downlevels — or return `null` to opt one out entirely.
|
|
106
|
+
For container components, `childComponents("Name")` extracts direct children by tag — the same way the built-in `<Steps>` serializer collects its `<Step>` items — and `childBlocks()` returns every direct child in order, components and prose alike, each already downleveled to a block of Markdown (the built-in `<CardGroup>` serializer is just those blocks joined by blank lines). A same-name entry replaces a built-in serializer, so you can restyle how `<Callout>` downlevels — or return `null` to opt one out entirely.
|
|
89
107
|
|
|
90
108
|
Serializers live in `blume.config.ts`, not `components.tsx`: the config file is executed at build time, while the components file is only statically analyzed (it may import `.astro` files, which can't run outside the site build). Your components themselves stay registered in `components.tsx` exactly as before — `markdownComponents` only adds their agent-facing Markdown form.
|
|
91
109
|
|
|
@@ -93,6 +111,8 @@ Serializers live in `blume.config.ts`, not `components.tsx`: the config file is
|
|
|
93
111
|
|
|
94
112
|
Every page carries a **Copy as Markdown** action — in the [page actions](/docs/content/navigation#page-actions) beneath the table of contents — that copies the page's raw Markdown to the clipboard. It's the same source served at the [`.md` URL](#raw-markdown) above, ready to paste into an LLM, an issue, or your notes. It's available on every page, in dev and production, with no configuration.
|
|
95
113
|
|
|
114
|
+
Where the Clipboard API is unavailable or the browser denies it — in-app browsers, WebViews, insecure origins — the action falls back to the legacy copy command, and if nothing lands on the clipboard the button reports **Copy failed** (localized via `actions.copyFailed`) rather than staying silent. The same fallback backs every copy button Blume renders.
|
|
115
|
+
|
|
96
116
|
## Open in chat
|
|
97
117
|
|
|
98
118
|
The **Open in chat** action opens the current page in an AI assistant — v0, ChatGPT, Claude, T3 Chat, Scira, or Cursor — pre-filled with a prompt that points it at the page's raw Markdown so it can answer questions about what you're reading:
|
|
@@ -101,6 +121,8 @@ The **Open in chat** action opens the current page in an AI assistant — v0, Ch
|
|
|
101
121
|
|
|
102
122
|
Like Copy as Markdown, it needs no setup. The assistant fetches the page over its public URL, so it works as soon as the page is deployed.
|
|
103
123
|
|
|
124
|
+
The prompt is part of the [UI dictionary](/docs/content/i18n#translated-ui) (`actions.openInChatPrompt`), so localized sites send it in their language, and `i18n.ui` can override the wording — keep the `{url}` placeholder, which is replaced with the page's raw-Markdown URL.
|
|
125
|
+
|
|
104
126
|
To tailor the action, set `ai.openInChat`. `false` hides it entirely, and an array of provider keys — `"v0"`, `"chatgpt"`, `"claude"`, `"t3"`, `"scira"`, `"cursor"` — shows just those providers, in the order you list them:
|
|
105
127
|
|
|
106
128
|
```ts blume.config.ts lineNumbers
|
|
@@ -306,7 +328,7 @@ ai: {
|
|
|
306
328
|
| `name` | title | Server name shown to clients (defaults to title). |
|
|
307
329
|
| `instructions` | — | Optional system hint passed to connecting agents. |
|
|
308
330
|
|
|
309
|
-
The server exposes read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. The server card follows the [SEP-2127](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) Server Card extension schema (reverse-DNS `name`, `remotes` transport endpoints), with initialize-shaped compat fields (`serverInfo`, `capabilities`, `transports`) for scanners built against the proposal's earlier revision. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, VS Code, and Codex (shown once [`deployment.site`](/docs/deployment) is set).
|
|
331
|
+
The server exposes read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and every page as an MCP resource (`resources/list` enumerates the pages at their served URLs with a `text/markdown` type; `resources/read` returns the page's agent Markdown, the same output as `get_page`), so clients that attach context by URI can browse the docs without calling a tool. It publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. The server card follows the [SEP-2127](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2127) Server Card extension schema (reverse-DNS `name`, `remotes` transport endpoints), with initialize-shaped compat fields (`serverInfo`, `capabilities`, `transports`) for scanners built against the proposal's earlier revision. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, VS Code, and Codex (shown once [`deployment.site`](/docs/deployment) is set).
|
|
310
332
|
|
|
311
333
|
`search_docs` runs its own full-text index, so it works regardless of your [search](/docs/configuration/search) provider — and even when search is set to `none`. The MCP server is a separate feature from on-page search.
|
|
312
334
|
|
|
@@ -285,6 +285,30 @@ github: {
|
|
|
285
285
|
| `repo` | — | Repository name. |
|
|
286
286
|
| `branch` | `"main"` | Branch that edit links point at. |
|
|
287
287
|
| `dir` | — | Path from the repo root to the project root (for monorepos). |
|
|
288
|
+
| `host` | `"https://github.com"` | Origin of the GitHub instance, for Enterprise installations. Must be HTTP(S); normalized to its origin. |
|
|
289
|
+
| `api` | derived from `host` | REST API base, for `<GithubInfo>` against an Enterprise instance. Must be HTTP(S); normalized to an origin and path. |
|
|
290
|
+
|
|
291
|
+
### GitHub Enterprise
|
|
292
|
+
|
|
293
|
+
Docs whose repository lives on a GitHub Enterprise instance set `host`, and every repo-derived link — the header mark, edit links, the agent manifest — points at that instance instead of the public site:
|
|
294
|
+
|
|
295
|
+
```ts blume.config.ts lineNumbers
|
|
296
|
+
github: {
|
|
297
|
+
host: "https://github.acme.com",
|
|
298
|
+
owner: "acme",
|
|
299
|
+
repo: "docs",
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The REST API base that [`<GithubInfo>`](/docs/content/components) queries is derived from `host`: an Enterprise Cloud tenant with data residency (`acme.ghe.com`) is served from its `api.` subdomain, and any other host is treated as Enterprise Server (`/api/v3`). Set `api` explicitly when your instance sits somewhere else.
|
|
304
|
+
|
|
305
|
+
:::warning
|
|
306
|
+
An instance reachable only over plain HTTP still renders its counts, but `GITHUB_TOKEN` is withheld from the request rather than sent in cleartext — so a private repo's card comes back without them.
|
|
307
|
+
:::
|
|
308
|
+
|
|
309
|
+
:::note
|
|
310
|
+
`host` covers links Blume derives from `github`. To point only the header mark somewhere else — an organization, say, when the docs repo itself is private — use [`navigation.repo`](/docs/content/navigation#repository-link) with an absolute URL.
|
|
311
|
+
:::
|
|
288
312
|
|
|
289
313
|
## Last modified
|
|
290
314
|
|
|
@@ -40,7 +40,19 @@ Write `href` as if the site were mounted at the root — a `basePath` is applied
|
|
|
40
40
|
|
|
41
41
|
## What's indexed
|
|
42
42
|
|
|
43
|
-
For
|
|
43
|
+
For Orama, FlexSearch, Algolia, Orama Cloud, and Typesense — and the MCP server's `search_docs` tool — Blume indexes each page's title, description, and body reduced to plain text: code blocks, images, and markup are stripped, so results stay relevant. These indexes are built from your source files, so they're identical in dev and production. Pagefind indexes the built HTML instead, and Mixedbread syncs your raw Markdown, so both always search code.
|
|
44
|
+
|
|
45
|
+
If your docs rely on code examples for searchable terms like options, methods, or error names, opt fenced code into the source-built indexes:
|
|
46
|
+
|
|
47
|
+
```ts blume.config.ts lineNumbers
|
|
48
|
+
search: {
|
|
49
|
+
indexing: {
|
|
50
|
+
includeCodeBlocks: true,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each fence's body and title (`blume.config.ts` above) become searchable; the language and fence markers don't. On `.mdx` pages the index reads components as the text they show — a Card's title, a Tab's label, a TypeTable's descriptions — using the same serializers as the [agent surfaces](/docs/configuration/ai), so an `ai.markdownComponents` entry covers your own components too. The option has no effect on Pagefind or Mixedbread. Expect the index to grow with your fenced content — the client index ships to every reader, hosted providers cap record size (Algolia rejects the sync batch when one page's record exceeds its plan's limit, leaving the previous index live), and a hit inside a fence shows flattened code in the result excerpt.
|
|
44
56
|
|
|
45
57
|
On a [versioned](/docs/content/versioning) site, results default to the version being viewed, with an "All versions" toggle in the dialog footer (remembered per reader). Cross-version hits name their version on the row. Orama, FlexSearch, Algolia, and Typesense honor the scoping — hosted records carry a `version` facet, with the current docs uploaded as `"current"` — while Pagefind stays unscoped, matching its locale behavior.
|
|
46
58
|
|
|
@@ -125,7 +125,7 @@ seo: {
|
|
|
125
125
|
}
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
By default, each card is derived from your content and theme — the **page title** as the headline, your **site title** as the eyebrow, and your theme **accent** for the mark. Images are served at `/og/<slug>.png`, mirroring each route, and are prerendered as static files even in server mode:
|
|
128
|
+
By default, each card is derived from your content and theme — the **page title** as the headline, the **page description** as the subtitle (the same text as its `og:description`, so `seo.description` wins over `description`), your **site title** as the eyebrow, and your theme **accent** for the mark. Images are served at `/og/<slug>.png`, mirroring each route, and are prerendered as static files even in server mode:
|
|
129
129
|
|
|
130
130
|
| Page route | Image URL |
|
|
131
131
|
| ------------------- | -------------------------- |
|
|
@@ -151,13 +151,13 @@ Emoji in a page title or site title render as [Twemoji](https://github.com/jdeck
|
|
|
151
151
|
|
|
152
152
|
### Show, hide, or override card layers
|
|
153
153
|
|
|
154
|
-
Beyond the headline, the card carries three optional layers: the **brand mark** in the top-left (your logo, or an accent tile with the site title's initial), the **subtitle** under the headline (your site `description`), and a **footer** with your repo slug (from `github`) and the site's URL — the deployment site's host plus [`deployment.base`](/docs/deployment#subpath-deploys), so a GitHub Pages project site reads `user.github.io/repo`. Override any of them with a string of your own, or hide one with `false`:
|
|
154
|
+
Beyond the headline, the card carries three optional layers: the **brand mark** in the top-left (your logo, or an accent tile with the site title's initial), the **subtitle** under the headline (the page `description`, or your site `description` for pages without one), and a **footer** with your repo slug (from `github`) and the site's URL — the deployment site's host plus [`deployment.base`](/docs/deployment#subpath-deploys), so a GitHub Pages project site reads `user.github.io/repo`. Override any of them with a string of your own, or hide one with `false`:
|
|
155
155
|
|
|
156
156
|
```ts blume.config.ts lineNumbers
|
|
157
157
|
seo: {
|
|
158
158
|
og: {
|
|
159
159
|
site: "docs.acme.com", // footer URL text, or false to hide it
|
|
160
|
-
description: false, // hide the subtitle; a string
|
|
160
|
+
description: false, // hide the subtitle on every card; a string replaces the site fallback
|
|
161
161
|
logo: false, // no brand mark at all — not even the initial tile
|
|
162
162
|
},
|
|
163
163
|
}
|
|
@@ -247,6 +247,33 @@ Each page includes:
|
|
|
247
247
|
|
|
248
248
|
URLs are absolute when `deployment.site` is set. Pages marked `seo.noindex` are skipped.
|
|
249
249
|
|
|
250
|
+
### Site identity
|
|
251
|
+
|
|
252
|
+
The WebSite node says a site exists; it doesn't say _what_ it is or _who_ runs it — which is what AI agents read JSON-LD for before they recommend or cite you. Two optional blocks fill that in, both needing `deployment.site` (the nodes carry absolute identifiers):
|
|
253
|
+
|
|
254
|
+
```ts blume.config.ts lineNumbers
|
|
255
|
+
seo: {
|
|
256
|
+
organization: {
|
|
257
|
+
name: "Acme", // defaults to the site title
|
|
258
|
+
email: "hello@acme.com",
|
|
259
|
+
telephone: "+1 555 0100",
|
|
260
|
+
address: { addressLocality: "Sydney", addressCountry: "AU" },
|
|
261
|
+
logo: "/logo.svg",
|
|
262
|
+
sameAs: ["https://github.com/acme", "https://x.com/acme"],
|
|
263
|
+
},
|
|
264
|
+
software: {
|
|
265
|
+
license: "MIT",
|
|
266
|
+
operatingSystem: "Node.js 22+",
|
|
267
|
+
price: 0, // emitted as an Offer; 0 marks it free
|
|
268
|
+
sameAs: ["https://www.npmjs.com/package/acme"],
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`organization` adds an **Organization** node to every page — the WebSite and article nodes cite it as `publisher` — with the email and telephone as a `ContactPoint` (`contactType` defaults to `"customer support"`) and the address as a `PostalAddress`, the two fields business-verification checks look for. `name` and `url` default to the site's; a root-relative `logo` is absolutized like any page URL.
|
|
274
|
+
|
|
275
|
+
`software` adds a **SoftwareApplication** node to the homepage: the product's name and description (defaulting to the site's), `applicationCategory` (default `"DeveloperApplication"`), operating system, license, an `Offer` when `price` is set, and the registry or repository URLs in `sameAs`. Pass `software: true` to take every default. The organization, when configured, is cited as its `publisher`.
|
|
276
|
+
|
|
250
277
|
## Sitemap
|
|
251
278
|
|
|
252
279
|
Blume writes a `sitemap.xml` of every indexable page at build time. It needs an absolute [`deployment.site`](/docs/deployment) and lists every page except drafts, hidden, and `noindex` pages. On a [versioned](/docs/content/versioning) site, archived pages whose canonical points at their live equivalent are left out too — the live page is the one to index. On by default:
|
|
@@ -105,8 +105,25 @@ theme: {
|
|
|
105
105
|
- **`name`** — the family name exactly as the provider lists it.
|
|
106
106
|
- **`provider`** — where the family comes from: `google` (default), `fontsource`, `bunny`, or `fontshare`.
|
|
107
107
|
- **`weights`** — the weights to load, as numbers or a variable range like `"100..900"`. Defaults to `[400, 500, 600, 700]`.
|
|
108
|
+
- **`subsets`** — the character subsets to load, by the provider's names (`latin`, `latin-ext`, `vietnamese`, `cyrillic`, `greek`, …). Defaults to `latin` plus whatever your configured locales need — see below.
|
|
108
109
|
- **`fallback`** — the system stack shown while the font loads and for missing glyphs: `sans`, `serif`, or `mono`. Defaults to `mono` for the mono role and `sans` otherwise.
|
|
109
110
|
|
|
111
|
+
#### Subsets and locales
|
|
112
|
+
|
|
113
|
+
Google, Bunny, and Fontsource split each family into per-script subsets, and only the subsets you load get a `@font-face`. Blume derives the list from your [`i18n.locales`](/docs/content/i18n): a site with Vietnamese, Polish, Russian, or Greek locales loads `vietnamese`, `latin-ext`, `cyrillic`, or `greek` alongside `latin`, so diacritics and non-Latin letters render in your chosen font instead of the system fallback. Sites without an `i18n` block, or with only Latin-1 languages, load `latin` alone. Browsers download a subset only when a page uses its characters, and preloads follow the same list.
|
|
114
|
+
|
|
115
|
+
Set `subsets` on a family to override the derived list — say, for a single-locale site whose content still needs a script its locale doesn't imply:
|
|
116
|
+
|
|
117
|
+
```ts blume.config.ts lineNumbers
|
|
118
|
+
theme: {
|
|
119
|
+
fonts: {
|
|
120
|
+
body: { name: "Be Vietnam Pro", subsets: ["latin", "vietnamese"] },
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Curated slugs like `inter` follow the locale-derived list; use the object form to pin subsets for those families too.
|
|
126
|
+
|
|
110
127
|
#### Local font files
|
|
111
128
|
|
|
112
129
|
For a font you own (or one no provider serves), point a role at font files in your project. Each variant becomes one `@font-face`:
|
|
@@ -189,6 +206,12 @@ Drop a `theme.css` in your project root to override any design token. It's the l
|
|
|
189
206
|
|
|
190
207
|
Set a token under `:root` for light mode and under `:root[data-theme="dark"]` for dark mode. Color tokens have distinct built-in dark values declared at the dark selector's higher specificity, so a `:root`-only override of `--blume-accent`, `--blume-background`, and friends applies to light mode only — declare the dark block too when both modes should change.
|
|
191
208
|
|
|
209
|
+
`theme.css` is inlined into the site's Tailwind entry, so Tailwind directives work in it too. The one to know for a monorepo is `@source`: Blume scans your project for utility classes, and a page that imports components from a sibling workspace package needs that package scanned as well. Point at it relative to `theme.css` — the standard Tailwind rule — and Blume carries the path into the generated sheet:
|
|
210
|
+
|
|
211
|
+
```css theme.css lineNumbers
|
|
212
|
+
@source "../../packages/ui/src";
|
|
213
|
+
```
|
|
214
|
+
|
|
192
215
|
### Design tokens
|
|
193
216
|
|
|
194
217
|
| Token | Controls |
|
|
@@ -71,6 +71,8 @@ Switch between equivalent content in place — language variants, OS-specific co
|
|
|
71
71
|
|
|
72
72
|
Add `inline` to render borderless — a tab strip on a full-width rule with the content flowing beneath as prose — instead of the bordered box. Add `param` to sync the active tab to a URL query param instead of the hash, which makes the selection shareable: a link ending in `?install=windows` opens on the Windows tab. Each group syncs to its own `param`, so you can use several independent, deep-linkable groups on one page.
|
|
73
73
|
|
|
74
|
+
Groups with same-titled tabs switch together — pick "macOS" in one and every group with a macOS tab follows. Add `syncKey` to scope that syncing: only groups sharing the same key switch together, so unrelated groups that happen to share a tab title stay independent.
|
|
75
|
+
|
|
74
76
|
<Tabs inline param="install">
|
|
75
77
|
<Tab title="macOS">Use Homebrew to install the toolchain.</Tab>
|
|
76
78
|
<Tab title="Windows">Use winget to install the toolchain.</Tab>
|
|
@@ -546,6 +548,8 @@ export interface ButtonProps {
|
|
|
546
548
|
|
|
547
549
|
A card linking to a GitHub repository with its live star and fork counts. Counts are fetched at build time — no client JavaScript — and the card still renders if the API is unreachable. Pass `owner` and `repo`, or omit them to use the repository from your `blume.config`. Set a `GITHUB_TOKEN` environment variable to lift the API rate limit.
|
|
548
550
|
|
|
551
|
+
The card reads the instance from [`github.host`](/docs/configuration#github-enterprise), so on an Enterprise-hosted site explicit `owner`/`repo` address that instance too. Pass `host` to point one card somewhere else — a public project from an Enterprise site, say; the REST base is derived from it the same way it is from `github.host`.
|
|
552
|
+
|
|
549
553
|
<GithubInfo owner="haydenbleasel" repo="blume" />
|
|
550
554
|
|
|
551
555
|
```astro lineNumbers
|
|
@@ -554,13 +558,16 @@ A card linking to a GitHub repository with its live star and fork counts. Counts
|
|
|
554
558
|
|
|
555
559
|
<!-- Or point it at any repository -->
|
|
556
560
|
<GithubInfo owner="haydenbleasel" repo="blume" />
|
|
561
|
+
|
|
562
|
+
<!-- Or at a repository on another instance -->
|
|
563
|
+
<GithubInfo host="https://github.com" owner="haydenbleasel" repo="blume" />
|
|
557
564
|
```
|
|
558
565
|
|
|
559
566
|
## Component
|
|
560
567
|
|
|
561
568
|
`Component` renders an example file from your project's `examples/` directory as a live preview alongside its highlighted source, in tabs. Point it at a file with `path` — its location under `examples/`, without the extension (so `examples/counter.tsx` is `path="counter"`). React, Vue, Svelte, and Astro examples are all supported; framework examples hydrate, Astro ones render statically. It keeps the preview and the code in sync from a single file.
|
|
562
569
|
|
|
563
|
-
The preview renders in an isolated frame that the docs styles never reach — no prose margins, typography, or theme chrome bleed into your component. The frame gets Tailwind (preflight + utilities scanned from your
|
|
570
|
+
The preview renders in an isolated frame that the docs styles never reach — no prose margins, typography, or theme chrome bleed into your component. The frame gets Tailwind (preflight + utilities scanned from your project and your examples directory), Blume's design tokens so classes like `bg-background` follow the site palette by default, and it follows the site's light/dark toggle live. The pane sizes itself to the rendered example — and keeps tracking it if the example grows or shrinks after load — with the Preview and Code tabs sharing one height so toggling them never shifts the page.
|
|
564
571
|
|
|
565
572
|
To style previews with your own design system — say, shadcn variables — point `examples.css` at a stylesheet. It's injected into every preview frame after Blume's defaults, so your tokens win. Don't `@import "tailwindcss"` in it; the frame already provides Tailwind. Both `.dark` and `[data-theme="dark"]` work for dark-mode overrides:
|
|
566
573
|
|
|
@@ -616,6 +623,13 @@ export default defineConfig({
|
|
|
616
623
|
<Component path="file-list/examples/basic" />
|
|
617
624
|
```
|
|
618
625
|
|
|
626
|
+
In a monorepo, the components your examples import usually live in a sibling workspace package. Tailwind's `@source` scans files, not imports: Blume scans your project and the `examples` directory (wherever `source` points, even outside the project), so a class used only inside that sibling package isn't generated until you add the package to the scan. Do that with an `@source` directive in `examples.css` (for the preview frames) or `theme.css` (for the site), written relative to the file it sits in — the standard Tailwind rule — and Blume carries it into the generated sheet:
|
|
627
|
+
|
|
628
|
+
```css
|
|
629
|
+
/* examples/theme.css */
|
|
630
|
+
@source "../../../packages/ui/src";
|
|
631
|
+
```
|
|
632
|
+
|
|
619
633
|
<Component path="counter" />
|
|
620
634
|
|
|
621
635
|
```astro
|