blume 2.1.3 → 2.2.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.
Files changed (377) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +126 -0
  3. package/README.md +4 -4
  4. package/dist/cli/{chunk-74wfhv79.js → chunk-05f21r8h.js} +1 -1
  5. package/dist/cli/{chunk-9qzgg6mj.js → chunk-1cm0h819.js} +3 -3
  6. package/dist/cli/{chunk-nbyvgnnm.js → chunk-1rkxzg3n.js} +104 -47
  7. package/dist/cli/chunk-1rkxzg3n.js.map +36 -0
  8. package/dist/cli/{chunk-desvm72z.js → chunk-2nr29y95.js} +22 -26
  9. package/dist/cli/chunk-2nr29y95.js.map +13 -0
  10. package/dist/cli/{chunk-32khwcb6.js → chunk-49r370mb.js} +42 -69
  11. package/dist/cli/{chunk-32khwcb6.js.map → chunk-49r370mb.js.map} +9 -10
  12. package/dist/cli/{chunk-ms9z66nz.js → chunk-4rftmttv.js} +2 -2
  13. package/dist/cli/{chunk-s0bdnc7v.js → chunk-79bzzkx4.js} +5 -4
  14. package/dist/cli/chunk-79bzzkx4.js.map +10 -0
  15. package/dist/cli/{chunk-hvcxng5f.js → chunk-7tekknev.js} +6 -6
  16. package/dist/cli/{chunk-dv2z0rzf.js → chunk-8b489v1b.js} +38 -13
  17. package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-8b489v1b.js.map} +5 -4
  18. package/dist/cli/{chunk-0pmb4tr4.js → chunk-8f8xw4x2.js} +1195 -222
  19. package/dist/cli/chunk-8f8xw4x2.js.map +44 -0
  20. package/dist/cli/chunk-96snx8yy.js +329 -0
  21. package/dist/cli/chunk-96snx8yy.js.map +13 -0
  22. package/dist/cli/{chunk-ckrcm08h.js → chunk-a3ydrqny.js} +48 -62
  23. package/dist/cli/{chunk-ckrcm08h.js.map → chunk-a3ydrqny.js.map} +4 -5
  24. package/dist/cli/{chunk-bxwhtvw8.js → chunk-ahc5gc9h.js} +5 -5
  25. package/dist/cli/{chunk-dpy8qdjf.js → chunk-as09pafp.js} +73 -182
  26. package/dist/cli/chunk-as09pafp.js.map +18 -0
  27. package/dist/cli/{chunk-tfycddbt.js → chunk-bjk964fp.js} +2 -2
  28. package/dist/cli/{chunk-gfc5fqvf.js → chunk-br0n8n3e.js} +18 -18
  29. package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-br0n8n3e.js.map} +1 -1
  30. package/dist/cli/{chunk-297ajdcv.js → chunk-c69tw29j.js} +6 -6
  31. package/dist/cli/{chunk-297ajdcv.js.map → chunk-c69tw29j.js.map} +3 -3
  32. package/dist/cli/chunk-cn3dy3hz.js +146 -0
  33. package/dist/cli/chunk-cn3dy3hz.js.map +11 -0
  34. package/dist/cli/{chunk-124fvmzq.js → chunk-dm4mg9bj.js} +11064 -8742
  35. package/dist/cli/{chunk-124fvmzq.js.map → chunk-dm4mg9bj.js.map} +78 -64
  36. package/dist/cli/{chunk-dqhg54mr.js → chunk-dmx6cezx.js} +1 -1
  37. package/dist/cli/{chunk-60hz265r.js → chunk-dpcahcrb.js} +24 -11
  38. package/dist/cli/chunk-dpcahcrb.js.map +10 -0
  39. package/dist/cli/{chunk-a57yh0h9.js → chunk-dyk3r7jw.js} +7 -7
  40. package/dist/cli/{chunk-a57yh0h9.js.map → chunk-dyk3r7jw.js.map} +1 -1
  41. package/dist/cli/{chunk-twy7psdz.js → chunk-enk76qd5.js} +14 -10
  42. package/dist/cli/chunk-enk76qd5.js.map +10 -0
  43. package/dist/cli/{chunk-k3gr1nm9.js → chunk-fqy4n3q2.js} +13 -16
  44. package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-fqy4n3q2.js.map} +3 -3
  45. package/dist/cli/{chunk-sc93apfs.js → chunk-frk2wdq1.js} +17 -17
  46. package/dist/cli/{chunk-sc93apfs.js.map → chunk-frk2wdq1.js.map} +2 -2
  47. package/dist/cli/{chunk-kdpzw38a.js → chunk-gprwej7h.js} +95 -7
  48. package/dist/cli/chunk-gprwej7h.js.map +11 -0
  49. package/dist/cli/chunk-kkrteb15.js +157 -0
  50. package/dist/cli/chunk-kkrteb15.js.map +10 -0
  51. package/dist/cli/{chunk-ave1wcbr.js → chunk-m9bp2hc1.js} +1 -1
  52. package/dist/cli/{chunk-58bt3hag.js → chunk-mrvh4c88.js} +4 -4
  53. package/dist/cli/{chunk-5cmts6dv.js → chunk-p8zg3a2h.js} +7 -7
  54. package/dist/cli/{chunk-5cmts6dv.js.map → chunk-p8zg3a2h.js.map} +1 -1
  55. package/dist/cli/{chunk-0w6hs479.js → chunk-rkqae7kh.js} +30 -18
  56. package/dist/cli/chunk-rkqae7kh.js.map +10 -0
  57. package/dist/cli/{chunk-bqxaqn5p.js → chunk-t5xx74rd.js} +8 -8
  58. package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-t5xx74rd.js.map} +1 -1
  59. package/dist/cli/{chunk-1f882900.js → chunk-th0pgaj5.js} +2 -2
  60. package/dist/cli/{chunk-dhwtqnnd.js → chunk-vnbvhgmd.js} +2 -2
  61. package/dist/cli/{chunk-gxtg6ed2.js → chunk-vzy9rh6y.js} +21 -21
  62. package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-vzy9rh6y.js.map} +3 -3
  63. package/dist/cli/{chunk-j1x92mx9.js → chunk-w6e455pz.js} +1 -1
  64. package/dist/cli/chunk-wehv26ry.js +155 -0
  65. package/dist/cli/chunk-wehv26ry.js.map +11 -0
  66. package/dist/cli/{chunk-y4msrwm9.js → chunk-xgq4eytj.js} +15 -15
  67. package/dist/cli/{chunk-36bxs9c7.js → chunk-xt0qvz1t.js} +27 -23
  68. package/dist/cli/chunk-xt0qvz1t.js.map +10 -0
  69. package/dist/cli/index.js +78 -42
  70. package/dist/cli/index.js.map +7 -6
  71. package/dist/types/ai/changelog-components.d.ts +18 -0
  72. package/dist/types/ai/changelog-markdown.d.ts +12 -0
  73. package/dist/types/ai/openapi-components.d.ts +7 -7
  74. package/dist/types/ai/operation-details.d.ts +39 -0
  75. package/dist/types/ai/relative-links.d.ts +3 -1
  76. package/dist/types/ai/serializers.d.ts +6 -4
  77. package/dist/types/analytics/head.d.ts +4 -2
  78. package/dist/types/analytics/script.d.ts +8 -2
  79. package/dist/types/components/content/youtube.d.ts +20 -0
  80. package/dist/types/components/layout/nav-utils.d.ts +8 -0
  81. package/dist/types/components/openapi/example-panels.d.ts +33 -0
  82. package/dist/types/components/openapi/helpers.d.ts +53 -11
  83. package/dist/types/components/openapi/snippets.d.ts +7 -1
  84. package/dist/types/core/alert-diagnostics.d.ts +10 -0
  85. package/dist/types/core/builtin-tags.d.ts +16 -0
  86. package/dist/types/core/changelog-list.d.ts +48 -0
  87. package/dist/types/core/closest-match.d.ts +6 -0
  88. package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
  89. package/dist/types/core/code-fences.d.ts +8 -1
  90. package/dist/types/core/config-input.d.ts +1 -1
  91. package/dist/types/core/content-assets.d.ts +16 -18
  92. package/dist/types/core/diagnostics.d.ts +14 -0
  93. package/dist/types/core/directive-diagnostics.d.ts +22 -8
  94. package/dist/types/core/former-routes.d.ts +14 -0
  95. package/dist/types/core/github.d.ts +5 -4
  96. package/dist/types/core/heading-markers.d.ts +65 -2
  97. package/dist/types/core/hidden-pages.d.ts +3 -0
  98. package/dist/types/core/includes.d.ts +23 -0
  99. package/dist/types/core/last-modified.d.ts +18 -6
  100. package/dist/types/core/links.d.ts +42 -2
  101. package/dist/types/core/load-module.d.ts +19 -4
  102. package/dist/types/core/manifest.d.ts +14 -1
  103. package/dist/types/core/markdown-mirrors.d.ts +22 -0
  104. package/dist/types/core/mdx-elements.d.ts +32 -0
  105. package/dist/types/core/mdx-syntax.d.ts +100 -0
  106. package/dist/types/core/meta-diagnostics.d.ts +7 -0
  107. package/dist/types/core/meta.d.ts +32 -5
  108. package/dist/types/core/nav-diagnostics.d.ts +9 -2
  109. package/dist/types/core/navigation.d.ts +13 -0
  110. package/dist/types/core/ordering-prefix.d.ts +6 -0
  111. package/dist/types/core/pinned-ids.d.ts +23 -0
  112. package/dist/types/core/project-graph.d.ts +9 -1
  113. package/dist/types/core/redirect-patterns.d.ts +18 -0
  114. package/dist/types/core/relative-files.d.ts +31 -0
  115. package/dist/types/core/schema.d.ts +267 -7
  116. package/dist/types/core/sources/normalize.d.ts +101 -9
  117. package/dist/types/core/sources/types.d.ts +16 -0
  118. package/dist/types/core/static-files.d.ts +10 -0
  119. package/dist/types/core/static-host.d.ts +42 -0
  120. package/dist/types/core/svg-images.d.ts +26 -0
  121. package/dist/types/core/syntax-diagnostics.d.ts +14 -0
  122. package/dist/types/core/types.d.ts +42 -1
  123. package/dist/types/deploy/generated-files.d.ts +41 -0
  124. package/dist/types/deploy/redirects.d.ts +21 -7
  125. package/dist/types/markdown/code-title.d.ts +9 -5
  126. package/dist/types/markdown/directives.d.ts +8 -0
  127. package/dist/types/markdown/fence-language.d.ts +51 -0
  128. package/dist/types/markdown/fence-meta.d.ts +5 -0
  129. package/dist/types/markdown/github-alerts.d.ts +35 -0
  130. package/dist/types/openapi/asyncapi.d.ts +6 -5
  131. package/dist/types/openapi/checks.d.ts +10 -5
  132. package/dist/types/openapi/model.d.ts +55 -8
  133. package/dist/types/openapi/parse.d.ts +4 -1
  134. package/dist/types/sources/filesystem.d.ts +14 -4
  135. package/dist/types/sources/mdx-remote.d.ts +6 -3
  136. package/dist/types/sources/registry.d.ts +3 -3
  137. package/dist/types/theme/color.d.ts +33 -0
  138. package/dist/types/theme/palette.d.ts +122 -0
  139. package/docs/01-quickstart.mdx +1 -1
  140. package/docs/02-deployment.mdx +23 -8
  141. package/docs/04-migrating.mdx +16 -4
  142. package/docs/advanced/changelog.mdx +27 -1
  143. package/docs/advanced/skills.mdx +1 -1
  144. package/docs/cli/audit.mdx +1 -1
  145. package/docs/cli/doctor.mdx +2 -2
  146. package/docs/cli/evals.mdx +1 -1
  147. package/docs/cli/index.mdx +4 -2
  148. package/docs/cli/validate.mdx +10 -7
  149. package/docs/configuration/analytics.mdx +2 -0
  150. package/docs/configuration/customization.mdx +3 -1
  151. package/docs/configuration/index.mdx +2 -2
  152. package/docs/configuration/theming.mdx +5 -1
  153. package/docs/content/components.mdx +26 -6
  154. package/docs/content/frontmatter.mdx +1 -1
  155. package/docs/content/includes.mdx +6 -2
  156. package/docs/content/index.mdx +11 -1
  157. package/docs/content/islands.mdx +2 -0
  158. package/docs/content/meta.mdx +9 -3
  159. package/docs/content/navigation.mdx +14 -6
  160. package/docs/content/sources/obsidian.mdx +1 -1
  161. package/docs/content/sources/remote-mdx.mdx +3 -1
  162. package/docs/content/syntax.mdx +76 -8
  163. package/docs/content/variables.mdx +3 -1
  164. package/docs/discoverability/agent-discovery.mdx +1 -1
  165. package/docs/discoverability/json-api.mdx +1 -1
  166. package/docs/discoverability/llms-txt.mdx +1 -1
  167. package/docs/discoverability/markdown.mdx +2 -2
  168. package/docs/discoverability/open-graph.mdx +1 -1
  169. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  170. package/docs/references/graphql.mdx +2 -0
  171. package/docs/references/openapi.mdx +45 -7
  172. package/package.json +4 -4
  173. package/skills/blume/SKILL.md +2 -2
  174. package/skills/blume-migrate/SKILL.md +49 -27
  175. package/skills/blume-migrate/references/docsify.md +262 -0
  176. package/skills/blume-migrate/references/docus.md +298 -0
  177. package/skills/blume-migrate/references/docusaurus.md +3 -3
  178. package/skills/blume-migrate/references/fern.md +327 -0
  179. package/skills/blume-migrate/references/fumadocs.md +1 -1
  180. package/skills/blume-migrate/references/gitbook.md +267 -0
  181. package/skills/blume-migrate/references/github-wiki.md +259 -0
  182. package/skills/blume-migrate/references/jekyll.md +253 -0
  183. package/skills/blume-migrate/references/mdbook.md +256 -0
  184. package/skills/blume-migrate/references/mintlify.md +5 -5
  185. package/skills/blume-migrate/references/mkdocs.md +236 -0
  186. package/skills/blume-migrate/references/monorepo.md +20 -8
  187. package/skills/blume-migrate/references/nextra.md +2 -2
  188. package/skills/blume-migrate/references/readme.md +257 -0
  189. package/skills/blume-migrate/references/redocly.md +198 -0
  190. package/skills/blume-migrate/references/starlight.md +3 -3
  191. package/skills/blume-migrate/references/vitepress.md +204 -0
  192. package/skills/blume-migrate/references/vuepress.md +271 -0
  193. package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
  194. package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
  195. package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
  196. package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
  197. package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
  198. package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
  199. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
  200. package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
  201. package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
  202. package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
  203. package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
  204. package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
  205. package/src/ai/changelog-components.ts +24 -0
  206. package/src/ai/changelog-markdown.ts +45 -41
  207. package/src/ai/component-markdown.ts +22 -9
  208. package/src/ai/llms.ts +36 -5
  209. package/src/ai/markdown.ts +2 -2
  210. package/src/ai/openapi-components.ts +25 -9
  211. package/src/ai/operation-details.ts +208 -0
  212. package/src/ai/relative-links.ts +48 -8
  213. package/src/ai/serializers.ts +7 -6
  214. package/src/ai/site-skill.ts +6 -2
  215. package/src/analytics/head.ts +11 -5
  216. package/src/analytics/script.ts +13 -3
  217. package/src/astro/generate.ts +32 -6
  218. package/src/astro/index.ts +1 -0
  219. package/src/astro/integration.ts +27 -1
  220. package/src/astro/mdx-source-errors.ts +46 -0
  221. package/src/astro/templates.ts +89 -185
  222. package/src/audit/checks/redirects.ts +5 -1
  223. package/src/audit/redirects.ts +36 -19
  224. package/src/audit/report.ts +22 -9
  225. package/src/audit/run.ts +12 -28
  226. package/src/audit/terms.ts +23 -0
  227. package/src/audit/url.ts +2 -2
  228. package/src/cli/build-failure.ts +256 -7
  229. package/src/cli/command-meta.ts +1 -1
  230. package/src/cli/commands/audit.ts +1 -1
  231. package/src/cli/commands/build.ts +12 -3
  232. package/src/cli/commands/check.ts +13 -2
  233. package/src/cli/commands/dev.ts +1 -1
  234. package/src/cli/commands/doctor.ts +4 -2
  235. package/src/cli/commands/init.ts +3 -3
  236. package/src/cli/commands/preview.ts +17 -1
  237. package/src/cli/commands/validate.ts +22 -10
  238. package/src/cli/prepare.ts +1 -1
  239. package/src/cli/preview-pages.ts +161 -0
  240. package/src/cli/unknown-flags.ts +1 -42
  241. package/src/components/code-copy.ts +51 -0
  242. package/src/components/code-language.ts +75 -0
  243. package/src/components/content/AccordionItem.astro +16 -1
  244. package/src/components/content/Callout.astro +11 -1
  245. package/src/components/content/Changelog.astro +189 -0
  246. package/src/components/content/CodeBlock.astro +8 -11
  247. package/src/components/content/Tabs.astro +16 -2
  248. package/src/components/content/YouTube.astro +6 -4
  249. package/src/components/content/code-block.ts +33 -0
  250. package/src/components/content/content-strings.ts +5 -1
  251. package/src/components/content/inline-markdown.ts +35 -8
  252. package/src/components/content/youtube.ts +49 -7
  253. package/src/components/layout/Analytics.astro +5 -1
  254. package/src/components/layout/NavTree.astro +8 -3
  255. package/src/components/layout/RootLayout.astro +18 -31
  256. package/src/components/layout/nav-utils.ts +14 -0
  257. package/src/components/layout/search/pagefind.ts +2 -1
  258. package/src/components/openapi/Operation.astro +4 -0
  259. package/src/components/openapi/PanelTabs.astro +9 -3
  260. package/src/components/openapi/RequestBody.astro +42 -8
  261. package/src/components/openapi/RequestPanel.astro +34 -53
  262. package/src/components/openapi/example-panels.ts +110 -0
  263. package/src/components/openapi/helpers.ts +195 -20
  264. package/src/components/openapi/operation-model.ts +63 -26
  265. package/src/components/openapi/request.ts +13 -0
  266. package/src/components/openapi/snippets.ts +14 -2
  267. package/src/core/alert-diagnostics.ts +89 -0
  268. package/src/core/builtin-tags.ts +76 -0
  269. package/src/core/changelog-list.ts +87 -0
  270. package/src/core/closest-match.ts +41 -0
  271. package/src/core/code-fence-diagnostics.ts +267 -0
  272. package/src/core/code-fences.ts +43 -7
  273. package/src/core/component-diagnostics.ts +65 -12
  274. package/src/core/config-input.ts +1 -1
  275. package/src/core/config.ts +46 -1
  276. package/src/core/content-assets.ts +64 -144
  277. package/src/core/diagnostics.ts +36 -3
  278. package/src/core/directive-diagnostics.ts +265 -47
  279. package/src/core/former-routes.ts +46 -0
  280. package/src/core/git-ignored.ts +53 -0
  281. package/src/core/github.ts +5 -4
  282. package/src/core/heading-markers.ts +156 -2
  283. package/src/core/hidden-pages.ts +64 -0
  284. package/src/core/includes.ts +181 -41
  285. package/src/core/last-modified.ts +47 -10
  286. package/src/core/links.ts +438 -83
  287. package/src/core/load-module.ts +98 -9
  288. package/src/core/manifest.ts +12 -9
  289. package/src/core/markdown-mirrors.ts +52 -0
  290. package/src/core/mdx-elements.ts +153 -0
  291. package/src/core/mdx-syntax.ts +377 -0
  292. package/src/core/meta-diagnostics.ts +134 -0
  293. package/src/core/meta.ts +154 -55
  294. package/src/core/nav-diagnostics.ts +16 -4
  295. package/src/core/navigation.ts +70 -10
  296. package/src/core/ordering-prefix.ts +22 -2
  297. package/src/core/pinned-ids.ts +141 -0
  298. package/src/core/project-graph.ts +221 -29
  299. package/src/core/redirect-patterns.ts +41 -0
  300. package/src/core/relative-files.ts +145 -0
  301. package/src/core/schema.ts +17 -3
  302. package/src/core/sources/mdx-remote.ts +75 -14
  303. package/src/core/sources/normalize.ts +885 -179
  304. package/src/core/sources/notion.ts +5 -4
  305. package/src/core/sources/obsidian.ts +2 -5
  306. package/src/core/sources/types.ts +17 -0
  307. package/src/core/static-files.ts +40 -0
  308. package/src/core/static-host.ts +125 -0
  309. package/src/core/svg-images.ts +98 -0
  310. package/src/core/syntax-diagnostics.ts +491 -0
  311. package/src/core/types.ts +43 -1
  312. package/src/deploy/artifacts.ts +2 -28
  313. package/src/deploy/generated-files.ts +128 -0
  314. package/src/deploy/platforms/vercel.ts +25 -1
  315. package/src/deploy/redirects.ts +43 -16
  316. package/src/deploy/sitemap.ts +2 -1
  317. package/src/markdown/base-links.ts +87 -6
  318. package/src/markdown/code-title.ts +29 -13
  319. package/src/markdown/content-assets.ts +210 -0
  320. package/src/markdown/directives.ts +69 -5
  321. package/src/markdown/fence-language.ts +106 -0
  322. package/src/markdown/fence-meta.ts +42 -0
  323. package/src/markdown/github-alerts.ts +172 -0
  324. package/src/markdown/heading-anchors.ts +265 -56
  325. package/src/markdown/include.ts +30 -3
  326. package/src/markdown/index.ts +145 -40
  327. package/src/markdown/prompt-text.ts +95 -0
  328. package/src/markdown/relative-links.ts +37 -2
  329. package/src/markdown/route-snapshot.ts +22 -12
  330. package/src/markdown/svg-fallback.ts +51 -0
  331. package/src/markdown/title-heading.ts +134 -0
  332. package/src/migrate/migrate.ts +127 -2
  333. package/src/og/card.ts +14 -4
  334. package/src/openapi/asyncapi.ts +14 -6
  335. package/src/openapi/checks.ts +160 -15
  336. package/src/openapi/model.ts +172 -33
  337. package/src/openapi/parse.ts +13 -4
  338. package/src/openapi/render-mdx.ts +55 -9
  339. package/src/openapi/source.ts +87 -6
  340. package/src/registry/eject.ts +0 -1
  341. package/src/search/documents.ts +10 -2
  342. package/src/sources/filesystem.ts +28 -3
  343. package/src/sources/mdx-remote.ts +7 -15
  344. package/src/theme/color.ts +14 -0
  345. package/src/theme/entry.ts +22 -1
  346. package/src/theme/palette.ts +74 -1
  347. package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
  348. package/dist/cli/chunk-0w6hs479.js.map +0 -10
  349. package/dist/cli/chunk-150jk7km.js +0 -52
  350. package/dist/cli/chunk-150jk7km.js.map +0 -10
  351. package/dist/cli/chunk-36bxs9c7.js.map +0 -10
  352. package/dist/cli/chunk-53jqw8z0.js +0 -89
  353. package/dist/cli/chunk-53jqw8z0.js.map +0 -10
  354. package/dist/cli/chunk-60hz265r.js.map +0 -10
  355. package/dist/cli/chunk-desvm72z.js.map +0 -14
  356. package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
  357. package/dist/cli/chunk-es8h0v3j.js +0 -441
  358. package/dist/cli/chunk-es8h0v3j.js.map +0 -11
  359. package/dist/cli/chunk-kdpzw38a.js.map +0 -11
  360. package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
  361. package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
  362. package/dist/cli/chunk-twy7psdz.js.map +0 -10
  363. package/dist/cli/chunk-ydp3065p.js +0 -64
  364. package/dist/cli/chunk-ydp3065p.js.map +0 -10
  365. /package/dist/cli/{chunk-74wfhv79.js.map → chunk-05f21r8h.js.map} +0 -0
  366. /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-1cm0h819.js.map} +0 -0
  367. /package/dist/cli/{chunk-ms9z66nz.js.map → chunk-4rftmttv.js.map} +0 -0
  368. /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-7tekknev.js.map} +0 -0
  369. /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-ahc5gc9h.js.map} +0 -0
  370. /package/dist/cli/{chunk-tfycddbt.js.map → chunk-bjk964fp.js.map} +0 -0
  371. /package/dist/cli/{chunk-dqhg54mr.js.map → chunk-dmx6cezx.js.map} +0 -0
  372. /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-m9bp2hc1.js.map} +0 -0
  373. /package/dist/cli/{chunk-58bt3hag.js.map → chunk-mrvh4c88.js.map} +0 -0
  374. /package/dist/cli/{chunk-1f882900.js.map → chunk-th0pgaj5.js.map} +0 -0
  375. /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-vnbvhgmd.js.map} +0 -0
  376. /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-w6e455pz.js.map} +0 -0
  377. /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-xgq4eytj.js.map} +0 -0
