blume 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
  3. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  4. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  5. package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
  6. package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
  7. package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
  8. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  9. package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
  10. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  11. package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
  12. package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
  13. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
  14. package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
  15. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  16. package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
  17. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  18. package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
  19. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
  20. package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
  21. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  22. package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
  23. package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
  24. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  25. package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
  26. package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
  27. package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
  28. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  29. package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
  30. package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
  31. package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
  32. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  33. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  34. package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
  35. package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
  36. package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
  37. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  38. package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
  39. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  40. package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
  41. package/dist/cli/chunk-j85scx15.js.map +10 -0
  42. package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
  43. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  44. package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
  45. package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
  46. package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
  47. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  48. package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
  49. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  50. package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
  51. package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
  52. package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
  53. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  54. package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
  55. package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
  56. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  57. package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
  58. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  59. package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
  60. package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
  61. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  62. package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
  63. package/dist/cli/index.js +160 -35
  64. package/dist/cli/index.js.map +4 -4
  65. package/dist/types/ai/agent-surface.d.ts +32 -0
  66. package/dist/types/ai/api-catalog.d.ts +7 -1
  67. package/dist/types/ai/ask-context.d.ts +7 -0
  68. package/dist/types/ai/component-markdown.d.ts +4 -4
  69. package/dist/types/ai/relative-links.d.ts +9 -4
  70. package/dist/types/ai/static-expression.d.ts +28 -0
  71. package/dist/types/analytics/databuddy.d.ts +43 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +14 -0
  74. package/dist/types/cli/env.d.ts +5 -0
  75. package/dist/types/cli/init/scaffold.d.ts +19 -3
  76. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  77. package/dist/types/core/base-path.d.ts +9 -0
  78. package/dist/types/core/config-input.d.ts +4 -4
  79. package/dist/types/core/config.d.ts +2 -2
  80. package/dist/types/core/graph.d.ts +2 -0
  81. package/dist/types/core/i18n-ui.d.ts +31 -0
  82. package/dist/types/core/i18n.d.ts +7 -1
  83. package/dist/types/core/links.d.ts +3 -1
  84. package/dist/types/core/load-module.d.ts +10 -0
  85. package/dist/types/core/locale-links.d.ts +12 -2
  86. package/dist/types/core/meta.d.ts +5 -1
  87. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  88. package/dist/types/core/navigation.d.ts +38 -0
  89. package/dist/types/core/ordering-prefix.d.ts +4 -0
  90. package/dist/types/core/safe-links.d.ts +3 -1
  91. package/dist/types/core/schema.d.ts +52 -13
  92. package/dist/types/core/sources/github-releases.d.ts +5 -0
  93. package/dist/types/core/sources/lower.d.ts +14 -2
  94. package/dist/types/core/sources/normalize.d.ts +10 -1
  95. package/dist/types/core/sources/remote.d.ts +11 -1
  96. package/dist/types/core/sources/resolve.d.ts +12 -0
  97. package/dist/types/core/sources/types.d.ts +31 -0
  98. package/dist/types/core/types.d.ts +9 -0
  99. package/dist/types/deploy/adapters/node.d.ts +5 -2
  100. package/dist/types/deploy/adapters/types.d.ts +7 -0
  101. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  102. package/dist/types/deploy/headers.d.ts +31 -7
  103. package/dist/types/deploy/node-headers.d.ts +43 -8
  104. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  105. package/dist/types/deploy/platforms/node.d.ts +6 -5
  106. package/dist/types/deploy/platforms/types.d.ts +7 -0
  107. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  108. package/dist/types/deploy/redirects.d.ts +7 -2
  109. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  110. package/dist/types/openapi/model.d.ts +20 -6
  111. package/dist/types/search/sync/algolia.d.ts +3 -1
  112. package/docs/01-quickstart.mdx +3 -2
  113. package/docs/02-deployment.mdx +17 -8
  114. package/docs/08-faq.mdx +9 -2
  115. package/docs/advanced/changelog.mdx +1 -1
  116. package/docs/advanced/custom-pages.mdx +4 -2
  117. package/docs/cli/audit.mdx +2 -2
  118. package/docs/cli/doctor.mdx +2 -2
  119. package/docs/cli/evals.mdx +2 -2
  120. package/docs/cli/index.mdx +4 -1
  121. package/docs/cli/translate.mdx +1 -1
  122. package/docs/configuration/analytics.mdx +23 -2
  123. package/docs/configuration/assistant.mdx +1 -1
  124. package/docs/configuration/customization.mdx +4 -3
  125. package/docs/configuration/index.mdx +5 -3
  126. package/docs/configuration/search.mdx +1 -1
  127. package/docs/content/components.mdx +1 -1
  128. package/docs/content/frontmatter.mdx +5 -1
  129. package/docs/content/i18n.mdx +1 -1
  130. package/docs/content/index.mdx +4 -2
  131. package/docs/content/meta.mdx +5 -3
  132. package/docs/content/navigation.mdx +29 -4
  133. package/docs/content/sources.mdx +18 -12
  134. package/docs/content/versioning.mdx +1 -0
  135. package/docs/discoverability/agent-discovery.mdx +21 -7
  136. package/docs/discoverability/index.mdx +2 -2
  137. package/docs/discoverability/llms-txt.mdx +2 -5
  138. package/docs/discoverability/markdown.mdx +5 -3
  139. package/docs/discoverability/mcp.mdx +4 -0
  140. package/docs/discoverability/metadata.mdx +3 -2
  141. package/docs/discoverability/open-graph.mdx +6 -4
  142. package/docs/discoverability/rss.mdx +3 -1
  143. package/docs/references/asyncapi.mdx +1 -1
  144. package/docs/references/graphql.mdx +2 -2
  145. package/docs/references/openapi.mdx +3 -3
  146. package/package.json +1 -1
  147. package/skills/blume-migrate/SKILL.md +6 -6
  148. package/skills/blume-migrate/references/docusaurus.md +6 -6
  149. package/skills/blume-migrate/references/mintlify.md +3 -3
  150. package/skills/blume-migrate/references/monorepo.md +1 -1
  151. package/skills/blume-migrate/references/nextra.md +1 -1
  152. package/skills/blume-migrate/references/starlight.md +5 -5
  153. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  154. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  155. package/src/ai/agent-surface.ts +56 -0
  156. package/src/ai/api/spec.ts +15 -2
  157. package/src/ai/api-catalog.ts +7 -1
  158. package/src/ai/ask-context.ts +14 -2
  159. package/src/ai/ask-data.ts +26 -12
  160. package/src/ai/component-markdown.ts +43 -37
  161. package/src/ai/link-headers.ts +8 -3
  162. package/src/ai/llms.ts +4 -3
  163. package/src/ai/markdown.ts +23 -11
  164. package/src/ai/mcp/server.ts +78 -7
  165. package/src/ai/relative-links.ts +78 -10
  166. package/src/ai/static-expression.ts +416 -0
  167. package/src/ai/visibility.ts +45 -14
  168. package/src/analytics/databuddy.ts +67 -0
  169. package/src/analytics/head.ts +4 -0
  170. package/src/analytics/index.ts +2 -0
  171. package/src/analytics/posthog.ts +21 -3
  172. package/src/analytics/schema.ts +2 -0
  173. package/src/astro/generate.ts +71 -27
  174. package/src/astro/templates.ts +61 -23
  175. package/src/audit/catalog.ts +2 -2
  176. package/src/audit/checks/assets.ts +25 -5
  177. package/src/audit/checks/content.ts +20 -2
  178. package/src/audit/checks/i18n.ts +46 -8
  179. package/src/audit/checks/indexability.ts +39 -19
  180. package/src/audit/checks/links.ts +14 -0
  181. package/src/audit/checks/llms.ts +6 -3
  182. package/src/audit/checks/network.ts +3 -1
  183. package/src/audit/checks/og-image.ts +10 -0
  184. package/src/audit/checks/robots.ts +6 -1
  185. package/src/audit/checks/sitemap.ts +56 -31
  186. package/src/audit/checks/social.ts +21 -2
  187. package/src/audit/crawl.ts +88 -15
  188. package/src/audit/report.ts +54 -17
  189. package/src/audit/run.ts +1 -0
  190. package/src/audit/types.ts +18 -0
  191. package/src/audit/url.ts +37 -6
  192. package/src/cli/build-failure.ts +50 -0
  193. package/src/cli/commands/audit.ts +7 -0
  194. package/src/cli/commands/build.ts +19 -5
  195. package/src/cli/commands/check.ts +2 -0
  196. package/src/cli/commands/eval.ts +1 -7
  197. package/src/cli/commands/init.ts +15 -5
  198. package/src/cli/commands/preview.ts +15 -0
  199. package/src/cli/commands/sync.ts +2 -0
  200. package/src/cli/commands/translate.ts +9 -6
  201. package/src/cli/commands/upgrade.ts +11 -0
  202. package/src/cli/eject-scripts.ts +32 -7
  203. package/src/cli/env.ts +12 -1
  204. package/src/cli/init/scaffold.ts +117 -11
  205. package/src/cli/init/starter-spec.ts +235 -0
  206. package/src/components/content/AccordionItem.astro +26 -22
  207. package/src/components/content/Badge.astro +2 -9
  208. package/src/components/content/Card.astro +2 -2
  209. package/src/components/content/Component.astro +9 -3
  210. package/src/components/content/Expandable.astro +5 -1
  211. package/src/components/content/Frame.astro +2 -7
  212. package/src/components/content/GithubInfo.astro +12 -2
  213. package/src/components/content/Prompt.astro +2 -7
  214. package/src/components/content/Tabs.astro +54 -5
  215. package/src/components/content/Tile.astro +1 -1
  216. package/src/components/content/Tooltip.astro +69 -7
  217. package/src/components/content/Tree.astro +7 -2
  218. package/src/components/content/TypeTable.astro +10 -5
  219. package/src/components/content/Update.astro +8 -2
  220. package/src/components/content/auto-type-table.ts +4 -1
  221. package/src/components/content/badge-color.ts +17 -0
  222. package/src/components/content/base-href.ts +18 -3
  223. package/src/components/content/inline-markdown.ts +27 -7
  224. package/src/components/copy-feedback.ts +35 -8
  225. package/src/components/islands/assistant.tsx +16 -2
  226. package/src/components/islands/hooks.ts +7 -2
  227. package/src/components/islands/webmcp.ts +12 -8
  228. package/src/components/layout/Banner.astro +23 -4
  229. package/src/components/layout/Header.astro +22 -9
  230. package/src/components/layout/Logo.astro +5 -0
  231. package/src/components/layout/NavSelector.astro +6 -1
  232. package/src/components/layout/NavTabMenu.astro +133 -0
  233. package/src/components/layout/NavTree.astro +15 -6
  234. package/src/components/layout/NavTreeCache.astro +5 -2
  235. package/src/components/layout/NavTreeScript.astro +45 -5
  236. package/src/components/layout/PageLayout.astro +22 -8
  237. package/src/components/layout/ReferenceLayout.astro +4 -1
  238. package/src/components/layout/RootLayout.astro +21 -8
  239. package/src/components/layout/Search.astro +5 -1
  240. package/src/components/layout/analytics-client.ts +2 -0
  241. package/src/components/openapi/GraphqlType.astro +11 -3
  242. package/src/components/openapi/MessageComposer.astro +1 -1
  243. package/src/components/openapi/Operation.astro +21 -5
  244. package/src/components/openapi/PanelTabs.astro +4 -1
  245. package/src/components/openapi/Playground.astro +8 -2
  246. package/src/components/openapi/RequestPanel.astro +9 -5
  247. package/src/components/openapi/SchemaProperty.astro +11 -33
  248. package/src/components/openapi/SchemaTable.astro +27 -52
  249. package/src/components/openapi/helpers.ts +90 -12
  250. package/src/components/openapi/message-composer.ts +8 -0
  251. package/src/components/openapi/message-model.ts +12 -2
  252. package/src/components/openapi/message.ts +4 -1
  253. package/src/components/openapi/operation-model.ts +40 -8
  254. package/src/components/openapi/panel.ts +29 -5
  255. package/src/components/openapi/playground-client.ts +37 -7
  256. package/src/components/openapi/playground-schema.ts +25 -6
  257. package/src/components/openapi/request.ts +2 -0
  258. package/src/components/openapi/schema-tree.ts +203 -0
  259. package/src/components/openapi/snippets.ts +30 -15
  260. package/src/components/openapi/validate-json.ts +1 -1
  261. package/src/core/base-path.ts +15 -0
  262. package/src/core/config-input.ts +4 -4
  263. package/src/core/config.ts +18 -4
  264. package/src/core/diagnostics.ts +217 -30
  265. package/src/core/graph.ts +120 -11
  266. package/src/core/i18n-ui.ts +39 -2
  267. package/src/core/i18n.ts +13 -2
  268. package/src/core/links.ts +7 -4
  269. package/src/core/load-module.ts +20 -0
  270. package/src/core/locale-links.ts +17 -17
  271. package/src/core/manifest.ts +3 -2
  272. package/src/core/meta.ts +69 -6
  273. package/src/core/nav-diagnostics.ts +56 -1
  274. package/src/core/navigation.ts +245 -76
  275. package/src/core/ordering-prefix.ts +27 -0
  276. package/src/core/project-graph.ts +23 -1
  277. package/src/core/safe-href.ts +53 -1
  278. package/src/core/safe-links.ts +11 -2
  279. package/src/core/schema.ts +70 -14
  280. package/src/core/sources/assets.ts +83 -41
  281. package/src/core/sources/contentful-rich-text.ts +3 -2
  282. package/src/core/sources/contentful.ts +25 -12
  283. package/src/core/sources/filesystem.ts +5 -1
  284. package/src/core/sources/github-releases.ts +106 -5
  285. package/src/core/sources/lexical.ts +9 -4
  286. package/src/core/sources/lower.ts +79 -26
  287. package/src/core/sources/mdx-remote.ts +1 -0
  288. package/src/core/sources/normalize.ts +132 -30
  289. package/src/core/sources/notion.ts +26 -10
  290. package/src/core/sources/obsidian.ts +17 -3
  291. package/src/core/sources/payload.ts +1 -0
  292. package/src/core/sources/portable-text.ts +63 -44
  293. package/src/core/sources/remote.ts +18 -2
  294. package/src/core/sources/resolve.ts +52 -33
  295. package/src/core/sources/sanity.ts +1 -0
  296. package/src/core/sources/strapi-blocks.ts +9 -4
  297. package/src/core/sources/strapi.ts +1 -0
  298. package/src/core/sources/types.ts +31 -0
  299. package/src/core/types.ts +9 -0
  300. package/src/core/ui-packs/ar.ts +13 -0
  301. package/src/core/ui-packs/bg.ts +13 -0
  302. package/src/core/ui-packs/bn.ts +13 -0
  303. package/src/core/ui-packs/ca.ts +13 -0
  304. package/src/core/ui-packs/cs.ts +13 -0
  305. package/src/core/ui-packs/da.ts +13 -0
  306. package/src/core/ui-packs/de.ts +13 -0
  307. package/src/core/ui-packs/el.ts +13 -0
  308. package/src/core/ui-packs/es.ts +13 -0
  309. package/src/core/ui-packs/fa.ts +13 -0
  310. package/src/core/ui-packs/fi.ts +13 -0
  311. package/src/core/ui-packs/fr.ts +13 -0
  312. package/src/core/ui-packs/he.ts +13 -0
  313. package/src/core/ui-packs/hi.ts +13 -0
  314. package/src/core/ui-packs/hr.ts +13 -0
  315. package/src/core/ui-packs/hu.ts +13 -0
  316. package/src/core/ui-packs/id.ts +13 -0
  317. package/src/core/ui-packs/it.ts +13 -0
  318. package/src/core/ui-packs/ja.ts +13 -0
  319. package/src/core/ui-packs/ko.ts +13 -0
  320. package/src/core/ui-packs/nl.ts +13 -0
  321. package/src/core/ui-packs/no.ts +13 -0
  322. package/src/core/ui-packs/pl.ts +13 -0
  323. package/src/core/ui-packs/pt-br.ts +13 -0
  324. package/src/core/ui-packs/pt.ts +13 -0
  325. package/src/core/ui-packs/ro.ts +13 -0
  326. package/src/core/ui-packs/ru.ts +13 -0
  327. package/src/core/ui-packs/sk.ts +13 -0
  328. package/src/core/ui-packs/sr.ts +13 -0
  329. package/src/core/ui-packs/sv.ts +13 -0
  330. package/src/core/ui-packs/th.ts +13 -0
  331. package/src/core/ui-packs/tr.ts +13 -0
  332. package/src/core/ui-packs/uk.ts +13 -0
  333. package/src/core/ui-packs/vi.ts +13 -0
  334. package/src/core/ui-packs/zh-tw.ts +13 -0
  335. package/src/core/ui-packs/zh.ts +13 -0
  336. package/src/core/version-cut.ts +17 -2
  337. package/src/core/versions.ts +4 -1
  338. package/src/deploy/adapters/node.ts +5 -2
  339. package/src/deploy/adapters/registry.ts +2 -1
  340. package/src/deploy/adapters/types.ts +13 -1
  341. package/src/deploy/artifacts.ts +25 -5
  342. package/src/deploy/cloudflare-negotiation.ts +23 -4
  343. package/src/deploy/headers.ts +67 -51
  344. package/src/deploy/node-headers.ts +148 -27
  345. package/src/deploy/platforms/cloudflare.ts +1 -0
  346. package/src/deploy/platforms/netlify.ts +82 -5
  347. package/src/deploy/platforms/node.ts +7 -5
  348. package/src/deploy/platforms/static.ts +1 -0
  349. package/src/deploy/platforms/types.ts +7 -0
  350. package/src/deploy/platforms/vercel.ts +9 -3
  351. package/src/deploy/redirects.ts +14 -3
  352. package/src/deploy/vercel-negotiation.ts +35 -3
  353. package/src/eval/agents.ts +10 -2
  354. package/src/eval/run.ts +25 -0
  355. package/src/markdown/base-links.ts +55 -8
  356. package/src/markdown/index.ts +6 -3
  357. package/src/markdown/relative-links.ts +3 -23
  358. package/src/markdown/route-snapshot.ts +37 -0
  359. package/src/og/card.ts +20 -4
  360. package/src/og/derive.ts +145 -4
  361. package/src/openapi/graphql-build.ts +28 -2
  362. package/src/openapi/model.ts +77 -13
  363. package/src/openapi/render-mdx.ts +10 -1
  364. package/src/registry/eject.ts +119 -22
  365. package/src/search/documents.ts +22 -1
  366. package/src/search/sync/algolia.ts +36 -2
  367. package/src/sources/registry.ts +5 -0
  368. package/src/theme/entry.ts +11 -4
  369. package/src/translate/agents.ts +6 -1
  370. package/src/translate/ledger.ts +26 -3
  371. package/src/translate/meta.ts +11 -3
  372. package/src/translate/run.ts +11 -5
  373. package/src/translate/validate.ts +10 -1
  374. package/src/translate/work-list.ts +36 -3
  375. package/src/upgrade/upgrade.ts +36 -4
  376. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  377. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  378. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  379. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  380. package/dist/cli/chunk-82bbrxdn.js +0 -51
  381. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  382. package/dist/cli/chunk-abh8yjkn.js +0 -31
  383. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  384. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  385. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  386. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  387. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  388. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  389. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  390. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  391. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  392. package/dist/cli/chunk-q5163e60.js.map +0 -11
  393. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  394. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  395. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  396. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  397. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  398. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
  399. /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
  400. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
  401. /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
  402. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
  403. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
  404. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
  405. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
  406. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
  407. /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
  408. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
  409. /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
  410. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
