blume 0.7.0 → 1.0.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 +666 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/cli/index.js +1852 -380
- package/dist/cli/index.js.map +98 -91
- package/dist/types/ai/component-markdown.d.ts +34 -0
- package/dist/types/components/content/youtube.d.ts +18 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +47 -2
- package/dist/types/core/config.d.ts +3 -2
- package/dist/types/core/data.d.ts +7 -0
- package/dist/types/core/i18n-ui.d.ts +526 -132
- package/dist/types/core/schema.d.ts +293 -146
- package/dist/types/index.d.ts +1 -0
- package/dist/types/openapi/references.d.ts +60 -0
- package/dist/types/seo/x-handle.d.ts +12 -0
- package/docs/01-quickstart.mdx +5 -2
- package/docs/02-deployment.mdx +8 -8
- package/docs/03-faq.mdx +46 -16
- package/docs/advanced/api-reference.mdx +1 -1
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/configuration/ai.mdx +49 -10
- package/docs/configuration/customization.mdx +11 -0
- package/docs/configuration/export.mdx +1 -1
- package/docs/configuration/index.mdx +27 -3
- package/docs/configuration/seo.mdx +35 -5
- package/docs/content/components.mdx +2 -2
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/navigation.mdx +3 -3
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +6 -4
- package/docs/index.mdx +2 -2
- package/docs/reference/cli.mdx +9 -7
- package/docs/reference/frontmatter.mdx +1 -1
- package/package.json +22 -4
- package/skills/blume/SKILL.md +5 -3
- package/skills/blume-update-docs/SKILL.md +3 -2
- package/src/ai/agent-readability.ts +9 -8
- package/src/ai/ask-context.ts +7 -2
- package/src/ai/ask-data.ts +3 -0
- package/src/ai/component-markdown.ts +461 -0
- package/src/ai/llms.ts +135 -26
- package/src/ai/markdown.ts +35 -6
- package/src/ai/mcp/data.ts +25 -4
- package/src/ai/mcp/discovery.ts +10 -3
- package/src/ai/mcp/server.ts +21 -7
- package/src/ai/mcp/tools.ts +1 -1
- package/src/ai/visibility.ts +74 -0
- package/src/astro/component-slots.ts +11 -1
- package/src/astro/generate.ts +77 -45
- package/src/astro/integration.ts +1 -1
- package/src/astro/markdown-negotiation.ts +1 -1
- package/src/astro/pages.ts +81 -19
- package/src/astro/templates.ts +150 -19
- package/src/blume-modules.d.ts +8 -0
- package/src/cli/commands/build.ts +120 -23
- package/src/cli/commands/check.ts +1 -1
- package/src/cli/commands/dev.ts +26 -5
- package/src/cli/commands/eject.ts +47 -19
- package/src/cli/commands/init.ts +120 -180
- package/src/cli/commands/preview.ts +4 -1
- package/src/cli/commands/validate.ts +43 -2
- package/src/cli/dev-lock.ts +8 -4
- package/src/cli/eject-scripts.ts +72 -0
- package/src/cli/env.ts +15 -5
- package/src/cli/init/questions.ts +158 -0
- package/src/cli/init/scaffold.ts +380 -0
- package/src/cli/internal-error.ts +9 -4
- package/src/cli/prepare.ts +3 -2
- package/src/components/Icon.astro +2 -1
- package/src/components/content/AccordionItem.astro +23 -4
- package/src/components/content/Badge.astro +3 -1
- package/src/components/content/Card.astro +4 -2
- package/src/components/content/Step.astro +10 -1
- package/src/components/content/Tabs.astro +15 -3
- package/src/components/content/Tile.astro +2 -1
- package/src/components/content/Tooltip.astro +3 -1
- package/src/components/content/Update.astro +9 -2
- package/src/components/content/auto-type-table.ts +7 -1
- package/src/components/content/base-href.ts +33 -0
- package/src/components/content/changelog-element.ts +9 -2
- package/src/components/content/mermaid-element.ts +7 -2
- package/src/components/islands/AskAI.astro +5 -2
- package/src/components/islands/ask-ai.tsx +86 -11
- package/src/components/islands/hooks.ts +28 -8
- package/src/components/layout/Banner.astro +10 -2
- package/src/components/layout/Breadcrumbs.astro +11 -2
- package/src/components/layout/Header.astro +13 -4
- package/src/components/layout/Logo.astro +11 -3
- package/src/components/layout/NavTree.astro +19 -5
- package/src/components/layout/PageActions.astro +25 -10
- package/src/components/layout/PageLayout.astro +85 -9
- package/src/components/layout/Pagination.astro +10 -4
- package/src/components/layout/ReferenceLayout.astro +20 -2
- package/src/components/layout/RootLayout.astro +142 -12
- package/src/components/layout/Search.astro +117 -27
- package/src/components/layout/search/algolia.ts +11 -2
- package/src/components/layout/search/endpoint.ts +11 -5
- package/src/components/layout/search/orama-cloud.ts +8 -2
- package/src/components/layout/search/types.ts +5 -1
- package/src/components/layout/search/typesense.ts +4 -1
- package/src/components/layout/toc-element.ts +1 -1
- package/src/components/openapi/ApiTagOperations.astro +2 -1
- package/src/components/openapi/Operation.astro +47 -40
- package/src/components/openapi/RequestPanel.astro +1 -1
- package/src/components/openapi/helpers.ts +71 -3
- package/src/components/openapi/panel.ts +1 -1
- package/src/core/base-path.ts +24 -0
- package/src/core/builtin-tags.ts +2 -0
- package/src/core/config-input.ts +48 -2
- package/src/core/config.ts +3 -2
- package/src/core/data.ts +4 -0
- package/src/core/frontmatter.ts +7 -0
- package/src/core/graph.ts +15 -5
- package/src/core/i18n-ui.ts +54 -0
- package/src/core/i18n.ts +16 -8
- package/src/core/last-modified.ts +13 -6
- package/src/core/links.ts +32 -8
- package/src/core/navigation.ts +29 -4
- package/src/core/package-json.ts +17 -2
- package/src/core/project-graph.ts +15 -6
- package/src/core/schema.ts +71 -2
- package/src/core/sources/assets.ts +6 -1
- package/src/core/sources/filesystem.ts +4 -0
- package/src/core/sources/mdx-remote.ts +23 -14
- package/src/core/sources/normalize.ts +152 -50
- package/src/core/sources/notion.ts +8 -8
- package/src/core/ui-packs/ar.ts +8 -0
- package/src/core/ui-packs/bg.ts +8 -0
- package/src/core/ui-packs/bn.ts +8 -0
- package/src/core/ui-packs/ca.ts +8 -0
- package/src/core/ui-packs/cs.ts +8 -0
- package/src/core/ui-packs/da.ts +8 -0
- package/src/core/ui-packs/de.ts +8 -0
- package/src/core/ui-packs/el.ts +8 -0
- package/src/core/ui-packs/es.ts +8 -0
- package/src/core/ui-packs/fa.ts +8 -0
- package/src/core/ui-packs/fi.ts +8 -0
- package/src/core/ui-packs/fr.ts +9 -1
- package/src/core/ui-packs/he.ts +8 -0
- package/src/core/ui-packs/hi.ts +8 -0
- package/src/core/ui-packs/hr.ts +8 -0
- package/src/core/ui-packs/hu.ts +8 -0
- package/src/core/ui-packs/id.ts +8 -0
- package/src/core/ui-packs/it.ts +8 -0
- package/src/core/ui-packs/ja.ts +8 -0
- package/src/core/ui-packs/ko.ts +8 -0
- package/src/core/ui-packs/nl.ts +8 -0
- package/src/core/ui-packs/no.ts +8 -0
- package/src/core/ui-packs/pl.ts +8 -0
- package/src/core/ui-packs/pt-br.ts +8 -0
- package/src/core/ui-packs/pt.ts +8 -0
- package/src/core/ui-packs/ro.ts +8 -0
- package/src/core/ui-packs/ru.ts +8 -0
- package/src/core/ui-packs/sk.ts +8 -0
- package/src/core/ui-packs/sr.ts +8 -0
- package/src/core/ui-packs/sv.ts +8 -0
- package/src/core/ui-packs/th.ts +8 -0
- package/src/core/ui-packs/tr.ts +8 -0
- package/src/core/ui-packs/uk.ts +8 -0
- package/src/core/ui-packs/vi.ts +8 -0
- package/src/core/ui-packs/zh-tw.ts +8 -0
- package/src/core/ui-packs/zh.ts +8 -0
- package/src/deploy/adapter-output.ts +18 -8
- package/src/deploy/redirects.ts +7 -2
- package/src/deploy/sitemap.ts +53 -11
- package/src/index.ts +5 -0
- package/src/markdown/base-links.ts +10 -8
- package/src/markdown/index.ts +15 -3
- package/src/markdown/inline-code.ts +7 -2
- package/src/markdown/package-commands.ts +10 -4
- package/src/og/card.ts +4 -2
- package/src/og/dimensions.ts +12 -0
- package/src/openapi/model.ts +12 -4
- package/src/openapi/parse.ts +21 -0
- package/src/openapi/references.ts +38 -8
- package/src/openapi/render-mdx.ts +62 -1
- package/src/openapi/source.ts +59 -10
- package/src/registry/eject.ts +184 -12
- package/src/registry/registry.ts +0 -3
- package/src/search/documents.ts +34 -2
- package/src/seo/jsonld.ts +20 -13
- package/src/seo/x-handle.ts +18 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type { ComponentMarkdown, ComponentMarkdownChild, ComponentMarkdownContext, } from "./ai/component-markdown.ts";
|
|
1
2
|
export { defineConfig } from "./core/config.ts";
|
|
2
3
|
export type { BlumeBanner, BlumeData, BlumeDataConfig, BlumeDataI18n, BlumeDataLocale, BlumeFavicon, BlumeFeed, BlumeLogo, BlumeRoute, } from "./core/data.ts";
|
|
3
4
|
export { defineComponents } from "./core/define-components.ts";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "../core/schema.ts";
|
|
2
|
+
import type { NavTab } from "../core/types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Pure resolution of the configured API reference blocks into concrete routes,
|
|
5
|
+
* labels, and a renderer choice — no file IO, so the content source, the nav
|
|
6
|
+
* tabs, the Scalar page generator, and the `blume:openapi` data module all share
|
|
7
|
+
* one source of truth. Kept free of any Astro/template imports so `core` can
|
|
8
|
+
* depend on it without a cycle.
|
|
9
|
+
*/
|
|
10
|
+
export type ReferenceKind = "openapi" | "asyncapi";
|
|
11
|
+
/** Who renders a reference: Blume's own UI, or the embedded Scalar SPA. */
|
|
12
|
+
export type ReferenceRenderer = "blume" | "scalar";
|
|
13
|
+
/** Per-block display options for the Blume renderer. */
|
|
14
|
+
export interface ReferenceDisplay {
|
|
15
|
+
/** Code-sample languages shown per operation. */
|
|
16
|
+
codeSamples: string[];
|
|
17
|
+
/** Whether nested schema rows start expanded. */
|
|
18
|
+
expandSchemas: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** A spec source resolved to a concrete route, label, and renderer. */
|
|
21
|
+
export interface ReferenceSource {
|
|
22
|
+
kind: ReferenceKind;
|
|
23
|
+
renderer: ReferenceRenderer;
|
|
24
|
+
/** Unique token derived from the route; the `<Operation source>` / data key. */
|
|
25
|
+
slug: string;
|
|
26
|
+
/** Normalized route the reference mounts at, e.g. `/reference`. */
|
|
27
|
+
route: string;
|
|
28
|
+
/**
|
|
29
|
+
* Site-wide `basePath` the rendered pages are mounted under (`""` when
|
|
30
|
+
* none). Kept separate from `route` — the content pipeline applies it to
|
|
31
|
+
* staged entries itself — so consumers prefix only the URLs they emit.
|
|
32
|
+
*/
|
|
33
|
+
basePath: string;
|
|
34
|
+
label: string;
|
|
35
|
+
/** Local path or `http(s)` URL, verbatim from config. */
|
|
36
|
+
spec: string;
|
|
37
|
+
/** Per-block Scalar theme name override, if any (Scalar renderer only). */
|
|
38
|
+
theme?: string;
|
|
39
|
+
/** Display options carried through to the Blume renderer. */
|
|
40
|
+
display: ReferenceDisplay;
|
|
41
|
+
/**
|
|
42
|
+
* Warnings recorded while deduping — another source's route collided with
|
|
43
|
+
* this one and was dropped. Surfaced as diagnostics when the source loads.
|
|
44
|
+
*/
|
|
45
|
+
collisions?: string[];
|
|
46
|
+
}
|
|
47
|
+
export declare const slugify: (text: string) => string;
|
|
48
|
+
/** Normalize a configured route to a single leading slash, no trailing slash. */
|
|
49
|
+
export declare const normalizeRoute: (route: string) => string;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve every enabled reference. OpenAPI honors its `renderer` (Blume's own UI
|
|
52
|
+
* by default); AsyncAPI is always rendered by Scalar for now.
|
|
53
|
+
*/
|
|
54
|
+
export declare const resolveReferences: (config: ResolvedConfig) => ReferenceSource[];
|
|
55
|
+
/** Nav tabs (header links) for every reference, regardless of renderer. */
|
|
56
|
+
export declare const referenceTabs: (config: ResolvedConfig) => NavTab[];
|
|
57
|
+
/** Blume-rendered OpenAPI references, deduped by route (first wins). */
|
|
58
|
+
export declare const blumeReferences: (config: ResolvedConfig) => ReferenceSource[];
|
|
59
|
+
/** Whether any reference is Scalar-rendered (gates the `@scalar/astro` dep + pages). */
|
|
60
|
+
export declare const hasScalarReferences: (config: ResolvedConfig) => boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize an X account to the leading `@` that `twitter:site`/`twitter:creator`
|
|
3
|
+
* require, so `acme`, `@acme`, and ` @acme ` all land on `@acme`. Empty or
|
|
4
|
+
* blank input yields undefined, which renders no tag at all.
|
|
5
|
+
*
|
|
6
|
+
* The layouts call this on values that never passed through the config schema:
|
|
7
|
+
* Astro's collections carry no schema here, so a page's `seo.x.creator` reaches
|
|
8
|
+
* them as raw frontmatter, and the schema's own transform never runs on it.
|
|
9
|
+
* (Blume's page pipeline does reject a non-string `creator` before the page is
|
|
10
|
+
* built, so `unknown` is defense in depth rather than the expected path.)
|
|
11
|
+
*/
|
|
12
|
+
export declare const normalizeXHandle: (value: unknown) => string | undefined;
|
package/docs/01-quickstart.mdx
CHANGED
|
@@ -6,7 +6,7 @@ sidebar:
|
|
|
6
6
|
order: 1
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
Go from an empty folder to a running docs site in a few commands. Blume needs **Node.js 22 or newer** and a `docs/` folder with at least one `.md` or `.mdx` file — there's nothing else to set up.
|
|
9
|
+
Go from an empty folder to a running docs site in a few commands. Blume needs **Node.js 22.12 or newer** and a `docs/` folder with at least one `.md` or `.mdx` file — there's nothing else to set up.
|
|
10
10
|
|
|
11
11
|
## Install and run
|
|
12
12
|
|
|
@@ -20,7 +20,10 @@ Go from an empty folder to a running docs site in a few commands. Blume needs **
|
|
|
20
20
|
|
|
21
21
|
</Step>
|
|
22
22
|
<Step title="Scaffold a project">
|
|
23
|
-
`blume init`
|
|
23
|
+
`blume init` asks a few questions — where to create the project, what your
|
|
24
|
+
docs site is called, which template, and where your content lives — then
|
|
25
|
+
scaffolds the minimum surface: a content folder and a config file. Pass
|
|
26
|
+
`--yes` to skip the prompts and take the defaults.
|
|
24
27
|
|
|
25
28
|
```bash
|
|
26
29
|
blume init
|
package/docs/02-deployment.mdx
CHANGED
|
@@ -10,11 +10,11 @@ sidebar:
|
|
|
10
10
|
|
|
11
11
|
`blume build` compiles your docs to plain HTML, CSS, and a local search index in `dist/`. There's no server to run — point any static host at the folder.
|
|
12
12
|
|
|
13
|
-
| Setting | Value
|
|
14
|
-
| ---------------- |
|
|
15
|
-
| Build command | `blume build`
|
|
16
|
-
| Output directory | `dist`
|
|
17
|
-
| Node version | 22 or newer
|
|
13
|
+
| Setting | Value |
|
|
14
|
+
| ---------------- | -------------- |
|
|
15
|
+
| Build command | `blume build` |
|
|
16
|
+
| Output directory | `dist` |
|
|
17
|
+
| Node version | 22.12 or newer |
|
|
18
18
|
|
|
19
19
|
These settings work on Vercel, Netlify, Cloudflare Pages, GitHub Pages, Amazon S3 + CloudFront, or any bucket or CDN. Make sure `blume` is a dependency so the host can run the build.
|
|
20
20
|
|
|
@@ -88,7 +88,7 @@ deployment: {
|
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
The `vercel` and `node` adapters ship with Blume — choosing one just works. The `netlify` and `cloudflare` adapters must be installed in your project (e.g. `bun add -d @astrojs/netlify`); the CLI warns you if the package is missing:
|
|
92
92
|
|
|
93
93
|
| Adapter | Package | Use for |
|
|
94
94
|
| ------------ | --------------------- | ------------------------------------ |
|
|
@@ -97,7 +97,7 @@ Choosing an adapter pulls in the matching Astro adapter automatically:
|
|
|
97
97
|
| `node` | `@astrojs/node` | Self-hosted Node servers, containers |
|
|
98
98
|
| `cloudflare` | `@astrojs/cloudflare` | Cloudflare Workers and Pages |
|
|
99
99
|
|
|
100
|
-
On **Vercel**, **Netlify**, and **Cloudflare Pages**, Blume picks the matching adapter automatically for server output — set `output: "server"` and deploy. Set `adapter` explicitly to override the detected value, or when self-hosting with `node`.
|
|
100
|
+
On **Vercel**, **Netlify**, and **Cloudflare Pages**, Blume picks the matching adapter automatically for server output — set `output: "server"` and deploy (on Netlify and Cloudflare, install the adapter package too). Set `adapter` explicitly to override the detected value, or when self-hosting with `node`.
|
|
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
|
|
|
@@ -126,7 +126,7 @@ When a feature needs a runtime secret, Blume warns at `blume dev`/`build` if it'
|
|
|
126
126
|
| Feature | Variable |
|
|
127
127
|
| --- | --- |
|
|
128
128
|
| Ask AI (AI Gateway) | `AI_GATEWAY_API_KEY` (or Vercel OIDC) |
|
|
129
|
-
| Ask AI (
|
|
129
|
+
| Ask AI (other providers) | the provider's default key env var (`OPENROUTER_API_KEY`, `LLMGATEWAY_API_KEY`, `INKEEP_API_KEY`), or your configured `apiKeyEnv` |
|
|
130
130
|
| Mixedbread search | `MIXEDBREAD_API_KEY` |
|
|
131
131
|
|
|
132
132
|
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.
|
package/docs/03-faq.mdx
CHANGED
|
@@ -79,26 +79,26 @@ Once the opening `:::note` fence is joined to the prose, it's no longer a direct
|
|
|
79
79
|
|
|
80
80
|
### Why it happens
|
|
81
81
|
|
|
82
|
-
This is a bug in oxfmt's Markdown formatter (inherited from Prettier's Markdown printer — see [prettier/prettier#19040](https://github.com/prettier/prettier/pull/19040)). When it wraps prose, it treats the `:::` fence lines as ordinary text and joins them with the adjacent line, breaking the directive. It affects every container directive — `:::note`, `:::tip`, `:::warning`, `:::danger`, `:::success`.
|
|
82
|
+
This is a bug in oxfmt's Markdown formatter (inherited from Prettier's Markdown printer — see [prettier/prettier#19040](https://github.com/prettier/prettier/pull/19040)). When it wraps prose, it treats the `:::` fence lines as ordinary text and joins them with the adjacent line, breaking the directive. It affects every container directive type — `:::note`, `:::tip`, `:::info`, `:::warning`, `:::danger`, `:::success`.
|
|
83
83
|
|
|
84
84
|
We reported it upstream in [oxc-project/oxc#24096](https://github.com/oxc-project/oxc/issues/24096); until it's fixed there, the patch below is the workaround.
|
|
85
85
|
|
|
86
86
|
### The fix
|
|
87
87
|
|
|
88
|
-
Patch oxfmt so it preserves the line break that sits directly against a `:::` fence. Blume ships
|
|
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.
|
|
90
|
+
1. Save the patch as `patches/oxfmt@0.58.0.patch`:
|
|
91
91
|
|
|
92
|
-
```diff patches/oxfmt@0.
|
|
93
|
-
diff --git a/dist/markdown-
|
|
94
|
-
index
|
|
95
|
-
--- a/dist/markdown-
|
|
96
|
-
+++ b/dist/markdown-
|
|
97
|
-
@@ -
|
|
98
|
-
case "sentence": return
|
|
99
|
-
case "word": return
|
|
92
|
+
```diff patches/oxfmt@0.58.0.patch
|
|
93
|
+
diff --git a/dist/markdown-BjyDFyaO.js b/dist/markdown-BjyDFyaO.js
|
|
94
|
+
index 1905aa7563e612808426e26f1ed28bebbef3456f..42bb9f66829068676c5697fc9fc39279bf937f3e 100644
|
|
95
|
+
--- a/dist/markdown-BjyDFyaO.js
|
|
96
|
+
+++ b/dist/markdown-BjyDFyaO.js
|
|
97
|
+
@@ -4830,7 +4830,43 @@ function lu(e, t, r) {
|
|
98
|
+
case "sentence": return Mh(e, r);
|
|
99
|
+
case "word": return t.parser !== "mdx" ? Vh(e, t) : Gh(e);
|
|
100
100
|
case "whitespace": {
|
|
101
|
-
- let { next: i } = e, u = i && /^>|^(?:[*+-]|#{1,6}|\d+[).])
|
|
101
|
+
- let { next: i } = e, u = i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(i.value) && !ME(e) && !(t.proseWrap === "preserve" && zE(e)) ? "never" : t.proseWrap;
|
|
102
102
|
+ let { next: i, previous: oxfmtFencePrev } = e;
|
|
103
103
|
+ // Preserve line breaks that sit directly against a `:::` container
|
|
104
104
|
+ // directive fence, so `proseWrap: "never"` keeps the opening/closing
|
|
@@ -106,8 +106,37 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
106
106
|
+ // breaks the directive). Ordinary prose still wraps per proseWrap.
|
|
107
107
|
+ // See prettier/prettier#19040.
|
|
108
108
|
+ let oxfmtIsFence = (w) => w != null && typeof w.value === "string" && w.value.startsWith(":::");
|
|
109
|
-
+
|
|
110
|
-
|
|
109
|
+
+ // A titled directive (`:::warning[Heads up]`) parses its `[title]` as a
|
|
110
|
+
+ // linkReference between two sentence nodes at the paragraph level: the
|
|
111
|
+
+ // fence word ends the sentence before the reference, and the body's
|
|
112
|
+
+ // leading newline opens the sentence after it. So when this whitespace
|
|
113
|
+
+ // starts its sentence, climb to the paragraph and check whether the two
|
|
114
|
+
+ // preceding siblings are a (link) reference and a sentence ending in a
|
|
115
|
+
+ // `:::` fence word.
|
|
116
|
+
+ let oxfmtPrevIsTitledFence = !1;
|
|
117
|
+
+ if (oxfmtFencePrev == null && e.index === 0 && e.grandparent != null && Array.isArray(e.grandparent.children)) {
|
|
118
|
+
+ let oxfmtSibs = e.grandparent.children, oxfmtSentIdx = oxfmtSibs.indexOf(e.parent);
|
|
119
|
+
+ if (oxfmtSentIdx >= 2) {
|
|
120
|
+
+ let oxfmtLink = oxfmtSibs[oxfmtSentIdx - 1], oxfmtBefore = oxfmtSibs[oxfmtSentIdx - 2];
|
|
121
|
+
+ let oxfmtLastWord = oxfmtBefore && oxfmtBefore.type === "sentence" && Array.isArray(oxfmtBefore.children) ? oxfmtBefore.children[oxfmtBefore.children.length - 1] : null;
|
|
122
|
+
+ oxfmtPrevIsTitledFence = oxfmtLink != null && (oxfmtLink.type === "linkReference" || oxfmtLink.type === "link") && oxfmtIsFence(oxfmtLastWord);
|
|
123
|
+
+ }
|
|
124
|
+
+ }
|
|
125
|
+
+ // The plain-markdown parser keeps a titled fence's `[title]` as literal
|
|
126
|
+
+ // words, so the whole directive is one sentence. For a newline
|
|
127
|
+
+ // whitespace, walk back to the start of its visual line within the
|
|
128
|
+
+ // sentence; a line led by a `:::` word is a fence whose break must stay.
|
|
129
|
+
+ if (!oxfmtPrevIsTitledFence && e.node.value.includes("\n") && e.parent != null && Array.isArray(e.parent.children)) {
|
|
130
|
+
+ let oxfmtLineFirst = null;
|
|
131
|
+
+ for (let oxfmtJ = e.index - 1; oxfmtJ >= 0; oxfmtJ--) {
|
|
132
|
+
+ let oxfmtSib = e.parent.children[oxfmtJ];
|
|
133
|
+
+ if (oxfmtSib.type === "whitespace" && typeof oxfmtSib.value === "string" && oxfmtSib.value.includes("\n")) break;
|
|
134
|
+
+ oxfmtLineFirst = oxfmtSib;
|
|
135
|
+
+ }
|
|
136
|
+
+ oxfmtPrevIsTitledFence = oxfmtIsFence(oxfmtLineFirst);
|
|
137
|
+
+ }
|
|
138
|
+
+ let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtPrevIsTitledFence || oxfmtIsFence(i) ? "preserve" : i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/.test(i.value) && !ME(e) && !(t.proseWrap === "preserve" && zE(e)) ? "never" : t.proseWrap;
|
|
139
|
+
return ou(e, n.value, u, !1, t);
|
|
111
140
|
}
|
|
112
141
|
case "emphasis": {
|
|
113
142
|
```
|
|
@@ -117,7 +146,7 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
117
146
|
```json package.json
|
|
118
147
|
{
|
|
119
148
|
"patchedDependencies": {
|
|
120
|
-
"oxfmt@0.
|
|
149
|
+
"oxfmt@0.58.0": "patches/oxfmt@0.58.0.patch"
|
|
121
150
|
}
|
|
122
151
|
}
|
|
123
152
|
```
|
|
@@ -128,5 +157,6 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
|
|
|
128
157
|
bun install
|
|
129
158
|
```
|
|
130
159
|
|
|
131
|
-
:::warning[Version-pinned]
|
|
160
|
+
:::warning[Version-pinned]
|
|
161
|
+
The patch targets a specific oxfmt build — its diff references a file whose name is hashed per release (`dist/markdown-*.js`). When you bump oxfmt, regenerate the patch (e.g. `bun patch oxfmt`) or check whether the upstream fix has landed and the patch is no longer needed.
|
|
132
162
|
:::
|
|
@@ -3,7 +3,7 @@ title: OpenAPI / AsyncAPI
|
|
|
3
3
|
description: Drop in an OpenAPI spec and get a native API reference — one real page per operation, in your sidebar and search.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Point Blume at an OpenAPI spec and it generates a native API reference: one **real page per operation**, grouped by tag in a tab-scoped sidebar, with schema tables, request/response examples, and generated code samples. Because each operation 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.
|
|
6
|
+
Point Blume at an OpenAPI spec and it generates a native API reference: one **real page per operation**, grouped by tag in a tab-scoped sidebar, with schema tables, request/response examples, and generated code samples. Because each operation 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. These docs ship one: the [Example API](/api) reference is generated from the Petstore spec by the config below.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts lineNumbers
|
|
9
9
|
openapi: {
|
|
@@ -71,7 +71,7 @@ When your versions follow [semver](https://semver.org) and span more than one ma
|
|
|
71
71
|
|
|
72
72
|
## From GitHub Releases
|
|
73
73
|
|
|
74
|
-
Rather than authoring entries by hand, point the built-in [`github-releases` source](/docs/content/sources#github-releases) at a repo and every release becomes a `type: changelog` entry — the same timeline and feed, fed straight from the releases you already publish:
|
|
74
|
+
Rather than authoring entries by hand, point the built-in [`github-releases` source](/docs/content/sources#github-releases) at a repo and every release becomes a `type: changelog` entry — the same timeline and feed, fed straight from the releases you already publish. Blume's own [changelog](/changelog) is built this way:
|
|
75
75
|
|
|
76
76
|
```ts blume.config.ts
|
|
77
77
|
content: {
|
|
@@ -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, og, analytics, feedback, structuredData, codeWrap, and imageZoom.",
|
|
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.",
|
|
78
78
|
},
|
|
79
79
|
navigation: {
|
|
80
80
|
type: "Navigation",
|
package/docs/advanced/skills.mdx
CHANGED
|
@@ -18,7 +18,7 @@ npx skills add haydenbleasel/blume
|
|
|
18
18
|
`blume-update-docs` keeps your docs in sync with the product they document. On each run — typically from a schedule you configure in your agent runner — it audits recently merged PRs, changelogs, config schemas, and CLI help against the docs content, updates only pages that are factually stale (feature-flagged work is ignored), verifies with `blume build`, and opens or updates a `blume/*` pull request. If nothing drifted, it reports a clean no-op instead of opening a noisy PR.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx skills
|
|
21
|
+
npx skills add haydenbleasel/blume --skill blume-update-docs
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Blume doesn't host the automation — wire the skill into a scheduled task in Claude Code, a Codex or Cursor automation, or plain cron, with permission to read repo history and open PRs. A typical weekly prompt:
|
|
@@ -17,23 +17,62 @@ ai: {
|
|
|
17
17
|
|
|
18
18
|
While enabled, `blume build` writes two files to the root of your site:
|
|
19
19
|
|
|
20
|
-
- **`/llms.txt`** — a compact index: your site title and description, then a linked list of every page with its summary.
|
|
20
|
+
- **`/llms.txt`** — a compact index: your site title and description, then a linked list of every page with its summary, organized into sections that mirror your sidebar — folders and groups become headings, so an agent sees the docs' structure, not one flat blob.
|
|
21
21
|
- **`/llms-full.txt`** — the entire corpus: each page's full Markdown body, with its source URL, in one file.
|
|
22
22
|
|
|
23
23
|
Draft pages are excluded. Set [`deployment.site`](/docs/deployment) so the links and source URLs resolve to absolute addresses.
|
|
24
24
|
|
|
25
|
+
`llmsTxt` also takes an object form with knobs for what the files include. If your [API reference](/docs/advanced/api-reference) documents a placeholder or example spec, set `openapi: false` to keep its generated pages out of both files:
|
|
26
|
+
|
|
27
|
+
```ts blume.config.ts lineNumbers
|
|
28
|
+
ai: {
|
|
29
|
+
llmsTxt: {
|
|
30
|
+
enabled: true, // default
|
|
31
|
+
openapi: false, // exclude generated API reference pages
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
To take full control of either file, add your own `llms.txt` or `llms-full.txt` to your `public/` folder. Like a custom favicon, it's picked up automatically and ships in place of the generated file — override one and Blume still generates the other.
|
|
37
|
+
|
|
25
38
|
## Raw Markdown
|
|
26
39
|
|
|
27
40
|
Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source — perfect for LLMs, coding agents, and "copy as Markdown" workflows. It's available for every page, in dev and production, with no configuration.
|
|
28
41
|
|
|
29
|
-
| URL | Returns
|
|
30
|
-
| ----------------- |
|
|
31
|
-
| `/quickstart` | The rendered page
|
|
32
|
-
| `/quickstart.md` |
|
|
33
|
-
| `/quickstart.mdx` | The raw
|
|
42
|
+
| URL | Returns |
|
|
43
|
+
| ----------------- | ----------------------------------------- |
|
|
44
|
+
| `/quickstart` | The rendered page |
|
|
45
|
+
| `/quickstart.md` | Plain Markdown, with components converted |
|
|
46
|
+
| `/quickstart.mdx` | The raw MDX source, exactly as written |
|
|
34
47
|
|
|
35
48
|
Nested routes work the same way (`/content/syntax.md`), and the home page is served at `/index.md`.
|
|
36
49
|
|
|
50
|
+
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. 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.
|
|
51
|
+
|
|
52
|
+
### Custom component serializers
|
|
53
|
+
|
|
54
|
+
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) and its `children` (already downleveled to Markdown), and returns the replacement — or `null` to leave the JSX as-is:
|
|
55
|
+
|
|
56
|
+
```ts blume.config.ts lineNumbers
|
|
57
|
+
import { defineConfig } from "blume";
|
|
58
|
+
import type { ComponentMarkdown } from "blume";
|
|
59
|
+
|
|
60
|
+
const chart: ComponentMarkdown = ({ props }) =>
|
|
61
|
+
``;
|
|
62
|
+
|
|
63
|
+
export default defineConfig({
|
|
64
|
+
ai: {
|
|
65
|
+
markdownComponents: {
|
|
66
|
+
Chart: chart,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
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.
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
37
76
|
## Copy as Markdown
|
|
38
77
|
|
|
39
78
|
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.
|
|
@@ -46,7 +85,7 @@ The **Open in chat** action opens the current page in an AI assistant — v0, Ch
|
|
|
46
85
|
|
|
47
86
|
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.
|
|
48
87
|
|
|
49
|
-
To embed a ready-to-copy prompt inline in your content — rather than a whole-page action — use the [Prompt component](/docs/content/components#prompt), which renders a
|
|
88
|
+
To embed a ready-to-copy prompt inline in your content — rather than a whole-page action — use the [Prompt component](/docs/content/components#prompt), which renders a labeled row with a **Copy prompt** button and an optional open-in-Cursor link.
|
|
50
89
|
|
|
51
90
|
## Ask AI
|
|
52
91
|
|
|
@@ -172,7 +211,7 @@ mcp: {
|
|
|
172
211
|
| `name` | title | Server name shown to clients (defaults to title). |
|
|
173
212
|
| `instructions` | — | Optional system hint passed to connecting agents. |
|
|
174
213
|
|
|
175
|
-
The server exposes
|
|
214
|
+
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`. 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).
|
|
176
215
|
|
|
177
216
|
`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.
|
|
178
217
|
|
|
@@ -196,7 +235,7 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp
|
|
|
196
235
|
|
|
197
236
|
## Agent readability
|
|
198
237
|
|
|
199
|
-
Blume writes an **`/agent-readability.json`** manifest at your site root that indexes the agent-facing surface described on this page — so an agent can discover it in a single fetch instead of guessing at conventions or scraping HTML.
|
|
238
|
+
Blume writes an **`/agent-readability.json`** manifest at your site root that indexes the agent-facing surface described on this page — so an agent can discover it in a single fetch instead of guessing at conventions or scraping HTML. Like `llms.txt`, it's on by default:
|
|
200
239
|
|
|
201
240
|
```ts blume.config.ts lineNumbers
|
|
202
241
|
seo: {
|
|
@@ -241,4 +280,4 @@ npx skills add haydenbleasel/blume
|
|
|
241
280
|
|
|
242
281
|
The skill teaches the agent what Blume is and how to scaffold, write, and configure a site, and points it at the full docs bundled in the installed package (`node_modules/blume/docs`).
|
|
243
282
|
|
|
244
|
-
It's one of the [agent skills Blume ships](/docs/advanced/skills), alongside
|
|
283
|
+
It's one of the [agent skills Blume ships](/docs/advanced/skills), alongside a skill for keeping docs in sync with your product from a scheduled agent run.
|
|
@@ -154,3 +154,14 @@ blume eject --yes
|
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
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.
|
|
157
|
+
|
|
158
|
+
### What eject leaves behind
|
|
159
|
+
|
|
160
|
+
After ejecting, your `build` script runs plain `astro build` — the site itself builds the same, but the artifacts `blume build` layered on top are no longer produced. The eject command warns about the ones your config actually uses. To keep them:
|
|
161
|
+
|
|
162
|
+
- **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"`.
|
|
163
|
+
- **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.
|
|
164
|
+
- **sitemap.xml** — recreate it with the standard [@astrojs/sitemap](https://docs.astro.build/en/guides/integrations-guide/sitemap/) integration.
|
|
165
|
+
- **robots.txt** — ship your own as `public/robots.txt`.
|
|
166
|
+
- **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/`.
|
|
167
|
+
- **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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Export
|
|
3
|
-
description: Let readers download any page as a PDF or EPUB.
|
|
3
|
+
description: Let readers download any page as a PDF or EPUB — client-side, so static builds stay static.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume can add an **Export** action to the [page actions](/docs/content/navigation#page-actions) beneath the table of contents, letting readers save the page they're on as a **PDF** or an **EPUB**. It's off by default and entirely client-side — no server, and [static](/docs/deployment) builds stay static.
|
|
@@ -189,6 +189,24 @@ content: {
|
|
|
189
189
|
|
|
190
190
|
Static assets live in `public/` — a file at `public/logo.png` is served at `/logo.png`, so a reference like `` resolves against `public/images/create.png`.
|
|
191
191
|
|
|
192
|
+
## GitHub
|
|
193
|
+
|
|
194
|
+
Point Blume at your repository with `github`. It powers the header [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub** and **Give feedback** [page actions](/docs/content/navigation#page-actions):
|
|
195
|
+
|
|
196
|
+
```ts blume.config.ts lineNumbers
|
|
197
|
+
github: {
|
|
198
|
+
owner: "acme",
|
|
199
|
+
repo: "docs",
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
| Option | Default | Description |
|
|
204
|
+
| --- | --- | --- |
|
|
205
|
+
| `owner` | — | GitHub account or organization that owns the repository. |
|
|
206
|
+
| `repo` | — | Repository name. |
|
|
207
|
+
| `branch` | `"main"` | Branch that edit links point at. |
|
|
208
|
+
| `dir` | — | Path from the repo root to the project root (for monorepos). |
|
|
209
|
+
|
|
192
210
|
## Last modified
|
|
193
211
|
|
|
194
212
|
Show a "Last updated on …" line at the bottom of each page. Off by default; set `lastModified` to `true` to derive each page's date from its git history:
|
|
@@ -248,7 +266,13 @@ The on-this-page outline is on by default and lists `H2`–`H3` headings. Turn i
|
|
|
248
266
|
```ts blume.config.ts
|
|
249
267
|
export default defineConfig({
|
|
250
268
|
toc: false, // hide it everywhere
|
|
251
|
-
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Or narrow the heading range instead:
|
|
273
|
+
|
|
274
|
+
```ts blume.config.ts
|
|
275
|
+
export default defineConfig({
|
|
252
276
|
toc: { minHeadingLevel: 2, maxHeadingLevel: 4 },
|
|
253
277
|
});
|
|
254
278
|
```
|
|
@@ -261,8 +285,8 @@ Each of these has its own guide. The config field is the entry point:
|
|
|
261
285
|
| --- | --- | --- |
|
|
262
286
|
| `theme` | Accent color, corner radius, fonts, light/dark mode | [Theming](/docs/configuration/theming) |
|
|
263
287
|
| `navigation` | Explicit sidebar and header tabs | [Navigation](/docs/content/navigation) |
|
|
264
|
-
| `search` | Provider (Orama
|
|
265
|
-
| `markdown` |
|
|
288
|
+
| `search` | Provider (Orama, Pagefind, Algolia, and more) and indexing | [Search](/docs/configuration/search) |
|
|
289
|
+
| `markdown` | Markdown rendering options — code blocks, heading anchors, image zoom | [Syntax](/docs/content/syntax) |
|
|
266
290
|
| `ai` | `llms.txt`, Ask AI, and the MCP server | [AI](/docs/configuration/ai) |
|
|
267
291
|
| `mcp` | Hosted MCP server for coding agents | [AI](/docs/configuration/ai#mcp-server) |
|
|
268
292
|
| `analytics` | Vercel, PostHog, and custom scripts | [Analytics](/docs/configuration/analytics) |
|
|
@@ -5,7 +5,7 @@ sidebar:
|
|
|
5
5
|
label: SEO
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Blume handles the discoverability layer for you: page metadata, social share images, feeds, and structured data. The
|
|
8
|
+
Blume handles the discoverability layer for you: page metadata, social share images, feeds, and structured data. The configurable features live under the `seo` key in `blume.config.ts`; metadata is driven by your content.
|
|
9
9
|
|
|
10
10
|
```ts blume.config.ts lineNumbers
|
|
11
11
|
seo: {
|
|
@@ -14,6 +14,7 @@ seo: {
|
|
|
14
14
|
sitemap: true,
|
|
15
15
|
robots: true,
|
|
16
16
|
structuredData: true,
|
|
17
|
+
x: { handle: "@acme" },
|
|
17
18
|
}
|
|
18
19
|
```
|
|
19
20
|
|
|
@@ -25,10 +26,34 @@ Every page renders the standard `<head>` tags from your config and frontmatter:
|
|
|
25
26
|
|
|
26
27
|
- `<title>` — the page title plus your site `title`.
|
|
27
28
|
- `<meta name="description">` and `og:description` — the page `description`, falling back to the site `description`.
|
|
28
|
-
- `og:title` — the page title
|
|
29
|
-
- `<link rel="canonical">` — the page's absolute URL (when `deployment.site` is set).
|
|
29
|
+
- `og:title` and `og:site_name` — the page title and your site `title`.
|
|
30
|
+
- `<link rel="canonical">` and `og:url` — the page's absolute URL (when `deployment.site` is set).
|
|
31
|
+
- `og:type` — `article` on blog posts and changelog entries, `website` elsewhere. Article pages also emit `article:published_time` and `article:modified_time` from the page's `date` and last-modified timestamp.
|
|
32
|
+
- `og:image` — the [OG image](#open-graph-images) for the page. A generated card also declares its `og:image:width`, `og:image:height`, `og:image:type`, and `og:image:alt`, so a crawler can lay the card out without fetching it first; an `seo.image` you supply yourself declares none of these, since its size and format are unknown.
|
|
33
|
+
- `twitter:card`, `twitter:title`, `twitter:description`, `twitter:image` — the X card. Pages with an image get the wide `summary_large_image` variant; pages without one still get the compact `summary` card rather than rendering as a bare link.
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
### X attribution
|
|
36
|
+
|
|
37
|
+
X reads everything else on the card from the `og:*` tags, so the only values it can't infer are the accounts to credit. Set them under `seo.x` and Blume emits `twitter:site` (your site's account) and `twitter:creator` (the author's). The `@` is optional — `acme` and `@acme` both work.
|
|
38
|
+
|
|
39
|
+
```ts blume.config.ts lineNumbers
|
|
40
|
+
seo: {
|
|
41
|
+
x: { handle: "@acme", creator: "@jane" },
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A page can claim its own author, which is what you want for a guest post:
|
|
46
|
+
|
|
47
|
+
```yaml lineNumbers
|
|
48
|
+
---
|
|
49
|
+
title: How we shipped it
|
|
50
|
+
seo:
|
|
51
|
+
x:
|
|
52
|
+
creator: "@guestauthor"
|
|
53
|
+
---
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Override any of the other tags per page with `seo` frontmatter:
|
|
32
57
|
|
|
33
58
|
```yaml lineNumbers
|
|
34
59
|
---
|
|
@@ -63,6 +88,11 @@ seo:
|
|
|
63
88
|
type: "boolean",
|
|
64
89
|
description: "Emit robots noindex and skip structured data.",
|
|
65
90
|
},
|
|
91
|
+
"seo.x.creator": {
|
|
92
|
+
type: "string",
|
|
93
|
+
description:
|
|
94
|
+
"Credit this page to an X account (twitter:creator), overriding seo.x.creator from your config.",
|
|
95
|
+
},
|
|
66
96
|
}}
|
|
67
97
|
/>
|
|
68
98
|
|
|
@@ -225,6 +255,6 @@ seo: {
|
|
|
225
255
|
}}
|
|
226
256
|
/>
|
|
227
257
|
|
|
228
|
-
Content signals express a preference, not access control: they tell well-behaved crawlers how you'd like your content used, and it's on the crawler to
|
|
258
|
+
Content signals express a preference, not access control: they tell well-behaved crawlers how you'd like your content used, and it's on the crawler to honor them.
|
|
229
259
|
|
|
230
260
|
Ship your own `public/robots.txt` to take over.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Components
|
|
3
|
-
description:
|
|
3
|
+
description: Cards, steps, tabs, accordions, badges, code groups, frames, trees, type tables, live previews, and diffs — the built-in components, usable in any MDX page.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume ships an accessible, themeable component set available in any `.mdx` page with **no imports**. Each one is shown below with a live preview and its source. Components are vanilla and React-free; React only switches on if you add your own island.
|
|
@@ -428,7 +428,7 @@ A single row with a label and a copy button. The `description` (Markdown) is the
|
|
|
428
428
|
|
|
429
429
|
## Visibility
|
|
430
430
|
|
|
431
|
-
Show or hide content by audience. `for="web"` renders only on the site; `for="agents"` targets the
|
|
431
|
+
Show or hide content by audience. `for="web"` renders only on the site; `for="agents"` targets the agent-facing Markdown that AI agents read (`llms-full.txt` and each page's `.md` mirror).
|
|
432
432
|
|
|
433
433
|
<Visibility for="web">
|
|
434
434
|
This note appears on the website but is omitted from the agent-facing
|
package/docs/content/i18n.mdx
CHANGED
|
@@ -108,7 +108,7 @@ When i18n is on, a language switcher appears in the header automatically, genera
|
|
|
108
108
|
|
|
109
109
|
Blume ships built-in translations for its own interface chrome — “On this page”, “Search”, “Edit on GitHub”, and the rest — so a locale with a built-in pack gets translated UI out of the box. **You only translate your content.**
|
|
110
110
|
|
|
111
|
-
Packs ship for
|
|
111
|
+
Packs ship for over 30 languages — Arabic, Bengali, Bulgarian, Catalan, Chinese (Simplified and Traditional), Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Norwegian, Persian, Polish, Portuguese (and Brazilian Portuguese), Romanian, Russian, Serbian, Slovak, Spanish, Swedish, Thai, Turkish, Ukrainian, and Vietnamese. They're community-maintained — open a PR to add a locale or sharpen a translation.
|
|
112
112
|
|
|
113
113
|
Missing or unshipped strings fall back to the default locale, then to English. To override a string or supply your own language, set `i18n.ui`, keyed by locale:
|
|
114
114
|
|
|
@@ -147,7 +147,7 @@ navigation: {
|
|
|
147
147
|
}
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/
|
|
150
|
+
Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG — the same as anywhere else). An `href` may point anywhere: an external URL opens in a new tab, while an internal route (`/contact`) is validated against your pages at build time, warning you if nothing matches.
|
|
151
151
|
|
|
152
152
|
## Explicit sidebar
|
|
153
153
|
|
|
@@ -196,10 +196,10 @@ A right-rail outline is generated automatically from each page's `##` and `###`
|
|
|
196
196
|
|
|
197
197
|
Below the table of contents, every page shows a set of quick actions:
|
|
198
198
|
|
|
199
|
-
- **Edit
|
|
199
|
+
- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
|
|
200
200
|
- **Scroll to top** — smoothly returns to the top of long pages.
|
|
201
201
|
- **Give feedback** — opens a prefilled GitHub issue with an optional reaction and note (also requires `github`).
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [AI](/docs/configuration/ai#copy-as-markdown).
|
|
204
204
|
|
|
205
205
|
With [`export`](/docs/configuration/export) on, an **Export** action also lets readers download the page as a PDF or EPUB.
|
package/docs/content/sources.mdx
CHANGED
|
@@ -159,7 +159,7 @@ Two flags control how remote content is fetched and what's included:
|
|
|
159
159
|
blume dev --preview # author workflow: see drafts live
|
|
160
160
|
blume build --preview # render a full preview build
|
|
161
161
|
blume sync # refresh remote content now
|
|
162
|
-
blume sync --force
|
|
162
|
+
blume sync --force # ...ignoring any cached snapshot
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
## Custom sources
|