blume 2.0.0 → 2.0.2
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 +110 -0
- package/README.md +2 -2
- package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
- package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
- package/dist/cli/chunk-273ygyr4.js.map +11 -0
- package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
- package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
- package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
- package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
- package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
- package/dist/cli/chunk-5r8g91qn.js.map +34 -0
- package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
- package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
- package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
- package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
- package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
- package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
- package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
- package/dist/cli/chunk-7vtckvaw.js.map +11 -0
- package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
- package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
- package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
- package/dist/cli/chunk-91ws1n6j.js.map +10 -0
- package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
- package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
- package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
- package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
- package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
- package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
- package/dist/cli/chunk-ddndchfr.js.map +14 -0
- package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
- package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
- package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
- package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
- package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
- package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
- package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
- package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
- package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
- package/dist/cli/chunk-hr8ne106.js.map +13 -0
- package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
- package/dist/cli/chunk-j85scx15.js.map +10 -0
- package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
- package/dist/cli/chunk-k7pj68a8.js.map +11 -0
- package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
- package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
- package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
- package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
- package/dist/cli/chunk-nfcyttvj.js.map +10 -0
- package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
- package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
- package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
- package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
- package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
- package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
- package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
- package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
- package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
- package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
- package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
- package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
- package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
- package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
- package/dist/cli/chunk-xh43dwgw.js.map +36 -0
- package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
- package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
- package/dist/cli/index.js +160 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-readability.d.ts +1 -1
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api/paths.d.ts +1 -1
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +14 -7
- package/dist/types/ai/ask.d.ts +43 -43
- package/dist/types/ai/component-markdown.d.ts +4 -4
- package/dist/types/ai/index.d.ts +3 -3
- package/dist/types/ai/openapi-components.d.ts +1 -1
- package/dist/types/ai/relative-links.d.ts +9 -4
- package/dist/types/ai/serializers.d.ts +1 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/visibility.d.ts +1 -1
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +21 -21
- package/dist/types/core/config.d.ts +5 -5
- package/dist/types/core/data.d.ts +3 -3
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +37 -8
- package/dist/types/core/i18n.d.ts +7 -1
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +5 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +57 -18
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +14 -2
- package/dist/types/core/sources/normalize.d.ts +10 -1
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -0
- package/dist/types/core/types.d.ts +9 -0
- package/dist/types/core/unrecognized-keys.d.ts +1 -1
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +31 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +7 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +7 -2
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/documents.d.ts +1 -1
- package/dist/types/search/orama-index.d.ts +1 -1
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +21 -12
- package/docs/03-upgrading.mdx +22 -9
- package/docs/04-migrating.mdx +4 -4
- package/docs/08-faq.mdx +12 -5
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +5 -3
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +5 -5
- package/docs/cli/doctor.mdx +4 -4
- package/docs/cli/evals.mdx +9 -9
- package/docs/cli/index.mdx +6 -3
- package/docs/cli/translate.mdx +8 -8
- package/docs/configuration/analytics.mdx +23 -2
- package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
- package/docs/configuration/customization.mdx +5 -4
- package/docs/configuration/index.mdx +7 -5
- package/docs/configuration/meta.ts +1 -1
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -3
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +5 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -12
- package/docs/content/versioning.mdx +1 -0
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +3 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +5 -3
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +6 -4
- package/docs/discoverability/rss.mdx +3 -1
- package/docs/index.mdx +2 -2
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +3 -3
- package/package.json +1 -1
- package/skills/blume/SKILL.md +5 -5
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +4 -4
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/api/paths.ts +1 -1
- package/src/ai/api/spec.ts +15 -2
- package/src/ai/api-catalog.ts +7 -1
- package/src/ai/ask-context.ts +21 -9
- package/src/ai/ask-data.ts +28 -14
- package/src/ai/ask.ts +84 -71
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/cors.ts +3 -3
- package/src/ai/index.ts +16 -16
- package/src/ai/link-headers.ts +8 -3
- package/src/ai/llms.ts +4 -3
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/server.ts +78 -7
- package/src/ai/openapi-components.ts +1 -1
- package/src/ai/relative-links.ts +78 -10
- package/src/ai/serializers.ts +1 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/visibility.ts +46 -15
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +90 -45
- package/src/astro/module-types.ts +1 -1
- package/src/astro/runtime-deps.ts +6 -6
- package/src/astro/templates.ts +87 -49
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +14 -0
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +1 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/blume-modules.d.ts +2 -2
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +8 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +7 -5
- package/src/cli/commands/eval.ts +4 -10
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/migrate.ts +2 -2
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +12 -9
- package/src/cli/commands/upgrade.ts +13 -2
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/cli/required-secrets.ts +3 -3
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +2 -9
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +9 -3
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tabs.astro +54 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +17 -0
- package/src/components/content/base-href.ts +18 -3
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +36 -9
- package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
- package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
- package/src/components/islands/hooks.ts +21 -14
- package/src/components/islands/webmcp.ts +12 -8
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Header.astro +32 -19
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +6 -1
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +15 -6
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageLayout.astro +28 -14
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/ReferenceLayout.astro +5 -2
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +18 -14
- package/src/components/layout/analytics-client.ts +3 -1
- package/src/components/layout/drawer-inert.ts +1 -1
- package/src/components/openapi/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +21 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +9 -5
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +27 -52
- package/src/components/openapi/description.ts +2 -2
- package/src/components/openapi/helpers.ts +90 -12
- package/src/components/openapi/message-composer.ts +8 -0
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +4 -1
- package/src/components/openapi/operation-model.ts +40 -8
- package/src/components/openapi/panel.ts +29 -5
- package/src/components/openapi/playground-client.ts +37 -7
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +2 -0
- package/src/components/openapi/schema-tree.ts +203 -0
- package/src/components/openapi/snippets.ts +30 -15
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/core/base-path.ts +15 -0
- package/src/core/code-fences.ts +1 -1
- package/src/core/config-input.ts +23 -23
- package/src/core/config.ts +21 -7
- package/src/core/data.ts +3 -3
- package/src/core/diagnostics.ts +217 -30
- package/src/core/graph.ts +120 -11
- package/src/core/i18n-ui.ts +74 -11
- package/src/core/i18n.ts +13 -2
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +17 -17
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +69 -6
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +245 -76
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +23 -1
- package/src/core/request-body.ts +1 -1
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +95 -34
- package/src/core/server-features.ts +2 -2
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +3 -2
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +5 -1
- package/src/core/sources/github-releases.ts +106 -5
- package/src/core/sources/lexical.ts +9 -4
- package/src/core/sources/lower.ts +79 -26
- package/src/core/sources/mdx-remote.ts +1 -0
- package/src/core/sources/normalize.ts +132 -30
- package/src/core/sources/notion.ts +26 -10
- package/src/core/sources/obsidian.ts +17 -3
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +63 -44
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +9 -4
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +17 -5
- package/src/core/ui-packs/bg.ts +17 -5
- package/src/core/ui-packs/bn.ts +17 -5
- package/src/core/ui-packs/ca.ts +17 -5
- package/src/core/ui-packs/cs.ts +17 -5
- package/src/core/ui-packs/da.ts +17 -5
- package/src/core/ui-packs/de.ts +17 -5
- package/src/core/ui-packs/el.ts +17 -5
- package/src/core/ui-packs/es.ts +17 -5
- package/src/core/ui-packs/fa.ts +17 -5
- package/src/core/ui-packs/fi.ts +17 -5
- package/src/core/ui-packs/fr.ts +17 -5
- package/src/core/ui-packs/he.ts +17 -5
- package/src/core/ui-packs/hi.ts +17 -5
- package/src/core/ui-packs/hr.ts +17 -5
- package/src/core/ui-packs/hu.ts +17 -5
- package/src/core/ui-packs/id.ts +17 -5
- package/src/core/ui-packs/it.ts +17 -5
- package/src/core/ui-packs/ja.ts +17 -5
- package/src/core/ui-packs/ko.ts +17 -5
- package/src/core/ui-packs/nl.ts +17 -5
- package/src/core/ui-packs/no.ts +17 -5
- package/src/core/ui-packs/pl.ts +17 -5
- package/src/core/ui-packs/pt-br.ts +17 -5
- package/src/core/ui-packs/pt.ts +17 -5
- package/src/core/ui-packs/ro.ts +17 -5
- package/src/core/ui-packs/ru.ts +17 -5
- package/src/core/ui-packs/sk.ts +17 -5
- package/src/core/ui-packs/sr.ts +17 -5
- package/src/core/ui-packs/sv.ts +17 -5
- package/src/core/ui-packs/th.ts +17 -5
- package/src/core/ui-packs/tr.ts +17 -5
- package/src/core/ui-packs/uk.ts +17 -5
- package/src/core/ui-packs/vi.ts +17 -5
- package/src/core/ui-packs/zh-tw.ts +17 -5
- package/src/core/ui-packs/zh.ts +17 -5
- package/src/core/unrecognized-keys.ts +1 -1
- package/src/core/version-cut.ts +17 -2
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +25 -5
- package/src/deploy/cloudflare-negotiation.ts +23 -4
- package/src/deploy/headers.ts +67 -51
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +1 -0
- package/src/deploy/platforms/netlify.ts +82 -5
- package/src/deploy/platforms/node.ts +7 -5
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +7 -0
- package/src/deploy/platforms/vercel.ts +9 -3
- package/src/deploy/redirects.ts +14 -3
- package/src/deploy/vercel-negotiation.ts +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +55 -8
- package/src/markdown/index.ts +6 -3
- package/src/markdown/relative-links.ts +3 -23
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +20 -4
- package/src/og/derive.ts +145 -4
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +77 -13
- package/src/openapi/render-mdx.ts +10 -1
- package/src/registry/eject.ts +135 -37
- package/src/search/documents.ts +24 -3
- package/src/search/orama-index.ts +1 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +11 -3
- package/src/translate/report.ts +1 -1
- package/src/translate/run.ts +11 -5
- package/src/translate/validate.ts +10 -1
- package/src/translate/work-list.ts +36 -3
- package/src/upgrade/upgrade.ts +37 -5
- package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
- package/dist/cli/chunk-5a2z0198.js.map +0 -10
- package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
- package/dist/cli/chunk-79njf86q.js.map +0 -11
- package/dist/cli/chunk-a9kptbw5.js.map +0 -14
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-bctazmbk.js.map +0 -10
- package/dist/cli/chunk-beat36xx.js.map +0 -10
- package/dist/cli/chunk-d1tadaw7.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-j8mw0za6.js.map +0 -35
- package/dist/cli/chunk-mnqj32sj.js.map +0 -12
- package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
- package/dist/cli/chunk-nk3ts2xk.js +0 -51
- package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
- package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
- package/dist/cli/chunk-tzne8qfq.js.map +0 -10
- package/dist/cli/chunk-xaz13gwg.js.map +0 -182
- package/dist/cli/chunk-z01ze5c1.js.map +0 -10
- package/dist/cli/chunk-z1f5arsg.js.map +0 -36
- package/dist/cli/chunk-zg2gtj10.js.map +0 -35
- /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
- /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
- /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
- /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
- /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
- /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
- /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
- /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
- /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Assistant
|
|
3
3
|
description: An in-page assistant grounded in your docs — suggested questions, custom instructions, retrieval sizing, provider adapters from the Vercel AI Gateway to any OpenAI-compatible endpoint, and the server output it needs.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ Add an assistant that answers reader questions in an in-page chat panel, backed
|
|
|
7
7
|
|
|
8
8
|
```ts blume.config.ts lineNumbers
|
|
9
9
|
ai: {
|
|
10
|
-
|
|
10
|
+
assistant: {
|
|
11
11
|
enabled: true,
|
|
12
12
|
},
|
|
13
13
|
}
|
|
@@ -21,7 +21,7 @@ Seed the empty state with a few starter prompts. Each renders as a clickable sug
|
|
|
21
21
|
|
|
22
22
|
```ts blume.config.ts lineNumbers
|
|
23
23
|
ai: {
|
|
24
|
-
|
|
24
|
+
assistant: {
|
|
25
25
|
enabled: true,
|
|
26
26
|
suggestions: [
|
|
27
27
|
{ label: "What is Blume?", icon: "rocket" },
|
|
@@ -40,7 +40,7 @@ Add your own system-prompt text with `instructions` — identity, language, tone
|
|
|
40
40
|
|
|
41
41
|
```ts blume.config.ts lineNumbers
|
|
42
42
|
ai: {
|
|
43
|
-
|
|
43
|
+
assistant: {
|
|
44
44
|
enabled: true,
|
|
45
45
|
instructions:
|
|
46
46
|
"You are Bloomy, the Acme docs assistant. Answer in the language the question was asked in, and keep answers under three paragraphs.",
|
|
@@ -52,9 +52,9 @@ Your text is **appended to** the built-in instructions rather than replacing the
|
|
|
52
52
|
|
|
53
53
|
## Grounding
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
The assistant is **grounded in your docs**. For each question it retrieves the most relevant pages — using the same lexical [Orama](/docs/configuration/search) index that powers on-page search — and injects them into the model's system prompt, so answers come from your content instead of the model's own knowledge. The assistant is told to answer only from the retrieved pages, to say when something isn't covered, and to cite the pages it drew from.
|
|
56
56
|
|
|
57
|
-
The page the reader is currently on is added to the context first and used to scope retrieval to that page's language, so answers stay relevant to where they are in the docs. Retrieval runs at request time from a snapshot baked into the build, so it works regardless of your [search](/docs/configuration/search) provider — even with `search: false` — and needs no configuration.
|
|
57
|
+
The page the reader is currently on is added to the context first and used to scope retrieval to that page's language — and, on a [versioned](/docs/content/versioning) site, to its docs version — so answers stay relevant to where they are in the docs. Retrieval runs at request time from a snapshot baked into the build, so it works regardless of your [search](/docs/configuration/search) provider — even with `search: false` — and needs no configuration.
|
|
58
58
|
|
|
59
59
|
Grounding is on for every adapter except **[Inkeep](#inkeep)**, which runs its own retrieval over the content you've indexed in its dashboard.
|
|
60
60
|
|
|
@@ -64,7 +64,7 @@ How much documentation a question carries is the biggest lever on how long the r
|
|
|
64
64
|
|
|
65
65
|
```ts blume.config.ts lineNumbers
|
|
66
66
|
ai: {
|
|
67
|
-
|
|
67
|
+
assistant: {
|
|
68
68
|
enabled: true,
|
|
69
69
|
retrieval: {
|
|
70
70
|
maxResults: 3, // fewer pages retrieved per question
|
|
@@ -91,7 +91,7 @@ Already have an API backend for AI? Point the panel at it and keep the docs buil
|
|
|
91
91
|
|
|
92
92
|
```ts blume.config.ts lineNumbers
|
|
93
93
|
ai: {
|
|
94
|
-
|
|
94
|
+
assistant: {
|
|
95
95
|
enabled: true,
|
|
96
96
|
endpoint: "https://api.example.com/v1/docs/ask",
|
|
97
97
|
},
|
|
@@ -115,7 +115,7 @@ The generated endpoint answers the in-page assistant on its own origin. To call
|
|
|
115
115
|
|
|
116
116
|
```ts blume.config.ts lineNumbers
|
|
117
117
|
ai: {
|
|
118
|
-
|
|
118
|
+
assistant: {
|
|
119
119
|
enabled: true,
|
|
120
120
|
cors: ["https://www.example.com"],
|
|
121
121
|
},
|
|
@@ -142,7 +142,7 @@ The content type matters: Astro's cross-site request check rejects a cross-origi
|
|
|
142
142
|
|
|
143
143
|
## Server output required
|
|
144
144
|
|
|
145
|
-
Blume's built-in
|
|
145
|
+
Blume's built-in assistant backend is a server route (`POST /api/ask`), so it can't run on a static build. Name a host adapter from `blume/deploy` to switch to server output:
|
|
146
146
|
|
|
147
147
|
```ts blume.config.ts lineNumbers
|
|
148
148
|
import { vercel } from "blume/deploy";
|
|
@@ -152,7 +152,7 @@ export default defineConfig({
|
|
|
152
152
|
});
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
A static build with
|
|
155
|
+
A static build with the assistant enabled and no external `endpoint` fails fast with a message telling you to set a host adapter. See [Deployment](/docs/deployment) for the adapters.
|
|
156
156
|
|
|
157
157
|
## Adapters
|
|
158
158
|
|
|
@@ -164,7 +164,7 @@ import { openrouter } from "blume/ai";
|
|
|
164
164
|
|
|
165
165
|
export default defineConfig({
|
|
166
166
|
ai: {
|
|
167
|
-
|
|
167
|
+
assistant: {
|
|
168
168
|
enabled: true,
|
|
169
169
|
provider: openrouter({ model: "anthropic/claude-sonnet-4-5" }),
|
|
170
170
|
},
|
|
@@ -194,7 +194,7 @@ import { gateway } from "blume/ai";
|
|
|
194
194
|
|
|
195
195
|
export default defineConfig({
|
|
196
196
|
ai: {
|
|
197
|
-
|
|
197
|
+
assistant: {
|
|
198
198
|
enabled: true,
|
|
199
199
|
provider: gateway({ model: "anthropic/claude-sonnet-4-5" }),
|
|
200
200
|
},
|
|
@@ -214,7 +214,7 @@ import { openrouter } from "blume/ai";
|
|
|
214
214
|
|
|
215
215
|
export default defineConfig({
|
|
216
216
|
ai: {
|
|
217
|
-
|
|
217
|
+
assistant: {
|
|
218
218
|
enabled: true,
|
|
219
219
|
provider: openrouter({
|
|
220
220
|
model: "anthropic/claude-sonnet-4-5",
|
|
@@ -235,7 +235,7 @@ import { llmgateway } from "blume/ai";
|
|
|
235
235
|
|
|
236
236
|
export default defineConfig({
|
|
237
237
|
ai: {
|
|
238
|
-
|
|
238
|
+
assistant: {
|
|
239
239
|
enabled: true,
|
|
240
240
|
provider: llmgateway({ model: "openai/gpt-5.5" }),
|
|
241
241
|
},
|
|
@@ -255,7 +255,7 @@ import { inkeep } from "blume/ai";
|
|
|
255
255
|
|
|
256
256
|
export default defineConfig({
|
|
257
257
|
ai: {
|
|
258
|
-
|
|
258
|
+
assistant: {
|
|
259
259
|
enabled: true,
|
|
260
260
|
provider: inkeep({ model: "inkeep-qa-expert" }),
|
|
261
261
|
},
|
|
@@ -275,7 +275,7 @@ import { openaiCompatible } from "blume/ai";
|
|
|
275
275
|
|
|
276
276
|
export default defineConfig({
|
|
277
277
|
ai: {
|
|
278
|
-
|
|
278
|
+
assistant: {
|
|
279
279
|
enabled: true,
|
|
280
280
|
provider: openaiCompatible({
|
|
281
281
|
baseUrl: "https://my-gateway.example.com/v1",
|
|
@@ -314,7 +314,7 @@ provider: gateway({
|
|
|
314
314
|
}),
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
Blume maps only the options it names (`model`, `reasoning`, `apiKeyEnv`, `headers`) and forwards `providerOptions` verbatim, so it has to be JSON — it's inlined into the route — and it has to use the key the underlying provider expects (`openai` for an OpenAI model behind the gateway, `openrouter` on OpenRouter). Enabling
|
|
317
|
+
Blume maps only the options it names (`model`, `reasoning`, `apiKeyEnv`, `headers`) and forwards `providerOptions` verbatim, so it has to be JSON — it's inlined into the route — and it has to use the key the underlying provider expects (`openai` for an OpenAI model behind the gateway, `openrouter` on OpenRouter). Enabling the assistant also turns on React for the in-page island — see [Customization](/docs/configuration/customization#interactive-islands).
|
|
318
318
|
|
|
319
319
|
## Reasoning
|
|
320
320
|
|
|
@@ -324,7 +324,7 @@ Reasoning models think before they answer, and how much they do so by default va
|
|
|
324
324
|
provider: gateway({ model: "openai/gpt-5.5", reasoning: "none" }),
|
|
325
325
|
```
|
|
326
326
|
|
|
327
|
-
Each adapter sends the level as its backend's own reasoning control, which is why it lives on the adapter rather than on `
|
|
327
|
+
Each adapter sends the level as its backend's own reasoning control, which is why it lives on the adapter rather than on `assistant`:
|
|
328
328
|
|
|
329
329
|
| Adapter | What the level becomes |
|
|
330
330
|
| --- | --- |
|
|
@@ -348,7 +348,7 @@ With an [analytics provider](/docs/configuration/analytics) configured, the assi
|
|
|
348
348
|
|
|
349
349
|
`path` is the page the reader asked from (the served pathname, so it matches the feedback widget and your pageviews under a `base`), `questionChars` the question's length, `ms` the time from sending the question to the last chunk, and `chars` the answer's length. `status` is the HTTP status: `0` when no response arrived at all (offline, DNS, CORS), and `200` when the response was fine but its stream broke mid-answer — how a provider or credential error surfaces, since the backend has already sent its headers — or delivered nothing. Clearing the conversation mid-answer reports neither outcome.
|
|
350
350
|
|
|
351
|
-
The question's text never reaches a provider: it is free-form reader input (pasted keys, error logs, names) that would breach most providers' terms and per-value size limits. It travels only on the `blume:track` DOM event, as `question` in `detail.props`, so a listener you write can forward it wherever you decide it belongs. A custom chat UI built on `
|
|
351
|
+
The question's text never reaches a provider: it is free-form reader input (pasted keys, error logs, names) that would breach most providers' terms and per-value size limits. It travels only on the `blume:track` DOM event, as `question` in `detail.props`, so a listener you write can forward it wherever you decide it belongs. A custom chat UI built on `useAssistant` from `blume/hooks` reports the same events. With no provider configured the built-in provider calls are no-ops, but the `blume:track` event still fires, so a custom integration listening for it receives them.
|
|
352
352
|
|
|
353
353
|
## Rate limiting
|
|
354
354
|
|
|
@@ -91,12 +91,13 @@ Wired slots:
|
|
|
91
91
|
| `Layout` | The entire page shell (`RootLayout`) | Everything the built-in layout receives, plus the `layout` map |
|
|
92
92
|
| `Header` | The top navigation bar | `site`, `logo`, `navigation`, `route`, `searchEnabled`, … |
|
|
93
93
|
| `Logo` | The brand link (mark + title) in the header | `site`, `logo`, `locale` |
|
|
94
|
-
| `Search` | The header search trigger + modal | `navigation`, `strings`, `locale`, `
|
|
94
|
+
| `Search` | The header search trigger + modal | `navigation`, `strings`, `locale`, `assistantEnabled` |
|
|
95
95
|
| `Sidebar` | The primary navigation tree | `items`, `currentRoute` |
|
|
96
96
|
| `MobileNav` | The nav inside the mobile drawer (defaults to `Sidebar`) | `items`, `currentRoute` |
|
|
97
97
|
| `Breadcrumbs` | The breadcrumb trail | `crumbs` |
|
|
98
98
|
| `TableOfContents` | The on-this-page outline | `headings`, `title`, `variant` |
|
|
99
99
|
| `Pagination` | The prev/next footer links | `prev`, `next`, `strings` |
|
|
100
|
+
| `Feedback` | The "Was this page helpful?" rating below the article (rendered only when [`feedback`](/docs/configuration#page-feedback) is on) | `strings` |
|
|
100
101
|
| `PageHeader` | An injection point above the article (no built-in) | `page`, `headings`, `route` |
|
|
101
102
|
| `PageFooter` | An injection point below the article (no built-in) | `page`, `headings`, `route` |
|
|
102
103
|
| `Footer` | A site-wide footer after the content grid (no built-in) | `site`, `navigation`, `ui` |
|
|
@@ -184,10 +185,10 @@ npx blume eject --yes
|
|
|
184
185
|
|
|
185
186
|
Eject is a one-way step: the hidden `.blume/` runtime becomes a normal Astro app you own and can modify directly. The `blume` package stays importable, so you keep its components, theme, and Markdown processors.
|
|
186
187
|
|
|
187
|
-
Eject points your `package.json` scripts at `astro dev` and `astro build`, and adds the packages the Astro app imports by name — `astro`, `@tailwindcss/vite`, the search client, integrations, and adapter the config wires in, React when an island, example, or
|
|
188
|
+
Eject points your `package.json` scripts at `astro dev` and `astro build`, and adds the packages the Astro app imports by name — `astro`, `@tailwindcss/vite`, `tailwindcss` and `@tailwindcss/typography` for the generated stylesheets, the search client, integrations, and adapter the config wires in, React when an island, example, or the assistant uses it, `ai` for the assistant route, and `epub-gen-memory` for EPUB export — at the ranges Blume itself uses. Run an install before `dev` or `build`; eject lists what it added. Every path in the ejected app is relative, so it builds from any checkout, CI included.
|
|
188
189
|
|
|
189
|
-
From then on, run the app through its own scripts (`npm run dev`, `npm run build`): `blume dev` and `blume
|
|
190
|
+
From then on, run the app through its own scripts (`npm run dev`, `npm run build`): `blume dev`, `blume build`, `blume check`, `blume sync`, and `blume preview` stop in an ejected project and point you there. Running `blume eject` again refuses too, since it would overwrite your edits; pass `--force` to regenerate the app anyway.
|
|
190
191
|
|
|
191
192
|
### What eject keeps
|
|
192
193
|
|
|
193
|
-
The ejected app's `build` script runs plain `astro build`, and the artifacts `blume build` layers on top — the search index (and a hosted provider's index sync), `llms.txt` and `llms-full.txt`, `sitemap.xml`, `robots.txt`, `agent-readability.json`, the `.well-known` discovery files, Agent Skills, and the platform `_redirects`/`_headers` files — are still produced: the Blume integration in the ejected `astro.config.mjs` writes them from Astro's `astro:build:done` hook, scanning the project (your `blume.config.ts` and content) the way the CLI did. What the ejected build does not do is the CLI's adapter post-processing: the Vercel and Cloudflare `Accept: text/markdown` routing splices, the Vercel function-bundle audit, and the `--analyze`/`--budget-*` gate.
|
|
194
|
+
The ejected app's `build` script runs plain `astro build`, and the artifacts `blume build` layers on top — the search index (and a hosted provider's index sync), `llms.txt` and `llms-full.txt`, `sitemap.xml`, `robots.txt`, `agent-readability.json`, the `.well-known` discovery files, Agent Skills, and the platform `_redirects`/`_headers` files — are still produced: the Blume integration in the ejected `astro.config.mjs` writes them from Astro's `astro:build:done` hook, scanning the project (your `blume.config.ts` and content) the way the CLI did. What the ejected build does not do is the CLI's adapter post-processing: the Vercel and Cloudflare `Accept: text/markdown` routing splices, the Vercel function-bundle audit, the `node()` server-entry wrapper (the `.well-known` discovery files' media types and CORS headers, the sandbox on downloaded SVGs, and each redirect's exact status), the header rules a `netlify()` server build writes into `.netlify/v1/config.json`, Cloudflare's Worker naming (after your project) and its `.wrangler/deploy` redirect for running `wrangler deploy` from the project root, and the `--analyze`/`--budget-*` gate.
|
|
@@ -125,7 +125,7 @@ logo: {
|
|
|
125
125
|
`text` controls the wordmark independently of the mark:
|
|
126
126
|
|
|
127
127
|
- **Omit `text`** and the brand uses your site `title` (the default).
|
|
128
|
-
- **Set `text: ""`** to show the mark alone — handy when the logo image already includes the wordmark.
|
|
128
|
+
- **Set `text: ""`** to show the mark alone — handy when the logo image already includes the wordmark. Screen readers then announce the brand link by the image's `alt`, or by your site `title` when it has none.
|
|
129
129
|
- **Set `text` with no `image`** for a text-only logo.
|
|
130
130
|
|
|
131
131
|
### Favicon
|
|
@@ -272,7 +272,7 @@ A key can be declared site-wide or per-type, not both. See [Per-type keys](/docs
|
|
|
272
272
|
|
|
273
273
|
## GitHub
|
|
274
274
|
|
|
275
|
-
Point Blume at your repository with `github`. It powers the header [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub**
|
|
275
|
+
Point Blume at your repository with `github`. It powers the header [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub** [page action](/docs/content/navigation#page-actions):
|
|
276
276
|
|
|
277
277
|
```ts blume.config.ts lineNumbers
|
|
278
278
|
github: {
|
|
@@ -364,9 +364,11 @@ dateFormat: { year: "numeric", month: "2-digit", day: "2-digit" },
|
|
|
364
364
|
| `timeZone` | IANA time zone. Defaults to `UTC`, so a date reads the same regardless of where the site builds. |
|
|
365
365
|
| `calendar`, `numberingSystem` | Calendar system (e.g. `"japanese"`) and numbering system (e.g. `"arab"`). |
|
|
366
366
|
|
|
367
|
+
`timeZone`, `calendar`, and `numberingSystem` don't change the shape: a `dateFormat` that sets only those keeps the long form.
|
|
368
|
+
|
|
367
369
|
## SEO and agents
|
|
368
370
|
|
|
369
|
-
Metadata, Open Graph images, RSS feeds, JSON-LD, the sitemap, and `robots.txt` live under `seo`; `llms.txt`, raw Markdown, the JSON API, the MCP server, and the discovery manifests live under `agents`. Both are covered page by page in the [Discoverability](/docs/discoverability) section, which treats search engines and AI agents as two audiences for the same machine-readable layer. The reader-facing model features — the
|
|
371
|
+
Metadata, Open Graph images, RSS feeds, JSON-LD, the sitemap, and `robots.txt` live under `seo`; `llms.txt`, raw Markdown, the JSON API, the MCP server, and the discovery manifests live under `agents`. Both are covered page by page in the [Discoverability](/docs/discoverability) section, which treats search engines and AI agents as two audiences for the same machine-readable layer. The reader-facing model features — the assistant and the Open in chat action — live under `ai`.
|
|
370
372
|
|
|
371
373
|
```ts blume.config.ts lineNumbers
|
|
372
374
|
seo: {
|
|
@@ -381,7 +383,7 @@ seo: {
|
|
|
381
383
|
| Option | Default | Description |
|
|
382
384
|
| --- | --- | --- |
|
|
383
385
|
| `og.enabled` | auto | Per-page Open Graph images — on when a site URL is set. |
|
|
384
|
-
| `rss.enabled` | `true` | Build feeds for blog and changelog content. |
|
|
386
|
+
| `rss.enabled` | `true` | Build feeds for blog and changelog content (needs deployment.site). |
|
|
385
387
|
| `rss.types` | `["blog", "changelog"]` | Content types that each get a feed. |
|
|
386
388
|
| `rss.limit` | `50` | Maximum items per feed. |
|
|
387
389
|
| `sitemap` | `true` | Generate sitemap.xml (needs deployment.site). |
|
|
@@ -431,7 +433,7 @@ Each of these has its own guide. The config field is the entry point:
|
|
|
431
433
|
| `search` | Provider (Orama, Pagefind, Algolia, and more) and indexing | [Search](/docs/configuration/search) |
|
|
432
434
|
| `markdown` | Markdown rendering options — code blocks, heading anchors, image zoom | [Syntax](/docs/content/syntax) |
|
|
433
435
|
| `agents` | `llms.txt`, Markdown mirrors, the JSON API, the hosted MCP server, skills, and discovery manifests for coding agents | [SEO and AEO](/docs/discoverability) |
|
|
434
|
-
| `ai` | The in-page
|
|
436
|
+
| `ai` | The in-page assistant and the Open in chat action | [Assistant](/docs/configuration/assistant) |
|
|
435
437
|
| `reference` | API references: `openapi()`, `asyncapi()`, `graphql()`, and `scalar()` adapters from `blume/reference` | [OpenAPI](/docs/references/openapi), [AsyncAPI](/docs/references/asyncapi), [GraphQL](/docs/references/graphql), [Scalar](/docs/references/scalar) |
|
|
436
438
|
| `analytics` | Adapters from `blume/analytics` — PostHog, Google Analytics, Plausible, Mixpanel, Segment, and more — plus custom scripts | [Analytics](/docs/configuration/analytics) |
|
|
437
439
|
| `seo` | Metadata, OG images, feeds, structured data, sitemap, robots | [SEO and AEO](/docs/discoverability) |
|
|
@@ -115,7 +115,7 @@ i18n: {
|
|
|
115
115
|
}
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
The same tokenizer serves the search dialog, the MCP server's `search_docs` tool, and
|
|
118
|
+
The same tokenizer serves the search dialog, the MCP server's `search_docs` tool, and assistant grounding. The script is what decides, not the language name — `az-Cyrl` is segmented while `sr-Latn` is not — and it is the default locale that decides for the whole index: on a mixed-language site every page shares the default locale's tokenizer. With a non-Latin default that's safe, because Latin words survive segmentation intact, so pages in English stay searchable alongside the default language. The reverse doesn't hold: non-Latin translations on a Latin-default site aren't searchable. Latin-script languages that lean heavily on diacritics (Vietnamese, or Serbian in Latin script) also fare worse on the standard tokenizer, which folds only a few accented vowels and splits words on the rest.
|
|
119
119
|
|
|
120
120
|
Japanese and Chinese go one step further. Segmenting alone indexes a compound term as its parts — 資金決済法 as 資金, 決済 and 法 — which lets a page mentioning each part somewhere outrank the page the term is actually about. Han, Hiragana and Katakana are therefore indexed as overlapping character pairs, and queries on those indexes prefer pages carrying a term's pairs together, loosening to any-pair matching when no page carries them all, so typing a whole sentence still returns its closest pages. Korean and Thai keep their segmented words.
|
|
121
121
|
|
|
@@ -145,7 +145,7 @@ Pagefind only runs during `blume build`, so search isn't available in `blume dev
|
|
|
145
145
|
|
|
146
146
|
### Algolia
|
|
147
147
|
|
|
148
|
-
The browser queries [Algolia](https://www.algolia.com) directly with your search-only key. Each `blume build` replaces the index using the admin key from `ALGOLIA_ADMIN_API_KEY` (the build warns and skips the upload if it's unset). The whole index is replaced on every sync, so pages you delete or rename don't linger as stale results.
|
|
148
|
+
The browser queries [Algolia](https://www.algolia.com) directly with your search-only key. Each `blume build` replaces the index using the admin key from `ALGOLIA_ADMIN_API_KEY` (the build warns and skips the upload if it's unset). The whole index is replaced on every sync, so pages you delete or rename don't linger as stale results. The sync also adds `filterOnly(locale)` and `filterOnly(version)` to the index's `attributesForFaceting`, keeping any facets you declared yourself, because the dialog scopes results by language and docs version, and in Algolia a filter on an attribute that isn't declared for faceting matches nothing.
|
|
149
149
|
|
|
150
150
|
```ts blume.config.ts lineNumbers
|
|
151
151
|
import { algolia } from "blume/search";
|
|
@@ -560,7 +560,7 @@ export interface ButtonProps {
|
|
|
560
560
|
|
|
561
561
|
## GitHub info
|
|
562
562
|
|
|
563
|
-
A card linking to a GitHub repository with its
|
|
563
|
+
A card linking to a GitHub repository with its star and fork counts. The counts are fetched at build time — no client JavaScript — so they show the numbers as of your last build, not live ones; the card still renders without them if the API is unreachable. Pass `owner` and `repo`, or omit them to use the repository from your `blume.config`. Set a `GITHUB_TOKEN` environment variable to lift the API rate limit; a `token` prop overrides it for one card, but the environment variable keeps the token out of your content.
|
|
564
564
|
|
|
565
565
|
The card reads the instance from [`github.host`](/docs/configuration#github-enterprise), so on an Enterprise-hosted site explicit `owner`/`repo` address that instance too. Pass `host` to point one card somewhere else — a public project from an Enterprise site, say; the REST base is derived from it the same way it is from `github.host`.
|
|
566
566
|
|
|
@@ -23,7 +23,11 @@ Every page accepts the following frontmatter. All fields are optional.
|
|
|
23
23
|
description:
|
|
24
24
|
"Post author(s) for blog/changelog content — a name, or objects with a name plus optional avatar/url and any extra fields. Preserved as-is.",
|
|
25
25
|
},
|
|
26
|
-
slug: {
|
|
26
|
+
slug: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description:
|
|
29
|
+
"Set the page's full route from the content root (guides/setup). It replaces the whole path the file's location gives the page, not just the last segment.",
|
|
30
|
+
},
|
|
27
31
|
draft: {
|
|
28
32
|
type: "boolean",
|
|
29
33
|
default: "false",
|
package/docs/content/i18n.mdx
CHANGED
|
@@ -81,7 +81,7 @@ i18n: {
|
|
|
81
81
|
|
|
82
82
|
## Per-locale navigation
|
|
83
83
|
|
|
84
|
-
Each language gets its own sidebar, built from that locale's files — so translations can diverge in structure, ordering, or labels. Folder [`meta.ts`](/docs/content/meta) files resolve per locale, too: under the default `dir` parser, put a `meta.ts` under `fr/guides/` to order the French group independently. Under the `dot` parser translations sit next to the originals, so a folder's `meta.ts` applies to every locale. Everything else about [navigation](/docs/content/navigation) works the same, per language.
|
|
84
|
+
Each language gets its own sidebar, built from that locale's files — so translations can diverge in structure, ordering, or labels. Folder [`meta.ts`](/docs/content/meta) files resolve per locale, too: under the default `dir` parser, put a `meta.ts` under `fr/guides/` to order the French group independently. Until a locale has one (or a shared `meta.$.ts`), its group mirrors the [fallback](#fallbacks) locale's — that folder's `meta.ts` and the `sidebar.display` its index page sets — so pages that fall back keep the same titles, order, and collapsible groups. Under the `dot` parser translations sit next to the originals, so a folder's `meta.ts` applies to every locale. Everything else about [navigation](/docs/content/navigation) works the same, per language.
|
|
85
85
|
|
|
86
86
|
Header tabs are configured, not derived from content, so their labels localize in `blume.config.ts`: a tab `label` accepts a per-locale map (`{ en: "Docs", fr: "Documentation" }`) alongside the plain-string form, falling back to the default locale's entry for locales you haven't filled in. See [Tabs](/docs/content/navigation#tabs). Tab paths, header links, and the header logo's link move into the reader's locale too, whenever that locale serves the route, so the header stays inside one language. A route only the default locale serves — a [custom page](/docs/advanced/custom-pages) or the generated [changelog](/docs/advanced/changelog) index — keeps its own path instead of pointing at a localized URL that would 404.
|
|
87
87
|
|
|
@@ -112,10 +112,10 @@ Anchors travel with the link, so heading ids have to agree across languages. [`b
|
|
|
112
112
|
|
|
113
113
|
## Translating with an agent
|
|
114
114
|
|
|
115
|
-
You don't have to fill in the locales by hand. [`blume translate`](/docs/cli/translate) finds every page that's missing or outdated in each locale and translates it with a local agent CLI ([
|
|
115
|
+
You don't have to fill in the locales by hand. [`blume translate`](/docs/cli/translate) finds every page that's missing or outdated in each locale and translates it with a local agent CLI ([Codex](https://developers.openai.com/codex/cli) or [Claude Code](https://claude.com/claude-code)):
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
blume translate --
|
|
118
|
+
blume translate --codex
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
Blume validates each result's structure — frontmatter, code fences, links — and writes the files itself; the agent only translates text. A committed ledger (`blume.translations.json`) tracks which source revision each translation came from, so reruns only touch what changed, and translations you wrote by hand are adopted as-is, never overwritten. In CI, `blume translate --check` fails when a source page has drifted ahead of its translations.
|
package/docs/content/index.mdx
CHANGED
|
@@ -31,13 +31,15 @@ Nested folders become nested routes, and an `index.mdx` inside a folder becomes
|
|
|
31
31
|
|
|
32
32
|
## Ordering with numeric prefixes
|
|
33
33
|
|
|
34
|
-
Prefix a file or folder with a number to control its order in the sidebar. The prefix is stripped from the URL, so you can reorder pages without breaking links:
|
|
34
|
+
Prefix a file or folder with a number and a `-`, `_`, or `.` to control its order in the sidebar. The prefix is stripped from the URL, so you can reorder pages without breaking links:
|
|
35
35
|
|
|
36
36
|
```txt
|
|
37
37
|
01-introduction.mdx -> /introduction
|
|
38
38
|
02-installation.mdx -> /installation
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
A version or an ISO date at the start of a name is part of the name, not an order: `1.2.0.mdx` routes to `/1.2.0`, and `2024-01-05-launch.mdx` to `/2024-01-05-launch`. Only file and folder names lose a prefix (an Obsidian vault's notes count as files). A frontmatter `slug`, and a page from a [content source](/docs/content/sources) such as a CMS or GitHub Releases, keep the name they were given.
|
|
42
|
+
|
|
41
43
|
Ordering has several layers — see [Navigation](/docs/content/navigation) for the full precedence rules.
|
|
42
44
|
|
|
43
45
|
## Group folders
|
|
@@ -108,7 +110,7 @@ Every page gets an automatic table of contents, built from its headings. On wide
|
|
|
108
110
|
|
|
109
111
|
Blume slugifies each heading into an anchor, so every entry links straight to its section — and you can deep-link to any heading by appending its slug to the URL (`.../my-page#getting-started`).
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
By default the contents list your `##` and `###` headings (H2 and H3); set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the table of contents off. A page with no headings in that range simply has no table of contents.
|
|
112
114
|
|
|
113
115
|
## Where to next
|
|
114
116
|
|
package/docs/content/islands.mdx
CHANGED
|
@@ -143,7 +143,7 @@ export default function PageInfo() {
|
|
|
143
143
|
| `useBlume()` | `{ config, navigation }` for the site, or `null` before mount |
|
|
144
144
|
| `usePage()` | `{ route, title }` for the current page, or `null` before mount |
|
|
145
145
|
| `useSearch()` | `{ search, results, loading }` — query the configured search provider |
|
|
146
|
-
| `
|
|
146
|
+
| `useAssistant()` | `{ ask, messages, loading, reset }` — stream from the assistant endpoint |
|
|
147
147
|
|
|
148
148
|
`useBlume()` and `usePage()` return `null` until the island mounts (so server and client render the same first frame) — guard for it. The snapshot is emitted only on pages that ship React, so a fully static site pays nothing.
|
|
149
149
|
|
package/docs/content/meta.mdx
CHANGED
|
@@ -23,6 +23,8 @@ 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: ["src/**"]`, an unrelated `src/lib/meta.ts` is left alone.
|
|
27
|
+
|
|
26
28
|
## Fields
|
|
27
29
|
|
|
28
30
|
| Field | Type | Description |
|
|
@@ -34,7 +36,7 @@ Every field is optional — set only what you want to override.
|
|
|
34
36
|
| `display` | `"flat" \| "group" \| "page"` | Render mode for this group; overrides the global [`navigation.sidebar.display`](/docs/content/navigation#display-modes). |
|
|
35
37
|
| `pages` | `string[]` | Explicit order for the group's children, by slug. |
|
|
36
38
|
|
|
37
|
-
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"`). Children you leave out still appear,
|
|
39
|
+
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.
|
|
38
40
|
|
|
39
41
|
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).
|
|
40
42
|
|
|
@@ -53,13 +55,13 @@ export default defineMeta(async () => ({
|
|
|
53
55
|
|
|
54
56
|
## Ordering within a group
|
|
55
57
|
|
|
56
|
-
The `pages` array sets the order of a group's children. Anything it omits falls back to each page's frontmatter `sidebar.order`, then the file system (an `index` page first, then numeric prefixes, then alphabetical). For the full sidebar precedence — including an explicit config sidebar — see [Navigation › Ordering](/docs/content/navigation#ordering).
|
|
58
|
+
The `pages` array sets the order of a group's children, and it wins over a listed page's own `sidebar.order` or a listed subfolder's own `order`. Anything it omits falls back to each page's frontmatter `sidebar.order`, then the file system (an `index` page first, then numeric prefixes, then alphabetical). For the full sidebar precedence — including an explicit config sidebar — see [Navigation › Ordering](/docs/content/navigation#ordering).
|
|
57
59
|
|
|
58
60
|
To group pages _without_ adding a URL segment, you don't need a `meta.ts` at all: use a parenthesized folder name — see [Pages › Group folders](/docs/content#group-folders).
|
|
59
61
|
|
|
60
62
|
## Internationalization
|
|
61
63
|
|
|
62
|
-
Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a `meta.ts` under `fr/guides/` to order the French group independently.
|
|
64
|
+
Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a `meta.ts` under `fr/guides/` to order the French group independently. Without one (or a shared `meta.$.ts`, below), the French group mirrors the fallback locale's `meta.ts`.
|
|
63
65
|
|
|
64
66
|
For folder meta that's identical in every language, add a `$` marker so one file serves all locales without duplication:
|
|
65
67
|
|
|
@@ -150,7 +150,9 @@ navigation: {
|
|
|
150
150
|
}
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
A tab's optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG) shows beside its label, in the header and in the mobile navigation drawer.
|
|
154
|
+
|
|
155
|
+
An enabled [OpenAPI](/docs/references/openapi), [AsyncAPI](/docs/references/asyncapi), or [GraphQL](/docs/references/graphql) reference mounts at its route but doesn't add a tab on its own — point a tab at that route to surface it in the header (and, for the native renderer, to scope its operations sidebar), with whatever label you like:
|
|
154
156
|
|
|
155
157
|
```ts blume.config.ts
|
|
156
158
|
navigation: {
|
|
@@ -174,6 +176,24 @@ navigation: {
|
|
|
174
176
|
|
|
175
177
|
Tabs that don't set `href` keep the resolution above.
|
|
176
178
|
|
|
179
|
+
Give a tab `items` to make it a dropdown. The tab no longer links anywhere itself: it opens a menu of its items in the header, and expands them in place in the mobile navigation drawer. Its `path` still scopes the sidebar and marks the tab as current, and `href` doesn't apply. Each item takes a `label` and a `path`, plus an optional `icon`, `description`, and `tag`, like a [selector](#selectors) item:
|
|
180
|
+
|
|
181
|
+
```ts blume.config.ts lineNumbers
|
|
182
|
+
navigation: {
|
|
183
|
+
tabs: [
|
|
184
|
+
{ label: "Guides", path: "/guides" },
|
|
185
|
+
{
|
|
186
|
+
label: "SDKs",
|
|
187
|
+
path: "/sdks",
|
|
188
|
+
items: [
|
|
189
|
+
{ label: "JavaScript", path: "/sdks/javascript", description: "Node and the browser" },
|
|
190
|
+
{ label: "Python", path: "/sdks/python", tag: "Beta" },
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
177
197
|
On an [i18n](/docs/content/i18n) site, a tab's `label` (and a dropdown item's) can be a per-locale map instead of a string — the active locale's entry wins, then the default locale's:
|
|
178
198
|
|
|
179
199
|
```ts blume.config.ts
|
|
@@ -185,6 +205,8 @@ navigation: {
|
|
|
185
205
|
}
|
|
186
206
|
```
|
|
187
207
|
|
|
208
|
+
A [selector](#selectors)'s labels don't take a map: its `label` and each of its items' labels are plain strings.
|
|
209
|
+
|
|
188
210
|
Tabs also **scope the sidebar**: when the current route falls under a tab's `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the adapters and nothing else. The folder at a tab's `path` becomes the section, so this needs no extra config beyond the tabs themselves; structure your content into a folder per tab and point each tab at it.
|
|
189
211
|
|
|
190
212
|
On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the pages that _don't_ belong to a tab — each tab's folder is hidden from it, since that section already has its own tab in the header. So a root landing page lists your loose top-level pages while the sectioned content stays behind its tab, mirroring Fumadocs' root folders. If a route has no pages of its own to show this way, the full tree is shown instead, so the sidebar is never left blank.
|
|
@@ -247,6 +269,8 @@ navigation: {
|
|
|
247
269
|
|
|
248
270
|
Each item is a page route (a string), a group (`label` + `items`), or a link (`label` + `href`). Groups can nest, override the global [`display` mode](#display-modes), and start `collapsed`.
|
|
249
271
|
|
|
272
|
+
Blume warns about an item it can't render as written: a route that matches no page (the item is left out), a `root` that matches no page (its link would 404), or an item with no route, `href`, `root`, or `items` (left out).
|
|
273
|
+
|
|
250
274
|
## Header actions
|
|
251
275
|
|
|
252
276
|
`navigation.actions` puts plain links in the header, left of the icon buttons, and `navigation.cta` is the one filled button:
|
|
@@ -293,7 +317,7 @@ These come for free from the sidebar tree — no configuration:
|
|
|
293
317
|
|
|
294
318
|
## On this page
|
|
295
319
|
|
|
296
|
-
A right-rail outline is generated automatically from each page's `##` and `###`
|
|
320
|
+
A right-rail outline is generated automatically from each page's headings — `##` and `###` by default — so long pages stay scannable. Set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the outline off. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.
|
|
297
321
|
|
|
298
322
|
## Page actions
|
|
299
323
|
|
|
@@ -301,8 +325,9 @@ Below the table of contents, every page shows a set of quick actions:
|
|
|
301
325
|
|
|
302
326
|
- **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
|
|
303
327
|
- **Scroll to top** — smoothly returns to the top of long pages.
|
|
304
|
-
- **Give feedback** — opens a prefilled GitHub issue with an optional reaction and note (also requires `github`).
|
|
305
328
|
|
|
306
|
-
Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [
|
|
329
|
+
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).
|
|
330
|
+
|
|
331
|
+
Feedback lives at the foot of the page instead: a "Was this page helpful?" yes/no rating that sends a `feedback` analytics event and doesn't need `github` — see [Page feedback](/docs/configuration#page-feedback).
|
|
307
332
|
|
|
308
333
|
With [`export`](/docs/configuration/export) on, an **Export** action also lets readers download the page as a PDF or EPUB.
|
package/docs/content/sources.mdx
CHANGED
|
@@ -87,7 +87,7 @@ A heading that itself contains a link gets its manifest anchor from the heading'
|
|
|
87
87
|
|
|
88
88
|
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.
|
|
89
89
|
|
|
90
|
-
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 or deleting a note to the trash doesn't rebuild your site. Editing a note does. 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. 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
|
|
90
|
+
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 or deleting a note to the trash doesn't rebuild your site. Editing a note does. 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. 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.
|
|
91
91
|
|
|
92
92
|
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.
|
|
93
93
|
|
|
@@ -109,9 +109,9 @@ Remote pages are rendered with full MDX-plus-component fidelity: their bodies ar
|
|
|
109
109
|
|
|
110
110
|
### Caching and offline builds
|
|
111
111
|
|
|
112
|
-
Each remote source keeps a snapshot under `.blume/cache/<source>/`. If a fetch fails — a network blip or a CMS outage — Blume serves the last-known-good snapshot with a warning rather than failing the build. The cache lives inside `.blume/` and is regenerated, never committed.
|
|
112
|
+
Each remote source keeps a snapshot under `.blume/cache/<source>/`. If a fetch fails — a network blip or a CMS outage — Blume serves the last-known-good snapshot with a warning rather than failing the build. Preview and published content get separate snapshots, and so does each set of source options, so a build never falls back to drafts fetched under `--preview`, and editing a source's `query` or `fields` fetches afresh. The cache lives inside `.blume/` and is regenerated, never committed.
|
|
113
113
|
|
|
114
|
-
In dev, remote
|
|
114
|
+
In dev, a remote source is served from its snapshot when it has one, so restarting the dev server doesn't refetch it. Run `blume sync` to pull the latest content (a running dev server hot-reloads), or `blume sync --force` to drop the snapshots first. Local filesystem sources hot-reload as usual. To poll a remote source for changes instead, set the shared `pollInterval` option (seconds) on it — the dev server re-fetches on that interval and reloads only when the content actually changes. Leave it unset to avoid hitting the API while you work.
|
|
115
115
|
|
|
116
116
|
## GitHub Releases
|
|
117
117
|
|
|
@@ -138,7 +138,7 @@ export default defineConfig({
|
|
|
138
138
|
});
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
Each release maps to the changelog fields automatically: its name (or tag) becomes the title, its published date drives the timeline order, the tag becomes `changelog.version`, and prereleases are tagged `Prerelease` (others `Release`). The notes render as the entry body. Give the source a `prefix` so its release pages nest under a route like `/changelog/v1-2-0`.
|
|
141
|
+
Each release maps to the changelog fields automatically: its name (or tag) becomes the title, its published date drives the timeline order, the tag becomes `changelog.version`, and prereleases are tagged `Prerelease` (others `Release`). The notes render as the entry body, with two changes to their links: a link that isn't a web, mail, phone, or relative address (a `javascript:` URL, say) keeps only its label, and a link back to your own [`deployment.site`](/docs/deployment) is rewritten to its root-relative path, so it follows preview deploys and your deployment base. Give the source a `prefix` so its release pages nest under a route like `/changelog/v1-2-0`.
|
|
142
142
|
|
|
143
143
|
A private repo authenticates with the `GITHUB_TOKEN` environment variable — the same token the other GitHub features use, never inlined into your config; the adapter declares it, so a build without it warns. Like every remote source it's cached under `.blume/cache/<source>/` and served offline if the API is unreachable. Because a changelog is supplementary, a fetch failure with no cache (say a CI build without a token) degrades to an empty changelog with a warning rather than failing the build — set `GITHUB_TOKEN` in your CI and deploy environments to populate it.
|
|
144
144
|
|
|
@@ -171,7 +171,7 @@ A read token for a private dataset comes from the `SANITY_TOKEN` environment var
|
|
|
171
171
|
|
|
172
172
|
## Notion
|
|
173
173
|
|
|
174
|
-
The built-in `notion()` adapter turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components, and the text you type in Notion renders as written: a `{`, `<`, or Markdown character in a page is escaped rather than read as MDX, JSX, or formatting. Video blocks become a `<YouTube>` embed when they hold a YouTube link and a `<video>` player otherwise, with the block's caption as a `<Frame>` caption either way
|
|
174
|
+
The built-in `notion()` adapter turns a Notion database into a collection: each row becomes a page, its properties become frontmatter, and its block tree becomes MDX. Callouts, toggles, columns, and code blocks map to the matching Blume components, and the text you type in Notion renders as written: a `{`, `<`, or Markdown character in a page is escaped rather than read as MDX, JSX, or formatting. Video blocks become a `<YouTube>` embed when they hold a YouTube link and a `<video>` player otherwise, with the block's caption as a `<Frame>` caption either way. A link to a video page rather than a media file (a Vimeo or Loom URL, say) is reported as a build warning, and its `<video>` player keeps pointing at the page, which it can't play — link to that video from the text instead. The adapter declares `@notionhq/client` (v5 or later) as its runtime dependency — an optional peer; Blume reads the database through its first data source.
|
|
175
175
|
|
|
176
176
|
```ts blume.config.ts
|
|
177
177
|
import { defineConfig } from "blume";
|
|
@@ -184,20 +184,24 @@ export default defineConfig({
|
|
|
184
184
|
notion({
|
|
185
185
|
prefix: "handbook",
|
|
186
186
|
database: "8f2c1e0a4b7d4f3c9e6a5d2b1c0f9e8d", // the id in the database URL
|
|
187
|
-
// Property names default to the title-typed prop / Description / Slug / Order
|
|
188
|
-
//
|
|
189
|
-
publishedValue: "
|
|
187
|
+
// Property names default to the title-typed prop / Description / Slug / Order / Status
|
|
188
|
+
// Pages whose Status isn't publishedValue (default "Published") import as drafts
|
|
189
|
+
publishedValue: "Done",
|
|
190
190
|
}),
|
|
191
191
|
],
|
|
192
192
|
},
|
|
193
193
|
});
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
-
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration); the adapter declares it, so a build without it warns.
|
|
196
|
+
The integration token comes from the `NOTION_TOKEN` environment variable (share the database with your integration); the adapter declares it, so a build without it warns.
|
|
197
|
+
|
|
198
|
+
The `Status` property is a publish gate by default. A page whose Status (a status or select property) holds any value other than `publishedValue`, which defaults to `Published`, imports with `draft: true`, and production builds drop drafts. A page with no Status value is published, and a database without the property publishes every page. Notion's default status options are Not started, In progress, and Done, so a database that uses them has no `Published` value and publishes nothing until you set `publishedValue: "Done"` (or whichever option means published). `properties.status` names a differently named property; to import every page whatever its status, point it at a property the database doesn't have.
|
|
199
|
+
|
|
200
|
+
**Notion image and video URLs are signed and expire**, so the adapter downloads them at build time into the site's assets and rewrites the references — a CMS asset never rots a static build. Only a file the server reports as an image or video is saved (or, when the response doesn't say, one whose URL names an image or video extension); anything else keeps its original URL with a build warning, so nothing but media is ever served from your site's origin. API calls are paced through a small request pool (3 at a time, matching Notion's per-integration rate limit) so databases with hundreds of pages import without tripping `429` responses; set `concurrency` on the source to tune it.
|
|
197
201
|
|
|
198
202
|
## Contentful
|
|
199
203
|
|
|
200
|
-
The built-in `contentful()` adapter reads the entries of one content type through the Content Delivery API and lowers each entry's rich text body to Markdown: headings, marks, links, lists, quotes, tables, and embedded assets map to their Markdown equivalents, and a body held in a Markdown long-text field passes through as written. Nothing to install — the adapter speaks the REST API directly.
|
|
204
|
+
The built-in `contentful()` adapter reads the entries of one content type through the Content Delivery API and lowers each entry's rich text body to Markdown: headings, marks, links, lists, quotes, tables, and embedded assets map to their Markdown equivalents, and a body held in a Markdown long-text field passes through as written, except that a link that isn't a web, mail, phone, or relative address (a `javascript:` URL, say) keeps only its label, as it does in rich text. Nothing to install — the adapter speaks the REST API directly.
|
|
201
205
|
|
|
202
206
|
```ts blume.config.ts
|
|
203
207
|
import { defineConfig } from "blume";
|
|
@@ -227,7 +231,7 @@ The Delivery API token comes from the `CONTENTFUL_ACCESS_TOKEN` environment vari
|
|
|
227
231
|
|
|
228
232
|
## Payload
|
|
229
233
|
|
|
230
|
-
The built-in `payload()` adapter reads a collection through the Payload REST API (`/api/<collection>`) and lowers each document's Lexical body to Markdown: paragraphs, headings, bullet, numbered, and check lists, quotes, links, uploads, and horizontal rules. A body held in a text field passes through as Markdown. Nothing to install.
|
|
234
|
+
The built-in `payload()` adapter reads a collection through the Payload REST API (`/api/<collection>`) and lowers each document's Lexical body to Markdown: paragraphs, headings, bullet, numbered, and check lists, quotes, links, uploads, and horizontal rules. A body held in a text field passes through as Markdown, except that a link that isn't a web, mail, phone, or relative address keeps only its label. Nothing to install.
|
|
231
235
|
|
|
232
236
|
```ts blume.config.ts
|
|
233
237
|
import { defineConfig } from "blume";
|
|
@@ -255,7 +259,7 @@ The API key comes from the `PAYLOAD_API_KEY` environment variable and is sent as
|
|
|
255
259
|
|
|
256
260
|
## Strapi
|
|
257
261
|
|
|
258
|
-
The built-in `strapi()` adapter reads a content type through the Strapi REST API (`/api/<pluralApiId>`) and lowers each entry's Blocks body to Markdown: paragraphs, headings, lists, quotes, code blocks, images, and links. A Markdown rich text field passes through as written. Strapi 5 responses are read as-is, and the Strapi 4 `attributes` envelope is flattened so the same field paths apply. Nothing to install.
|
|
262
|
+
The built-in `strapi()` adapter reads a content type through the Strapi REST API (`/api/<pluralApiId>`) and lowers each entry's Blocks body to Markdown: paragraphs, headings, lists, quotes, code blocks, images, and links. A Markdown rich text field passes through as written, except that a link that isn't a web, mail, phone, or relative address keeps only its label. Strapi 5 responses are read as-is, and the Strapi 4 `attributes` envelope is flattened so the same field paths apply. Nothing to install.
|
|
259
263
|
|
|
260
264
|
```ts blume.config.ts
|
|
261
265
|
import { defineConfig } from "blume";
|
|
@@ -327,6 +331,8 @@ export default defineConfig({
|
|
|
327
331
|
});
|
|
328
332
|
```
|
|
329
333
|
|
|
334
|
+
A source built with one of Blume's engine factories, like `sanitySource` above, is rebuilt on the running command's context, so it reads drafts under `--preview` and keeps its snapshot in `.blume/cache` the way the built-in adapter does. A source of your own can do the same by implementing `withContext(ctx)` and returning itself rebuilt on that context.
|
|
335
|
+
|
|
330
336
|
A source normalizes its native shape (Portable Text, Notion blocks, remote HTML) to Markdown/MDX text, so the same components and markdown features apply no matter where a page comes from. The built-in adapters escape rich text as they lower it, so what an author typed in the CMS — a `{`, a `<b>`, a paragraph starting with `import`, or `©` — renders as written. Links keep only `http(s)`, `mailto:`, `tel:`, and relative targets; any other scheme (`javascript:`, `data:`) renders as the link's text, and SVG images a source downloads are served sandboxed. Release notes from `githubReleases()` and files from `mdxRemote()` are treated as your own content: their raw HTML renders as written, so point them only at repositories you trust. Unlike the built-in adapters, `custom()` carries a live instance rather than plain data, so it declares no runtime dependency or secret of its own — the instance manages those itself.
|
|
331
337
|
|
|
332
338
|
A custom source that reads local files should set `sourcePath` on each entry and `contentRoot` on the source itself. `sourcePath` names the file in diagnostics and resolves relative images beside it; `contentRoot` bounds the git `log` that dates pages, so without it the source's pages get no git-derived ["Last updated" date](/docs/configuration#last-modified).
|
|
@@ -90,6 +90,7 @@ The agent surface is version-aware — something no other docs framework does:
|
|
|
90
90
|
- The MCP `search_docs` and `list_pages` tools default to the current docs and accept `version`: an archived id (`"v1.0"`) or `"all"`. `get_navigation` returns an archived snapshot's tree on request.
|
|
91
91
|
- `llms.txt` sections archived versions after the current docs, labeled with the version's `label` or `id` plus `(archived)` — `v1.0 (archived)` for the `{ id: "v1.0" }` above — so an agent reading the index knows which docs are frozen.
|
|
92
92
|
- `llms-full.txt` stays current-only — the flat dump never interleaves frozen copies of the same page.
|
|
93
|
+
- The [assistant](/docs/configuration/assistant) grounds its answers in the version the reader is viewing — the current docs, unless they're on an archived page — so frozen copies of a page never crowd out the one being read.
|
|
93
94
|
- Raw Markdown mirrors (`.md` URLs) exist for every version's pages, as for any route.
|
|
94
95
|
|
|
95
96
|
## With i18n
|