@@ -22,7 +22,8 @@ A static build includes:
22
22
 
23
23
  - every docs and custom page as static HTML
24
24
  - a local search index (Orama by default, Pagefind opt-in)
25
- - a [`sitemap.xml`](/docs/discoverability/sitemap-and-robots#sitemap) and [`robots.txt`](/docs/discoverability/sitemap-and-robots#robots) when the site URL is known
25
+ - a [`sitemap.xml`](/docs/discoverability/sitemap-and-robots#sitemap) when the site URL is known
26
+ - a [`robots.txt`](/docs/discoverability/sitemap-and-robots#robots), which points to the sitemap when the site URL is known
26
27
  - `llms.txt` and `llms-full.txt` for AI tools
27
28
  - redirect pages
28
29
  - prerendered [Open Graph images](/docs/discoverability/open-graph) when `seo.og.enabled` is on
@@ -31,7 +32,7 @@ A static build includes:
31
32
 
32
33
  Sitemaps, canonical tags, RSS, and Open Graph images need an absolute origin. On **Vercel**, **Netlify**, and **Cloudflare Pages**, Blume detects it from the platform's environment at build time — no config required.
33
34
 
34
- Set `site` to override the detected value, or to provide one on hosts that don't expose it (GitHub Pages, S3, a custom CDN). For a static build that's the plain `deployment` object:
35
+ Set `site`, an absolute `http://` or `https://` URL, to override the detected value, or to provide one on hosts that don't expose it (GitHub Pages, S3, a custom CDN). For a static build that's the plain `deployment` object:
35
36
 
36
37
  ```ts blume.config.ts lineNumbers
37
38
  deployment: {
@@ -39,7 +40,7 @@ deployment: {
39
40
  }
40
41
  ```
41
42
 
42
- When detecting automatically, Blume prefers your stable production domain over per-deploy preview URLs, so the canonical origin stays put across deploys.
43
+ On Vercel and Netlify, automatic detection prefers your stable production domain over per-deploy preview URLs, so the canonical origin stays put across deploys. Cloudflare Pages exposes only the current deployment's URL (`CF_PAGES_URL`), which changes with every deploy, so set `site` there.
43
44
 
44
45
  During `blume dev`, the site URL falls back to your local dev server (e.g. `http://localhost:4321`) when none is set, so site-gated features — Open Graph images, canonicals, the sitemap — work out of the box. Builds never use this fallback, so production output is never pointed at localhost.
45
46
 
@@ -52,6 +53,8 @@ blume build
52
53
  blume preview
53
54
  ```
54
55
 
56
+ `blume preview` serves static builds and `node()` and `cloudflare()` server builds. The Vercel and Netlify adapters have no local preview server, so after a `vercel()` or `netlify()` server build it stops with an error instead. Try the site with `blume dev`, or deploy a preview with `vercel deploy` or `netlify deploy`.
57
+
55
58
  ## Subpath deploys
56
59
 
57
60
  Serving docs under a path like `example.com/docs`? Set `base` — common for GitHub Pages project sites. The whole site, root included, moves under the base, and internal links and assets are rewritten to include it.
@@ -117,7 +120,7 @@ Naming an adapter switches the build to server output. To keep a static build on
117
120
  deployment: netlify({ output: "static" }),
118
121
  ```
119
122
 
120
- A server build includes everything a static build does, plus any Astro endpoints or middleware you add. The `node()` adapter produces a standalone server you can run directly with `node dist/server/entry.mjs`. The server resolves its packages through links into your project's `node_modules`, so deploy the project with its installed dependencies, not `dist/` alone. When the site publishes discovery files, Blume puts a small wrapper in front of Astro's entry (moved to `astro-entry.mjs` beside it) so the `.well-known` discovery files go out with their media types and CORS headers, which the standalone server's static handler can't set on its own.
123
+ A server build includes everything a static build does, plus any Astro endpoints or middleware you add. The `node()` adapter produces a standalone server you can run directly with `node dist/server/entry.mjs`. It listens on `localhost:4321` unless you set the `HOST` and `PORT` environment variables when you start it (`HOST=0.0.0.0 PORT=8080 node dist/server/entry.mjs`); `host` and `port` passed to `node()` have no effect, because `@astrojs/node` replaces them with Astro's own server settings. The server resolves its packages through links into your project's `node_modules`, so deploy the project with its installed dependencies, not `dist/` alone. When the site publishes discovery files, serves images a content source downloaded, or configures redirects, Blume puts a small wrapper in front of Astro's entry (moved to `astro-entry.mjs` beside it). It sends the `.well-known` discovery files with their media types and CORS headers and downloaded SVGs sandboxed, which the standalone server's static handler can't do on its own, and answers each redirect with its configured status.
121
124
 
122
125
  A `cloudflare()` server build deploys with `npx wrangler deploy` from the project root after `blume build`: Blume writes the redirected Wrangler config to `.wrangler/deploy/` (and adds `.wrangler/` to `.gitignore`), and names the Worker after your project — the `package.json` `name`, else the site's hostname, else the folder — unless a `wrangler.jsonc` of your own at the project root sets `name`.
123
126
 
@@ -139,19 +142,24 @@ Map old URLs to new ones in `blume.config.ts`:
139
142
  redirects: [{ from: "/old", to: "/new", status: 301 }],
140
143
  ```
141
144
 
142
- `status` accepts `301`, `302`, `307`, or `308` (default `301`). Server builds handle redirects at request time. Static builds emit redirect pages **and** the platform files your host reads, so it issues a real HTTP redirect: `_redirects` for `netlify()` and `cloudflare()`, `vercel.json` for `vercel()`, and — when no host is named — both of those plus `blume-redirects.json`, a structured manifest for anything else (nginx/Apache rules, an edge worker). A `_redirects` or `vercel.json` you ship in `public/` is left untouched.
145
+ `status` accepts `301`, `302`, `307`, or `308` (default `301`). Server builds answer redirects at request time with the configured status. Static builds emit redirect pages **and** the platform files your host reads, so it issues a real HTTP redirect: `_redirects` for `netlify()` and `cloudflare()`, `vercel.json` for `vercel()`, and — when no host is named — both of those plus `blume-redirects.json`, a structured manifest for anything else (nginx/Apache rules, an edge worker). A `_redirects` or `vercel.json` you ship in `public/` is left untouched.
146
+
147
+ Two hosts need more than the file:
148
+
149
+ - **Netlify** serves a file that exists ahead of a redirect rule unless the rule is forced, and a static build has a redirect page at every `from`. The `_redirects` for `netlify()` forces its rules (`/old /new 301!`). Cloudflare rejects that flag, so the file a build for no named host writes leaves it off, and on Netlify that build answers with the redirect page instead; name the host with `netlify({ output: "static" })` to get the HTTP redirect.
150
+ - **Vercel** reads `vercel.json` from the project's root directory, never from the output directory, so the copy in `dist/` applies only when you deploy that folder itself with the Vercel CLI (`vercel deploy dist`). A Git-connected project never reads it: it serves the redirect pages and none of the headers from [Content types](#content-types). Copy the `redirects` and `headers` from `dist/vercel.json` into the `vercel.json` in your project's root directory, or use `vercel()` for a server build, whose routing config carries the redirects and the discovery headers.
143
151
 
144
152
  :::note
145
153
  `from` and `to` are exact paths — a `:param` segment or `*` wildcard (e.g. `/blog/:slug` or `/old/*`) fails config validation, since hosts disagree on patterns. If you need pattern-based rules, handle them in an infrastructure file like `vercel.json` (which supports wildcard `source` patterns) or your host's redirect config instead. A `vercel.json` you ship in `public/` is preserved as-is.
146
154
  :::
147
155
 
148
156
  :::note
149
- Write both `from` and `to` as if mounted at root — under [`base`](#subpath-deploys) and [`basePath`](#mount-the-docs-under-a-path) alike, Blume rewrites both sides for you, so a redirect lands inside the base. A base you've already written into `to` by hand is preserved rather than doubled.
157
+ Write both `from` and `to` as if mounted at root, starting with `/` (`to` can also be a full `https://` URL) — under [`base`](#subpath-deploys) and [`basePath`](#mount-the-docs-under-a-path) alike, Blume rewrites both sides for you, so a redirect lands inside the base. A base you've already written into `to` by hand is preserved rather than doubled.
150
158
  :::
151
159
 
152
160
  ## Content types
153
161
 
154
- Where the host reads one, a build also emits a `_headers` file that pins `charset=utf-8` onto the raw AI-ready endpoints — `/<route>.md`, `/<route>.mdx`, and the `.txt` files (`llms.txt`, `llms-full.txt`). Those responses are valid UTF-8, but many static hosts serve them as `text/markdown` / `text/plain` with **no** charset, and browsers then fall back to Windows-1252 — so non-ASCII docs (Japanese, accented Latin, …) render as mojibake when the raw URL is opened directly. HTML pages are unaffected because they carry `<meta charset>`. Netlify reads `_headers` on a static deploy and Cloudflare (Pages, and Workers static assets) on both static and server builds, so those adapters and an unnamed static host get the file; Vercel (whose headers ride the routing config) and Node (whose server ignores the file) don't. A `_headers` you ship in `public/` is left untouched.
162
+ Where the host reads one, a build also emits a `_headers` file that pins `charset=utf-8` onto the raw AI-ready endpoints — `/<route>.md`, `/<route>.mdx`, and the `.txt` files (`llms.txt`, `llms-full.txt`). Those responses are valid UTF-8, but many static hosts serve them as `text/markdown` / `text/plain` with **no** charset, and browsers then fall back to Windows-1252 — so non-ASCII docs (Japanese, accented Latin, …) render as mojibake when the raw URL is opened directly. HTML pages are unaffected because they carry `<meta charset>`. Netlify reads `_headers` on a static deploy and Cloudflare (Pages, and Workers static assets) on both static and server builds, so those adapters and an unnamed static host get the file; Vercel (whose headers ride the routing config) and Node (whose server ignores the file) don't. A Netlify server build writes the same rules into the `headers` of its Frameworks API config (`.netlify/v1/config.json`) instead. A static Vercel build writes the same rules into `dist/vercel.json` instead, which a Git-connected project doesn't read (see [Redirects](#redirects)). A `_headers` you ship in `public/` is left untouched.
155
163
 
156
164
  ## Environment variables
157
165
 
@@ -162,8 +170,9 @@ When a feature needs a runtime secret, Blume warns at `blume dev`/`build` if it'
162
170
  | Assistant (AI Gateway) | `AI_GATEWAY_API_KEY` (or Vercel OIDC) |
163
171
  | Assistant (other adapters) | the adapter's default key env var (`OPENROUTER_API_KEY`, `LLMGATEWAY_API_KEY`, `INKEEP_API_KEY`), or the `apiKeyEnv` you passed it |
164
172
  | Mixedbread search | `MIXEDBREAD_API_KEY` |
173
+ | [Content sources](/docs/content/sources) | `NOTION_TOKEN` (`notion()`), `SANITY_TOKEN` (`sanity()`), `CONTENTFUL_ACCESS_TOKEN` (`contentful()`), `PAYLOAD_API_KEY` (`payload()`), `STRAPI_API_TOKEN` (`strapi()`), `GITHUB_TOKEN` (`githubReleases()`, and `mdxRemote()` reading from GitHub) |
165
174
 
166
- Set them in `.env.local` for local dev and in your host's environment for production. Build-time secrets for search-index sync (Algolia, Orama Cloud, Typesense) are warned about separately during the sync step.
175
+ Set them in `.env.local` for local dev and in your host's environment for production. A content source reads its token when it fetches, during `blume dev` and `blume build`, so set it where your site builds. Build-time secrets for search-index sync (Algolia, Orama Cloud, Typesense) are warned about separately during the sync step.
167
176
 
168
177
  ## Build cache
169
178
 
package/docs/08-faq.mdx CHANGED
@@ -57,7 +57,7 @@ No. [Orama](/docs/configuration/search) builds a local index that works in both
57
57
 
58
58
  ## How do I customize the look?
59
59
 
60
- Start with [theme tokens](/docs/configuration/theming) — accent color, fonts, radius, and a `theme.css` for anything else Tailwind can express. Go further by [overriding built-in components](/docs/configuration/customization) or adding [custom pages](/docs/configuration/customization#custom-pages). When you want the Astro project itself, [`blume eject`](/docs/cli) hands you a standalone app that still uses the `blume` package.
60
+ Start with [theme tokens](/docs/configuration/theming) — accent color, fonts, radius, and a `theme.css` for anything else Tailwind can express. Go further by [overriding built-in components](/docs/configuration/customization) or adding [custom pages](/docs/configuration/customization#custom-pages). When you want the Astro project itself, [`blume eject`](/docs/configuration/customization#eject) hands you a standalone app that still uses the `blume` package.
61
61
 
62
62
  ## Why is oxfmt / Ultracite collapsing my directives?
63
63
 
@@ -141,7 +141,7 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
141
141
  case "emphasis": {
142
142
  ```
143
143
 
144
- 2. Register it with your package manager's `patchedDependencies`. With Bun or pnpm, add to `package.json`:
144
+ 2. Register it with your package manager's `patchedDependencies`. With Bun, add to `package.json`:
145
145
 
146
146
  ```json package.json
147
147
  {
@@ -151,6 +151,13 @@ Patch oxfmt so it preserves the line break that sits directly against a `:::` fe
151
151
  }
152
152
  ```
153
153
 
154
+ With pnpm, add to `pnpm-workspace.yaml` (pnpm 11 and later no longer read settings from `package.json`):
155
+
156
+ ```yaml pnpm-workspace.yaml
157
+ patchedDependencies:
158
+ oxfmt@0.67.0: patches/oxfmt@0.67.0.patch
159
+ ```
160
+
154
161
  3. Reinstall so the patch is applied:
155
162
 
156
163
  ```package-install
@@ -62,7 +62,7 @@ Once you have at least one `type: changelog` entry, Blume generates a **`/change
62
62
  - The date follows the configured [`dateFormat`](/docs/configuration#date-format), minus the year the row's group already shows.
63
63
  - Drafts and `sidebar.hidden` entries are skipped.
64
64
 
65
- The page appears only when nothing already occupies the `/changelog` route. To replace it with your own design, add a [custom page](/docs/advanced/custom-pages) at `pages/changelog.astro` — it takes over and Blume stops generating the default index. The `<Update>` component the previous timeline was built from is still available for a hand-authored page that wants inline release notes.
65
+ The page appears only when nothing already occupies the `/changelog` route. To replace it with your own design, add a [custom page](/docs/advanced/custom-pages) at `pages/changelog.astro` — it takes over and Blume stops generating the default index. The `<Update>` component the previous timeline was built from still ships for a custom page that wants inline release notes. It isn't one of the MDX components, so import it in the `.astro` page itself: `import Update from "blume/components/content/Update.astro";`.
66
66
 
67
67
  A header [tab](/docs/content/navigation#tabs) pointing at `/changelog` opens this index — no `href` needed.
68
68
 
@@ -230,13 +230,15 @@ const { config } = data;
230
230
  </PageLayout>
231
231
  ```
232
232
 
233
- The header a custom page gets is the same one the docs pages get, so the chrome that lives in it comes along: search, the theme toggle, the language switcher, and — when the [assistant](/docs/configuration/assistant) is configured — its trigger. None of it needs wiring up per page. Pass `assistantEnabled={false}` to leave the assistant trigger off one page while keeping it everywhere else.
233
+ The header a custom page gets is the same one the docs pages get, so the chrome that lives in it comes along: search, the theme toggle, and — when the [assistant](/docs/configuration/assistant) is configured — its trigger. None of it needs wiring up per page. Pass `assistantEnabled={false}` to leave the assistant trigger off one page while keeping it everywhere else.
234
+
235
+ The language switcher is the exception. A custom page is served only at its own route, so Blume can't know which other locales have a version of it, and the header shows no switcher unless you pass one: `localeSwitch` takes an entry per locale — `{ code, label, dir, href, current, untranslated }` — pointing at the page you built for each language.
234
236
 
235
237
  The [agent-discovery head links](/docs/discoverability/agent-discovery#discovery-link-header) come along too: the layout reads the resolved config, so a custom page carries the same `describedby`, `ai-catalog`, and `ard` links the docs pages do without passing a prop. The homepage also advertises its `/index.md` Markdown mirror as a `text/markdown` alternate, since that mirror always exists; other custom pages have none, so none is advertised. Pass `discovery={null}` to drop the links from one page.
236
238
 
237
239
  Pass `transparentHeader` to start the header see-through with its chrome in white, so it can sit over a dark hero at the top of the page; it becomes the usual frosted bar as soon as the page scrolls, and the search dialog keeps the page's own colors throughout. The hero has to run under the header for this to show — pull it up by the header's height (`-mt-16`) and pad its top to compensate.
238
240
 
239
- Passing `siteUrl` (and `ogEnabled`) derives the page's `canonical` and a generated `og:image` automatically: Blume renders an Open Graph card for every static custom page (not a dynamic `[param]` one) — the home included, the most-shared URL — served at `/og/<route>.png` (`/og/index.png` for `/`). The home card uses the site title with the description as its eyebrow; a deeper page is titled from its last path segment. Set `ogImage` or `canonical` explicitly to override either. `ogImage` takes a root-relative path — a file in `public/`, resolved against [`deployment.site`](/docs/deployment) to the absolute URL crawlers need — or an external URL, which passes through untouched:
241
+ Passing `siteUrl` (and `ogEnabled`) derives the page's `canonical` and a generated `og:image` automatically: Blume renders an Open Graph card for every static custom page (not a dynamic `[param]` one) — the home included, the most-shared URL — served at `/og/<route>.png` (`/og/index.png` for `/`). The home card's headline is the site title, with the site description as the subtitle beneath it; a deeper page is titled from its last path segment. Set `ogImage` or `canonical` explicitly to override either. `ogImage` takes a root-relative path — a file in `public/`, resolved against [`deployment.site`](/docs/deployment) to the absolute URL crawlers need — or an external URL, which passes through untouched:
240
242
 
241
243
  ```astro pages/index.astro lineNumbers
242
244
  <PageLayout
@@ -497,7 +497,7 @@ Fix: Remove the URL from the sitemap, or build the page it names.
497
497
 
498
498
  `BLUME_AUDIT_SITEMAP_INVALID` · error · from the built HTML
499
499
 
500
- Fix: Sitemaps must be valid XML in the sitemaps.org urlset format.
500
+ Fix: Sitemaps must be valid XML in the sitemaps.org urlset or sitemap index format.
501
501
 
502
502
  #### Sitemap exceeds 50 MB or 50,000 URLs [#sitemap-too-large]
503
503
 
@@ -573,7 +573,7 @@ Fix: Rebuild so llms.txt matches the site — a stale entry sends an AI agent to
573
573
 
574
574
  `BLUME_AUDIT_LLMS_TXT_PAGE_MISSING` · warning · from the built HTML
575
575
 
576
- Fix: Rebuild so llms.txt matches the site; if the page is deliberately excluded, mark it `seo.noindex` or `sidebar.hidden`.
576
+ Fix: Rebuild so llms.txt matches the site; if the page is deliberately excluded, set `ai.exclude: true` in its front matter.
577
577
 
578
578
  #### No DNS-AID agent-discovery records [#dns-aid-missing]
579
579
 
@@ -14,11 +14,11 @@ blume doctor
14
14
  - **The Node version** against the range the installed `blume` package supports, read from its own `engines` field. A version outside it is a warning: things may work, but it isn't a combination Blume tests.
15
15
  - **`blume.config.ts`**, with the same validation a build runs. A removed or renamed key fails with a hint naming its replacement rather than a bare "unrecognized key".
16
16
  - **Every content page and folder meta**: the diagnostics `blume dev` and `blume build` print as they load the project — invalid frontmatter, navigation problems, missing include targets, and the rest — collected in one report.
17
- - **Features that need a server** on a site configured for static output — the assistant, the MCP server, or the Try it playground's built-in proxy: an error naming the feature, with the deployment adapter to switch to (or, when a host adapter is set to `output: "static"`, telling you to drop that option).
17
+ - **Features that need a server** on a site configured for static output — the assistant, the MCP server, the Try it playground's built-in proxy, or server-mode search (Mixedbread): an error naming the feature, with the deployment adapter to switch to (or, when a host adapter is set to `output: "static"`, telling you to drop that option).
18
18
  - **Packages your config needs** that aren't installed — the SDK a search, content source, or assistant adapter imports, a deployment adapter's `@astrojs/*` package, or the renderer for Vue or Svelte islands: an error naming each package, with the install command for your package manager. `blume build` stops on the same check.
19
19
  - **`components.ts` overrides** Blume can't plan — an inline or computed entry, or an import whose file doesn't exist: an error for each, at its line.
20
20
  - **A version-shaped folder** (`v1.0/`) on a site with no `versions` configured, which would otherwise build as ordinary content: a warning pointing at [`blume version`](/docs/cli/version).
21
- - **Secrets an enabled feature reads** that aren't set, such as `ALGOLIA_ADMIN_API_KEY` or `OPENROUTER_API_KEY`: a warning naming the variable. Doctor loads `.env` and `.env.local` first, like `blume dev` and `blume build`.
21
+ - **Secrets an enabled feature reads** that aren't set, such as `MIXEDBREAD_API_KEY` or `OPENROUTER_API_KEY`: a warning naming the variable. Doctor loads `.env` and `.env.local` first, like `blume dev` and `blume build`.
22
22
 
23
23
  ## The summary
24
24
 
@@ -74,7 +74,7 @@ Write questions your users actually ask — the ones from support threads, GitHu
74
74
 
75
75
  ## Failing CI
76
76
 
77
- The exit code is the contract: any failed question exits non-zero. When the agent run itself fails — the reader or judge errors out rather than grading an answer — the report says `run failed:` and points at the question in your evals file instead of naming a docs page to fix, since the docs weren't graded. `--threshold` relaxes the gate to a passing fraction when you're digging out of a backlog:
77
+ The exit code is the contract: any failed question exits non-zero, except a `severity: warning` one — its miss is reported as a warning and never counts against the gate or `--threshold`. When the agent run itself fails — the reader or judge errors out rather than grading an answer — the report says `run failed:` and points at the question in your evals file instead of naming a docs page to fix, since the docs weren't graded. `--threshold` relaxes the gate to a passing fraction when you're digging out of a backlog:
78
78
 
79
79
  ```bash
80
80
  blume eval # every question must pass
@@ -100,7 +100,7 @@ This writes the full JSON report to a file and opens the agent interactively wit
100
100
 
101
101
  - `--agent codex|claude` — which agent CLI runs the reader and judge. Defaults to `codex`.
102
102
  - `--file <path>` — the evals file. Defaults to `evals.yaml`.
103
- - `--threshold <0..1>` — minimum passing fraction before the run exits non-zero. Defaults to `1`.
103
+ - `--threshold <0..1>` — minimum passing fraction before the run exits non-zero, with `severity: warning` misses counted as passing. Defaults to `1`.
104
104
  - `--timeout <seconds>` — reader time limit per question. Defaults to `180`.
105
105
  - `--json` — emit the report as JSON on stdout.
106
106
  - `--fix` — after a failing run, hand the report to the agent to fix the docs interactively.
@@ -15,7 +15,7 @@ blume <command> [options]
15
15
  | `blume dev` | Start the dev server with hot reload. |
16
16
  | `blume build` | Build the static (or server) site. |
17
17
  | `blume preview` | Preview the last build. |
18
- | `blume add <item>` | Install a source component from the registry. |
18
+ | `blume add [item]` | Install a source component from the registry (no item lists what's available). |
19
19
  | `blume sync` | Re-fetch remote content sources and regenerate. |
20
20
  | `blume eject` | Promote the runtime into a standalone Astro app. |
21
21
  | `blume check` | Type-check the site with `astro check`. |
@@ -47,11 +47,14 @@ blume <command> [options]
47
47
  - `blume build --isolated` — build into a throwaway `.blume-verify/` runtime (and its own `dist/`) instead of `.blume/`, so a running `blume dev` server and your real `dist/` are left untouched. See [Verifying while the dev server runs](#verifying-while-the-dev-server-runs).
48
48
  - `blume preview --host --port <n>` — bind the preview server.
49
49
  - `blume sync --force` — re-fetch remote sources, dropping the cached snapshot first.
50
+ - `blume sync --preview` — include drafts and unpublished CMS content.
51
+ - `blume sync --strict` — fail on diagnostics.
50
52
  - `blume add <item> --force` — overwrite files that already exist.
51
53
  - `blume check --preview` — include drafts and unpublished CMS content when checking.
52
54
  - `blume check --strict` — fail on content diagnostics as well as type errors.
53
55
  - `blume check --isolated` — type-check in a throwaway `.blume-verify/` runtime so a running `blume dev` server is left untouched. See [Verifying while the dev server runs](#verifying-while-the-dev-server-runs).
54
56
  - `blume eject --yes` — skip the confirmation prompt.
57
+ - `blume eject --force` — eject again over an already-ejected app, overwriting its `astro.config.mjs` and `src/`.
55
58
 
56
59
  The commands with a page of their own list every flag there: [`blume doctor`](/docs/cli/doctor), [`blume validate`](/docs/cli/validate), [`blume audit`](/docs/cli/audit), [`blume eval`](/docs/cli/evals), [`blume translate`](/docs/cli/translate), and [`blume version`](/docs/cli/version). `blume validate`, `blume doctor`, `blume audit`, `blume eval`, and `blume translate` take `--json` to print machine-readable results on stdout for CI and editor integrations (see [Validate](/docs/cli/validate#json-output) for the diagnostics shape); `build`, `check`, and `dev` report to the terminal only. Every command rejects a flag it doesn't take, suggesting the closest match and listing the flags it accepts, so a typo like `--isolatd` fails instead of being ignored.
57
60
 
@@ -72,7 +72,7 @@ The JSON report carries the same `diagnostics` + `summary` shape as `blume valid
72
72
 
73
73
  ## Flags
74
74
 
75
- - `--codex` / `--claude` — which agent CLI translates. Exactly one is required (except with `--check`).
75
+ - `--codex` / `--claude` — which agent CLI translates. Exactly one is required, except with `--check`, which runs no agent and takes neither.
76
76
  - `--check` — report drift and exit non-zero, without writing anything.
77
77
  - `--concurrency <n>` — parallel agent sessions. Defaults to `4`, max `16`.
78
78
  - `--locale <codes>` — comma-separated target locales (defaults to every non-default locale).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Analytics
3
- description: First-party web analytics — PostHog, Google Analytics, Plausible, Mixpanel, Segment, and a dozen more — as adapters in blume.config.ts.
3
+ description: First-party web analytics — PostHog, Google Analytics, Plausible, Mixpanel, Segment, and more — as adapters in blume.config.ts.
4
4
  ---
5
5
 
6
6
  Blume injects analytics for you from an `analytics` list in `blume.config.ts`. Each entry is an adapter imported from `blume/analytics`: one per provider, plus `script()` for anything without one.
@@ -38,6 +38,7 @@ Every identifier below — a project key, a measurement ID, a site token — is
38
38
  | `clarity()` | [Microsoft Clarity](#microsoft-clarity) | `id` |
39
39
  | `clearbit()` | [Clearbit](#clearbit) | `key` |
40
40
  | `cloudflare()` | [Cloudflare Web Analytics](#cloudflare-web-analytics) | `token` |
41
+ | `databuddy()` | [Databuddy](#databuddy) | `clientId` |
41
42
  | `fathom()` | [Fathom](#fathom) | `site` |
42
43
  | `googleAnalytics()` | [Google Analytics 4](#google-analytics-4) | `id` |
43
44
  | `googleTagManager()` | [Google Tag Manager](#google-tag-manager) | `id` |
@@ -70,6 +71,8 @@ analytics: [
70
71
 
71
72
  `key` and `host` are the two options Blume maps (`host` becomes `api_host`). Anything else you pass — `persistence`, `capture_pageview`, `autocapture`, `disable_session_recording`, and every other `posthog.init` option — is forwarded to `posthog.init` verbatim.
72
73
 
74
+ Blume sends a `$pageview` for each client-router navigation only while PostHog captures page loads alone, which is its behavior when you set neither `capture_pageview` nor `defaults`. With `capture_pageview: "history_change"`, or a `defaults` date such as `"2025-05-24"` from PostHog's current snippet, PostHog captures navigations itself, so Blume sends nothing extra. With `capture_pageview: false`, no pageviews are sent at all.
75
+
73
76
  ## Vercel Web Analytics
74
77
 
75
78
  Add `vercel()` to include [Vercel Web Analytics](https://vercel.com/docs/analytics). Blume renders Vercel's official Astro component, which injects the first-party script served from your own domain once Web Analytics is enabled for the project in the Vercel dashboard.
@@ -172,6 +175,22 @@ analytics: [
172
175
 
173
176
  `code` becomes `data-code`, and Blume gives the tag the `pianjs` id Pirsch's script finds itself by. Any other option becomes its own `data-` attribute (`dev`, `exclude`, `include`, `domain`, `endpoint`, …).
174
177
 
178
+ ## Databuddy
179
+
180
+ Pass your **client ID** (from the website's settings in [Databuddy](https://databuddy.cc)) to `databuddy()`.
181
+
182
+ ```ts blume.config.ts lineNumbers
183
+ analytics: [
184
+ databuddy({
185
+ clientId: "xxxxxxxxxxxxxxxxxxxxx",
186
+ "track-web-vitals": "true", // optional: any other `data-` setting
187
+ "skip-patterns": '["/admin/*"]',
188
+ }),
189
+ ],
190
+ ```
191
+
192
+ `clientId` becomes `data-client-id`. Any other option becomes its own `data-` attribute on the tag, so name it the way the attribute reads, in kebab-case (`track-web-vitals`, `track-errors`, `track-outgoing-links`, `api-url`, …). Databuddy ignores a camelCase name like `trackWebVitals`. Values are strings: `"true"` or `"false"` for a switch, and a JSON array for `skip-patterns` and `mask-patterns`. Databuddy reads any other list value as empty.
193
+
175
194
  ## Mixpanel
176
195
 
177
196
  Pass your **project token** (project settings → Access Keys in [Mixpanel](https://mixpanel.com)) to `mixpanel()`. If the project uses EU or India data residency, set `region` to match, or Mixpanel drops the events.
@@ -351,6 +370,8 @@ analytics: [
351
370
  | `clearbit()` | `key` | — | Clearbit publishable API key. Required. |
352
371
  | `cloudflare()` | `token` | — | Cloudflare Web Analytics site token (manual setup). Required. |
353
372
  | `cloudflare()` | anything else | — | Forwarded in the beacon's `data-cf-beacon` JSON verbatim. |
373
+ | `databuddy()` | `clientId` | — | Databuddy client ID. Required. |
374
+ | `databuddy()` | anything else | — | Rendered as a `data-` attribute on the tag. |
354
375
  | `fathom()` | `site` | — | Fathom site ID. Required. |
355
376
  | `fathom()` | `spa` | `"auto"` | Fathom's history-change tracking (`data-spa`). |
356
377
  | `fathom()` | anything else | — | Rendered as a `data-` attribute on the tag. |
@@ -389,4 +410,4 @@ analytics: [
389
410
 
390
411
  ## Custom events
391
412
 
392
- Blume's page feedback sends a custom event through every configured adapter that has a client API — PostHog, Mixpanel, Heap, Segment, Hightouch, Amplitude, LogRocket, Adobe, Google Analytics, Google Tag Manager (as a `{ event }` push on `window.dataLayer`), Plausible, Fathom, Pirsch, Clarity, Hotjar, and Vercel. Cloudflare and Clearbit have no event API. Every event also fires as a `blume:track` CustomEvent on `window` with `{ event, props }` in `detail`, so a `script()` adapter can forward it anywhere else. For deploying your built site, see [Deployment](/docs/deployment).
413
+ Blume's page feedback sends a custom event through every configured adapter that has a client API — PostHog, Mixpanel, Heap, Segment, Hightouch, Amplitude, LogRocket, Adobe, Google Analytics, Google Tag Manager (as a `{ event }` push on `window.dataLayer`), Plausible, Databuddy, Fathom, Pirsch, Clarity, Hotjar, and Vercel. Cloudflare and Clearbit have no event API. Every event also fires as a `blume:track` CustomEvent on `window` with `{ event, props }` in `detail`, so a `script()` adapter can forward it anywhere else. For deploying your built site, see [Deployment](/docs/deployment).
@@ -54,7 +54,7 @@ Your text is **appended to** the built-in instructions rather than replacing the
54
54
 
55
55
  The assistant is **grounded in your docs**. For each question it retrieves the most relevant pages — using the same lexical [Orama](/docs/configuration/search) index that powers on-page search — and injects them into the model's system prompt, so answers come from your content instead of the model's own knowledge. The assistant is told to answer only from the retrieved pages, to say when something isn't covered, and to cite the pages it drew from.
56
56
 
57
- The page the reader is currently on is added to the context first and used to scope retrieval to that page's language, so answers stay relevant to where they are in the docs. Retrieval runs at request time from a snapshot baked into the build, so it works regardless of your [search](/docs/configuration/search) provider — even with `search: false` — and needs no configuration.
57
+ The page the reader is currently on is added to the context first and used to scope retrieval to that page's language — and, on a [versioned](/docs/content/versioning) site, to its docs version — so answers stay relevant to where they are in the docs. Retrieval runs at request time from a snapshot baked into the build, so it works regardless of your [search](/docs/configuration/search) provider — even with `search: false` — and needs no configuration.
58
58
 
59
59
  Grounding is on for every adapter except **[Inkeep](#inkeep)**, which runs its own retrieval over the content you've indexed in its dashboard.
60
60
 
@@ -97,6 +97,7 @@ 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
101
  | `PageHeader` | An injection point above the article (no built-in) | `page`, `headings`, `route` |
101
102
  | `PageFooter` | An injection point below the article (no built-in) | `page`, `headings`, `route` |
102
103
  | `Footer` | A site-wide footer after the content grid (no built-in) | `site`, `navigation`, `ui` |
@@ -184,10 +185,10 @@ npx blume eject --yes
184
185
 
185
186
  Eject is a one-way step: the hidden `.blume/` runtime becomes a normal Astro app you own and can modify directly. The `blume` package stays importable, so you keep its components, theme, and Markdown processors.
186
187
 
187
- Eject points your `package.json` scripts at `astro dev` and `astro build`, and adds the packages the Astro app imports by name — `astro`, `@tailwindcss/vite`, the search client, integrations, and adapter the config wires in, React when an island, example, or the assistant uses it, `ai` for the assistant route, and `epub-gen-memory` for EPUB export — at the ranges Blume itself uses. Run an install before `dev` or `build`; eject lists what it added. Every path in the ejected app is relative, so it builds from any checkout, CI included.
188
+ Eject points your `package.json` scripts at `astro dev` and `astro build`, and adds the packages the Astro app imports by name — `astro`, `@tailwindcss/vite`, `tailwindcss` and `@tailwindcss/typography` for the generated stylesheets, the search client, integrations, and adapter the config wires in, React when an island, example, or the assistant uses it, `ai` for the assistant route, and `epub-gen-memory` for EPUB export — at the ranges Blume itself uses. Run an install before `dev` or `build`; eject lists what it added. Every path in the ejected app is relative, so it builds from any checkout, CI included.
188
189
 
189
- From then on, run the app through its own scripts (`npm run dev`, `npm run build`): `blume dev` and `blume build` stop in an ejected project and point you there. Running `blume eject` again refuses too, since it would overwrite your edits; pass `--force` to regenerate the app anyway.
190
+ From then on, run the app through its own scripts (`npm run dev`, `npm run build`): `blume dev`, `blume build`, `blume check`, `blume sync`, and `blume preview` stop in an ejected project and point you there. Running `blume eject` again refuses too, since it would overwrite your edits; pass `--force` to regenerate the app anyway.
190
191
 
191
192
  ### What eject keeps
192
193
 
193
- The ejected app's `build` script runs plain `astro build`, and the artifacts `blume build` layers on top — the search index (and a hosted provider's index sync), `llms.txt` and `llms-full.txt`, `sitemap.xml`, `robots.txt`, `agent-readability.json`, the `.well-known` discovery files, Agent Skills, and the platform `_redirects`/`_headers` files — are still produced: the Blume integration in the ejected `astro.config.mjs` writes them from Astro's `astro:build:done` hook, scanning the project (your `blume.config.ts` and content) the way the CLI did. What the ejected build does not do is the CLI's adapter post-processing: the Vercel and Cloudflare `Accept: text/markdown` routing splices, the Vercel function-bundle audit, and the `--analyze`/`--budget-*` gate.
194
+ The ejected app's `build` script runs plain `astro build`, and the artifacts `blume build` layers on top — the search index (and a hosted provider's index sync), `llms.txt` and `llms-full.txt`, `sitemap.xml`, `robots.txt`, `agent-readability.json`, the `.well-known` discovery files, Agent Skills, and the platform `_redirects`/`_headers` files — are still produced: the Blume integration in the ejected `astro.config.mjs` writes them from Astro's `astro:build:done` hook, scanning the project (your `blume.config.ts` and content) the way the CLI did. What the ejected build does not do is the CLI's adapter post-processing: the Vercel and Cloudflare `Accept: text/markdown` routing splices, the Vercel function-bundle audit, the `node()` server-entry wrapper (the `.well-known` discovery files' media types and CORS headers, the sandbox on downloaded SVGs, and each redirect's exact status), the header rules a `netlify()` server build writes into `.netlify/v1/config.json`, Cloudflare's Worker naming (after your project) and its `.wrangler/deploy` redirect for running `wrangler deploy` from the project root, and the `--analyze`/`--budget-*` gate.
@@ -125,7 +125,7 @@ logo: {
125
125
  `text` controls the wordmark independently of the mark:
126
126
 
127
127
  - **Omit `text`** and the brand uses your site `title` (the default).
128
- - **Set `text: ""`** to show the mark alone — handy when the logo image already includes the wordmark.
128
+ - **Set `text: ""`** to show the mark alone — handy when the logo image already includes the wordmark. Screen readers then announce the brand link by the image's `alt`, or by your site `title` when it has none.
129
129
  - **Set `text` with no `image`** for a text-only logo.
130
130
 
131
131
  ### Favicon
@@ -272,7 +272,7 @@ A key can be declared site-wide or per-type, not both. See [Per-type keys](/docs
272
272
 
273
273
  ## GitHub
274
274
 
275
- Point Blume at your repository with `github`. It powers the header [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub** and **Give feedback** [page actions](/docs/content/navigation#page-actions):
275
+ Point Blume at your repository with `github`. It powers the header [repository link](/docs/content/navigation#repository-link) and the **Edit on GitHub** [page action](/docs/content/navigation#page-actions):
276
276
 
277
277
  ```ts blume.config.ts lineNumbers
278
278
  github: {
@@ -364,6 +364,8 @@ dateFormat: { year: "numeric", month: "2-digit", day: "2-digit" },
364
364
  | `timeZone` | IANA time zone. Defaults to `UTC`, so a date reads the same regardless of where the site builds. |
365
365
  | `calendar`, `numberingSystem` | Calendar system (e.g. `"japanese"`) and numbering system (e.g. `"arab"`). |
366
366
 
367
+ `timeZone`, `calendar`, and `numberingSystem` don't change the shape: a `dateFormat` that sets only those keeps the long form.
368
+
367
369
  ## SEO and agents
368
370
 
369
371
  Metadata, Open Graph images, RSS feeds, JSON-LD, the sitemap, and `robots.txt` live under `seo`; `llms.txt`, raw Markdown, the JSON API, the MCP server, and the discovery manifests live under `agents`. Both are covered page by page in the [Discoverability](/docs/discoverability) section, which treats search engines and AI agents as two audiences for the same machine-readable layer. The reader-facing model features — the assistant and the Open in chat action — live under `ai`.
@@ -381,7 +383,7 @@ seo: {
381
383
  | Option | Default | Description |
382
384
  | --- | --- | --- |
383
385
  | `og.enabled` | auto | Per-page Open Graph images — on when a site URL is set. |
384
- | `rss.enabled` | `true` | Build feeds for blog and changelog content. |
386
+ | `rss.enabled` | `true` | Build feeds for blog and changelog content (needs deployment.site). |
385
387
  | `rss.types` | `["blog", "changelog"]` | Content types that each get a feed. |
386
388
  | `rss.limit` | `50` | Maximum items per feed. |
387
389
  | `sitemap` | `true` | Generate sitemap.xml (needs deployment.site). |
@@ -145,7 +145,7 @@ Pagefind only runs during `blume build`, so search isn't available in `blume dev
145
145
 
146
146
  ### Algolia
147
147
 
148
- The browser queries [Algolia](https://www.algolia.com) directly with your search-only key. Each `blume build` replaces the index using the admin key from `ALGOLIA_ADMIN_API_KEY` (the build warns and skips the upload if it's unset). The whole index is replaced on every sync, so pages you delete or rename don't linger as stale results.
148
+ The browser queries [Algolia](https://www.algolia.com) directly with your search-only key. Each `blume build` replaces the index using the admin key from `ALGOLIA_ADMIN_API_KEY` (the build warns and skips the upload if it's unset). The whole index is replaced on every sync, so pages you delete or rename don't linger as stale results. The sync also adds `filterOnly(locale)` and `filterOnly(version)` to the index's `attributesForFaceting`, keeping any facets you declared yourself, because the dialog scopes results by language and docs version, and in Algolia a filter on an attribute that isn't declared for faceting matches nothing.
149
149
 
150
150
  ```ts blume.config.ts lineNumbers
151
151
  import { algolia } from "blume/search";
@@ -560,7 +560,7 @@ export interface ButtonProps {
560
560
 
561
561
  ## GitHub info
562
562
 
563
- A card linking to a GitHub repository with its live star and fork counts. Counts are fetched at build time — no client JavaScript — and the card still renders if the API is unreachable. Pass `owner` and `repo`, or omit them to use the repository from your `blume.config`. Set a `GITHUB_TOKEN` environment variable to lift the API rate limit; a `token` prop overrides it for one card, but the environment variable keeps the token out of your content.
563
+ A card linking to a GitHub repository with its star and fork counts. The counts are fetched at build time — no client JavaScript — so they show the numbers as of your last build, not live ones; the card still renders without them if the API is unreachable. Pass `owner` and `repo`, or omit them to use the repository from your `blume.config`. Set a `GITHUB_TOKEN` environment variable to lift the API rate limit; a `token` prop overrides it for one card, but the environment variable keeps the token out of your content.
564
564
 
565
565
  The card reads the instance from [`github.host`](/docs/configuration#github-enterprise), so on an Enterprise-hosted site explicit `owner`/`repo` address that instance too. Pass `host` to point one card somewhere else — a public project from an Enterprise site, say; the REST base is derived from it the same way it is from `github.host`.
566
566
 
@@ -23,7 +23,11 @@ Every page accepts the following frontmatter. All fields are optional.
23
23
  description:
24
24
  "Post author(s) for blog/changelog content — a name, or objects with a name plus optional avatar/url and any extra fields. Preserved as-is.",
25
25
  },
26
- slug: { type: "string", description: "Override the generated slug." },
26
+ slug: {
27
+ type: "string",
28
+ description:
29
+ "Set the page's full route from the content root (guides/setup). It replaces the whole path the file's location gives the page, not just the last segment.",
30
+ },
27
31
  draft: {
28
32
  type: "boolean",
29
33
  default: "false",
@@ -81,7 +81,7 @@ i18n: {
81
81
 
82
82
  ## Per-locale navigation
83
83
 
84
- Each language gets its own sidebar, built from that locale's files — so translations can diverge in structure, ordering, or labels. Folder [`meta.ts`](/docs/content/meta) files resolve per locale, too: under the default `dir` parser, put a `meta.ts` under `fr/guides/` to order the French group independently. Under the `dot` parser translations sit next to the originals, so a folder's `meta.ts` applies to every locale. Everything else about [navigation](/docs/content/navigation) works the same, per language.
84
+ Each language gets its own sidebar, built from that locale's files — so translations can diverge in structure, ordering, or labels. Folder [`meta.ts`](/docs/content/meta) files resolve per locale, too: under the default `dir` parser, put a `meta.ts` under `fr/guides/` to order the French group independently. Until a locale has one (or a shared `meta.$.ts`), its group mirrors the [fallback](#fallbacks) locale's — that folder's `meta.ts` and the `sidebar.display` its index page sets — so pages that fall back keep the same titles, order, and collapsible groups. Under the `dot` parser translations sit next to the originals, so a folder's `meta.ts` applies to every locale. Everything else about [navigation](/docs/content/navigation) works the same, per language.
85
85
 
86
86
  Header tabs are configured, not derived from content, so their labels localize in `blume.config.ts`: a tab `label` accepts a per-locale map (`{ en: "Docs", fr: "Documentation" }`) alongside the plain-string form, falling back to the default locale's entry for locales you haven't filled in. See [Tabs](/docs/content/navigation#tabs). Tab paths, header links, and the header logo's link move into the reader's locale too, whenever that locale serves the route, so the header stays inside one language. A route only the default locale serves — a [custom page](/docs/advanced/custom-pages) or the generated [changelog](/docs/advanced/changelog) index — keeps its own path instead of pointing at a localized URL that would 404.
87
87
 
@@ -31,13 +31,15 @@ Nested folders become nested routes, and an `index.mdx` inside a folder becomes
31
31
 
32
32
  ## Ordering with numeric prefixes
33
33
 
34
- Prefix a file or folder with a number to control its order in the sidebar. The prefix is stripped from the URL, so you can reorder pages without breaking links:
34
+ Prefix a file or folder with a number and a `-`, `_`, or `.` to control its order in the sidebar. The prefix is stripped from the URL, so you can reorder pages without breaking links:
35
35
 
36
36
  ```txt
37
37
  01-introduction.mdx -> /introduction
38
38
  02-installation.mdx -> /installation
39
39
  ```
40
40
 
41
+ A version or an ISO date at the start of a name is part of the name, not an order: `1.2.0.mdx` routes to `/1.2.0`, and `2024-01-05-launch.mdx` to `/2024-01-05-launch`. Only file and folder names lose a prefix (an Obsidian vault's notes count as files). A frontmatter `slug`, and a page from a [content source](/docs/content/sources) such as a CMS or GitHub Releases, keep the name they were given.
42
+
41
43
  Ordering has several layers — see [Navigation](/docs/content/navigation) for the full precedence rules.
42
44
 
43
45
  ## Group folders
@@ -108,7 +110,7 @@ Every page gets an automatic table of contents, built from its headings. On wide
108
110
 
109
111
  Blume slugifies each heading into an anchor, so every entry links straight to its section — and you can deep-link to any heading by appending its slug to the URL (`.../my-page#getting-started`).
110
112
 
111
- The contents list your `##` and `###` headings (H2 and H3). A page with no headings at that level simply has no table of contents.
113
+ By default the contents list your `##` and `###` headings (H2 and H3); set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the table of contents off. A page with no headings in that range simply has no table of contents.
112
114
 
113
115
  ## Where to next
114
116
 
@@ -23,6 +23,8 @@ export default defineMeta({
23
23
 
24
24
  Every field is optional — set only what you want to override.
25
25
 
26
+ Blume only reads `meta.ts` files from folders your content covers: one under a folder your content `exclude` globs skip, or outside every `include` glob, is never imported. With `root: "."` and `exclude: ["src/**"]`, an unrelated `src/lib/meta.ts` is left alone.
27
+
26
28
  ## Fields
27
29
 
28
30
  | Field | Type | Description |
@@ -34,7 +36,7 @@ Every field is optional — set only what you want to override.
34
36
  | `display` | `"flat" \| "group" \| "page"` | Render mode for this group; overrides the global [`navigation.sidebar.display`](/docs/content/navigation#display-modes). |
35
37
  | `pages` | `string[]` | Explicit order for the group's children, by slug. |
36
38
 
37
- The `pages` array lists children by slug — the folder or file name with its numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is `"quickstart"`). Children you leave out still appear, after the listed ones.
39
+ The `pages` array lists children by slug — the folder or file name with its numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is `"quickstart"`). Each listed child takes its position in the array as its order (`0`, `1`, `2`, …). Children you leave out still appear, sorted by their own order: an `index` page stays first, a child with a `sidebar.order`, a numeric prefix, or its own `meta.ts` `order` sorts by that number among the listed ones, and a child with none of these goes after them. List every child when the array should be the whole order.
38
40
 
39
41
  How groups render — flat headers, collapsible disclosures, or drill-in panels — defaults to the sidebar-wide `navigation.sidebar.display`; set `display` here to override it for this group alone. A folder's `index` page can also set it from frontmatter, which wins over `meta.ts` — see [per-group overrides](/docs/content/navigation#per-group-overrides).
40
42
 
@@ -53,13 +55,13 @@ export default defineMeta(async () => ({
53
55
 
54
56
  ## Ordering within a group
55
57
 
56
- The `pages` array sets the order of a group's children. Anything it omits falls back to each page's frontmatter `sidebar.order`, then the file system (an `index` page first, then numeric prefixes, then alphabetical). For the full sidebar precedence — including an explicit config sidebar — see [Navigation › Ordering](/docs/content/navigation#ordering).
58
+ The `pages` array sets the order of a group's children, and it wins over a listed page's own `sidebar.order` or a listed subfolder's own `order`. Anything it omits falls back to each page's frontmatter `sidebar.order`, then the file system (an `index` page first, then numeric prefixes, then alphabetical). For the full sidebar precedence — including an explicit config sidebar — see [Navigation › Ordering](/docs/content/navigation#ordering).
57
59
 
58
60
  To group pages _without_ adding a URL segment, you don't need a `meta.ts` at all: use a parenthesized folder name — see [Pages › Group folders](/docs/content#group-folders).
59
61
 
60
62
  ## Internationalization
61
63
 
62
- Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a `meta.ts` under `fr/guides/` to order the French group independently.
64
+ Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a `meta.ts` under `fr/guides/` to order the French group independently. Without one (or a shared `meta.$.ts`, below), the French group mirrors the fallback locale's `meta.ts`.
63
65
 
64
66
  For folder meta that's identical in every language, add a `$` marker so one file serves all locales without duplication:
65
67
 
@@ -150,7 +150,9 @@ navigation: {
150
150
  }
151
151
  ```
152
152
 
153
- An enabled [OpenAPI or AsyncAPI reference](/docs/references/openapi) mounts at its route but doesn't add a tab on its own — point a tab at that route to surface it in the header (and, for the native renderer, to scope its operations sidebar), with whatever label you like:
153
+ A tab's optional `icon` (a [built-in icon](/docs/content/components#icon) name, image path/URL, or inline SVG) shows beside its label, in the header and in the mobile navigation drawer.
154
+
155
+ An enabled [OpenAPI](/docs/references/openapi), [AsyncAPI](/docs/references/asyncapi), or [GraphQL](/docs/references/graphql) reference mounts at its route but doesn't add a tab on its own — point a tab at that route to surface it in the header (and, for the native renderer, to scope its operations sidebar), with whatever label you like:
154
156
 
155
157
  ```ts blume.config.ts
156
158
  navigation: {
@@ -174,6 +176,24 @@ navigation: {
174
176
 
175
177
  Tabs that don't set `href` keep the resolution above.
176
178
 
179
+ Give a tab `items` to make it a dropdown. The tab no longer links anywhere itself: it opens a menu of its items in the header, and expands them in place in the mobile navigation drawer. Its `path` still scopes the sidebar and marks the tab as current, and `href` doesn't apply. Each item takes a `label` and a `path`, plus an optional `icon`, `description`, and `tag`, like a [selector](#selectors) item:
180
+
181
+ ```ts blume.config.ts lineNumbers
182
+ navigation: {
183
+ tabs: [
184
+ { label: "Guides", path: "/guides" },
185
+ {
186
+ label: "SDKs",
187
+ path: "/sdks",
188
+ items: [
189
+ { label: "JavaScript", path: "/sdks/javascript", description: "Node and the browser" },
190
+ { label: "Python", path: "/sdks/python", tag: "Beta" },
191
+ ],
192
+ },
193
+ ],
194
+ }
195
+ ```
196
+
177
197
  On an [i18n](/docs/content/i18n) site, a tab's `label` (and a dropdown item's) can be a per-locale map instead of a string — the active locale's entry wins, then the default locale's:
178
198
 
179
199
  ```ts blume.config.ts
@@ -185,6 +205,8 @@ navigation: {
185
205
  }
186
206
  ```
187
207
 
208
+ A [selector](#selectors)'s labels don't take a map: its `label` and each of its items' labels are plain strings.
209
+
188
210
  Tabs also **scope the sidebar**: when the current route falls under a tab's `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the adapters and nothing else. The folder at a tab's `path` becomes the section, so this needs no extra config beyond the tabs themselves; structure your content into a folder per tab and point each tab at it.
189
211
 
190
212
  On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the pages that _don't_ belong to a tab — each tab's folder is hidden from it, since that section already has its own tab in the header. So a root landing page lists your loose top-level pages while the sectioned content stays behind its tab, mirroring Fumadocs' root folders. If a route has no pages of its own to show this way, the full tree is shown instead, so the sidebar is never left blank.
@@ -247,6 +269,8 @@ navigation: {
247
269
 
248
270
  Each item is a page route (a string), a group (`label` + `items`), or a link (`label` + `href`). Groups can nest, override the global [`display` mode](#display-modes), and start `collapsed`.
249
271
 
272
+ Blume warns about an item it can't render as written: a route that matches no page (the item is left out), a `root` that matches no page (its link would 404), or an item with no route, `href`, `root`, or `items` (left out).
273
+
250
274
  ## Header actions
251
275
 
252
276
  `navigation.actions` puts plain links in the header, left of the icon buttons, and `navigation.cta` is the one filled button:
@@ -293,7 +317,7 @@ These come for free from the sidebar tree — no configuration:
293
317
 
294
318
  ## On this page
295
319
 
296
- A right-rail outline is generated automatically from each page's `##` and `###` headings, so long pages stay scannable. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.
320
+ A right-rail outline is generated automatically from each page's headings — `##` and `###` by default — so long pages stay scannable. Set [`toc`](/docs/configuration#table-of-contents) to change the heading range or turn the outline off. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.
297
321
 
298
322
  ## Page actions
299
323
 
@@ -301,8 +325,9 @@ Below the table of contents, every page shows a set of quick actions:
301
325
 
302
326
  - **Edit on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
303
327
  - **Scroll to top** — smoothly returns to the top of long pages.
304
- - **Give feedback** — opens a prefilled GitHub issue with an optional reaction and note (also requires `github`).
305
328
 
306
- Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [AI](/docs/discoverability/markdown#copy-as-markdown).
329
+ Others hand the page to AI tools — **Copy as Markdown** and **Open in chat** — covered in [Markdown for agents](/docs/discoverability/markdown#copy-as-markdown).
330
+
331
+ Feedback lives at the foot of the page instead: a "Was this page helpful?" yes/no rating that sends a `feedback` analytics event and doesn't need `github` — see [Page feedback](/docs/configuration#page-feedback).
307
332
 
308
333
  With [`export`](/docs/configuration/export) on, an **Export** action also lets readers download the page as a PDF or EPUB.