blume 1.0.3 → 1.1.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 +94 -0
- package/dist/cli/index.js +13784 -10579
- package/dist/cli/index.js.map +93 -61
- package/dist/types/core/config-input.d.ts +87 -8
- package/dist/types/core/data.d.ts +21 -0
- package/dist/types/core/deployment-env.d.ts +6 -0
- package/dist/types/core/diagnostics.d.ts +23 -0
- package/dist/types/core/i18n-ui.d.ts +140 -140
- package/dist/types/core/schema.d.ts +549 -370
- package/dist/types/core/sources/types.d.ts +3 -1
- package/dist/types/core/standard-schema.d.ts +41 -0
- package/dist/types/core/types.d.ts +23 -0
- package/dist/types/og/card.d.ts +63 -0
- package/dist/types/og/dimensions.d.ts +12 -0
- package/dist/types/openapi/references.d.ts +12 -7
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +9 -1
- package/docs/advanced/api-reference.mdx +22 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/configuration/ai.mdx +1 -1
- package/docs/configuration/customization.mdx +1 -1
- package/docs/configuration/export.mdx +1 -1
- package/docs/configuration/index.mdx +21 -1
- package/docs/configuration/search.mdx +28 -1
- package/docs/configuration/seo.mdx +40 -2
- package/docs/configuration/theming.mdx +1 -1
- package/docs/content/components.mdx +15 -2
- package/docs/content/index.mdx +1 -1
- package/docs/content/meta.mdx +1 -1
- package/docs/content/navigation.mdx +11 -1
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +116 -4
- package/docs/reference/cli.mdx +79 -1
- package/docs/reference/frontmatter.mdx +29 -1
- package/package.json +3 -3
- package/skills/blume-migrate/SKILL.md +170 -0
- package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +20 -0
- package/skills/blume-migrate/references/docusaurus.md +95 -0
- package/skills/blume-migrate/references/fumadocs.md +95 -0
- package/skills/blume-migrate/references/mintlify.md +156 -0
- package/skills/blume-migrate/references/monorepo.md +224 -0
- package/skills/blume-migrate/references/nextra.md +76 -0
- package/skills/blume-migrate/references/starlight.md +116 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +478 -0
- package/src/ai/llms.ts +15 -0
- package/src/astro/adapter-root.ts +70 -0
- package/src/astro/component-slots.ts +3 -2
- package/src/astro/generate.ts +132 -42
- package/src/astro/index.ts +1 -0
- package/src/astro/pages.ts +18 -3
- package/src/astro/templates.ts +158 -56
- package/src/audit/agent.ts +114 -0
- package/src/audit/catalog.ts +826 -0
- package/src/audit/checks/assets.ts +177 -0
- package/src/audit/checks/content.ts +231 -0
- package/src/audit/checks/duplicates.ts +131 -0
- package/src/audit/checks/i18n.ts +246 -0
- package/src/audit/checks/indexability.ts +213 -0
- package/src/audit/checks/links.ts +223 -0
- package/src/audit/checks/llms.ts +135 -0
- package/src/audit/checks/network.ts +272 -0
- package/src/audit/checks/og-image.ts +113 -0
- package/src/audit/checks/redirects.ts +87 -0
- package/src/audit/checks/robots.ts +114 -0
- package/src/audit/checks/sitemap.ts +229 -0
- package/src/audit/checks/social.ts +238 -0
- package/src/audit/crawl.ts +259 -0
- package/src/audit/graph.ts +74 -0
- package/src/audit/html.ts +54 -0
- package/src/audit/image-size.ts +63 -0
- package/src/audit/locate.ts +33 -0
- package/src/audit/redirects.ts +74 -0
- package/src/audit/report.ts +278 -0
- package/src/audit/run.ts +198 -0
- package/src/audit/snapshot.ts +189 -0
- package/src/audit/types.ts +214 -0
- package/src/audit/url.ts +103 -0
- package/src/cli/commands/audit.ts +205 -0
- package/src/cli/commands/build.ts +51 -12
- package/src/cli/index.ts +2 -0
- package/src/components/content/Callout.astro +8 -2
- package/src/components/content/Prompt.astro +25 -13
- package/src/components/content/Tabs.astro +98 -15
- package/src/components/layout/Breadcrumbs.astro +1 -1
- package/src/components/layout/Header.astro +5 -8
- package/src/components/layout/Logo.astro +13 -1
- package/src/components/layout/PageFeedback.astro +2 -2
- package/src/components/layout/PageLayout.astro +9 -9
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/RootLayout.astro +9 -11
- package/src/components/layout/Search.astro +36 -7
- package/src/components/layout/TableOfContents.astro +1 -1
- package/src/components/layout/nav-utils.ts +9 -7
- package/src/components/openapi/Authorization.astro +80 -0
- package/src/components/openapi/Operation.astro +19 -1
- package/src/components/openapi/ParametersTable.astro +1 -1
- package/src/components/openapi/security.ts +201 -0
- package/src/components/openapi/snippets.ts +42 -13
- package/src/core/config-input.ts +94 -8
- package/src/core/data.ts +18 -2
- package/src/core/deployment-env.ts +9 -0
- package/src/core/diagnostics.ts +59 -12
- package/src/core/links.ts +2 -91
- package/src/core/nav-diagnostics.ts +48 -4
- package/src/core/navigation.ts +55 -13
- package/src/core/probe.ts +136 -0
- package/src/core/project-graph.ts +8 -0
- package/src/core/schema.ts +100 -1
- package/src/core/sources/normalize.ts +198 -25
- package/src/core/sources/types.ts +3 -1
- package/src/core/sources/watch.ts +5 -0
- package/src/core/standard-schema.ts +54 -0
- package/src/core/types.ts +23 -0
- package/src/deploy/adapter-output.ts +27 -15
- package/src/deploy/headers.ts +66 -0
- package/src/deploy/redirects.ts +49 -9
- package/src/markdown/index.ts +2 -0
- package/src/markdown/language-icon.ts +2 -1
- package/src/markdown/table-wrap.ts +43 -0
- package/src/og/card.ts +128 -36
- package/src/og/index.ts +1 -1
- package/src/og/logo.ts +21 -0
- package/src/openapi/references.ts +19 -16
- package/src/search/popular.ts +33 -0
- package/src/theme/entry.ts +56 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResolvedI18nConfig } from "../schema.ts";
|
|
1
|
+
import type { FrontmatterExtend, ResolvedI18nConfig } from "../schema.ts";
|
|
2
2
|
import type { Diagnostic } from "../types.ts";
|
|
3
3
|
/**
|
|
4
4
|
* A single content item, normalized by a source adapter. Adapters lower their
|
|
@@ -111,5 +111,7 @@ export interface NormalizeContext {
|
|
|
111
111
|
/** Site-wide route mount point (`""` or `/seg`), prepended to every route. */
|
|
112
112
|
basePath?: string;
|
|
113
113
|
defaultType: string;
|
|
114
|
+
/** Opt-in custom frontmatter keys (`frontmatter.extend`), schema per key. */
|
|
115
|
+
frontmatterExtend?: FrontmatterExtend;
|
|
114
116
|
i18n?: ResolvedI18nConfig;
|
|
115
117
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Standard Schema interface (https://standardschema.dev), the minimal
|
|
3
|
+
* `~standard` surface shared by Zod 3.24+, Zod 4, Valibot, ArkType, and
|
|
4
|
+
* friends. Blume accepts user-supplied validation schemas (e.g.
|
|
5
|
+
* `frontmatter.extend`) through this interface instead of Zod's own types:
|
|
6
|
+
* `blume.config.ts` imports zod from the *consumer's* node_modules, which may
|
|
7
|
+
* be a different major version than the zod Blume bundles, and calling Zod
|
|
8
|
+
* methods (`.extend()`, `.safeParse()`) across instances is unsupported. The
|
|
9
|
+
* `~standard.validate` contract is version- and library-agnostic.
|
|
10
|
+
*/
|
|
11
|
+
/** One validation failure, with an optional path into the checked value. */
|
|
12
|
+
export interface StandardSchemaIssue {
|
|
13
|
+
readonly message: string;
|
|
14
|
+
readonly path?: readonly (PropertyKey | {
|
|
15
|
+
readonly key: PropertyKey;
|
|
16
|
+
})[] | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** A passing validation: the (possibly transformed) output value. */
|
|
19
|
+
export interface StandardSchemaSuccess<Output> {
|
|
20
|
+
readonly value: Output;
|
|
21
|
+
readonly issues?: undefined;
|
|
22
|
+
}
|
|
23
|
+
/** A failing validation: one or more issues. */
|
|
24
|
+
export interface StandardSchemaFailure {
|
|
25
|
+
readonly issues: readonly StandardSchemaIssue[];
|
|
26
|
+
}
|
|
27
|
+
export type StandardSchemaResult<Output> = StandardSchemaSuccess<Output> | StandardSchemaFailure;
|
|
28
|
+
/** A validation schema exposing the Standard Schema `~standard` contract. */
|
|
29
|
+
export interface StandardSchema<Input = unknown, Output = Input> {
|
|
30
|
+
readonly "~standard": {
|
|
31
|
+
readonly version: 1;
|
|
32
|
+
readonly vendor: string;
|
|
33
|
+
readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
|
|
34
|
+
readonly types?: {
|
|
35
|
+
readonly input: Input;
|
|
36
|
+
readonly output: Output;
|
|
37
|
+
} | undefined;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Whether a config-supplied value implements the `~standard` contract. */
|
|
41
|
+
export declare const isStandardSchema: (value: unknown) => value is StandardSchema;
|
|
@@ -12,6 +12,13 @@ export interface Diagnostic {
|
|
|
12
12
|
file?: string;
|
|
13
13
|
line?: number;
|
|
14
14
|
column?: number;
|
|
15
|
+
/**
|
|
16
|
+
* The built URL this diagnostic is about, for findings that are a property of
|
|
17
|
+
* the output rather than of a source file (`blume audit`). Set alongside
|
|
18
|
+
* `file`/`line` where the page maps back to authored content, so a finding can
|
|
19
|
+
* name both the URL that's wrong and the frontmatter line that fixes it.
|
|
20
|
+
*/
|
|
21
|
+
url?: string;
|
|
15
22
|
schemaPath?: string;
|
|
16
23
|
suggestion?: string;
|
|
17
24
|
docsUrl?: string;
|
|
@@ -110,6 +117,12 @@ export interface PageRecord {
|
|
|
110
117
|
description?: string;
|
|
111
118
|
contentType: string;
|
|
112
119
|
meta: PageMeta;
|
|
120
|
+
/**
|
|
121
|
+
* Custom frontmatter values declared via `frontmatter.extend`, validated by
|
|
122
|
+
* the user-supplied schemas (schema output, so transforms apply). Present
|
|
123
|
+
* only when the project opts in and the page carries at least one value.
|
|
124
|
+
*/
|
|
125
|
+
custom?: Record<string, unknown>;
|
|
113
126
|
headings: Heading[];
|
|
114
127
|
/** Whether the file is `.md`/`.mdx`. */
|
|
115
128
|
format: "md" | "mdx";
|
|
@@ -150,7 +163,17 @@ export type NavNode = {
|
|
|
150
163
|
/** Top-level tab/section. */
|
|
151
164
|
export interface NavTab {
|
|
152
165
|
label: string;
|
|
166
|
+
/**
|
|
167
|
+
* The tab's section prefix, used to scope the sidebar and match the active
|
|
168
|
+
* tab. Not necessarily a linkable route — a section may have no index page.
|
|
169
|
+
*/
|
|
153
170
|
path: string;
|
|
171
|
+
/**
|
|
172
|
+
* The clickable target. Equals `path` when the section has an index page;
|
|
173
|
+
* otherwise it's resolved to the section's first page so the tab never links
|
|
174
|
+
* to a 404. Absent when it matches `path`.
|
|
175
|
+
*/
|
|
176
|
+
href?: string;
|
|
154
177
|
icon?: string;
|
|
155
178
|
items?: NavSelectorItem[];
|
|
156
179
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { RenderOptions } from "takumi-js";
|
|
2
|
+
/**
|
|
3
|
+
* A Google Font family to load into the OG card renderer. A bare string is the
|
|
4
|
+
* family name (weight 400, normal style); the object form pins weight and style.
|
|
5
|
+
* Handed straight to Takumi's `googleFonts` helper, which fetches the family
|
|
6
|
+
* from Google Fonts at build and returns per-glyph coverage subsets.
|
|
7
|
+
*/
|
|
8
|
+
export type OgFont = string | {
|
|
9
|
+
/** Google Fonts family name, e.g. `"Noto Sans JP"`. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** `400`, `[400, 700]`, or a variable range like `"100..900"`. */
|
|
12
|
+
weight?: number | number[] | string;
|
|
13
|
+
/** `"normal"`, `"italic"`, or both. */
|
|
14
|
+
style?: "normal" | "italic" | ("normal" | "italic")[];
|
|
15
|
+
};
|
|
16
|
+
export interface OgCardPalette {
|
|
17
|
+
accent?: string;
|
|
18
|
+
background?: string;
|
|
19
|
+
border?: string;
|
|
20
|
+
foreground?: string;
|
|
21
|
+
muted?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface OgCardOptions {
|
|
24
|
+
/** Large headline — the page title. */
|
|
25
|
+
title: string;
|
|
26
|
+
/** Accent color (named preset or any CSS color) for the fallback brand mark. */
|
|
27
|
+
accent?: string;
|
|
28
|
+
/** Brand/site name shown in the top-left lockup. */
|
|
29
|
+
brand?: string;
|
|
30
|
+
/** Muted subtitle under the headline (usually the site description). */
|
|
31
|
+
description?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Inlined SVG markup of the configured logo, painted into
|
|
34
|
+
* the brand lockup. Falls back to an accent mark when absent.
|
|
35
|
+
*/
|
|
36
|
+
logo?: string;
|
|
37
|
+
/** Optional colors for the generated card. */
|
|
38
|
+
palette?: OgCardPalette;
|
|
39
|
+
/** Footer-left repository slug, e.g. `owner/repo`. */
|
|
40
|
+
repo?: string;
|
|
41
|
+
/** Footer-right site host, e.g. `docs.acme.com`. */
|
|
42
|
+
site?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Pre-fetched image entries, or a group controlling how remote images (and
|
|
45
|
+
* emoji glyphs) are fetched. Blume merges in a shared glyph cache; see
|
|
46
|
+
* {@link resolveImages}.
|
|
47
|
+
*/
|
|
48
|
+
images?: RenderOptions["images"];
|
|
49
|
+
/**
|
|
50
|
+
* Google Font families for non-Latin titles. Takumi's built-in font covers
|
|
51
|
+
* only Latin, so a CJK (etc.) title renders as tofu without a family that
|
|
52
|
+
* covers its script — see {@link loadFonts}.
|
|
53
|
+
*/
|
|
54
|
+
fonts?: OgFont[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Truncate to `max` code points with an ellipsis. Slices by code points, not
|
|
58
|
+
* UTF-16 units, so cutting mid-emoji doesn't leave a lone surrogate (a broken
|
|
59
|
+
* glyph) before the ellipsis.
|
|
60
|
+
*/
|
|
61
|
+
export declare const truncate: (value: string, max: number) => string;
|
|
62
|
+
/** Render a 1200x630 Open Graph card to a PNG buffer. */
|
|
63
|
+
export declare const renderOgImage: (options: OgCardOptions) => Promise<Uint8Array>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dimensions of a generated OG card, shared by the renderer (`card.ts`) and the
|
|
3
|
+
* layouts that declare them as `og:image:width`/`og:image:height` so a crawler
|
|
4
|
+
* can lay out the card without fetching the PNG first.
|
|
5
|
+
*
|
|
6
|
+
* This lives apart from `card.ts` because that module imports the Takumi native
|
|
7
|
+
* binding at load; a layout importing it would drag the renderer into every
|
|
8
|
+
* page render (and into the prerender/SSR bundles that externalize it).
|
|
9
|
+
*/
|
|
10
|
+
export declare const OG_IMAGE_WIDTH = 1200;
|
|
11
|
+
export declare const OG_IMAGE_HEIGHT = 630;
|
|
12
|
+
export declare const OG_IMAGE_TYPE = "image/png";
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
2
|
-
import type { NavTab } from "../core/types.ts";
|
|
3
2
|
/**
|
|
4
3
|
* 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
|
|
6
|
-
*
|
|
7
|
-
* one source of truth. Kept free of any Astro/template
|
|
8
|
-
* depend on it without a cycle.
|
|
4
|
+
* labels, and a renderer choice — no file IO, so the content source, the
|
|
5
|
+
* nav-target validation, the Scalar page generator, and the `blume:openapi`
|
|
6
|
+
* data module all share one source of truth. Kept free of any Astro/template
|
|
7
|
+
* imports so `core` can depend on it without a cycle.
|
|
9
8
|
*/
|
|
10
9
|
export type ReferenceKind = "openapi" | "asyncapi";
|
|
11
10
|
/** Who renders a reference: Blume's own UI, or the embedded Scalar SPA. */
|
|
@@ -52,8 +51,14 @@ export declare const normalizeRoute: (route: string) => string;
|
|
|
52
51
|
* by default); AsyncAPI is always rendered by Scalar for now.
|
|
53
52
|
*/
|
|
54
53
|
export declare const resolveReferences: (config: ResolvedConfig) => ReferenceSource[];
|
|
55
|
-
/**
|
|
56
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Mounted route for every reference, regardless of renderer. References no
|
|
56
|
+
* longer add a header tab automatically — authors point a `navigation.tabs`
|
|
57
|
+
* entry at one of these routes to surface it (and, for Blume-rendered specs, to
|
|
58
|
+
* scope its operations sidebar). These routes are whitelisted as valid nav
|
|
59
|
+
* targets so such a tab doesn't read as a broken link.
|
|
60
|
+
*/
|
|
61
|
+
export declare const referenceRoutes: (config: ResolvedConfig) => string[];
|
|
57
62
|
/** Blume-rendered OpenAPI references, deduped by route (first wins). */
|
|
58
63
|
export declare const blumeReferences: (config: ResolvedConfig) => ReferenceSource[];
|
|
59
64
|
/** Whether any reference is Scalar-rendered (gates the `@scalar/astro` dep + pages). */
|
package/docs/01-quickstart.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Quickstart
|
|
3
|
-
description: Install Blume, scaffold a project, and ship your first page in minutes.
|
|
3
|
+
description: Install Blume, scaffold a new project, and ship your first documentation page in minutes — then grow it into a full site at your own pace.
|
|
4
4
|
sidebar:
|
|
5
5
|
label: Quickstart
|
|
6
6
|
order: 1
|
package/docs/02-deployment.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Deployment
|
|
3
|
-
description: Deploy static docs to any host, or switch to server rendering with an adapter.
|
|
3
|
+
description: Deploy static docs to any host with zero configuration, or switch to server-side rendering with an adapter when you need dynamic behavior.
|
|
4
4
|
sidebar:
|
|
5
5
|
label: Deployment
|
|
6
6
|
order: 2
|
|
@@ -119,6 +119,14 @@ redirects: [{ from: "/old", to: "/new", status: 301 }];
|
|
|
119
119
|
`from` is matched as an exact path — wildcards and pattern matching (e.g. `/blog/:slug` or `/old/*`) aren't supported. If you need pattern-based rules, handle them in an infrastructure file like `vercel.json` (which supports wildcard `source` patterns) or your host's redirect config instead. A `vercel.json` you ship in `public/` is preserved as-is.
|
|
120
120
|
:::
|
|
121
121
|
|
|
122
|
+
:::note
|
|
123
|
+
Write both `from` and `to` as if mounted at root — under [`deployment.base`](#subpath-deploys) and [`basePath`](#mount-the-docs-under-a-path) alike, Blume rewrites both sides for you, so a redirect lands inside the base. A base you've already written into `to` by hand is preserved rather than doubled.
|
|
124
|
+
:::
|
|
125
|
+
|
|
126
|
+
## Content types
|
|
127
|
+
|
|
128
|
+
A static build also emits a `_headers` file that pins `charset=utf-8` onto the raw AI-ready endpoints — `/<route>.md`, `/<route>.mdx`, and the `.txt` files (`llms.txt`, `llms-full.txt`). Those responses are valid UTF-8, but many static hosts serve them as `text/markdown` / `text/plain` with **no** charset, and browsers then fall back to Windows-1252 — so non-ASCII docs (Japanese, accented Latin, …) render as mojibake when the raw URL is opened directly. HTML pages are unaffected because they carry `<meta charset>`. Netlify and Cloudflare (Pages/Workers static assets) read `_headers`; hosts that don't (Vercel, S3) ignore the file harmlessly. A `_headers` you ship in `public/` is left untouched.
|
|
129
|
+
|
|
122
130
|
## Environment variables
|
|
123
131
|
|
|
124
132
|
When a feature needs a runtime secret, Blume warns at `blume dev`/`build` if it's missing — so the problem surfaces early instead of at the first request:
|
|
@@ -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. The config below points Blume at the public Petstore spec as an example.
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts lineNumbers
|
|
9
9
|
openapi: {
|
|
@@ -12,7 +12,15 @@ openapi: {
|
|
|
12
12
|
}
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
That mounts the reference at `/reference` (an overview page) with each operation at `/reference/<tag>/<operation
|
|
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.
|
|
16
|
+
|
|
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
|
+
|
|
19
|
+
```ts blume.config.ts
|
|
20
|
+
navigation: {
|
|
21
|
+
tabs: [{ label: "API", path: "/reference" }],
|
|
22
|
+
}
|
|
23
|
+
```
|
|
16
24
|
|
|
17
25
|
:::note
|
|
18
26
|
Operations are indexed for search by their **summary and tag**. The rendered schema tables and code samples aren't full-text indexed; search matches an operation's title and section, then links to its own page.
|
|
@@ -31,7 +39,7 @@ openapi: {
|
|
|
31
39
|
|
|
32
40
|
## Route
|
|
33
41
|
|
|
34
|
-
`route` controls where the reference mounts — the overview page and the prefix for every operation route (and the
|
|
42
|
+
`route` controls where the reference mounts — the overview page and the prefix for every operation route (and the route you point a navigation tab at):
|
|
35
43
|
|
|
36
44
|
```ts blume.config.ts lineNumbers
|
|
37
45
|
openapi: {
|
|
@@ -70,6 +78,17 @@ openapi: {
|
|
|
70
78
|
|
|
71
79
|
`spec` is shorthand for a single-entry `sources`, so you only reach for `sources` when you have more than one.
|
|
72
80
|
|
|
81
|
+
## Authorization
|
|
82
|
+
|
|
83
|
+
Operations that declare [security requirements](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) render an **Authorization** section above their parameters, and the generated code samples send a placeholder credential (`Authorization: Bearer YOUR_TOKEN`, an API-key header, or a query key — whatever the scheme calls for). There's nothing to configure: Blume reads `security` from the spec, so the reference always matches what the API actually enforces.
|
|
84
|
+
|
|
85
|
+
The OpenAPI semantics carry over as written:
|
|
86
|
+
|
|
87
|
+
- An operation's own `security` overrides the document's root default; `security: []` marks it **public** and renders no Authorization section.
|
|
88
|
+
- Multiple requirement entries are alternatives — rendered as "or" groups; every scheme inside one entry is required together. The first alternative feeds the code samples.
|
|
89
|
+
- An empty `{}` entry means auth is **optional** for that operation, and the section says so.
|
|
90
|
+
- OAuth2 scopes are listed per scheme; scheme `description`s from `components.securitySchemes` render inline.
|
|
91
|
+
|
|
73
92
|
## The Scalar renderer
|
|
74
93
|
|
|
75
94
|
The native renderer is the default. If you'd rather embed [Scalar](https://scalar.com)'s self-contained API reference — its own sidebar, search, theme, and "Try it" playground on a single route — set `renderer: "scalar"`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Changelog
|
|
3
|
-
description: Author release notes as content, and Blume builds a timeline page and an RSS feed automatically.
|
|
3
|
+
description: Author release notes as ordinary content files or source them from GitHub Releases, and Blume builds a timeline page and an RSS feed automatically.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume ships a changelog out of the box. Write each release as a normal content file, mark it `type: changelog`, and Blume collects every entry into a generated timeline page and an RSS feed — no layout to build, no list to maintain. Or skip the files entirely and [source your changelog from GitHub Releases](#from-github-releases).
|
package/docs/advanced/skills.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Skills
|
|
3
|
-
description: The agent skills Blume ships — playbooks that teach a coding agent to build and maintain a Blume docs site.
|
|
3
|
+
description: The agent skills Blume ships — playbooks that teach a coding agent like Claude Code, Codex, or Cursor to build and maintain a Blume docs site.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume ships [agent skills](https://docs.claude.com/en/docs/claude-code/skills) — playbooks that teach a coding agent (Claude Code, Codex, Cursor) how to do a Blume-shaped job without you explaining it. They live on GitHub in the repo's `skills/` folder and are bundled in the package at `node_modules/blume/skills/` once Blume is installed, so any agent can be pointed at a `SKILL.md` directly.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: AI
|
|
3
|
-
description:
|
|
3
|
+
description: Make your docs machine-readable with llms.txt, add an optional in-page Ask AI assistant, and expose a hosted MCP server for coding agents.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume has a few AI features: machine-readable docs for external tools (`llms.txt`, on by default), an in-page **Ask AI** assistant, and a hosted **MCP server** for coding agents. Ask AI and MCP are opt-in, and static docs stay fully static until you turn a feature on.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Customization
|
|
3
|
-
description: Override components, add interactive islands, mount custom pages, install registry components, or eject.
|
|
3
|
+
description: Override components, add interactive islands, mount custom pages, install registry components, or eject entirely when you need full control.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Component overrides
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Export
|
|
3
|
-
description: Let readers download any page as a PDF or EPUB — client-side, so static builds stay static.
|
|
3
|
+
description: Let readers download any page as a PDF or EPUB — rendered client-side, so your static builds stay static and need no server infrastructure.
|
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Configuration file
|
|
3
|
-
description: Every option in blume.config.ts
|
|
3
|
+
description: Every option in blume.config.ts, from site metadata and content sources to the links that lead into each individual feature configuration guide.
|
|
4
4
|
sidebar:
|
|
5
5
|
label: blume.config.ts
|
|
6
6
|
---
|
|
@@ -188,6 +188,26 @@ content: {
|
|
|
188
188
|
|
|
189
189
|
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`.
|
|
190
190
|
|
|
191
|
+
## Frontmatter
|
|
192
|
+
|
|
193
|
+
Page frontmatter is strictly validated — an unknown key fails the build, so typos are caught early. To carry project-specific metadata (an owner, a review date), declare the extra keys under `frontmatter.extend`, each mapped to a schema you supply:
|
|
194
|
+
|
|
195
|
+
```ts blume.config.ts lineNumbers
|
|
196
|
+
import { defineConfig } from "blume";
|
|
197
|
+
import { z } from "zod";
|
|
198
|
+
|
|
199
|
+
export default defineConfig({
|
|
200
|
+
frontmatter: {
|
|
201
|
+
extend: {
|
|
202
|
+
owner: z.string(),
|
|
203
|
+
reviewedAt: z.coerce.date().optional(),
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Any [Standard Schema](https://standardschema.dev) library works — Zod (whichever version your project installs), Valibot, ArkType. Keys outside the extension stay strictly validated, so typo-catching is unchanged. See [Custom keys](/docs/reference/frontmatter#custom-keys) for the validation semantics.
|
|
210
|
+
|
|
191
211
|
## GitHub
|
|
192
212
|
|
|
193
213
|
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):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Search
|
|
3
|
-
description: Client-side search out of the box
|
|
3
|
+
description: Client-side search that works out of the box with no API keys, plus optional hosted and semantic backends you can switch to as your docs grow.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume ships local search with no hosted infrastructure and no API keys. It runs in the browser, works in both `blume dev` and `blume build`, and indexes only your real content — navigation chrome and excluded pages are skipped. When you outgrow it, you can switch to a hosted or semantic backend without changing how search looks or behaves — only the `search.provider` you configure changes.
|
|
@@ -13,6 +13,33 @@ Open search with <Badge variant="accent">⌘K</Badge> (or `Ctrl K`), or press `/
|
|
|
13
13
|
|
|
14
14
|
Queries match page **titles**, **descriptions**, and **body text**, with title matches ranked highest and descriptions above body.
|
|
15
15
|
|
|
16
|
+
## Popular pages
|
|
17
|
+
|
|
18
|
+
Before a reader types a query, the search dialog shows a **Popular** list. By default it is the first six sidebar pages — which on multi-tab sites often surfaces the wrong section. Pin the links you want instead:
|
|
19
|
+
|
|
20
|
+
```ts blume.config.ts lineNumbers
|
|
21
|
+
search: {
|
|
22
|
+
popular: [
|
|
23
|
+
{ href: "/guides/getting-started", icon: "rocket", label: "Getting started" },
|
|
24
|
+
{ href: "/guides/install", icon: "download", label: "Install" },
|
|
25
|
+
{ href: "/concepts/overview", label: "Overview" },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each entry takes an `href` (internal route or external URL) and a `label`, plus an optional `icon` — a built-in icon name shown beside the label, defaulting to a file glyph. Omit `popular` or leave it empty to keep the sidebar fallback.
|
|
31
|
+
|
|
32
|
+
Unlike icons elsewhere in Blume, `icon` here must be a built-in name: these rows render in a client island, so an image path or inline SVG isn't supported and falls back to the file glyph.
|
|
33
|
+
|
|
34
|
+
Write `href` as if the site were mounted at the root — a `basePath` is applied for you, the same as `navigation.featured`. External URLs pass through untouched.
|
|
35
|
+
|
|
36
|
+
<Callout type="warning">
|
|
37
|
+
A curated list is a single set of links shared by every language. On a site
|
|
38
|
+
with `i18n` configured, the sidebar fallback follows the reader's locale, but
|
|
39
|
+
`popular` entries point wherever their `href` says — so pin locale-prefixed
|
|
40
|
+
routes only if you want every reader sent to that one language.
|
|
41
|
+
</Callout>
|
|
42
|
+
|
|
16
43
|
## What's indexed
|
|
17
44
|
|
|
18
45
|
For every indexable page, Blume indexes its title, description, and body reduced to plain text — code blocks, images, and markup are stripped, so results stay relevant. The index is built from your source files, so it's identical in dev and production.
|
|
@@ -106,7 +106,26 @@ seo: {
|
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
### Brand the generated card
|
|
110
|
+
|
|
111
|
+
Set a local SVG and color palette to match the generated card to your brand. The logo can live in `public/` or at the project root. Omit any palette value to keep its default.
|
|
112
|
+
|
|
113
|
+
```ts blume.config.ts lineNumbers
|
|
114
|
+
seo: {
|
|
115
|
+
og: {
|
|
116
|
+
logo: "/logo/og.svg",
|
|
117
|
+
palette: {
|
|
118
|
+
accent: "#ff5410",
|
|
119
|
+
background: "#1d1d1d",
|
|
120
|
+
foreground: "#fff6f2",
|
|
121
|
+
muted: "#a6a19f",
|
|
122
|
+
border: "#323232",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
```
|
|
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:
|
|
110
129
|
|
|
111
130
|
| Page route | Image URL |
|
|
112
131
|
| ------------------- | -------------------------- |
|
|
@@ -125,9 +144,28 @@ seo:
|
|
|
125
144
|
```
|
|
126
145
|
|
|
127
146
|
:::note
|
|
128
|
-
|
|
147
|
+
Every palette color accepts any CSS color — hex, `oklch(…)`, `rgb(…)`, and so on. The accent also accepts a named preset (`blue`, `teal`, …), matching [`theme.accent`](/docs/configuration/theming#accent). A color the renderer can't parse fails the build rather than silently shipping a default-colored card.
|
|
129
148
|
:::
|
|
130
149
|
|
|
150
|
+
Emoji in a page title or site title render as [Twemoji](https://github.com/jdecked/twemoji) glyphs, fetched from a CDN while the card renders — so a build whose titles contain emoji needs network access. Each glyph is fetched once per build, however many pages use it.
|
|
151
|
+
|
|
152
|
+
### Non-Latin titles
|
|
153
|
+
|
|
154
|
+
The card's built-in font covers only Latin glyphs, so a title in another script (Japanese, Chinese, Korean, Arabic, …) renders as tofu — empty boxes. List one or more [Google Fonts](https://fonts.google.com) families in `og.fonts` that cover your script, by name:
|
|
155
|
+
|
|
156
|
+
```ts blume.config.ts lineNumbers
|
|
157
|
+
seo: {
|
|
158
|
+
og: {
|
|
159
|
+
fonts: [
|
|
160
|
+
"Noto Sans JP",
|
|
161
|
+
{ name: "Inter", weight: [400, 700] },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Each entry is a family name, or an object pinning its `weight` (a number, a list, or a variable range like `"100..900"`) and `style` (`"normal"`, `"italic"`, or both). The families are fetched from Google Fonts at build — so a build that uses them needs network access — and the renderer only pulls the glyph subsets each title actually uses. Fallback is per-glyph, so adding a family only affects glyphs the built-in font can't draw; Latin text renders exactly as before.
|
|
168
|
+
|
|
131
169
|
`seo.image` is frontmatter, so it only covers Markdown and MDX content. To give a custom [`.astro` page](/docs/advanced/custom-pages) its own social image — a marketing home or landing page, and the way to give the home page alone a bespoke share image — pass the `ogImage` prop to `PageLayout`.
|
|
132
170
|
|
|
133
171
|
## RSS feeds
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Theming
|
|
3
|
-
description: Tune the look with config tokens, override any CSS variable in theme.css, or
|
|
3
|
+
description: Tune the look with a handful of config tokens, override any CSS variable in theme.css, or drop down to Tailwind utilities for custom components.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume's theme is token-driven and works in light and dark mode out of the box. Reach for as little or as much as you need: a few config tokens for the common cases, a `theme.css` to override any design token, or Tailwind utilities for custom components.
|
|
@@ -69,6 +69,20 @@ Switch between equivalent content in place — language variants, OS-specific co
|
|
|
69
69
|
</Tabs>
|
|
70
70
|
```
|
|
71
71
|
|
|
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
|
+
|
|
74
|
+
<Tabs inline param="install">
|
|
75
|
+
<Tab title="macOS">Use Homebrew to install the toolchain.</Tab>
|
|
76
|
+
<Tab title="Windows">Use winget to install the toolchain.</Tab>
|
|
77
|
+
</Tabs>
|
|
78
|
+
|
|
79
|
+
```astro lineNumbers
|
|
80
|
+
<Tabs inline param="install">
|
|
81
|
+
<Tab title="macOS">Use Homebrew to install the toolchain.</Tab>
|
|
82
|
+
<Tab title="Windows">Use winget to install the toolchain.</Tab>
|
|
83
|
+
</Tabs>
|
|
84
|
+
```
|
|
85
|
+
|
|
72
86
|
## Badge
|
|
73
87
|
|
|
74
88
|
A small inline label for status or metadata — version tags, “new” or “beta” markers, stability levels. The `variant` tunes the color to the meaning.
|
|
@@ -619,7 +633,6 @@ An Astro example renders live with no client JavaScript:
|
|
|
619
633
|
|
|
620
634
|
<CodeBlock
|
|
621
635
|
lang="ts"
|
|
622
|
-
icons={false}
|
|
623
636
|
code={`export const greet = (name: string): string =>
|
|
624
637
|
\`Hello, \${name}!\`;`}
|
|
625
638
|
/>
|
|
@@ -629,7 +642,7 @@ An Astro example renders live with no client JavaScript:
|
|
|
629
642
|
import CodeBlock from "blume/components/content/CodeBlock.astro";
|
|
630
643
|
---
|
|
631
644
|
|
|
632
|
-
<CodeBlock lang="ts" code={source}
|
|
645
|
+
<CodeBlock lang="ts" code={source} />
|
|
633
646
|
```
|
|
634
647
|
|
|
635
648
|
To highlight to an HTML string yourself (e.g. inside your own component), import the underlying helper from `blume/markdown`:
|
package/docs/content/index.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Pages
|
|
3
|
-
description: How files in your content folder become pages, and how to organize them.
|
|
3
|
+
description: How the files in your content folder become pages, and how to organize and name them so routing and navigation are inferred automatically.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Your docs are just a folder of Markdown and MDX files. Blume turns each file into a page — routing, navigation, and metadata are inferred from the file system, so there's no manifest to keep in sync.
|
package/docs/content/meta.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Folder meta
|
|
3
|
-
description: Configure a sidebar group
|
|
3
|
+
description: Configure a sidebar group with a meta.ts file beside its pages — set the group's title, icon, order, and the order of the pages nested inside.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Every folder in your content tree becomes a sidebar group. Drop a `meta.ts` beside its pages to control how that group looks and how its children are ordered. It's entirely optional: without one, the group's label is the humanized folder name and its pages sort by [index, numeric prefix, then alphabetically](/docs/content/navigation#ordering).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Navigation
|
|
3
|
-
description:
|
|
3
|
+
description: Blume builds the sidebar from your files, then lets you refine it with frontmatter, folder meta, or config — breadcrumbs and outlines follow along.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Blume builds your sidebar from the file system, then lets you refine it as much — or as little — as you want: page by page, folder by folder, or with one explicit config. Breadcrumbs, previous/next links, and the on-page outline all follow from the same model, with nothing to wire up.
|
|
@@ -109,6 +109,16 @@ navigation: {
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
An enabled [OpenAPI or AsyncAPI reference](/docs/advanced/api-reference) mounts at its route but doesn't add a tab on its own — point a tab at that route to surface it in the header (and, for the native renderer, to scope its operations sidebar), with whatever label you like:
|
|
113
|
+
|
|
114
|
+
```ts blume.config.ts
|
|
115
|
+
navigation: {
|
|
116
|
+
tabs: [
|
|
117
|
+
{ label: "API", path: "/reference" },
|
|
118
|
+
],
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
112
122
|
Tabs also **scope the sidebar**: when the current route falls under a tab's `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the adapters and nothing else. The folder at a tab's `path` becomes the section, so this needs no extra config beyond the tabs themselves; structure your content into a folder per tab and point each tab at it.
|
|
113
123
|
|
|
114
124
|
On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the pages that _don't_ belong to a tab — each tab's folder is hidden from it, since that section already has its own tab in the header. So a root landing page lists your loose top-level pages while the sectioned content stays behind its tab, mirroring Fumadocs' root folders. If a route has no pages of its own to show this way, the full tree is shown instead, so the sidebar is never left blank.
|
package/docs/content/sources.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Content sources
|
|
3
|
-
description: Pull docs from local files, a remote
|
|
3
|
+
description: Pull docs from local files, a remote repository, or any custom backend — and mix several sources into one static-first site read at build time.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
By default Blume reads a folder of `.md`/`.mdx` files. **Content sources** let you pull pages from somewhere else — a remote repository, a CMS, or any custom backend — and mix several sources into a single site. Sources are read at build time; Blume stays static-first.
|