blume 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +113 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
  12. package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
  13. package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
  14. package/dist/cli/chunk-43zcf4gz.js.map +36 -0
  15. package/dist/cli/chunk-4ejxbzee.js +39 -0
  16. package/dist/cli/chunk-4ejxbzee.js.map +10 -0
  17. package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
  18. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  19. package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
  20. package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
  21. package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
  22. package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
  23. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  24. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  25. package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
  26. package/dist/cli/chunk-713mngdq.js.map +14 -0
  27. package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
  28. package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
  29. package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
  30. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  31. package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
  32. package/dist/cli/chunk-cm89scdv.js +443 -0
  33. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  34. package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
  35. package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
  36. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  37. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  38. package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
  39. package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
  40. package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
  41. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  42. package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
  43. package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
  44. package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
  45. package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
  46. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  47. package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
  48. package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
  49. package/dist/cli/chunk-kwrg63tm.js +31 -0
  50. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  51. package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
  52. package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
  53. package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
  54. package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
  55. package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
  56. package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
  57. package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
  58. package/dist/cli/chunk-q3wxxq39.js +126 -0
  59. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  60. package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
  61. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  62. package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
  63. package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
  64. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  65. package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
  66. package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
  67. package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
  68. package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
  69. package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
  70. package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
  71. package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
  72. package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
  73. package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
  74. package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
  75. package/dist/cli/chunk-zf748avv.js.map +37 -0
  76. package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
  77. package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
  78. package/dist/cli/index.js +28 -20
  79. package/dist/cli/index.js.map +5 -5
  80. package/dist/types/ai/agent-surface.d.ts +8 -7
  81. package/dist/types/ai/ai-catalog.d.ts +7 -0
  82. package/dist/types/ai/ask-context.d.ts +22 -0
  83. package/dist/types/ai/ask.d.ts +271 -58
  84. package/dist/types/ai/index.d.ts +2 -2
  85. package/dist/types/ai/link-headers.d.ts +8 -1
  86. package/dist/types/ai/openapi-components.d.ts +5 -2
  87. package/dist/types/ai/relative-links.d.ts +9 -7
  88. package/dist/types/ai/site-skill.d.ts +26 -0
  89. package/dist/types/ai/skills.d.ts +20 -1
  90. package/dist/types/ai/tar.d.ts +1 -3
  91. package/dist/types/analytics/index.d.ts +2 -0
  92. package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
  93. package/dist/types/analytics/schema.d.ts +14 -0
  94. package/dist/types/astro/integration.d.ts +11 -2
  95. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  96. package/dist/types/captcha/index.d.ts +28 -0
  97. package/dist/types/captcha/schema.d.ts +29 -0
  98. package/dist/types/captcha/turnstile.d.ts +28 -0
  99. package/dist/types/components/content/api-field.d.ts +59 -0
  100. package/dist/types/components/content/api-page.d.ts +95 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/helpers.d.ts +137 -0
  103. package/dist/types/components/openapi/security.d.ts +85 -0
  104. package/dist/types/consent/ethyca.d.ts +48 -0
  105. package/dist/types/consent/index.d.ts +37 -0
  106. package/dist/types/consent/native.d.ts +28 -0
  107. package/dist/types/consent/osano.d.ts +42 -0
  108. package/dist/types/consent/schema.d.ts +110 -0
  109. package/dist/types/core/base-path.d.ts +21 -7
  110. package/dist/types/core/config-input.d.ts +196 -27
  111. package/dist/types/core/data.d.ts +49 -4
  112. package/dist/types/core/directive-diagnostics.d.ts +12 -0
  113. package/dist/types/core/footer.d.ts +8 -0
  114. package/dist/types/core/heading-markers.d.ts +5 -7
  115. package/dist/types/core/i18n-ui.d.ts +86 -0
  116. package/dist/types/core/i18n.d.ts +2 -0
  117. package/dist/types/core/includes.d.ts +8 -2
  118. package/dist/types/core/last-modified.d.ts +10 -0
  119. package/dist/types/core/localizable.d.ts +7 -0
  120. package/dist/types/core/meta.d.ts +8 -0
  121. package/dist/types/core/page-modes.d.ts +29 -0
  122. package/dist/types/core/redirect-patterns.d.ts +144 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +558 -56
  125. package/dist/types/core/sources/lower.d.ts +29 -16
  126. package/dist/types/core/sources/normalize.d.ts +13 -1
  127. package/dist/types/core/sources/read.d.ts +5 -0
  128. package/dist/types/core/sources/watch.d.ts +12 -5
  129. package/dist/types/core/standard-schema.d.ts +5 -0
  130. package/dist/types/core/types.d.ts +1 -1
  131. package/dist/types/core/variables.d.ts +42 -0
  132. package/dist/types/deploy/artifacts.d.ts +6 -4
  133. package/dist/types/deploy/headers.d.ts +5 -0
  134. package/dist/types/deploy/node-headers.d.ts +13 -2
  135. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  136. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  137. package/dist/types/deploy/platforms/types.d.ts +8 -0
  138. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  139. package/dist/types/deploy/redirects.d.ts +61 -22
  140. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  141. package/dist/types/markdown/code-title.d.ts +17 -0
  142. package/dist/types/markdown/directives.d.ts +62 -0
  143. package/dist/types/markdown/features.d.ts +24 -2
  144. package/dist/types/markdown/mdast.d.ts +63 -0
  145. package/dist/types/narration/build.d.ts +70 -0
  146. package/dist/types/narration/provider.d.ts +26 -0
  147. package/dist/types/narration/script.d.ts +154 -0
  148. package/dist/types/openapi/asyncapi.d.ts +4 -2
  149. package/dist/types/openapi/model.d.ts +12 -6
  150. package/dist/types/openapi/overlay.d.ts +54 -0
  151. package/dist/types/openapi/parse.d.ts +13 -0
  152. package/dist/types/openapi/references.d.ts +14 -4
  153. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  154. package/dist/types/ratelimit/index.d.ts +27 -0
  155. package/dist/types/ratelimit/memory.d.ts +36 -0
  156. package/dist/types/ratelimit/schema.d.ts +111 -0
  157. package/dist/types/ratelimit/upstash.d.ts +21 -0
  158. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  159. package/dist/types/reference/asyncapi.d.ts +10 -10
  160. package/dist/types/reference/graphql.d.ts +13 -10
  161. package/dist/types/reference/openapi.d.ts +33 -12
  162. package/dist/types/reference/options.d.ts +45 -5
  163. package/dist/types/reference/scalar.d.ts +13 -0
  164. package/dist/types/reference/schema.d.ts +60 -36
  165. package/dist/types/search/documents.d.ts +12 -1
  166. package/dist/types/search/orama-index.d.ts +4 -0
  167. package/dist/types/seo/metatags.d.ts +20 -0
  168. package/docs/02-deployment.mdx +44 -6
  169. package/docs/03-upgrading.mdx +4 -4
  170. package/docs/04-migrating.mdx +1 -1
  171. package/docs/08-faq.mdx +1 -1
  172. package/docs/advanced/changelog.mdx +2 -2
  173. package/docs/advanced/custom-pages.mdx +3 -3
  174. package/docs/advanced/skills.mdx +14 -0
  175. package/docs/cli/audit.mdx +38 -2
  176. package/docs/cli/evals.mdx +3 -3
  177. package/docs/cli/index.mdx +1 -0
  178. package/docs/cli/translate.mdx +3 -3
  179. package/docs/cli/version.mdx +1 -1
  180. package/docs/configuration/analytics.mdx +22 -1
  181. package/docs/configuration/assistant.mdx +188 -36
  182. package/docs/configuration/consent.mdx +100 -0
  183. package/docs/configuration/customization.mdx +6 -4
  184. package/docs/configuration/index.mdx +63 -5
  185. package/docs/configuration/meta.ts +3 -0
  186. package/docs/configuration/narration.mdx +119 -0
  187. package/docs/configuration/rate-limiting.mdx +69 -0
  188. package/docs/configuration/search.mdx +30 -2
  189. package/docs/configuration/theming.mdx +3 -1
  190. package/docs/content/components.mdx +107 -1
  191. package/docs/content/frontmatter.mdx +77 -1
  192. package/docs/content/i18n.mdx +40 -3
  193. package/docs/content/includes.mdx +16 -0
  194. package/docs/content/index.mdx +4 -2
  195. package/docs/content/islands.mdx +1 -1
  196. package/docs/content/meta.mdx +4 -1
  197. package/docs/content/meta.ts +1 -0
  198. package/docs/content/navigation.mdx +28 -6
  199. package/docs/content/sources/contentful.mdx +32 -0
  200. package/docs/content/sources/custom.mdx +39 -0
  201. package/docs/content/sources/github-releases.mdx +31 -0
  202. package/docs/content/sources/index.mdx +91 -0
  203. package/docs/content/sources/meta.ts +16 -0
  204. package/docs/content/sources/notion.mdx +32 -0
  205. package/docs/content/sources/obsidian.mdx +37 -0
  206. package/docs/content/sources/payload.mdx +30 -0
  207. package/docs/content/sources/remote-mdx.mdx +40 -0
  208. package/docs/content/sources/sanity.mdx +29 -0
  209. package/docs/content/sources/strapi.mdx +31 -0
  210. package/docs/content/syntax.mdx +81 -2
  211. package/docs/content/variables.mdx +41 -0
  212. package/docs/content/versioning.mdx +1 -1
  213. package/docs/discoverability/agent-discovery.mdx +30 -3
  214. package/docs/discoverability/index.mdx +3 -2
  215. package/docs/discoverability/llms-txt.mdx +1 -1
  216. package/docs/discoverability/markdown.mdx +2 -2
  217. package/docs/discoverability/metadata.mdx +17 -0
  218. package/docs/discoverability/open-graph.mdx +5 -3
  219. package/docs/discoverability/rss.mdx +1 -1
  220. package/docs/references/api-pages.mdx +74 -0
  221. package/docs/references/asyncapi.mdx +2 -2
  222. package/docs/references/graphql.mdx +3 -3
  223. package/docs/references/meta.ts +1 -1
  224. package/docs/references/openapi.mdx +106 -5
  225. package/docs/references/scalar.mdx +3 -1
  226. package/package.json +36 -1
  227. package/skills/blume/SKILL.md +3 -0
  228. package/skills/blume-migrate/SKILL.md +5 -5
  229. package/skills/blume-migrate/references/docusaurus.md +3 -3
  230. package/skills/blume-migrate/references/fumadocs.md +1 -1
  231. package/skills/blume-migrate/references/mintlify.md +37 -19
  232. package/skills/blume-migrate/references/nextra.md +3 -3
  233. package/skills/blume-migrate/references/starlight.md +2 -1
  234. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
  235. package/skills/blume-write-skill/SKILL.md +82 -0
  236. package/src/ai/agent-readability.ts +4 -3
  237. package/src/ai/agent-surface.ts +9 -7
  238. package/src/ai/ai-catalog.ts +12 -5
  239. package/src/ai/api/handlers.ts +2 -2
  240. package/src/ai/api/spec.ts +2 -2
  241. package/src/ai/api-catalog.ts +6 -2
  242. package/src/ai/ask-context.ts +73 -20
  243. package/src/ai/ask-data.ts +6 -0
  244. package/src/ai/ask-tools.ts +142 -0
  245. package/src/ai/ask.ts +354 -76
  246. package/src/ai/changelog-markdown.ts +2 -2
  247. package/src/ai/component-markdown.ts +75 -0
  248. package/src/ai/cors.ts +2 -0
  249. package/src/ai/index.ts +12 -0
  250. package/src/ai/link-headers.ts +14 -5
  251. package/src/ai/llms.ts +24 -9
  252. package/src/ai/markdown.ts +2 -1
  253. package/src/ai/mcp/data.ts +6 -0
  254. package/src/ai/mcp/discovery.ts +2 -2
  255. package/src/ai/mcp/query.ts +2 -2
  256. package/src/ai/mcp/server.ts +2 -2
  257. package/src/ai/openapi-components.ts +31 -9
  258. package/src/ai/relative-links.ts +42 -26
  259. package/src/ai/serializers.ts +2 -1
  260. package/src/ai/site-skill.ts +274 -0
  261. package/src/ai/skills.ts +49 -3
  262. package/src/ai/tar.ts +139 -12
  263. package/src/ai/write-skill.ts +97 -0
  264. package/src/analytics/head.ts +4 -0
  265. package/src/analytics/index.ts +5 -0
  266. package/src/analytics/one-dollar-stats.ts +86 -0
  267. package/src/analytics/schema.ts +2 -0
  268. package/src/astro/generate.ts +133 -19
  269. package/src/astro/include-hmr.ts +31 -12
  270. package/src/astro/include-refresh.ts +19 -8
  271. package/src/astro/index.ts +1 -0
  272. package/src/astro/integration.ts +143 -14
  273. package/src/astro/module-types.ts +6 -0
  274. package/src/astro/runtime-modules.ts +28 -13
  275. package/src/astro/templates.ts +364 -67
  276. package/src/astro/variables.ts +31 -0
  277. package/src/audit/catalog.ts +19 -0
  278. package/src/audit/checks/contrast.ts +183 -0
  279. package/src/audit/checks/i18n.ts +2 -3
  280. package/src/audit/checks/indexability.ts +2 -3
  281. package/src/audit/checks/links.ts +13 -13
  282. package/src/audit/checks/sitemap.ts +2 -3
  283. package/src/audit/graph.ts +3 -1
  284. package/src/audit/redirects.ts +46 -15
  285. package/src/audit/run.ts +14 -9
  286. package/src/audit/snapshot.ts +5 -0
  287. package/src/audit/types.ts +2 -1
  288. package/src/blume-modules.d.ts +7 -0
  289. package/src/captcha/client.ts +152 -0
  290. package/src/captcha/hcaptcha.ts +36 -0
  291. package/src/captcha/index.ts +28 -0
  292. package/src/captcha/schema.ts +32 -0
  293. package/src/captcha/turnstile.ts +38 -0
  294. package/src/captcha/verify.ts +71 -0
  295. package/src/cli/args.ts +32 -0
  296. package/src/cli/command-meta.ts +5 -0
  297. package/src/cli/commands/audit.ts +4 -1
  298. package/src/cli/commands/doctor.ts +25 -17
  299. package/src/cli/commands/eval.ts +19 -11
  300. package/src/cli/commands/skill.ts +96 -0
  301. package/src/cli/commands/translate.ts +9 -12
  302. package/src/cli/commands/validate.ts +3 -1
  303. package/src/cli/dev-lock.ts +157 -37
  304. package/src/cli/index.ts +5 -0
  305. package/src/cli/report-format.ts +11 -6
  306. package/src/cli/required-secrets.ts +71 -29
  307. package/src/components/BlumePage.astro +2 -0
  308. package/src/components/colors.ts +19 -0
  309. package/src/components/content/ApiEndpoint.astro +18 -0
  310. package/src/components/content/ApiField.astro +79 -0
  311. package/src/components/content/ApiPlayground.astro +62 -0
  312. package/src/components/content/ApiRail.astro +19 -0
  313. package/src/components/content/Badge.astro +5 -3
  314. package/src/components/content/Color.astro +1 -0
  315. package/src/components/content/Component.astro +2 -2
  316. package/src/components/content/FileTree.astro +1 -0
  317. package/src/components/content/GithubInfo.astro +1 -0
  318. package/src/components/content/Panel.astro +1 -0
  319. package/src/components/content/ParamField.astro +25 -0
  320. package/src/components/content/RequestExample.astro +17 -0
  321. package/src/components/content/ResponseExample.astro +17 -0
  322. package/src/components/content/ResponseField.astro +32 -0
  323. package/src/components/content/Step.astro +1 -0
  324. package/src/components/content/Tab.astro +0 -1
  325. package/src/components/content/Tabs.astro +4 -0
  326. package/src/components/content/Tile.astro +1 -0
  327. package/src/components/content/Tooltip.astro +1 -0
  328. package/src/components/content/Tree.astro +1 -0
  329. package/src/components/content/TypeTable.astro +4 -1
  330. package/src/components/content/View.astro +21 -0
  331. package/src/components/content/ViewSwitcher.astro +168 -0
  332. package/src/components/content/api-field.ts +100 -0
  333. package/src/components/content/api-page.ts +224 -0
  334. package/src/components/content/badge-color.ts +5 -2
  335. package/src/components/content/base-href.ts +13 -36
  336. package/src/components/content/view-switcher.ts +71 -0
  337. package/src/components/index.ts +2 -0
  338. package/src/components/islands/Assistant.astro +10 -1
  339. package/src/components/islands/assistant.tsx +125 -18
  340. package/src/components/islands/base-path.ts +47 -10
  341. package/src/components/islands/code-question.ts +62 -0
  342. package/src/components/islands/hooks.ts +145 -40
  343. package/src/components/islands/support-link.ts +68 -0
  344. package/src/components/islands/webmcp.ts +4 -2
  345. package/src/components/layout/Analytics.astro +11 -2
  346. package/src/components/layout/Banner.astro +14 -10
  347. package/src/components/layout/Breadcrumbs.astro +11 -4
  348. package/src/components/layout/ConsentBanner.astro +62 -0
  349. package/src/components/layout/ConsentHead.astro +38 -0
  350. package/src/components/layout/Directory.astro +94 -0
  351. package/src/components/layout/DiscoveryLinks.astro +9 -5
  352. package/src/components/layout/DrawerSwitchers.astro +51 -0
  353. package/src/components/layout/Empty.astro +2 -2
  354. package/src/components/layout/Header.astro +124 -82
  355. package/src/components/layout/LanguageSwitcher.astro +50 -17
  356. package/src/components/layout/NarrationPlayer.astro +1085 -0
  357. package/src/components/layout/NavSelector.astro +35 -9
  358. package/src/components/layout/NavTabMenu.astro +3 -3
  359. package/src/components/layout/NavTree.astro +16 -6
  360. package/src/components/layout/PageActions.astro +4 -3
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +78 -9
  363. package/src/components/layout/Pagination.astro +3 -3
  364. package/src/components/layout/ReferenceLayout.astro +17 -6
  365. package/src/components/layout/RelatedPages.astro +42 -0
  366. package/src/components/layout/RootLayout.astro +313 -50
  367. package/src/components/layout/Search.astro +64 -30
  368. package/src/components/layout/SiteFooter.astro +124 -0
  369. package/src/components/layout/SiteMetatags.astro +16 -0
  370. package/src/components/layout/VersionBanner.astro +2 -2
  371. package/src/components/layout/analytics-client.ts +12 -0
  372. package/src/components/layout/consent/native.ts +79 -0
  373. package/src/components/layout/consent/types.ts +37 -0
  374. package/src/components/layout/footer-links.ts +53 -0
  375. package/src/components/layout/footer-socials.ts +87 -0
  376. package/src/components/layout/locale-redirect.ts +107 -0
  377. package/src/components/layout/nav-utils.ts +83 -0
  378. package/src/components/layout/page-feedback.ts +96 -0
  379. package/src/components/layout/search/analytics.ts +98 -0
  380. package/src/components/layout/search/flexsearch.ts +17 -5
  381. package/src/components/layout/search/orama-cloud.ts +26 -14
  382. package/src/components/layout/search/types.ts +20 -0
  383. package/src/components/layout/search/typesense.ts +5 -1
  384. package/src/components/layout/toc-active.ts +41 -0
  385. package/src/components/layout/toc-element.ts +8 -14
  386. package/src/components/openapi/ApiTagOperations.astro +2 -2
  387. package/src/components/openapi/AsyncApiOperation.astro +7 -4
  388. package/src/components/openapi/GraphqlChip.astro +2 -2
  389. package/src/components/openapi/Operation.astro +163 -13
  390. package/src/components/openapi/Playground.astro +4 -4
  391. package/src/components/openapi/RequestBody.astro +6 -4
  392. package/src/components/openapi/RequestPanel.astro +63 -8
  393. package/src/components/openapi/Responses.astro +6 -4
  394. package/src/components/openapi/SchemaTable.astro +7 -0
  395. package/src/components/openapi/async-snippets.ts +4 -4
  396. package/src/components/openapi/async.ts +38 -6
  397. package/src/components/openapi/code-samples.ts +97 -0
  398. package/src/components/openapi/helpers.ts +19 -9
  399. package/src/components/openapi/message-composer.ts +6 -1
  400. package/src/components/openapi/message.ts +17 -2
  401. package/src/components/openapi/operation-model.ts +81 -11
  402. package/src/components/openapi/panel.ts +4 -2
  403. package/src/components/openapi/param-style.ts +181 -0
  404. package/src/components/openapi/playground-client.ts +73 -16
  405. package/src/components/openapi/request.ts +190 -26
  406. package/src/components/openapi/schema-tree.ts +30 -24
  407. package/src/components/openapi/security.ts +6 -3
  408. package/src/components/openapi/snippets.ts +953 -13
  409. package/src/components/openapi/ws-client.ts +18 -2
  410. package/src/components/props.ts +6 -0
  411. package/src/consent/client.ts +95 -0
  412. package/src/consent/clients.ts +19 -0
  413. package/src/consent/ethyca.ts +71 -0
  414. package/src/consent/head.ts +24 -0
  415. package/src/consent/index.ts +37 -0
  416. package/src/consent/init.ts +29 -0
  417. package/src/consent/native.ts +36 -0
  418. package/src/consent/osano.ts +59 -0
  419. package/src/consent/schema.ts +30 -0
  420. package/src/core/base-path.ts +62 -16
  421. package/src/core/builtin-tags.ts +5 -0
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/config-input.ts +203 -27
  424. package/src/core/data.ts +44 -4
  425. package/src/core/diagnostics.ts +5 -2
  426. package/src/core/directive-diagnostics.ts +99 -0
  427. package/src/core/footer.ts +26 -0
  428. package/src/core/frontmatter.ts +21 -18
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/heading-markers.ts +5 -18
  431. package/src/core/i18n-ui.ts +82 -2
  432. package/src/core/i18n.ts +31 -3
  433. package/src/core/includes.ts +20 -3
  434. package/src/core/last-modified.ts +25 -3
  435. package/src/core/links.ts +16 -2
  436. package/src/core/locale-links.ts +5 -1
  437. package/src/core/localizable.ts +26 -0
  438. package/src/core/meta.ts +33 -19
  439. package/src/core/navigation.ts +71 -20
  440. package/src/core/page-modes.ts +56 -0
  441. package/src/core/project-graph.ts +138 -10
  442. package/src/core/redirect-patterns.ts +491 -0
  443. package/src/core/related.ts +131 -0
  444. package/src/core/schema.ts +342 -68
  445. package/src/core/sources/contentful-rich-text.ts +27 -20
  446. package/src/core/sources/filesystem.ts +20 -2
  447. package/src/core/sources/github-releases.ts +25 -6
  448. package/src/core/sources/lexical.ts +23 -18
  449. package/src/core/sources/lower.ts +201 -34
  450. package/src/core/sources/mdx-remote.ts +51 -16
  451. package/src/core/sources/normalize.ts +327 -91
  452. package/src/core/sources/notion.ts +52 -25
  453. package/src/core/sources/obsidian.ts +23 -6
  454. package/src/core/sources/portable-text.ts +38 -21
  455. package/src/core/sources/read.ts +8 -3
  456. package/src/core/sources/strapi-blocks.ts +20 -16
  457. package/src/core/sources/watch.ts +20 -7
  458. package/src/core/standard-schema.ts +10 -6
  459. package/src/core/types.ts +1 -1
  460. package/src/core/ui-packs/ar.ts +43 -0
  461. package/src/core/ui-packs/bg.ts +45 -0
  462. package/src/core/ui-packs/bn.ts +43 -0
  463. package/src/core/ui-packs/ca.ts +46 -0
  464. package/src/core/ui-packs/cs.ts +44 -0
  465. package/src/core/ui-packs/da.ts +45 -0
  466. package/src/core/ui-packs/de.ts +46 -0
  467. package/src/core/ui-packs/el.ts +45 -0
  468. package/src/core/ui-packs/es.ts +46 -0
  469. package/src/core/ui-packs/fa.ts +45 -0
  470. package/src/core/ui-packs/fi.ts +45 -0
  471. package/src/core/ui-packs/fr.ts +46 -0
  472. package/src/core/ui-packs/he.ts +43 -0
  473. package/src/core/ui-packs/hi.ts +44 -0
  474. package/src/core/ui-packs/hr.ts +45 -0
  475. package/src/core/ui-packs/hu.ts +44 -0
  476. package/src/core/ui-packs/id.ts +45 -0
  477. package/src/core/ui-packs/it.ts +45 -0
  478. package/src/core/ui-packs/ja.ts +45 -0
  479. package/src/core/ui-packs/ko.ts +44 -0
  480. package/src/core/ui-packs/nl.ts +46 -0
  481. package/src/core/ui-packs/no.ts +44 -0
  482. package/src/core/ui-packs/pl.ts +45 -0
  483. package/src/core/ui-packs/pt-br.ts +46 -0
  484. package/src/core/ui-packs/pt.ts +45 -0
  485. package/src/core/ui-packs/ro.ts +45 -0
  486. package/src/core/ui-packs/ru.ts +45 -0
  487. package/src/core/ui-packs/sk.ts +44 -0
  488. package/src/core/ui-packs/sr.ts +45 -0
  489. package/src/core/ui-packs/sv.ts +44 -0
  490. package/src/core/ui-packs/th.ts +43 -0
  491. package/src/core/ui-packs/tr.ts +45 -0
  492. package/src/core/ui-packs/uk.ts +45 -0
  493. package/src/core/ui-packs/vi.ts +45 -0
  494. package/src/core/ui-packs/zh-tw.ts +43 -0
  495. package/src/core/ui-packs/zh.ts +43 -0
  496. package/src/core/variables.ts +103 -0
  497. package/src/core/version-cut.ts +52 -15
  498. package/src/deploy/artifacts.ts +104 -14
  499. package/src/deploy/cloudflare-negotiation.ts +39 -17
  500. package/src/deploy/headers.ts +10 -5
  501. package/src/deploy/node-headers.ts +64 -10
  502. package/src/deploy/platforms/cloudflare.ts +44 -3
  503. package/src/deploy/platforms/netlify.ts +34 -1
  504. package/src/deploy/platforms/node.ts +1 -0
  505. package/src/deploy/platforms/static.ts +1 -0
  506. package/src/deploy/platforms/types.ts +8 -0
  507. package/src/deploy/platforms/vercel.ts +38 -2
  508. package/src/deploy/redirects.ts +139 -36
  509. package/src/deploy/robots.ts +2 -2
  510. package/src/deploy/rss.ts +4 -3
  511. package/src/deploy/sitemap.ts +7 -5
  512. package/src/deploy/vercel-negotiation.ts +36 -0
  513. package/src/markdown/api-rail.ts +259 -0
  514. package/src/markdown/base-links.ts +34 -36
  515. package/src/markdown/code-title.ts +35 -9
  516. package/src/markdown/directives.ts +242 -36
  517. package/src/markdown/features.ts +20 -2
  518. package/src/markdown/fence-meta.ts +2 -0
  519. package/src/markdown/include.ts +11 -1
  520. package/src/markdown/index.ts +35 -11
  521. package/src/markdown/math.ts +4 -4
  522. package/src/markdown/mdast.ts +5 -2
  523. package/src/markdown/relative-links.ts +12 -4
  524. package/src/markdown/variables.ts +56 -0
  525. package/src/markdown/views.ts +82 -0
  526. package/src/narration/build.ts +412 -0
  527. package/src/narration/player.ts +249 -0
  528. package/src/narration/provider.ts +29 -0
  529. package/src/narration/script.ts +592 -0
  530. package/src/og/card.ts +136 -11
  531. package/src/og/derive.ts +41 -31
  532. package/src/og/index.ts +1 -0
  533. package/src/openapi/asyncapi.ts +4 -2
  534. package/src/openapi/model.ts +77 -36
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +55 -2
  537. package/src/openapi/proxy.ts +63 -10
  538. package/src/openapi/references.ts +21 -5
  539. package/src/openapi/render-mdx.ts +46 -3
  540. package/src/openapi/scalar.ts +40 -6
  541. package/src/openapi/source.ts +1 -1
  542. package/src/ratelimit/cloudflare.ts +54 -0
  543. package/src/ratelimit/index.ts +30 -0
  544. package/src/ratelimit/memory.ts +45 -0
  545. package/src/ratelimit/runtime.ts +229 -0
  546. package/src/ratelimit/schema.ts +28 -0
  547. package/src/ratelimit/upstash.ts +30 -0
  548. package/src/ratelimit/wrangler.ts +70 -0
  549. package/src/reference/asyncapi.ts +2 -2
  550. package/src/reference/graphql.ts +5 -2
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +51 -7
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +201 -28
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/version-scope.ts +30 -0
  557. package/src/search/documents.ts +93 -28
  558. package/src/search/orama-index.ts +28 -3
  559. package/src/search/pagefind-ranking.ts +39 -0
  560. package/src/search/popular.ts +2 -1
  561. package/src/search/sync/algolia.ts +15 -4
  562. package/src/search/sync/orama-cloud.ts +3 -0
  563. package/src/search/sync/typesense.ts +5 -0
  564. package/src/seo/jsonld.ts +7 -3
  565. package/src/seo/metatags.ts +67 -0
  566. package/src/theme/chrome-icons.ts +1 -0
  567. package/src/theme/color.ts +500 -0
  568. package/src/theme/entry.ts +56 -3
  569. package/src/theme/palette.ts +121 -45
  570. package/src/translate/meta.ts +68 -24
  571. package/src/translate/prompts.ts +1 -0
  572. package/src/translate/run.ts +3 -3
  573. package/src/translate/validate.ts +12 -3
  574. package/src/translate/work-list.ts +56 -17
  575. package/src/upgrade/upgrade.ts +4 -171
  576. package/dist/cli/chunk-3yce002v.js.map +0 -14
  577. package/dist/cli/chunk-5r8g91qn.js.map +0 -34
  578. package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
  579. package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
  580. package/dist/cli/chunk-ddndchfr.js.map +0 -14
  581. package/dist/cli/chunk-esh98wmb.js +0 -23
  582. package/dist/cli/chunk-esh98wmb.js.map +0 -10
  583. package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
  584. package/dist/cli/chunk-hr8ne106.js.map +0 -13
  585. package/dist/cli/chunk-j85scx15.js.map +0 -10
  586. package/dist/cli/chunk-mqc662a6.js +0 -75
  587. package/dist/cli/chunk-mqc662a6.js.map +0 -11
  588. package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
  589. package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
  590. package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
  591. package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
  592. package/dist/cli/chunk-xh43dwgw.js.map +0 -36
  593. package/docs/content/sources.mdx +0 -338
  594. /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
  595. /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
  596. /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
  597. /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
  598. /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
  599. /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
  600. /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
  601. /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
