blume 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
- package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
- package/dist/cli/chunk-43zcf4gz.js.map +36 -0
- package/dist/cli/chunk-4ejxbzee.js +39 -0
- package/dist/cli/chunk-4ejxbzee.js.map +10 -0
- package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
- package/dist/cli/chunk-6bm0xpgd.js +18 -0
- package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
- package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
- package/dist/cli/chunk-713mngdq.js.map +14 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
- package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
- package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
- package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
- package/dist/cli/chunk-q3wxxq39.js +126 -0
- package/dist/cli/chunk-q3wxxq39.js.map +11 -0
- package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
- package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
- package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
- package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +28 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +22 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +20 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +11 -2
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/components/content/api-field.d.ts +59 -0
- package/dist/types/components/content/api-page.d.ts +95 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/helpers.d.ts +137 -0
- package/dist/types/components/openapi/security.d.ts +85 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +196 -27
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/redirect-patterns.d.ts +144 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +558 -56
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +1 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/platforms/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +61 -22
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +24 -2
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +12 -6
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +13 -0
- package/dist/types/openapi/references.d.ts +14 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +111 -0
- package/dist/types/ratelimit/upstash.d.ts +21 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +13 -10
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +45 -5
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +60 -36
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +4 -0
- package/dist/types/seo/metatags.d.ts +20 -0
- package/docs/02-deployment.mdx +44 -6
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +38 -2
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +22 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +6 -4
- package/docs/configuration/index.mdx +63 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +69 -0
- package/docs/configuration/search.mdx +30 -2
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +107 -1
- package/docs/content/frontmatter.mdx +77 -1
- package/docs/content/i18n.mdx +40 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +4 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +28 -6
- package/docs/content/sources/contentful.mdx +32 -0
- package/docs/content/sources/custom.mdx +39 -0
- package/docs/content/sources/github-releases.mdx +31 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +30 -0
- package/docs/content/sources/remote-mdx.mdx +40 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +31 -0
- package/docs/content/syntax.mdx +81 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +30 -3
- package/docs/discoverability/index.mdx +3 -2
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +3 -3
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +106 -5
- package/docs/references/scalar.mdx +3 -1
- package/package.json +36 -1
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +5 -5
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +3 -3
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +4 -3
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +12 -5
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/ask-context.ts +73 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +24 -9
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +31 -9
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +49 -3
- package/src/ai/tar.ts +139 -12
- package/src/ai/write-skill.ts +97 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +133 -19
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +143 -14
- package/src/astro/module-types.ts +6 -0
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +364 -67
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +19 -0
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +13 -13
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/graph.ts +3 -1
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +14 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +2 -1
- package/src/blume-modules.d.ts +7 -0
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +25 -17
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/index.ts +5 -0
- package/src/cli/report-format.ts +11 -6
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -0
- package/src/components/colors.ts +19 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +62 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +100 -0
- package/src/components/content/api-page.ts +224 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +125 -18
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +145 -40
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +11 -4
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +124 -82
- package/src/components/layout/LanguageSwitcher.astro +50 -17
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +35 -9
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +78 -9
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +313 -50
- package/src/components/layout/Search.astro +64 -30
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/analytics.ts +98 -0
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/types.ts +20 -0
- package/src/components/layout/search/typesense.ts +5 -1
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +163 -13
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +63 -8
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +953 -13
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/base-path.ts +62 -16
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +203 -27
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +5 -2
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/footer.ts +26 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +39 -36
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/i18n.ts +31 -3
- package/src/core/includes.ts +20 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +16 -2
- package/src/core/locale-links.ts +5 -1
- package/src/core/localizable.ts +26 -0
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +71 -20
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +138 -10
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +342 -68
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +327 -91
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +1 -1
- package/src/core/ui-packs/ar.ts +43 -0
- package/src/core/ui-packs/bg.ts +45 -0
- package/src/core/ui-packs/bn.ts +43 -0
- package/src/core/ui-packs/ca.ts +46 -0
- package/src/core/ui-packs/cs.ts +44 -0
- package/src/core/ui-packs/da.ts +45 -0
- package/src/core/ui-packs/de.ts +46 -0
- package/src/core/ui-packs/el.ts +45 -0
- package/src/core/ui-packs/es.ts +46 -0
- package/src/core/ui-packs/fa.ts +45 -0
- package/src/core/ui-packs/fi.ts +45 -0
- package/src/core/ui-packs/fr.ts +46 -0
- package/src/core/ui-packs/he.ts +43 -0
- package/src/core/ui-packs/hi.ts +44 -0
- package/src/core/ui-packs/hr.ts +45 -0
- package/src/core/ui-packs/hu.ts +44 -0
- package/src/core/ui-packs/id.ts +45 -0
- package/src/core/ui-packs/it.ts +45 -0
- package/src/core/ui-packs/ja.ts +45 -0
- package/src/core/ui-packs/ko.ts +44 -0
- package/src/core/ui-packs/nl.ts +46 -0
- package/src/core/ui-packs/no.ts +44 -0
- package/src/core/ui-packs/pl.ts +45 -0
- package/src/core/ui-packs/pt-br.ts +46 -0
- package/src/core/ui-packs/pt.ts +45 -0
- package/src/core/ui-packs/ro.ts +45 -0
- package/src/core/ui-packs/ru.ts +45 -0
- package/src/core/ui-packs/sk.ts +44 -0
- package/src/core/ui-packs/sr.ts +45 -0
- package/src/core/ui-packs/sv.ts +44 -0
- package/src/core/ui-packs/th.ts +43 -0
- package/src/core/ui-packs/tr.ts +45 -0
- package/src/core/ui-packs/uk.ts +45 -0
- package/src/core/ui-packs/vi.ts +45 -0
- package/src/core/ui-packs/zh-tw.ts +43 -0
- package/src/core/ui-packs/zh.ts +43 -0
- package/src/core/variables.ts +103 -0
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +104 -14
- package/src/deploy/cloudflare-negotiation.ts +39 -17
- package/src/deploy/headers.ts +10 -5
- package/src/deploy/node-headers.ts +64 -10
- package/src/deploy/platforms/cloudflare.ts +44 -3
- package/src/deploy/platforms/netlify.ts +34 -1
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +38 -2
- package/src/deploy/redirects.ts +139 -36
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +20 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +35 -11
- package/src/markdown/math.ts +4 -4
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +136 -11
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +77 -36
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +46 -3
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +1 -1
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +229 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +30 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +5 -2
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +51 -7
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +201 -28
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +93 -28
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +15 -4
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +5 -0
- package/src/seo/jsonld.ts +7 -3
- package/src/seo/metatags.ts +67 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +56 -3
- package/src/theme/palette.ts +121 -45
- package/src/translate/meta.ts +68 -24
- package/src/translate/prompts.ts +1 -0
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +12 -3
- package/src/translate/work-list.ts +56 -17
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-3yce002v.js.map +0 -14
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-mqc662a6.js +0 -75
- package/dist/cli/chunk-mqc662a6.js.map +0 -11
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
- package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- package/dist/cli/chunk-xh43dwgw.js.map +0 -36
- package/docs/content/sources.mdx +0 -338
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
|
@@ -68,7 +68,7 @@ A header [tab](/docs/content/navigation#tabs) pointing at `/changelog` opens thi
|
|
|
68
68
|
|
|
69
69
|
## From GitHub Releases
|
|
70
70
|
|
|
71
|
-
Rather than authoring entries by hand, point the built-in [`githubReleases()` source](/docs/content/sources
|
|
71
|
+
Rather than authoring entries by hand, point the built-in [`githubReleases()` source](/docs/content/sources/github-releases) at a repo and every release becomes a `type: changelog` entry — the same timeline and feed, fed straight from the releases you already publish. Blume's own [changelog](/changelog) is built this way:
|
|
72
72
|
|
|
73
73
|
```ts blume.config.ts
|
|
74
74
|
import { filesystem, githubReleases } from "blume/sources";
|
|
@@ -85,7 +85,7 @@ content: {
|
|
|
85
85
|
}
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
The release name becomes the title, its tag becomes `changelog.version`, and its published date sorts the timeline. Each release page also gets a unique meta description summarized from its notes — markdown stripped, section headings and changeset commit-hash prefixes dropped, trimmed to the search-snippet length [`blume audit`](/docs/cli/audit) checks for — instead of falling back to the site description. A private repo authenticates with the `GITHUB_TOKEN` environment variable. See [
|
|
88
|
+
The release name becomes the title, its tag becomes `changelog.version`, and its published date sorts the timeline. Each release page also gets a unique meta description summarized from its notes — markdown stripped, section headings and changeset commit-hash prefixes dropped, trimmed to the search-snippet length [`blume audit`](/docs/cli/audit) checks for — instead of falling back to the site description. A private repo authenticates with the `GITHUB_TOKEN` environment variable. See [GitHub Releases](/docs/content/sources/github-releases) for every option.
|
|
89
89
|
|
|
90
90
|
## The RSS feed
|
|
91
91
|
|
|
@@ -102,10 +102,10 @@ The module exposes:
|
|
|
102
102
|
description: "Generated RSS feeds: { href, title }.",
|
|
103
103
|
},
|
|
104
104
|
fontCssVars: {
|
|
105
|
-
type: "
|
|
105
|
+
type: "FontHead[]",
|
|
106
106
|
required: true,
|
|
107
107
|
description:
|
|
108
|
-
"
|
|
108
|
+
"The configured fonts for Astro's <Font> component in the head, one per family: { cssVariable, preloadWeights, preloadSubsets? }.",
|
|
109
109
|
},
|
|
110
110
|
ui: {
|
|
111
111
|
type: "UIStrings",
|
|
@@ -290,7 +290,7 @@ Blume ships a default **not found** page out of the box: a centered "404" messag
|
|
|
290
290
|
|
|
291
291
|
The page also has a Markdown twin at `/404.md` and a JSON twin at `/404.json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details) with the same recovery links (absolute URLs once [`deployment.site`](/docs/deployment) is set), plus the [`openapi.json`](/docs/discoverability/json-api) description when the JSON API is on. On a [Vercel or Cloudflare server build](/docs/deployment#server-rendering), a request for a missing page that sends [`Accept: text/markdown`](/docs/discoverability/markdown#content-negotiation) gets the Markdown body with the `404` status instead of the HTML shell, and one that sends `Accept: application/json` gets the problem document — so an agent never has to parse a page of chrome to learn where to go next. On Vercel the same goes for a `.md` or `.json` URL no file backs.
|
|
292
292
|
|
|
293
|
-
To replace it with your own, add a `pages/404.astro`. It owns the `/404` route the same way `pages/changelog.astro` takes over the changelog — your page wins and the default is dropped. Build it like any other custom page, in `PageLayout` or `RootLayout`:
|
|
293
|
+
To replace it with your own, add a `pages/404.astro`. It owns the `/404` route the same way `pages/changelog.astro` takes over the changelog — your page wins and the default is dropped, along with its `/404.md` and `/404.json` twins. Build it like any other custom page, in `PageLayout` or `RootLayout`:
|
|
294
294
|
|
|
295
295
|
```astro pages/404.astro lineNumbers
|
|
296
296
|
---
|
package/docs/advanced/skills.mdx
CHANGED
|
@@ -34,3 +34,17 @@ Blume doesn't host the automation — wire the skill into a scheduled task in Cl
|
|
|
34
34
|
```text
|
|
35
35
|
Use the blume-update-docs skill. Review the PRs merged within the last 7 days and compare them to the docs content. Ignore work behind feature flags. If docs need updates, make them, verify the docs build, and open a blume/* PR. If not, report what you checked and do not open a PR.
|
|
36
36
|
```
|
|
37
|
+
|
|
38
|
+
## Writing your site's skill
|
|
39
|
+
|
|
40
|
+
`blume-write-skill` writes the [agent skill for your own docs](/docs/discoverability/agent-discovery#your-sites-skill): one `SKILL.md`, grounded in the docs, that teaches a coding agent to use your product — setup, core concepts, common tasks, and gotchas — with each topic linked to its page. It takes the place of the page map Blume generates at `/skill.md`. The quickest way to run it is `blume skill`, which works out where the skill goes and opens Codex or Claude Code on the copy bundled in the package:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
blume skill --claude # or --codex
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To use it from another agent, install it:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx skills add haydenbleasel/blume --skill blume-write-skill
|
|
50
|
+
```
|
package/docs/cli/audit.mdx
CHANGED
|
@@ -69,9 +69,10 @@ This writes the complete JSON report — every affected page, not the terminal's
|
|
|
69
69
|
|
|
70
70
|
The check set is deliberately narrower than a general-purpose SEO crawler's. Much of what such a crawler reports cannot happen to a Blume site — it never emits `rel=nofollow`, and Vite's content-hashed bundles are never missing or redirecting — and reporting those as permanent zeroes would just teach you to ignore the report.
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
Limits worth stating plainly:
|
|
73
73
|
|
|
74
74
|
- **Structured data** is validated for well-formedness (valid JSON, a `@context`, a `@type` on every node). Blume does not validate against the full schema.org vocabulary or Google's rich-results rules.
|
|
75
|
+
- **Contrast** is checked for the colors your theme config sets, not element by element. The audit doesn't render pages, so a color you write by hand in MDX or `theme.css` isn't measured.
|
|
75
76
|
- **Core Web Vitals** are not checked. They need a real browser, and a flag that quietly measured nothing would be worse than not having one — so `blume audit` reports the layout-shift causes it _can_ see offline (images with no `width`/`height`, oversized assets) and leaves the rest alone for now.
|
|
76
77
|
|
|
77
78
|
Anything the audit did not run is reported as skipped rather than silently passing:
|
|
@@ -83,7 +84,24 @@ Anything the audit did not run is reported as skipped rather than silently passi
|
|
|
83
84
|
|
|
84
85
|
## Check catalog
|
|
85
86
|
|
|
86
|
-
Every check `blume audit` can report, grouped by category, with its default severity, the tier it runs in, and the fix the report suggests. `--only` and `--skip` accept these ids (without the `BLUME_AUDIT_` prefix is fine too) or the category
|
|
87
|
+
Every check `blume audit` can report, grouped by category, with its default severity, the tier it runs in, and the fix the report suggests. `--only` and `--skip` accept these ids (without the `BLUME_AUDIT_` prefix is fine too) or a category's key. The key isn't always the category's heading, so use this table: `blume audit --only i18n`, not `--only Internationalization`.
|
|
88
|
+
|
|
89
|
+
| Category | Key |
|
|
90
|
+
| -------------------- | ----------------- |
|
|
91
|
+
| Accessibility | `accessibility` |
|
|
92
|
+
| Content | `content` |
|
|
93
|
+
| Duplicates | `duplicates` |
|
|
94
|
+
| Indexability | `indexability` |
|
|
95
|
+
| Links | `links` |
|
|
96
|
+
| Redirects | `redirects` |
|
|
97
|
+
| Social cards | `social` |
|
|
98
|
+
| Internationalization | `i18n` |
|
|
99
|
+
| Assets | `assets` |
|
|
100
|
+
| Sitemap | `sitemap` |
|
|
101
|
+
| robots.txt | `robots` |
|
|
102
|
+
| AI discoverability | `ai` |
|
|
103
|
+
| Structured data | `structured-data` |
|
|
104
|
+
| Live deployment | `network` |
|
|
87
105
|
|
|
88
106
|
### Content
|
|
89
107
|
|
|
@@ -429,6 +447,24 @@ Fix: Point the hreflang alternate at a page that exists and is canonical.
|
|
|
429
447
|
|
|
430
448
|
Fix: Each language in an hreflang group must name exactly one page.
|
|
431
449
|
|
|
450
|
+
### Accessibility
|
|
451
|
+
|
|
452
|
+
These run on your theme config, not on each page: a theme color is one choice for the whole site. The check resolves colors the way the build does (a preset's shade for each mode, and white or dark labels on fills) and measures every text color the theme draws against the WCAG AA ratio for body text, 4.5:1, in light and dark mode.
|
|
453
|
+
|
|
454
|
+
#### Theme colors miss WCAG AA contrast [#theme-contrast-low]
|
|
455
|
+
|
|
456
|
+
`BLUME_AUDIT_THEME_CONTRAST_LOW` · warning · from the theme config
|
|
457
|
+
|
|
458
|
+
Fix: Pick a darker shade for light mode or a lighter one for dark mode, per mode with `theme.accent: { light, dark }`.
|
|
459
|
+
|
|
460
|
+
It checks accent text on the page, labels on accent and `action` fills, and body and secondary text on a custom `background`. Each finding names the pair, the mode, and its ratio.
|
|
461
|
+
|
|
462
|
+
#### Theme color can't be checked for contrast [#theme-color-unchecked]
|
|
463
|
+
|
|
464
|
+
`BLUME_AUDIT_THEME_COLOR_UNCHECKED` · info · from the theme config
|
|
465
|
+
|
|
466
|
+
Fix: Write the color as hex, `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, or a named color so its contrast can be checked.
|
|
467
|
+
|
|
432
468
|
### Assets
|
|
433
469
|
|
|
434
470
|
#### Missing alt text [#image-alt-missing]
|
package/docs/cli/evals.mdx
CHANGED
|
@@ -99,9 +99,9 @@ This writes the full JSON report to a file and opens the agent interactively wit
|
|
|
99
99
|
## Flags
|
|
100
100
|
|
|
101
101
|
- `--agent codex|claude` — which agent CLI runs the reader and judge. Defaults to `codex`.
|
|
102
|
-
- `--file <path>` — the evals file. Defaults to `evals.yaml`.
|
|
103
|
-
- `--threshold <0..1>` — minimum passing fraction before the run exits non-zero, with `severity: warning` misses counted as passing. Defaults to `1`.
|
|
104
|
-
- `--timeout <seconds>` — reader time limit per question. Defaults to `180
|
|
102
|
+
- `--file <path>` — the evals file, relative to the project root or absolute. Defaults to `evals.yaml`.
|
|
103
|
+
- `--threshold <0..1>` — minimum passing fraction before the run exits non-zero, with `severity: warning` misses counted as passing. Defaults to `1`. An empty value is an error, so `--threshold "$EVAL_THRESHOLD"` with the variable unset can't switch the gate off.
|
|
104
|
+
- `--timeout <seconds>` — reader time limit per question. Defaults to `180`, and can be at most `2147483` (about 24 days), the longest a timer can wait.
|
|
105
105
|
- `--json` — emit the report as JSON on stdout.
|
|
106
106
|
- `--fix` — after a failing run, hand the report to the agent to fix the docs interactively.
|
|
107
107
|
- `--verbose` — include the reader's full answer under each failure.
|
package/docs/cli/index.mdx
CHANGED
|
@@ -25,6 +25,7 @@ blume <command> [options]
|
|
|
25
25
|
| [`blume eval`](/docs/cli/evals) | Test the docs: an agent answers your questions using only the documentation. |
|
|
26
26
|
| [`blume translate`](/docs/cli/translate) | Translate docs into the configured locales with a local agent CLI. |
|
|
27
27
|
| [`blume version [id]`](/docs/cli/version) | Freeze the current docs as an archived version (no id lists configured versions). |
|
|
28
|
+
| [`blume skill`](/docs/discoverability/agent-discovery#your-sites-skill) | Write your docs' agent skill with Codex or Claude Code, in place of the generated one. |
|
|
28
29
|
| [`blume migrate [source]`](/docs/migrating) | Move a Mintlify, Fumadocs, Docusaurus, Starlight, or Nextra site to Blume with Codex or Claude Code. |
|
|
29
30
|
| [`blume upgrade`](/docs/upgrading) | Move to a new major: bump `blume`, then list the config changes left or hand them to Codex or Claude Code. |
|
|
30
31
|
|
package/docs/cli/translate.mdx
CHANGED
|
@@ -33,8 +33,8 @@ A first translation has no precedent to match, so pin the choice up front with [
|
|
|
33
33
|
|
|
34
34
|
## What gets translated
|
|
35
35
|
|
|
36
|
-
- **Pages** — `.md`/`.mdx` files in the default locale. The agent translates the prose and only the human-visible frontmatter values (`title`, `description`, `sidebar.label`, `sidebar.badge`, `seo.title`, `seo.description`). Targets follow your parser: `fr/guides/install.mdx` under `dir`, `guides/install.fr.mdx` under `dot`.
|
|
37
|
-
- **Folder navigation titles** — under the `dir` parser, each locale's needed [`meta.ts`](/docs/content/meta) titles are translated in one batched call, and the generated per-locale `meta.ts` copies every other key (`order`, `pages`, `icon`, `collapsed`) verbatim so the locale's sidebar keeps its ordering.
|
|
36
|
+
- **Pages** — `.md`/`.mdx` files in the default locale. The agent translates the prose and only the human-visible frontmatter values (`title`, `description`, `sidebar.label`, `sidebar.badge`, `seo.title`, `seo.description`). Targets follow your parser: `fr/guides/install.mdx` under `dir`, `guides/install.fr.mdx` under `dot`. A translation that already exists is rewritten where it lives, even under another name (a hand-written `fr/guides/install.md`), so a retranslation never adds a second copy of the page.
|
|
37
|
+
- **Folder navigation titles** — under the `dir` parser, each locale's needed [`meta.ts`](/docs/content/meta) titles are translated in one batched call, and the generated per-locale `meta.ts` copies every other key (`order`, `pages`, `icon`, `collapsed`) verbatim so the locale's sidebar keeps its ordering. It goes in the locale's folder as it exists on disk (`pt-br/` for a configured `pt-BR`), and a `meta.js` or `meta.mjs` already there is rewritten instead of gaining a `meta.ts` beside it.
|
|
38
38
|
|
|
39
39
|
Translations you wrote by hand are **adopted, never overwritten**: a translation that exists but has no ledger entry is stamped as current and left alone. Only `--force` retranslates it.
|
|
40
40
|
|
|
@@ -77,5 +77,5 @@ The JSON report carries the same `diagnostics` + `summary` shape as `blume valid
|
|
|
77
77
|
- `--concurrency <n>` — parallel agent sessions. Defaults to `4`, max `16`.
|
|
78
78
|
- `--locale <codes>` — comma-separated target locales (defaults to every non-default locale).
|
|
79
79
|
- `--force` — retranslate everything, up-to-date and hand-authored files included.
|
|
80
|
-
- `--timeout <seconds>` — agent time limit per file. Defaults to `600`; the ceiling exists to catch hung agents, so large pages have room to finish.
|
|
80
|
+
- `--timeout <seconds>` — agent time limit per file. Defaults to `600`; the ceiling exists to catch hung agents, so large pages have room to finish. At most `2147483` (about 24 days), the longest a timer can wait.
|
|
81
81
|
- `--json` — emit the report as JSON on stdout, in both modes.
|
package/docs/cli/version.mdx
CHANGED
|
@@ -12,7 +12,7 @@ blume version v1.0
|
|
|
12
12
|
That does four things:
|
|
13
13
|
|
|
14
14
|
1. **Copies the content tree** into a folder named after the id (`docs/v1.0/`), leaving existing snapshots out of the copy.
|
|
15
|
-
2. **Rewrites root-absolute links inside the copy** so they stay within the snapshot: `/guides/x` becomes `/v1.0/guides/x`. Fenced and inline code are left untouched, and links to pages the snapshot has no copy of — generated API references, remote sources like a changelog — keep pointing at the live pages.
|
|
15
|
+
2. **Rewrites root-absolute links inside the copy** so they stay within the snapshot: `/guides/x` becomes `/v1.0/guides/x`. That covers inline links and images, reference-style definitions (`[x]: /guides/x`), and HTML `href` and `src` attributes in either quote style, and a link that spells out your [`basePath`](/docs/deployment#mount-the-docs-under-a-path) keeps it (`/docs/guides/x` becomes `/docs/v1.0/guides/x`). Fenced and inline code are left untouched, and links to pages the snapshot has no copy of — generated API references, remote sources like a changelog — keep pointing at the live pages.
|
|
16
16
|
3. **Registers the id** in `versions.archived` in `blume.config.ts`. The first cut turns versioning on, adding a `versions` block with the id archived and the live docs labeled "Latest". When the config is shaped in a way it won't edit, it warns and prints the entry to paste instead.
|
|
17
17
|
4. **Reports what it did**: the files copied, the pages whose links were rewritten, and a reminder that archived versions are frozen and that `blume dev` needs a restart to pick the snapshot up.
|
|
18
18
|
|
|
@@ -27,6 +27,8 @@ Analytics loads in **production builds only**. The scripts are emitted by `blume
|
|
|
27
27
|
Analytics is opt-in. With no `analytics` list (or an empty one), Blume injects nothing.
|
|
28
28
|
:::
|
|
29
29
|
|
|
30
|
+
To ask readers first, set [`consent`](/docs/configuration/consent): every adapter here then waits until the reader allows analytics.
|
|
31
|
+
|
|
30
32
|
Every identifier below — a project key, a measurement ID, a site token — is the public, browser-side one from the provider's own install snippet. Each adapter renders that snippet, so it's safe to commit to your docs repository; Blume never asks for a secret.
|
|
31
33
|
|
|
32
34
|
## Adapters
|
|
@@ -47,6 +49,7 @@ Every identifier below — a project key, a measurement ID, a site token — is
|
|
|
47
49
|
| `hotjar()` | [Hotjar](#hotjar) | `id` |
|
|
48
50
|
| `logrocket()` | [LogRocket](#logrocket) | `id` |
|
|
49
51
|
| `mixpanel()` | [Mixpanel](#mixpanel) | `token` |
|
|
52
|
+
| `oneDollarStats()` | [OneDollarStats](#onedollarstats) | — |
|
|
50
53
|
| `pirsch()` | [Pirsch](#pirsch) | `code` |
|
|
51
54
|
| `plausible()` | [Plausible](#plausible) | `domain` |
|
|
52
55
|
| `posthog()` | [PostHog](#posthog) | `key` |
|
|
@@ -191,6 +194,22 @@ analytics: [
|
|
|
191
194
|
|
|
192
195
|
`clientId` becomes `data-client-id`. Any other option becomes its own `data-` attribute on the tag, so name it the way the attribute reads, in kebab-case (`track-web-vitals`, `track-errors`, `track-outgoing-links`, `api-url`, …). Databuddy ignores a camelCase name like `trackWebVitals`. Values are strings: `"true"` or `"false"` for a switch, and a JSON array for `skip-patterns` and `mask-patterns`. Databuddy reads any other list value as empty.
|
|
193
196
|
|
|
197
|
+
## OneDollarStats
|
|
198
|
+
|
|
199
|
+
Add the site's domain in [OneDollarStats](https://onedollarstats.com), then list `oneDollarStats()`. It needs no key, because OneDollarStats matches events to a site by the domain they come from.
|
|
200
|
+
|
|
201
|
+
```ts blume.config.ts lineNumbers
|
|
202
|
+
analytics: [oneDollarStats()],
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Each option becomes its own `data-` attribute on the tag, and values are strings:
|
|
206
|
+
|
|
207
|
+
- `hostname` reports every event under that host name (`docs.example.com`, with no `https://` or path) on every host, not just `localhost`, so preview deploys and staging count as that site's traffic. Leave it unset unless every build should report under that name.
|
|
208
|
+
- `devmode: "true"` together with `hostname` lets a local `blume preview` send events. On `localhost` the tracker sends nothing without both, so keep them out of the committed config.
|
|
209
|
+
- `url` is the collector endpoint events are posted to (`https://collector.onedollarstats.com/events` by default), not the site's URL.
|
|
210
|
+
- `autocollect: "false"` turns off automatic page views.
|
|
211
|
+
- `"hash-routing": "true"` sends a page view on every navigation, even when only the `#fragment` changes, like a table-of-contents click. Blume leaves the attribute off for `"false"`, because the tracker turns hash routing on whenever it's present.
|
|
212
|
+
|
|
194
213
|
## Mixpanel
|
|
195
214
|
|
|
196
215
|
Pass your **project token** (project settings → Access Keys in [Mixpanel](https://mixpanel.com)) to `mixpanel()`. If the project uses EU or India data residency, set `region` to match, or Mixpanel drops the events.
|
|
@@ -391,6 +410,8 @@ analytics: [
|
|
|
391
410
|
| `mixpanel()` | `token` | — | Mixpanel project token. Required. |
|
|
392
411
|
| `mixpanel()` | `region` | `us` | Data residency region: `us`, `eu`, or `in`. |
|
|
393
412
|
| `mixpanel()` | anything else | `track_pageview: "url-with-path-and-query-string"` | Merged into the `mixpanel.init` options verbatim. |
|
|
413
|
+
| `oneDollarStats()` | `hostname` | — | Bare host name every event is reported under, on every host. |
|
|
414
|
+
| `oneDollarStats()` | anything else | — | Rendered as a `data-` attribute on the tag (`"hash-routing": "false"` is left off). |
|
|
394
415
|
| `pirsch()` | `code` | — | Pirsch identification code. Required. |
|
|
395
416
|
| `pirsch()` | anything else | — | Rendered as a `data-` attribute on the tag. |
|
|
396
417
|
| `plausible()` | `domain` | — | The site's domain in Plausible. Required. |
|
|
@@ -410,4 +431,4 @@ analytics: [
|
|
|
410
431
|
|
|
411
432
|
## Custom events
|
|
412
433
|
|
|
413
|
-
Blume's page feedback
|
|
434
|
+
Blume's page feedback (`feedback`, and `feedback_comment` when [written feedback](/docs/configuration#written-feedback) is on), [search](/docs/configuration/search#search-analytics) (`search`, `search_select`), and [narration](/docs/configuration/narration#analytics) player (`narration_play`, `narration_complete`) send custom events through every configured adapter that has a client API — PostHog, Mixpanel, Heap, Segment, Hightouch, Amplitude, LogRocket, Adobe, Google Analytics, Google Tag Manager (as a `{ event }` push on `window.dataLayer`), Plausible, Databuddy, Fathom, OneDollarStats (with each property value as a string), Pirsch, Clarity, Hotjar, and Vercel. Cloudflare and Clearbit have no event API. Every event also fires as a `blume:track` CustomEvent on `window` with `{ event, props }` in `detail`, so a `script()` adapter can forward it anywhere else. For deploying your built site, see [Deployment](/docs/deployment).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Assistant
|
|
3
|
-
description: An in-page assistant grounded in your docs — suggested questions, custom instructions, retrieval sizing, provider adapters
|
|
3
|
+
description: An in-page assistant grounded in your docs — suggested questions, custom instructions, retrieval sizing, provider adapters for OpenAI, Anthropic, Gemini, Grok, the Vercel AI Gateway, and any OpenAI-compatible endpoint, and the server output it needs.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Add an assistant that answers reader questions in an in-page chat panel, backed by a streaming server endpoint and the [AI SDK](https://ai-sdk.dev). It's opt-in, and static docs stay fully static until you turn it on:
|
|
@@ -34,6 +34,27 @@ ai: {
|
|
|
34
34
|
|
|
35
35
|
`label` is the question that gets asked; `icon` is optional. Leave `suggestions` unset (or empty) and the panel opens to a plain input.
|
|
36
36
|
|
|
37
|
+
## Asking about code
|
|
38
|
+
|
|
39
|
+
With the assistant on, every code block gets an **Ask** button beside its copy button. It opens the assistant with the block attached, shown as a chip above the input, so the reader can ask what it does, why it fails, or how to adapt it. Sent without a question, it asks the assistant to explain the code. The code goes to the model as a fenced block after the question, up to 6,000 characters.
|
|
40
|
+
|
|
41
|
+
A custom chat UI built on `useAssistant` can do the same: listen for `blume:open-assistant` on `window`, whose `detail.code` holds the block's `language`, `source`, and `title`.
|
|
42
|
+
|
|
43
|
+
## Contact support
|
|
44
|
+
|
|
45
|
+
When the docs can't answer, hand the reader to a person. Set `support`, and once there's a conversation the panel shows a **Contact support** link under it:
|
|
46
|
+
|
|
47
|
+
```ts blume.config.ts lineNumbers
|
|
48
|
+
ai: {
|
|
49
|
+
assistant: {
|
|
50
|
+
enabled: true,
|
|
51
|
+
support: "mailto:help@example.com", // or "https://example.com/support", or "/support"
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A `mailto:` address starts an email with the conversation as its body, so the reader doesn't have to explain again (a long conversation keeps its latest turns). A URL or a page on your site gets the conversation's id as a `thread` query parameter. The assistant's `ask`, `ask_answer`, and `ask_error` [analytics events](#analytics) carry the same `thread`, so your support team can find the conversation in your analytics.
|
|
57
|
+
|
|
37
58
|
## Custom instructions
|
|
38
59
|
|
|
39
60
|
Add your own system-prompt text with `instructions` — identity, language, tone, or anything else the assistant should keep in mind:
|
|
@@ -58,6 +79,24 @@ The page the reader is currently on is added to the context first and used to sc
|
|
|
58
79
|
|
|
59
80
|
Grounding is on for every adapter except **[Inkeep](#inkeep)**, which runs its own retrieval over the content you've indexed in its dashboard.
|
|
60
81
|
|
|
82
|
+
## Searching and reading pages
|
|
83
|
+
|
|
84
|
+
The retrieved pages are a head start. The assistant can also look things up itself: it has a `search_docs` tool that searches your docs, and a `read_page` tool that reads a whole page, for when an excerpt stops short of the answer. It can search, read a page or two, search again, and then answer, all within one question. Tool calls run on your server against the same snapshot as the grounding, keep to the reader's language and docs version, and never reach the reader; they only see the answer.
|
|
85
|
+
|
|
86
|
+
The tools are on by default for every adapter whose models support tool calling. They're off for [OpenAI-compatible endpoints](#openai-compatible-endpoints), since a self-hosted model may not. Set `tools` to change either default:
|
|
87
|
+
|
|
88
|
+
```ts blume.config.ts lineNumbers
|
|
89
|
+
ai: {
|
|
90
|
+
assistant: {
|
|
91
|
+
enabled: true,
|
|
92
|
+
provider: openai({ baseUrl, model, apiKeyEnv }),
|
|
93
|
+
tools: true, // this model supports tool calling
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Each question takes at most five model steps: up to four rounds of tool calls, then the answer. With `tools: false`, the assistant answers from the retrieved pages alone, as it did before tools existed.
|
|
99
|
+
|
|
61
100
|
## Retrieval size
|
|
62
101
|
|
|
63
102
|
How much documentation a question carries is the biggest lever on how long the reader waits for the first word: the model reads every injected character before it emits a token. On a hosted frontier model that's invisible, but on a self-hosted backend it dominates. `retrieval` sizes it:
|
|
@@ -160,13 +199,13 @@ A static build with the assistant enabled and no external `endpoint` fails fast
|
|
|
160
199
|
|
|
161
200
|
```ts blume.config.ts lineNumbers
|
|
162
201
|
import { defineConfig } from "blume";
|
|
163
|
-
import {
|
|
202
|
+
import { anthropic } from "blume/ai";
|
|
164
203
|
|
|
165
204
|
export default defineConfig({
|
|
166
205
|
ai: {
|
|
167
206
|
assistant: {
|
|
168
207
|
enabled: true,
|
|
169
|
-
provider:
|
|
208
|
+
provider: anthropic({ model: "claude-sonnet-5" }),
|
|
170
209
|
},
|
|
171
210
|
},
|
|
172
211
|
});
|
|
@@ -174,16 +213,120 @@ export default defineConfig({
|
|
|
174
213
|
|
|
175
214
|
| Adapter | Answers with | API key env var | SDK to install |
|
|
176
215
|
| --- | --- | --- | --- |
|
|
216
|
+
| [`openai()`](#openai) | an [OpenAI](https://platform.openai.com) model, or any [OpenAI-compatible endpoint](#openai-compatible-endpoints) | `OPENAI_API_KEY` | `@ai-sdk/openai` (`@ai-sdk/openai-compatible` with a `baseUrl`) |
|
|
217
|
+
| [`anthropic()`](#anthropic) | a [Claude](https://platform.claude.com) model | `ANTHROPIC_API_KEY` | `@ai-sdk/anthropic` |
|
|
218
|
+
| [`gemini()`](#gemini) | a [Gemini](https://ai.google.dev) model | `GEMINI_API_KEY` | `@ai-sdk/google` |
|
|
219
|
+
| [`grok()`](#grok) | an [xAI](https://x.ai/api) Grok model | `XAI_API_KEY` | `@ai-sdk/xai` |
|
|
177
220
|
| [`gateway()`](#vercel-ai-gateway) (default) | a `provider/model` string via the Vercel AI Gateway | `AI_GATEWAY_API_KEY` | none — ships with Blume |
|
|
178
221
|
| [`openrouter()`](#openrouter) | any [OpenRouter](https://openrouter.ai) model | `OPENROUTER_API_KEY` | `@openrouter/ai-sdk-provider` |
|
|
179
222
|
| [`llmgateway()`](#llmgateway) | any [LLMGateway](https://llmgateway.io) model | `LLMGATEWAY_API_KEY` | `@ai-sdk/openai-compatible` |
|
|
180
223
|
| [`inkeep()`](#inkeep) | an [Inkeep](https://inkeep.com) QA model | `INKEEP_API_KEY` | `@ai-sdk/openai-compatible` |
|
|
181
|
-
| [`openaiCompatible()`](#openai-compatible-endpoints) | whatever your endpoint serves | the `apiKeyEnv` you name | `@ai-sdk/openai-compatible` |
|
|
182
224
|
|
|
183
|
-
The SDKs are optional peer dependencies, so add the one your adapter needs to your project (`npm install @
|
|
225
|
+
`openai()`, `anthropic()`, `gemini()`, and `grok()` call the provider's own API with your key — nothing sits in between. The SDKs are optional peer dependencies, so add the one your adapter needs to your project (`npm install @ai-sdk/anthropic`, say). If it's missing, `blume build` stops before Vite runs, naming the package and the command that installs it, and [`blume doctor`](/docs/cli/doctor) reports it too.
|
|
184
226
|
|
|
185
227
|
The descriptor an adapter returns is plain data — its kind, its options, the env vars it reads, and the SDK it needs — so the generated route (and the [ejected](/docs/configuration/customization#eject) one) inlines it as literals and imports the provider SDK by name. Nothing reads `blume.config.ts` at request time, and no secret is ever written into a route: adapters take the **name** of the env var holding the key, and the route reads the value through Astro's [`getSecret()`](https://docs.astro.build/en/guides/environment-variables/#retrieving-secrets-programmatically), so each deployment adapter supplies it its own way — environment variables on Node, Vercel, and Netlify, and the Worker's [bindings](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#environment-variables-and-secrets) on Cloudflare.
|
|
186
228
|
|
|
229
|
+
### OpenAI
|
|
230
|
+
|
|
231
|
+
Any [OpenAI](https://platform.openai.com) model, called directly through OpenAI's own AI SDK provider:
|
|
232
|
+
|
|
233
|
+
```ts blume.config.ts lineNumbers
|
|
234
|
+
import { defineConfig } from "blume";
|
|
235
|
+
import { openai } from "blume/ai";
|
|
236
|
+
|
|
237
|
+
export default defineConfig({
|
|
238
|
+
ai: {
|
|
239
|
+
assistant: {
|
|
240
|
+
enabled: true,
|
|
241
|
+
provider: openai({ model: "gpt-5.5" }),
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### OpenAI-compatible endpoints
|
|
248
|
+
|
|
249
|
+
Any endpoint that speaks the OpenAI API — a self-hosted model, an internal gateway — works through `openai()` too: add its `baseUrl`, and usually `apiKeyEnv` for the env var holding its key (it defaults to `OPENAI_API_KEY`). `name` is the provider name the AI SDK reports, and the key the endpoint reads [`providerOptions`](#options-every-adapter-takes) under; it defaults to `openai-compatible`:
|
|
250
|
+
|
|
251
|
+
```ts blume.config.ts lineNumbers
|
|
252
|
+
import { defineConfig } from "blume";
|
|
253
|
+
import { openai } from "blume/ai";
|
|
254
|
+
|
|
255
|
+
export default defineConfig({
|
|
256
|
+
ai: {
|
|
257
|
+
assistant: {
|
|
258
|
+
enabled: true,
|
|
259
|
+
provider: openai({
|
|
260
|
+
baseUrl: "https://my-gateway.example.com/v1",
|
|
261
|
+
apiKeyEnv: "MY_GATEWAY_API_KEY",
|
|
262
|
+
model: "gpt-4o",
|
|
263
|
+
name: "my-gateway",
|
|
264
|
+
}),
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
With a `baseUrl`, the route calls the endpoint's Chat Completions API through `@ai-sdk/openai-compatible`, so install that package instead of `@ai-sdk/openai`: most compatible endpoints don't serve OpenAI's Responses API. The [docs tools](#searching-and-reading-pages) are off by default, since a self-hosted model may not support tool calling.
|
|
271
|
+
|
|
272
|
+
:::note
|
|
273
|
+
`openaiCompatible()` from earlier Blume versions still works: it's the same adapter as `openai()` with a `baseUrl`.
|
|
274
|
+
:::
|
|
275
|
+
|
|
276
|
+
### Anthropic
|
|
277
|
+
|
|
278
|
+
Any [Claude](https://platform.claude.com) model, called directly through Anthropic's AI SDK provider:
|
|
279
|
+
|
|
280
|
+
```ts blume.config.ts lineNumbers
|
|
281
|
+
import { defineConfig } from "blume";
|
|
282
|
+
import { anthropic } from "blume/ai";
|
|
283
|
+
|
|
284
|
+
export default defineConfig({
|
|
285
|
+
ai: {
|
|
286
|
+
assistant: {
|
|
287
|
+
enabled: true,
|
|
288
|
+
provider: anthropic({ model: "claude-sonnet-5" }),
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Gemini
|
|
295
|
+
|
|
296
|
+
Any [Gemini](https://ai.google.dev) model, called directly through Google's AI SDK provider. The key is an API key from Google AI Studio:
|
|
297
|
+
|
|
298
|
+
```ts blume.config.ts lineNumbers
|
|
299
|
+
import { defineConfig } from "blume";
|
|
300
|
+
import { gemini } from "blume/ai";
|
|
301
|
+
|
|
302
|
+
export default defineConfig({
|
|
303
|
+
ai: {
|
|
304
|
+
assistant: {
|
|
305
|
+
enabled: true,
|
|
306
|
+
provider: gemini({ model: "gemini-3.5-flash" }),
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Grok
|
|
313
|
+
|
|
314
|
+
Any [xAI](https://x.ai/api) Grok model, called directly through xAI's AI SDK provider:
|
|
315
|
+
|
|
316
|
+
```ts blume.config.ts lineNumbers
|
|
317
|
+
import { defineConfig } from "blume";
|
|
318
|
+
import { grok } from "blume/ai";
|
|
319
|
+
|
|
320
|
+
export default defineConfig({
|
|
321
|
+
ai: {
|
|
322
|
+
assistant: {
|
|
323
|
+
enabled: true,
|
|
324
|
+
provider: grok({ model: "grok-4.7" }),
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
187
330
|
### Vercel AI Gateway
|
|
188
331
|
|
|
189
332
|
The default. `model` is a `provider/model` string, so you switch models by changing it (`openai/gpt-5.5`, `anthropic/claude-sonnet-4-5`, and so on) with no provider SDK to install. The gateway reads `AI_GATEWAY_API_KEY` from your environment and is wired up automatically when you deploy on Vercel, where it can also authenticate with the deployment's OIDC token:
|
|
@@ -265,29 +408,6 @@ export default defineConfig({
|
|
|
265
408
|
|
|
266
409
|
`baseUrl` overrides the preset endpoint (`https://api.inkeep.com/v1`).
|
|
267
410
|
|
|
268
|
-
### OpenAI-compatible endpoints
|
|
269
|
-
|
|
270
|
-
Any endpoint that speaks the OpenAI API works through `openaiCompatible()` — supply its `baseUrl`, the `model` it serves, and the env var holding its key. A generic endpoint has no preset for any of these, so all three are required; `name` is the provider name the AI SDK reports and defaults to `openai-compatible`:
|
|
271
|
-
|
|
272
|
-
```ts blume.config.ts lineNumbers
|
|
273
|
-
import { defineConfig } from "blume";
|
|
274
|
-
import { openaiCompatible } from "blume/ai";
|
|
275
|
-
|
|
276
|
-
export default defineConfig({
|
|
277
|
-
ai: {
|
|
278
|
-
assistant: {
|
|
279
|
-
enabled: true,
|
|
280
|
-
provider: openaiCompatible({
|
|
281
|
-
baseUrl: "https://my-gateway.example.com/v1",
|
|
282
|
-
apiKeyEnv: "MY_GATEWAY_API_KEY",
|
|
283
|
-
model: "gpt-4o",
|
|
284
|
-
name: "my-gateway",
|
|
285
|
-
}),
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
});
|
|
289
|
-
```
|
|
290
|
-
|
|
291
411
|
### Options every adapter takes
|
|
292
412
|
|
|
293
413
|
**`apiKeyEnv`** points an adapter at a different env var than its default — `gateway({ apiKeyEnv: "DOCS_GATEWAY_KEY" })` reads that variable instead of `AI_GATEWAY_API_KEY`, and the missing-secret warning at `blume dev`/`build` checks it too. Until the key is set, the deployed route answers `503` with a message naming the variable. The route reads a request body only up to 64 KB and answers anything larger with `413`.
|
|
@@ -295,7 +415,7 @@ export default defineConfig({
|
|
|
295
415
|
**`headers`** sends static request headers with every call — a caller-identifying header for a shared backend, say, so its own observability or rate limiting can tell your docs apart from other traffic:
|
|
296
416
|
|
|
297
417
|
```ts blume.config.ts lineNumbers
|
|
298
|
-
provider:
|
|
418
|
+
provider: openai({
|
|
299
419
|
baseUrl: "https://llm.internal.example.com/v1",
|
|
300
420
|
apiKeyEnv: "INTERNAL_LLM_API_KEY",
|
|
301
421
|
model: "gpt-4o",
|
|
@@ -314,7 +434,7 @@ provider: gateway({
|
|
|
314
434
|
}),
|
|
315
435
|
```
|
|
316
436
|
|
|
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).
|
|
437
|
+
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 `openai()` or an OpenAI model behind the gateway, `anthropic` for `anthropic()`, `google` for `gemini()`, `xai` for `grok()`, `openrouter` on OpenRouter, and the adapter's `name` on an OpenAI-compatible endpoint). Enabling the assistant also turns on React for the in-page island — see [Customization](/docs/configuration/customization#interactive-islands).
|
|
318
438
|
|
|
319
439
|
## Reasoning
|
|
320
440
|
|
|
@@ -328,10 +448,13 @@ Each adapter sends the level as its backend's own reasoning control, which is wh
|
|
|
328
448
|
|
|
329
449
|
| Adapter | What the level becomes |
|
|
330
450
|
| --- | --- |
|
|
451
|
+
| `openai()` | OpenAI's `reasoning_effort`. With a `baseUrl`, it's sent as `reasoning_effort` in the request, so the endpoint has to accept that parameter. |
|
|
452
|
+
| `anthropic()` | Claude's thinking: an effort level on models with adaptive thinking, a thinking budget on older ones, and thinking off for `"none"`. |
|
|
453
|
+
| `gemini()` | Gemini's thinking level, or a thinking budget on models that take one. |
|
|
454
|
+
| `grok()` | xAI's reasoning effort, on the models that offer one. |
|
|
331
455
|
| `gateway()` | The AI SDK's [`reasoning`](https://ai-sdk.dev/docs/ai-sdk-core/reasoning) call option, which the gateway maps to the model's own setting — OpenAI's `reasoning_effort`, for example. |
|
|
332
456
|
| `openrouter()` | OpenRouter's `reasoning.effort`, set on the model. Its provider ignores the AI SDK's call option, so the level is placed where OpenRouter reads it. |
|
|
333
457
|
| `llmgateway()` | `reasoning_effort` in the request, through the AI SDK's call option. |
|
|
334
|
-
| `openaiCompatible()` | `reasoning_effort` in the request, so the endpoint has to accept that parameter. |
|
|
335
458
|
| `inkeep()` | Not available. Inkeep runs its own QA pipeline with no reasoning control, so the adapter has no `reasoning` option and setting one is a config error. |
|
|
336
459
|
|
|
337
460
|
The model has to support the level you pick: OpenAI rejects a level a model doesn't offer (`"none"` and `"xhigh"` exist only on some), so check the model's documentation before setting one. Leave it unset to keep the model's default. Like [retrieval size](#retrieval-size), it trades thoroughness for time-to-first-token, and answers stay grounded either way.
|
|
@@ -342,16 +465,45 @@ With an [analytics provider](/docs/configuration/analytics) configured, the assi
|
|
|
342
465
|
|
|
343
466
|
| Event | When | Properties |
|
|
344
467
|
| --- | --- | --- |
|
|
345
|
-
| `ask` | A question is sent | `path`, `questionChars` |
|
|
346
|
-
| `ask_answer` | The answer finishes streaming | `path`, `questionChars`, `ms`, `chars` |
|
|
347
|
-
| `ask_error` | The request fails, breaks, or comes back empty | `path`, `questionChars`, `ms`, `status` |
|
|
468
|
+
| `ask` | A question is sent | `path`, `questionChars`, `thread` |
|
|
469
|
+
| `ask_answer` | The answer finishes streaming | `path`, `questionChars`, `thread`, `ms`, `chars` |
|
|
470
|
+
| `ask_error` | The request fails, breaks, or comes back empty | `path`, `questionChars`, `thread`, `ms`, `status` |
|
|
348
471
|
|
|
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.
|
|
472
|
+
`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, `thread` the conversation's id (new when the reader clears it), `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
473
|
|
|
351
474
|
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
475
|
|
|
353
476
|
## Rate limiting
|
|
354
477
|
|
|
355
|
-
The `POST /api/ask` endpoint is **unauthenticated** — it has to be, so the in-page assistant can call it. Blume validates each request — rejecting malformed bodies, capping it to 1–40 messages, and accepting only `user`/`assistant` roles so a caller can't inject their own system prompt and repurpose the route as a general LLM proxy — to bound how much a single call can spend against your model
|
|
478
|
+
The `POST /api/ask` endpoint is **unauthenticated** — it has to be, so the in-page assistant can call it. Blume validates each request — rejecting malformed bodies, capping it to 1–40 messages, and accepting only `user`/`assistant` roles so a caller can't inject their own system prompt and repurpose the route as a general LLM proxy — to bound how much a single call can spend against your model. [Rate limiting](/docs/configuration/rate-limiting), on by default, then caps how many questions one reader can ask: past it, the panel tells them to try again in a few minutes.
|
|
479
|
+
|
|
480
|
+
## Bot protection
|
|
481
|
+
|
|
482
|
+
Rate limiting counts by IP address, so a script that spreads its questions across many addresses slips past it. To stop scripts, add a bot check from `blume/captcha`: before each question, the panel gets a token from the check, and the route verifies it with the provider before the model runs. Most readers never see a challenge; the check only asks for a click when it's unsure.
|
|
483
|
+
|
|
484
|
+
```ts blume.config.ts lineNumbers
|
|
485
|
+
import { defineConfig } from "blume";
|
|
486
|
+
import { turnstile } from "blume/captcha";
|
|
487
|
+
|
|
488
|
+
export default defineConfig({
|
|
489
|
+
ai: {
|
|
490
|
+
assistant: {
|
|
491
|
+
enabled: true,
|
|
492
|
+
captcha: turnstile({ siteKey: "0x4AAAAAAA…" }),
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
| Adapter | Provider | Secret key env var |
|
|
499
|
+
| --- | --- | --- |
|
|
500
|
+
| `turnstile()` | [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/) | `TURNSTILE_SECRET_KEY` |
|
|
501
|
+
| `hcaptcha()` | [hCaptcha](https://www.hcaptcha.com), run invisibly | `HCAPTCHA_SECRET_KEY` |
|
|
502
|
+
|
|
503
|
+
The site key goes to the browser and is safe to commit; the secret key stays on the server. For Turnstile, create a widget in the Cloudflare dashboard and choose **Managed** or **Invisible** mode. Until the secret is set, the assistant answers that it isn't configured, and `blume build` warns. The provider's script loads with a reader's first question, not with the page.
|
|
504
|
+
|
|
505
|
+
A failed check shows the reader a translated "we couldn't check that you're human" notice, and the route answers `403`. Both providers publish test keys that always pass, for trying the check locally: Turnstile's `1x00000000000000000000BB` with the secret `1x0000000000000000000000000000000AA`, and hCaptcha's `10000000-ffff-ffff-ffff-000000000001` with `0x0000000000000000000000000000000000000000`.
|
|
506
|
+
|
|
507
|
+
With an [external endpoint](#external-endpoint), the panel still sends the token, as `captcha` in the request body, for your backend to verify.
|
|
356
508
|
|
|
357
509
|
The endpoint is advertised in the [agent readability manifest](/docs/discoverability/agent-discovery#agent-readability) alongside the rest of the site's machine-readable surface.
|