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
@@ -4,19 +4,25 @@ import {
4
4
  resolveDocsCollection,
5
5
  TOC_HIDDEN_KEY,
6
6
  deployPassthrough,
7
+ RATE_LIMIT_BINDING,
7
8
  fontLocaleCodes,
8
9
  buildFontEntries,
10
+ isPatternPath,
11
+ compileRedirects,
12
+ hasVariables,
9
13
  SVG_ASSET_HEADERS,
10
14
  applyBaseToAstroRedirects,
11
15
  distDir,
12
16
  adapterRoot,
13
- deployPlatform,
14
- BLUME_IGNORE_DIRS
15
- } from "./chunk-v6ya5kcb.js";
17
+ deployPlatform
18
+ } from "./chunk-xkz5xbtv.js";
16
19
  import {
17
20
  trimChar,
18
21
  normalizeBasePath
19
- } from "./chunk-gs7r695n.js";
22
+ } from "./chunk-zccbjcj0.js";
23
+ import {
24
+ BLUME_IGNORE_DIRS
25
+ } from "./chunk-kwrg63tm.js";
20
26
 
21
27
  // src/deploy/adapter-output.ts
22
28
  import { existsSync } from "node:fs";
@@ -195,10 +201,12 @@ var publishRuntimeModules = (modules) => {
195
201
  return changed;
196
202
  }