package/src/core/meta.ts CHANGED
@@ -89,6 +89,38 @@ const withinInclude = (
89
89
  (pattern) => !pattern.startsWith("!") && onPathOf(dir, globBase(pattern))
90
90
  );
91
91
 
92
+ /**
93
+ * The folder-meta files a filesystem source contributes: `patterns` (every
94
+ * meta module name, shared `meta.$.*` included, by default) under the source's
95
+ * root, minus its `exclude` globs, dependencies, and build output, and only in
96
+ * folders its `include` globs reach. `blume translate` finds the meta files it
97
+ * translates through this too, so it covers exactly the ones the scan reads.
98
+ */
99
+ export const findFolderMetaFiles = async (
100
+ source: FolderMetaSource,
101
+ patterns: readonly string[] = META_FILES
102
+ ): Promise<string[]> => {
103
+ const found = await glob([...patterns], {
104
+ absolute: true,
105
+ cwd: source.root,
106
+ // Never descend into dependencies or build output — relevant when the
107
+ // root is the project root (e.g. a `.`-rooted or all-staged project).
108
+ // The source's own `exclude` applies too: a `meta.ts` under an excluded
109
+ // folder (`src/lib/meta.ts` beside `exclude: ["src/**"]`) is application
110
+ // code, not folder meta, and importing it would fail.
111
+ ignore: [
112
+ ...(source.exclude ?? []),
113
+ "**/node_modules/**",
114
+ "**/.blume/**",
115
+ "**/dist/**",
116
+ ],
117
+ onlyFiles: true,
118
+ });
119
+ return found.filter((file) =>
120
+ withinInclude(relative(source.root, dirname(file)), source.include)
121
+ );
122
+ };
123
+
92
124
  /**
93
125
  * The folder-meta key for a directory. Mirrors the sidebar group path: the
94
126
  * source's route prefix (`docs`) followed by the directory relative to the
@@ -155,25 +187,7 @@ export const discoverFolderMeta = async (
155
187
  // `content.root` still contributes its folder meta.
156
188
  const perSource = await Promise.all(
157
189
  list.map(async (source) => {
158
- const found = await glob(META_FILES, {
159
- absolute: true,
160
- cwd: source.root,
161
- // Never descend into dependencies or build output — relevant when the
162
- // root is the project root (e.g. a `.`-rooted or all-staged project).
163
- // The source's own `exclude` applies too: a `meta.ts` under an
164
- // excluded folder (`src/lib/meta.ts` beside `exclude: ["src/**"]`)
165
- // is application code, not folder meta, and importing it would fail.
166
- ignore: [
167
- ...(source.exclude ?? []),
168
- "**/node_modules/**",
169
- "**/.blume/**",
170
- "**/dist/**",
171
- ],
172
- onlyFiles: true,
173
- });
174
- const files = found.filter((file) =>
175
- withinInclude(relative(source.root, dirname(file)), source.include)
176
- );
190
+ const files = await findFolderMetaFiles(source);
177
191
  const loaded = await Promise.all(
178
192
  files.map(
179
193
  async (
@@ -3,6 +3,7 @@ import { extname } from "pathe";
3
3
  import { stripBasePath, withBasePath } from "./base-path.ts";
4
4
  import { orderingPrefix, stripOrderingPrefix } from "./ordering-prefix.ts";
5
5
  import type {
6
+ DirectoryMode,
6
7
  FolderMeta,
7
8
  SidebarDisplay,
8
9
  SidebarItemConfig,
@@ -115,11 +116,29 @@ const numericOrder = (segment: string): number => {
115
116
  return value ? Math.trunc(Number(value)) : Number.POSITIVE_INFINITY;
116
117
  };
117
118
 
119
+ /**
120
+ * The name inside a group folder's parentheses — `(guides)`, or `01-(guides)`
121
+ * with its ordering prefix outside them — else undefined. A prefix written
122
+ * inside (`(01-guides)`) stays in the name, where it sorts and drops like any
123
+ * folder's.
124
+ */
125
+ const groupName = (raw: string): string | undefined =>
126
+ (raw.match(GROUP_FOLDER) ?? stripOrderingPrefix(raw).match(GROUP_FOLDER))
127
+ ?.groups?.label;
128
+
129
+ /**
130
+ * A folder's sidebar order: its ordering prefix, or — for a group folder
131
+ * without one outside the parentheses — the prefix inside them, so
132
+ * `01-(guides)` and `(01-guides)` both sort like `01-guides`.
133
+ */
134
+ const folderOrder = (raw: string): number =>
135
+ numericOrder(
136
+ orderingPrefix(raw) === undefined ? (groupName(raw) ?? raw) : raw
137
+ );
138
+
118
139
  /** The nav key of a raw path segment: group label or numeric-stripped name. */
