blume 2.0.3 → 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 (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
@@ -2,15 +2,20 @@ import type { AstroIntegration } from "astro";
2
2
  import type { z } from "zod";
3
3
 
4
4
  import type { AskRetrievalOptions } from "../ai/ask-context.ts";
5
- import type { AssistantAdapter } from "../ai/ask.ts";
5
+ import type { AssistantAdapter, AssistantGatewayAdapter } from "../ai/ask.ts";
6
6
  import type { ComponentMarkdown } from "../ai/component-markdown.ts";
7
7
  import type { AnalyticsAdapter } from "../analytics/schema.ts";
8
+ import type { CaptchaAdapter } from "../captcha/schema.ts";
9
+ import type { ConsentAdapter } from "../consent/schema.ts";
8
10
  import type { DeploymentInput } from "../deploy/adapters/registry.ts";
9
11
  import type { CodeTheme } from "../markdown/themes.ts";
12
+ import type { RateLimitAdapter } from "../ratelimit/schema.ts";
13
+ import type { PlaygroundOptions } from "../reference/options.ts";
10
14
  import type { ReferenceAdapter } from "../reference/schema.ts";
11
15
  import type { AnySearchAdapter } from "../search/adapters/registry.ts";
12
16
  import type { SourceAdapterInput } from "../sources/registry.ts";
13
17
  import type { FontSlug } from "../theme/fonts.ts";
18
+ import type { FooterSocial } from "./footer.ts";
14
19
  import type {
15
20
  blumeConfigSchema,
16
21
  OpenInChatProvider,
@@ -92,21 +97,64 @@ export type LogoConfig =
92
97
  export type BannerConfig =
93
98
  | string
94
99
  | {
95
- /** The banner message. */
96
- content: string;
100
+ /** The banner message, or a map of locale code to message. */
101
+ content: LocalizableLabel;
97
102
  /** Show a dismiss button; the choice is remembered per visitor. */
98
103
  dismissible?: boolean;
99
- /** Stable key for remembering dismissal; defaults to the content. */
104
+ /**
105
+ * Stable key for remembering dismissal; defaults to the content (the
106
+ * default locale's, for a per-locale map).
107
+ */
100
108
  id?: string;
101
109
  /** An optional call-to-action link. */
102
110
  link?: {
103
111
  /** Link target (internal route or external URL). */
104
112
  href: string;
105
- /** Link text. */
106
- text: string;
113
+ /** Link text, or a map of locale code to text. */
114
+ text: LocalizableLabel;
107
115
  };
108
116
  };
109
117
 
118
+ /** Defaults for hand-written endpoint pages, the ones with `api` frontmatter. */
119
+ export interface ApiConfig {
120
+ /** How requests authenticate, unless a page sets `authMethod`. Defaults to none. */
121
+ auth?: {
122
+ method: "bearer" | "basic" | "key" | "none";
123
+ /** The header an API key goes in. Defaults to `x-api-key`. */
124
+ name?: string;
125
+ };
126
+ /**
127
+ * The Try it panel: `false` hides it, and `{ proxy }` sends requests through
128
+ * a CORS proxy, a URL or `true` for the built-in `/_api-proxy` route (server
129
+ * output only). Defaults to `true`, sending directly.
130
+ */
131
+ playground?: PlaygroundOptions;
132
+ /** The base URL an `api` path joins, like `https://api.acme.com/v1`. */
133
+ server?: string;
134
+ }
135
+
136
+ /** A link in the footer's row. */
137
+ export interface FooterLink {
138
+ /** Link target (internal route or external URL). An external href opens in a new tab. */
139
+ href: string;
140
+ /** Link text, or a map of locale code to text. */
141
+ label: LocalizableLabel;
142
+ }
143
+
144
+ /**
145
+ * The site footer, below the content on every page: a row of links on one
146
+ * side, and social profile icons on the other.
147
+ */
148
+ export interface FooterConfig {
149
+ /** Links in one row, left to right. */
150
+ links?: FooterLink[];
151
+ /**
152
+ * Social profiles, platform to URL, shown as icons in the order written:
153
+ * `{ github: "https://github.com/acme", x: "https://x.com/acme" }`.
154
+ */
155
+ socials?: Partial<Record<FooterSocial, string>>;
156
+ }
157
+
110
158
  /**
111
159
  * Where content lives and how it's discovered. `root`/`include`/`exclude` are
112
160
  * zero-config shorthand for a single `filesystem()` source; set `sources` to
@@ -203,7 +251,8 @@ export interface ContentTypeConfig {
203
251
  // ---------------------------------------------------------------------------
204
252
 
205
253
  /**
206
- * A header label, optionally per locale: a plain string, or a map of locale
254
+ * A label, optionally per locale (header tabs and links, the banner, the
255
+ * footer): a plain string, or a map of locale
207
256
  * code to label (`{ en: "Docs", ja: "ドキュメント" }`). The active locale's
208
257
  * entry wins, then the default locale's, then the map's first entry.
209
258
  */
@@ -286,8 +335,8 @@ export interface FeaturedLink {
286
335
  href: string;
287
336
  /** Lucide icon name shown beside the label. */
288
337
  icon?: string;
289
- /** Link label. */
290
- label: string;
338
+ /** Link label, or a map of locale code to label. */
339
+ label: LocalizableLabel;
291
340
  }
292
341
 
293
342
  /**
@@ -312,8 +361,8 @@ export type SidebarConfig =
312
361
  export interface HeaderAction {
313
362
  /** Link target. An external href opens in a new tab. */
314
363
  href: string;
315
- /** Link label. */
316
- label: string;
364
+ /** Link label, or a map of locale code to label. */
365
+ label: LocalizableLabel;
317
366
  }
318
367
 
319
368
  /** Header, sidebar, tabs, and switcher configuration. */
@@ -325,7 +374,7 @@ export interface NavigationConfig {
325
374
  /** Pinned links shown above the generated sidebar sections. */
326
375
  featured?: FeaturedLink[];
327
376
  /**
328
- * The GitHub link in the header. `true` derives it from `github` (the
377
+ * The GitHub link in the footer. `true` derives it from `github` (the
329
378
  * default), `false` hides it, and an absolute URL points it anywhere on
330
379
  * GitHub — an organization, say, when the docs repo itself is private.
331
380
  */
@@ -520,6 +569,13 @@ export interface AssistantRetrievalConfig {
520
569
 
521
570
  /** The chat assistant. */
522
571
  export interface AssistantConfig {
572
+ /**
573
+ * A bot check from `blume/captcha` — `turnstile({ siteKey })` or
574
+ * `hcaptcha({ siteKey })`. The panel sends a token with each question and
575
+ * the generated route verifies it (with `TURNSTILE_SECRET_KEY` or
576
+ * `HCAPTCHA_SECRET_KEY`) before the model runs.
577
+ */
578
+ captcha?: CaptchaAdapter;
523
579
  /**
524
580
  * Origins allowed to call the generated endpoint from another site — a
525
581
  * marketing page that embeds an ask box, for example — or `"*"` to allow
@@ -548,10 +604,10 @@ export interface AssistantConfig {
548
604
  instructions?: string;
549
605
  /**
550
606
  * Which backend answers: the descriptor an adapter factory returns —
551
- * `gateway({ model })`, `openrouter({ model, reasoning })`,
552
- * `llmgateway({ model })`, `inkeep({ model })`, or
553
- * `openaiCompatible({ baseUrl, name, model, apiKeyEnv })`, all exported
554
- * from `blume/ai`. Each adapter owns its model, key env var, reasoning
607
+ * `openai({ model, baseUrl })`, `anthropic({ model })`,
608
+ * `gemini({ model })`, `grok({ model })`, `gateway({ model })`,
609
+ * `openrouter({ model, reasoning })`, `llmgateway({ model })`, or
610
+ * `inkeep({ model })`, all exported from `blume/ai`. Each adapter owns its model, key env var, reasoning
555
611
  * mapping, and `providerOptions` passthrough. Defaults to
556
612
  * `gateway({ model: "openai/gpt-5.5" })`.
557
613
  */
@@ -562,8 +618,23 @@ export interface AssistantConfig {
562
618
  * backend — at the cost of recall. Defaults keep the built-in behavior.
563
619
  */
564
620
  retrieval?: AssistantRetrievalConfig;
621
+ /**
622
+ * A Contact support link in the panel once there's a conversation: a
623
+ * `mailto:` address starts an email with the conversation as its body; a
624
+ * URL or root-relative path gets the conversation's `thread` id, which the
625
+ * assistant's analytics events carry too.
626
+ */
627
+ support?: string;
565
628
  /** Starter prompts shown before the first question. */
566
629
  suggestions?: AssistantSuggestion[];
630
+ /**
631
+ * Let the model search the docs and read whole pages itself, over several
632
+ * steps, instead of answering only from the excerpts retrieved up front.
633
+ * Defaults to `true`, except for `openai()` with a `baseUrl`, whose model
634
+ * may not support tool calling. `inkeep()` does its own retrieval and
635
+ * ignores it.
636
+ */
637
+ tools?: boolean;
567
638
  }
568
639
 
569
640
  /** What the AI Catalog (ARD) manifest carries. */
@@ -693,6 +764,12 @@ export interface I18nConfig {
693
764
  locales: LocaleConfigInput[];
694
765
  /** `dir`: locale directories (`fr/page.mdx`). `dot`: filename suffix (`page.fr.mdx`). */
695
766
  parser?: "dir" | "dot";
767
+ /**
768
+ * Send a visitor who lands on the default language's home page to the home
769
+ * page in their browser's preferred language, until they pick a language
770
+ * with the switcher. Defaults to `false`.
771
+ */
772
+ routeByBrowserLanguage?: boolean;
696
773
  /**
697
774
  * Per-locale UI string overrides, e.g.
698
775
  * `{ fr: { search: { button: "Rechercher" } } }`.
@@ -802,11 +879,19 @@ type RedirectStatus = RedirectStatusPermanent | RedirectStatusTemporary;
802
879
 
803
880
  /** A URL redirect rule. */
804
881
  export interface RedirectConfig {
805
- /** Path to redirect from. */
882
+ /**
883
+ * Path to redirect from: an exact path, or a pattern. `:name` matches one
884
+ * segment, `:name*` as the last segment the rest of the path
885
+ * (`/beta/:slug*`), and `*` ending a segment the rest from there
886
+ * (`/old/article-*`).
887
+ */
806
888
  from: string;
807
889
  /** HTTP status. Defaults to `301`. */
808
890
  status?: RedirectStatus;
809
- /** Path or URL to redirect to. */
891
+ /**
892
+ * Path or URL to redirect to. Reads `from`'s captures as `/:name` (or `*`
893
+ * for what `*` matched): `/v2/:slug*`.
894
+ */
810
895
  to: string;
811
896
  }
812
897
 
@@ -890,6 +975,13 @@ export interface AgentsConfig {
890
975
  markdownComponents?: Record<string, ComponentMarkdown>;
891
976
  /** Expose the docs as an MCP server for agents. */
892
977
  mcp?: McpConfig;
978
+ /**
979
+ * Generate the site's own agent skill: a `SKILL.md` named after the site,
980
+ * built from its navigation and page descriptions, served at `/skill.md`
981
+ * and in the skills discovery index. Needs `deployment.site`. A skill in
982
+ * `agents.skills` with the same name replaces it. Defaults to `true`.
983
+ */
984
+ skillMd?: boolean;
893
985
  /**
894
986
  * Publish Agent Skills for discovery: a directory (resolved against the
895
987
  * project root) whose subdirectories each hold a `SKILL.md`. Skills are
@@ -1070,6 +1162,23 @@ export interface SoftwareConfig {
1070
1162
 
1071
1163
  /** Discoverability: OG images, feeds, sitemap, robots, and structured data. */
1072
1164
  export interface SeoConfig {
1165
+ /**
1166
+ * Meta tags written into every page's head, name to content: site
1167
+ * verification tokens, `theme-color`, an app banner, and anything else
1168
+ * Blume has no setting for. `og:*`, `fb:*`, and `article:*` tags render
1169
+ * with `property`, the rest with `name`. A tag Blume writes itself
1170
+ * (`description`, `robots`, `og:image`, `twitter:card`, …) is refused.
1171
+ *
1172
+ * ```ts
1173
+ * seo: {
1174
+ * metatags: {
1175
+ * "google-site-verification": "abc123",
1176
+ * "theme-color": "#0b5fff",
1177
+ * },
1178
+ * }
1179
+ * ```
1180
+ */
1181
+ metatags?: Record<string, string>;
1073
1182
  /** Per-page Open Graph image generation. */
1074
1183
  og?: OgConfig;
1075
1184
  /**
@@ -1241,6 +1350,37 @@ export type ExportConfig =
1241
1350
  pdf?: boolean;
1242
1351
  };
1243
1352
 
1353
+ /** Page feedback: the rating, and an optional written comment after it. */
1354
+ export interface FeedbackConfig {
1355
+ /**
1356
+ * After the rating, offer a box to say more. The comment is sent as a
1357
+ * `feedback_comment` event through every analytics adapter with an event
1358
+ * API. Defaults to `false`.
1359
+ */
1360
+ comments?: boolean;
1361
+ /** Show the rating. Defaults to `true`. */
1362
+ enabled?: boolean;
1363
+ }
1364
+
1365
+ /**
1366
+ * "Listen to this page". `true` reads pages aloud with the reader's browser
1367
+ * voices (no key, any host). The object form's `provider` generates neural
1368
+ * audio at build instead, one cached clip per sentence, and falls back to
1369
+ * browser voices where no clips exist. Defaults to `false`.
1370
+ */
1371
+ export type NarrationConfig =
1372
+ | boolean
1373
+ | {
1374
+ /** Show the player. Defaults to `true` in the object form. */
1375
+ enabled?: boolean;
1376
+ /**
1377
+ * Generate audio at build with a speech model:
1378
+ * `gateway({ model: "openai/tts-1-hd", voice: "alloy" })` from
1379
+ * `blume/ai`. Without it, pages are read with browser voices.
1380
+ */
1381
+ provider?: AssistantGatewayAdapter;
1382
+ };
1383
+
1244
1384
  /**
1245
1385
  * Opt-in custom frontmatter keys. Page frontmatter is strictly validated —
1246
1386
  * an unknown key fails the build so typos are caught — and `extend` carves
@@ -1335,6 +1475,8 @@ export interface BlumeConfig {
1335
1475
  agents?: AgentsConfig;
1336
1476
  /** Model-facing features: the assistant and the "Open in chat" action. */
1337
1477
  ai?: AiConfig;
1478
+ /** Defaults for hand-written endpoint pages (`api` frontmatter). */
1479
+ api?: ApiConfig;
1338
1480
  /**
1339
1481
  * Analytics adapters from `blume/analytics`, emitted into `<head>` of every
1340
1482
  * production page in this order: `[posthog({ key }), vercel(),
@@ -1353,6 +1495,14 @@ export interface BlumeConfig {
1353
1495
  * compose: with both set, a page lands at `{deployment.base}/{basePath}/page`.
1354
1496
  */
1355
1497
  basePath?: string;
1498
+ /**
1499
+ * Ask readers before analytics runs, with an adapter from `blume/consent`:
1500
+ * `native()` for Blume's own banner, or a hosted consent manager
1501
+ * (`osano({ customerId, configId })`, `ethyca({ privacyCenter })`). Every
1502
+ * `analytics` adapter waits until the reader allows analytics. Unset runs
1503
+ * analytics as it loads.
1504
+ */
1505
+ consent?: ConsentAdapter;
1356
1506
  /** Where content lives and how it's discovered. */
1357
1507
  content?: ContentConfig;
1358
1508
  /**
@@ -1376,8 +1526,17 @@ export interface BlumeConfig {
1376
1526
  examples?: string | ExamplesConfig;
1377
1527
  /** Reader-facing PDF/EPUB export actions. Defaults to `false`. */
1378
1528
  export?: ExportConfig;
1379
- /** Show the per-page "Was this helpful?" widget. Defaults to `true`. */
1380
- feedback?: boolean;
1529
+ /**
1530
+ * The "Was this page helpful?" rating at the foot of each page. On by
1531
+ * default; `false` hides it. `{ comments: true }` also asks for a written
1532
+ * comment after the rating, sent through your analytics adapters.
1533
+ */
1534
+ feedback?: boolean | FeedbackConfig;
1535
+ /**
1536
+ * The site footer: social profile icons and up to four link columns. Unset,
1537
+ * the site has no footer.
1538
+ */
1539
+ footer?: FooterConfig;
1381
1540
  /** Opt-in custom frontmatter keys, validated by schemas you supply. */
1382
1541
  frontmatter?: FrontmatterConfig;
1383
1542
  /** Source repository (Edit-this-page links and the header repo link). */
@@ -1394,8 +1553,17 @@ export interface BlumeConfig {
1394
1553
  logo?: LogoConfig;
1395
1554
  /** Markdown / MDX rendering behavior. */
1396
1555
  markdown?: MarkdownConfig;
1556
+ /** "Listen to this page" narration. Defaults to `false`. */
1557
+ narration?: NarrationConfig;
1397
1558
  /** Header, sidebar, tabs, and switchers. */
1398
1559
  navigation?: NavigationConfig;
1560
+ /**
1561
+ * Limit how often one reader (by IP address) can call the server routes —
1562
+ * the assistant, the API playground proxy, server-side search — with an
1563
+ * adapter from `blume/ratelimit`: `memory()` (the default, 30 requests per
1564
+ * 10 minutes), `upstash()`, or `cloudflare()`. `false` turns it off.
1565
+ */
1566
+ rateLimit?: false | RateLimitAdapter;
1399
1567
  /** React island behavior (compiler auto-memoization). */
1400
1568
  react?: ReactConfig;
1401
1569
  /** URL redirect rules. */
@@ -1416,6 +1584,12 @@ export interface BlumeConfig {
1416
1584
  title?: string;
1417
1585
  /** On-page table of contents. Defaults to on (H2–H3). */
1418
1586
  toc?: TocConfig;
1587
+ /**
1588
+ * Content variables: `{{name}}` in a page reads the value, in prose, code,
1589
+ * and links alike. Names take letters, digits, `_`, and `-`; an undefined
1590
+ * name in prose fails the build. Defaults to none.
1591
+ */
1592
+ variables?: Record<string, string>;
1419
1593
  /** Docs versioning (opt-in frozen snapshots with a version switcher). */
1420
1594
  versions?: VersionsConfig;
1421
1595
  }
package/src/core/data.ts CHANGED
@@ -1,8 +1,10 @@
1
+ import type { CaptchaSettings } from "../captcha/schema.ts";
2
+ import type { EndpointAuth } from "../components/content/api-page.ts";
1
3
  import type { SearchAdapterKind } from "../search/adapters/registry.ts";
2
4
  import type { StructuredDataIdentity } from "../seo/jsonld.ts";
3
5
  import type { FontHead } from "../theme/fonts.ts";
4
6
  import type { UIStrings } from "./i18n-ui.ts";
5
- import type { ResolvedConfig } from "./schema.ts";
7
+ import type { LocalizableLabel, ResolvedConfig } from "./schema.ts";
6
8
  import type { Navigation, RouteAlternate, VersionAlternate } from "./types.ts";
7
9
 
8
10
  /**
@@ -42,10 +44,14 @@ export interface BlumeFavicon {
42
44
  dark?: { href: string; type?: string };
43
45
  }
44
46
 
45
- /** Announcement banner, normalized from its config (string shorthand or object). */
47
+ /**
48
+ * Announcement banner, normalized from its config (string shorthand or
49
+ * object). `content` and the link's `text` may be per-locale maps, resolved
50
+ * when the banner renders (see `core/localizable.ts`).
51
+ */
46
52
  export interface BlumeBanner {
47
- content: string;
48
- link?: { href: string; text: string };
53
+ content: LocalizableLabel;
54
+ link?: { href: string; text: LocalizableLabel };
49
55
  dismissible: boolean;
50
56
  /** Dismissal key: the configured id, else the content itself. */
51
57
  key: string;
@@ -71,6 +77,8 @@ export interface BlumeDataI18n {
71
77
  fallbackLocale: string | null;
72
78
  hideDefaultLocalePrefix: boolean;
73
79
  locales: BlumeDataLocale[];
80
+ /** Send visitors from the default home page to their browser's language. */
81
+ routeByBrowserLanguage: boolean;
74
82
  }
75
83
 
76
84
  /** A single content route, with the metadata custom pages can read. */
@@ -124,9 +132,23 @@ export interface BlumeDataConfig {
124
132
  appleIcon: BlumeFavicon | null;
125
133
  /** The assistant's empty-state suggestions, or `null` when the assistant is off. */
126
134
  assistant: {
135
+ /** The bot check's public settings, or `null` without one. */
136
+ captcha: CaptchaSettings | null;
127
137
  endpoint: string | null;
128
138
  suggestions: NonNullable<ResolvedConfig["ai"]["assistant"]>["suggestions"];
139
+ /** The Contact support link (`ai.assistant.support`), or `null`. */
140
+ support: string | null;
129
141
  } | null;
142
+ /**
143
+ * Defaults for hand-written endpoint pages (`api` frontmatter): the server a
144
+ * path joins, the default auth, and the playground, its `proxy` resolved to
145
+ * the URL sends go through (the built-in route under `basePath`) or `false`.
146
+ */
147
+ api: {
148
+ auth?: EndpointAuth;
149
+ playground: { enabled: boolean; proxy: string | false };
150
+ server?: string;
151
+ };
130
152
  banner: BlumeBanner | null;
131
153
  /** Site-wide route mount point, normalized to `""` or `/seg` (see config). */
132
154
  basePath: string;
@@ -134,6 +156,11 @@ export interface BlumeDataConfig {
134
156
  codeThemes: ResolvedConfig["markdown"]["code"]["theme"];
135
157
  /** `markdown.code.wrap`: wrap long code lines instead of scrolling. */
136
158
  codeWrap: boolean;
159
+ /**
160
+ * The consent adapter's descriptor, or `null` without one. When set, the
161
+ * layouts hold analytics until the reader allows it.
162
+ */
163
+ consent: ResolvedConfig["consent"];
137
164
  /** `dateFormat`: `Intl.DateTimeFormat` options for the date stamps. */
138
165
  dateFormat: ResolvedConfig["dateFormat"];
139
166
  description: string | undefined;
@@ -156,7 +183,12 @@ export interface BlumeDataConfig {
156
183
  sitemap: boolean;
157
184
  };
158
185
  favicon: BlumeFavicon;
186
+ /** Whether pages end with the "Was this page helpful?" rating. */
159
187
  feedback: boolean;
188
+ /** Whether the rating asks for a written comment after it (`feedback.comments`). */
189
+ feedbackComments: boolean;
190
+ /** `footer`: social profile icons and link columns, or `null` when unset. */
191
+ footer: ResolvedConfig["footer"] | null;
160
192
  /**
161
193
  * Repo coordinates for content components that address the API or build
162
194
  * their own repo links (`<GithubInfo>`, the OG card's footer slug), carrying
@@ -176,6 +208,14 @@ export interface BlumeDataConfig {
176
208
  logo: BlumeLogo | null;
177
209
  /** Hosted MCP server, or `null` when MCP is off. */
178
210
  mcp: { name: string; route: string } | null;
211
+ /** `seo.metatags`: the site's own tags for every page's head, or `null`. */
212
+ metatags: Record<string, string> | null;
213
+ /**
214
+ * "Listen to this page": `generated` when a `narration.provider` makes
215
+ * `blume build` write audio clips the player should look for, else the
216
+ * player reads with browser voices. `null` when narration is off.
217
+ */
218
+ narration: { generated: boolean } | null;
179
219
  /**
180
220
  * Open Graph image generation. Card fonts are baked into the generated OG
181
221
  * endpoint (they can carry absolute build-machine paths) and deliberately
@@ -24,6 +24,7 @@ const DOCS_BASE = "https://useblume.dev";
24
24
  const DOCS_DEPLOYMENT = "/docs/deployment";
25
25
  const DOCS_CLI_VALIDATE = "/docs/cli/validate";
26
26
  const DOCS_CONTENT_SOURCES = "/docs/content/sources";
27
+ const DOCS_CONTENT_SOURCES_OBSIDIAN = "/docs/content/sources/obsidian";
27
28
  const DOCS_CONTENT_NAVIGATION = "/docs/content/navigation";
28
29
  const DOCS_CONTENT_INCLUDES = "/docs/content/includes";
29
30
 
@@ -73,8 +74,8 @@ const DOCS_PATHS = new Map(
73
74
  BLUME_UNKNOWN_ICON: DOCS_CONTENT_NAVIGATION,
74
75
  BLUME_UNKNOWN_OPTION: "/docs/cli",
75
76
  BLUME_UNLOADABLE_FILE_NAME: "/docs/content",
76
- BLUME_WIKILINK_AMBIGUOUS: DOCS_CONTENT_SOURCES,
77
- BLUME_WIKILINK_UNRESOLVED: DOCS_CONTENT_SOURCES,
77
+ BLUME_WIKILINK_AMBIGUOUS: DOCS_CONTENT_SOURCES_OBSIDIAN,
78
+ BLUME_WIKILINK_UNRESOLVED: DOCS_CONTENT_SOURCES_OBSIDIAN,
78
79
  BLUME_YARN_PNP: "/docs/quickstart",
79
80
  })
80
81
  );
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The social platforms `footer.socials` links to, each shown by its brand
3
+ * icon: Mintlify's set, without its aliases (`twitter` and `x-twitter` are
4
+ * `x`, `earth-americas` is `website`).
5
+ */
6
+ export const FOOTER_SOCIALS = [
7
+ "bluesky",
8
+ "discord",
9
+ "facebook",
10
+ "github",
11
+ "hacker-news",
12
+ "instagram",
13
+ "linkedin",
14
+ "medium",
15
+ "podcast",
16
+ "reddit",
17
+ "slack",
18
+ "telegram",
19
+ "threads",
20
+ "website",
21
+ "x",
22
+ "youtube",
23
+ ] as const;
24
+
25
+ /** A platform `footer.socials` links to. */
26
+ export type FooterSocial = (typeof FOOTER_SOCIALS)[number];
package/src/core/graph.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  localizeRoute,
5
5
  resolveFallbackLocale,
6
6
  } from "./i18n.ts";
7
+ import { resolveLocalizable } from "./localizable.ts";
7
8
  import {
8
9
  validateNavIcons,
9
10
  validateNavStructure,
@@ -97,31 +98,6 @@ const localePagesFor = (
97
98
  return [...real, ...filled];
98
99
  };
99
100
 
100
- /**
101
- * Resolve a possibly-per-locale header label to the string a locale renders:
102
- * the active locale's entry, else the default locale's, else the map's first
103
- * entry (which is also what a single-locale site gets).
104
- */
105
- /** A label is either one string for every locale or a per-locale map. */
106
- const isSingleLabel = (label: LocalizableLabel): label is string =>
107
- typeof label === "string";
108
-
109
- const resolveLabel = (
110
- label: LocalizableLabel,
111
- locale: string,
112
- defaultLocale?: string
113
- ): string => {
114
- if (isSingleLabel(label)) {
115
- return label;
116
- }
117
- return (
118
- label[locale] ??
119
- (defaultLocale === undefined ? undefined : label[defaultLocale]) ??
120
- Object.values(label)[0] ??
121
- ""
122
- );
123
- };
124
-
125
101
  /**
126
102
  * An explicit sidebar with every item's `href` passed through `localize` (page
127
103
  * refs and `root`s resolve against each locale's own pages already).
@@ -154,11 +130,36 @@ const resolveTabLabels = (
154
130
  ...tab,
155
131
  items: tab.items?.map((item) => ({
156
132
  ...item,
157
- label: resolveLabel(item.label, locale, defaultLocale),
133
+ label: resolveLocalizable(item.label, locale, defaultLocale),
158
134
  })),
159
- label: resolveLabel(tab.label, locale, defaultLocale),
135
+ label: resolveLocalizable(tab.label, locale, defaultLocale),
160
136
  }));
161
137
 
138
+ /** A configured header or footer link with its label resolved for a locale. */
139
+ const withLabel = <T extends { label: LocalizableLabel }>(
140
+ link: T,
141
+ locale: string,
142
+ defaultLocale?: string
143
+ ): Omit<T, "label"> & { label: string } => ({
144
+ ...link,
145
+ label: resolveLocalizable(link.label, locale, defaultLocale),
146
+ });
147
+
148
+ /** The configured header links (plain, call to action, featured) in `locale`. */
149
+ const resolveHeaderLinks = (
150
+ navigation: BuildContentGraphOptions["navigation"],
151
+ locale: string,
152
+ defaultLocale?: string
153
+ ) => ({
154
+ actions: navigation.actions.map((link) =>
155
+ withLabel(link, locale, defaultLocale)
156
+ ),
157
+ cta: navigation.cta ? withLabel(navigation.cta, locale, defaultLocale) : null,
158
+ featured: navigation.featured.map((link) =>
159
+ withLabel(link, locale, defaultLocale)
160
+ ),
161
+ });
162
+
162
163
  /**
163
164
  * Mount a configured link authored as if the site were served at root (the
164
165
  * banner's): under `basePath`, except a route served outside the content tree
@@ -274,18 +275,22 @@ const buildLocaleNavigation = (
274
275
  ...item,
275
276
  href: localizeServed(item.href, false),
276
277
  });
277
- const { actions, cta, featured } = options.navigation;
278
+ const { actions, cta, featured } = resolveHeaderLinks(
279
+ options.navigation,
280
+ code,
281
+ i18n.defaultLocale
282
+ );
278
283
  const sidebarItems = options.navigation.sidebar.items;
279
284
 
280
285
  const navigation = buildNavigation(localePages, {
281
- actions: actions?.map(localizeHref),
286
+ actions: actions.map(localizeHref),
282
287
  basePath,
283
288
  cta: cta ? localizeHref(cta) : null,
284
289
  diagnostics,
285
290
  display: options.navigation.sidebar.display,
286
291
  extraRoutes: options.extraRoutes,
287
292
  fallbackMetaPrefix,
288
- featured: featured?.map(localizeHref),
293
+ featured: featured.map(localizeHref),
289
294
  folderMeta: options.folderMeta,
290
295
  // The localized tree root ("/" for the hidden default, "/fr" otherwise;
291
296
  // "/fr/v1.0" inside a snapshot): the tab pointing here spans the whole
@@ -415,13 +420,11 @@ const buildVersionNavigation = (
415
420
  }
416
421
  return {
417
422
  "": buildNavigation(versionPages, {
418
- actions: options.navigation.actions,
423
+ ...resolveHeaderLinks(options.navigation, ""),
419
424
  basePath: options.basePath ?? "",
420
- cta: options.navigation.cta,
421
425
  diagnostics,
422
426
  display: options.navigation.sidebar.display,
423
427
  extraRoutes: options.extraRoutes,
424
- featured: options.navigation.featured,
425
428
  folderMeta: options.folderMeta,
426
429
  localizedRoot: versionizeRoute("/", id),
427
430
  metaPrefix: id,
@@ -465,13 +468,13 @@ export const buildContentGraph = (
465
468
  ));
466
469
  } else {
467
470
  navigation = buildNavigation(currentPages, {
468
- actions: options.navigation.actions,
471
+ // No locale to prefer: a per-locale label map resolves to its first
472
+ // entry on a single-locale site.
473
+ ...resolveHeaderLinks(options.navigation, ""),
469
474
  basePath: options.basePath ?? "",
470
- cta: options.navigation.cta,
471
475
  diagnostics,
472
476
  display: options.navigation.sidebar.display,
473
477
  extraRoutes: options.extraRoutes,
474
- featured: options.navigation.featured,
475
478
  folderMeta: options.folderMeta,
476
479
  selectors: options.navigation.selectors,
477
480
  sharedFolderMeta: options.sharedFolderMeta,