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
|
@@ -29,12 +29,12 @@ Resolve `$ref` includes first (Mintlify splits config across files). Map only wh
|
|
|
29
29
|
| `background.decoration` | **drop** | no Blume equivalent |
|
|
30
30
|
| `fonts.family` / `fonts.{heading,body}.family` | `theme.fonts.{display,body}` | a curated slug (kebab-case, e.g. `space-grotesk`) maps directly; any other Google family maps to the object form `{ name: "Family Name" }`; a self-hosted font (`fonts.*.src` URLs) maps to `{ name, variants: [{ src, weight }] }` after downloading the files into the project |
|
|
31
31
|
| `banner` | `banner` (`{ content, dismissible, id, link: { href, text } }`) | **only** those keys — drop `banner.color`/`banner.type` |
|
|
32
|
-
| `styling.latex: true` | **drop the field** — block math `$$…$$` renders in `.mdx` with no config | there is **no** `markdown.math` field; inline
|
|
32
|
+
| `styling.latex: true` | **drop the field** — block math `$$…$$` renders in `.mdx` with no config | there is **no** `markdown.math` field; Blume writes inline math with two dollar signs too — convert each inline `$…$` to `$$…$$` inside the sentence, checking every pair is math and not currency or a shell variable |
|
|
33
33
|
| `styling.codeblocks.theme` | `markdown.code.theme` (`{ light, dark }`) | |
|
|
34
34
|
| `search.prompt` | **drop** | no equivalent |
|
|
35
|
-
| `seo.metatags` |
|
|
35
|
+
| `seo.metatags` | `seo.metatags`, minus the tags Blume writes itself | keep verification tokens, `theme-color`, and the like as written; Blume refuses `description`, `robots`, `og:title`/`og:description`/`og:image`/`og:url`/`og:type`/`og:site_name`, `twitter:*` card tags, and `article:*` dates, so move those to their settings (`og:image` → `seo.og` or a page's `seo.image`, `twitter:site` → `seo.x.handle`) and drop the rest. `seo.paths` (folder tags) has no equivalent: set per-page `seo` frontmatter |
|
|
36
36
|
| `seo.indexing: "all"` | `search.indexing.includeHiddenPages: true` | |
|
|
37
|
-
| `variables` (`{{name}}`) |
|
|
37
|
+
| `variables` (`{{name}}`) | `variables` | same syntax and names (letters, digits, `_`, `-`); pages keep their `{{name}}` references unchanged |
|
|
38
38
|
| `integrations.posthog` (`{ apiKey, apiHost, sessionRecording }`) | `posthog({ key, host })` in the `analytics` list | preserve the host verbatim (e.g. `us.posthog.com` — Blume's default is `us.i.posthog.com`); `sessionRecording: false` → `disable_session_recording: true`; every adapter is imported from `blume/analytics` |
|
|
39
39
|
| `integrations.ga4` (`{ measurementId }`) | `googleAnalytics({ id })` | |
|
|
40
40
|
| `integrations.gtm` (`{ tagId }`) | `googleTagManager({ id })` | |
|
|
@@ -52,8 +52,15 @@ Resolve `$ref` includes first (Mintlify splits config across files). Map only wh
|
|
|
52
52
|
| `integrations.clearbit` (`{ publicApiKey }`) | `clearbit({ key })` | |
|
|
53
53
|
| `integrations.adobe` (`{ launchUrl }`) | `adobe({ url })` | |
|
|
54
54
|
| any other `integrations` script | `script({ src, strategy, attributes })` / `vercel()` in the `analytics` list | one `script()` adapter per provider without a factory |
|
|
55
|
+
| `telemetry` (`{ enabled: false }`) | `feedback: false` | Mintlify's switch also turned off its feedback widgets; Blume sends no telemetry of its own, so only the rating needs turning off |
|
|
56
|
+
| Feedback toggles in the Mintlify dashboard (not in `docs.json`) | `feedback: { comments: true }` for contextual (written) feedback | ask whether the site collected written feedback; Blume sends comments through the `analytics` adapters as `feedback_comment` events. Code-snippet feedback, edit suggestions, and raise-issue have no equivalent: report them |
|
|
57
|
+
| `integrations.cookies` (`{ key, value }`), or a content-folder `.js` file that injects Osano or Ethyca/Fides | `consent`, imported from `blume/consent`: `osano({ customerId, configId })` (both IDs from the `cmp.osano.com/<customerId>/<configId>/osano.js` URL), `ethyca({ privacyCenter })` (the origin that serves `fides.js`), or `native()` for Blume's own banner | Mintlify's `cookies` pair only switched off its own telemetry for readers whose localStorage lacked the value; under `consent`, every `analytics` adapter waits for the reader instead. Delete the injector script. Another consent manager (Transcend, OneTrust, …) has no adapter: report it |
|
|
58
|
+
| A root `skill.md`, or `.mintlify/skills/<name>/SKILL.md` | move each into a `skills/<name>/` folder and set `agents.skills: "./skills"`; drop Mintlify-only frontmatter (`groups`) | Blume already generates a skill for the site at `/skill.md` (`agents.skillMd`, on when `deployment.site` is set); a root `skill.md` replaces it only if its `name` matches the site's title slug (`Acme Docs` → `acme-docs`), so set that name |
|
|
55
59
|
| `contextual` (`["copy","chatgpt","claude",…]`) | **mostly free** | Copy-as-Markdown and Open-in-chat are default page actions; `mcp` needs `agents.mcp.enabled` + server output (report as a follow-up) |
|
|
56
|
-
| `
|
|
60
|
+
| `footer` (`{ socials, links }`) | `footer: { socials, links }` | `socials` keeps its keys, except `twitter`/`x-twitter` → `x` and `earth-americas` → `website`; a key outside Blume's list drops (report); `links` is one row of `{ label, href }`: every column's `items` flatten into it in order, and each column's `header` drops (report) |
|
|
61
|
+
| `api.mdx` (`{ server, auth: { method, name } }`) | `api: { server, auth: { method, name } }` | the defaults for `api`-frontmatter pages |
|
|
62
|
+
| `api.playground` (`{ display, proxy }`) | `api: { playground }` | `display: "none"` → `playground: false`; `"simple"` has no site-wide form, so set `playground: simple` in each page's frontmatter; `proxy` (on by default in Mintlify) → `playground: { proxy: true }`, which needs a server-output adapter, or leave it off to send directly |
|
|
63
|
+
| `redirects` (`{ source, destination, permanent }`) | `redirects: [{ from, to, status }]` | `source` → `from`, `destination` → `to` as written, patterns included (`/beta/:slug*`, `/old/article-*`); `permanent: false` → `status: 307`, otherwise `status: 308` |
|
|
57
64
|
| `navigation.languages` | `i18n` | see i18n below |
|
|
58
65
|
|
|
59
66
|
A minimal result is often just `defineConfig({ title, logo, theme: { accent } })`.
|
|
@@ -92,13 +99,13 @@ The codemod touches **only frontmatter**. Icons in MDX **body** (`<Icon icon="
|
|
|
92
99
|
| `life-ring` | `life-buoy` | | `shield-halved` | `shield` |
|
|
93
100
|
| `rocket`/`book`/`book-open`/`code`/`terminal`/`key`/`lock`/`user`/`users`/`database`/`server`/`cloud`/`bell`/`calendar`/`star`/`heart`/`tag`/`folder`/`globe`/`link`/`download`/`upload`/`check`/`copy`/`play`/`filter` | _(same name — verify)_ |
|
|
94
101
|
|
|
95
|
-
**Rules:** verify each Lucide name exists at [lucide.dev/icons](https://lucide.dev/icons) before writing it. **Brand icons** (`fa6-brands:*` — github, discord, x, slack, linkedin…) mostly have **no** Lucide equivalent: for GitHub use the `github` config (renders the
|
|
102
|
+
**Rules:** verify each Lucide name exists at [lucide.dev/icons](https://lucide.dev/icons) before writing it. **Brand icons** (`fa6-brands:*` — github, discord, x, slack, linkedin…) mostly have **no** Lucide equivalent: for GitHub use the `github` config (renders the footer's repo link); for other socials, use `footer.socials`. Where no Lucide counterpart exists, **drop the icon and report it**. The build won't catch a wrong name for you: an unknown icon renders nothing, and it's only a warning (`BLUME_UNKNOWN_ICON`) for navigation icons — an icon prop on a component like `<Card>` fails silently — so check every name.
|
|
96
103
|
|
|
97
104
|
## Navigation: `docs.json` `navigation` → filesystem + tabs
|
|
98
105
|
|
|
99
106
|
Mintlify's `navigation` object (`tabs`/`anchors`/`dropdowns`/`products`/`versions`/`languages`/`groups`/`pages`) is fully config-declared. **Prefer restructuring content into folders**, not porting the config verbatim:
|
|
100
107
|
|
|
101
|
-
- **`groups`** (`{ group, pages: [...] }`) → a folder per group. The `group` name → the folder's humanized name or a `meta.ts` `title`. Nested groups → nested folders. `expanded: false` → `meta.ts` `collapsed: true` (inverted). `tag` → the folder/page `sidebar.badge`. **Folder metadata is a `meta.ts` module** (`export default defineMeta({ title, icon, order, collapsed, pages, display })`), a TypeScript file — **not** JSON. Blume does not read a `meta.json`, so any folder-level config you carry over from the source (or hand-write to preserve a group's label/order/collapse) must be authored as `meta.ts`. (Render mode — flat/group/page — defaults to the global `navigation.sidebar.display` in `blume.config.ts`; a folder overrides its own group with `meta.ts` `display`, or `sidebar.display` in its `index` page's frontmatter.)
|
|
108
|
+
- **`groups`** (`{ group, pages: [...] }`) → a folder per group. The `group` name → the folder's humanized name or a `meta.ts` `title`. Nested groups → nested folders. `expanded: false` → `meta.ts` `collapsed: true` (inverted). `tag` → the folder/page `sidebar.badge`. `directory` (`card`/`accordion`/`none`, on a group, tab, or the navigation itself) → `meta.ts` `directory` on that folder, or on the content root's `meta.ts` for the whole navigation; it inherits down the tree the same way, and lists the pages on the group's `root`/`index` page. **Folder metadata is a `meta.ts` module** (`export default defineMeta({ title, icon, order, collapsed, pages, display, directory })`), a TypeScript file — **not** JSON. Blume does not read a `meta.json`, so any folder-level config you carry over from the source (or hand-write to preserve a group's label/order/collapse) must be authored as `meta.ts`. (Render mode — flat/group/page — defaults to the global `navigation.sidebar.display` in `blume.config.ts`; a folder overrides its own group with `meta.ts` `display`, or `sidebar.display` in its `index` page's frontmatter.)
|
|
102
109
|
- **Config-only nested groups don't exist on disk — you must materialize them, or they flatten silently.** A nested `{ group, pages }` almost never has a matching subfolder: its pages sit **flat in the parent directory** (e.g. `platform/analytics/getting-started.mdx`, `…/quick-reference.mdx`) and the grouping lives **only** in the `docs.json` `pages` array. If you leave the files where they are, filesystem-derived nav sees one flat folder and the inner group vanishes — Mintlify's `Analytics → Reference → {…}` becomes a flat `Analytics → {…}`. To preserve it you must **either** move those pages into a real subfolder (`platform/analytics/reference/`, with a `meta.ts` for the label/`collapsed`), **or** declare the shape in an explicit `navigation.sidebar`. Walk **every** `pages` array recursively and treat any nested `group` object as a folder-move to plan, not files already in place. When inventorying the nav (workflow step 2), record the config nesting depth separately from the on-disk depth — they diverge exactly here.
|
|
103
110
|
- **This is the canonical case for `navigation.sidebar` over folders.** Materializing a purely-presentational nested group as a subfolder changes URLs (`/platform/analytics/getting-started` → `/platform/analytics/reference/getting-started`) for no reason other than a visual grouping, forcing a `redirects` entry per page. When you want to keep the nesting **and** the URLs, an explicit `navigation.sidebar` group (`{ label, items }`) is the better trade — it nests the existing routes without moving any file. Pick per group; don't reflexively flatten.
|
|
104
111
|
- **`pages`** entries are page refs (paths without extension) → files at the corresponding path. An entry that's `"GET /path"` is an OpenAPI endpoint stub → **delete it** (Blume generates these; see OpenAPI).
|
|
@@ -110,7 +117,7 @@ Mintlify's `navigation` object (`tabs`/`anchors`/`dropdowns`/`products`/`version
|
|
|
110
117
|
|
|
111
118
|
- **`dropdowns`/`products`/`versions`** → `navigation.selectors` (`{ kind, label, items: [{ label, path, icon?, description?, tag? }] }`). Use `kind` `dropdown`/`product`/`version` accordingly.
|
|
112
119
|
- **`anchors` / `navigation.global.anchors`** (persistent sidebar-top links — Blog, Changelog, Community, Contact/Support) → **`navigation.featured`** (`{ label, href, icon? }`). These pin to the **top of the sidebar, above every section, on every route** (not tab-scoped) — the right home for Mintlify's always-visible utility links. An external `href` opens in a new tab; an internal one (`/contact`) is build-time validated against your pages. Convert the FontAwesome `icon` to Lucide as usual. (This replaces the old "drop and report" for anchors.)
|
|
113
|
-
- **`navbar.links`** (plain header links — Log in, Status, Support) → **`navigation.actions`** (`[{ label, href }]`), rendered in the header left of the icon buttons. A GitHub link → the `github` config (or `navigation.repo` as a URL when the docs repo is private) instead. Header links hide on phones; a link that must survive there belongs in `featured`.
|
|
120
|
+
- **`navbar.links`** (plain header links — Log in, Status, Support) → **`navigation.actions`** (`[{ label, href }]`), rendered in the header left of the icon buttons. A GitHub link → the `github` config (or `navigation.repo` as a URL when the docs repo is private) instead; it renders in the footer, the only place Blume links a repository. Header links hide on phones; a link that must survive there belongs in `featured`.
|
|
114
121
|
- **`navbar.primary`** (the header **CTA button** — "Get Started"/"Sign Up", `type: "button"`) → **`navigation.cta`** (`{ label, href }`, singular: Blume renders exactly one filled button). A `type: "github"` primary → the `github` config instead. A route the docs don't serve (the product's own `/signup`) must be an absolute URL, or it raises `BLUME_NAV_MISSING_PAGE`.
|
|
115
122
|
- **`languages`** → `i18n`, not a selector (see below).
|
|
116
123
|
- Only fall back to an explicit `navigation.sidebar` for a shape the filesystem genuinely can't express.
|
|
@@ -123,16 +130,18 @@ Rewrite each page's MDX:
|
|
|
123
130
|
|
|
124
131
|
- **Callouts → directives** (directives are **MDX-only** — Mintlify content is already `.mdx`, so this just works; never rename a page to `.md`): `<Note>`→`:::note`, `<Tip>`→`:::tip`, `<Warning>`→`:::warning`, `<Info>`→`:::info`, `<Check>`→`:::success`, `<Danger>`/`<Error>`→`:::danger`. `<Callout type="x">` maps by type (`caution`→warning, `check`→success). A `title` attr → `:::type[Title]`. Drop `icon`/color props.
|
|
125
132
|
- **Accordions — container/item inversion!** Mintlify nests `<Accordion title="…">` inside `<AccordionGroup>`. Blume inverts: `<AccordionGroup>`→`<Accordion>` (container), and each Mintlify `<Accordion title="…">`→`<AccordionItem title="…">` (item).
|
|
126
|
-
- **`<RequestExample>`/`<ResponseExample>`** → `<CodeGroup>` (titled-fence tabs).
|
|
127
133
|
- **`<Tabs>` → `<Tabs inline>`.** Mintlify renders tabs **borderless** — a strip on a full-width rule with the content flowing beneath as prose — while Blume's `<Tabs>` defaults to a bordered box. Add `inline` to each `<Tabs>` to preserve Mintlify's appearance; every child `<Tab title="…">` is unchanged. Don't add `param` — Mintlify tabs switch in place and don't deep-link to the URL, so plain `inline` is the faithful mapping.
|
|
128
|
-
- **These pass through — Blume ships them natively:** `<Columns>`/`<Column>`, `<Expandable>`, `<Tooltip>`, `<Frame>`, `<Panel>`, `<Card>`/`<CardGroup>`, `<Tab>`, `<Steps>`/`<Step
|
|
129
|
-
- **
|
|
134
|
+
- **These pass through — Blume ships them natively:** `<Columns>`/`<Column>`, `<Expandable>`, `<Tooltip>`, `<Frame>`, `<Panel>`, `<Card>`/`<CardGroup>`, `<Tab>`, `<Steps>`/`<Step>`, `<View>` (its `title` becomes an option in the page's view picker, as on Mintlify; remap its `icon` with the table below and drop `iconType`). Keep them as-is.
|
|
135
|
+
- **Multi-word code-block titles need quotes.** Mintlify reads everything after the language as the title (` ```ts Acme SDK `); Blume reads one bare word, so write ` ```ts title="Acme SDK" `. One-word titles (` ```bash cURL `) stay as they are.
|
|
136
|
+
- **`<RequestExample>`/`<ResponseExample>` pass through.** Top-level ones pin to a column beside the page on wide screens, request above response, as on Mintlify; `dropdown` works the same.
|
|
137
|
+
- **Hand-written endpoint pages pass through.** `api: "POST /v1/users"` frontmatter, with `authMethod` (`bearer`/`basic`/`key`/`none`) and `playground` (`interactive`/`simple`/`none`), works as on Mintlify: the page's `<ParamField>`s build its Try it panel and request samples. Keep them as written.
|
|
138
|
+
- **API fields pass through.** `<ParamField>` (`path`/`query`/`header`/`body`, `type`, `required`, `deprecated`, `default`, `placeholder`) and `<ResponseField>` (`name`, `type`, `required`, `deprecated`, `default`, `pre`, `post`) render natively, nested `<Expandable>` fields included. Keep them as written. An older `<RequestField>` isn't supported: rewrite it as a `<ParamField>`.
|
|
130
139
|
- **`<Update>`** (a Mintlify changelog entry) has no component form → convert to a `type: changelog` page, or use the `githubReleases()` source.
|
|
131
|
-
- **Snippets
|
|
140
|
+
- **Snippets become includes.** Blume has no `/snippets` import mechanism; its reuse is `<include>`. Move each snippet into the content folder as a `_`-prefixed partial (`_snippets/x.mdx`, which never renders as a page), and replace `import X from "/snippets/x.mdx"` + `<X prop="v" />` with `<include prop="v">./_snippets/x.mdx</include>` on its own line. In the partial, rewrite each `{prop}` placeholder to `{{prop}}`: include attributes are props read with the variables syntax. Named string imports (`import { foo } from "/snippets/vars.mdx"`) → a site-wide `variables` entry read as `{{foo}}`, or inline the value.
|
|
132
141
|
|
|
133
142
|
## Frontmatter
|
|
134
143
|
|
|
135
|
-
Mintlify page frontmatter → Blume's strict schema. **`scripts/mintlify-codemod.mjs --write` does the mechanical rows automatically** (rename, drop, icon-remap) and flags the judgment rows (`openapi`/`asyncapi
|
|
144
|
+
Mintlify page frontmatter → Blume's strict schema. **`scripts/mintlify-codemod.mjs --write` does the mechanical rows automatically** (rename, drop, icon-remap) and flags the judgment rows (`openapi`/`asyncapi`) for you — see [Icons](#icons-fontawesome--lucide-required) for the invocation. The table is both the mapping reference and a description of exactly what the codemod does:
|
|
136
145
|
|
|
137
146
|
| Mintlify | Blume | Codemod |
|
|
138
147
|
| --- | --- | --- |
|
|
@@ -143,17 +152,25 @@ Mintlify page frontmatter → Blume's strict schema. **`scripts/mintlify-codemod
|
|
|
143
152
|
| `canonical` | `seo.canonical` | renames |
|
|
144
153
|
| `og:image` | `seo.image` | renames |
|
|
145
154
|
| `hidden: true` | valid top-level in Blume — **kept as-is**; add `noindex: true` yourself if the page must also leave the search index | left (do by hand) |
|
|
146
|
-
| `openapi`/`asyncapi
|
|
147
|
-
| `
|
|
155
|
+
| `openapi`/`asyncapi` | usually an endpoint stub → **delete the page** (Blume generates operation pages); else drop the key and keep it as a normal page | **flags** for review — never auto-deletes a page |
|
|
156
|
+
| `api`, `authMethod`, `playground` | kept as written (a hand-written endpoint page, see above) | left |
|
|
157
|
+
| `related` (a list) | kept as written: root-relative paths, URLs, and `{ Title: link }` entries render as Related pages cards | left; `related: true` has no equivalent (Blume has no automatic related pages), so drop it (report) |
|
|
158
|
+
| `mode` | `mode`, as written | `default`, `wide`, `center`, `custom`, and `frame` carry over; `assistant` has no equivalent → drop (report) |
|
|
159
|
+
| `hideFooterPagination: true` | `pagination: false` | renames (`false` drops, since the links show by default) |
|
|
160
|
+
| `keywords`, `boost` | `search.keywords`, `search.boost` | renames |
|
|
161
|
+
| `searchable: false` | `search.exclude: true` | renames (`searchable: true` drops, since pages are searchable by default) |
|
|
162
|
+
| `public`, `rss`, `groups`, `hideApiMarker`, `iconType` | **drop** (report) | drops |
|
|
148
163
|
|
|
149
164
|
The codemod leaves the source key in place and reports a conflict rather than clobbering data when a rename target already exists (e.g. a page already has `sidebar.label`) or the value is too structured to move safely — resolve those by hand. Remove any duplicate H1 in the body — `title` renders the H1. (The codemod only edits frontmatter; it never touches the body.)
|
|
150
165
|
|
|
151
166
|
## OpenAPI
|
|
152
167
|
|
|
153
|
-
Top-level `openapi`, `api.openapi`, or a per-group/per-tab `openapi` → `reference: [openapi({ sources: [{ spec, label?, route? }] })]`, with `openapi` imported from `blume/reference` (`spec` alone is the single-source shorthand; several per-tab specs become several sources, or several `openapi()` entries when they need different routes; a spec the source embedded with Scalar becomes a `scalar({ spec })` entry). A Mintlify `{ source, directory }` object: `directory` → the source's `route`. An `asyncapi` field maps the same way to `asyncapi({ … })` in the list. **Delete every per-endpoint stub page** (frontmatter `openapi: "GET /path"` or a `"GET /path"` nav entry) — Blume's native renderer generates one real page per operation. **Add a `navigation.tabs` entry pointing at the reference `route` yourself** (Mintlify's API tab maps to it); the reference does not create a header tab automatically.
|
|
168
|
+
Top-level `openapi`, `api.openapi`, or a per-group/per-tab `openapi` → `reference: [openapi({ sources: [{ spec, label?, route? }] })]`, with `openapi` imported from `blume/reference` (`spec` alone is the single-source shorthand; several per-tab specs become several sources, or several `openapi()` entries when they need different routes; a spec the source embedded with Scalar becomes a `scalar({ spec })` entry). A Mintlify `{ source, directory }` object: `directory` → the source's `route`. An `asyncapi` field maps the same way to `asyncapi({ … })` in the list. **Delete every per-endpoint stub page** (frontmatter `openapi: "GET /path"`, `openapi: "spec.json webhook name"`, or a `"GET /path"` nav entry) — Blume's native renderer generates one real page per operation and per webhook, and shows callbacks on their operation's page. **Add a `navigation.tabs` entry pointing at the reference `route` yourself** (Mintlify's API tab maps to it); the reference does not create a header tab automatically.
|
|
154
169
|
|
|
155
170
|
- **Vendor the spec.** Mintlify usually points at a spec **URL**. Copying that straight into `spec:` makes every build fetch it at build time — a single point of failure in CI/offline/behind a proxy, and a failed fetch silently drops the reference (leaving the tab pointing at a route that 404s). Prefer downloading it into the repo (`curl … -o openapi/<name>.json`) and pointing `spec` at that local path. If you keep the URL, report the dependency and consider a `prebuild` refresh-with-fallback.
|
|
156
171
|
- **Fix endpoint links.** Blume operation routes are `<route>/<slugified-tag>/<slugified-operationId>` (tag `Models` + id `listModels` → `/api-reference/models/list-models`: a camelCase id is split into kebab-case, not just lowercased — see SKILL.md "OpenAPI") — this differs from Mintlify's endpoint URLs, so **rewrite every inbound link to an operation**. `blume validate` resolves operation pages like any other route and flags the ones you miss.
|
|
172
|
+
- **Overlays carry over.** An `openapi: { source, overlays: [...] }` object → that source's `overlays` (`openapi({ spec, overlays })`, or `overlays` on each entry in `sources`), in the same order; Blume applies Overlay 1.0 and 1.1 the same way. An overlay Mintlify found only through its `extends` field (auto-discovered, not listed in `docs.json`) must be listed explicitly: Blume doesn't scan for overlays.
|
|
173
|
+
- **Code samples carry over.** `api.examples.languages` → `openapi({ codeSamples: [...] })`; Blume generates the same 18 languages and accepts Mintlify's keys and aliases (`bash`, `javascript`, `node`, `csharp`, `dotnet`, `c++`, …) as written. `api.examples.autogenerate: false` → `codeSamples: false`. `x-codeSamples` in the spec needs nothing: Blume renders them ahead of the generated samples.
|
|
157
174
|
- **Keep the "Introduction" page.** Mintlify commonly has a written intro/auth page in an "Introduction" group beside the "Endpoints" (openapi) group in the same tab. Keep it: a normal content page placed under the `openapi()` adapter's `route` (e.g. `<root>/api-reference/introduction.mdx`) merges into the reference tab's sidebar alongside the generated operations. Delete only the per-endpoint stubs, not the conceptual pages.
|
|
158
175
|
|
|
159
176
|
## Assets
|
|
@@ -164,9 +181,10 @@ Mintlify serves every top-level dir (e.g. `/images`) at the site root. Blume ser
|
|
|
164
181
|
|
|
165
182
|
`navigation.languages` (≥2) → `i18n: { defaultLocale, locales: [{ code, label }] }`. The `default: true` language → `defaultLocale`. Translated content already lives in ISO-code directories, which match Blume's `dir` parser — no file moves. Remove any language selector; language switching is handled by i18n.
|
|
166
183
|
|
|
184
|
+
Per-language `banner`, `navbar`, and `footer` (on `navigation.languages[]`) merge into the one site-wide config, with each label as a map of language code to that language's text: `banner: { content: { en: "…", fr: "…" } }` (and `link.text`), `navigation.actions`/`cta` labels, and `footer` link labels. Hrefs stay single — Blume moves an internal link into the reader's language itself. A language without an entry shows the default language's text. Report what doesn't merge: a banner or link set for only some languages, or one whose href differs per language.
|
|
185
|
+
|
|
167
186
|
## Dropped — report these
|
|
168
187
|
|
|
169
|
-
-
|
|
170
|
-
- **
|
|
171
|
-
-
|
|
172
|
-
- **`<Update>`** changelog components, `iconType`, `background.decoration`, `search.prompt`, `seo.metatags`.
|
|
188
|
+
- **A group's `boost`** (on a `navigation` group, inherited by its pages) → no inherited boost; set `search.boost` on the pages that need it.
|
|
189
|
+
- **Authentication** (password or SSO, set in the Mintlify dashboard) → Blume has none of its own; point the user at host-level protection (Vercel Deployment Protection, Netlify password protection, Cloudflare Access), per the deployment docs' **Private docs** section. It covers the whole site, so a mix of public and private pages needs two sites.
|
|
190
|
+
- **`<Update>`** changelog components, `iconType`, `background.decoration`, `search.prompt`.
|
|
@@ -15,9 +15,9 @@ Nextra (on Next.js) declares navigation and per-page labels in `_meta` files —
|
|
|
15
15
|
|
|
16
16
|
None of it maps automatically — read the config surface for the generation by hand and reconstruct in `blume.config.ts`:
|
|
17
17
|
|
|
18
|
-
- **v2/v3 — `theme.config.*`:** `logo` (JSX — extract text/image) → `logo`; `project.link` → `github` (renders the
|
|
18
|
+
- **v2/v3 — `theme.config.*`:** `logo` (JSX — extract text/image) → `logo`; `project.link` → `github` (renders the footer's repo link); `docsRepositoryBase` → `github` (owner/repo/branch — and any trailing sub-path → `github.dir`; if its origin is not `https://github.com`, set `github.host` to that origin — with `host` omitted, Blume builds the repository and edit links against public GitHub); `banner.text`/`banner.key` → `banner.content`/`banner.id` (`dismissible` maps); `primaryHue`/`primarySaturation` → pick an equivalent `theme.accent` color; `footer` → drop (report); `faviconGlyph` → drop (Blume's favicon is a file convention); `useNextSeoProps`/head → per-page `seo` frontmatter or drop.
|
|
19
19
|
- **v4 — `app/layout.*` props:** the same facts moved: `<Navbar logo projectLink>`, `<Layout docsRepositoryBase editLink sidebar={{…}} toc={{…}}>`, `<Banner>`, `<Footer>`. Map them the same way; sidebar/TOC tuning mostly drops (Blume's `toc` config covers min/max heading levels).
|
|
20
|
-
- **`next.config.*`:** harvest **`redirects()`** — static entries become Blume `redirects: [{ from, to }]
|
|
20
|
+
- **`next.config.*`:** harvest **`redirects()`** — static entries and `:param`/`:path*` patterns become Blume `redirects: [{ from, to }]` as written (`permanent: true` → `status: 308`, `false` → `307`); ones with regex params (`:slug(\d+)`) or `has`/`missing` conditions move to host config (report). A `latex: true` flag means math is in play (see Math below). Then delete the file.
|
|
21
21
|
- Root `_meta` entries with **`type: "page"`** → `navigation.tabs` (`{ label, path }`, where `path` is `/` for `index`, else `/<slug>`). `type: "page"` only maps at the **root**.
|
|
22
22
|
|
|
23
23
|
## Navigation: `_meta` → `meta.ts` + frontmatter
|
|
@@ -64,7 +64,7 @@ Nextra enables math via `nextra({ latex: true })` (KaTeX or MathJax). In Blume,
|
|
|
64
64
|
|
|
65
65
|
## i18n
|
|
66
66
|
|
|
67
|
-
- **v2/v3:** locale **file suffixes** (`index.en.mdx`, `index.zh.mdx`) + `i18n` in `next.config` →
|
|
67
|
+
- **v2/v3:** locale **file suffixes** (`index.en.mdx`, `index.zh.mdx`) + `i18n` in `next.config` → match Blume's `dot` parser as-is, default-locale suffix included: `i18n: { defaultLocale, locales: [{ code, label }], parser: "dot" }`, no file moves. Page titles land in each file's own frontmatter, so they stay per-locale, but under `dot` a folder's `meta.ts` serves every locale — build it from the default locale's `_meta` and report translated folder titles from the other locales' `_meta` files.
|
|
68
68
|
- **v4:** `content/<lang>/` dirs already match Blume's `dir` parser — map the locale list, no file moves.
|
|
69
69
|
|
|
70
70
|
## Package.json & teardown
|
|
@@ -57,7 +57,8 @@ Starlight's `sidebar` array → prefer letting Blume generate from the filesyste
|
|
|
57
57
|
| `pagefind: false` | `search.exclude: true` |
|
|
58
58
|
| `lastUpdated` (date/string) | `lastModified` |
|
|
59
59
|
| `lastUpdated: false` (boolean) | **remove** (Blume's `lastModified` takes a date; a boolean is a build error) |
|
|
60
|
-
| `prev`
|
|
60
|
+
| `prev: false` **and** `next: false` | `pagination: false` (hides both links) |
|
|
61
|
+
| `prev` / `next` otherwise (one side, or a label/link) | **drop** (report — the links can only be hidden together, and their labels come from the linked pages) |
|
|
61
62
|
| `template: splash` / `hero` | **no equivalent** — rebuild as a custom `.astro` page under `content.pages`; a `hero` on a normal page drops (report) |
|
|
62
63
|
| `banner`, `tableOfContents`, `editUrl`, `head` | drop (report) |
|
|
63
64
|
|
|
@@ -156,22 +156,31 @@ const ICONS = {
|
|
|
156
156
|
|
|
157
157
|
// --- Frontmatter field policy ----------------------------------------------
|
|
158
158
|
// Top-level keys Blume's strict schema rejects: delete the whole block, report.
|
|
159
|
-
const DROP = new Set([
|
|
160
|
-
"groups",
|
|
161
|
-
"hideApiMarker",
|
|
162
|
-
"hideFooterPagination",
|
|
163
|
-
"iconType",
|
|
164
|
-
"keywords",
|
|
165
|
-
"mode",
|
|
166
|
-
"public",
|
|
167
|
-
"rss",
|
|
168
|
-
]);
|
|
159
|
+
const DROP = new Set(["groups", "hideApiMarker", "iconType", "public", "rss"]);
|
|
169
160
|
|
|
170
|
-
//
|
|
161
|
+
// Keys whose fate depends on their value: the source value (and its line) →
|
|
162
|
+
// the line that replaces it, the line itself to keep it, or `null` to drop it
|
|
163
|
+
// (a value Blume has no counterpart for, or one that restates its default).
|
|
164
|
+
const REPLACE = {
|
|
165
|
+
hideFooterPagination: (value) =>
|
|
166
|
+
value === "true" ? "pagination: false" : null,
|
|
167
|
+
mode: (value, line) => (value === "assistant" ? null : line),
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Mintlify-only keys → Blume nested target. `[parent, child]`, plus a value
|
|
171
|
+
// mapper where the meaning flips: it returns the new value, or `null` to drop a
|
|
172
|
+
// value that restates Blume's default.
|
|
171
173
|
const RENAME = {
|
|
174
|
+
boost: ["search", "boost"],
|
|
172
175
|
canonical: ["seo", "canonical"],
|
|
176
|
+
keywords: ["search", "keywords"],
|
|
173
177
|
"og:image": ["seo", "image"],
|
|
174
178
|
ogImage: ["seo", "image"],
|
|
179
|
+
searchable: [
|
|
180
|
+
"search",
|
|
181
|
+
"exclude",
|
|
182
|
+
(value) => (value === "false" ? "true" : null),
|
|
183
|
+
],
|
|
175
184
|
sidebarTitle: ["sidebar", "label"],
|
|
176
185
|
tag: ["sidebar", "badge"],
|
|
177
186
|
};
|
|
@@ -179,7 +188,8 @@ const RENAME = {
|
|
|
179
188
|
// Keys we deliberately do NOT auto-transform — they usually mean the page is an
|
|
180
189
|
// OpenAPI endpoint stub that should be deleted (Blume generates operation pages),
|
|
181
190
|
// or else a normal page that just loses the key. Flag for the human; never guess.
|
|
182
|
-
|
|
191
|
+
// (`api` is not one: a hand-written endpoint page keeps it as written.)
|
|
192
|
+
const FLAG = new Set(["asyncapi", "openapi"]);
|
|
183
193
|
|
|
184
194
|
// Which change kinds actually edit the file. Report-only kinds (flags,
|
|
185
195
|
// unknowns, conflicts, manual-rename notices) leave the bytes untouched.
|
|
@@ -219,10 +229,15 @@ const topKey = (line) => {
|
|
|
219
229
|
return { key: m.groups.key, value: (m.groups.rest ?? "").trim() };
|
|
220
230
|
};
|
|
221
231
|
|
|
222
|
-
// The line index range [start, endExclusive) of a top-level key's block
|
|
232
|
+
// The line index range [start, endExclusive) of a top-level key's block: its
|
|
233
|
+
// indented lines plus any `- ` sequence items written flush at column 0,
|
|
234
|
+
// which YAML also reads as the key's value (`keywords:` then `- one`).
|
|
223
235
|
const blockRange = (fm, start) => {
|
|
224
236
|
let end = start + 1;
|
|
225
|
-
while (
|
|
237
|
+
while (
|
|
238
|
+
end < fm.length &&
|
|
239
|
+
(fm[end] === "" || /^\s/u.test(fm[end]) || /^-(?:\s|$)/u.test(fm[end]))
|
|
240
|
+
) {
|
|
226
241
|
end += 1;
|
|
227
242
|
}
|
|
228
243
|
// Trim trailing blank lines back out so the gap before the next key survives.
|
|
@@ -329,11 +344,41 @@ const rewriteFields = (fm) => {
|
|
|
329
344
|
changes.push({ detail: tk.key, kind: "flag" });
|
|
330
345
|
continue;
|
|
331
346
|
}
|
|
347
|
+
if (Object.hasOwn(REPLACE, tk.key)) {
|
|
348
|
+
const [start, end] = blockRange(fm, i);
|
|
349
|
+
const value = tk.value.replace(
|
|
350
|
+
/^(?<q>["'])(?<inner>.*)\k<q>$/u,
|
|
351
|
+
"$<inner>"
|
|
352
|
+
);
|
|
353
|
+
const line =
|
|
354
|
+
end - start === 1 ? REPLACE[tk.key](value, fm[start]) : undefined;
|
|
355
|
+
if (line === undefined) {
|
|
356
|
+
changes.push({ detail: tk.key, kind: "rename-manual" });
|
|
357
|
+
} else if (line === null) {
|
|
358
|
+
fm.splice(start, 1);
|
|
359
|
+
changes.push({ detail: `${tk.key}: ${value}`, kind: "drop" });
|
|
360
|
+
} else if (line !== fm[start]) {
|
|
361
|
+
const [key] = line.split(":");
|
|
362
|
+
if (fm.some((other, j) => j !== start && topKey(other)?.key === key)) {
|
|
363
|
+
changes.push({
|
|
364
|
+
detail: `${tk.key} → ${key} (child-exists)`,
|
|
365
|
+
kind: "rename-conflict",
|
|
366
|
+
});
|
|
367
|
+
} else {
|
|
368
|
+
fm[start] = line;
|
|
369
|
+
changes.push({
|
|
370
|
+
detail: `${tk.key}: ${value} → ${line}`,
|
|
371
|
+
kind: "rename",
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
332
377
|
const target = RENAME[tk.key];
|
|
333
378
|
if (!target) {
|
|
334
379
|
continue;
|
|
335
380
|
}
|
|
336
|
-
const [parent, child] = target;
|
|
381
|
+
const [parent, child, map] = target;
|
|
337
382
|
const [start, end] = blockRange(fm, i);
|
|
338
383
|
if (end - start !== 1 || tk.value === "") {
|
|
339
384
|
// Multi-line or valueless source — too structured to move safely.
|
|
@@ -343,12 +388,18 @@ const rewriteFields = (fm) => {
|
|
|
343
388
|
});
|
|
344
389
|
continue;
|
|
345
390
|
}
|
|
391
|
+
const value = map ? map(tk.value) : tk.value;
|
|
392
|
+
if (value === null) {
|
|
393
|
+
fm.splice(start, 1);
|
|
394
|
+
changes.push({ detail: `${tk.key}: ${tk.value}`, kind: "drop" });
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
346
397
|
// Remove the source line before inserting: setNested splices into the
|
|
347
398
|
// parent block, and when that block sits above the source key the insert
|
|
348
399
|
// would otherwise shift `start` onto the wrong line. Failure paths don't
|
|
349
400
|
// mutate `fm`, so the line can be restored as-is on conflict.
|
|
350
401
|
const [removed] = fm.splice(start, 1);
|
|
351
|
-
const placed = setNested(fm, parent, child,
|
|
402
|
+
const placed = setNested(fm, parent, child, value);
|
|
352
403
|
if (placed.ok) {
|
|
353
404
|
if (placed.index < start) {
|
|
354
405
|
// The insert above the cursor pushed the unvisited lines down one
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blume-write-skill
|
|
3
|
+
description: Write the agent skill for a product's Blume docs site — one SKILL.md, grounded in the docs, that teaches a coding agent to use the product (setup, core concepts, common tasks, gotchas) and links each topic's page. Blume publishes it at the site's /skill.md in place of the page map it generates. Use when asked to write, improve, or refresh a docs site's agent skill, or when `blume skill` hands you this task.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write a docs site's agent skill
|
|
7
|
+
|
|
8
|
+
Every Blume site already publishes a generated skill at `/skill.md`: a map of its pages with their one-line descriptions, built without a model. It tells an agent where things are, not how the product works. Your job is to write the skill that does: what the product is, how to set it up, the concepts and tasks that matter, and where it bites, with every topic linked to the page that covers it.
|
|
9
|
+
|
|
10
|
+
The reader is a coding agent working in someone else's project. It loaded your skill because a task touches this product, and it needs to act correctly without reading the whole site first.
|
|
11
|
+
|
|
12
|
+
## Ground rules
|
|
13
|
+
|
|
14
|
+
- **Only what the docs say.** Every claim, command, config key, flag, environment variable, and code sample must come from a page in the content root, copied rather than paraphrased. Never fill a gap from memory or from what similar products do. Where the docs don't answer something the skill needs, leave it out and list it in your report as a docs gap.
|
|
15
|
+
- **One file.** Keep it a lone `SKILL.md`: no `references/`, `scripts/`, or `assets/`. A single file is what the site serves at `/skill.md` and what an agent can fetch in one request. Depth lives in the docs, so link to them instead of copying them in.
|
|
16
|
+
- **Absolute links to Markdown.** Link pages as `<site><route>.md` (the home page is `<site>/index.md`), using the site URL your prompt gives. Link only pages that exist: a page's route is its path under the content root without the extension, and a folder's `index` page takes the folder's route.
|
|
17
|
+
- **Keep the given name.** The frontmatter `name` must be exactly the one in your prompt. That match is what makes your skill replace the generated one.
|
|
18
|
+
- **Short beats complete.** Aim for well under 300 lines. Skip anything a capable agent already knows (general programming, Markdown, how HTTP works) and anything it can look up from a link when it needs it.
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
1. **Read the docs.** Start with `blume.config.ts` (the site's title, description, `content.root`, navigation, API references) and the `meta.ts` files, which give the sidebar order. Then read the home page, the quickstart or installation page, each core-concept page, the configuration reference, the API reference overview, and any troubleshooting or FAQ pages. If the site has been built, its `llms.txt` lists every page with its description.
|
|
23
|
+
|
|
24
|
+
2. **Decide what matters.** Before writing, note:
|
|
25
|
+
- the product in two or three sentences: what it is, who uses it, and how it's installed or reached
|
|
26
|
+
- the five to ten tasks people most often come to the docs to do
|
|
27
|
+
- the concepts an agent must understand to do those tasks right
|
|
28
|
+
- the sharp edges: limits, required setup, defaults that surprise people, common errors, and anything deprecated along with what replaced it
|
|
29
|
+
|
|
30
|
+
3. **Write the skill.** Use this shape, dropping any section the docs give you nothing for:
|
|
31
|
+
|
|
32
|
+
```markdown
|
|
33
|
+
---
|
|
34
|
+
name: <the name from your prompt>
|
|
35
|
+
description: <What the product is and when to use this skill, in the words a user would type: the product name, its package and CLI names, its key concepts. At most 1024 characters.>
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
# <Product>
|
|
39
|
+
|
|
40
|
+
<Two or three sentences: what it is, who it's for, how to install or reach it.>
|
|
41
|
+
|
|
42
|
+
## Setup
|
|
43
|
+
|
|
44
|
+
<Install, configure, and authenticate, verbatim from the docs, with a link to the page.>
|
|
45
|
+
|
|
46
|
+
## Core concepts
|
|
47
|
+
|
|
48
|
+
- **<Concept>**: <one line>. [<Page title>](<site><route>.md)
|
|
49
|
+
|
|
50
|
+
## Common tasks
|
|
51
|
+
|
|
52
|
+
### <Task>
|
|
53
|
+
|
|
54
|
+
1. <Step, with the exact command or code from the docs.>
|
|
55
|
+
|
|
56
|
+
See [<Page title>](<site><route>.md).
|
|
57
|
+
|
|
58
|
+
## Gotchas
|
|
59
|
+
|
|
60
|
+
- <The limit, default, or error, and what to do about it.>
|
|
61
|
+
|
|
62
|
+
## Where to look
|
|
63
|
+
|
|
64
|
+
- [<Section>](<site><route>.md): <when to read it>
|
|
65
|
+
- [llms.txt](<site>/llms.txt): every page with a one-line summary.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `description` decides whether an agent loads the skill at all, so spend time on it. Name the product the way users do, and cover the situations it's for ("setting up X", "calling the X API", "configuring X"). Write the body in the imperative, for an agent that will act on it.
|
|
69
|
+
|
|
70
|
+
If the site serves an MCP server or other agent surfaces, the generated skill names them. Carry those lines over under "Where to look".
|
|
71
|
+
|
|
72
|
+
4. **Check it.**
|
|
73
|
+
- Every link points at a page that exists in the content root, as `<site><route>.md`.
|
|
74
|
+
- Every command, code sample, config key, and default appears in the docs as written.
|
|
75
|
+
- The frontmatter parses, `name` is exactly as given, and `description` is 1024 characters or fewer.
|
|
76
|
+
- Then run the docs build (`blume build`). The build output's `skill.md` should now be yours, and `.well-known/agent-skills/index.json` should list the name once.
|
|
77
|
+
|
|
78
|
+
5. **Report.** Say where you wrote the skill and any config you changed, summarize what it covers, and list the docs gaps you found: questions the skill needed answered that no page does. Those are worth fixing in the docs.
|
|
79
|
+
|
|
80
|
+
## Keeping it current
|
|
81
|
+
|
|
82
|
+
A hand-written skill doesn't update itself the way the generated one does. After a meaningful docs change (new features, renamed APIs, a changed setup flow), run `blume skill` again to refresh it. Update the existing file rather than starting over, and keep what's still true.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath, normalizeBasePath } from "../core/base-path.ts";
|
|
2
2
|
import { repoUrl } from "../core/github.ts";
|
|
3
3
|
import type { BlumeProject } from "../core/project-graph.ts";
|
|
4
4
|
import type { ContentSignalPolicy, ContentSignals } from "../core/schema.ts";
|
|
@@ -8,6 +8,7 @@ import { buildRssFeeds } from "../deploy/rss.ts";
|
|
|
8
8
|
import { AI_CATALOG_PATH, hasAiCatalog } from "./ai-catalog.ts";
|
|
9
9
|
import { hasApiCatalog } from "./api-catalog.ts";
|
|
10
10
|
import { API_PAGES_PATH, API_SEARCH_PATH, OPENAPI_PATH } from "./api/paths.ts";
|
|
11
|
+
import { servesSkillsIndex } from "./skills.ts";
|
|
11
12
|
|
|
12
13
|
/** Token map for the machine-readable content-usage echo. */
|
|
13
14
|
const USAGE_TOKENS: [keyof ContentSignalPolicy, string][] = [
|
|
@@ -145,7 +146,7 @@ const wellKnownArtifacts = (
|
|
|
145
146
|
if (hasApiCatalog(config)) {
|
|
146
147
|
artifacts.apiCatalog = abs("/.well-known/api-catalog");
|
|
147
148
|
}
|
|
148
|
-
if (config
|
|
149
|
+
if (servesSkillsIndex(config)) {
|
|
149
150
|
artifacts.agentSkills = abs("/.well-known/agent-skills/index.json");
|
|
150
151
|
}
|
|
151
152
|
return artifacts;
|
|
@@ -173,7 +174,7 @@ export const buildAgentReadability = (
|
|
|
173
174
|
// `site`; concatenate rather than `new URL()` so the subpath is preserved.
|
|
174
175
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
175
176
|
const abs = (path: string): string => {
|
|
176
|
-
const based =
|
|
177
|
+
const based = mountBasePath(deployBase, path);
|
|
177
178
|
return site ? absoluteUrl(site, based) : based;
|
|
178
179
|
};
|
|
179
180
|
|
package/src/ai/agent-surface.ts
CHANGED
|
@@ -11,7 +11,8 @@ export interface EmittedAgentSurface {
|
|
|
11
11
|
mcp: boolean;
|
|
12
12
|
/**
|
|
13
13
|
* A skills discovery index is served: generated from `agents.skills` (at
|
|
14
|
-
* least one valid skill)
|
|
14
|
+
* least one valid skill) or the site skill (`agents.skillMd`), or shipped
|
|
15
|
+
* by the user in `public/`.
|
|
15
16
|
*/
|
|
16
17
|
skills: boolean;
|
|
17
18
|
}
|
|
@@ -33,12 +34,12 @@ export const servesMcp = (
|
|
|
33
34
|
);
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
|
-
* The config the discovery documents are built from: `agents.mcp
|
|
37
|
-
* `agents.skills` switched off when the build didn't
|
|
38
|
-
* agent-readability.json, the catalogs, and the
|
|
39
|
-
* what is there. A config flag says what was asked
|
|
40
|
-
* skipped when a page owns its route, and skills
|
|
41
|
-
* directory is missing or holds no valid skill.
|
|
37
|
+
* The config the discovery documents are built from: `agents.mcp`,
|
|
38
|
+
* `agents.skills`, and `agents.skillMd` switched off when the build didn't
|
|
39
|
+
* emit them, so llms.txt, agent-readability.json, the catalogs, and the
|
|
40
|
+
* header rules only point at what is there. A config flag says what was asked
|
|
41
|
+
* for; the MCP server is skipped when a page owns its route, and skills
|
|
42
|
+
* publish nothing when their directory is missing or holds no valid skill.
|
|
42
43
|
*/
|
|
43
44
|
export const advertisedConfig = (
|
|
44
45
|
config: ResolvedConfig,
|
|
@@ -51,6 +52,7 @@ export const advertisedConfig = (
|
|
|
51
52
|
...config.agents.mcp,
|
|
52
53
|
enabled: config.agents.mcp.enabled && emitted.mcp,
|
|
53
54
|
},
|
|
55
|
+
skillMd: config.agents.skillMd && emitted.skills,
|
|
54
56
|
skills: emitted.skills ? config.agents.skills : undefined,
|
|
55
57
|
},
|
|
56
58
|
});
|
package/src/ai/ai-catalog.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { apiNamePhrase } from "../core/api-name.ts";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
mountBasePath,
|
|
4
|
+
normalizeBasePath,
|
|
5
|
+
withBasePath,
|
|
6
|
+
} from "../core/base-path.ts";
|
|
3
7
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
4
8
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
5
9
|
import { resolveReferences } from "../openapi/references.ts";
|
|
6
10
|
import { API_CATALOG_PATH, hasApiCatalog } from "./api-catalog.ts";
|
|
7
11
|
import { OPENAPI_PATH } from "./api/paths.ts";
|
|
8
12
|
import { asciiSlugify } from "./mcp/discovery.ts";
|
|
9
|
-
import { AGENT_SKILLS_DIR } from "./skills.ts";
|
|
13
|
+
import { AGENT_SKILLS_DIR, servesSkillsIndex } from "./skills.ts";
|
|
10
14
|
import type { SkillArtifact } from "./skills.ts";
|
|
11
15
|
|
|
12
16
|
/**
|
|
@@ -82,7 +86,7 @@ export const hasAiCatalog = (config: ResolvedConfig): boolean =>
|
|
|
82
86
|
(config.agents.mcp.enabled ||
|
|
83
87
|
config.agents.api ||
|
|
84
88
|
config.agents.llmsTxt.enabled ||
|
|
85
|
-
|
|
89
|
+
servesSkillsIndex(config) ||
|
|
86
90
|
resolveReferences(config).length > 0);
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -114,7 +118,10 @@ const NAMES_DOCS = /\b(?:docs|documentation)$/iu;
|
|
|
114
118
|
* reads "the Acme documentation" and "Acme Docs" reads "the Acme Docs", not
|
|
115
119
|
* "the Acme Docs docs".
|
|
116
120
|
*/
|
|
117
|
-
const docsPhrase = (
|
|
121
|
+
export const docsPhrase = (
|
|
122
|
+
title: string,
|
|
123
|
+
noun: "docs" | "documentation"
|
|
124
|
+
): string =>
|
|
118
125
|
NAMES_DOCS.test(title.trim()) ? `the ${title}` : `the ${title} ${noun}`;
|
|
119
126
|
|
|
120
127
|
const entrySeeds = (
|
|
@@ -227,7 +234,7 @@ export const buildAiCatalog = (
|
|
|
227
234
|
}
|
|
228
235
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
229
236
|
const abs = (path: string): string =>
|
|
230
|
-
absoluteUrl(site,
|
|
237
|
+
absoluteUrl(site, mountBasePath(deployBase, path));
|
|
231
238
|
const host = publisherHost(site);
|
|
232
239
|
const entries = entrySeeds(config, skills, abs).map((seed): CatalogEntry => {
|
|
233
240
|
const entry: CatalogEntry = {
|
package/src/ai/api/handlers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mountBasePath } from "../../core/base-path.ts";
|
|
2
2
|
import { absoluteUrl } from "../../core/site-url.ts";
|
|
3
3
|
import type { Navigation } from "../../core/types.ts";
|
|
4
4
|
import type { McpData, McpRoute } from "../mcp/data.ts";
|
|
@@ -88,7 +88,7 @@ export const jsonResponse = (payload: ApiPayload, status = 200): Response =>
|
|
|
88
88
|
|
|
89
89
|
/** The absolute (or root-relative) URL for a base-less path. */
|
|
90
90
|
const siteUrl = (path: string, context: ApiSiteContext): string => {
|
|
91
|
-
const based =
|
|
91
|
+
const based = mountBasePath(context.base, path);
|
|
92
92
|
return context.site ? absoluteUrl(context.site, based) : based;
|
|
93
93
|
};
|
|
94
94
|
|
package/src/ai/api/spec.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { apiNamePhrase } from "../../core/api-name.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { mountBasePath } from "../../core/base-path.ts";
|
|
3
3
|
import { absoluteUrl, siteRoot } from "../../core/site-url.ts";
|
|
4
4
|
import {
|
|
5
5
|
API_NAVIGATION_PATH,
|
|
@@ -688,7 +688,7 @@ export const buildApiSpec = (input: ApiSpecInput): ApiSpecDocument => {
|
|
|
688
688
|
if (input.site) {
|
|
689
689
|
document.externalDocs = {
|
|
690
690
|
description: `${input.name} documentation`,
|
|
691
|
-
url: absoluteUrl(input.site,
|
|
691
|
+
url: absoluteUrl(input.site, mountBasePath(input.base, "/")),
|
|
692
692
|
};
|
|
693
693
|
}
|
|
694
694
|
return document;
|
package/src/ai/api-catalog.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
mountBasePath,
|
|
3
|
+
normalizeBasePath,
|
|
4
|
+
withBasePath,
|
|
5
|
+
} from "../core/base-path.ts";
|
|
2
6
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
3
7
|
import { absoluteUrl } from "../core/site-url.ts";
|
|
4
8
|
import { resolveReferences } from "../openapi/references.ts";
|
|
@@ -39,7 +43,7 @@ const linksetEntries = (config: ResolvedConfig): LinksetEntry[] => {
|
|
|
39
43
|
const site = config.deployment.options.site ?? null;
|
|
40
44
|
const deployBase = normalizeBasePath(config.deployment.options.base);
|
|
41
45
|
const abs = (path: string): string => {
|
|
42
|
-
const based =
|
|
46
|
+
const based = mountBasePath(deployBase, path);
|
|
43
47
|
return site ? absoluteUrl(site, based) : based;
|
|
44
48
|
};
|
|
45
49
|
|