@@ -14,7 +14,7 @@ sidebar:
14
14
  | ---------------- | -------------- |
15
15
  | Build command | `blume build` |
16
16
  | Output directory | `dist` |
17
- | Node version | 22.12 or newer |
17
+ | Node version | 22.19 or newer |
18
18
 
19
19
  These settings work on Vercel, Netlify, Cloudflare Pages, GitHub Pages, or any bucket or CDN. Make sure `blume` is a dependency so the host can run the build.
20
20
 
@@ -68,9 +68,16 @@ blume preview
68
68
 
69
69
  `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`.
70
70
 
71
+ On a static build, `blume preview` also answers what the host would, beyond the files in `dist/`. It applies the [redirects](#redirects), patterns included, with their configured status, as the host files the build writes do. It also handles trailing slashes the way static hosts do:
72
+
73
+ - A page's URL with a trailing slash (`/guide/`) redirects to the page (`/guide`), keeping the query string.
74
+ - A folder of HTML you ship in `public/` (`public/demo/index.html`) is served at `/demo/`, and `/demo` redirects there, so the relative links inside it resolve.
75
+
76
+ `blume dev` handles trailing slashes the same way.
77
+
71
78
  ## Subpath deploys
72
79
 
73
- 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.
80
+ 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, along with a root-relative `href` or `src` in raw HTML (`<a href="/guide">`, `<img src="/logo.png">`).
74
81
 
75
82
  ```ts blume.config.ts lineNumbers
76
83
  deployment: {
@@ -153,6 +160,8 @@ deployment: netlify({ output: "static" }),
153
160
 
154
161
  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. Behind a reverse proxy, set `allowedDomains` so the [rate limit](/docs/configuration/rate-limiting#behind-a-reverse-proxy) counts each reader apart. 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.
155
162
 
163
+ A `vercel()` server build checks the function bundle the adapter traced for packages it imports but didn't copy, which would crash the function on Vercel. A missing import of your own is a warning. A missing Blume dependency stops the build, and the build removes `.vercel/output/config.json`, so `vercel deploy --prebuilt` can't ship the output without the routes Blume adds to it.
164
+
156
165
  A `cloudflare()` server build runs on Cloudflare Workers only, since `@astrojs/cloudflare` dropped Cloudflare Pages in version 13; a static build doesn't load the package, so its `dist/` still deploys to Pages. The 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`.
157
166
 
158
167
  On Vercel and Cloudflare, a server build also turns on [`Accept: text/markdown` content negotiation](/docs/discoverability/markdown#content-negotiation), so an agent requesting any content page with that header receives its raw-Markdown mirror at the same URL. On Vercel, Blume splices header-conditional rewrites into the deploy's routing config; on Cloudflare, it generates a small Worker in front of the Astro one and points `assets.run_worker_first` at every path except the fingerprinted build assets and the raw `.md`, `.txt`, `.json`, and `.well-known` files, since the platform would otherwise serve the prerendered pages — and its `404.html` for a URL no page backs — before any server code runs; those exempted files keep their zero-Worker fast path. That Worker also answers the prerendered per-page JSON documents (`/api/docs/pages/{route}.json`) from the asset binding when a request for one reaches it, because Astro would otherwise route it to the `/api/` catch-all.
@@ -190,9 +199,13 @@ Map old URLs to new ones in `blume.config.ts`:
190
199
  redirects: [{ from: "/old", to: "/new", status: 301 }],
191
200
  ```
192
201
 
193
- `status` accepts `301`, `302`, `307`, or `308` (default `301`). Server builds answer redirects at request time with the configured status, and so does `blume dev`. 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.
202
+ `status` accepts `301`, `302`, `307`, or `308` (default `301`). Server builds answer redirects at request time with the configured status, and so do `blume dev` and `blume preview`. 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.
194
203
 
195
- A redirect that moves one page to another moves the page's [raw Markdown](/docs/discoverability/markdown#raw-markdown) with it: `/old.md` and `/old.mdx` redirect to `/new.md` and `/new.mdx` with the same status, in `blume dev` and every build.
204
+ A redirect that moves one page to another moves the page's [raw Markdown](/docs/discoverability/markdown#raw-markdown) with it: `/old.md` and `/old.mdx` redirect to `/new.md` and `/new.mdx` with the same status, in `blume dev` and every build. A redirect from an `.html` URL, like `/old.html` from a site you migrated, doesn't, since that URL never had a Markdown copy. Nor does one whose Markdown URL already serves a copy: `/index.md` is the home page's, so a redirect from `/index` to `/` leaves it alone.
205
+
206
+ A redirect from a page's own `index.html` URL, like `/guide/index.html` to `/guide`, gets no redirect page in a static build, because the page itself is written to that file. Hosts that read the redirect files answer it with the redirect. Any other host serves the page at that URL.
207
+
208
+ Blume adds one kind of redirect itself: a date-named page that an earlier version published under a shorter URL, like `changelog/12-05-2022.mdx` at `/changelog/05-2022`, redirects from that URL to its own (see [ordering with numeric prefixes](/docs/content#ordering-with-numeric-prefixes)). It adds none where another page lives at the old URL or a redirect you configured already starts there.
196
209
 
197
210
  Two hosts need more than the file:
198
211
 
@@ -217,14 +230,16 @@ redirects: [
217
230
  ```
218
231
 
219
232
  - `:name` matches one whole segment: `/blog/:slug` covers `/blog/hello`, not `/blog/a/b`.
220
- - `:name*` as the last segment matches the rest of the path, any number of segments: `/beta/:slug*` sends `/beta/guides/setup` to `/v2/guides/setup`, and `/beta` itself to `/v2`. A last segment of `*` does the same.
233
+ - `:name*` as the last segment matches the rest of the path, any number of segments: `/beta/:slug*` sends `/beta/guides/setup` to `/v2/guides/setup`, and `/beta` itself to `/v2`. A last segment of `*` does the same. Every host gets that bare path as a rule of its own (`/beta /v2` in `_redirects`), so all of them send it to the same place. `blume audit` checks where it lands, and reports `/beta` going to a `/v2` the build doesn't serve as a broken redirect.
221
234
  - `*` at the end of a segment matches the rest of the path from there: `/articles/concepts-*` covers `/articles/concepts-overview`.
222
235
 
223
236
  `to` reads a capture by name, as a whole segment (`/:slug*`, `/:slug`), or a `*` for what the `*` in `from` matched (`:splat`, the way `_redirects` spells it, reads it too). A `to` that reads no capture sends every path the pattern covers to one page. An exact redirect wins over a pattern that covers the same path, and patterns are tried in the order you list them.
224
237
 
225
- Each host gets the patterns in its own syntax: splats in `_redirects`, `path-to-regexp` sources in `vercel.json`, and the routing config of a `vercel()` or `netlify()` server build. The `node()` and `cloudflare()` servers match them themselves, and so does `blume dev`. A static build writes no redirect page for a pattern, since it would need one for every path the pattern covers, so a host that reads none of these files (GitHub Pages, say) applies only the exact redirects; `blume-redirects.json` lists patterns as you wrote them.
238
+ Each host gets the patterns in its own syntax: splats in `_redirects`, `path-to-regexp` sources in `vercel.json`, and the routing config of a `vercel()` or `netlify()` server build. The `node()` and `cloudflare()` servers match them themselves, and so do `blume dev` and `blume preview`. A static build writes no redirect page for a pattern, since it would need one for every path the pattern covers, so a host that reads none of these files (GitHub Pages, say) applies only the exact redirects; `blume-redirects.json` lists patterns as you wrote them.
239
+
240
+ [`blume validate`](/docs/cli/validate) checks where each redirect leads. An exact `to` must be a page, a file in `public/` or one Blume generates, or another redirect's `from`; for a pattern, something must be served under the literal part of `to` before its first capture (`/v2/` in `/v2/:slug*`). One that leads nowhere warns `BLUME_BROKEN_REDIRECT` at its `to`. [`blume audit`](/docs/cli/audit) follows every redirect through the built site.
226
241
 
227
- A pattern can't also match a page: hosts disagree on which of the two answers, so the build stops with `BLUME_REDIRECT_MATCHES_PAGE`, naming the page. Narrow the pattern to the paths that moved. An exact redirect from a page's own URL answers the same way on every host, taking the URL over so the page never publishes, so the same code is a warning there: delete the page if it moved, or drop the redirect.
242
+ A pattern can't also match a page: hosts disagree on which of the two answers, so the build stops with `BLUME_REDIRECT_MATCHES_PAGE`, naming the page. Narrow the pattern to the paths that moved. An exact redirect from a page's own URL answers the same way on every host, taking the URL over so the page never publishes, so the same code is a warning there: delete the page if it moved, or drop the redirect. So is one from a page's Markdown copy, like `/guide.md` or the home page's `/index.md`, which takes that copy away from agents: drop the redirect.
228
243
 
229
244
  ## Content types
230
245
 
@@ -254,7 +269,7 @@ When a feature needs a secret, Blume warns at `blume dev`/`build` if it's missin
254
269
  | [Rate limiting](/docs/configuration/rate-limiting) (`upstash()`) | `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` |
255
270
  | Mixedbread search | `MIXEDBREAD_API_KEY` |
256
271
  | [Narration](/docs/configuration/narration) (generated voices) | `AI_GATEWAY_API_KEY` (or Vercel OIDC) for `gateway()`, `OPENAI_API_KEY` for `openai()` (none with a `baseUrl`, unless you name one), or the `apiKeyEnv` you passed to either |
257
- | [Content sources](/docs/content/sources) | `NOTION_TOKEN` (`notion()`), `SANITY_TOKEN` (`sanity()`), `CONTENTFUL_ACCESS_TOKEN` (`contentful()`, plus `CONTENTFUL_PREVIEW_TOKEN` for `--preview`, which fails without it), `PAYLOAD_API_KEY` (`payload()`), `STRAPI_API_TOKEN` (`strapi()`), `GITHUB_TOKEN` (`githubReleases()`, and `mdxRemote()` reading from GitHub) |
272
+ | [Content sources](/docs/content/sources) | `NOTION_TOKEN` (`notion()`), `SANITY_TOKEN` (`sanity()`), `CONTENTFUL_ACCESS_TOKEN` (`contentful()`, plus `CONTENTFUL_PREVIEW_TOKEN` for `--preview`, which fails without it), `PAYLOAD_API_KEY` (`payload()`), `STRAPI_API_TOKEN` (`strapi()`), `GITHUB_TOKEN` (`githubReleases()`, and `mdxRemote()` with `github`) |
258
273
 
259
274
  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`, and narration reads its key during `blume build`, so set those where your site builds. Build-time secrets for search-index sync (`ALGOLIA_ADMIN_API_KEY`, `ORAMA_PRIVATE_API_KEY`, `TYPESENSE_ADMIN_API_KEY`) are checked by the sync step itself: when one is unset, the build warns and skips the sync, and when it's set, a failed sync [fails the build](/docs/configuration/search#adapters). The [GitHub info](/docs/content/components#github-info) card also sends `GITHUB_TOKEN`, when it's set, to lift GitHub's API rate limit.
260
275
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Migrate to Blume
3
- description: Move a Mintlify, Fumadocs, Docusaurus, Starlight, or Nextra site to Blume with one command that hands the migration to Codex or Claude Code.
3
+ description: Move a Mintlify, Docusaurus, GitBook, MkDocs, ReadMe, or other docs site to Blume with one command that hands the migration to Codex or Claude Code.
4
4
  sidebar:
5
5
  label: Migrate to Blume
6
6
  order: 2.4
@@ -29,8 +29,20 @@ Name the framework you're migrating from, or leave it out and Blume detects it f
29
29
  | `docusaurus` | `docusaurus.config.*` | [Migrate from Docusaurus](/guides/migrate-from-docusaurus) |
30
30
  | `starlight` | `@astrojs/starlight` in `package.json` | [Migrate from Starlight](/guides/migrate-from-starlight) |
31
31
  | `nextra` | `nextra` in `package.json` | [Migrate from Nextra](/guides/migrate-from-nextra) |
32
-
33
- Each source has its own mapping reference in the playbook. A site built with anything else still migrates: run the command without a source, and the agent inventories the repo first, then works from the playbook's general rules. The [MkDocs Material guide](/guides/migrate-from-mkdocs-material) walks through a migration that way.
32
+ | `vitepress` | `.vitepress/config.ts` (or `.mts`, `.js`, `.mjs`) at the root or in `docs/`, or `vitepress` in `package.json` | [Migrate from VitePress](/guides/migrate-from-vitepress) |
33
+ | `vuepress` | `.vuepress/config.ts` (or `.js`, `.mjs`, `.yml`, `.toml`) at the root or in `docs/`, `vuepress.config.*`, or `vuepress` (or `vuepress-vite`, `vuepress-webpack`) in `package.json` | [Migrate from VuePress](/guides/migrate-from-vuepress) |
34
+ | `docus` | `docus` (Docus 3 and later) or `@nuxt-themes/docus` (Docus 1) in `package.json`; run it from the docs package in a workspace | [Migrate from Docus](/guides/migrate-from-docus) |
35
+ | `mkdocs` | `mkdocs.yml`, `mkdocs.yaml`, `zensical.toml`, `properdocs.yml`, or `properdocs.yaml` | [Migrate from MkDocs Material](/guides/migrate-from-mkdocs-material) |
36
+ | `mdbook` | `book.toml` at the root or in `docs/` or `book/` | [Migrate from mdBook](/guides/migrate-from-mdbook) |
37
+ | `fern` | `fern/docs.yml` | [Migrate from Fern](/guides/migrate-from-fern) |
38
+ | `gitbook` | `.gitbook.yaml`, `.gitbook.yml`, or `gitbook-docs.yaml`, or `honkit` or `gitbook-cli` in `package.json`; never `SUMMARY.md` alone | [Migrate from GitBook](/guides/migrate-from-gitbook) |
39
+ | `redocly` | `sidebars.yaml`, or `@redocly/realm` or another Realm-family package in `package.json`; never `redocly.yaml` alone | [Migrate from Redocly](/guides/migrate-from-redocly) |
40
+ | `readme` | `_order.yaml` in `docs/`, `reference/`, `recipes/`, or `custom_pages/`; a repository that uploads with `rdme` needs the source named | [Migrate from ReadMe](/guides/migrate-from-readme) |
41
+ | `docsify` | `docsify-cli` or `docsify` in `package.json`; a site without a `package.json` needs the source named | [Migrate from Docsify](/guides/migrate-from-docsify) |
42
+ | `jekyll` | `_config.yml` at the root or in `docs/`, checked after every other source Blume detects | [Migrate from Just the Docs](/guides/migrate-from-jekyll) |
43
+ | `github-wiki` | Never detected: a wiki has no config file, so name the source | [Migrate from a GitHub wiki](/guides/migrate-from-github-wiki) |
44
+
45
+ Detection checks the folder you run the command in. Each source has its own mapping reference in the playbook, and several also have a codemod the agent runs first for the mechanical rewrites, so its own effort goes to the judgment calls. A site built with anything else still migrates: run the command without a source, and the agent inventories the repo first, then works from the playbook's general rules.
34
46
 
35
47
  ## What the agent does
36
48
 
@@ -39,7 +51,7 @@ The agent follows the playbook's workflow from the source's config to a passing
39
51
  1. Writes `blume.config.ts`, mapping only what your source declares and leaving Blume's defaults to cover the rest.
40
52
  2. Restructures content into [filesystem navigation](/docs/content/navigation), converting per-folder ordering files (Fumadocs `meta.json`, Nextra `_meta`) to `meta.ts`.
41
53
  3. Rewrites pages: frontmatter to Blume's schema, callout components to [directives](/docs/content/syntax), icons to Lucide, and snippets inlined.
42
- 4. Adds a [redirect](/docs/deployment#redirects) for every URL that moves, so no link breaks.
54
+ 4. Adds a [redirect](/docs/deployment#redirects) for every URL that moves, so no link breaks. Docsify's `#/` URLs never reach the server, so for those it adds a small script that sends each one to its new page. A GitHub wiki's URLs stay on github.com, which can't redirect, so the agent maps each page to its new route and prepares link stubs you can push to the old wiki.
43
55
  5. Points your `package.json` scripts at `blume dev` and `blume build`, and swaps the old framework's dependencies for `blume`.
44
56
  6. Runs `blume build` and `blume validate` until both pass.
45
57
 
@@ -73,7 +73,33 @@ changelog: {
73
73
 
74
74
  Each takes a map of locale code to text too, like a [header tab label](/docs/content/i18n#per-locale-navigation). The index renders in the default locale, so it shows that locale's entry. Leave either unset and it keeps the built-in text, which a [UI string override](/docs/content/i18n#translated-ui) (`changelog.title` and `changelog.description` under `i18n.ui`) replaces.
75
75
 
76
- 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";`.
76
+ The page appears only when nothing already occupies the `/changelog` route.
77
+
78
+ ### Writing your own index
79
+
80
+ To put something above the list — an introduction, a link to the feed, a note about your release cadence — write the page yourself and place `<Changelog />` where the list belongs. Blume still owns the list, so you only write the parts it has no slot for:
81
+
82
+ ```mdx changelog/index.mdx
83
+ ---
84
+ title: Changelog
85
+ description: Every Acme SDK release, newest first.
86
+ mode: center
87
+ ---
88
+
89
+ Every release, newest first. Subscribe to the [RSS feed](/changelog/rss.xml) to follow along.
90
+
91
+ <Changelog />
92
+ ```
93
+
94
+ A content page at `/changelog` takes over the route, so the generated page steps aside and yours renders instead. Put it at `changelog/index.mdx`, beside your entries, so the sidebar's Changelog group links to it — a `changelog.mdx` next to the folder adds a second Changelog item to the sidebar. [`mode: center`](/docs/content/frontmatter#layout) gives it the generated page's layout — no sidebar, no table of contents, a wider centered column — and the generated page renders the same component, so the two can't drift apart. The list downlevels to Markdown on the agent surfaces (`/changelog.md`, llms-full.txt, MCP `get_page`, search) exactly as the generated page's does.
95
+
96
+ The list is always the default locale's entries, wherever the tag sits — the rule every surface that publishes it already follows, so a translated page lists the same releases rather than disagreeing about which exist. Its dates read in the page's own language.
97
+
98
+ ### Replacing the page entirely
99
+
100
+ For a design the component can't express, 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";`.
101
+
102
+ Building the list yourself means reproducing what `<Changelog />` does, including one thing that fails quietly: entries from a non-filesystem source (such as [GitHub Releases](#from-github-releases)) render through a parallel `staged` collection, so a page that reads `getCollection("docs")` alone lists none of them, with no warning. Prefer `<Changelog />` in a content page unless you genuinely need different markup.
77
103
 
78
104
  ## From GitHub Releases
79
105
 
@@ -15,7 +15,7 @@ npx skills add haydenbleasel/blume
15
15
 
16
16
  ## Migration
17
17
 
18
- `blume-migrate` moves an existing docs site to Blume: Mintlify, Fumadocs, Docusaurus, Starlight, Nextra, or any other framework. It targets idiomatic Blume rather than a line-by-line port, with a mapping reference for each named framework, a redirect for every URL that moves, and a report of anything it drops. The quickest way to run it is [`blume migrate`](/docs/migrating), which detects your framework and opens Codex or Claude Code on the copy bundled in the package. To use it from another agent, install it:
18
+ `blume-migrate` moves an existing docs site to Blume from Mintlify, Docusaurus, GitBook, MkDocs, ReadMe, the [other named sources](/docs/migrating#sources), or any other framework. It targets idiomatic Blume rather than a line-by-line port, with a mapping reference for each named framework, a redirect for every URL that moves, and a report of anything it drops. The quickest way to run it is [`blume migrate`](/docs/migrating), which detects your framework and opens Codex or Claude Code on the copy bundled in the package. To use it from another agent, install it:
19
19
 
20
20
  ```bash
21
21
  npx skills add haydenbleasel/blume --skill blume-migrate
@@ -28,7 +28,7 @@ Findings are grouped by check rather than listed per page, so the report reads a
28
28
  - `--url <origin>` — also probe a live deployment for status codes, response headers, and configured redirects.
29
29
  - `--external` — probe outbound links over the network.
30
30
  - `--ignore <glob>` — with `--external`, don't probe an outbound link whose URL matches the glob, like `--ignore "https://api.acme.example/**"`. Repeat the flag for more patterns. It matches the way [`blume validate --ignore`](/docs/cli/validate#flags) does, and internal links are always checked.
31
- - `--only <check|category>` / `--skip <check|category>` — narrow the report to, or away from, these checks or categories (comma-separated). A term that names no check or category is an error that suggests the closest one, so a typo can't quietly empty the report.
31
+ - `--only <check|category>` / `--skip <check|category>` — narrow the report to, or away from, these checks or categories (comma-separated). The audit count in the summary counts only the checks left in. A term that names no check or category is an error that suggests the closest one, so a typo can't quietly empty the report.
32
32
  - `--list-checks` — print every check the audit can report, then exit.
33
33
  - `--verbose` — list every affected page with each finding's full detail, instead of the first few.
34
34
  - `--json` — emit the report as JSON on stdout.
@@ -15,8 +15,8 @@ blume doctor
15
15
 
16
16
  - **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.
17
17
  - **`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".
18
- - **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.
19
- - **Navigation that leads nowhere**: a tab, selector item, featured link, header action, or call to action whose path matches no page, custom `.astro` page, generated route, or reference: a warning (`BLUME_NAV_MISSING_PAGE`), the same one `blume dev` and `blume build` print.
18
+ - **Every content page and folder meta**: the diagnostics `blume dev` and `blume build` print as they load the project — invalid frontmatter, `.mdx` that doesn't [parse](/docs/content#markdown-and-mdx), navigation problems, missing include targets, and the rest — collected in one report.
19
+ - **Navigation that leads nowhere**: a tab, selector item, featured link, header action, or call to action whose path matches no page, custom `.astro` page, generated route, reference, `public/` file, or file Blume generates: a warning (`BLUME_NAV_MISSING_PAGE`), the same one `blume dev` and `blume build` print. A tab is checked where it links: its `href` when it has one, else its `path`.
20
20
  - **[`<Component>`](/docs/content/components#component) examples that don't exist**: a `path` that names no file under `examples`, which the page would show as a "No example found" box: a warning (`BLUME_EXAMPLE_NOT_FOUND`) at its line, as `blume dev` and `blume build` print it. A `components.ts` override or island named `Component` replaces the built-in, and isn't checked.
21
21
  - **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).
22
22
  - **Packages your config needs** that aren't installed — the SDK a search, content source, assistant, or narration 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.
@@ -49,7 +49,7 @@ questions:
49
49
  - id: install-node-version
50
50
  question: What is the minimum Node.js version required?
51
51
  expected:
52
- - Node 22.12 or newer
52
+ - Node 22.19 or newer
53
53
  routes: /docs/quickstart
54
54
  - id: deploy-vercel
55
55
  question: How do I deploy to Vercel?
@@ -26,7 +26,7 @@ blume <command> [options]
26
26
  | [`blume translate`](/docs/cli/translate) | Translate docs into the configured locales with a local agent CLI. |
27
27
  | [`blume version [id]`](/docs/cli/version) | Freeze the current docs as an archived version (no id lists configured versions). |
28
28
  | [`blume skill`](/docs/discoverability/agent-discovery#your-sites-skill) | Write your docs' agent skill with Codex or Claude Code, in place of the generated one. |
29
- | [`blume migrate [source]`](/docs/migrating) | Move a Mintlify, Fumadocs, Docusaurus, Starlight, or Nextra site to Blume with Codex or Claude Code. |
29
+ | [`blume migrate [source]`](/docs/migrating) | Move a Mintlify, Docusaurus, GitBook, MkDocs, ReadMe, or other docs site to Blume with Codex or Claude Code. |
30
30
  | [`blume upgrade`](/docs/upgrading) | Move to a new major: bump `blume`, then list the config changes left or hand them to Codex or Claude Code. |
31
31
 
32
32
  ## Common flags
@@ -42,7 +42,7 @@ blume <command> [options]
42
42
  - `blume dev --content-dir <dir>` — scan a different content folder without editing `blume.config.ts`.
43
43
  - `blume dev --debug` — verbose Astro/Vite logging for troubleshooting.
44
44
  - `blume dev --preview` / `blume build --preview` — include drafts and unpublished CMS content.
45
- - `blume build --no-strict` — build despite diagnostic errors. By default `blume build` fails (exit 1) on any error diagnostic, because pages that fail frontmatter validation are dropped from the output; with `--no-strict` the build succeeds and reports how many pages are missing. `blume dev --strict` opts dev into the same fail-fast behavior.
45
+ - `blume build --no-strict` — build despite diagnostic errors. By default `blume build` fails (exit 1) on any error diagnostic, because pages that fail frontmatter validation, and `.mdx` pages that don't [parse](/docs/content#markdown-and-mdx), are dropped from the output; with `--no-strict` the build succeeds and reports how many pages are missing. A failure only the build itself finds, such as a page that throws while it renders, still fails it. `blume dev --strict` opts dev into the same fail-fast behavior.
46
46
  - `blume build --analyze` — print the client JavaScript bundle sizes (largest first) after the build.
47
47
  - `blume build --budget-js <kb> --budget-css <kb>` — fail the build when total client JavaScript/CSS exceeds the budget, turning a performance target into a CI gate.
48
48
  - `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).
@@ -107,3 +107,5 @@ Add a `tsconfig.json` extending Astro's config to your project root so authored
107
107
  ```
108
108
 
109
109
  Without a project `tsconfig.json`, only the generated runtime is checked.
110
+
111
+ The build reads this file too, to resolve imports, so an `extends` or `references` entry that doesn't resolve (a package that isn't installed, a file that was moved, a framework's generated tsconfig that doesn't exist yet) stops `blume build`, `blume dev`, and `blume check` with `BLUME_TSCONFIG_EXTENDS`, at the line that names it. Install, restore, or generate the file, or remove the entry.
@@ -9,14 +9,16 @@ description: blume validate checks every link in your content — pages, anchors
9
9
  blume validate
10
10
  ```
11
11
 
12
- - **Internal page links** (`/guides/intro`, `./sibling`) must resolve to a real page — broken ones are reported as errors. Every link form Markdown and MDX render is read: inline links, including one whose label wraps onto the next line; reference-style links, checked at their `[label]: /target` definition; autolinks (`<https://…>`, in `.md`); and a component's string `href` (`<Card href="./install">`). A lowercase HTML `<a href>` ships exactly as written, so it's checked the way a browser reads it: a relative path resolves against the page's URL, and no [`basePath`](/docs/deployment#mount-the-docs-under-a-path) is added. An `href={…}` expression isn't checked.
13
- - **Anchor links** (`#section`, `/guides/intro#setup`) must match an anchor on the target page — a heading's id (generated or [pinned](/docs/content/syntax#custom-anchors)) or a raw HTML element's `id` attribute — with misses reported as warnings. Ids inside code blocks, inline code, HTML comments, and `<Prompt>` blocks don't count.
14
- - **Asset links** are checked where the file lives: an absolute path (`/logo.png`) against the `public/` directory, a relative image embed (`![](./diagram.png)`) against the page's own folder. A plain link to a relative path still resolves as a site route — only image embeds go through the image pipeline.
12
+ - **Internal page links** (`/guides/intro`, `./sibling`) must resolve to a real page — broken ones are reported as errors. Every link form Markdown and MDX render is read: inline links, including one whose label wraps onto the next line; reference-style links, checked at their `[label]: /target` definition (the first one, when a label is defined twice, since that's the one that renders); autolinks (`<https://…>`, in `.md`); and a component's string `href` (`<Card href="./install">`). A root-relative link to a Markdown file in your content (`/guides/setup.md`) is checked at the page that file publishes, where the build points it; one naming no file there is checked as the page's [Markdown copy](/docs/discoverability/markdown#raw-markdown). A link's target is read the way the renderer reads it, so `[x](<https://example.com/a(b>)` points at `https://example.com/a(b` and `![](image\(1\).png)` at `image(1).png`, with any spaces around it and a title in any of its quotes (`"…"`, `'…'`, or `(…)`) after it. Links in code (fenced, including a fence inside a block quote, or inline) and in comments (`<!-- … -->` in `.md`, `{/* … */}` in `.mdx`) never render, so they aren't checked. A lowercase HTML `<a href>` to a page ships as written, so it's checked the way a browser reads it: a relative path resolves against the page's URL, and no [`basePath`](/docs/deployment#mount-the-docs-under-a-path) is added (a root-relative one gains a [deployment `base`](/docs/deployment#subpath-deploys), as every link does). An `href={…}` expression isn't checked.
13
+ - **Anchor links** (`#section`, `/guides/intro#setup`) must match an anchor on the target page — a heading's id (generated or [pinned](/docs/content/syntax#custom-anchors)), a raw HTML element's `id` attribute, or an `<a name>` — with misses reported as warnings. Ids inside code blocks, inline code, HTML comments, and `<Prompt>` blocks don't count.
14
+ - **Other link schemes** (`mailto:`, `tel:`, an app's `vscode:` deep link) aren't checked: whether a reader's browser or an installed app handles one can't be known. ReadMe's own link schemes are the exception: `doc:`, `ref:`, `page:`, `changelog:`, and `blog:` only resolve on ReadMe and ship as dead links anywhere else, so each is reported as a warning. Link the page by its path instead.
15
+ - **Asset links** are checked where the file lives: an absolute path (`/logo.png`) against the `public/` directory, a relative image embed (`![](./diagram.png)`, or a card's `<Card img="./cover.png">`) against the page's own folder. A relative link to any other file (`[spec](./spec.pdf)`, or an `<a href>` or a component's `href`) is checked against the page's own folder too, since Blume [publishes a file there](/docs/content/syntax#links-and-images) with the page; one that names no file beside the page resolves as a site route against `public/`. A media element's `src` (`<img>`, `<source>`, `<video>`, or `<audio>`, as raw HTML in `.md` or an element in `.mdx`) is checked the same way: a relative one is found beside the page, or else where the browser requests it, against `public/`. A project with no `public/` folder ships no public files, so an absolute asset path there is reported missing. An `.html` link (`./setup.html`, `/guides/setup.html`) is a file link too: Blume serves pages at their routes, never at `.html` URLs, so unless `public/` has that file, the link is reported, with the page's route as the fix when a page matches it.
16
+ - **Redirect targets** in [`redirects`](/docs/deployment#redirects) must lead somewhere: an exact internal `to` to a page, a file, or another redirect, and a [pattern](/docs/deployment#pattern-redirects) `to` to a path with something served under its literal start (`/v2/` in `/v2/:slug*`). One that leads nowhere is a warning, located at its `to` in `blume.config.ts`. A link to a redirect's `from` is valid either way. External targets aren't checked.
15
17
  - **External links** are only checked with [`--external`](#flags) (off by default since it requires the network). A dead link is an error: a 404 or 410, or a host name that doesn't resolve. Anything else that fails is a warning, since it's more often the other site or the network than the link: any other error status (a 401 or 403 from a site that turns bots away, a 429 rate limit, a 5xx outage), a refused or dropped connection, or no answer within 10 seconds. To leave out URLs that can't be checked from where the command runs, like a placeholder domain, a local server, or a site that turns bots away, pass [`--ignore`](#flags). A bare URL that GitHub-flavored Markdown links on its own, with no link syntax around it, isn't read; [`blume audit --external`](/docs/cli/audit) checks it in the built site.
16
18
 
17
19
  ## What counts as a page
18
20
 
19
- Links resolve against everything the built site serves, not just Markdown pages: custom [`.astro` pages](/docs/advanced/custom-pages), the generated [changelog](/docs/advanced/changelog) index, every configured [redirect](/docs/deployment#redirects), and, on a [multi-language site](/docs/content/i18n), an untranslated page's fallback URL under each locale. A page that fails to parse is reported alongside the link findings, because a page that never loads is a link-validation blind spot rather than a clean result.
21
+ Links resolve against everything the built site serves, not just Markdown pages: custom [`.astro` pages](/docs/advanced/custom-pages), [API reference](/docs/references/openapi) pages, the generated [changelog](/docs/advanced/changelog) index, every configured [redirect](/docs/deployment#redirects), and, on a [multi-language site](/docs/content/i18n), an untranslated page's fallback URL under each locale. The files Blume writes beside your pages count too, while the config has them on: `/llms.txt` and `/llms-full.txt`, the sitemap, `/robots.txt`, the RSS feeds, `/openapi.json`, `/agent-readability.json`, `/skill.md`, and the `.well-known` discovery files such as `/.well-known/agent-skills/index.json`. So does a folder in `public/` with an `index.html`, at `/demo` or `/demo/` for `public/demo/index.html`, as a static host serves it. A page that fails to load, with invalid front matter or `.mdx` that doesn't [parse](/docs/content#markdown-and-mdx) (`BLUME_MDX_SYNTAX`), is reported alongside the link findings, because a page that never loads is a link-validation blind spot rather than a clean result.
20
22
 
21
23
  ## Flags
22
24
 
@@ -29,10 +31,11 @@ Links resolve against everything the built site serves, not just Markdown pages:
29
31
 
30
32
  | Code | Severity | Meaning |
31
33
  | --- | --- | --- |
32
- | `BLUME_BROKEN_LINK` | error | An internal link points at a route no page serves. |
34
+ | `BLUME_BROKEN_LINK` | error | An internal link points at a route no page serves. When it's a relative link to a file that exists but whose name, or a folder's, starts with `_`, the fix says so: that file is a [partial](/docs/content/includes#partials), which isn't published. |
33
35
  | `BLUME_BROKEN_ANCHOR` | warning | The page exists but has no anchor matching the fragment. |
34
- | `BLUME_BROKEN_ASSET` | warning | An absolute path isn't in `public/`, or a relative image embed isn't beside the page. |
35
- | `BLUME_ASSETS_UNCHECKED` | info | There is no `public/` directory, so absolute asset paths weren't checked. |
36
+ | `BLUME_BROKEN_ASSET` | warning | An absolute path isn't in `public/`, a relative image embed isn't beside the page, an `.html` link names no file in `public/`, or a media `src` names a file the site doesn't serve at that URL. |
37
+ | `BLUME_BROKEN_REDIRECT` | warning | A redirect's internal `to` leads to no page, file, or other redirect. |
38
+ | `BLUME_UNSUPPORTED_LINK_SCHEME` | warning | A link uses one of ReadMe's link schemes (`doc:`, `ref:`, `page:`, `changelog:`, `blog:`), which browsers can't follow. |
36
39
  | `BLUME_DEAD_LINK` | error or warning | An external link (with `--external`) returned 404/410 or its host doesn't resolve (error), or it got any other error status, no connection, or no answer in time (warning). |
37
40
 
38
41
  The exit code is the contract: errors exit non-zero, and `--strict` makes warnings do the same.
@@ -393,6 +393,8 @@ analytics: [
393
393
 
394
394
  `attributes` is a map of extra HTML attributes (`data-*`, `id`, …) spread onto the tag, and `strategy` adds `async` or `defer` to external scripts.
395
395
 
396
+ A root-relative `src` points at a file in `public/` (`src: "/js/redirects.js"` for `public/js/redirects.js`), so under a [`deployment.base`](/docs/deployment#subpath-deploys) it gets the base, like a link does: with `base: "/docs"`, the tag loads `/docs/js/redirects.js`. A `src` that already starts with the base is left as written, and an absolute or protocol-relative URL passes through, so a script served outside the site's base takes its full URL.
397
+
396
398
  ## Options
397
399
 
398
400
  | Adapter | Option | Default | Description |
@@ -22,6 +22,8 @@ export default defineComponents({
22
22
 
23
23
  Keys are the names you write in MDX (`<Callout>`, `<Pricing>`). Use the `.tsx` filename when you import React components.
24
24
 
25
+ A capitalized tag that's neither a built-in, an [island](/docs/content/islands), nor an override fails the page when it renders, so `blume dev`, `blume build`, and `blume check` warn about it first as `BLUME_UNKNOWN_COMPONENT`, once per tag, naming every page that uses it.
26
+
25
27
  ### Reference form
26
28
 
27
29
  Every override — in `mdx` or `layout` — accepts three forms:
@@ -104,7 +106,7 @@ Wired slots (names are case-sensitive; any other key under `layout` renders nowh
104
106
 
105
107
  The `mobile` variant also receives the [page actions](/docs/content/navigation#page-actions) as its children (an empty element the actions move into from the right rail below 1,280px), so render a `<slot />` in it to keep them on narrower screens. It renders on a page with no headings as well, with `headings` empty, so the actions still show there.
106
108
 
107
- `PageHeader` and `PageFooter` have no built-in component — they render nothing until you set them, which makes them handy injection points for a promo banner or a "last updated" note. The built-in `Footer` holds the only links to your repository and social profiles, so a `Footer` override (the place for a marketing footer the config can't express) should keep them. With `consent` set, also keep a way to reopen it: any element with `data-blume-consent-open` works as the [Cookie settings](/docs/configuration/consent#changing-an-answer) link.
109
+ `PageHeader` and `PageFooter` have no built-in component — they render nothing until you set them, which makes them handy injection points for a promo banner or a "last updated" note. `PageFooter` renders at the foot of every page Blume builds from your content, API operation pages included, with `Feedback` and `Pagination` after it. A page in [`custom` or `frame` mode](/docs/content/frontmatter#layout) drops that page-end area along with its title and breadcrumbs. A [custom page](/docs/advanced/custom-pages) under `pages/` renders the layout it wraps itself in: `RootLayout` takes slot overrides through its `layout` prop, and `PageLayout` has no page-end area. The built-in `Footer` holds the only links to your repository and social profiles, so a `Footer` override (the place for a marketing footer the config can't express) should keep them. With `consent` set, also keep a way to reopen it: any element with `data-blume-consent-open` works as the [Cookie settings](/docs/configuration/consent#changing-an-answer) link.
108
110
 
109
111
  Layout slots accept the same [three reference forms](#reference-form) as MDX overrides, so a slot can be a path string or a hydrated descriptor (`{ component, client }`) when you want an interactive header or footer.
110
112
 
@@ -238,7 +238,7 @@ content: {
238
238
  | --- | --- | --- |
239
239
  | `root` | `"docs"` | Folder Blume scans for content. Shorthand for a single `filesystem()` source; not allowed beside `sources`. |
240
240
  | `include` | `["**/*.{md,mdx}"]` | Globs that match content files. Shorthand, like `root`. |
241
- | `exclude` | `["**/_*", "**/.*"]` | Globs to ignore (underscore- and dot-files). Shorthand, like `root`. |
241
+ | `exclude` | `["**/_*", "**/.*"]` | Globs to ignore. Yours add to the defaults, which skip underscore- and dot-files; a `!` entry drops a pattern, so `"!**/_*"` publishes `_`-prefixed files. Shorthand, like `root`. |
242
242
  | `sources` | one `filesystem()` | Content source adapters from `blume/sources`, replacing the shorthand. See [Content sources](/docs/content/sources). |
243
243
  | `pages` | `"pages"` | Folder for custom `.astro` pages. |
244
244
  | `defaultType` | `"doc"` | Page `type` used when frontmatter omits it. |
@@ -365,7 +365,7 @@ lastModified: "git",
365
365
  | `"git"` | Read the date from git history (commit dates). |
366
366
  | `"frontmatter"` | Never run git — use only the `lastModified` frontmatter field. |
367
367
 
368
- The git source reads the most recent commit that touched each file, so it works in any git repository — including monorepos — and needs the repo's history at build time. CI platforms usually check out a shallow clone, which silently drops most dates (the build warns with `BLUME_SHALLOW_GIT_HISTORY` when that happens): on Vercel, set the `VERCEL_DEEP_CLONE=true` environment variable; with `actions/checkout`, set `fetch-depth: 0`. A page's own `lastModified` frontmatter always wins, which is handy for pinning a date or for files that aren't committed yet:
368
+ The git source reads the most recent commit that touched each file, so it works in any git repository — including monorepos — and needs the repo's history at build time. It follows renames, as `git log --follow` does: renaming `page.md` to `page.mdx`, or moving a file to another folder under the same content root, keeps the page's date as long as the commit leaves its content unchanged. A rename that also edits the file dates the page at that commit, and so does moving a file in from outside the content root. CI platforms usually check out a shallow clone, which silently drops most dates (the build warns with `BLUME_SHALLOW_GIT_HISTORY` when that happens): on Vercel, set the `VERCEL_DEEP_CLONE=true` environment variable; with `actions/checkout`, set `fetch-depth: 0`. A page's own `lastModified` frontmatter always wins, which is handy for pinning a date or for files that aren't committed yet:
369
369
 
370
370
  ```mdx page.mdx
371
371
  ---
@@ -36,6 +36,8 @@ Named presets: `blue` (default), `green`, `orange`, `pink`, `purple`, `red`, and
36
36
 
37
37
  A string applies to both color modes; pass an object for [a different accent per mode](#dark-mode-colors).
38
38
 
39
+ A value that's neither a preset nor a CSS color, like `"deep purple"` or a hex value without its `#`, warns `BLUME_THEME_COLOR_INVALID` at the line in `blume.config.ts` that sets it, since browsers ignore the styles that use it. The same check covers `action`, `background`, and the [Open Graph card's](/docs/discoverability/open-graph) `seo.og.palette`. Custom properties (`var(--brand)`), `color-mix()`, and the other color functions pass as written.
40
+
39
41
  Text on an accent or action fill, like a button label or a step number, is white while white meets AA on that color, and dark otherwise. A light accent gets dark labels without any extra config. [`blume audit`](/docs/cli/audit#theme-contrast-low) checks a custom accent, action, or background against the same bar.
40
42
 
41
43
  ### Radius
@@ -231,6 +233,8 @@ Set a token under `:root` for light mode and under `:root[data-theme="dark"]` fo
231
233
  | `--blume-code-highlight`, `--blume-code-highlight-border` | Background and left rule of a highlighted code line (`// [!code highlight]` or a `{1,4-5}` range) |
232
234
  | `--blume-code-add`, `--blume-code-add-border` | Background and left rule of an added line (`// [!code ++]`) |
233
235
  | `--blume-code-remove`, `--blume-code-remove-border` | Background and left rule of a removed line (`// [!code --]`) |
236
+ | `--blume-code-error`, `--blume-code-error-border` | Background and left rule of an error line (`// [!code error]`) |
237
+ | `--blume-code-warning`, `--blume-code-warning-border` | Background and left rule of a warning line (`// [!code warning]`) |
234
238
  | `--blume-code-word`, `--blume-code-word-border` | Background and outline of a highlighted word (`// [!code word:…]`) |
235
239
  | `--blume-content-width` | Max width of the prose column — article, breadcrumb, table of contents, feedback, and pagination (default `42rem`) |
236
240
  | `--blume-radius` | Corner radius |
@@ -248,7 +252,7 @@ Set a `--blume-font-*` token to any font stack to use a font outside the curated
248
252
 
249
253
  ## Tailwind utilities
250
254
 
251
- Blume's theme is built with Tailwind v4 internally, and your project's `.astro`, `.tsx`, and `.jsx` files are scanned too — so you can style custom components and pages with utility classes, no Tailwind setup required. Every token is exposed as a utility, so your components track the theme automatically:
255
+ Blume's theme is built with Tailwind v4 internally, and your project's `.astro`, `.jsx`, `.mdx`, `.ts`, and `.tsx` files are scanned too — so you can style custom components and pages with utility classes, no Tailwind setup required. Every token is exposed as a utility, so your components track the theme automatically:
252
256
 
253
257
  | Token | Utilities |
254
258
  | --------------------------- | -------------------------- |
@@ -5,6 +5,8 @@ description: Cards, steps, tabs, accordions, badges, code groups, frames, trees,
5
5
 
6
6
  Blume ships an accessible, themeable component set available in any `.mdx` page with **no imports**. Each one is shown below with a live preview and its source. Components are vanilla and React-free; React switches on only when your project uses it — any `.tsx` or `.jsx` file in the project, a React [island](/docs/content/islands), [`<Component>`](#component) example, or [component override](/docs/configuration/customization) — or when the [assistant](/docs/configuration/assistant) is enabled.
7
7
 
8
+ A component ignores props it doesn't take. Most show their children, so other docs tools' spellings can render empty: VitePress's `<Badge type="tip" text="beta" />` is a Badge with no children, which shows nothing. Blume warns about a childless component given props it doesn't take as `BLUME_UNKNOWN_PROP`, with the props it does take. Write the content between the tags instead: `<Badge>beta</Badge>`.
9
+
8
10
  ## Card and CardGroup
9
11
 
10
12
  Cards link to a destination with an icon, title, and short blurb. Group them with `CardGroup` for a responsive grid. Reach for them on landing pages, section indexes, and “next steps” — anywhere you’re guiding the reader onward.
@@ -31,7 +33,7 @@ Cards link to a destination with an icon, title, and short blurb. Group them wit
31
33
 
32
34
  `Card` takes `title`, an optional `href` (omit it for a non-clickable card), and an `icon` from Blume's built-in icon set. `CardGroup` takes `cols` (default `2`).
33
35
 
34
- A card also takes `img`, an image across its top (or beside the text from the `sm` breakpoint up, with `horizontal`); `cta`, a call-to-action line in the accent color beneath the text; `arrow`, an arrow after the title and the CTA (shown by default only for external links); `type`, which tints the card and picks a matching icon the way a callout does (`note`, `info`, `tip`, `check`, `warning`, or `danger`); and `color`, any CSS color for the icon.
36
+ A card also takes `img`, an image across its top (or beside the text from the `sm` breakpoint up, with `horizontal`), given like a Markdown image's path: a file next to the page (`./cover.png`), a path under `public/`, or a URL; `cta`, a call-to-action line in the accent color beneath the text; `arrow`, an arrow after the title and the CTA (shown by default only for external links); `type`, which tints the card and picks a matching icon the way a callout does (`note`, `info`, `tip`, `check`, `warning`, or `danger`); and `color`, any CSS color for the icon.
35
37
 
36
38
  ## Steps
37
39
 
@@ -119,6 +121,22 @@ pip install acme-commerce
119
121
 
120
122
  The first view shows until the reader picks another. Blume remembers that pick across pages written in views, and writes it to the URL as `?view=Python`, so a link opens on the same view. A link to a heading inside a view opens that view. Use `<View>` when most of the page changes with the choice, and [Tabs](#tabs) when only one part of the page does. The agent-facing Markdown includes every view, each under its name.
121
123
 
124
+ ## Callout
125
+
126
+ `<Callout>` is the component behind [callout directives](/docs/content/syntax#callouts): `:::warning[Heads up]` renders `<Callout type="warning" title="Heads up">`. Write the component itself when a callout needs its own icon or color, which a directive can't set.
127
+
128
+ <Callout type="tip" title="Ship it" icon="rocket">
129
+ Run `blume build` before you deploy.
130
+ </Callout>
131
+
132
+ ```astro
133
+ <Callout type="tip" title="Ship it" icon="rocket">
134
+ Run `blume build` before you deploy.
135
+ </Callout>
136
+ ```
137
+
138
+ `type` sets the tint and the default icon: `note`, `info` (the default), `tip`, `success` (or `check`), `warning`, or `danger`. The aliases `caution`, `error`, `important`, and `warn` work only in the `:::` form. `title` adds a bold first line. `icon` replaces the type's icon and takes the same inputs as [`<Icon>`](#icon): a Lucide name, an image path or URL, or an inline SVG. `color`, any CSS color, replaces the type's tint: the border, the icon, and the body text take that color over the page background.
139
+
122
140
  ## Badge
123
141
 
124
142
  A small inline label for status or metadata — version tags, “new” or “beta” markers, stability levels. The `variant` tunes the color to the meaning.
@@ -225,7 +243,7 @@ Illustrate a project or folder layout. Wrap a normal Markdown list and Blume sty
225
243
 
226
244
  ## Accordion
227
245
 
228
- Stack related collapsibles in a single bordered container with dividers between them — FAQs, optional steps, or long examples. Each child is an `AccordionItem` (`title`, optional `icon`, `description`, `defaultOpen`). For a single standalone disclosure, use [Expandable](#expandable).
246
+ Stack related collapsibles in a single bordered container with dividers between them — FAQs, optional steps, or long examples. Each child is an `AccordionItem` (`title`, optional `icon`, `description`, `defaultOpen`). The `title` renders inline Markdown, so code spans, emphasis, and links in it show as they would in a sentence; raw HTML in it shows as text. For a single standalone disclosure, use [Expandable](#expandable).
229
247
 
230
248
  <Accordion>
231
249
  <AccordionItem title="Does it support MDX?">
@@ -249,7 +267,7 @@ Stack related collapsibles in a single bordered container with dividers between
249
267
 
250
268
  ## Expandable
251
269
 
252
- A lightweight inline disclosure for nested detail — expanding a field's sub-properties or an optional aside. `title` labels the toggle (defaults to “Show more”); set `defaultOpen` to start expanded.
270
+ A lightweight inline disclosure for nested detail — expanding a field's sub-properties or an optional aside. `title` labels the toggle (defaults to “Show more”) and renders inline Markdown, as an accordion item's does; set `defaultOpen` to start expanded.
253
271
 
254
272
  <Expandable title="Show advanced options">
255
273
  These settings are optional and rarely need changing.
@@ -295,7 +313,7 @@ Lay cards or blocks out in a responsive grid of equal columns that reflows on mo
295
313
 
296
314
  ## CodeGroup
297
315
 
298
- Group several code blocks into one tabbed switcher — a tab per language or file. The tab label is each block's title (the text after the language), and the group's copy button sits in the tab bar and copies the block that is showing. Add `dropdown` to switch with a menu instead of a tab bar.
316
+ Group several code blocks into one tabbed switcher — a tab per language or file. The tab label is each block's title (the text after the language), or its language's name (`TypeScript`, `Python`) for a block without one, and the group's copy button sits in the tab bar and copies the block that is showing. Add `dropdown` to switch with a menu instead of a tab bar.
299
317
 
300
318
  <CodeGroup>
301
319
 
@@ -367,7 +385,7 @@ Wrap an image or any visual in a centered, bordered frame with an optional `capt
367
385
 
368
386
  ## YouTube
369
387
 
370
- Embed a YouTube video in a responsive, privacy-friendly (`youtube-nocookie.com`) 16:9 frame that ships no client JavaScript. Pass a video `id` or a full `url`, plus an optional `title` (for accessibility) and a `start` time in seconds.
388
+ Embed a YouTube video in a responsive, privacy-friendly (`youtube-nocookie.com`) 16:9 frame that ships no client JavaScript. Pass a video `id` or a full `url`, plus an optional `title` (for accessibility) and a `start` time in seconds. A playlist `url` (`youtube.com/playlist?list=…` or `youtube.com/embed/videoseries?list=…`) embeds the whole playlist.
371
389
 
372
390
  <YouTube id="aqz-KE-bpKQ" title="Big Buck Bunny" />
373
391
 
@@ -468,7 +486,7 @@ A clickable preview that leads with a visual — an icon or image — above a ti
468
486
 
469
487
  ## Prompt
470
488
 
471
- A single row with a label and a copy button. The `description` (Markdown) is the visible label; the body is the prompt itself — hidden, and copied to the clipboard as Markdown, with its links, lists, and code intact, when the **Copy prompt** button is pressed. `actions` controls the buttons (e.g. `["copy", "cursor"]`).
489
+ A single row with a label and a copy button. The `description` (Markdown) is the visible label; the body is the prompt itself — hidden, and copied to the clipboard as Markdown, with its links, lists, and code intact, when the **Copy prompt** button is pressed. Its punctuation copies as you typed it: straight quotes, `--`, and `...` stay as written, though prose elsewhere on the page shows them as curly quotes, dashes, and ellipses. `actions` controls the buttons (e.g. `["copy", "cursor"]`).
472
490
 
473
491
  <Prompt
474
492
  description="Ask the model to **document** an endpoint."
@@ -781,6 +799,8 @@ import CodeBlock from "blume/components/content/CodeBlock.astro";
781
799
 
782
800
  `title` sets the header label — a filename, say — which otherwise shows the language, and `icons={false}` hides the language's brand icon, as [`markdown.code.icons`](/docs/content/syntax#code-blocks) does for fences.
783
801
 
802
+ Without `code`, `CodeBlock` renders its children as written, so a fenced block wrapped in `<CodeBlock>…</CodeBlock>` (the way Fern and Mintlify write code) shows as that fence. `title` and the other props don't apply to it then: give the fence its [title](/docs/content/syntax#code-blocks) after the language instead.
803
+
784
804
  To highlight to an HTML string yourself (e.g. inside your own component), import the underlying helper from `blume/markdown`:
785
805
 
786
806
  ```ts
@@ -148,7 +148,7 @@ sidebar:
148
148
  display: page
149
149
  ```
150
150
 
151
- `hidden` removes the page from the sidebar and from previous/next pagination. On a folder's `index` page it removes only the page's own row: the group row keeps linking to the page, and previous/next links still pass through it.
151
+ `hidden` removes the page from the sidebar and from previous/next pagination, and so from the sitemap, search, and `llms.txt` (see [Hidden pages](/docs/content/navigation#hidden-pages)). On a folder's `index` page it removes only the page's own row: the group row keeps linking to the page, so previous/next links still pass through it and the page stays in the sitemap, search, and `llms.txt`. On the home page, the root `index` page, it also removes only the row: the page stays in the sitemap, search, and `llms.txt`.
152
152
 
153
153
  `display` sets the render mode of the page's folder group ([per-group overrides](/docs/content/navigation#per-group-overrides)) and is only meaningful on a folder's `index` page under the generated sidebar — anywhere else (a non-index page, the content root's own `index` page, or any page under an explicit `navigation.sidebar`) it has no group to configure, and Blume warns with `BLUME_SIDEBAR_DISPLAY_IGNORED`.
154
154
 
@@ -23,7 +23,7 @@ Here it is live — this next callout is spliced from a shared snippet:
23
23
 
24
24
  ## Partials
25
25
 
26
- Any file whose name (or folder) starts with an underscore is excluded from routing, navigation, search, and sitemaps by default — that convention is the natural home for shared snippets:
26
+ Any file whose name (or folder) starts with an underscore is excluded from routing, navigation, search, and sitemaps — that convention is the natural home for shared snippets. Patterns you add to [`content.exclude`](/docs/configuration#content) add to this rule rather than replacing it; to publish underscore files after all, add `"!**/_*"` to `exclude`:
27
27
 
28
28
  ```text
29
29
  docs/
@@ -37,7 +37,9 @@ docs/
37
37
 
38
38
  A partial is a normal Markdown or MDX file. Its front matter is stripped when spliced (the including page's front matter wins), and everything else — callouts, code fences, components, math — renders exactly as it would inline. Partials can include other partials; a circular include is reported as an error.
39
39
 
40
- Relative image references inside a partial keep working: they're rebased onto the including page, so a colocated `![diagram](./diagram.png)` next to the partial resolves wherever the partial is spliced.
40
+ Link reference definitions (`[pep 508]: https://peps.python.org/pep-0508/`) cross includes too, so a partial can hold a site's shared link definitions: the page and its other partials can cite a partial's (`[PEP 508]`), and a partial can cite the page's. When the page and a partial both define a label, each uses its own.
41
+
42
+ Relative image references inside a partial keep working: they're rebased onto the including page, so a colocated `![diagram](./diagram.png)` or `<Card img="./cover.png">` next to the partial resolves wherever the partial is spliced. An image's path is rebased in whatever form it's written: in angle brackets (`![diagram](<./my diagram.png>)`), with escaped parentheses, or followed by a title in any of its quotes. A link to another file next to the partial (`[spec](./spec.pdf)`, or an `<img src>` or `<a href>` naming one) moves with it too. A link to a page resolves from the including page, as if it were written there.
41
43
 
42
44
  Editing a partial while `blume dev` is running reloads every page that includes it.
43
45
 
@@ -77,6 +79,8 @@ Targets must live inside your content root: a file outside it would be silently
77
79
 
78
80
  Broken links inside a partial are reported against the partial file, not the pages that splice it, so you fix them where they live.
79
81
 
82
+ A partial is read in the including page's format, so a `.md` partial that an `.mdx` page includes is read as MDX. MDX has no HTML comments, so the partial's `<!-- … -->` comments are spliced as MDX comments (`{/* … */}`) and stay hidden. Other HTML there has to be valid JSX: an `<img …>` or `<br>` without a closing slash takes in everything after it in the partial, with no error. Blume warns about it as `BLUME_MDX_UNCLOSED_ELEMENT` at the partial's line; close it as `<br />`. Anything else in the partial that MDX can't parse once it's spliced in, like a stray `{`, is a [`BLUME_MDX_SYNTAX`](/docs/content#markdown-and-mdx) warning at the partial's line, and when it fails the build, the build reports it there too, not at the page that includes it.
83
+
80
84
  :::note
81
85
  Partials are shared across locales and are not translated by `blume translate` — keep partials language-neutral (code, tables, diagrams), or create per-locale partials and include them from each locale's pages.
82
86
  :::
@@ -16,6 +16,10 @@ Blume renders two kinds of file:
16
16
 
17
17
  Reach for `.md` when a page is just prose, and `.mdx` when it needs components or directives. Switching is as simple as renaming the file.
18
18
 
19
+ In `.mdx`, `{` starts a JavaScript expression and `<` a JSX tag, so text that's fine in `.md` can break a page: a literal brace, an HTML comment (write `{/* … */}`), or an element left open (write `<br />`). Blume parses every `.mdx` page when it reads the project, so `blume dev`, `blume validate`, `blume doctor`, and `blume check` report one that doesn't parse as `BLUME_MDX_SYNTAX`, an error at the line and column where MDX stopped, and `blume build` stops there before it compiles anything. With `--no-strict`, the build leaves the page out and builds the rest of the site. `blume dev` keeps the page, and opening it shows the error.
20
+
21
+ An expression can parse and still fail when the page renders, like `{user.name}` on a page where nothing defines `user`. `blume build` reports that as `BLUME_MDX_UNDEFINED_NAME` at the expression's line, in the page or in the [partial](/docs/content/includes) that holds it. To show braces as text, escape each opening brace (`\{`) or put the text in inline code. For a value every page shares, define a [variable](/docs/content/variables).
22
+
19
23
  ## Files and routes
20
24
 
21
25
  Each file maps to a route by its path under the content root:
@@ -40,7 +44,13 @@ Prefix a file or folder with a number and a `-`, `_`, or `.` to control its orde
40
44
  02-installation.mdx -> /installation
41
45
  ```
42
46
 
43
- 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.
47
+ A version or a 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`. Blume reads these as dates:
48
+
49
+ - An ISO date, `YYYY-MM-DD`: `2024-01-05.mdx` routes to `/2024-01-05`.
50
+ - A day or month first, then a four-digit year, `D-M-YYYY` or `M-D-YYYY` with one or two digits for the day and month: `12-05-2022.mdx` routes to `/12-05-2022`.
51
+ - A year and a month, `YYYY-MM`: `2024-01.mdx` routes to `/2024-01`.
52
+
53
+ A date ends the name or is followed by a `-`, `_`, or `.`, like the rest of a name (`2024-01-recap.mdx`). Earlier Blume versions read the first number of the last two forms as an ordering prefix, so `12-05-2022.mdx` published at `/05-2022` and `2024-01.mdx` at `/01`. Those old URLs now redirect to the new ones, unless another page lives there. 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.
44
54
 
45
55
  Ordering has several layers — see [Navigation](/docs/content/navigation) for the full precedence rules.
46
56
 
@@ -28,6 +28,8 @@ The filename is the component name, so it **must be a PascalCase identifier**
28
28
  Islands are for **interactive** UI. For a static component you reuse across pages (a styled callout, a pricing table), use an [MDX override](/docs/configuration/customization) instead — it ships no JavaScript.
29
29
  :::
30
30
 
31
+ An island is the only way to run code in the browser from a page. A JavaScript event handler on a plain HTML element in MDX, like `<button onClick={() => open()}>`, never runs: the page is static HTML, so the function is written into the attribute as text. Blume warns about one as `BLUME_MDX_EVENT_HANDLER` at its line. Move that markup into an island and use the island in the page.
32
+
31
33
  ## Registering islands in `components.ts`
32
34
 
33
35
  If you'd rather keep islands next to the rest of your components — or give them a different name than the file — register them with `defineComponents` as an `mdx` entry with a `client` mode. That's all an island is: an MDX component that hydrates. Each entry is available in every MDX page, and an entry with the same name as an `islands/` file replaces it.