197
203
  for (const server of servers) {
198
- for (const id of changed) {
199
- const mod = server.moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
200
- if (mod) {
201
- server.moduleGraph.invalidateModule(mod);
204
+ for (const { moduleGraph } of Object.values(server.environments)) {
205
+ for (const id of changed) {
206
+ const mod = moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
207
+ if (mod) {
208
+ moduleGraph.invalidateModule(mod);
209
+ }
202
210
  }
203
211
  }
204
212
  server.ws.send({ type: "full-reload" });
@@ -210,6 +218,19 @@ var publishRuntimeModules = (modules) => {
210
218
  import { existsSync as existsSync2, readFileSync } from "node:fs";
211
219
  import { pathToFileURL } from "node:url";
212
220
  import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3, relative as relative2 } from "pathe";
221
+
222
+ // src/consent/clients.ts
223
+ var CONSENT_CLIENT_MODULES = new Map([
224
+ ["native", "blume/components/layout/consent/native.ts"]
225
+ ]);
226
+
227
+ // src/markdown/api-rail.ts
228
+ var API_RAIL_KEY = "blumeApiRail";
229
+
230
+ // src/markdown/views.ts
231
+ var VIEWS_KEY = "blumeViews";
232
+
233
+ // src/astro/templates.ts
213
234
  var WORKSPACE_MARKERS = [
214
235
  ".git",
215
236
  "bun.lock",
@@ -286,7 +307,7 @@ var runtimeDependencies = (options) => {
286
307
  if (needsSvelte) {
287
308
  deps.push("@astrojs/svelte");
288
309
  }
289
- deps.push(...new Set(config.reference.flatMap((adapter) => adapter.runtimeDeps)), ...config.search.provider.runtimeDeps, ...config.analytics.flatMap((adapter) => adapter.runtimeDeps));
310
+ deps.push(...new Set(config.reference.flatMap((adapter) => adapter.runtimeDeps)), ...config.search.provider.runtimeDeps, ...config.analytics.flatMap((adapter) => adapter.runtimeDeps), ...config.consent?.runtimeDeps ?? []);
290
311
  for (const source of config.content.sources) {
291
312
  for (const dep of source.runtimeDeps) {
292
313
  if (!deps.includes(dep)) {
@@ -356,8 +377,16 @@ const blumeConfig = await loadBlumeConfig(resolve(dirname(fileURLToPath(import.m
356
377
  var renderImageOption = (config) => config.image.domains.length > 0 || config.image.remotePatterns.length > 0 ? `
357
378
  image: ${JSON.stringify(config.image)},` : "";
358
379
  var ALL_CLIENT_FEATURES = { epub: true, mermaid: true };
380
+ var SEARCH_CLIENT_DEPS = {
381
+ algolia: "algoliasearch/lite",
382
+ flexsearch: "flexsearch",
383
+ orama: "@orama/orama",
384
+ "orama-cloud": "@oramacloud/client",
385
+ typesense: "typesense"
386
+ };
359
387
  var resolveOptimizeDeps = (options) => {
360
388
  const { context, features } = options;
389
+ const searchDep = SEARCH_CLIENT_DEPS[options.searchKind];
361
390
  const optimizeDepsEntries = [
362
391
  ...context.pagesRoot ? [`${context.pagesRoot}/**/*.astro`] : [],
363
392
  `${context.root}/islands/**/*.{jsx,svelte,tsx,vue}`,
@@ -366,6 +395,7 @@ var resolveOptimizeDeps = (options) => {
366
395
  const optimizeDepsInclude = [
367
396
  ...features.mermaid ? ["blume > mermaid"] : [],
368
397
  ...features.epub ? ["blume > epub-gen-memory/bundle"] : [],
398
+ ...searchDep ? [`blume > ${searchDep}`] : [],
369
399
  ...options.needsReact && options.reactCompilerPath ? ["react/compiler-runtime"] : []
370
400
  ];
371
401
  return { optimizeDepsEntries, optimizeDepsInclude };
@@ -393,17 +423,24 @@ var renderRuntimeModuleWiring = (generatedModulesDir) => {
393
423
  ${JSON.stringify(id)}: ${configPath(`${generatedModulesDir}/${file}`, true)},`).join("");
394
424
  return { aliasLines, imports: [], pluginEntry: "" };
395
425
  };
396
- var blumeIntegrationOptions = (options) => options.ejected ? {
397
- buildArtifactsRoot: ".",
398
- contentRoutes: options.contentRoutes,
399
- homeLinkHeader: buildHomeLinkHeader(options.config, options.contentRoutes) ?? undefined,
400
- pages: options.pages
401
- } : { pages: options.pages };
426
+ var blumeIntegrationOptions = (options) => {
427
+ const shared = { pages: options.pages };
428
+ if (options.redirects.length > 0) {
429
+ shared.redirects = options.redirects;
430
+ }
431
+ return options.ejected ? {
432
+ ...shared,
433
+ buildArtifactsRoot: ".",
434
+ contentRoutes: options.contentRoutes,
435
+ homeLinkHeader: buildHomeLinkHeader(options.config, options.contentRoutes, "dev") ?? undefined
436
+ } : shared;
437
+ };
402
438
  var astroConfigTemplate = (options) => {
403
439
  const { context, config, needsReact, pages, themePath } = options;
404
440
  const { astro: cacheOptions, vite: viteCacheOption } = runtimeCacheOptions(context, options.generatedModulesDir);
405
441
  const {
406
442
  askPath,
443
+ consentClientPath,
407
444
  contentRoutes,
408
445
  examplesPath,
409
446
  examplesThemePath,
@@ -428,7 +465,8 @@ var astroConfigTemplate = (options) => {
428
465
  context,
429
466
  features,
430
467
  needsReact,
431
- reactCompilerPath: options.reactCompilerPath
468
+ reactCompilerPath: options.reactCompilerPath,
469
+ searchKind: config.search.provider.kind
432
470
  });
433
471
  const {
434
472
  configEntries: adapterConfigEntries,
@@ -448,9 +486,10 @@ var astroConfigTemplate = (options) => {
448
486
  prefixDefaultLocale: !config.i18n.hideDefaultLocalePrefix
449
487
  }
450
488
  })},` : "";
451
- const basedRedirects = applyBaseToAstroRedirects(config.redirects, config.basePath, deployment.options.base ?? "");
452
- const redirectsOption = basedRedirects.length > 0 ? `
453
- redirects: ${JSON.stringify(Object.fromEntries(basedRedirects.map((redirect) => [
489
+ const basedRedirects = applyBaseToAstroRedirects(config.redirects, config.basePath, deployment.options.base ?? "", new Set(contentRoutes));
490
+ const exactRedirects = basedRedirects.filter((redirect) => !isPatternPath(redirect.from));
491
+ const redirectsOption = exactRedirects.length > 0 ? `
492
+ redirects: ${JSON.stringify(Object.fromEntries(exactRedirects.map((redirect) => [
454
493
  redirect.from,
455
494
  { destination: redirect.to, status: redirect.status }
456
495
  ])))},` : "";
@@ -480,12 +519,15 @@ var astroConfigTemplate = (options) => {
480
519
  ` : "";
481
520
  const svelteImport = needsSvelte ? `import svelte from "@astrojs/svelte";
482
521
  ` : "";
522
+ const substitutesVariables = hasVariables(config.variables);
523
+ const variablesPluginEntry = substitutesVariables ? `variablesVitePlugin(${JSON.stringify(config.variables)}), ` : "";
483
524
  const blumeImports = [
484
525
  "blumeIntegration",
485
526
  "includeHmrPlugin",
486
527
  "prerenderDepsPlugin",
487
528
  ...runtimeModuleImports,
488
- ...adapterOption.includes("withAdapterRoot") ? ["withAdapterRoot"] : []
529
+ ...adapterOption.includes("withAdapterRoot") ? ["withAdapterRoot"] : [],
530
+ ...substitutesVariables ? ["variablesVitePlugin"] : []
489
531
  ];
490
532
  const blumeImport = `import { ${blumeImports.join(", ")} } from "blume/astro";
491
533
  `;
@@ -497,7 +539,8 @@ var astroConfigTemplate = (options) => {
497
539
  contentRoot: options.contentRoot,
498
540
  deployBase,
499
541
  externalLinks: config.markdown.externalLinks,
500
- headingAnchors: config.markdown.headingAnchors
542
+ headingAnchors: config.markdown.headingAnchors,
543
+ variables: config.variables
501
544
  });
502
545
  const processorOptions = options.generatedModulesDir === undefined ? processorLiteral : `{ ...${processorLiteral}, dataFile: ${configPath(`${options.generatedModulesDir}/data.json`, true)} }`;
503
546
  const integrations = [
@@ -516,7 +559,8 @@ var astroConfigTemplate = (options) => {
516
559
  config,
517
560
  contentRoutes,
518
561
  ejected,
519
- pages
562
+ pages,
563
+ redirects: compileRedirects(basedRedirects)
520
564
  }))})`);
521
565
  const {
522
566
  configSourceMarker,
@@ -572,7 +616,7 @@ ${userConfigSetup}export default defineConfig({
572
616
  // the overlap only adds memory.
573
617
  build: { concurrency: Math.min(8, availableParallelism()) },
574
618
  vite: {${viteCacheOption}
575
- plugins: [${runtimeModulesPluginEntry}tailwindcss(), includeHmrPlugin(${configPath(`${context.outDir}/src/generated/includes.json`, ejected)}), prerenderDepsPlugin()],
619
+ plugins: [${runtimeModulesPluginEntry}${variablesPluginEntry}tailwindcss(), includeHmrPlugin(${configPath(`${context.outDir}/src/generated/includes.json`, ejected)}), prerenderDepsPlugin()],
576
620
  build: {${features.mermaid ? MERMAID_CHUNK_LIMIT : ""}
577
621
  rolldownOptions: {
578
622
  ${ROLLDOWN_ON_LOG},
@@ -601,6 +645,10 @@ ${userConfigSetup}export default defineConfig({
601
645
  // \`/bundle\` subpath that is actually imported: optimizing the package
602
646
  // root leaves that entry out. Production (Rollup) already handles the
603
647
  // interop, so all of this only affects dev.
648
+ //
649
+ // The search adapter's client library rides the list for the same reason
650
+ // as the compiler runtime: the search dialog lazy-imports it on first
651
+ // open, and discovering it then re-optimizes and reloads the page.
604
652
  optimizeDeps: {
605
653
  entries: ${JSON.stringify(optimizeDepsEntries)},
606
654
  include: ${JSON.stringify(optimizeDepsInclude)},
@@ -624,6 +672,7 @@ ${userConfigSetup}export default defineConfig({
624
672
  resolve: {
625
673
  alias: {
626
674
  "blume:ask": ${configPath(askPath, ejected)},
675
+ "blume:consent-client": ${configPath(consentClientPath, ejected)},
627
676
  "blume:examples": ${configPath(examplesPath, ejected)},
628
677
  "blume:examples-theme": ${configPath(examplesThemePath, ejected)},
629
678
  "blume:features": ${configPath(featuresPath, ejected)},
@@ -703,18 +752,73 @@ var askCorsTemplate = (cors = []) => cors.length > 0 ? {
703
752
  imports: [
704
753
  'import { preflightResponse, withCors } from "blume/ai/cors.ts";'
705
754
  ],
706
- open: "withCors(ALLOWED_ORIGINS, async ({ request }) => {",
755
+ open: "withCors(ALLOWED_ORIGINS, async (context) => {",
707
756
  setup: `
708
757
  const ALLOWED_ORIGINS = ${JSON.stringify(cors)};
709
758
 
710
759
  export const OPTIONS: APIRoute = ({ request }) =>
711
760
  preflightResponse(request, ALLOWED_ORIGINS);
712
761
  `
713
- } : { close: "};", imports: [], open: "async ({ request }) => {", setup: "" };
762
+ } : { close: "};", imports: [], open: "async (context) => {", setup: "" };
763
+ var rateLimitTemplate = (adapter, scope) => {
764
+ if (!adapter) {
765
+ return { check: "", imports: [], setup: "" };
766
+ }
767
+ const imports = [
768
+ 'import { createLimiter, rateLimited } from "blume/ratelimit/runtime.ts";'
769
+ ];
770
+ let runtime = "";
771
+ if (adapter.kind === "upstash") {
772
+ imports.push('import { getSecret } from "astro:env/server";');
773
+ runtime = ", { secret: getSecret }";
774
+ } else if (adapter.kind === "cloudflare") {
775
+ imports.push("// @ts-ignore `cloudflare:workers` is typed once `wrangler types` has run.", 'import { env } from "cloudflare:workers";');
776
+ runtime = `, { binding: Reflect.get(env, ${JSON.stringify(RATE_LIMIT_BINDING)}) }`;
777
+ }
778
+ return {
779
+ check: ` const limited = await rateLimited(limiter, context, ${JSON.stringify(scope)});
780
+ if (limited) {
781
+ return limited;
782
+ }
783
+ `,
784
+ imports,
785
+ setup: `
786
+ const limiter = createLimiter(${JSON.stringify(adapter)}${runtime});
787
+ `
788
+ };
789
+ };
790
+ var askCaptchaTemplate = (adapter) => {
791
+ if (!adapter) {
792
+ return { check: "", imports: [], setup: "" };
793
+ }
794
+ const [secret = ""] = adapter.requiredSecrets;
795
+ return {
796
+ check: ` const captchaSecret = getSecret(${JSON.stringify(secret)});
797
+ if (!captchaSecret) {
798
+ return new Response(
799
+ ${JSON.stringify(`The assistant is not configured: set ${secret}.`)},
800
+ { status: 503 }
801
+ );
802
+ }
803
+ const captchaToken =
804
+ typeof body.captcha === "string" ? body.captcha : undefined;
805
+ if (!(await verifyCaptcha(CAPTCHA, captchaToken, context, { secret: captchaSecret }))) {
806
+ return new Response("Verification failed: the bot check didn't pass.", {
807
+ status: 403,
808
+ });
809
+ }
810
+ `,
811
+ imports: ['import { verifyCaptcha } from "blume/captcha/verify.ts";'],
812
+ setup: `
813
+ const CAPTCHA = ${JSON.stringify(adapter)};
814
+ `
815
+ };
816
+ };
714
817
  var ASK_BODY_LIMIT_BYTES = 65536;
715
818
  var askEndpointTemplate = (backend, options) => {
716
819
  const { instructions, retrieval } = options ?? {};
717
820
  const { grounded } = backend;
821
+ const tools = grounded && Boolean(options?.tools);
718
822
  const fallbackPrompt = instructions ? `${ASK_FALLBACK_PROMPT}
719
823
 
720
824
  ${instructions}` : ASK_FALLBACK_PROMPT;
@@ -734,14 +838,33 @@ ${instructions}` : ASK_FALLBACK_PROMPT;
734
838
  if (retrieval) {
735
839
  groundFields.push(`retrieval: ${JSON.stringify(retrieval)}`);
736
840
  }
841
+ if (tools) {
842
+ groundFields.push("tools: true");
843
+ }
737
844
  const groundOptions = groundFields.length > 0 ? `, { ${groundFields.join(", ")} }` : "";
738
845
  setup += `
739
846
  const ground = createAskContext(askData${groundOptions});
740
847
  `;
848
+ if (tools) {
849
+ imports.push('import { stepCountIs } from "ai";', 'import { ASK_MAX_STEPS, createAskTools } from "blume/ai/ask-tools.ts";');
850
+ setup += `const askTools = createAskTools(askData);
851
+ `;
852
+ }
741
853
  }
742
854
  const cors = askCorsTemplate(options?.cors);
743
855
  imports.push(...cors.imports);
744
- const validate = ` const text = await readCappedText(request, ${ASK_BODY_LIMIT_BYTES});
856
+ const limit = rateLimitTemplate(options?.rateLimit, "ask");
857
+ for (const line of limit.imports) {
858
+ if (!imports.includes(line)) {
859
+ imports.push(line);
860
+ }
861
+ }
862
+ setup += limit.setup;
863
+ const captcha = askCaptchaTemplate(options?.captcha);
864
+ imports.push(...captcha.imports);
865
+ setup += captcha.setup;
866
+ const validate = ` const { request } = context;
867
+ ${limit.check} const text = await readCappedText(request, ${ASK_BODY_LIMIT_BYTES});
745
868
  if (text === undefined) {
746
869
  return new Response("Request too large: the body must be at most 64 KB.", {
747
870
  status: 413,
@@ -786,12 +909,14 @@ const ground = createAskContext(askData${groundOptions});
786
909
  grounded ? "instructions" : `instructions:
787
910
  ${JSON.stringify(fallbackPrompt)}`,
788
911
  "messages",
912
+ ...tools ? ["tools: askTools(body.page)", "stopWhen: stepCountIs(ASK_MAX_STEPS)"] : [],
789
913
  ...backend.template.fields
790
914
  ];
791
915
  const call = ` const result = streamText({
792
916
  ${streamFields.join(`,
793
917
  `)},
794
918
  ${onError}
919
+ abortSignal: request.signal,
795
920
  });`;
796
921
  const stream = grounded ? ` const instructions =
797
922
  (await ground(messages, body.page)) ??
@@ -800,7 +925,7 @@ ${call}` : call;
800
925
  const handler = `export const POST: APIRoute = ${cors.open}
801
926
  ${validate}
802
927
  ${keyCheck}
803
- try {
928
+ ${captcha.check} try {
804
929
  ${stream}
805
930
  return createTextStreamResponse({
806
931
  stream: toTextStream({ stream: result.stream }),
@@ -827,9 +952,11 @@ const { strings } = Astro.props;
827
952
  ---
828
953
 
829
954
  <Assistant
955
+ captcha={data.config.assistant?.captcha ?? undefined}
830
956
  endpoint={data.config.assistant?.endpoint ?? undefined}
831
957
  strings={strings ?? data.ui.assistant}
832
958
  suggestions={data.config.assistant?.suggestions ?? []}
959
+ support={data.config.assistant?.support ?? undefined}
833
960
  />
834
961
  ` : `---
835
962
  // Generated by Blume. Do not edit.
@@ -877,6 +1004,25 @@ ${features.epub ? `export const loadEpub:
877
1004
  | (() => Promise<typeof import("epub-gen-memory/bundle")>)
878
1005
  | null = () => import("epub-gen-memory/bundle");` : "export const loadEpub: (() => Promise<unknown>) | null = null;"}
879
1006
  `;
1007
+ var consentClientTemplate = (consent) => {
1008
+ const module = consent ? CONSENT_CLIENT_MODULES.get(consent.kind) : undefined;
1009
+ if (!(consent && module)) {
1010
+ return `// Generated by Blume. Do not edit.
1011
+ // The consent adapter has no browser module, or consent is off.
1012
+ export const startConsentClient = (): void => {};
1013
+ `;
1014
+ }
1015
+ return `// Generated by Blume. Do not edit.
1016
+ // The \`${consent.kind}\` consent adapter's browser module, with its options.
1017
+ import { start } from ${JSON.stringify(module)};
1018
+
1019
+ export const startConsentClient = (
1020
+ consent: Parameters<typeof start>[0]
1021
+ ): void => {
1022
+ start(consent, ${JSON.stringify(consent.options)});
1023
+ };
1024
+ `;
1025
+ };
880
1026
  var searchClientTemplate = (config) => {
881
1027
  const { provider } = config.search;
882
1028
  switch (provider.mode) {
@@ -908,11 +1054,18 @@ export const createSearch = () => create({ url });
908
1054
  }
909
1055
  }
910
1056
  };
911
- var mixedbreadSearchEndpointTemplate = (options) => `// Generated by Blume. Do not edit.
912
- import type { APIRoute } from "astro";
913
- import { getSecret } from "astro:env/server";
914
- import Mixedbread from "@mixedbread/sdk";
915
- import { readCappedText } from "blume/core/request-body.ts";
1057
+ var mixedbreadSearchEndpointTemplate = (options, rateLimit) => {
1058
+ const limit = rateLimitTemplate(rateLimit, "search");
1059
+ const imports = [
1060
+ 'import type { APIRoute } from "astro";',
1061
+ 'import { getSecret } from "astro:env/server";',
1062
+ 'import Mixedbread from "@mixedbread/sdk";',
1063
+ 'import { readCappedText } from "blume/core/request-body.ts";',
1064
+ ...limit.imports.filter((line) => !line.includes('"astro:env/server"'))
1065
+ ];
1066
+ return `// Generated by Blume. Do not edit.
1067
+ ${imports.join(`
1068
+ `)}
916
1069
 
917
1070
  export const prerender = false;
918
1071
 
@@ -920,9 +1073,10 @@ const client = new Mixedbread({ apiKey: getSecret("MIXEDBREAD_API_KEY") ?? "" })
920
1073
  const OPTIONS = ${JSON.stringify(options)};
921
1074
  // Every option besides the store reaches the search call verbatim.
922
1075
  const { storeId: STORE_ID, ...SEARCH_OPTIONS } = OPTIONS;
923
-
924
- export const POST: APIRoute = async ({ request }) => {
925
- // A search body is one short query: read it under a 16 KB cap, so a
1076
+ ${limit.setup}
1077
+ export const POST: APIRoute = async (context) => {
1078
+ const { request } = context;
1079
+ ${limit.check} // A search body is one short query: read it under a 16 KB cap, so a
926
1080
  // self-hosted server never buffers an arbitrarily large POST first.
927
1081
  const text = await readCappedText(request, 16_384);
928
1082
  if (text === undefined) {
@@ -967,6 +1121,7 @@ export const POST: APIRoute = async ({ request }) => {
967
1121
  });
968
1122
  };
969
1123
  `;
1124
+ };
970
1125
  var rawMarkdownEndpointTemplate = (kind) => `// Generated by Blume. Do not edit.
971
1126
  import raw from "blume:raw-markdown";
972
1127
 
@@ -1111,16 +1266,22 @@ const handler = createMcpFetchHandler(data);
1111
1266
 
1112
1267
  export const ALL: APIRoute = ({ request }) => handler(request);
1113
1268
  `;
1114
- var playgroundProxyTemplate = (origins) => `// Generated by Blume. Do not edit.
1115
- import type { APIRoute } from "astro";
1116
- import { createPlaygroundProxyHandler } from "blume/openapi/proxy.ts";
1269
+ var playgroundProxyTemplate = (origins, rateLimit) => {
1270
+ const limit = rateLimitTemplate(rateLimit, "api-proxy");
1271
+ const route = limit.check ? `export const ALL: APIRoute = async (context) => {
1272
+ ${limit.check} return handler(context.request);
1273
+ };` : "export const ALL: APIRoute = ({ request }) => handler(request);";
1274
+ return `// Generated by Blume. Do not edit.
1275
+ ${['import type { APIRoute } from "astro";', 'import { createPlaygroundProxyHandler } from "blume/openapi/proxy.ts";', ...limit.imports].join(`
1276
+ `)}
1117
1277
 
1118
1278
  export const prerender = false;
1119
1279
 
1120
1280
  const handler = createPlaygroundProxyHandler(${JSON.stringify(origins)});
1121
-
1122
- export const ALL: APIRoute = ({ request }) => handler(request);
1281
+ ${limit.setup}
1282
+ ${route}
1123
1283
  `;
1284
+ };
1124
1285
  var staticJsonEndpointTemplate = (payload) => `// Generated by Blume. Do not edit.
1125
1286
  export const prerender = true;
1126
1287
 
@@ -1154,7 +1315,7 @@ export function GET({ props }: { props: { section: string } }) {
1154
1315
  var navFragmentTemplate = () => `---
1155
1316
  // Generated by Blume. Do not edit.
1156
1317
  import NavTree from "blume/components/layout/NavTree.astro";
1157
- import { withBase } from "blume/components/islands/base-path.ts";
1318
+ import { withMountedBase } from "blume/components/islands/base-path.ts";
1158
1319
  import {
1159
1320
  hiddenDefaultLocale,
1160
1321
  navGroupIds,
@@ -1193,7 +1354,7 @@ if (!(variant && node && node.kind === "group")) {
1193
1354
  }
1194
1355
  const strings =
1195
1356
  locale === "default" ? data.ui.nav : (data.uiByLocale[locale] ?? data.ui).nav;
1196
- const fragmentBase = withBase(\`/blume-nav/\${version}/\${locale}\`);
1357
+ const fragmentBase = withMountedBase(\`/blume-nav/\${version}/\${locale}\`);
1197
1358
  ---
1198
1359
 
1199
1360
  <NavTree
@@ -1209,7 +1370,7 @@ const fragmentBase = withBase(\`/blume-nav/\${version}/\${locale}\`);
1209
1370
  `;
1210
1371
  var ogEndpointTemplate = (customRoutes = [], og = {}, includeChangelog = false) => `// Generated by Blume. Do not edit.
1211
1372
  import { cachedOgImage } from "blume/og";
1212
- import type { OgCache, OgFont, OgFontFamilies } from "blume/og";
1373
+ import type { OgCache, OgFont, OgFontFamilies, OgGoogleFont } from "blume/og";
1213
1374
  import data from "blume:data";
1214
1375
 
1215
1376
  export const prerender = true;
@@ -1225,6 +1386,9 @@ const customRoutes: { slug: string; title: string }[] = ${JSON.stringify(customR
1225
1386
  // data that pages serialize into HTML.
1226
1387
  const fonts: OgFont[] = ${JSON.stringify(og.fonts ?? [])};
1227
1388
  const families: OgFontFamilies | undefined = ${og.families ? JSON.stringify(og.families) : "undefined"};
1389
+ // Script fallbacks (Noto families, the configured locales' first). A card
1390
+ // fetches them only when its text has a glyph Takumi's built-in font lacks.
1391
+ const fallbacks: OgGoogleFont[] = ${JSON.stringify(og.fallbacks ?? [])};
1228
1392
 
1229
1393
  // Rendered cards are kept on disk between builds, keyed by everything that
1230
1394
  // decides their pixels, so a rebuild renders only the cards whose title,
@@ -1291,6 +1455,7 @@ export async function GET({ props }: { props: CardProps }) {
1291
1455
  accent: data.config.og.palette?.accent ?? data.config.theme.accent.light,
1292
1456
  brand: data.config.title,
1293
1457
  description: props.description ?? data.config.og.description,
1458
+ fallbacks,
1294
1459
  families,
1295
1460
  fonts,
1296
1461
  logo: data.config.og.logo,
@@ -1357,6 +1522,9 @@ var contentComponentsSource = (mathEnabled) => {
1357
1522
  return {
1358
1523
  imports: `import Accordion from "blume/components/content/Accordion.astro";
1359
1524
  import AccordionItem from "blume/components/content/AccordionItem.astro";
1525
+ import ApiEndpoint from "blume/components/content/ApiEndpoint.astro";
1526
+ import ApiPlayground from "blume/components/content/ApiPlayground.astro";
1527
+ import ApiRail from "blume/components/content/ApiRail.astro";
1360
1528
  import AutoTypeTable from "blume/components/content/AutoTypeTable.astro";
1361
1529
  import Badge from "blume/components/content/Badge.astro";
1362
1530
  import Callout from "blume/components/content/Callout.astro";
@@ -1376,7 +1544,11 @@ import FileTree from "blume/components/content/FileTree.astro";
1376
1544
  import Frame from "blume/components/content/Frame.astro";
1377
1545
  import GithubInfo from "blume/components/content/GithubInfo.astro";
1378
1546
  import Panel from "blume/components/content/Panel.astro";
1547
+ import ParamField from "blume/components/content/ParamField.astro";
1379
1548
  import Prompt from "blume/components/content/Prompt.astro";
1549
+ import RequestExample from "blume/components/content/RequestExample.astro";
1550
+ import ResponseExample from "blume/components/content/ResponseExample.astro";
1551
+ import ResponseField from "blume/components/content/ResponseField.astro";
1380
1552
  import Step from "blume/components/content/Step.astro";
1381
1553
  import Steps from "blume/components/content/Steps.astro";
1382
1554
  import Tab from "blume/components/content/Tab.astro";
@@ -1387,10 +1559,13 @@ import TreeRoot from "blume/components/content/Tree.astro";
1387
1559
  import TreeFile from "blume/components/content/TreeFile.astro";
1388
1560
  import TreeFolder from "blume/components/content/TreeFolder.astro";
1389
1561
  import TypeTable from "blume/components/content/TypeTable.astro";
1562
+ import View from "blume/components/content/View.astro";
1390
1563
  import Visibility from "blume/components/content/Visibility.astro";
1391
1564
  import YouTube from "blume/components/content/YouTube.astro";
1392
1565
  import Icon from "blume/components/Icon.astro";
1393
1566
  import LocaleLinks from "blume/components/layout/LocaleLinks.astro";
1567
+ import NarrationPlayer from "blume/components/layout/NarrationPlayer.astro";
1568
+ import ViewSwitcher from "blume/components/content/ViewSwitcher.astro";
1394
1569
  import ApiOverview from "blume/components/openapi/ApiOverview.astro";
1395
1570
  import ApiTagOperations from "blume/components/openapi/ApiTagOperations.astro";
1396
1571
  import Operation from "blume/components/openapi/Operation.astro";
@@ -1398,7 +1573,10 @@ ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../gener
1398
1573
  map: `{
1399
1574
  Accordion,
1400
1575
  AccordionItem,
1576
+ ApiEndpoint,
1401
1577
  ApiOverview,
1578
+ ApiPlayground,
1579
+ ApiRail,
1402
1580
  ApiTagOperations,
1403
1581
  AutoTypeTable,
1404
1582
  Badge,
@@ -1419,7 +1597,11 @@ ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../gener
1419
1597
  Icon,
1420
1598
  Operation,
1421
1599
  Panel,
1600
+ ParamField,
1422
1601
  Prompt,
1602
+ RequestExample,
1603
+ ResponseExample,
1604
+ ResponseField,
1423
1605
  Step,
1424
1606
  Steps,
1425
1607
  Tab,
@@ -1428,6 +1610,7 @@ ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../gener
1428
1610
  Tooltip,
1429
1611
  Tree,
1430
1612
  TypeTable,
1613
+ View,
1431
1614
  Visibility,
1432
1615
  YouTube,
1433
1616
  ${mathEntry}...userMdx,
@@ -1441,10 +1624,11 @@ var catchAllPageTemplate = (options) => {
1441
1624
  return `---
1442
1625
  // Generated by Blume. Do not edit.
1443
1626
  import { getEntry, render } from "astro:content";
1627
+ import { pageModeLayout } from "blume/core/page-modes.ts";
1444
1628
  import type { CollectionKey } from "astro:content";
1445
1629
  import RootLayout from "blume/components/layout/RootLayout.astro";
1446
- import { withBase } from "blume/components/islands/base-path.ts";
1447
- import { stripBasePath, withBasePath } from "blume/core/base-path.ts";
1630
+ import { withBase, withMountedBase } from "blume/components/islands/base-path.ts";
1631
+ import { mountBasePath, stripBasePath } from "blume/core/base-path.ts";
1448
1632
  import { resolveSlot } from "blume/components/layout/overrides.ts";
1449
1633
  ${componentImports}
1450
1634
  import data from "blume:data";
@@ -1492,6 +1676,13 @@ const { Content, headings: allHeadings, remarkPluginFrontmatter } = await render
1492
1676
  // frontmatter (see markdown/heading-anchors.ts). Only the plugin's array
1493
1677
  // counts: \`frontmatter.extend\` can declare the same key, and on a page with
1494
1678
  // no headings that user-supplied value would pass straight through.
1679
+ // A page written in \`<View>\` blocks gets their picker above the content;
1680
+ // the views plugin lists them through the render's frontmatter (see
1681
+ // markdown/views.ts), and only a list of titled entries counts.
1682
+ const viewsRaw = remarkPluginFrontmatter?.${VIEWS_KEY};
1683
+ const views = Array.isArray(viewsRaw)
1684
+ ? viewsRaw.filter((view) => typeof view?.title === "string")
1685
+ : [];
1495
1686
  const tocHiddenRaw = remarkPluginFrontmatter?.${TOC_HIDDEN_KEY};
1496
1687
  const tocHidden = new Set(Array.isArray(tocHiddenRaw) ? tocHiddenRaw : []);
1497
1688
  const headings =
@@ -1509,12 +1700,16 @@ const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
1509
1700
  const ogPath = data.config.og.enabled
1510
1701
  ? encodeURI(\`/og/\${route === "/" ? "index" : route.slice(1)}.png\`)
1511
1702
  : null;
1512
- const ogRel = seo.image ?? ogPath;
1513
1703
  // Absolute URLs also carry the deployment base (the page is served under it):
1514
1704
  // \`site + base + path\`. Only absolutize root-relative paths: \`seo.image\` may be
1515
- // an external URL, which passes through verbatim (mirrors PageLayout).
1516
- const ogImage =
1517
- ogRel && base && ogRel.startsWith("/") ? \`\${base}\${withBase(ogRel)}\` : ogRel;
1705
+ // an external URL, which passes through verbatim (mirrors PageLayout). An
1706
+ // authored \`seo.image\` keeps a base written by hand; the generated card is
1707
+ // always mounted under it.
1708
+ const absoluteOg = (path: string, based: (path: string) => string) =>
1709
+ base && path.startsWith("/") ? \`\${base}\${based(path)}\` : path;
1710
+ const ogImage = seo.image
1711
+ ? absoluteOg(seo.image, withBase)
1712
+ : ogPath && absoluteOg(ogPath, withMountedBase);
1518
1713
  // Blume's generated card has known dimensions the layout can declare; a user's
1519
1714
  // \`seo.image\` could be any size or format, so it gets none.
1520
1715
  const ogGenerated = !seo.image && Boolean(ogPath);
@@ -1523,7 +1718,7 @@ const ogGenerated = !seo.image && Boolean(ogPath);
1523
1718
  // itself (a guest post crediting its own author) over the configured default.
1524
1719
  const x = { ...data.config.x, ...(seo.x?.creator ? { creator: seo.x.creator } : {}) };
1525
1720
 
1526
- const basedRoute = withBase(route);
1721
+ const basedRoute = withMountedBase(route);
1527
1722
 
1528
1723
  // Locale resolution. With i18n on, pick the active locale's nav + dictionary,
1529
1724
  // build hreflang alternates, and derive the language-switcher targets.
@@ -1566,12 +1761,20 @@ const htmlLang = i18n ? locale : "en";
1566
1761
  // follows that language — not the (mirrored) page locale.
1567
1762
  const contentLocale =
1568
1763
  fallback && i18n?.fallbackLocale ? i18n.fallbackLocale : locale;
1764
+ // "Listen to this page", unless the page opts out. The spoken cues are read
1765
+ // between the content's own sentences, so they come from the content's
1766
+ // language (a fallback page's), while the player's labels follow the page.
1767
+ // A \`custom\` or \`frame\` page brings its own heading: no title, description,
1768
+ // or narration player above its content.
1769
+ const pageChrome = pageModeLayout(frontmatter.mode).chrome;
1770
+ const narration = pageChrome && frontmatter.narration ? data.config.narration : null;
1771
+ const narrationCues = (i18n ? (data.uiByLocale[contentLocale] ?? data.ui) : data.ui).narration;
1569
1772
  const contentDir = i18n
1570
1773
  ? (i18n.locales.find((l) => l.code === contentLocale)?.dir ?? "ltr")
1571
1774
  : "ltr";
1572
1775
  // The root route keeps its trailing slash (\`https://site/\`) so canonical and
1573
1776
  // hreflang URLs byte-match the sitemap's <loc> for the home page.
1574
- const absolute = (path: string) => base + withBase(path);
1777
+ const absolute = (path: string) => base + withMountedBase(path);
1575
1778
 
1576
1779
  // A fallback page renders the fallback locale's page at this locale's URL, so
1577
1780
  // its canonical names the page it copies, and search engines never rank the
@@ -1616,7 +1819,7 @@ const xDefault = defaultAlt && base ? absolute(defaultAlt.path) : null;
1616
1819
  // carries sits *after* the base (\`/docs/ja/guide\`), while \`localePrefix\` is
1617
1820
  // base-less (\`/ja\`). Stripping and re-adding a locale therefore happen in
1618
1821
  // base-less space, with the base re-applied at the end — the same
1619
- // \`withBasePath(basePath, localizeRoute(...))\` composition the manifest uses to
1822
+ // \`mountBasePath(basePath, localizeRoute(...))\` composition the manifest uses to
1620
1823
  // build every real route. Done in based space, \`stripLocale\` matches nothing
1621
1824
  // and \`localizeRoute\` prepends a second prefix (\`/ja/docs/ja/guide\`). Only a
1622
1825
  // switcher entry for a locale with no real translation reaches this fallback:
@@ -1624,7 +1827,7 @@ const xDefault = defaultAlt && base ? absolute(defaultAlt.path) : null;
1624
1827
  // generated reference (which has no \`alternates\` at all) for every locale
1625
1828
  // but its own.
1626
1829
  const mountLocalized = (logical: string, codeArg: string) =>
1627
- withBasePath(data.config.basePath, localizeRoute(logical, codeArg));
1830
+ mountBasePath(data.config.basePath, localizeRoute(logical, codeArg));
1628
1831
  const logicalRoute = i18n
1629
1832
  ? stripLocale(stripBasePath(data.config.basePath, route), locale)
1630
1833
  : route;
@@ -1653,7 +1856,7 @@ const versionRootFor = (id: string) => {
1653
1856
  const logical = id ? \`/\${id}\` : "/";
1654
1857
  return i18n
1655
1858
  ? mountLocalized(logical, locale)
1656
- : withBasePath(data.config.basePath, logical);
1859
+ : mountBasePath(data.config.basePath, logical);
1657
1860
  };
1658
1861
  const samePageSwitch = versionsConfig
1659
1862
  ? versionsConfig.switcher.redirect === "same-page"
@@ -1749,21 +1952,37 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
1749
1952
  canonical={canonical}
1750
1953
  editUrl={editUrl}
1751
1954
  feedback={data.config.feedback}
1955
+ feedbackComments={data.config.feedbackComments}
1956
+ pagination={frontmatter.pagination}
1957
+ related={frontmatter.related}
1958
+ search={frontmatter.search}
1752
1959
  exportPdf={${options.exportPdf}}
1753
1960
  exportEpub={${options.exportEpub}}${options.navFragments ? `
1754
- navFragmentBase={withBase(\`/blume-nav/\${version || "current"}/\${i18n && localePrefix(locale) ? locale : "default"}\`)}` : ""}
1961
+ navFragmentBase={withMountedBase(\`/blume-nav/\${version || "current"}/\${i18n && localePrefix(locale) ? locale : "default"}\`)}` : ""}
1755
1962
  openInChat={data.config.openInChat}
1756
1963
  feeds={data.feeds}
1757
1964
  discovery={data.config.discovery}
1758
1965
  siteUrl={data.config.site}
1759
1966
  pageType={frontmatter.type}
1967
+ apiRail={remarkPluginFrontmatter?.${API_RAIL_KEY} === true}
1968
+ pageMode={frontmatter.mode}
1760
1969
  published={frontmatter.date ?? frontmatter.changelog?.date ?? null}
1761
1970
  lastModified={lastModified}
1762
1971
  noindex={effectiveNoindex}
1763
1972
  structuredDataEnabled={data.config.structuredData}
1764
1973
  >
1765
- <h1>{title}</h1>
1766
- {frontmatter.description && <p class="text-lg text-muted-foreground">{frontmatter.description}</p>}
1974
+ {pageChrome && <h1>{title}</h1>}
1975
+ {pageChrome && frontmatter.description && <p class="text-lg text-muted-foreground">{frontmatter.description}</p>}
1976
+ {narration && (
1977
+ <NarrationPlayer
1978
+ audioBase={narration.generated ? withMountedBase("/blume-narration/audio/") : undefined}
1979
+ cues={narrationCues}
1980
+ lang={i18n ? contentLocale : htmlLang}
1981
+ manifest={narration.generated ? withMountedBase(encodeURI(\`/blume-narration/\${route === "/" ? "index" : route.slice(1)}.json\`)) : undefined}
1982
+ strings={ui.narration}
1983
+ />
1984
+ )}
1985
+ {views.length > 1 && <ViewSwitcher label={ui.content.selectView} views={views} />}
1767
1986
  <LocaleLinks locale={locale}>
1768
1987
  <Content components={components} />
1769
1988
  </LocaleLinks>
@@ -1779,7 +1998,7 @@ var changelogIndexTemplate = (options) => {
1779
1998
  // Generated by Blume. Do not edit.
1780
1999
  import { getCollection } from "astro:content";
1781
2000
  import RootLayout from "blume/components/layout/RootLayout.astro";
1782
- import { withBase } from "blume/components/islands/base-path.ts";
2001
+ import { withMountedBase } from "blume/components/islands/base-path.ts";
1783
2002
  import { resolveSlot } from "blume/components/layout/overrides.ts";
1784
2003
  import { resolveDateFormatOptions } from "blume/core/date-format.ts";
1785
2004
  import { layoutOverrides } from "../generated/components.ts";
@@ -1878,7 +2097,7 @@ const items = changelogEntries.map((entry) => {
1878
2097
  return {
1879
2098
  date: date ? formatRowDate(date) : null,
1880
2099
  dateTime: date ? date.toISOString().slice(0, 10) : null,
1881
- href: route ? withBase(route) : null,
2100
+ href: route ? withMountedBase(route) : null,
1882
2101
  id: slugify(label),
1883
2102
  label,
1884
2103
  tag: entry.data.changelog?.category ?? null,
@@ -1914,14 +2133,14 @@ for (const item of items) {
1914
2133
 
1915
2134
  const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
1916
2135
  // The canonical URL carries the deployment base (the page is served under it),
1917
- // matching how the catch-all canonicalizes via \`withBase(route)\`.
1918
- const basedRoute = withBase("/changelog");
2136
+ // matching how the catch-all canonicalizes via \`withMountedBase(route)\`.
2137
+ const basedRoute = withMountedBase("/changelog");
1919
2138
  const canonical = base ? base + basedRoute : null;
1920
2139
 
1921
2140
  // The generated OG card for this route (the /og endpoint emits it alongside
1922
2141
  // the content-route cards), absolutized like the catch-all's so crawlers get
1923
2142
  // a full URL when the site is known.
1924
- const ogPath = data.config.og.enabled ? withBase("/og/changelog.png") : null;
2143
+ const ogPath = data.config.og.enabled ? withMountedBase("/og/changelog.png") : null;
1925
2144
  const ogImage = ogPath && base ? base + ogPath : ogPath;
1926
2145
 
1927
2146
  // The page chrome (h1, title, description) comes from the translatable
@@ -2033,8 +2252,8 @@ var notFoundPageTemplate = () => `---
2033
2252
  // Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
2034
2253
  import Icon from "blume/components/Icon.astro";
2035
2254
  import PageLayout from "blume/components/layout/PageLayout.astro";
2036
- import { withBase } from "blume/components/islands/base-path.ts";
2037
- import { withBasePath } from "blume/core/base-path.ts";
2255
+ import { withMountedBase } from "blume/components/islands/base-path.ts";
2256
+ import { mountBasePath } from "blume/core/base-path.ts";
2038
2257
  import data from "blume:data";
2039
2258
 
2040
2259
  export const prerender = true;
@@ -2058,14 +2277,14 @@ const htmlLang = i18n ? i18n.defaultLocale : "en";
2058
2277
  // their resolved target when the section has no index page of its own.
2059
2278
  const suggestions = [
2060
2279
  ...data.navigation.tabs.map((tab) => ({
2061
- href: withBase(tab.href ?? tab.path),
2280
+ href: withMountedBase(tab.href ?? tab.path),
2062
2281
  label: tab.label,
2063
2282
  })),
2064
2283
  ...(data.config.discovery.sitemap
2065
- ? [{ href: withBase("/sitemap.xml"), label: nf.sitemap }]
2284
+ ? [{ href: withMountedBase("/sitemap.xml"), label: nf.sitemap }]
2066
2285
  : []),
2067
2286
  ...(data.config.discovery.llmsTxt
2068
- ? [{ href: withBase("/llms.txt"), label: nf.llms }]
2287
+ ? [{ href: withMountedBase("/llms.txt"), label: nf.llms }]
2069
2288
  : []),
2070
2289
  ];
2071
2290
 
@@ -2077,7 +2296,7 @@ const suggestions = [
2077
2296
  // Localized routes sit under both bases (\`{deployment.base}/{basePath}/ar/…\`),
2078
2297
  // so the script strips the two together, trailing slash included.
2079
2298
  const { basePath } = data.config;
2080
- const siteRoot = withBase(withBasePath(basePath, "/"));
2299
+ const siteRoot = withMountedBase(mountBasePath(basePath, "/"));
2081
2300
  const localeBase = siteRoot.endsWith("/") ? siteRoot : siteRoot + "/";
2082
2301
  const localized = Object.fromEntries(
2083
2302
  (i18n?.locales ?? [])
@@ -2090,7 +2309,7 @@ const localized = Object.fromEntries(
2090
2309
  description: strings.description,
2091
2310
  dir: l.dir ?? "ltr",
2092
2311
  home: strings.home,
2093
- homeHref: withBase(withBasePath(basePath, "/" + l.code)),
2312
+ homeHref: withMountedBase(mountBasePath(basePath, "/" + l.code)),
2094
2313
  suggestions: strings.suggestions,
2095
2314
  title: strings.title,
2096
2315
  },
@@ -2140,7 +2359,7 @@ const localizedJson = JSON.stringify(localized).replaceAll("<", "\\\\u003c");
2140
2359
  <a
2141
2360
  class="mt-8 inline-flex items-center gap-1.5 rounded-full bg-accent py-2 pe-4 ps-3.5 text-sm font-medium text-accent-foreground transition-opacity hover:opacity-90"
2142
2361
  data-nf-home
2143
- href={withBase("/")}
2362
+ href={withMountedBase("/")}
2144
2363
  >
2145
2364
  <Icon class="rtl:-scale-x-100" name="arrow-left" size={14} />
2146
2365
  <span data-nf="home">{nf.home}</span>
@@ -2207,7 +2426,7 @@ const localizedJson = JSON.stringify(localized).replaceAll("<", "\\\\u003c");
2207
2426
  </PageLayout>
2208
2427
  `;
2209
2428
  var notFoundMarkdownTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
2210
- import { withBase } from "blume/components/islands/base-path.ts";
2429
+ import { withMountedBase } from "blume/components/islands/base-path.ts";
2211
2430
  import { absoluteUrl } from "blume/core/site-url.ts";
2212
2431
  import data from "blume:data";
2213
2432
 
@@ -2218,7 +2437,7 @@ const nf = data.ui.notFound;
2218
2437
  // Absolute for internal routes when the site is known; an external tab href
2219
2438
  // passes through untouched.
2220
2439
  const href = (path: string): string => {
2221
- const based = withBase(path);
2440
+ const based = withMountedBase(path);
2222
2441
  return data.config.site && based.startsWith("/") && !based.startsWith("//")
2223
2442
  ? absoluteUrl(data.config.site, based)
2224
2443
  : based;
@@ -2266,7 +2485,7 @@ export function GET() {
2266
2485
  `;
2267
2486
  var notFoundJsonTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
2268
2487
  import { problem } from "blume/ai/api/problem.ts";
2269
- import { withBase } from "blume/components/islands/base-path.ts";
2488
+ import { withMountedBase } from "blume/components/islands/base-path.ts";
2270
2489
  import { absoluteUrl } from "blume/core/site-url.ts";
2271
2490
  import data from "blume:data";
2272
2491
 
@@ -2277,7 +2496,7 @@ const nf = data.ui.notFound;
2277
2496
  // Absolute for internal routes when the site is known; an external tab href
2278
2497
  // passes through untouched.
2279
2498
  const href = (path: string): string => {
2280
- const based = withBase(path);
2499
+ const based = withMountedBase(path);
2281
2500
  return data.config.site && based.startsWith("/") && !based.startsWith("//")
2282
2501
  ? absoluteUrl(data.config.site, based)
2283
2502
  : based;
@@ -2534,7 +2753,7 @@ var runtimeTsconfigTemplate = () => `${JSON.stringify({
2534
2753
  }, null, 2)}
2535
2754
  `;
2536
2755
 
2537
- export { deployOutputDir, deployStaticDir, surfaceAdapterOutput, planComponentSlots, publishRuntimeModules, runtimeDependencies, EJECTED_CONFIG_HEADER, astroConfigTemplate, stagedContentDir, contentConfigTemplate, askEndpointTemplate, askComponentTemplate, searchEndpointTemplate, featuresTemplate, searchClientTemplate, mixedbreadSearchEndpointTemplate, rawMarkdownEndpointTemplate, contentAssetsEndpointTemplate, mcpPageFile, mcpEndpointTemplate, playgroundProxyTemplate, staticJsonEndpointTemplate, rssEndpointTemplate, navFragmentTemplate, ogEndpointTemplate, scalarReferenceTemplate, catchAllPageTemplate, changelogIndexTemplate, notFoundPageTemplate, notFoundMarkdownTemplate, notFoundJsonTemplate, apiPagesIndexTemplate, apiPageTemplate, apiNavigationTemplate, apiSearchTemplate, apiNotFoundTemplate, exampleSlug, exampleWrapperTemplate, examplesRouteBase, exampleMapTemplate, examplesPageTemplate, runtimePackageTemplate, runtimeTsconfigTemplate };
2756
+ export { deployOutputDir, deployStaticDir, surfaceAdapterOutput, planComponentSlots, publishRuntimeModules, runtimeDependencies, EJECTED_CONFIG_HEADER, astroConfigTemplate, stagedContentDir, contentConfigTemplate, askEndpointTemplate, askComponentTemplate, searchEndpointTemplate, featuresTemplate, consentClientTemplate, searchClientTemplate, mixedbreadSearchEndpointTemplate, rawMarkdownEndpointTemplate, contentAssetsEndpointTemplate, mcpPageFile, mcpEndpointTemplate, playgroundProxyTemplate, staticJsonEndpointTemplate, rssEndpointTemplate, navFragmentTemplate, ogEndpointTemplate, scalarReferenceTemplate, catchAllPageTemplate, changelogIndexTemplate, notFoundPageTemplate, notFoundMarkdownTemplate, notFoundJsonTemplate, apiPagesIndexTemplate, apiPageTemplate, apiNavigationTemplate, apiSearchTemplate, apiNotFoundTemplate, exampleSlug, exampleWrapperTemplate, examplesRouteBase, exampleMapTemplate, examplesPageTemplate, runtimePackageTemplate, runtimeTsconfigTemplate };
2538
2757
 
2539
- //# debugId=941D4E587200F86C64756E2164756E21
2540
- //# sourceMappingURL=chunk-hr8ne106.js.map
2758
+ //# debugId=F21B5F971981289464756E2164756E21
2759
+ //# sourceMappingURL=chunk-4h8z9vv5.js.map