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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cookie consent
|
|
3
|
+
description: Ask readers before analytics runs, with Blume's own banner or a hosted consent manager like Osano or Ethyca, as an adapter in blume.config.ts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Set `consent` in `blume.config.ts` to ask readers before your [analytics](/docs/configuration/analytics) runs. It takes one adapter from `blume/consent`: Blume's own banner, or a hosted consent manager you already use.
|
|
7
|
+
|
|
8
|
+
```ts blume.config.ts lineNumbers
|
|
9
|
+
import { defineConfig } from "blume";
|
|
10
|
+
import { googleAnalytics, posthog } from "blume/analytics";
|
|
11
|
+
import { native } from "blume/consent";
|
|
12
|
+
|
|
13
|
+
export default defineConfig({
|
|
14
|
+
analytics: [
|
|
15
|
+
googleAnalytics({ id: "G-XXXXXXXXXX" }),
|
|
16
|
+
posthog({ key: "phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }),
|
|
17
|
+
],
|
|
18
|
+
consent: native({ policy: "/privacy" }),
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
With `consent` set, every analytics adapter waits. Its tags are in the page but don't run until the reader allows analytics, and then they run in the order you listed them. A reader who declines never loads them, and doesn't see the [page rating](/docs/configuration#page-feedback) or its comment box either, since their answers would have nowhere to go. Vercel Web Analytics loads either way, since its component isn't a tag Blume can hold, but it sends nothing until the reader allows analytics.
|
|
23
|
+
|
|
24
|
+
:::note
|
|
25
|
+
Consent is opt-in. Without `consent`, analytics runs as the page loads, as it always has.
|
|
26
|
+
:::
|
|
27
|
+
|
|
28
|
+
## Adapters
|
|
29
|
+
|
|
30
|
+
| Adapter | Consent manager | Required |
|
|
31
|
+
| ---------- | ------------------------------------ | ------------------------ |
|
|
32
|
+
| `native()` | [Blume's own banner](#blumes-banner) | — |
|
|
33
|
+
| `osano()` | [Osano](#osano) | `customerId`, `configId` |
|
|
34
|
+
| `ethyca()` | [Ethyca](#ethyca) | `privacyCenter` |
|
|
35
|
+
|
|
36
|
+
## Blume's banner
|
|
37
|
+
|
|
38
|
+
`native()` shows a small card at the foot of the page until the reader answers, with **Accept** and **Decline** side by side, so declining is as easy as accepting. The answer stays in the reader's browser, and the banner doesn't come back once they've given it.
|
|
39
|
+
|
|
40
|
+
```ts blume.config.ts lineNumbers
|
|
41
|
+
consent: native({
|
|
42
|
+
policy: "/privacy", // optional: links the banner to your privacy policy
|
|
43
|
+
}),
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`policy` takes a page route or a full URL. The banner's text is translated in every built-in language. To change the wording, override the `consent` [UI strings](/docs/content/i18n#translated-ui) (`message`, `accept`, `decline`, `policy`, `settings`) with `i18n.ui`.
|
|
47
|
+
|
|
48
|
+
The banner also works in `blume dev`, so you can see it while you write. Analytics itself only loads in production builds.
|
|
49
|
+
|
|
50
|
+
## Osano
|
|
51
|
+
|
|
52
|
+
`osano()` loads [Osano Cookie Consent](https://www.osano.com), which shows the banner and drawer you set up in the Osano dashboard. Blume runs your analytics once Osano reports the reader's analytics consent, and again whenever they change it. Both IDs come from the script tag Osano gives you: `https://cmp.osano.com/<customerId>/<configId>/osano.js`.
|
|
53
|
+
|
|
54
|
+
```ts blume.config.ts lineNumbers
|
|
55
|
+
import { osano } from "blume/consent";
|
|
56
|
+
|
|
57
|
+
consent: osano({ customerId: "AzZdRbSIoEzZU2", configId: "7b9e2f4a-…" }),
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Osano decides what counts as consent in each region, following the compliance mode you chose in its dashboard.
|
|
61
|
+
|
|
62
|
+
## Ethyca
|
|
63
|
+
|
|
64
|
+
`ethyca()` loads [Ethyca's Fides](https://www.ethyca.com) consent manager from your privacy center, which shows the banner and modal you set up in Fides. Blume runs your analytics once the reader's analytics notice is on: opted in, not opted out of, or a notice that only asks them to acknowledge it.
|
|
65
|
+
|
|
66
|
+
```ts blume.config.ts lineNumbers
|
|
67
|
+
import { ethyca } from "blume/consent";
|
|
68
|
+
|
|
69
|
+
consent: ethyca({
|
|
70
|
+
privacyCenter: "https://privacy.example.com",
|
|
71
|
+
propertyId: "FDS-XXXXXX", // optional: which Fides property to load
|
|
72
|
+
notice: "analytics", // optional: the notice key that covers analytics
|
|
73
|
+
}),
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`notice` defaults to `analytics`. Set it when your Fides notice for analytics has another key.
|
|
77
|
+
|
|
78
|
+
Like analytics, Osano and Fides load in production builds only, so testing in `blume dev` creates no consent records. Preview them with `blume build` and `blume preview`.
|
|
79
|
+
|
|
80
|
+
## Changing an answer
|
|
81
|
+
|
|
82
|
+
When `consent` is set, the site footer shows a **Cookie settings** link. It reopens Blume's banner, or the hosted consent manager's preferences. If you replace the footer with your own [`Footer`](/docs/configuration/customization#layout-slots), give any element the `data-blume-consent-open` attribute to make it the link:
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<button data-blume-consent-open type="button">Cookie settings</button>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A reader who allows analytics and later takes it back gets a page reload, the one way to stop scripts that already started. From then on, analytics stays off.
|
|
89
|
+
|
|
90
|
+
## Your own scripts
|
|
91
|
+
|
|
92
|
+
Hold any script of your own the same way: give it `type="text/plain"` and `data-blume-consent="analytics"`, and it runs when the reader allows analytics. To react to answers yourself, listen for the `blume:consent` event on `window`, whose `detail.analytics` is the reader's choice. `window.blumeConsent.analytics` holds the current one: `null` until the consent manager answers, then `true` or `false`.
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
window.addEventListener("blume:consent", (event) => {
|
|
96
|
+
if (event.detail.analytics) {
|
|
97
|
+
// start something that needs consent
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
```
|
|
@@ -79,7 +79,7 @@ import Logo from "./components/Logo.astro";
|
|
|
79
79
|
export default defineComponents({
|
|
80
80
|
layout: {
|
|
81
81
|
Logo, // brand mark + title in the header
|
|
82
|
-
Footer, // site-wide footer
|
|
82
|
+
Footer, // site-wide footer, in place of the one `footer` configures
|
|
83
83
|
},
|
|
84
84
|
});
|
|
85
85
|
```
|
|
@@ -97,12 +97,12 @@ Wired slots:
|
|
|
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
|
+
| `Feedback` | The "Was this page helpful?" rating below the article (rendered only when [`feedback`](/docs/configuration#page-feedback) is on) | `strings`, `comments` |
|
|
101
101
|
| `PageHeader` | An injection point above the article (no built-in) | `page`, `headings`, `route` |
|
|
102
102
|
| `PageFooter` | An injection point below the article (no built-in) | `page`, `headings`, `route` |
|
|
103
|
-
| `Footer` |
|
|
103
|
+
| `Footer` | The [site footer](/docs/configuration#footer) after the content grid: links, social profiles, the repository link, and Cookie settings | `footer`, `locale`, `site`, `navigation`, `ui` |
|
|
104
104
|
|
|
105
|
-
`PageHeader
|
|
105
|
+
`PageHeader` and `PageFooter` have no built-in component — they render nothing until you set them, which makes them handy injection points for a promo banner or a "last updated" note. The built-in `Footer` holds the only links to your repository and social profiles, so a `Footer` override (the place for a marketing footer the config can't express) should keep them. With `consent` set, also keep a way to reopen it: any element with `data-blume-consent-open` works as the [Cookie settings](/docs/configuration/consent#changing-an-answer) link.
|
|
106
106
|
|
|
107
107
|
Layout slots accept the same [three reference forms](#reference-form) as MDX overrides, so a slot can be a path string or a hydrated descriptor (`{ component, client }`) when you want an interactive header or footer.
|
|
108
108
|
|
|
@@ -191,4 +191,6 @@ From then on, run the app through its own scripts (`npm run dev`, `npm run build
|
|
|
191
191
|
|
|
192
192
|
### What eject keeps
|
|
193
193
|
|
|
194
|
+
The routes the hidden runtime serves as pages are written into the app's `src/pages`, so it answers the same URLs: each page's Markdown twin, the [404 page](/docs/advanced/custom-pages#404-page) with its `/404.md` and `/404.json` twins, the hosted MCP server, and the [JSON docs API](/docs/discoverability/json-api) at `/api/docs/…` and `/openapi.json` that `llms.txt` and the not-found page point agents to.
|
|
195
|
+
|
|
194
196
|
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.
|
|
@@ -68,6 +68,8 @@ export default defineConfig({
|
|
|
68
68
|
llmsTxt: true,
|
|
69
69
|
// AI Catalog / ARD manifest at /.well-known/ai-catalog.json (needs deployment.site)
|
|
70
70
|
catalog: true,
|
|
71
|
+
// Your site's agent skill at /skill.md (needs deployment.site)
|
|
72
|
+
skillMd: true,
|
|
71
73
|
// MCP server (needs server output)
|
|
72
74
|
mcp: {
|
|
73
75
|
enabled: false,
|
|
@@ -99,6 +101,7 @@ export default defineConfig({
|
|
|
99
101
|
| `description` | — | Default meta description, used for SEO and OG. |
|
|
100
102
|
| `logo` | — | Brand mark and/or wordmark shown in the header. |
|
|
101
103
|
| `banner` | — | Site-wide announcement bar above the header. |
|
|
104
|
+
| `footer` | — | A row of links and social profile icons below the content. |
|
|
102
105
|
|
|
103
106
|
### Logo
|
|
104
107
|
|
|
@@ -185,6 +188,42 @@ banner: {
|
|
|
185
188
|
|
|
186
189
|
When `dismissible` is on, the bar shows a close button and stays hidden for that visitor afterward. The dismissal key defaults to the content text, so editing the message brings the banner back; set a stable `id` to keep it dismissed across edits.
|
|
187
190
|
|
|
191
|
+
On a [multilingual](/docs/content/i18n) site, `content` and the link's `text` also take a map of locale code to text. Each page shows its own locale's entry, falling back to the default locale's, and the link moves into the reader's locale whenever that locale serves the page:
|
|
192
|
+
|
|
193
|
+
```ts blume.config.ts lineNumbers
|
|
194
|
+
banner: {
|
|
195
|
+
content: { en: "Version 2.0 is here!", fr: "La version 2.0 est là !" },
|
|
196
|
+
link: {
|
|
197
|
+
text: { en: "Read the release notes", fr: "Lire les notes de version" },
|
|
198
|
+
href: "/changelog",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A translated banner keys dismissal on the default locale's text, so one dismissal hides it in every language.
|
|
204
|
+
|
|
205
|
+
### Footer
|
|
206
|
+
|
|
207
|
+
Every page ends with a footer: one row, as tall as the header, with your links on one side and social profile icons on the other. The footer is the only place Blume links your social profiles and repository, so the [`github`](#github) repository comes first among the icons whenever it's set. `footer` adds the rest:
|
|
208
|
+
|
|
209
|
+
```ts blume.config.ts lineNumbers
|
|
210
|
+
footer: {
|
|
211
|
+
links: [
|
|
212
|
+
{ label: "Pricing", href: "https://acme.dev/pricing" },
|
|
213
|
+
{ label: "Changelog", href: "/changelog" },
|
|
214
|
+
{ label: "Blog", href: "https://acme.dev/blog" },
|
|
215
|
+
],
|
|
216
|
+
socials: {
|
|
217
|
+
x: "https://x.com/acme",
|
|
218
|
+
discord: "https://discord.gg/acme",
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`links` appear in the order you write them. Write internal links as if mounted at root; an external link opens in a new tab. On a [multilingual](/docs/content/i18n#per-locale-navigation) site, a `label` can be a map of locale code to text, and an internal link moves into the reader's locale whenever that locale serves the page. `socials` maps a platform to its profile URL, and the icons appear in the order you write them. The platforms are `bluesky`, `discord`, `facebook`, `github`, `hacker-news`, `instagram`, `linkedin`, `medium`, `podcast`, `reddit`, `slack`, `telegram`, `threads`, `website`, `x`, and `youtube`. A `github` social replaces the repository link; hide that link with [`navigation.repo`](/docs/content/navigation#repository-link).
|
|
224
|
+
|
|
225
|
+
A site with no links, profiles, or repository has no footer. Custom pages built on `PageLayout` show it too, unless they fill the layout's own `footer` slot. To replace it entirely, override the [`Footer` layout slot](/docs/configuration/customization#layout-slots).
|
|
226
|
+
|
|
188
227
|
## Content
|
|
189
228
|
|
|
190
229
|
Where your content lives and how Blume discovers it. See [Pages](/docs/content) for how files become routes.
|
|
@@ -272,7 +311,7 @@ A key can be declared site-wide or per-type, not both. See [Per-type keys](/docs
|
|
|
272
311
|
|
|
273
312
|
## GitHub
|
|
274
313
|
|
|
275
|
-
Point Blume at your repository with `github`. It powers the
|
|
314
|
+
Point Blume at your repository with `github`. It powers the footer's [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub** [page action](/docs/content/navigation#page-actions):
|
|
276
315
|
|
|
277
316
|
```ts blume.config.ts lineNumbers
|
|
278
317
|
github: {
|
|
@@ -420,7 +459,21 @@ export default defineConfig({
|
|
|
420
459
|
});
|
|
421
460
|
```
|
|
422
461
|
|
|
423
|
-
A reader's answer is sent as a `feedback` [custom event](/docs/configuration/analytics#custom-events) — with `helpful` (`"yes"` or `"no"`), `path`, and `title` — through every configured analytics adapter that has an event API, and as a `blume:track` event on `window`. Without an analytics adapter, the answer isn't recorded anywhere. The question and thank-you text are UI strings you can translate with [`i18n.ui`](/docs/content/i18n#translated-ui).
|
|
462
|
+
A reader's answer is sent as a `feedback` [custom event](/docs/configuration/analytics#custom-events) — with `helpful` (`"yes"` or `"no"`), `path`, and `title` — through every configured analytics adapter that has an event API, and as a `blume:track` event on `window`. Without an analytics adapter, the answer isn't recorded anywhere, and with [cookie consent](/docs/configuration/consent) on, the rating only shows for readers who allowed analytics, since from anyone else an answer would go nowhere. The question and thank-you text are UI strings you can translate with [`i18n.ui`](/docs/content/i18n#translated-ui).
|
|
463
|
+
|
|
464
|
+
### Written feedback
|
|
465
|
+
|
|
466
|
+
To hear more than yes or no, turn on comments. After rating a page, the reader gets a box to say what worked or what's missing:
|
|
467
|
+
|
|
468
|
+
```ts blume.config.ts
|
|
469
|
+
export default defineConfig({
|
|
470
|
+
feedback: { comments: true },
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
A comment is sent as a `feedback_comment` event with the same `helpful`, `path`, and `title` as the rating, plus the reader's `comment` (up to 1,000 characters). It goes through the same adapters as the rating, so read comments where you read events. Providers cap how long a property value can be, so a long comment may arrive cut short: Google Analytics keeps 100 characters and Mixpanel 255, while PostHog and Segment pass comments whole. Plausible groups identical values rather than listing each comment, so it suits ratings better than comments. Readers can write anything in the box, so treat comments as they might contain personal details.
|
|
475
|
+
|
|
476
|
+
The box's label and button are UI strings too (`feedback.comment` and `feedback.send`).
|
|
424
477
|
|
|
425
478
|
## Feature options
|
|
426
479
|
|
|
@@ -432,11 +485,15 @@ Each of these has its own guide. The config field is the entry point:
|
|
|
432
485
|
| `navigation` | Explicit sidebar and header tabs | [Navigation](/docs/content/navigation) |
|
|
433
486
|
| `search` | Provider (Orama, Pagefind, Algolia, and more) and indexing | [Search](/docs/configuration/search) |
|
|
434
487
|
| `markdown` | Markdown rendering options — code blocks, heading anchors, image zoom | [Syntax](/docs/content/syntax) |
|
|
435
|
-
| `agents` | `llms.txt`, Markdown mirrors, the JSON API, the hosted MCP server, skills, and discovery manifests for coding agents | [SEO and
|
|
488
|
+
| `agents` | `llms.txt`, Markdown mirrors, the JSON API, the hosted MCP server, skills, and discovery manifests for coding agents | [SEO and GEO](/docs/discoverability) |
|
|
436
489
|
| `ai` | The in-page assistant and the Open in chat action | [Assistant](/docs/configuration/assistant) |
|
|
490
|
+
| `rateLimit` | How often one reader can call the assistant, the playground proxy, and server-side search, with an adapter from `blume/ratelimit` (on by default) | [Rate limiting](/docs/configuration/rate-limiting) |
|
|
491
|
+
| `narration` | The "Listen to this page" player, with browser voices or voices generated at build | [Narration](/docs/configuration/narration) |
|
|
437
492
|
| `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) |
|
|
493
|
+
| `api` | The server, auth, and Try it panel of hand-written endpoint pages | [Hand-written API pages](/docs/references/api-pages) |
|
|
438
494
|
| `analytics` | Adapters from `blume/analytics` — PostHog, Google Analytics, Plausible, Mixpanel, Segment, and more — plus custom scripts | [Analytics](/docs/configuration/analytics) |
|
|
439
|
-
| `
|
|
495
|
+
| `consent` | A cookie consent adapter from `blume/consent` — Blume's own banner, Osano, or Ethyca — that holds analytics until the reader allows it | [Cookie consent](/docs/configuration/consent) |
|
|
496
|
+
| `seo` | Metadata, OG images, feeds, structured data, sitemap, robots | [SEO and GEO](/docs/discoverability) |
|
|
440
497
|
| `deployment` | A host adapter from `blume/deploy`, or `{ site, base }` for a static build | [Deployment](/docs/deployment) |
|
|
441
498
|
| `redirects` | Permanent and temporary redirects | [Deployment](/docs/deployment#redirects) |
|
|
442
499
|
| `integrations` | Astro integrations appended after Blume's built-ins | [Customization](/docs/configuration/customization#astro-integrations) |
|
|
@@ -446,7 +503,8 @@ Each of these has its own guide. The config field is the entry point:
|
|
|
446
503
|
| `export` | Reader-facing PDF and EPUB downloads | [Export](/docs/configuration/export) |
|
|
447
504
|
| `examples` | Where `<Component path>` example previews live, and the CSS injected into their frames | [Component](/docs/content/components#component) |
|
|
448
505
|
| `react` | React island behavior — the React Compiler's auto-memoization | [Islands](/docs/content/islands#frameworks) |
|
|
449
|
-
| `
|
|
506
|
+
| `variables` | Values `{{name}}` reads in any page | [Variables](/docs/content/variables) |
|
|
507
|
+
| `feedback` | The "Was this page helpful?" rating at the foot of each page (default `true`), and optional written comments after it | [Page feedback](#page-feedback) |
|
|
450
508
|
|
|
451
509
|
## Precedence
|
|
452
510
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Narration
|
|
3
|
+
description: A "Listen to this page" player that reads each page aloud and highlights the sentence being read, using the reader's own browser voices or neural voices generated at build time.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Narration adds a **Listen to this page** player under each page's description. Readers press play, and the page is read aloud from the title down while the sentence being read is highlighted and kept in view. It's opt-in:
|
|
7
|
+
|
|
8
|
+
```ts blume.config.ts lineNumbers
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
narration: true,
|
|
11
|
+
});
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
With `true`, pages are read with the reader's own [browser voices](#browser-voices): no key, no build step, and it works on any host, static or not. Add a `provider` to use [generated voices](#generated-voices) instead.
|
|
15
|
+
|
|
16
|
+
## What it reads
|
|
17
|
+
|
|
18
|
+
Narration reads the page title, the description, headings, paragraphs, list items, and card text, in order. Some components are introduced with a short spoken cue so the listener knows what kind of content comes next:
|
|
19
|
+
|
|
20
|
+
| Component | Cue |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| [Callouts](/docs/content/syntax#callouts) | "Note.", "Tip.", "Warning.", and so on |
|
|
23
|
+
| [Steps](/docs/content/components#steps) | "Step 1.", "Step 2." |
|
|
24
|
+
| [Tabs](/docs/content/components#tabs) | "macOS tab." Every tab is read, not just the open one |
|
|
25
|
+
| [Accordions](/docs/content/components#accordion) and [Expandable](/docs/content/components#expandable) | "Expandable section." |
|
|
26
|
+
|
|
27
|
+
When narration reaches a closed section or a tab that isn't showing, it opens it, so the highlight always lands on text the reader can see.
|
|
28
|
+
|
|
29
|
+
Code blocks, tables, images, video, diagrams, math, type tables, file trees, and live component previews are skipped. They don't make sense read aloud.
|
|
30
|
+
|
|
31
|
+
The player only appears on pages with enough prose to be worth listening to, roughly 50 words. Very short pages, and pages that are mostly code or API fields, don't get one.
|
|
32
|
+
|
|
33
|
+
## Listening
|
|
34
|
+
|
|
35
|
+
While a page plays, the player stays pinned under the header with play and pause, previous and next sentence, a progress slider, the time read against the page's length, and a speed control from 0.8× to 2×. The speed is remembered for the next page.
|
|
36
|
+
|
|
37
|
+
The page scrolls to keep the sentence being read in view. If the reader scrolls away, following stops and the audio carries on; scrolling back to the sentence, or pressing **Follow along**, picks it up again. Opening another page stops the narration.
|
|
38
|
+
|
|
39
|
+
## Browser voices
|
|
40
|
+
|
|
41
|
+
`narration: true` reads the page with the [Web Speech API](https://developer.mozilla.org/docs/Web/API/SpeechSynthesis) and the voices on the reader's device, picking one for the page's language. The player stays hidden on a browser with no voice for that language rather than reading the page in the wrong accent.
|
|
42
|
+
|
|
43
|
+
This needs nothing from you and costs nothing, but how it sounds depends on the device: the system voices on macOS, iOS, and Windows are good, while some Linux browsers ship few or none.
|
|
44
|
+
|
|
45
|
+
## Generated voices
|
|
46
|
+
|
|
47
|
+
Pass a `provider` to generate audio with a neural speech model at build time. The provider is the same [`gateway()`](/docs/configuration/assistant#adapters) adapter the assistant uses, pointed at a speech model:
|
|
48
|
+
|
|
49
|
+
```ts blume.config.ts lineNumbers
|
|
50
|
+
import { defineConfig } from "blume";
|
|
51
|
+
import { gateway } from "blume/ai";
|
|
52
|
+
|
|
53
|
+
export default defineConfig({
|
|
54
|
+
narration: {
|
|
55
|
+
provider: gateway({
|
|
56
|
+
model: "openai/tts-1-hd",
|
|
57
|
+
voice: "alloy",
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`blume build` reads each built page exactly as the player will, splits it into sentences, and generates one clip per sentence through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). The clips and a small manifest per page are written into your build as static files under `/blume-narration/`, so replays cost nothing and nothing runs on a server.
|
|
64
|
+
|
|
65
|
+
Before it generates anything, the build logs how many new clips it needs and how many characters they cover, so the cost is visible up front:
|
|
66
|
+
|
|
67
|
+
```txt
|
|
68
|
+
Generating narration: 214 new clip(s), 15,880 characters, with openai/tts-1-hd
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Option | Default | Description |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| `model` | `openai/tts-1-hd` | A gateway speech model: `openai/tts-1`, `openai/tts-1-hd`, `fish-audio/s2.1-pro`, `spacexai/grok-tts`, and others the gateway lists. |
|
|
74
|
+
| `voice` | `alloy` | The model's voice. |
|
|
75
|
+
| `instructions` | | How the voice should sound, for models that take instructions ("Read calmly, like a teacher"). |
|
|
76
|
+
| `apiKeyEnv` | `AI_GATEWAY_API_KEY` | The env var holding the gateway key. On Vercel, the build's OIDC token works too. |
|
|
77
|
+
| `headers` | | Static headers sent with every request. |
|
|
78
|
+
| `providerOptions` | | Passed to the AI SDK's `generateSpeech` as-is, for model settings Blume doesn't name. |
|
|
79
|
+
|
|
80
|
+
### Caching
|
|
81
|
+
|
|
82
|
+
Clips are cached in `node_modules/.cache/blume/narration` by what decides their sound: the sentence, its language, the model, the voice, and the instructions. A rebuild only pays for sentences that changed, and a sentence shared by many pages is generated once. Vercel and Netlify restore `node_modules` from their build caches, so a deploy there regenerates only what changed. On other CI, cache that directory between runs.
|
|
83
|
+
|
|
84
|
+
### Fallbacks
|
|
85
|
+
|
|
86
|
+
Generated voices fall back to browser voices wherever no clips exist:
|
|
87
|
+
|
|
88
|
+
- In `blume dev`, which never generates audio. Run `blume build` and `blume preview` to hear the generated voice locally.
|
|
89
|
+
- When the key isn't set at build time. The build warns and skips generation.
|
|
90
|
+
- When generation fails. The build stops at the first failed clip, since the AI SDK has already retried it, and keeps every clip it has cached.
|
|
91
|
+
|
|
92
|
+
## Languages
|
|
93
|
+
|
|
94
|
+
Narration reads each page in its content language, so on an [internationalized](/docs/content/i18n) site every locale is read in its own voice. The spoken cues and the player's labels are translated in every built-in UI language. Override them per locale under `narration` in [`i18n.ui`](/docs/content/i18n#translated-ui).
|
|
95
|
+
|
|
96
|
+
## Turning it off for a page
|
|
97
|
+
|
|
98
|
+
Set `narration: false` in a page's [frontmatter](/docs/content/frontmatter) to leave that page without a player:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
---
|
|
102
|
+
title: Changelog
|
|
103
|
+
narration: false
|
|
104
|
+
---
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Keeping content out
|
|
108
|
+
|
|
109
|
+
Add `data-blume-narration="skip"` to an element to leave it, and everything inside it, out of narration. This is how Blume skips its own type tables and previews, and it works the same on your own components and islands:
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<div data-blume-narration="skip">
|
|
113
|
+
<PricingCalculator />
|
|
114
|
+
</div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Analytics
|
|
118
|
+
|
|
119
|
+
The player sends a `narration_play` event, with the `engine` (`audio` or `browser`) and the page's `path`, when a reader starts listening, and `narration_complete` when a page plays to the end. Both go through your [analytics](/docs/configuration/analytics#custom-events) adapters like the other custom events.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rate limiting
|
|
3
|
+
description: Limit how often one reader can call the assistant, the API playground proxy, and server-side search, on by default, with the count kept in memory, Upstash, or Cloudflare.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The server routes a reader can call cost you something each time: the [assistant](/docs/configuration/assistant) spends model tokens, the [API playground](/docs/references/openapi#cors-and-the-proxy) proxy relays requests to your API, and [Mixedbread search](/docs/configuration/search#mixedbread) runs a paid query. Blume limits how often one reader, identified by IP address, can call each of them. A reader over the limit gets `429 Too Many Requests` with a `Retry-After` header, and the assistant tells them to try again in a few minutes.
|
|
7
|
+
|
|
8
|
+
Rate limiting is on by default, at 30 requests per reader per route every 10 minutes: enough that a person never meets it, low enough to stop a script. It only matters for a server build; a static site has no server routes to limit.
|
|
9
|
+
|
|
10
|
+
```ts blume.config.ts lineNumbers
|
|
11
|
+
import { defineConfig } from "blume";
|
|
12
|
+
import { upstash } from "blume/ratelimit";
|
|
13
|
+
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
rateLimit: upstash({ requests: 30, window: 600 }), // window in seconds
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Each route keeps its own count, so a busy playground session doesn't use up the reader's assistant questions.
|
|
20
|
+
|
|
21
|
+
## Where the count lives
|
|
22
|
+
|
|
23
|
+
| Adapter | Count kept in | Limit holds |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `memory()` | The server's memory (the default) | Exactly on one server; per instance on serverless hosts |
|
|
26
|
+
| `upstash()` | [Upstash Redis](https://upstash.com) | Exactly, on every host |
|
|
27
|
+
| `cloudflare()` | Cloudflare's Workers rate limiting | Per Cloudflare location |
|
|
28
|
+
|
|
29
|
+
### Memory
|
|
30
|
+
|
|
31
|
+
`memory()` needs nothing. On a server that runs as one process, like `deployment: node()`, the count is exact. Vercel, Netlify, and Cloudflare run many short-lived instances, and each keeps its own count, so there it stops a burst from one reader rather than enforcing the limit exactly. For an exact limit on those hosts, use one of the shared stores below.
|
|
32
|
+
|
|
33
|
+
```ts blume.config.ts lineNumbers
|
|
34
|
+
import { memory } from "blume/ratelimit";
|
|
35
|
+
|
|
36
|
+
rateLimit: memory({ requests: 60, window: 600 }),
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Upstash
|
|
40
|
+
|
|
41
|
+
`upstash()` keeps the count in Upstash Redis, which every instance shares. Create a database (on Vercel, add Upstash from the Marketplace), then set its REST endpoint and token as `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN`. Blume talks to Upstash's REST API, so there's nothing to install. Until both are set, the routes count in memory and `blume build` warns.
|
|
42
|
+
|
|
43
|
+
```ts blume.config.ts lineNumbers
|
|
44
|
+
import { upstash } from "blume/ratelimit";
|
|
45
|
+
|
|
46
|
+
rateLimit: upstash(),
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Cloudflare
|
|
50
|
+
|
|
51
|
+
`cloudflare()` counts with [Workers rate limiting](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/), for a site deployed with `deployment: cloudflare()`. Blume declares the binding in the Worker's config when it builds, so there's nothing to set up. Cloudflare counts per location and takes a window of 10 or 60 seconds, so its defaults are 10 requests per 60 seconds.
|
|
52
|
+
|
|
53
|
+
```ts blume.config.ts lineNumbers
|
|
54
|
+
import { cloudflare } from "blume/deploy";
|
|
55
|
+
import { cloudflare as cloudflareRateLimit } from "blume/ratelimit";
|
|
56
|
+
|
|
57
|
+
export default defineConfig({
|
|
58
|
+
deployment: cloudflare(),
|
|
59
|
+
rateLimit: cloudflareRateLimit({ requests: 10, window: 60 }),
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The binding's namespace comes from the Worker's name, so two sites on one account don't share a count. Set `namespaceId` to choose it yourself.
|
|
64
|
+
|
|
65
|
+
## Turning it off
|
|
66
|
+
|
|
67
|
+
Set `rateLimit: false` to let every request through, when your host's firewall already limits these routes, say. A request whose address the host can't tell is always let through, and so is one a shared store fails to count: the failure is logged, and readers never get locked out by a store outage.
|
|
68
|
+
|
|
69
|
+
For the assistant, pair rate limiting with a [bot check](/docs/configuration/assistant#bot-protection) to stop scripts that spread their requests across many addresses.
|
|
@@ -34,7 +34,7 @@ search: {
|
|
|
34
34
|
|
|
35
35
|
## Using search
|
|
36
36
|
|
|
37
|
-
Open search with <Badge variant="accent">⌘K</Badge> (or `Ctrl K`), or
|
|
37
|
+
Open search with the search icon in the header, with <Badge variant="accent">⌘K</Badge> (or `Ctrl K`), or by pressing `/` when you're not typing in a field. `Esc` closes it, and `⌘J` (or `Ctrl J`) toggles the result preview pane.
|
|
38
38
|
|
|
39
39
|
Queries match page **titles**, **descriptions**, and **body text**, with title matches ranked highest and descriptions above body.
|
|
40
40
|
|
|
@@ -79,7 +79,7 @@ search: {
|
|
|
79
79
|
|
|
80
80
|
Each fence's body and title (`blume.config.ts` above) become searchable; the language and fence markers don't. On `.mdx` pages the index reads components as the text they show — a Card's title, a Tab's label, a TypeTable's descriptions — using the same serializers as the [agent surfaces](/docs/discoverability/markdown), so an `agents.markdownComponents` entry covers your own components too. The option has no effect on Pagefind or Mixedbread. Expect the index to grow with your fenced content — the client index ships to every reader, hosted adapters cap record size (Algolia rejects the sync batch when one page's record exceeds its plan's limit, leaving the previous index live), and a hit inside a fence shows flattened code in the result excerpt.
|
|
81
81
|
|
|
82
|
-
On a [versioned](/docs/content/versioning) site, results default to the version being viewed, with an "All versions" toggle in the dialog footer (remembered per reader). Cross-version hits name their version on the row. Orama, FlexSearch, Algolia, and Typesense honor the scoping — hosted records carry a `version` facet, with the current docs uploaded as `"current"
|
|
82
|
+
On a [versioned](/docs/content/versioning) site, results default to the version being viewed, with an "All versions" toggle in the dialog footer (remembered per reader). Cross-version hits name their version on the row. Orama, FlexSearch, Algolia, and Typesense honor the scoping — hosted records carry a `version` facet, with the current docs uploaded as `"current"`. Pagefind, Orama Cloud, and Mixedbread don't scope by version: their results span every version, and the dialog leaves out the toggle.
|
|
83
83
|
|
|
84
84
|
## Tags
|
|
85
85
|
|
|
@@ -90,6 +90,34 @@ search:
|
|
|
90
90
|
tags: [api, reference]
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
## Ranking
|
|
94
|
+
|
|
95
|
+
Two frontmatter fields shape how a page ranks. `search.keywords` lists extra terms the page is found by, even when its text doesn't use them. It suits a page readers search for by an old name, an acronym, or a competitor's term. `search.boost` multiplies the page's relevance: above 1 moves it up, below 1 moves it down.
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
search:
|
|
99
|
+
keywords: [install, setup, getting started]
|
|
100
|
+
boost: 3
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Keep boosts to a few pages readers most often need, such as a quickstart or an overview, with values from 2 to 5. Use a value below 1, like `0.5`, for a page that should rank under others that match as well, such as a legacy guide. Keep boosts under 10: past that a boost drowns out how well a page matches, and the page tops searches it barely matches.
|
|
104
|
+
|
|
105
|
+
How much a boost moves a page depends on the adapter:
|
|
106
|
+
|
|
107
|
+
- **Orama** (the default) and **FlexSearch** multiply every match by its page's boost before sorting, so a boost of 3 counts exactly three times. The [MCP server](/docs/discoverability/mcp) and the [assistant](/docs/configuration/assistant)'s search rank the same way.
|
|
108
|
+
- **Pagefind** weighs a boosted page's text more heavily. It damps repeated matches, so the lift is smaller than the multiplier: a boost of 5 ranks the page higher, but closer to twice as high. Pagefind counts a boost over 10 as 10.
|
|
109
|
+
- **Algolia** and **Typesense** sort by boost among results that match about equally well, so a boost settles close calls without lifting a weaker match over a strong one. On Algolia, the sync adds `desc(boost)` to the index's custom ranking, after any ranking you set.
|
|
110
|
+
- **Orama Cloud** fetches a wider set of results and re-sorts them by relevance times boost.
|
|
111
|
+
- **Mixedbread** searches your own store, which Blume doesn't upload to, so boosts and keywords don't reach it.
|
|
112
|
+
|
|
113
|
+
Keywords are searched on every adapter except Mixedbread.
|
|
114
|
+
|
|
115
|
+
## Search analytics
|
|
116
|
+
|
|
117
|
+
With an [analytics adapter](/docs/configuration/analytics) configured, search reports what readers look for. A query is recorded once it settles, after a second without typing or when the reader picks a result or closes the dialog, as a `search` event with the `query`, the number of `results`, and the `path` it was searched from. A query with no results shows up with `results: 0`, so filtering on it lists what your docs don't cover yet. Picking a result sends a `search_select` event with the `query`, the result's 1-based `position`, and its `url`, for click-through.
|
|
118
|
+
|
|
119
|
+
Queries are sent as typed, up to 100 characters. Your analytics sees them the way it sees any page's search terms, so treat them as reader input. With [cookie consent](/docs/configuration/consent) on, only readers who allowed analytics are counted.
|
|
120
|
+
|
|
93
121
|
## Adapters
|
|
94
122
|
|
|
95
123
|
The client-side adapters are keyless and need no options — their clients are configured from `i18n` (or, for Pagefind, from the built HTML), so an unknown key is rejected by config validation rather than ignored. The hosted ones take **public** credentials (safe to ship to the browser) and read their **secret** admin key from an environment variable at build time — the secret never lands in the config or the client bundle. Every option you pass to a hosted adapter is kept verbatim and handed to its search client in the browser (or, for Mixedbread, to the search endpoint), so an option Blume doesn't name still reaches the SDK — Algolia's `liteClient`, the Typesense `Client`, or `OramaClient`. Those options must be JSON values — the descriptor is inlined into the generated project as a literal, so a function, `undefined`, or a bigint fails config validation with a path instead of vanishing on the way.
|
|
@@ -32,10 +32,12 @@ theme: {
|
|
|
32
32
|
}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Named presets: `blue` (default), `green`, `orange`, `pink`, `purple`, `red`, and `teal`.
|
|
35
|
+
Named presets: `blue` (default), `green`, `orange`, `pink`, `purple`, `red`, and `teal`. Each preset has a darker light-mode shade and a lighter dark-mode shade, so accent text and button labels meet WCAG AA contrast (4.5:1) in both modes.
|
|
36
36
|
|
|
37
37
|
A string applies to both color modes; pass an object for [a different accent per mode](#dark-mode-colors).
|
|
38
38
|
|
|
39
|
+
Text on an accent or action fill, like a button label or a step number, is white while white meets AA on that color, and dark otherwise. A light accent gets dark labels without any extra config. [`blume audit`](/docs/cli/audit#theme-contrast-low) checks a custom accent, action, or background against the same bar.
|
|
40
|
+
|
|
39
41
|
### Radius
|
|
40
42
|
|
|
41
43
|
`radius` sets the corner rounding shared by cards, code blocks, callouts, and inputs — `none`, `sm`, `md` (default), or `lg`.
|