blume 2.1.3 → 2.2.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/AGENTS.md +1 -1
- package/CHANGELOG.md +126 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-74wfhv79.js → chunk-05f21r8h.js} +1 -1
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-1cm0h819.js} +3 -3
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-1rkxzg3n.js} +104 -47
- package/dist/cli/chunk-1rkxzg3n.js.map +36 -0
- package/dist/cli/{chunk-desvm72z.js → chunk-2nr29y95.js} +22 -26
- package/dist/cli/chunk-2nr29y95.js.map +13 -0
- package/dist/cli/{chunk-32khwcb6.js → chunk-49r370mb.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-49r370mb.js.map} +9 -10
- package/dist/cli/{chunk-ms9z66nz.js → chunk-4rftmttv.js} +2 -2
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-79bzzkx4.js} +5 -4
- package/dist/cli/chunk-79bzzkx4.js.map +10 -0
- package/dist/cli/{chunk-hvcxng5f.js → chunk-7tekknev.js} +6 -6
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-8b489v1b.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-8b489v1b.js.map} +5 -4
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-8f8xw4x2.js} +1195 -222
- package/dist/cli/chunk-8f8xw4x2.js.map +44 -0
- package/dist/cli/chunk-96snx8yy.js +329 -0
- package/dist/cli/chunk-96snx8yy.js.map +13 -0
- package/dist/cli/{chunk-ckrcm08h.js → chunk-a3ydrqny.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-a3ydrqny.js.map} +4 -5
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-ahc5gc9h.js} +5 -5
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-as09pafp.js} +73 -182
- package/dist/cli/chunk-as09pafp.js.map +18 -0
- package/dist/cli/{chunk-tfycddbt.js → chunk-bjk964fp.js} +2 -2
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-br0n8n3e.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-br0n8n3e.js.map} +1 -1
- package/dist/cli/{chunk-297ajdcv.js → chunk-c69tw29j.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-c69tw29j.js.map} +3 -3
- package/dist/cli/chunk-cn3dy3hz.js +146 -0
- package/dist/cli/chunk-cn3dy3hz.js.map +11 -0
- package/dist/cli/{chunk-124fvmzq.js → chunk-dm4mg9bj.js} +11064 -8742
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-dm4mg9bj.js.map} +78 -64
- package/dist/cli/{chunk-dqhg54mr.js → chunk-dmx6cezx.js} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-dpcahcrb.js} +24 -11
- package/dist/cli/chunk-dpcahcrb.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-dyk3r7jw.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-dyk3r7jw.js.map} +1 -1
- package/dist/cli/{chunk-twy7psdz.js → chunk-enk76qd5.js} +14 -10
- package/dist/cli/chunk-enk76qd5.js.map +10 -0
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-fqy4n3q2.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-fqy4n3q2.js.map} +3 -3
- package/dist/cli/{chunk-sc93apfs.js → chunk-frk2wdq1.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-frk2wdq1.js.map} +2 -2
- package/dist/cli/{chunk-kdpzw38a.js → chunk-gprwej7h.js} +95 -7
- package/dist/cli/chunk-gprwej7h.js.map +11 -0
- package/dist/cli/chunk-kkrteb15.js +157 -0
- package/dist/cli/chunk-kkrteb15.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-m9bp2hc1.js} +1 -1
- package/dist/cli/{chunk-58bt3hag.js → chunk-mrvh4c88.js} +4 -4
- package/dist/cli/{chunk-5cmts6dv.js → chunk-p8zg3a2h.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-p8zg3a2h.js.map} +1 -1
- package/dist/cli/{chunk-0w6hs479.js → chunk-rkqae7kh.js} +30 -18
- package/dist/cli/chunk-rkqae7kh.js.map +10 -0
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-t5xx74rd.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-t5xx74rd.js.map} +1 -1
- package/dist/cli/{chunk-1f882900.js → chunk-th0pgaj5.js} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-vnbvhgmd.js} +2 -2
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-vzy9rh6y.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-vzy9rh6y.js.map} +3 -3
- package/dist/cli/{chunk-j1x92mx9.js → chunk-w6e455pz.js} +1 -1
- package/dist/cli/chunk-wehv26ry.js +155 -0
- package/dist/cli/chunk-wehv26ry.js.map +11 -0
- package/dist/cli/{chunk-y4msrwm9.js → chunk-xgq4eytj.js} +15 -15
- package/dist/cli/{chunk-36bxs9c7.js → chunk-xt0qvz1t.js} +27 -23
- package/dist/cli/chunk-xt0qvz1t.js.map +10 -0
- package/dist/cli/index.js +78 -42
- package/dist/cli/index.js.map +7 -6
- package/dist/types/ai/changelog-components.d.ts +18 -0
- package/dist/types/ai/changelog-markdown.d.ts +12 -0
- package/dist/types/ai/openapi-components.d.ts +7 -7
- package/dist/types/ai/operation-details.d.ts +39 -0
- package/dist/types/ai/relative-links.d.ts +3 -1
- package/dist/types/ai/serializers.d.ts +6 -4
- package/dist/types/analytics/head.d.ts +4 -2
- package/dist/types/analytics/script.d.ts +8 -2
- package/dist/types/components/content/youtube.d.ts +20 -0
- package/dist/types/components/layout/nav-utils.d.ts +8 -0
- package/dist/types/components/openapi/example-panels.d.ts +33 -0
- package/dist/types/components/openapi/helpers.d.ts +53 -11
- package/dist/types/components/openapi/snippets.d.ts +7 -1
- package/dist/types/core/alert-diagnostics.d.ts +10 -0
- package/dist/types/core/builtin-tags.d.ts +16 -0
- package/dist/types/core/changelog-list.d.ts +48 -0
- package/dist/types/core/closest-match.d.ts +6 -0
- package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
- package/dist/types/core/code-fences.d.ts +8 -1
- package/dist/types/core/config-input.d.ts +1 -1
- package/dist/types/core/content-assets.d.ts +16 -18
- package/dist/types/core/diagnostics.d.ts +14 -0
- package/dist/types/core/directive-diagnostics.d.ts +22 -8
- package/dist/types/core/former-routes.d.ts +14 -0
- package/dist/types/core/github.d.ts +5 -4
- package/dist/types/core/heading-markers.d.ts +65 -2
- package/dist/types/core/hidden-pages.d.ts +3 -0
- package/dist/types/core/includes.d.ts +23 -0
- package/dist/types/core/last-modified.d.ts +18 -6
- package/dist/types/core/links.d.ts +42 -2
- package/dist/types/core/load-module.d.ts +19 -4
- package/dist/types/core/manifest.d.ts +14 -1
- package/dist/types/core/markdown-mirrors.d.ts +22 -0
- package/dist/types/core/mdx-elements.d.ts +32 -0
- package/dist/types/core/mdx-syntax.d.ts +100 -0
- package/dist/types/core/meta-diagnostics.d.ts +7 -0
- package/dist/types/core/meta.d.ts +32 -5
- package/dist/types/core/nav-diagnostics.d.ts +9 -2
- package/dist/types/core/navigation.d.ts +13 -0
- package/dist/types/core/ordering-prefix.d.ts +6 -0
- package/dist/types/core/pinned-ids.d.ts +23 -0
- package/dist/types/core/project-graph.d.ts +9 -1
- package/dist/types/core/redirect-patterns.d.ts +18 -0
- package/dist/types/core/relative-files.d.ts +31 -0
- package/dist/types/core/schema.d.ts +267 -7
- package/dist/types/core/sources/normalize.d.ts +101 -9
- package/dist/types/core/sources/types.d.ts +16 -0
- package/dist/types/core/static-files.d.ts +10 -0
- package/dist/types/core/static-host.d.ts +42 -0
- package/dist/types/core/svg-images.d.ts +26 -0
- package/dist/types/core/syntax-diagnostics.d.ts +14 -0
- package/dist/types/core/types.d.ts +42 -1
- package/dist/types/deploy/generated-files.d.ts +41 -0
- package/dist/types/deploy/redirects.d.ts +21 -7
- package/dist/types/markdown/code-title.d.ts +9 -5
- package/dist/types/markdown/directives.d.ts +8 -0
- package/dist/types/markdown/fence-language.d.ts +51 -0
- package/dist/types/markdown/fence-meta.d.ts +5 -0
- package/dist/types/markdown/github-alerts.d.ts +35 -0
- package/dist/types/openapi/asyncapi.d.ts +6 -5
- package/dist/types/openapi/checks.d.ts +10 -5
- package/dist/types/openapi/model.d.ts +55 -8
- package/dist/types/openapi/parse.d.ts +4 -1
- package/dist/types/sources/filesystem.d.ts +14 -4
- package/dist/types/sources/mdx-remote.d.ts +6 -3
- package/dist/types/sources/registry.d.ts +3 -3
- package/dist/types/theme/color.d.ts +33 -0
- package/dist/types/theme/palette.d.ts +122 -0
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +23 -8
- package/docs/04-migrating.mdx +16 -4
- package/docs/advanced/changelog.mdx +27 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +1 -1
- package/docs/cli/index.mdx +4 -2
- package/docs/cli/validate.mdx +10 -7
- package/docs/configuration/analytics.mdx +2 -0
- package/docs/configuration/customization.mdx +3 -1
- package/docs/configuration/index.mdx +2 -2
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/includes.mdx +6 -2
- package/docs/content/index.mdx +11 -1
- package/docs/content/islands.mdx +2 -0
- package/docs/content/meta.mdx +9 -3
- package/docs/content/navigation.mdx +14 -6
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/remote-mdx.mdx +3 -1
- package/docs/content/syntax.mdx +76 -8
- package/docs/content/variables.mdx +3 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/json-api.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +1 -1
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/graphql.mdx +2 -0
- package/docs/references/openapi.mdx +45 -7
- package/package.json +4 -4
- package/skills/blume/SKILL.md +2 -2
- package/skills/blume-migrate/SKILL.md +49 -27
- package/skills/blume-migrate/references/docsify.md +262 -0
- package/skills/blume-migrate/references/docus.md +298 -0
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fern.md +327 -0
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/gitbook.md +267 -0
- package/skills/blume-migrate/references/github-wiki.md +259 -0
- package/skills/blume-migrate/references/jekyll.md +253 -0
- package/skills/blume-migrate/references/mdbook.md +256 -0
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/mkdocs.md +236 -0
- package/skills/blume-migrate/references/monorepo.md +20 -8
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/readme.md +257 -0
- package/skills/blume-migrate/references/redocly.md +198 -0
- package/skills/blume-migrate/references/starlight.md +3 -3
- package/skills/blume-migrate/references/vitepress.md +204 -0
- package/skills/blume-migrate/references/vuepress.md +271 -0
- package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
- package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
- package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
- package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
- package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
- package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
- package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
- package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
- package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
- package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
- package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
- package/src/ai/changelog-components.ts +24 -0
- package/src/ai/changelog-markdown.ts +45 -41
- package/src/ai/component-markdown.ts +22 -9
- package/src/ai/llms.ts +36 -5
- package/src/ai/markdown.ts +2 -2
- package/src/ai/openapi-components.ts +25 -9
- package/src/ai/operation-details.ts +208 -0
- package/src/ai/relative-links.ts +48 -8
- package/src/ai/serializers.ts +7 -6
- package/src/ai/site-skill.ts +6 -2
- package/src/analytics/head.ts +11 -5
- package/src/analytics/script.ts +13 -3
- package/src/astro/generate.ts +32 -6
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +27 -1
- package/src/astro/mdx-source-errors.ts +46 -0
- package/src/astro/templates.ts +89 -185
- package/src/audit/checks/redirects.ts +5 -1
- package/src/audit/redirects.ts +36 -19
- package/src/audit/report.ts +22 -9
- package/src/audit/run.ts +12 -28
- package/src/audit/terms.ts +23 -0
- package/src/audit/url.ts +2 -2
- package/src/cli/build-failure.ts +256 -7
- package/src/cli/command-meta.ts +1 -1
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +12 -3
- package/src/cli/commands/check.ts +13 -2
- package/src/cli/commands/dev.ts +1 -1
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/preview.ts +17 -1
- package/src/cli/commands/validate.ts +22 -10
- package/src/cli/prepare.ts +1 -1
- package/src/cli/preview-pages.ts +161 -0
- package/src/cli/unknown-flags.ts +1 -42
- package/src/components/code-copy.ts +51 -0
- package/src/components/code-language.ts +75 -0
- package/src/components/content/AccordionItem.astro +16 -1
- package/src/components/content/Callout.astro +11 -1
- package/src/components/content/Changelog.astro +189 -0
- package/src/components/content/CodeBlock.astro +8 -11
- package/src/components/content/Tabs.astro +16 -2
- package/src/components/content/YouTube.astro +6 -4
- package/src/components/content/code-block.ts +33 -0
- package/src/components/content/content-strings.ts +5 -1
- package/src/components/content/inline-markdown.ts +35 -8
- package/src/components/content/youtube.ts +49 -7
- package/src/components/layout/Analytics.astro +5 -1
- package/src/components/layout/NavTree.astro +8 -3
- package/src/components/layout/RootLayout.astro +18 -31
- package/src/components/layout/nav-utils.ts +14 -0
- package/src/components/layout/search/pagefind.ts +2 -1
- package/src/components/openapi/Operation.astro +4 -0
- package/src/components/openapi/PanelTabs.astro +9 -3
- package/src/components/openapi/RequestBody.astro +42 -8
- package/src/components/openapi/RequestPanel.astro +34 -53
- package/src/components/openapi/example-panels.ts +110 -0
- package/src/components/openapi/helpers.ts +195 -20
- package/src/components/openapi/operation-model.ts +63 -26
- package/src/components/openapi/request.ts +13 -0
- package/src/components/openapi/snippets.ts +14 -2
- package/src/core/alert-diagnostics.ts +89 -0
- package/src/core/builtin-tags.ts +76 -0
- package/src/core/changelog-list.ts +87 -0
- package/src/core/closest-match.ts +41 -0
- package/src/core/code-fence-diagnostics.ts +267 -0
- package/src/core/code-fences.ts +43 -7
- package/src/core/component-diagnostics.ts +65 -12
- package/src/core/config-input.ts +1 -1
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- package/src/core/diagnostics.ts +36 -3
- package/src/core/directive-diagnostics.ts +265 -47
- package/src/core/former-routes.ts +46 -0
- package/src/core/git-ignored.ts +53 -0
- package/src/core/github.ts +5 -4
- package/src/core/heading-markers.ts +156 -2
- package/src/core/hidden-pages.ts +64 -0
- package/src/core/includes.ts +181 -41
- package/src/core/last-modified.ts +47 -10
- package/src/core/links.ts +438 -83
- package/src/core/load-module.ts +98 -9
- package/src/core/manifest.ts +12 -9
- package/src/core/markdown-mirrors.ts +52 -0
- package/src/core/mdx-elements.ts +153 -0
- package/src/core/mdx-syntax.ts +377 -0
- package/src/core/meta-diagnostics.ts +134 -0
- package/src/core/meta.ts +154 -55
- package/src/core/nav-diagnostics.ts +16 -4
- package/src/core/navigation.ts +70 -10
- package/src/core/ordering-prefix.ts +22 -2
- package/src/core/pinned-ids.ts +141 -0
- package/src/core/project-graph.ts +221 -29
- package/src/core/redirect-patterns.ts +41 -0
- package/src/core/relative-files.ts +145 -0
- package/src/core/schema.ts +17 -3
- package/src/core/sources/mdx-remote.ts +75 -14
- package/src/core/sources/normalize.ts +885 -179
- package/src/core/sources/notion.ts +5 -4
- package/src/core/sources/obsidian.ts +2 -5
- package/src/core/sources/types.ts +17 -0
- package/src/core/static-files.ts +40 -0
- package/src/core/static-host.ts +125 -0
- package/src/core/svg-images.ts +98 -0
- package/src/core/syntax-diagnostics.ts +491 -0
- package/src/core/types.ts +43 -1
- package/src/deploy/artifacts.ts +2 -28
- package/src/deploy/generated-files.ts +128 -0
- package/src/deploy/platforms/vercel.ts +25 -1
- package/src/deploy/redirects.ts +43 -16
- package/src/deploy/sitemap.ts +2 -1
- package/src/markdown/base-links.ts +87 -6
- package/src/markdown/code-title.ts +29 -13
- package/src/markdown/content-assets.ts +210 -0
- package/src/markdown/directives.ts +69 -5
- package/src/markdown/fence-language.ts +106 -0
- package/src/markdown/fence-meta.ts +42 -0
- package/src/markdown/github-alerts.ts +172 -0
- package/src/markdown/heading-anchors.ts +265 -56
- package/src/markdown/include.ts +30 -3
- package/src/markdown/index.ts +145 -40
- package/src/markdown/prompt-text.ts +95 -0
- package/src/markdown/relative-links.ts +37 -2
- package/src/markdown/route-snapshot.ts +22 -12
- package/src/markdown/svg-fallback.ts +51 -0
- package/src/markdown/title-heading.ts +134 -0
- package/src/migrate/migrate.ts +127 -2
- package/src/og/card.ts +14 -4
- package/src/openapi/asyncapi.ts +14 -6
- package/src/openapi/checks.ts +160 -15
- package/src/openapi/model.ts +172 -33
- package/src/openapi/parse.ts +13 -4
- package/src/openapi/render-mdx.ts +55 -9
- package/src/openapi/source.ts +87 -6
- package/src/registry/eject.ts +0 -1
- package/src/search/documents.ts +10 -2
- package/src/sources/filesystem.ts +28 -3
- package/src/sources/mdx-remote.ts +7 -15
- package/src/theme/color.ts +14 -0
- package/src/theme/entry.ts +22 -1
- package/src/theme/palette.ts +74 -1
- package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
- package/dist/cli/chunk-0w6hs479.js.map +0 -10
- package/dist/cli/chunk-150jk7km.js +0 -52
- package/dist/cli/chunk-150jk7km.js.map +0 -10
- package/dist/cli/chunk-36bxs9c7.js.map +0 -10
- package/dist/cli/chunk-53jqw8z0.js +0 -89
- package/dist/cli/chunk-53jqw8z0.js.map +0 -10
- package/dist/cli/chunk-60hz265r.js.map +0 -10
- package/dist/cli/chunk-desvm72z.js.map +0 -14
- package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
- package/dist/cli/chunk-es8h0v3j.js +0 -441
- package/dist/cli/chunk-es8h0v3j.js.map +0 -11
- package/dist/cli/chunk-kdpzw38a.js.map +0 -11
- package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
- package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
- package/dist/cli/chunk-twy7psdz.js.map +0 -10
- package/dist/cli/chunk-ydp3065p.js +0 -64
- package/dist/cli/chunk-ydp3065p.js.map +0 -10
- /package/dist/cli/{chunk-74wfhv79.js.map → chunk-05f21r8h.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-1cm0h819.js.map} +0 -0
- /package/dist/cli/{chunk-ms9z66nz.js.map → chunk-4rftmttv.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-7tekknev.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-ahc5gc9h.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-bjk964fp.js.map} +0 -0
- /package/dist/cli/{chunk-dqhg54mr.js.map → chunk-dmx6cezx.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-m9bp2hc1.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-mrvh4c88.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-th0pgaj5.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-vnbvhgmd.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-w6e455pz.js.map} +0 -0
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-xgq4eytj.js.map} +0 -0
package/docs/content/meta.mdx
CHANGED
|
@@ -23,7 +23,9 @@ export default defineMeta({
|
|
|
23
23
|
|
|
24
24
|
Every field is optional — set only what you want to override.
|
|
25
25
|
|
|
26
|
-
Blume only reads `meta.ts` files from folders your content covers: one under a folder your content `exclude` globs skip, or outside every `include` glob, is never imported. With `root: "."` and `exclude: ["
|
|
26
|
+
Blume only reads `meta.ts` files from folders your content covers: one under a folder your content `exclude` globs skip, or outside every `include` glob, is never imported. With `root: "."` and `exclude: ["src/**"]`, an unrelated `src/lib/meta.ts` is left alone.
|
|
27
|
+
|
|
28
|
+
A `meta.ts` outside every `include` glob is still ignored when its folder is a sidebar group, like a reference's [tag folder](#generated-groups) on a site whose `include` lists only `guides/**`. Blume warns about that one as `BLUME_META_OUTSIDE_INCLUDE`: add an `include` glob that reaches the folder, such as `"api/**/*.{md,mdx}"`, so Blume reads it.
|
|
27
29
|
|
|
28
30
|
## Fields
|
|
29
31
|
|
|
@@ -32,17 +34,21 @@ Blume only reads `meta.ts` files from folders your content covers: one under a f
|
|
|
32
34
|
| `title` | `string` | The group's label. Defaults to the humanized folder name. |
|
|
33
35
|
| `icon` | `string` | Icon shown next to the label. |
|
|
34
36
|
| `order` | `number` | Position among sibling groups and pages. Lower numbers sort first. |
|
|
35
|
-
| `collapsed` | `boolean` | Under the [`group` display mode](/docs/content/navigation#display-modes), whether the group starts collapsed. |
|
|
37
|
+
| `collapsed` | `boolean` | Under the [`group` display mode](/docs/content/navigation#display-modes), whether the group starts collapsed. Unset, it starts collapsed unless it holds the current page or is the only row at the top of the sidebar. |
|
|
36
38
|
| `display` | `"flat" \| "group" \| "page"` | Render mode for this group; overrides the global [`navigation.sidebar.display`](/docs/content/navigation#display-modes). |
|
|
37
39
|
| `directory` | `"card" \| "accordion" \| "none"` | List the group's pages below its `index` page's content. Nested folders inherit it. See [directory listings](/docs/content/navigation#directory-listings). |
|
|
38
40
|
| `pages` | `string[]` | Explicit order for the group's children, by slug. |
|
|
39
41
|
|
|
40
|
-
The `pages` array lists children by slug — the folder or file name with its numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is `"quickstart"`). Each listed child takes its position in the array as its order (`0`, `1`, `2`, …). Children you leave out still appear, sorted by their own order: an `index` page stays first, a child with a `sidebar.order`, a numeric prefix, or its own `meta.ts` `order` sorts by that number among the listed ones, and a child with none of these goes after them. List every child when the array should be the whole order.
|
|
42
|
+
The `pages` array lists children by slug — the folder or file name with its numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is `"quickstart"`). Each listed child takes its position in the array as its order (`0`, `1`, `2`, …). Children you leave out still appear, sorted by their own order: an `index` page stays first, a child with a `sidebar.order`, a numeric prefix, or its own `meta.ts` `order` sorts by that number among the listed ones, and a child with none of these goes after them. List every child when the array should be the whole order. An entry that names no page or folder in the group orders nothing, so Blume warns about it as `BLUME_META_UNKNOWN_PAGE`, at its line in `meta.ts`, and suggests the slug it most likely means.
|
|
41
43
|
|
|
42
44
|
The array orders pages among pages and groups among groups, but it doesn't interleave the two where loose pages list above groups: at the top of the sidebar (and of each [tab](/docs/content/navigation#tabs) section), and in any group with a [`flat`](/docs/content/navigation#display-modes) subgroup, whose header would otherwise seem to own the pages after it. There, `pages: ["advanced", "intro"]` still lists the `intro` page above the `advanced` group.
|
|
43
45
|
|
|
44
46
|
How groups render — flat headers, collapsible disclosures, or drill-in panels — defaults to the sidebar-wide `navigation.sidebar.display`; set `display` here to override it for this group alone. A folder's `index` page can also set it from frontmatter, which wins over `meta.ts` — see [per-group overrides](/docs/content/navigation#per-group-overrides).
|
|
45
47
|
|
|
48
|
+
## Generated groups
|
|
49
|
+
|
|
50
|
+
An [OpenAPI](/docs/references/openapi), [AsyncAPI](/docs/references/asyncapi), or [GraphQL](/docs/references/graphql) reference names and orders its tag groups from the spec. To change one, add a `meta.ts` in the folder the tag's pages publish under: with the reference at `/api`, `api/pets/meta.ts` in your content folder configures the **Pets** group. The fields it sets replace the generated ones, and the rest keep their generated values, so a `meta.ts` that sets only `title` keeps the tag's place in the spec's order.
|
|
51
|
+
|
|
46
52
|
## Computed meta
|
|
47
53
|
|
|
48
54
|
Because `meta.ts` is a real module, you can compute the meta — pass a function (sync or `async`) instead of an object to build it at scan time. Handy for ordering pages from an external source:
|
|
@@ -46,7 +46,13 @@ export default defineMeta({
|
|
|
46
46
|
|
|
47
47
|
See [Folder meta](/docs/content/meta) for every field and computing meta at scan time.
|
|
48
48
|
|
|
49
|
-
A folder's `meta.title` and its own `index` page's frontmatter `title` are resolved independently — translating one under i18n and forgetting the other renders a correct sidebar with a stale
|
|
49
|
+
A folder's `meta.title` and its own `index` page's frontmatter `title` are resolved independently — translating one under i18n and forgetting the other renders a correct sidebar with a stale heading on the landing page itself. Blume reports a `BLUME_NAV_INDEX_TITLE_MISMATCH` warning when they diverge on an index page that hides its own sidebar row (`sidebar.hidden: true`), where the folder title is the only sidebar label the page has. The check reads `title`, which the page's heading shows; `seo.title` replaces only the `<title>`, so it doesn't count. Untranslated pages filled in from the fallback locale are exempt — their title belongs to the fallback locale, and the fix is translating the page, not editing its frontmatter.
|
|
50
|
+
|
|
51
|
+
Like every warning, it fails [`blume validate --strict`](/docs/cli/validate). Any of these clears it:
|
|
52
|
+
|
|
53
|
+
- Change the page's `title` or the folder's `meta.title` so the two match.
|
|
54
|
+
- Keep the page's title on purpose by setting its `sidebar.label` to the folder title, the way any page sets a sidebar label that differs from its title.
|
|
55
|
+
- Show the index row again. The sidebar then shows both titles, so pairing a folder title with a different page title ("CLI" over "Overview") is fine.
|
|
50
56
|
|
|
51
57
|
To group pages _without_ adding a URL segment, use a parenthesized folder name — see [Pages](/docs/content#group-folders).
|
|
52
58
|
|
|
@@ -63,7 +69,7 @@ navigation: {
|
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
- **`flat`** (default) — a non-collapsible header with its pages listed beneath. Pages that aren't in any group always list first, above the group sections, so they can't be mistaken for a group's children.
|
|
66
|
-
- **`group`** — a collapsible `<details>` disclosure per group. Groups start collapsed by default; a group containing the current page always starts open, so only the section you're in is expanded. Set `collapsed: false` in [folder meta](/docs/content/meta) to force a group open regardless.
|
|
72
|
+
- **`group`** — a collapsible `<details>` disclosure per group. Groups start collapsed by default; a group containing the current page always starts open, so only the section you're in is expanded. A group that is the only row at the top of the sidebar, like a folder that wraps every page, starts open too, so the sidebar never shows a single closed row. Set `collapsed: false` in [folder meta](/docs/content/meta) to force a group open regardless, or `collapsed: true` to keep that lone group closed.
|
|
67
73
|
- **`page`** — each group is a single row whose chevron slides the sidebar into a sub-panel showing only that group's items, with a back arrow at the top. A group with an index page links the row's name to that page, and a group without one slides the panel in from anywhere on the row. The panel is route-aware, so landing directly on a page inside the group opens straight to it. After the back arrow, the row of the section you're in stays highlighted.
|
|
68
74
|
|
|
69
75
|
:::tip
|
|
@@ -152,7 +158,9 @@ sidebar:
|
|
|
152
158
|
hidden: true
|
|
153
159
|
```
|
|
154
160
|
|
|
155
|
-
A
|
|
161
|
+
A hidden page also stays out of the sitemap and `llms.txt`, and out of site search unless [`search.indexing.includeHiddenPages`](/docs/configuration/search#excluding-pages) is on.
|
|
162
|
+
|
|
163
|
+
A folder's `index` page appears both as the group row's link and as the first row inside the group. Hide the index page to keep only the linked header: the group row still opens the landing page, and previous/next links still pass through it. Since the navigation still links the page, it isn't a hidden page anywhere else: it stays in the sitemap, site search, and `llms.txt`. The same goes for the site's home page, the root `index` page: hiding it drops its sidebar row, but the site's root URL still serves it, so it stays listed too.
|
|
156
164
|
|
|
157
165
|
## Tabs
|
|
158
166
|
|
|
@@ -180,7 +188,7 @@ navigation: {
|
|
|
180
188
|
}
|
|
181
189
|
```
|
|
182
190
|
|
|
183
|
-
A tab's `path` is its section prefix, and it doubles as the link target. A section whose `path` isn't a page of its own — a folder with no `index.mdx` — would link to a 404, so the tab falls back to the first page in the section instead. A static [custom page](/docs/advanced/custom-pages) at the tab's `path` counts as the section's own page: with `pages/guides.astro`, a `/guides` tab lands on that page while the `guides/` folder fills its sidebar. So does the generated [changelog](/docs/advanced/changelog) index, so a `/changelog` tab opens the timeline rather than the newest entry.
|
|
191
|
+
A tab's `path` is its section prefix, and it doubles as the link target. A section whose `path` isn't a page of its own — a folder with no `index.mdx` — would link to a 404, so the tab falls back to the first page in the section instead. That includes a tab at `path: "/"` on a site with no root `index.mdx`: it links to the first page in the sidebar. A static [custom page](/docs/advanced/custom-pages) at the tab's `path` counts as the section's own page: with `pages/guides.astro`, a `/guides` tab lands on that page while the `guides/` folder fills its sidebar. So does the generated [changelog](/docs/advanced/changelog) index, so a `/changelog` tab opens the timeline rather than the newest entry.
|
|
184
192
|
|
|
185
193
|
Set `href` when you want a tab to land somewhere else — a particular page in the section, say:
|
|
186
194
|
|
|
@@ -267,7 +275,7 @@ navigation: {
|
|
|
267
275
|
}
|
|
268
276
|
```
|
|
269
277
|
|
|
270
|
-
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 and by [`blume doctor`](/docs/cli/doctor), warning you if nothing matches.
|
|
278
|
+
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 and by [`blume doctor`](/docs/cli/doctor), warning you if nothing matches. A path to a file in `public/` (`/spec.pdf`) or one Blume generates (`/llms.txt`) matches too.
|
|
271
279
|
|
|
272
280
|
## Explicit sidebar
|
|
273
281
|
|
|
@@ -347,7 +355,7 @@ A right-rail outline is generated automatically from each page's headings — `#
|
|
|
347
355
|
|
|
348
356
|
Below the table of contents, every page shows a set of quick actions. In windows at least 1,280px wide they sit in the right-hand column with the outline. In narrower windows and on phones they sit at the bottom of the “On this page” dropdown above the content, which shows on a page with no headings too:
|
|
349
357
|
|
|
350
|
-
- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
|
|
358
|
+
- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config, on every page whose file is in the repository: a file git ignores, like a generated API reference written into a gitignored folder, gets no edit link, since GitHub has no file to open.
|
|
351
359
|
- **Scroll to top** — smoothly returns to the top of long pages. It's in the right-hand column only, since the dropdown already sits at the top of the page.
|
|
352
360
|
|
|
353
361
|
Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [Markdown for agents](/docs/discoverability/markdown#copy-as-markdown).
|
|
@@ -32,7 +32,7 @@ Locale directories and version snapshots inside the vault are read the same way
|
|
|
32
32
|
|
|
33
33
|
A link to an `index` note lands on its folder's route rather than a phantom `/index`. **An unresolved wikilink degrades to plain text with a build warning instead of failing the build**, so a vault mid-refactor still publishes. Single-line `%%comments%%` are stripped, a wikilink inside an HTML comment (`<!-- [[Draft]] -->`) is left alone since Obsidian hides it too, and a note with no `title` in its frontmatter is titled by its filename — the same rule Obsidian itself applies. An `index` note is the one exception: it names a route rather than a note, so its title falls through to Blume's usual derivation (first heading, then the humanized segment). Fenced, indented, and inline code passes through verbatim, so a note documenting the syntax survives.
|
|
34
34
|
|
|
35
|
-
Dot-folders are skipped, including Obsidian's own `.obsidian` config directory and `.trash` — which the dev watcher also ignores, so moving a pane in the app doesn't rebuild your site. Editing a note does, and so does deleting one to the trash, since the note leaves the vault. The directories no content scan reads (`node_modules`, `dist`, `.git`, …) are skipped too, so a vault rooted at the project itself doesn't publish dependency READMEs. A note with `#` or `?` in its path is left out with an error, the way a [content file](/docs/content#files-and-routes) is: Astro can't load its copy, so rename it. Symlinks inside the vault are followed, the way the filesystem source follows them, so a shared folder linked into the vault publishes with it. A vault that sits inside the filesystem source's root must be excluded from it (`filesystem({ root: "docs", exclude: ["
|
|
35
|
+
Dot-folders are skipped, including Obsidian's own `.obsidian` config directory and `.trash` — which the dev watcher also ignores, so moving a pane in the app doesn't rebuild your site. Editing a note does, and so does deleting one to the trash, since the note leaves the vault. The directories no content scan reads (`node_modules`, `dist`, `.git`, …) are skipped too, so a vault rooted at the project itself doesn't publish dependency READMEs. A note with `#` or `?` in its path is left out with an error, the way a [content file](/docs/content#files-and-routes) is: Astro can't load its copy, so rename it. Symlinks inside the vault are followed, the way the filesystem source follows them, so a shared folder linked into the vault publishes with it. A vault that sits inside the filesystem source's root must be excluded from it (`filesystem({ root: "docs", exclude: ["vault/**"] })`); [`blume version <id>`](/docs/cli/version) then leaves it out of the snapshot, since the vault keeps publishing its own notes as current.
|
|
36
36
|
|
|
37
37
|
Not yet lowered: callouts (`> [!note]`) render as plain blockquotes, embeds (`![[image.png]]`) pass through untouched, multi-line `%%comments%%` are left in place, and there is no backlink graph.
|
|
38
38
|
|
|
@@ -37,7 +37,9 @@ mdxRemote({
|
|
|
37
37
|
|
|
38
38
|
A relative link between remote files (`[Errors](./02-errors.mdx)`) lands on the page the linked file publishes at, with its ordering prefix dropped from the route, and `blume validate` checks it the same way.
|
|
39
39
|
|
|
40
|
-
A private repo's token is read from the `GITHUB_TOKEN` environment variable
|
|
40
|
+
A private repo's token is read from the `GITHUB_TOKEN` environment variable. It is never inlined into your config or generated output, and it is only ever sent to GitHub's own hosts (`api.github.com`, `raw.githubusercontent.com`), never to a custom `url` base. A public repo works without it. With `github`, the adapter declares the variable, so `blume dev` and `blume build` warn `BLUME_MISSING_SECRET` when it's unset: the file listing goes through GitHub's API, which allows 60 unauthenticated requests an hour. A raw `url` reads files only, so nothing warns up front; when GitHub refuses a file with a 401, 403, or 404 while `GITHUB_TOKEN` is unset, the warning for the skipped file says the variable isn't set.
|
|
41
|
+
|
|
42
|
+
A file written to read on GitHub often opens with a `# Heading`, while Blume renders the page's `title` as its `<h1>`. When a remote page's first line is an h1 that matches its front matter `title`, or the front matter sets no `title`, the heading is dropped and its text is the title, so the page shows one `<h1>`. A first heading that differs from `title` stays.
|
|
41
43
|
|
|
42
44
|
Remote pages are rendered with full MDX-plus-component fidelity: their bodies are materialized into a hidden staging directory and rendered through Astro alongside your local docs, so callouts, tabs, and every other Blume component keep working.
|
|
43
45
|
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -7,7 +7,7 @@ Blume renders standard Markdown and MDX with a curated, GitHub-flavored feature
|
|
|
7
7
|
|
|
8
8
|
## Headings
|
|
9
9
|
|
|
10
|
-
Structure a page with headings. Blume renders your frontmatter `title` as the page heading, so start your content at `##` — `##` and `###` become entries in the table of contents. Every `##`–`######` heading is also wrapped in a link to its own anchor, so readers can click a heading to copy, bookmark, or share a permalink straight to that section (hover to reveal the `#`). Turn this off with `markdown: { headingAnchors: false }` in `blume.config.ts`.
|
|
10
|
+
Structure a page with headings. Blume renders your frontmatter `title` as the page heading, so start your content at `##` — `##` and `###` become entries in the table of contents. A page without a `title` takes its first `#` heading as its title, and when the page opens with that heading, it shows once, as the page heading, which keeps the heading's anchor so links to it still land. `llms-full.txt` lists it once too. Every `##`–`######` heading is also wrapped in a link to its own anchor, so readers can click a heading to copy, bookmark, or share a permalink straight to that section (hover to reveal the `#`). Turn this off with `markdown: { headingAnchors: false }` in `blume.config.ts`.
|
|
11
11
|
|
|
12
12
|
```md
|
|
13
13
|
## Section
|
|
@@ -19,21 +19,27 @@ Structure a page with headings. Blume renders your frontmatter `title` as the pa
|
|
|
19
19
|
|
|
20
20
|
### Custom anchors
|
|
21
21
|
|
|
22
|
-
Anchor ids are generated from the heading text, so a reworded heading gets a new anchor. Append `[#custom-id]` to pin the anchor instead — the marker never renders, and links keep working no matter how the heading reads. Pinned anchors also stay identical across [translated locales](/docs/content/i18n), where auto-generated ids would otherwise differ per language.
|
|
22
|
+
Anchor ids are generated from the heading text, so a reworded heading gets a new anchor. A `<Badge>` in a heading is a label, not part of that text: `## Install <Badge>beta</Badge>` anchors as `#install` and reads "Install" in the table of contents. An id never ends in a dash, so `## Features ✨` anchors as `#features`, and a component or image at either end of a heading adds no dash. Append `[#custom-id]` to pin the anchor instead — the marker never renders, and links keep working no matter how the heading reads. Pinned anchors also stay identical across [translated locales](/docs/content/i18n), where auto-generated ids would otherwise differ per language.
|
|
23
23
|
|
|
24
24
|
```md
|
|
25
25
|
## Getting started [#setup]
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Link to it as `/page#setup`. The syntax matches Fumadocs, so migrated content works verbatim.
|
|
28
|
+
Link to it as `/page#setup`. The syntax matches Fumadocs, so migrated content works verbatim. The id is used exactly as written: smart punctuation turns `--` into a dash and curls quotes in the heading's text, but not in the marker, so `[#read--write]` anchors as `#read--write`.
|
|
29
29
|
|
|
30
|
-
The `{#custom-id}` form used by Pandoc, kramdown, and Markdown-based specification toolchains is accepted as an equivalent in `.md` files. Only that unspaced spelling pins an anchor: the spaced `{ #custom-id }` and kramdown's `{: #custom-id }` (both from MkDocs' `attr_list`) stay in the heading text
|
|
30
|
+
The `{#custom-id}` form used by Pandoc, kramdown, and Markdown-based specification toolchains is accepted as an equivalent in `.md` files. Only that unspaced spelling pins an anchor: the spaced `{ #custom-id }` and kramdown's `{: #custom-id }` (both from MkDocs' `attr_list`), and an attribute list that sets the id beside classes or attributes (`{ #custom-id .wide }`), stay in the heading's text and its anchor, and `blume check` warns about them in `.md` as `BLUME_MD_CURLY_ANCHOR`. In `.mdx`, a bare `{…}` is a JSX expression and the page fails to compile — `blume check` reports all three spellings as `BLUME_MDX_CURLY_ANCHOR` — so write `[#custom-id]` there, or escape the braces. The escaped form pins the same anchor in both formats, which makes it the right spelling for a partial that `.mdx` pages [include](/docs/content/includes):
|
|
31
31
|
|
|
32
32
|
```md
|
|
33
33
|
## Getting started \{#setup\}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Fragment links can also target a raw HTML element with an `id` (`<a id="setup"></a>`)
|
|
36
|
+
Fragment links can also target a raw HTML element with an `id` (`<a id="setup"></a>`), or an `<a name="setup">`; `blume validate` accepts those alongside heading anchors.
|
|
37
|
+
|
|
38
|
+
An empty `<a>` inside a heading is the heading's own anchor, the way GitBook exports (`<a href="#setup" id="setup"></a>`) and wikis (`<a name="setup"></a>`) write one. Blume takes it out of the heading and gives the heading its `id`, or else its `name`, so `#setup` links keep landing there and the heading keeps its own self-link. A `[#custom-id]` marker on the same heading wins.
|
|
39
|
+
|
|
40
|
+
```md
|
|
41
|
+
## Getting started <a id="setup"></a>
|
|
42
|
+
```
|
|
37
43
|
|
|
38
44
|
### Table of contents markers
|
|
39
45
|
|
|
@@ -150,6 +156,10 @@ Link to other pages or external sites. Images accept a relative path to a file n
|
|
|
150
156
|
|
|
151
157
|
A relative page link (`./install`, `../guides/setup`) resolves from the page's own folder — on an index page, the folder it introduces — and a link to a Markdown file (`./setup.md`, `../intro.mdx`) lands on the page that file publishes, its `slug` included. A dotted page name (`./node.js` for a `node.js.mdx` page) counts as a page link when a page publishes there, and a component's string `href` (`<Card href="./install">`) resolves the same way. Blume writes each one as the root-relative route in the built page, so links authored for GitHub or Docusaurus keep working, and [`blume validate`](/docs/cli/validate) checks them the same way.
|
|
152
158
|
|
|
159
|
+
A root-relative link to a Markdown file (`/guides/setup.md`, the way VitePress and Docsify write one) lands on that file's page too, read from your content root; under a [deployment `base`](/docs/deployment#subpath-deploys), one that starts with the base is read as including it. One that names no file in your content keeps its path, since `/guides/setup.md` is also the URL of that page's [Markdown copy](/docs/discoverability/markdown#raw-markdown). To link a page's Markdown copy whatever its file is called, write a raw `<a href="/guides/setup.md">`, which keeps its path, or the copy's full URL.
|
|
160
|
+
|
|
161
|
+
A link to a Markdown file that doesn't exist tries the same file with the other extension first. So after you rename `setup.md` to `setup.mdx`, a link to `./setup.md` still lands on that file's page, its `slug` and ordering prefix included, and `blume validate` passes it. A link that names neither file falls back to a plain relative link with the extension dropped. Either way, the link no longer names a real file, so it breaks wherever the Markdown is read as files, such as on GitHub. Update the extension in your links when you rename a file.
|
|
162
|
+
|
|
153
163
|
Read the [quickstart](/docs/quickstart) to get started.
|
|
154
164
|
|
|
155
165
|
```md
|
|
@@ -166,6 +176,10 @@ External links open in the same tab by default. Set `markdown: { externalLinks:
|
|
|
166
176
|

|
|
167
177
|
```
|
|
168
178
|
|
|
179
|
+
An SVG whose size Astro can't read is served as it is instead of failing the build. Astro reads an SVG's size from its `<svg>` tag, which needs a width and height or a viewBox and has to end within the file's first 1,000 bytes; a draw.io export puts its whole diagram in a `content` attribute on that tag, which pushes it past them. Blume warns about each one as `BLUME_SVG_UNOPTIMIZED` at the line that embeds it. The image still shows, without the intrinsic size an optimized image carries; export the diagram without the copy of its source, or remove the `content` attribute, to have it optimized.
|
|
180
|
+
|
|
181
|
+
A link to a file next to your content that isn't a page (`[the spec](./spec.pdf)`, `[data](../files/data.csv)`, or a reference definition like `[spec]: ./spec.pdf`) publishes that file with the page, and the built link points at the published copy, keeping a suffix like `#page=2`. So does an HTML element that names one: the `src` of an `<img>`, `<video>`, `<source>`, or `<audio>`, and the `href` of an `<a>` or of a component like `<Card>`, as raw HTML in `.md` or an element in `.mdx`. These copies are served as they are, without the optimization an image embed gets. A relative link that names no file next to the page keeps its path, so one to a file under `public/` still works.
|
|
182
|
+
|
|
169
183
|
Absolute paths under `public/` (``) are served verbatim with no optimization — use them for files that must keep their exact bytes and URL, like a logo referenced from outside your docs. Remote images are also passed through untouched unless their host is authorized in the [`image` config](/docs/configuration#images).
|
|
170
184
|
|
|
171
185
|
Content images are click-to-zoom by default — readers can click any image to open it in a lightbox. Turn this off with `markdown: { imageZoom: false }` in `blume.config.ts`, or opt a single image out with `data-no-zoom`.
|
|
@@ -202,6 +216,20 @@ export default defineConfig({
|
|
|
202
216
|
```
|
|
203
217
|
````
|
|
204
218
|
|
|
219
|
+
The title is every word after the language, so ` ```js Install the client ` titles the block "Install the client". Keywords like `lineNumbers` and `wrap`, line ranges like `{1,4-5}`, and `key="value"` options aren't part of it, and `title="..."` sets the title explicitly. A title in brackets, like ` ```ts [blume.config.ts] `, shows without them, and a line range written against the brackets, as in ` ```ts [blume.config.ts]{2} `, still highlights its lines.
|
|
220
|
+
|
|
221
|
+
The language is any [Shiki language](https://shiki.style/languages) id or alias, in any case: ` ```JSON ` highlights like ` ```json `. A block in a language Shiki doesn't know renders as plain text, and Blume warns about its fence as `BLUME_UNKNOWN_CODE_LANGUAGE`; write `text` for a block that's plain on purpose.
|
|
222
|
+
|
|
223
|
+
Options other docs tools read do nothing here, and Blume warns about each as `BLUME_CODE_FENCE_OPTION` with the spelling to use instead: MkDocs' `hl_lines="2 3"` (a `{2,3}` range here) and `linenums="1"` (`lineNumbers`), `showLineNumbers` (`lineNumbers`), Mintlify's `lines` (`lineNumbers`), Fern's `wordWrap` (`wrap`), and `filename="app.js"` (`title="app.js"`). On a Rust block, rustdoc's and mdBook's `ignore`, `no_run`, `should_panic`, `compile_fail`, `edition2015` through `edition2024`, `noplayground`, and `editable` warn too: Blume never compiles, tests, or runs a code block, so remove them. None of them becomes part of the title.
|
|
224
|
+
|
|
225
|
+
A `console` (or `shellsession`) block is a terminal session: commands after a prompt like `$`, with their output. Its copy button copies only the commands, without their prompts, so the copy pastes straight into a terminal. A command that ends in `\` keeps its continuation lines.
|
|
226
|
+
|
|
227
|
+
```console
|
|
228
|
+
$ npm install blume
|
|
229
|
+
added 1 package in 2s
|
|
230
|
+
$ npx blume dev
|
|
231
|
+
```
|
|
232
|
+
|
|
205
233
|
Inline code can be highlighted too: add a `{:lang}` marker inside a backtick span and it's colored like a tiny code block — `useState(){:js}` or `T extends object{:ts}`. It only kicks in when you add the marker, so plain inline code stays untouched — nothing to switch on.
|
|
206
234
|
|
|
207
235
|
Highlighting uses the `github-light`/`github-dark` themes by default. Swap in any [bundled Shiki theme](https://shiki.style/themes) per color mode with `markdown.code.theme` — it colors every code surface at once (fences, inline snippets, `<CodeBlock>`, and `<Diff>`):
|
|
@@ -309,7 +337,7 @@ Both work alongside a title and `lineNumbers`. To wrap every block, set `markdow
|
|
|
309
337
|
|
|
310
338
|
### Highlighting
|
|
311
339
|
|
|
312
|
-
Annotate code with GitHub-style comments to draw attention to lines, words, and changes. The comments are stripped from the rendered output, so the code stays copy-paste clean. All
|
|
340
|
+
Annotate code with GitHub-style comments to draw attention to lines, words, and changes. The comments are stripped from the rendered output, so the code stays copy-paste clean. All of them are on by default — no configuration.
|
|
313
341
|
|
|
314
342
|
Mark a line with `// [!code highlight]` to give it a highlighted background:
|
|
315
343
|
|
|
@@ -336,6 +364,13 @@ import { createServer } from "node:http"; // [!code word:createServer]
|
|
|
336
364
|
createServer().listen(3000);
|
|
337
365
|
```
|
|
338
366
|
|
|
367
|
+
Flag a line as a problem with `// [!code error]` or `// [!code warning]`, tinted red and amber:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
const port = process.env.PORT; // [!code warning]
|
|
371
|
+
server.listen(port.trim()); // [!code error]
|
|
372
|
+
```
|
|
373
|
+
|
|
339
374
|
Dim everything except the lines you mark with `// [!code focus]` (the rest sharpens on hover):
|
|
340
375
|
|
|
341
376
|
```ts
|
|
@@ -345,7 +380,7 @@ export default defineConfig({
|
|
|
345
380
|
});
|
|
346
381
|
```
|
|
347
382
|
|
|
348
|
-
Or highlight lines by **number** instead of comments — useful when you can't edit the code. Put a brace range after the language; single lines, comma lists, and `start-end` spans all work:
|
|
383
|
+
Or highlight lines by **number** instead of comments — useful when you can't edit the code. Put a brace range after the language, with or without a space between them (` ```ts{1,4-5} ` works too); single lines, comma lists, and `start-end` spans all work:
|
|
349
384
|
|
|
350
385
|
```ts {1,4-5}
|
|
351
386
|
import { defineConfig } from "blume";
|
|
@@ -580,7 +615,7 @@ timeline
|
|
|
580
615
|
|
|
581
616
|
## Callouts
|
|
582
617
|
|
|
583
|
-
Callouts pull a reader's attention to context, advice, or risk. Write them as `:::type` directives; add a title in brackets, like `:::warning[Heads up]`. Directives are an MDX-only feature — in a `.md` file a `:::note` line stays literal text.
|
|
618
|
+
Callouts pull a reader's attention to context, advice, or risk. Write them as `:::type` directives; add a title in brackets, like `:::warning[Heads up]`. The title keeps its inline formatting, so code, emphasis, and links in the brackets render in the title. Directives are an MDX-only feature — in a `.md` file a `:::note` line stays literal text, and `blume dev`, `blume build`, and `blume check` warn about it as `BLUME_MD_DIRECTIVE`. Rename the page to `.mdx` to render it as a callout.
|
|
584
619
|
|
|
585
620
|
### Note
|
|
586
621
|
|
|
@@ -682,6 +717,28 @@ Commit `blume.config.ts`, not `.blume/`.
|
|
|
682
717
|
::::
|
|
683
718
|
```
|
|
684
719
|
|
|
720
|
+
Write the name right after the colons. `::: tip`, with a space (the spelling VitePress, VuePress, and Docusaurus v2 use), isn't a directive, so the page shows the line as text, and Blume warns about a callout name written that way as `BLUME_DIRECTIVE_SPACED_NAME`. A title goes in brackets: text after the name, as in `:::tip Some title`, opens the callout but never shows, and Blume warns about it as `BLUME_DIRECTIVE_OPENING_TEXT` with the bracketed spelling, `:::tip[Some title]`. Inside a container, a closing fence with text after its colons still closes it, and the text is dropped: a `::: card` line inside a `:::warning` ends the callout there, and `card` never shows. Blume warns about that line as `BLUME_DIRECTIVE_CLOSING_TEXT`. To keep it inside the callout as text, give the callout a longer fence, as above, or remove the text after the colons.
|
|
721
|
+
|
|
722
|
+
### GitHub alerts
|
|
723
|
+
|
|
724
|
+
GitHub's alert syntax renders as a callout too: a quote whose first line is `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, or `[!CAUTION]`, in any case. Text after the marker on that line becomes the title.
|
|
725
|
+
|
|
726
|
+
{/* prettier-ignore */}
|
|
727
|
+
> [!TIP]
|
|
728
|
+
> Set `deployment.site` so sitemaps and Open Graph images use absolute URLs.
|
|
729
|
+
|
|
730
|
+
{/* prettier-ignore */}
|
|
731
|
+
```md
|
|
732
|
+
> [!TIP]
|
|
733
|
+
> Set `deployment.site` so sitemaps and Open Graph images use absolute URLs.
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
`[!NOTE]` and `[!TIP]` render as the callouts of the same name, `[!IMPORTANT]` as a note, `[!WARNING]` as a warning, and `[!CAUTION]` as a danger callout, matching GitHub's red. As on GitHub, an alert quoted inside another stays a quote, and so does any other marker, such as `[!INFO]`.
|
|
737
|
+
|
|
738
|
+
A formatter that joins lines, like oxfmt with `proseWrap: "never"`, pulls the first body line up beside the marker, where it becomes the title (it [collapses directives](/docs/faq#why-is-oxfmt--ultracite-collapsing-my-directives) the same way). Leave a `>` line blank after the marker to keep them apart; GitHub renders that form too.
|
|
739
|
+
|
|
740
|
+
Alerts are MDX-only, like directives. In a `.md` file the quote stays a quote and shows its marker as text, and `blume dev`, `blume build`, and `blume check` warn about it as `BLUME_MD_GITHUB_ALERT`. Rename the page to `.mdx` to render it as a callout.
|
|
741
|
+
|
|
685
742
|
## Math
|
|
686
743
|
|
|
687
744
|
Render LaTeX with KaTeX, useful for math-heavy or scientific docs. Put `$$…$$` on lines of its own for a centered block:
|
|
@@ -715,3 +772,14 @@ Blume converts straight quotes and dashes to typographic equivalents as you writ
|
|
|
715
772
|
```md
|
|
716
773
|
"Quotes" become curly, -- becomes an en dash, --- an em dash, and ... an ellipsis.
|
|
717
774
|
```
|
|
775
|
+
|
|
776
|
+
## Syntax from other tools
|
|
777
|
+
|
|
778
|
+
Blume reads the syntax on this page and nothing else. Syntax another docs tool reads shows on the page as written, or breaks an `.mdx` page when it renders, so `blume dev`, `blume build`, and `blume check` warn about it at its line. Code blocks, inline code, and comments are skipped, so wrap an example in inline code to show it as text.
|
|
779
|
+
|
|
780
|
+
- `BLUME_TEMPLATE_TAG`: a Liquid or Markdoc tag (`{% include note.html %}`, `{% /callout %}`) or a Liquid output (`{{ site.title }}`) in a `.md` page. Blume runs no template language, so replace the tag with what it produced, and a site-wide value with a [variable](/docs/content/variables). A `{{name}}` that could be a variable isn't reported here.
|
|
781
|
+
- `BLUME_WIKILINK_UNSUPPORTED`: a wiki link (`[[Home]]`, `[[Text|Page]]`) that names one of the site's pages by its file name or title, or any wiki link with a `|`. Only an [Obsidian vault](/docs/content/sources/obsidian) source reads wiki links. The warning gives the Markdown link that replaces it.
|
|
782
|
+
- `BLUME_MDC_SYNTAX`: a Nuxt Content (MDC) block component (`::callout` … `::`) or inline component (`:badge[New]{color="primary"}`). Use the matching [component](/docs/content/components) or a `:::` callout instead.
|
|
783
|
+
- `BLUME_MDX_ATTRIBUTE_LIST`: an attribute list (`{ width="300" }`, `{: .note }`) in an `.mdx` page. MDX reads the braces as JavaScript, so the page fails to build. Set the attributes on a JSX element instead (`<img src="…" width="300" />`), and pin a heading's anchor with `[#id]`.
|
|
784
|
+
- `BLUME_MD_ATTRIBUTE_LIST`: an attribute list in a `.md` page, like kramdown's `{: .note }` or `{: #intro }` on its own line or after a block, or `{ width="300" }` after an image. The page shows it as written. Set the attributes on an HTML element instead (`<img src="…" width="300">`), and pin a heading's anchor with `[#id]`. A heading's `{#id}` marker is covered by [`BLUME_MD_CURLY_ANCHOR`](#custom-anchors) instead.
|
|
785
|
+
- `BLUME_MDX_UNCLOSED_ELEMENT`: an HTML void element without a closing slash (`<img …>`, `<br>`) in an `.mdx` page, or in a partial an `.mdx` page [includes](/docs/content/includes). MDX reads it as an unclosed JSX element, so close it: `<br />`.
|
|
@@ -38,4 +38,6 @@ A `{{name}}` that isn't defined in prose is a build error, `BLUME_UNDEFINED_VARI
|
|
|
38
38
|
|
|
39
39
|
To show a reference in prose without defining it, put it in inline code: `` `{{name}}` ``.
|
|
40
40
|
|
|
41
|
-
A site that defines no variables is unaffected: `{{…}}` in its pages stays as written.
|
|
41
|
+
A site that defines no variables is unaffected: `{{…}}` in its `.md` pages stays as written. In `.mdx`, braces are a JavaScript expression, so an undefined `{{name}}` fails the page when it renders, reported as [`BLUME_MDX_UNDEFINED_NAME`](/docs/content#markdown-and-mdx) at its line. Escape both opening braces (`\{\{name}}`) or put it in inline code.
|
|
42
|
+
|
|
43
|
+
A `{{…}}` that holds more than a name, like Liquid's `{{ site.title }}` or `{{ page.url | relative_url }}`, is never a variable. Blume warns about one in a `.md` page as `BLUME_TEMPLATE_TAG`, since the page would show it as written.
|
|
@@ -235,7 +235,7 @@ Every build writes an [agent skill](https://agentskills.io) for your docs, a `SK
|
|
|
235
235
|
- the site's title and description, and when an agent should use the skill
|
|
236
236
|
- how to read any page as Markdown, plus `llms.txt`, `llms-full.txt`, the [MCP server](/docs/discoverability/mcp), the JSON API, and the changelog, when the site serves them
|
|
237
237
|
- your API references
|
|
238
|
-
- a map of the docs in sidebar order, linking each page's Markdown with its one-line description
|
|
238
|
+
- a map of the docs in sidebar order, linking each page's Markdown with its one-line description (its meta description, as in `llms.txt`)
|
|
239
239
|
|
|
240
240
|
The skill is named after your site's `title` (`Acme Docs` becomes `acme-docs`). It covers the current version in the default language, leaves out API operation pages and changelog entries (each has one line of its own above the map), skips the pages `llms.txt` does (drafts, hidden and `noindex` pages, and pages with [`ai.exclude`](/docs/discoverability/llms-txt#excluding-a-page)), and lists up to 200 pages, pointing at `llms.txt` for the rest. The links are absolute, so the skill needs [`deployment.site`](/docs/deployment); a build without one skips it.
|
|
241
241
|
|
|
@@ -47,7 +47,7 @@ Errors are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details (`
|
|
|
47
47
|
|
|
48
48
|
The **OpenAPI document** at `/openapi.json` is generated per build from your config, so it describes only what the deployed site serves: every JSON endpoint with a unique `operationId`, typed parameters, and response schemas, plus the text surfaces alongside — the [`.md` mirrors](/docs/discoverability/markdown), [`llms.txt`](/docs/discoverability/llms-txt) and `llms-full.txt`, [`agent-readability.json`](/docs/discoverability/agent-discovery#agent-readability) — and the [MCP endpoint](/docs/discoverability/mcp) when it's enabled. Frameworks that build tools from an OpenAPI description get the same reach an MCP client has. The document is linked from the [API catalog](/docs/discoverability/agent-discovery#api-catalog), the [readability manifest](/docs/discoverability/agent-discovery#agent-readability), the homepage `Link` header as `rel="service-desc"`, and `llms.txt`.
|
|
49
49
|
|
|
50
|
-
None of this touches your own [API reference](/docs/references/openapi): a documented spec is rendered into pages, never served at `/openapi.json`, and the catalog lists both. A `public/openapi.json` you ship yourself takes over that route (the JSON endpoints stay). The `/api/…` catch-all steps aside when a docs section is served from the `/api` namespace (`content/api/overview.md`) or a custom page owns a rest route under `/api/`, so those pages keep winning.
|
|
50
|
+
None of this touches your own [API reference](/docs/references/openapi): a documented spec is rendered into pages, never served at `/openapi.json`, and the catalog lists both. A `public/openapi.json` you ship yourself takes over that route (the JSON endpoints stay), with a `BLUME_PUBLIC_OPENAPI_JSON` warning: the catalog still lists `/openapi.json` as the docs API's description, so agents would read your file as that. To publish a spec file of your own, put it at another path, like `public/specs/openapi.json`. The `/api/…` catch-all steps aside when a docs section is served from the `/api` namespace (`content/api/overview.md`) or a custom page owns a rest route under `/api/`, so those pages keep winning.
|
|
51
51
|
|
|
52
52
|
## Turning it off
|
|
53
53
|
|
|
@@ -13,7 +13,7 @@ agents: {
|
|
|
13
13
|
|
|
14
14
|
While enabled, `blume build` writes two files to the root of your site:
|
|
15
15
|
|
|
16
|
-
- **`/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.
|
|
16
|
+
- **`/llms.txt`** — a compact index: your site title and description, then a linked list of every page with its summary (its meta description: `seo.description` when the page sets one, else `description`), organized into sections that mirror your sidebar — folders and groups become headings, so an agent sees the docs' structure, not one flat blob.
|
|
17
17
|
- **`/llms-full.txt`** — the entire corpus: each page's full Markdown body, with its source URL, in one file.
|
|
18
18
|
|
|
19
19
|
Drafts, pages hidden from the sidebar (`sidebar.hidden`, or the top-level `hidden` shorthand), and `noindex` pages are excluded — except generated API reference pages, whose place in both files is set by `openapi` below rather than by `noindex`. Set [`deployment.site`](/docs/deployment) so the links and source URLs resolve to absolute addresses.
|
|
@@ -17,9 +17,9 @@ Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source — pe
|
|
|
17
17
|
|
|
18
18
|
Nested routes work the same way (`/content/syntax.md`), and the home page is served at `/index.md`.
|
|
19
19
|
|
|
20
|
-
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), `<Accordion>` bold questions over their answers, `<FileTree>` its list, `<CodeGroup>` its titled code blocks, `<YouTube>` a link, and every other built-in component — Columns, Frame, Expandable, Badge, Tooltip, and the rest — its readable content. `<AutoTypeTable>`, which needs the type checker, stays as written, as do a `<Diff>` that reads its sides from files (`src`, or `before` and `after`) and a `<GithubInfo>` without `owner` and `repo`. The components a generated [API reference](/docs/references/openapi) page is made of downlevel too: `<Operation>` becomes the endpoint in its spec's own notation (`GET /pets/{id}`, `SEND user/signup`, `query pets`) with a deprecation marker, `<ApiTagOperations>` a list of those endpoints linked to their pages with their summaries, and `<ApiOverview>` the API's version and base URLs — so an agent reading a reference page knows what to call, and site search matches an endpoint's path. Props are read as literal data and never executed — strings, numbers, booleans, arrays, objects, and template strings, plus references to the page's `frontmatter`, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Only props resolve: an expression in the page's own text, like `Owned by {frontmatter.owner}`, stays as written in the Markdown copy. For a value agents should read too, write it into the text, or, when it's the same on every page, make it a [content variable](/docs/content/variables), which every surface replaces. Anything that can't be converted faithfully — a custom component, or a prop computed by a function call or from an import — is left as-is, and component markup inside code, fenced or inline, is never touched. The same conversion applies to [`llms-full.txt`](/docs/discoverability/llms-txt) and the [MCP server](/docs/discoverability/mcp)'s `get_page` tool, so every agent-facing surface reads clean Markdown.
|
|
20
|
+
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), `<Accordion>` bold questions over their answers, `<FileTree>` its list, `<CodeGroup>` its titled code blocks, `<YouTube>` a link, and every other built-in component — Columns, Frame, Expandable, Badge, Tooltip, and the rest — its readable content. `<AutoTypeTable>`, which needs the type checker, stays as written, as do a `<Diff>` that reads its sides from files (`src`, or `before` and `after`) and a `<GithubInfo>` without `owner` and `repo`. The components a generated [API reference](/docs/references/openapi) page is made of downlevel too: `<Operation>` becomes the endpoint in its spec's own notation (`GET /pets/{id}`, `SEND user/signup`, `query pets`) with a deprecation marker, and for an OpenAPI operation, its request body's properties and example and its responses with their examples, `<ApiTagOperations>` a list of those endpoints linked to their pages with their summaries, and `<ApiOverview>` the API's version and base URLs — so an agent reading a reference page knows what to call, and site search matches an endpoint's path. [`<Changelog />`](/docs/advanced/changelog#writing-your-own-index) becomes the release list it renders: year headings over a linked row per release. Props are read as literal data and never executed — strings, numbers, booleans, arrays, objects, and template strings, plus references to the page's `frontmatter`, so a prop like `title={frontmatter.status}` resolves to the same value the rendered page shows. Only props resolve: an expression in the page's own text, like `Owned by {frontmatter.owner}`, stays as written in the Markdown copy. For a value agents should read too, write it into the text, or, when it's the same on every page, make it a [content variable](/docs/content/variables), which every surface replaces. Anything that can't be converted faithfully — a custom component, or a prop computed by a function call or from an import — is left as-is, and component markup inside code, fenced or inline, is never touched. The same conversion applies to [`llms-full.txt`](/docs/discoverability/llms-txt) and the [MCP server](/docs/discoverability/mcp)'s `get_page` tool, so every agent-facing surface reads clean Markdown.
|
|
21
21
|
|
|
22
|
-
When you want the MDX itself, use the `.mdx` variant: its components stay as written, and the rest is prepared for an agent reading it by URL, as in the `.md` variant. [Includes](/docs/content/includes) are spliced in, [`<Visibility>`](/docs/content/components#visibility) resolves for agents (`for="web"` content is removed, `for="agents"` content kept), relative images point at their served URLs, relative page links point at the routes they mean, and root-relative links (`/guides/install`) gain the site's `deployment.base` and `basePath` and, on a translated page, move into the page's locale, the way the rendered page's links do. Root-relative images and links to files in `public/` (`/spec.pdf`) gain `deployment.base` alone, since that's where those files are served.
|
|
22
|
+
When you want the MDX itself, use the `.mdx` variant: its components stay as written, and the rest is prepared for an agent reading it by URL, as in the `.md` variant. [Includes](/docs/content/includes) are spliced in, [`<Visibility>`](/docs/content/components#visibility) resolves for agents (`for="web"` content is removed, `for="agents"` content kept), relative images and links to other files beside the page point at their served URLs, relative page links and root-relative links to a content file (`/guides/setup.md`) point at the routes they mean, and root-relative links (`/guides/install`) gain the site's `deployment.base` and `basePath` and, on a translated page, move into the page's locale, the way the rendered page's links do. Root-relative images and links to files in `public/` (`/spec.pdf`) gain `deployment.base` alone, since that's where those files are served.
|
|
23
23
|
|
|
24
24
|
### Content negotiation
|
|
25
25
|
|
|
@@ -49,7 +49,7 @@ seo:
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
:::note
|
|
52
|
-
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.
|
|
52
|
+
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, and one that isn't a CSS color at all also warns `BLUME_THEME_COLOR_INVALID` with the line that sets it, before the card renders.
|
|
53
53
|
:::
|
|
54
54
|
|
|
55
55
|
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.
|
|
@@ -7,7 +7,7 @@ Search crawlers and AI crawlers both start from the same two files at your site
|
|
|
7
7
|
|
|
8
8
|
## Sitemap
|
|
9
9
|
|
|
10
|
-
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`](/docs/discoverability/metadata#per-page-overrides) pages, and pages whose `seo.canonical` names another URL. 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:
|
|
10
|
+
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](/docs/content/navigation#hidden-pages), and [`noindex`](/docs/discoverability/metadata#per-page-overrides) pages, and pages whose `seo.canonical` names another URL. 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:
|
|
11
11
|
|
|
12
12
|
```ts blume.config.ts lineNumbers
|
|
13
13
|
seo: {
|
|
@@ -53,6 +53,8 @@ reference: [
|
|
|
53
53
|
],
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
An id Blume doesn't generate a sample for is left out, with a `BLUME_GRAPHQL_UNKNOWN_CODE_SAMPLE` warning that lists the ones it does.
|
|
57
|
+
|
|
56
58
|
## Type pages
|
|
57
59
|
|
|
58
60
|
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.
|
|
@@ -20,7 +20,7 @@ export default defineConfig({
|
|
|
20
20
|
|
|
21
21
|
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, and 3.1 and [3.2](#openapi-32) specs are read as written. An adapter is a plain description of the reference — not a parsed spec — so Blume can validate it up front and inline it into the generated site; leaving `reference` out (or empty) renders no reference at all. Documenting an event-driven or GraphQL API instead? See [AsyncAPI](/docs/references/asyncapi) and [GraphQL](/docs/references/graphql).
|
|
22
22
|
|
|
23
|
-
To go from your framework's generated spec to a deployed reference, follow the guide for [FastAPI](/guides/fastapi-documentation-website), [Django REST Framework](/guides/django-rest-framework-api-documentation), [NestJS](/guides/nestjs-api-documentation-website), [Spring Boot](/guides/spring-boot-api-documentation), [Hono](/guides/hono-zod-api-documentation), [Fastify](/guides/fastify-api-documentation), [Elysia](/guides/elysia-api-documentation), or [oRPC](/guides/orpc-openapi-documentation). [Generate API docs from an OpenAPI spec](/guides/openapi-documentation-website) starts from any spec file.
|
|
23
|
+
To go from your framework's generated spec to a deployed reference, follow the guide for [FastAPI](/guides/fastapi-documentation-website), [Django REST Framework](/guides/django-rest-framework-api-documentation), [NestJS](/guides/nestjs-api-documentation-website), [Spring Boot](/guides/spring-boot-api-documentation), [ASP.NET Core](/guides/aspnet-core-api-documentation), [Laravel](/guides/laravel-api-documentation-scramble), [Rails](/guides/rails-api-documentation-rswag), [Hono](/guides/hono-zod-api-documentation), [Fastify](/guides/fastify-api-documentation), [Elysia](/guides/elysia-api-documentation), or [oRPC](/guides/orpc-openapi-documentation). If you define the API in TypeSpec, [publish it from the TypeSpec definition](/guides/typespec-documentation-website). [Generate API docs from an OpenAPI spec](/guides/openapi-documentation-website) starts from any spec file.
|
|
24
24
|
|
|
25
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 operations sidebar for the native renderer:
|
|
26
26
|
|
|
@@ -30,10 +30,14 @@ navigation: {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The sidebar's tag groups and the overview page's sections follow the same order: the order of the spec's top-level `tags` list, then any tag it doesn't declare, in the order operations first use it.
|
|
33
|
+
The sidebar's tag groups and the overview page's sections follow the same order: the order of the spec's top-level `tags` list, then any tag it doesn't declare, in the order operations first use it. Within a tag, operations follow the spec too: paths in the order they're listed, and each path's methods in the order they're written. A [`meta.ts`](/docs/content/meta) in a tag's folder with its own `pages` list overrides that order.
|
|
34
|
+
|
|
35
|
+
A tag's `x-displayName`, Redocly's extension, labels its sidebar group and overview section in place of its name. Its URL still comes from its name, so adding one doesn't move any page. An operation is labeled by its `summary`, or by its method and path (`GET /pets`), as its page is titled, when it has none.
|
|
36
|
+
|
|
37
|
+
The spec's `info.description` opens the overview page, and each tag's `description` opens its section there. Their headings move one level under where they sit, so the page's title stays its only top-level heading: a `# Authentication` in `info.description` or an operation's `description` renders as a second-level heading, and in a tag's description as a third-level one, under the tag's own.
|
|
34
38
|
|
|
35
39
|
:::note
|
|
36
|
-
Operations are indexed for search by their **summary, description, tag, and endpoint** (`GET /pets/{id}`). The rendered schema tables and generated code samples aren't full-text indexed; search matches an operation's title, prose, section, and path, then links to its own page. [Your own samples](#your-own-samples) are indexed like any code block, only with [`search.indexing.includeCodeBlocks`](/docs/configuration/search).
|
|
40
|
+
Operations are indexed for search by their **summary, description, tag, and endpoint** (`GET /pets/{id}`), and by what their [Markdown copy](#your-own-samples) lists: the request body's top-level properties and the responses' descriptions. The rendered schema tables and generated code samples aren't full-text indexed; search matches an operation's title, prose, section, and path, then links to its own page. [Your own samples](#your-own-samples) are indexed like any code block, only with [`search.indexing.includeCodeBlocks`](/docs/configuration/search).
|
|
37
41
|
:::
|
|
38
42
|
|
|
39
43
|
## A local spec
|
|
@@ -67,6 +71,10 @@ reference: [
|
|
|
67
71
|
],
|
|
68
72
|
```
|
|
69
73
|
|
|
74
|
+
An operation's `<tag>` is its first tag (`operations` when it has none), and its `<operation>` is its `operationId`, else its method and path. Both are lowercased with their words split by hyphens, camelCase and PascalCase included: the tag `InboxesThreads` gives `inboxes-threads`, and the operationId `listThreads` gives `list-threads`. An operation's slug only has to be unique within its tag, so two tags can each have a `list`; a second `list` in the same tag gets its method added (`list-post`), with a [`BLUME_OPENAPI_DUPLICATE_OPERATION_ID`](#spec-warnings) warning when the two share an `operationId`.
|
|
75
|
+
|
|
76
|
+
Earlier versions of Blume kept a tag's words together (`inboxesthreads`) and made an operation's slug unique across the whole spec (`list-get` for the second `list`). Blume redirects each of those URLs, and its Markdown copy, to the page's current one, unless a page or one of your [`redirects`](/docs/deployment#redirects) is already at that URL.
|
|
77
|
+
|
|
70
78
|
## Code samples and schemas
|
|
71
79
|
|
|
72
80
|
`codeSamples` picks which languages render per operation, in the order you list them. It defaults to `["curl", "js", "python"]`, and `false` or an empty list shows none. `expandSchemas` starts nested schema rows expanded rather than collapsed:
|
|
@@ -99,14 +107,20 @@ Every sample is plain, copy-pasteable code for the request, written with the lan
|
|
|
99
107
|
| `csharp` | C# | `HttpClient` | `c#`, `cs` |
|
|
100
108
|
| `dotnet` | .NET | RestSharp | `.net`, `dot-net` |
|
|
101
109
|
| `c` | C | libcurl | |
|
|
102
|
-
| `cpp` | C++ | cpr | `c
|
|
110
|
+
| `cpp` | C++ | cpr | `c++`, `cplusplus` |
|
|
103
111
|
| `kotlin` | Kotlin | OkHttp | `kt` |
|
|
104
112
|
| `rust` | Rust | reqwest | `rs` |
|
|
105
113
|
| `dart` | Dart | `package:http` | `flutter` |
|
|
106
114
|
|
|
107
115
|
Each value is quoted by its own language's rules, so a header like `If-Match: "33a64df5"` or a body with a `$` in it reaches the API as written.
|
|
108
116
|
|
|
109
|
-
|
|
117
|
+
An id Blume doesn't generate a sample for, such as ReadMe's `objectivec`, is left out of every page, with a `BLUME_OPENAPI_UNKNOWN_CODE_SAMPLE` warning that lists the ids above. To show a sample in another language, write it into the spec as [your own sample](#your-own-samples).
|
|
118
|
+
|
|
119
|
+
Schema tables follow the way the data travels, as the examples do: a request body leaves out `readOnly` properties, which the server sets, and a response leaves out `writeOnly` ones, which it never returns. A [webhook or callback](#webhooks-and-callbacks) body is a request your API sends, so it reads like a response. The same goes for the request samples, the [Try it](#try-it-playground) form and its prefill, and the response examples, including examples written into the spec, such as a model-level `example` that holds the model's `id`.
|
|
120
|
+
|
|
121
|
+
A body or response whose `examples` map names two or more examples shows each one, labeled by its `summary`, or by its key when it has none. A response gets a tab per example after its status (`200 · A cat`), a webhook payload one per example after its media type, and a request body an **Examples** block under its schema. The code samples and Try it start from the first.
|
|
122
|
+
|
|
123
|
+
A property written as a `$ref` with keywords beside it, which OpenAPI 3.1 allows, reads those keywords over the referenced schema's. Its own `description` is the one its row shows, and its own `example` or `default` is the one the samples use.
|
|
110
124
|
|
|
111
125
|
### Your own samples
|
|
112
126
|
|
|
@@ -127,7 +141,7 @@ paths:
|
|
|
127
141
|
planter list
|
|
128
142
|
```
|
|
129
143
|
|
|
130
|
-
These samples render as their own tabs, ahead of the generated ones, and stay as written when readers change the [Try it](#try-it-playground) form. The older `x-code-samples` spelling works too. To show only your own samples, set `codeSamples: false`. Unlike the generated samples, they're also in the operation page's Markdown copy, so `llms-full.txt`, the MCP server, and the assistant see your SDK calls.
|
|
144
|
+
These samples render as their own tabs, ahead of the generated ones, and stay as written when readers change the [Try it](#try-it-playground) form. The older `x-code-samples` spelling works too. To show only your own samples, set `codeSamples: false`. Unlike the generated samples, they're also in the operation page's Markdown copy, so `llms-full.txt`, the MCP server, and the assistant see your SDK calls. That copy also lists the request body's media type and top-level properties with its example (every named one), and each response's status and description, with the examples the spec records and a sampled one for the first success response that records none.
|
|
131
145
|
|
|
132
146
|
A `source` can also be a `$ref` to a file that holds the sample's code, the way Redocly describes keeping samples in files of their own. Blume reads the file relative to the spec, fetching it when the spec is a URL. A `$ref` it can't read, or one that points inside a document (`#/…`), leaves that sample out with a [`BLUME_OPENAPI_CODE_SAMPLE_REF`](#spec-warnings) warning:
|
|
133
147
|
|
|
@@ -147,6 +161,8 @@ paths:
|
|
|
147
161
|
|
|
148
162
|
Operation pages rendered natively ship an interactive **Try it** panel by default. Blume generates the form from the operation itself: an input per path, query, and header parameter, a body editor built from the request-body schema, everything prefilled from the spec's examples. Values go on the wire the way the spec describes them: array and object parameters follow their `style` and `explode` (`tags=dog&tags=cat` in a query, `1,2` in a path, `filter[color]=red` for `deepObject`), and an `application/x-www-form-urlencoded` or `multipart/form-data` body is sent as form fields rather than JSON. A server picker lists the operation's servers (its own `servers`, else its path's, else the spec's, with each `{variable}` at its `default`), with a free-text field for any other base URL, and auth inputs match the operation's [resolved security](#authorization) — bearer token, API key, and basic credentials, with OAuth2 as a token paste field (bring an access token; Blume doesn't run the flow).
|
|
149
163
|
|
|
164
|
+
When any of the operation's responses is JSON, requests ask for it: Send and the code samples carry an `Accept` header with the first JSON media type the responses list, a success response's ahead of the others. Frameworks like Laravel answer a request without one as if a browser sent it, redirecting an unauthenticated call to a login page instead of returning the 401 the API documents. An `Accept` header parameter the operation declares takes its place.
|
|
165
|
+
|
|
150
166
|
The panel and the code samples stay in lockstep: values typed into the form update the generated samples live, so a copied curl command always matches exactly what **Send** would do. And it stays out of the way — the panel is server-rendered collapsed, and its JavaScript loads only when a reader first opens it. Readers who never touch it download none of it.
|
|
151
167
|
|
|
152
168
|
The one method a browser can't send is `TRACE`: `fetch` refuses it, so on a `trace` operation **Send** says so instead of sending, and the JavaScript sample (built on `fetch`) is a note rather than code. The cURL and Python samples send it fine.
|
|
@@ -176,7 +192,7 @@ reference: [
|
|
|
176
192
|
],
|
|
177
193
|
```
|
|
178
194
|
|
|
179
|
-
The built-in proxy only forwards requests to the origins your specs declare in `servers` (at the document, path, or operation level, with variables at their defaults) — including across redirects — so a public docs deployment can't be aimed at other hosts on its network. A **Custom base URL** typed into the panel isn't a documented server: with the proxy enabled, requests to it are refused with a 403. It forwards only the headers the panel sets itself — the credentials and header parameters filled in, and the body's `Content-Type` — so cookies, credentials the browser attaches for the docs site (HTTP Basic auth on a password-protected preview, say), and headers your host adds (`X-Forwarded-For`, `CF-*`, `X-Vercel-*`) never reach the API. It reads a request body only up to 4 MB (anything larger gets a `413`), and every response it relays carries `Content-Security-Policy: sandbox`, `X-Content-Type-Options: nosniff`, and `Cross-Origin-Resource-Policy: same-origin` — plus `Content-Disposition: attachment` for HTML or SVG — so an API error page that echoes its input can't run script on the docs origin. The proxy is also [rate limited](/docs/configuration/rate-limiting) per reader, like every server route a reader can call.
|
|
195
|
+
The built-in proxy only forwards requests to the origins your specs declare in `servers` (at the document, path, or operation level, with variables at their defaults) — including across redirects — so a public docs deployment can't be aimed at other hosts on its network. A **Custom base URL** typed into the panel isn't a documented server: with the proxy enabled, requests to it are refused with a 403. It forwards only the headers the panel sets itself — the credentials and header parameters filled in, the `Accept` header, and the body's `Content-Type` — so cookies, credentials the browser attaches for the docs site (HTTP Basic auth on a password-protected preview, say), and headers your host adds (`X-Forwarded-For`, `CF-*`, `X-Vercel-*`) never reach the API. It reads a request body only up to 4 MB (anything larger gets a `413`), and every response it relays carries `Content-Security-Policy: sandbox`, `X-Content-Type-Options: nosniff`, and `Cross-Origin-Resource-Policy: same-origin` — plus `Content-Disposition: attachment` for HTML or SVG — so an API error page that echoes its input can't run script on the docs origin. The proxy is also [rate limited](/docs/configuration/rate-limiting) per reader, like every server route a reader can call.
|
|
180
196
|
|
|
181
197
|
When a Try it request is blocked, [Fix CORS errors in an API documentation playground](/guides/api-documentation-cors-errors) reproduces it, reads the preflight the browser sends, and fixes it with a CORS policy on your API or the built-in proxy.
|
|
182
198
|
|
|
@@ -195,6 +211,8 @@ reference: [
|
|
|
195
211
|
],
|
|
196
212
|
```
|
|
197
213
|
|
|
214
|
+
A source's group sits at its route, and each folder of the route above it is a sidebar group too. So route a spec at the folder it describes: `route: "/apis/messaging"` makes the source's group the `messaging` folder's own group, named by its `label`, while `"/apis/messaging/openapi"` nests that group inside a **Messaging** group that holds nothing else.
|
|
215
|
+
|
|
198
216
|
`spec` is shorthand for a single-entry `sources`, so you only reach for `sources` when you have more than one. When two specs need different display options — a different code-sample set, say — list two `openapi()` adapters instead, each with its own `route`. An embedded [Scalar](/docs/references/scalar) reference beside native pages is its own `scalar()` adapter in the list. Sources are resolved in list order, and when two resolve to the same route the first one wins (the build warns about the one it dropped).
|
|
199
217
|
|
|
200
218
|
[Combine multiple OpenAPI specifications in one docs site](/guides/multiple-openapi-specs-documentation) builds an API portal this way, with each service's spec on its own route and tab.
|
|
@@ -320,9 +338,29 @@ Blume doesn't validate a spec against the OpenAPI schema: run a linter such as [
|
|
|
320
338
|
| `BLUME_OPENAPI_CODE_SAMPLE_REF` | An `x-codeSamples` entry's `source` is a `$ref` Blume can't read, or one that points inside a document, so that [sample](#your-own-samples) is left out. |
|
|
321
339
|
| `BLUME_OPENAPI_X_WEBHOOKS` | A 3.1 or later spec keeps its webhooks under `x-webhooks`, the extension 3.0 tools read, and has no `webhooks`, so none of them render. Rename the field to `webhooks`. |
|
|
322
340
|
| `BLUME_OPENAPI_REF_PATH_ITEM` | A path or webhook is a `$ref` to a shared path item, such as one under `components.pathItems`. Blume doesn't resolve these, so its operations are missing from the reference. Inline the path item under `paths` or `webhooks`; its schemas, parameters, and responses can stay `$ref`s. |
|
|
341
|
+
| `BLUME_OPENAPI_DUPLICATE_OPERATION_ID` | Two operations share an `operationId`, which must be unique in a spec. The warning names both. Blume adds the method to the second one's id in `<Operation id>`, and to its [URL](#route) when the two share a tag. Give each operation its own `operationId`. |
|
|
342
|
+
| `BLUME_OPENAPI_LOCAL_SERVER` | A server the spec, a path, or an operation declares is a local address (`localhost`, `127.0.0.1`, `0.0.0.0`, `[::1]`), often written by a framework that exported the spec on a dev machine. Try it and the code samples would send requests there, which readers can't reach. List the public URL in `servers`, or set it with an [overlay](#overlays). |
|
|
343
|
+
| `BLUME_OPENAPI_NULLABLE` | A spec that declares OpenAPI 3.1 or later uses `nullable`, which 3.1 removed. Blume still shows those values as nullable, but validators and SDK generators that follow 3.1 read them as never null. The warning names where, as JSON Pointers into the spec. Add `"null"` to the schema's `type` instead (`type: [string, "null"]`). |
|
|
323
344
|
|
|
324
345
|
[Keep API documentation in sync with backend changes in CI](/guides/openapi-documentation-ci) runs those checks in GitHub Actions: it exports the spec from the backend, fails when the spec is stale, invalid, or breaking, and rebuilds the reference from it.
|
|
325
346
|
|
|
347
|
+
## Operations in your own pages
|
|
348
|
+
|
|
349
|
+
Any MDX page can show an operation from a reference with `<Operation>`: its authorization, parameters, request body, and responses, beside the Try it panel and code samples, as the operation's own page shows them. A quickstart can walk through the one call it's about without sending readers away:
|
|
350
|
+
|
|
351
|
+
```mdx docs/quickstart.mdx lineNumbers
|
|
352
|
+
---
|
|
353
|
+
title: Quickstart
|
|
354
|
+
mode: wide
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
Create your first pet:
|
|
358
|
+
|
|
359
|
+
<Operation source="reference" id="add-pet" />
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`source` names the reference by its route, without slashes and lowercased with hyphens (`/reference` is `reference`, `/api/v2` is `api-v2`). `id` names the operation: its `operationId`, lowercased with its words split by hyphens (`addPet` is `add-pet`), or its method and path (`post-pets`) when it has none. The surest way to get both is an operation page's raw MDX copy, at its URL plus `.mdx`, whose body is the tag to copy. The operation lays out in two columns, so give the page [`mode: wide`](/docs/content/frontmatter#layout): beside the table of contents, the left column is narrow. In the page's Markdown copy the tag becomes the endpoint, request body, and responses, as on the operation's own page.
|
|
363
|
+
|
|
326
364
|
## Embedding Scalar instead
|
|
327
365
|
|
|
328
366
|
`openapi()` always renders Blume's own pages. To embed [Scalar](https://scalar.com)'s self-contained API reference UI on a single route instead — its own sidebar, search, theme, and request client — list a `scalar()` adapter from `blume/reference` in place of (or beside) this one. The [Scalar](/docs/references/scalar) page covers what the embed does and doesn't do, and how to pass Scalar's own options through.
|