119
- const segmentKey = (raw: string): string => {
120
- const group = raw.match(GROUP_FOLDER)?.groups?.label;
121
- return stripOrderingPrefix(group ?? raw);
122
- };
140
+ const segmentKey = (raw: string): string =>
141
+ stripOrderingPrefix(groupName(raw) ?? raw);
123
142
 
124
143
  /**
125
144
  * Whether a filename stem is a directory index, ignoring an ordering prefix:
@@ -184,6 +203,7 @@ interface MutableGroup {
184
203
  label: string;
185
204
  icon?: string;
186
205
  collapsed?: boolean;
206
+ directory?: DirectoryMode;
187
207
  display?: SidebarDisplay;
188
208
  order: number;
189
209
  /** Whether `order` is the node's position in its folder meta `pages` list. */
@@ -221,8 +241,8 @@ const ensureGroup = (
221
241
  const group = createGroup(
222
242
  segmentKey(rawSegment),
223
243
  path,
224
- humanize(rawSegment.match(GROUP_FOLDER)?.groups?.label ?? rawSegment),
225
- numericOrder(rawSegment)
244
+ humanize(groupName(rawSegment) ?? rawSegment),
245
+ folderOrder(rawSegment)
226
246
  );
227
247
  parent.index.set(rawSegment, group);
228
248
  parent.children.push(group);
@@ -287,9 +307,19 @@ interface FallbackFolderMeta {
287
307
  meta: (path: string) => FolderMeta | undefined;
288
308
  }
289
309
 
310
+ /** A folder meta's own fields onto its group, over what the folder implied. */
311
+ const applyMetaFields = (group: MutableGroup, meta: FolderMeta): void => {
312
+ group.label = meta.title ?? group.label;
313
+ group.icon = meta.icon ?? group.icon;
314
+ group.order = meta.order ?? group.order;
315
+ group.collapsed = meta.collapsed ?? group.collapsed;
316
+ group.directory = meta.directory ?? group.directory;
317
+ };
318
+
290
319
  /**
291
- * Apply folder meta (title/order/icon/collapsed/display and explicit page
292
- * order), plus the index-frontmatter display sugar collected per folder path.
320
+ * Apply folder meta (title/order/icon/collapsed/display/directory and
321
+ * explicit page order), plus the index-frontmatter display sugar collected
322
+ * per folder path.
293
323
  */
294
324
  const applyFolderMeta = (
295
325
  group: MutableGroup,
@@ -328,10 +358,7 @@ const applyFolderMeta = (
328
358
  (mirrored ? fallback.indexDisplay.get(group.path) : undefined) ??
329
359
  meta?.display;
330
360
  if (meta) {
331
- group.label = meta.title ?? group.label;
332
- group.icon = meta.icon ?? group.icon;
333
- group.order = meta.order ?? group.order;
334
- group.collapsed = meta.collapsed ?? group.collapsed;
361
+ applyMetaFields(group, meta);
335
362
  }
336
363
 
337
364
  for (const child of group.children) {
@@ -612,7 +639,15 @@ const hoistTabSections = (
612
639
  }
613
640
  };
614
641
 
615
- const toNavNode = (node: MutableNode, display: SidebarDisplay): NavNode => {
642
+ /**
643
+ * A group's directory mode: its own, else the nearest one set above it (see
644
+ * `directory` in `schema.ts`).
645
+ */
646
+ const toNavNode = (
647
+ node: MutableNode,
648
+ display: SidebarDisplay,
649
+ inheritedDirectory?: DirectoryMode
650
+ ): NavNode => {
616
651
  if (node.kind === "page") {
617
652
  return {
618
653
  badge: node.badge,
@@ -625,9 +660,13 @@ const toNavNode = (node: MutableNode, display: SidebarDisplay): NavNode => {
625
660
  route: node.route,
626
661
  };
627
662
  }
663
+ const directory = node.directory ?? inheritedDirectory;
628
664
  return {
629
- children: node.children.map((child) => toNavNode(child, display)),
665
+ children: node.children.map((child) =>
666
+ toNavNode(child, display, directory)
667
+ ),
630
668
  collapsed: node.collapsed,
669
+ directory,
631
670
  display: node.display ?? display,
632
671
  icon: node.icon,
633
672
  kind: "group",
@@ -722,7 +761,9 @@ const buildFileSystemSidebar = (
722
761
  const folderParts = isIndexStem(stem)
723
762
  ? routeSegments
724
763
  : routeSegments.slice(0, -1);
725
- const routeDirCount = dirs.filter((dir) => !GROUP_FOLDER.test(dir)).length;
764
+ const routeDirCount = dirs.filter(
765
+ (dir) => groupName(dir) === undefined
766
+ ).length;
726
767
  const offset = Math.max(0, folderParts.length - routeDirCount);
727
768
  // A frontmatter `slug` publishes the page away from its folder, so its
728
769
  // route says nothing about where the folder lives (`guides/a.md` with
@@ -744,7 +785,7 @@ const buildFileSystemSidebar = (
744
785
  const folders: string[] = [];
745
786
  for (const dir of dirs) {
746
787
  parent = ensureGroup(parent, dir);
747
- if (!GROUP_FOLDER.test(dir)) {
788
+ if (groupName(dir) === undefined) {
748
789
  consumed += 1;
749
790
  folders.push(stripOrderingPrefix(dir));
750
791
  }
@@ -785,7 +826,9 @@ const buildFileSystemSidebar = (
785
826
  sortNodes(root.children, diagnostics);
786
827
  hoistPages(root.children, display, true);
787
828
  hoistTabSections(root.children, tabPaths, display);
788
- return root.children.map((child) => toNavNode(child, display));
829
+ return root.children.map((child) =>
830
+ toNavNode(child, display, root.directory)
831
+ );
789
832
  };
790
833
 
791
834
  /**
@@ -912,16 +955,24 @@ const buildConfigSidebar = (
912
955
  items: SidebarItemConfig[],
913
956
  byRoute: Map<string, PageRecord>,
914
957
  display: SidebarDisplay,
915
- basePath: string
958
+ basePath: string,
959
+ inheritedDirectory?: DirectoryMode
916
960
  ): NavNode[] => {
917
961
  const nodes: NavNode[] = [];
918
962
  for (const item of items) {
919
963
  if (!isPageRef(item) && item.items) {
964
+ const directory = item.directory ?? inheritedDirectory;
920
965
  nodes.push({
921
966
  badge: item.badge,
922
- children: buildConfigSidebar(item.items, byRoute, display, basePath),
967
+ children: buildConfigSidebar(
968
+ item.items,
969
+ byRoute,
970
+ display,
971
+ basePath,
972
+ directory
973
+ ),
923
974
  collapsed: item.collapsed,
924
- directory: item.directory,
975
+ directory,
925
976
  display: item.display ?? display,
926
977
  icon: item.icon,
927
978
  kind: "group",
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Page layout modes (`mode` frontmatter), Mintlify's names for what a page
3
+ * shows around its content:
4
+ *
5
+ * - `default`: the sidebar, the page's title and table of contents, and the
6
+ * page-end links, around the prose column.
7
+ * - `wide`: no table of contents; the content takes its width.
8
+ * - `center`: no sidebar or table of contents; a wider column, centered.
9
+ * For changelogs and other pages read top to bottom.
10
+ * - `custom`: only the header. No sidebar, title, breadcrumbs, table of
11
+ * contents, page-end links, or site footer, and the content spans the
12
+ * page, for a landing page written in MDX.
13
+ * - `frame`: `custom`, keeping the sidebar.
14
+ *
15
+ * Mintlify's `assistant` mode, a full-page chat, has no equivalent.
16
+ */
17
+ export const PAGE_MODES = [
18
+ "default",
19
+ "wide",
20
+ "center",
21
+ "custom",
22
+ "frame",
23
+ ] as const;
24
+
25
+ export type PageMode = (typeof PAGE_MODES)[number];
26
+
27
+ /** What a page shows around its content, and how wide the content runs. */
28
+ export interface PageModeLayout {
29
+ /** The page's own chrome: title, description, breadcrumbs, page-end links, and the site footer. */
30
+ chrome: boolean;
31
+ sidebar: boolean;
32
+ toc: boolean;
33
+ /** `content` is the prose measure, `center` a wider centered column, `full` the whole column. */
34
+ width: "content" | "center" | "full";
35
+ }
36
+
37
+ /** The layout a mode gives a page. */
38
+ export const pageModeLayout = (mode: PageMode = "default"): PageModeLayout => {
39
+ switch (mode) {
40
+ case "wide": {
41
+ return { chrome: true, sidebar: true, toc: false, width: "full" };
42
+ }
43
+ case "center": {
44
+ return { chrome: true, sidebar: false, toc: false, width: "center" };
45
+ }
46
+ case "custom": {
47
+ return { chrome: false, sidebar: false, toc: false, width: "full" };
48
+ }
49
+ case "frame": {
50
+ return { chrome: false, sidebar: true, toc: false, width: "full" };
51
+ }
52
+ default: {
53
+ return { chrome: true, sidebar: true, toc: true, width: "content" };
54
+ }
55
+ }
56
+ };
@@ -1,8 +1,10 @@
1
1
  import { isAbsolute, relative } from "pathe";
2
2
 
3
+ import { withBasePath } from "./base-path.ts";
3
4
  import { CHANGELOG_INDEX_ROUTE, hasChangelogIndex } from "./changelog-index.ts";
4
5
  import { loadConfig } from "./config.ts";
5
6
  import { customStaticRoutes, discoverPages } from "./custom-pages.ts";
7
+ import { unknownDirectiveDiagnostics } from "./directive-diagnostics.ts";
6
8
  import { buildContentGraph } from "./graph.ts";
7
9
  import { i18nDiagnostics } from "./i18n.ts";
8
10
  import { expandIncludes, hasIncludeStatements } from "./includes.ts";
@@ -10,16 +12,22 @@ import {
10
12
  gitLastModifiedTimes,
11
13
  gitRepositoryRoot,
12
14
  lastModifiedShallowWarning,
15
+ realPath,
13
16
  resolveLastModifiedConfig,
14
17
  } from "./last-modified.ts";
15
18
  import { buildManifest } from "./manifest.ts";
16
19
  import { discoverFolderMeta } from "./meta.ts";
17
20
  import type { FolderMetaSource } from "./meta.ts";
18
21
  import { resolveProjectContext } from "./project.ts";
22
+ import { pathsUnderPattern } from "./redirect-patterns.ts";
19
23
  import type { ResolvedConfig } from "./schema.ts";
20
24
  import { normalizeEntry, strippedLineOffset } from "./sources/normalize.ts";
21
25
  import { resolveDocsCollection, resolveSources } from "./sources/resolve.ts";
22
- import type { ContentSource, SourceLoadResult } from "./sources/types.ts";
26
+ import type {
27
+ ContentSource,
28
+ SourceEntry,
29
+ SourceLoadResult,
30
+ } from "./sources/types.ts";
23
31
  import type {
24
32
  BlumeManifest,
25
33
  ContentGraph,
@@ -28,6 +36,12 @@ import type {
28
36
  PageRecord,
29
37
  ProjectContext,
30
38
  } from "./types.ts";
39
+ import {
40
+ hasVariables,
41
+ substituteVariables,
42
+ undefinedVariables,
43
+ } from "./variables.ts";
44
+ import type { ContentVariables } from "./variables.ts";
31
45
  import { versionsDiagnostics } from "./versions.ts";
32
46
 
33
47
  /** Build mode: drafts are kept in `dev` and dropped in `build`. */
@@ -196,11 +210,34 @@ const normalizeLoadedEntries = (
196
210
  }
197
211
  pages.push(...normalized.pages);
198
212
  allDiagnostics.push(...normalized.diagnostics);
213
+ if (normalized.pages.length > 0) {
214
+ allDiagnostics.push(...unknownDirectiveDiagnostics(entry, source.name));
215
+ }
199
216
  }
200
217
  }
201
218
  return { diagnostics: allDiagnostics, droppedPages, pages };
202
219
  };
203
220
 
221
+ /**
222
+ * The local sources' on-disk roots a `git log` pathspec can cover: those
223
+ * inside the repository at `gitRoot`. A root outside it would fail the log
224
+ * outright and can never yield dates. Compared by real path, since git spells
225
+ * its toplevel with every symlink resolved (`/private/tmp/site` for a project
226
+ * opened as `/tmp/site`); the roots themselves keep the project's spelling.
227
+ */
228
+ const gitContentRoots = (
229
+ sources: readonly ContentSource[],
230
+ gitRoot: string | null
231
+ ): string[] => {
232
+ if (gitRoot === null) {
233
+ return [];
234
+ }
235
+ const top = realPath(gitRoot);
236
+ return sources
237
+ .flatMap((source) => (source.contentRoot ? [source.contentRoot] : []))
238
+ .filter((dir) => isWithin(top, realPath(dir)));
239
+ };
240
+
204
241
  /**
205
242
  * Run the full core pipeline for a project root: load config, resolve paths,
206
243
  * discover content and folder meta, build the graph, and assemble the manifest.
@@ -221,6 +258,85 @@ const isBannerShorthand = (
221
258
  banner: NonNullable<ResolvedConfig["banner"]>
222
259
  ): banner is string => typeof banner === "string";
223
260
 
261
+ /**
262
+ * Report an entry's undefined content variables at their source lines, then
263
+ * replace the defined ones in the text the scan extracts from: the expanded
264
+ * text when includes were spliced (its `origins` map lines back to the file
265
+ * each came from), else the body.
266
+ */
267
+ const substituteEntryVariables = (
268
+ entry: SourceEntry,
269
+ variables: ContentVariables
270
+ ): Diagnostic[] => {
271
+ const { expanded } = entry;
272
+ const text = expanded?.text ?? entry.body.text;
273
+ const offset = expanded ? 0 : strippedLineOffset(entry.raw, entry.body.text);
274
+ const diagnostics = undefinedVariables(text, variables).map(
275
+ ({ line, name }): Diagnostic => {
276
+ const origin = expanded?.origins[line - 1];
277
+ return {
278
+ code: "BLUME_UNDEFINED_VARIABLE",
279
+ file: origin?.file ?? entry.sourcePath,
280
+ line: origin?.line ?? line + offset,
281
+ message: `{{${name}}} isn't a defined variable, so it would show as written.`,
282
+ severity: "error",
283
+ suggestion: `Define "${name}" under variables in blume.config.ts, or put it in inline code to show it as written.`,
284
+ };
285
+ }
286
+ );
287
+ if (expanded) {
288
+ expanded.text = substituteVariables(expanded.text, variables);
289
+ } else {
290
+ entry.body.text = substituteVariables(entry.body.text, variables);
291
+ }
292
+ return diagnostics;
293
+ };
294
+
295
+ /** {@link substituteEntryVariables} over every loaded entry, when any are defined. */
296
+ const substituteLoadedVariables = (
297
+ loaded: readonly SourceLoadResult[],
298
+ variables: ContentVariables
299
+ ): Diagnostic[] =>
300
+ hasVariables(variables)
301
+ ? loaded.flatMap(({ entries }) =>
302
+ entries.flatMap((entry) => substituteEntryVariables(entry, variables))
303
+ )
304
+ : [];
305
+
306
+ /**
307
+ * A pattern redirect that also matches a page. Hosts disagree on which
308
+ * answers (Vercel and Cloudflare apply the rule ahead of the page, Netlify
309
+ * and the Node server serve the page), so the pattern is rejected rather than
310
+ * let the answer depend on the host. `pages` are the served page paths, which
311
+ * carry `basePath`, as the based `from` does.
312
+ */
313
+ const redirectPageDiagnostics = (
314
+ config: ResolvedConfig,
315
+ pages: readonly string[],
316
+ configFile: string | null
317
+ ): Diagnostic[] =>
318
+ config.redirects.flatMap((redirect) => {
319
+ const matched = pathsUnderPattern(
320
+ withBasePath(config.basePath, redirect.from),
321
+ pages
322
+ );
323
+ const [first] = matched;
324
+ if (first === undefined) {
325
+ return [];
326
+ }
327
+ const more = matched.length > 1 ? ` and ${matched.length - 1} more` : "";
328
+ return [
329
+ {
330
+ code: "BLUME_REDIRECT_MATCHES_PAGE",
331
+ file: configFile ?? undefined,
332
+ message: `The redirect from ${redirect.from} also matches the page ${first}${more}, which some hosts would redirect and others would serve.`,
333
+ severity: "error",
334
+ suggestion:
335
+ "Narrow the pattern to the paths that moved, or move the pages out from under it.",
336
+ } satisfies Diagnostic,
337
+ ];
338
+ });
339
+
224
340
  /** The configured banner link target, when the banner has a link. */
225
341
  const bannerLinkHref = (
226
342
  banner: ResolvedConfig["banner"]
@@ -327,6 +443,14 @@ export const scanProject = async (
327
443
  })
328
444
  );
329
445
 
446
+ // Content variables: an undefined name in prose is an error at its source
447
+ // line, and every defined one is replaced in the text headings, links, and
448
+ // components are extracted from, so they match the rendered page.
449
+ const variableDiagnostics = substituteLoadedVariables(
450
+ loaded,
451
+ config.variables
452
+ );
453
+
330
454
  // Folder meta contributed by the sources themselves (the OpenAPI source
331
455
  // labels each tag directory with the spec's own tag name). It applies to
332
456
  // every locale, so it merges into the shared map — beneath user-authored
@@ -362,12 +486,9 @@ export const scanProject = async (
362
486
  // The git pathspecs must cover where the pages actually live: each local
363
487
  // source's own on-disk root — a filesystem source's `root`, or a staged
364
488
  // local source's tree (an Obsidian vault) — which diverges from the global
365
- // `content.root`. A root outside the repository would fail the log outright
366
- // and can never yield dates, so those are dropped up front.
489
+ // `content.root`.
367
490
  const gitRoot = gitRepositoryRoot(context.root);
368
- const contentRoots = sources
369
- .flatMap((source) => (source.contentRoot ? [source.contentRoot] : []))
370
- .filter((dir) => gitRoot !== null && isWithin(gitRoot, dir));
491
+ const contentRoots = gitContentRoots(sources, gitRoot);
371
492
  const gitTimes = gitLastModifiedTimes(
372
493
  context.root,
373
494
  contentRoots,
@@ -402,14 +523,15 @@ export const scanProject = async (
402
523
  const customPages = context.pagesRoot
403
524
  ? await discoverPages(context.pagesRoot)
404
525
  : [];
526
+ const extraRoutes = new Set([
527
+ ...customStaticRoutes(customPages),
528
+ ...(hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []),
529
+ ]);
405
530
  const graph = buildContentGraph(pages, {
406
531
  bannerHref: bannerLinkHref(config.banner),
407
532
  basePath: config.basePath,
408
533
  brandHref: logoHref(config.logo),
409
- extraRoutes: new Set([
410
- ...customStaticRoutes(customPages),
411
- ...(hasChangelogIndex(pages, config) ? [CHANGELOG_INDEX_ROUTE] : []),
412
- ]),
534
+ extraRoutes,
413
535
  folderMeta: folderMeta.meta,
414
536
  i18n: config.i18n,
415
537
  navigation: config.navigation,
@@ -431,6 +553,7 @@ export const scanProject = async (
431
553
  diagnostics: [
432
554
  ...contentDiagnostics,
433
555
  ...includeDiagnostics,
556
+ ...variableDiagnostics,
434
557
  ...folderMeta.diagnostics,
435
558
  ...entryIdDiagnostics(
436
559
  pages,
@@ -440,6 +563,11 @@ export const scanProject = async (
440
563
  ...i18nWarnings,
441
564
  ...versionWarnings,
442
565
  ...lastModifiedWarnings,
566
+ ...redirectPageDiagnostics(
567
+ config,
568
+ [...manifest.routes.map((route) => route.path), ...extraRoutes],
569
+ context.configFile
570
+ ),
443
571
  ],
444
572
  droppedPages,
445
573
  graph,