blume 2.0.3 → 2.1.1

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 (607) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +142 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
  5. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
  6. package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
  7. package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
  8. package/dist/cli/chunk-150jk7km.js +52 -0
  9. package/dist/cli/chunk-150jk7km.js.map +10 -0
  10. package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
  11. package/dist/cli/chunk-313vnn40.js.map +10 -0
  12. package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
  13. package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
  14. package/dist/cli/chunk-3x6x3pph.js.map +10 -0
  15. package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
  16. package/dist/cli/chunk-42t4jxdx.js.map +12 -0
  17. package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
  18. package/dist/cli/chunk-54ztw1nj.js.map +10 -0
  19. package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
  20. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
  21. package/dist/cli/chunk-66w9j7p6.js +322 -0
  22. package/dist/cli/chunk-66w9j7p6.js.map +14 -0
  23. package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
  24. package/dist/cli/chunk-7ppf1rva.js.map +10 -0
  25. package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
  26. package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
  27. package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
  28. package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
  29. package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
  30. package/dist/cli/chunk-cfyg47zg.js.map +10 -0
  31. package/dist/cli/chunk-cm89scdv.js +443 -0
  32. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  33. package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
  34. package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
  35. package/dist/cli/chunk-e6atmn4h.js.map +36 -0
  36. package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
  37. package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
  38. package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
  39. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
  40. package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
  41. package/dist/cli/chunk-ehweg4fr.js.map +218 -0
  42. package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
  43. package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
  44. package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
  45. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
  46. package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
  47. package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
  48. package/dist/cli/chunk-gb420vvz.js.map +17 -0
  49. package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
  50. package/dist/cli/chunk-hebn0tpx.js.map +14 -0
  51. package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
  52. package/dist/cli/chunk-htrwgabr.js.map +15 -0
  53. package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
  54. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
  55. package/dist/cli/chunk-j134vrp2.js +65 -0
  56. package/dist/cli/chunk-j134vrp2.js.map +10 -0
  57. package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
  58. package/dist/cli/chunk-kwrg63tm.js +31 -0
  59. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  60. package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
  61. package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
  62. package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
  63. package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
  64. package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
  65. package/dist/cli/chunk-ncj7w596.js.map +35 -0
  66. package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
  67. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
  68. package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
  69. package/dist/cli/chunk-nrns10mj.js.map +10 -0
  70. package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
  71. package/dist/cli/chunk-t2m5j4pr.js +104 -0
  72. package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
  73. package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
  74. package/dist/cli/chunk-tphac74f.js +126 -0
  75. package/dist/cli/chunk-tphac74f.js.map +11 -0
  76. package/dist/cli/chunk-w1r4vase.js +18 -0
  77. package/dist/cli/chunk-w1r4vase.js.map +10 -0
  78. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  79. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  80. package/dist/cli/index.js +31 -20
  81. package/dist/cli/index.js.map +5 -5
  82. package/dist/types/ai/agent-surface.d.ts +8 -7
  83. package/dist/types/ai/ai-catalog.d.ts +7 -0
  84. package/dist/types/ai/ask-context.d.ts +26 -0
  85. package/dist/types/ai/ask.d.ts +271 -58
  86. package/dist/types/ai/component-markdown.d.ts +2 -0
  87. package/dist/types/ai/index.d.ts +2 -2
  88. package/dist/types/ai/openapi-components.d.ts +4 -1
  89. package/dist/types/ai/site-skill.d.ts +26 -0
  90. package/dist/types/ai/skills.d.ts +16 -0
  91. package/dist/types/astro/integration.d.ts +9 -0
  92. package/dist/types/astro/markdown-negotiation.d.ts +3 -0
  93. package/dist/types/astro/pages.d.ts +10 -0
  94. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  95. package/dist/types/captcha/index.d.ts +28 -0
  96. package/dist/types/captcha/schema.d.ts +29 -0
  97. package/dist/types/captcha/turnstile.d.ts +28 -0
  98. package/dist/types/cli/init/scaffold.d.ts +1 -1
  99. package/dist/types/components/content/api-field.d.ts +62 -0
  100. package/dist/types/components/content/api-page.d.ts +100 -0
  101. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  102. package/dist/types/components/openapi/code-samples.d.ts +29 -0
  103. package/dist/types/components/openapi/helpers.d.ts +158 -0
  104. package/dist/types/components/openapi/security.d.ts +95 -0
  105. package/dist/types/components/openapi/snippets.d.ts +60 -0
  106. package/dist/types/consent/ethyca.d.ts +48 -0
  107. package/dist/types/consent/index.d.ts +37 -0
  108. package/dist/types/consent/native.d.ts +28 -0
  109. package/dist/types/consent/osano.d.ts +42 -0
  110. package/dist/types/consent/schema.d.ts +110 -0
  111. package/dist/types/core/changelog-index.d.ts +8 -0
  112. package/dist/types/core/config-input.d.ts +217 -23
  113. package/dist/types/core/data.d.ts +53 -4
  114. package/dist/types/core/footer.d.ts +8 -0
  115. package/dist/types/core/i18n-ui.d.ts +88 -0
  116. package/dist/types/core/includes.d.ts +8 -2
  117. package/dist/types/core/links.d.ts +8 -0
  118. package/dist/types/core/localizable.d.ts +7 -0
  119. package/dist/types/core/page-modes.d.ts +29 -0
  120. package/dist/types/core/probe.d.ts +2 -0
  121. package/dist/types/core/project-graph.d.ts +6 -0
  122. package/dist/types/core/redirect-patterns.d.ts +152 -0
  123. package/dist/types/core/related.d.ts +40 -0
  124. package/dist/types/core/schema.d.ts +670 -56
  125. package/dist/types/core/sources/cache.d.ts +3 -0
  126. package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
  127. package/dist/types/core/sources/contentful.d.ts +17 -1
  128. package/dist/types/core/sources/lexical.d.ts +5 -0
  129. package/dist/types/core/sources/normalize.d.ts +20 -1
  130. package/dist/types/core/sources/read.d.ts +5 -0
  131. package/dist/types/core/sources/remote.d.ts +37 -10
  132. package/dist/types/core/sources/sanity.d.ts +1 -1
  133. package/dist/types/core/types.d.ts +18 -1
  134. package/dist/types/core/variables.d.ts +42 -0
  135. package/dist/types/deploy/adapter-output.d.ts +2 -1
  136. package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
  137. package/dist/types/deploy/adapters/node.d.ts +58 -23
  138. package/dist/types/deploy/adapters/registry.d.ts +10 -0
  139. package/dist/types/deploy/adapters/types.d.ts +3 -2
  140. package/dist/types/deploy/node-headers.d.ts +13 -2
  141. package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
  142. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  143. package/dist/types/deploy/platforms/node.d.ts +3 -1
  144. package/dist/types/deploy/platforms/types.d.ts +18 -7
  145. package/dist/types/deploy/platforms/vercel.d.ts +26 -1
  146. package/dist/types/deploy/redirects.d.ts +55 -11
  147. package/dist/types/deploy/sitemap.d.ts +2 -1
  148. package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
  149. package/dist/types/markdown/code-title.d.ts +17 -0
  150. package/dist/types/markdown/features.d.ts +3 -2
  151. package/dist/types/narration/build.d.ts +70 -0
  152. package/dist/types/narration/provider.d.ts +26 -0
  153. package/dist/types/narration/script.d.ts +154 -0
  154. package/dist/types/openapi/checks.d.ts +27 -0
  155. package/dist/types/openapi/graphql.d.ts +6 -0
  156. package/dist/types/openapi/model.d.ts +27 -10
  157. package/dist/types/openapi/overlay.d.ts +54 -0
  158. package/dist/types/openapi/parse.d.ts +16 -0
  159. package/dist/types/openapi/references.d.ts +30 -4
  160. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  161. package/dist/types/ratelimit/index.d.ts +27 -0
  162. package/dist/types/ratelimit/memory.d.ts +36 -0
  163. package/dist/types/ratelimit/schema.d.ts +119 -0
  164. package/dist/types/ratelimit/upstash.d.ts +46 -0
  165. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  166. package/dist/types/reference/asyncapi.d.ts +10 -10
  167. package/dist/types/reference/graphql.d.ts +89 -11
  168. package/dist/types/reference/index.d.ts +1 -1
  169. package/dist/types/reference/openapi.d.ts +33 -12
  170. package/dist/types/reference/options.d.ts +82 -6
  171. package/dist/types/reference/scalar.d.ts +13 -0
  172. package/dist/types/reference/schema.d.ts +144 -36
  173. package/dist/types/search/adapters/registry.d.ts +3 -0
  174. package/dist/types/search/adapters/typesense.d.ts +14 -4
  175. package/dist/types/search/build.d.ts +3 -1
  176. package/dist/types/search/documents.d.ts +12 -1
  177. package/dist/types/search/orama-index.d.ts +13 -9
  178. package/dist/types/search/sync/algolia.d.ts +5 -2
  179. package/dist/types/search/sync/index.d.ts +3 -1
  180. package/dist/types/search/sync/typesense.d.ts +11 -7
  181. package/dist/types/seo/metatags.d.ts +20 -0
  182. package/dist/types/sources/contentful.d.ts +16 -0
  183. package/dist/types/sources/github-releases.d.ts +8 -1
  184. package/dist/types/sources/payload.d.ts +2 -0
  185. package/dist/types/sources/registry.d.ts +54 -0
  186. package/dist/types/sources/shared.d.ts +3 -0
  187. package/dist/types/sources/strapi.d.ts +2 -0
  188. package/docs/01-quickstart.mdx +4 -2
  189. package/docs/02-deployment.mdx +71 -17
  190. package/docs/03-upgrading.mdx +4 -4
  191. package/docs/04-migrating.mdx +1 -1
  192. package/docs/advanced/changelog.mdx +13 -2
  193. package/docs/advanced/skills.mdx +15 -1
  194. package/docs/cli/audit.mdx +51 -8
  195. package/docs/cli/doctor.mdx +5 -1
  196. package/docs/cli/evals.mdx +4 -4
  197. package/docs/cli/index.mdx +1 -0
  198. package/docs/cli/translate.mdx +1 -0
  199. package/docs/cli/validate.mdx +4 -3
  200. package/docs/configuration/analytics.mdx +3 -1
  201. package/docs/configuration/assistant.mdx +194 -38
  202. package/docs/configuration/consent.mdx +100 -0
  203. package/docs/configuration/customization.mdx +8 -6
  204. package/docs/configuration/export.mdx +5 -3
  205. package/docs/configuration/index.mdx +64 -5
  206. package/docs/configuration/meta.ts +3 -0
  207. package/docs/configuration/narration.mdx +119 -0
  208. package/docs/configuration/rate-limiting.mdx +90 -0
  209. package/docs/configuration/search.mdx +63 -12
  210. package/docs/configuration/theming.mdx +3 -1
  211. package/docs/content/components.mdx +108 -2
  212. package/docs/content/frontmatter.mdx +80 -2
  213. package/docs/content/i18n.mdx +39 -4
  214. package/docs/content/includes.mdx +16 -0
  215. package/docs/content/meta.mdx +1 -0
  216. package/docs/content/meta.ts +1 -0
  217. package/docs/content/navigation.mdx +32 -10
  218. package/docs/content/sources/contentful.mdx +35 -0
  219. package/docs/content/sources/custom.mdx +43 -0
  220. package/docs/content/sources/github-releases.mdx +39 -0
  221. package/docs/content/sources/index.mdx +91 -0
  222. package/docs/content/sources/meta.ts +16 -0
  223. package/docs/content/sources/notion.mdx +32 -0
  224. package/docs/content/sources/obsidian.mdx +37 -0
  225. package/docs/content/sources/payload.mdx +31 -0
  226. package/docs/content/sources/remote-mdx.mdx +44 -0
  227. package/docs/content/sources/sanity.mdx +29 -0
  228. package/docs/content/sources/strapi.mdx +32 -0
  229. package/docs/content/syntax.mdx +69 -4
  230. package/docs/content/variables.mdx +41 -0
  231. package/docs/discoverability/agent-discovery.mdx +29 -2
  232. package/docs/discoverability/index.mdx +2 -2
  233. package/docs/discoverability/json-api.mdx +2 -0
  234. package/docs/discoverability/llms-txt.mdx +2 -2
  235. package/docs/discoverability/markdown.mdx +3 -1
  236. package/docs/discoverability/mcp.mdx +4 -0
  237. package/docs/discoverability/metadata.mdx +17 -0
  238. package/docs/discoverability/sitemap-and-robots.mdx +1 -1
  239. package/docs/references/api-pages.mdx +74 -0
  240. package/docs/references/asyncapi.mdx +4 -2
  241. package/docs/references/graphql.mdx +14 -2
  242. package/docs/references/meta.ts +1 -1
  243. package/docs/references/openapi.mdx +148 -7
  244. package/docs/references/scalar.mdx +3 -1
  245. package/package.json +43 -8
  246. package/skills/blume/SKILL.md +3 -0
  247. package/skills/blume-migrate/SKILL.md +6 -6
  248. package/skills/blume-migrate/references/docusaurus.md +7 -7
  249. package/skills/blume-migrate/references/fumadocs.md +17 -13
  250. package/skills/blume-migrate/references/mintlify.md +37 -19
  251. package/skills/blume-migrate/references/nextra.md +10 -7
  252. package/skills/blume-migrate/references/starlight.md +11 -8
  253. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  254. package/skills/blume-write-skill/SKILL.md +82 -0
  255. package/src/ai/agent-readability.ts +2 -1
  256. package/src/ai/agent-surface.ts +9 -7
  257. package/src/ai/ai-catalog.ts +6 -3
  258. package/src/ai/ask-context.ts +103 -20
  259. package/src/ai/ask-data.ts +6 -0
  260. package/src/ai/ask-limits.ts +13 -0
  261. package/src/ai/ask-tools.ts +142 -0
  262. package/src/ai/ask.ts +354 -76
  263. package/src/ai/changelog-markdown.ts +11 -6
  264. package/src/ai/component-markdown.ts +76 -1
  265. package/src/ai/cors.ts +14 -3
  266. package/src/ai/index.ts +12 -0
  267. package/src/ai/llms.ts +6 -2
  268. package/src/ai/markdown.ts +2 -1
  269. package/src/ai/mcp/data.ts +6 -0
  270. package/src/ai/mcp/query.ts +14 -6
  271. package/src/ai/openapi-components.ts +36 -6
  272. package/src/ai/relative-links.ts +4 -38
  273. package/src/ai/site-skill.ts +274 -0
  274. package/src/ai/skills.ts +35 -2
  275. package/src/ai/write-skill.ts +97 -0
  276. package/src/astro/generate.ts +210 -66
  277. package/src/astro/index.ts +1 -0
  278. package/src/astro/integration.ts +42 -4
  279. package/src/astro/markdown-negotiation.ts +3 -0
  280. package/src/astro/module-types.ts +9 -1
  281. package/src/astro/pages.ts +23 -0
  282. package/src/astro/templates.ts +406 -108
  283. package/src/astro/variables.ts +31 -0
  284. package/src/audit/catalog.ts +38 -3
  285. package/src/audit/checks/contrast.ts +183 -0
  286. package/src/audit/checks/i18n.ts +2 -3
  287. package/src/audit/checks/indexability.ts +2 -3
  288. package/src/audit/checks/links.ts +19 -13
  289. package/src/audit/checks/network.ts +110 -6
  290. package/src/audit/checks/redirects.ts +6 -4
  291. package/src/audit/checks/robots.ts +58 -2
  292. package/src/audit/checks/sitemap.ts +2 -3
  293. package/src/audit/redirects.ts +46 -15
  294. package/src/audit/run.ts +5 -0
  295. package/src/audit/types.ts +4 -1
  296. package/src/audit/url.ts +14 -5
  297. package/src/blume-modules.d.ts +12 -1
  298. package/src/captcha/client.ts +152 -0
  299. package/src/captcha/hcaptcha.ts +36 -0
  300. package/src/captcha/index.ts +28 -0
  301. package/src/captcha/schema.ts +32 -0
  302. package/src/captcha/turnstile.ts +38 -0
  303. package/src/captcha/verify.ts +71 -0
  304. package/src/cli/args.ts +51 -0
  305. package/src/cli/build-failure.ts +23 -0
  306. package/src/cli/command-meta.ts +5 -0
  307. package/src/cli/commands/audit.ts +10 -1
  308. package/src/cli/commands/dev.ts +18 -2
  309. package/src/cli/commands/doctor.ts +67 -28
  310. package/src/cli/commands/eval.ts +36 -4
  311. package/src/cli/commands/init.ts +6 -4
  312. package/src/cli/commands/skill.ts +96 -0
  313. package/src/cli/commands/validate.ts +10 -1
  314. package/src/cli/index.ts +5 -0
  315. package/src/cli/init/scaffold.ts +3 -3
  316. package/src/cli/prepare.ts +6 -3
  317. package/src/cli/required-secrets.ts +72 -30
  318. package/src/components/BlumePage.astro +2 -0
  319. package/src/components/content/ApiEndpoint.astro +18 -0
  320. package/src/components/content/ApiField.astro +79 -0
  321. package/src/components/content/ApiPlayground.astro +66 -0
  322. package/src/components/content/ApiRail.astro +19 -0
  323. package/src/components/content/Color.astro +1 -0
  324. package/src/components/content/FileTree.astro +1 -0
  325. package/src/components/content/GithubInfo.astro +1 -0
  326. package/src/components/content/Panel.astro +1 -0
  327. package/src/components/content/ParamField.astro +25 -0
  328. package/src/components/content/RequestExample.astro +17 -0
  329. package/src/components/content/ResponseExample.astro +17 -0
  330. package/src/components/content/ResponseField.astro +32 -0
  331. package/src/components/content/Step.astro +1 -0
  332. package/src/components/content/Tile.astro +1 -0
  333. package/src/components/content/Tooltip.astro +1 -0
  334. package/src/components/content/Tree.astro +1 -0
  335. package/src/components/content/TypeTable.astro +4 -1
  336. package/src/components/content/View.astro +21 -0
  337. package/src/components/content/ViewSwitcher.astro +168 -0
  338. package/src/components/content/api-field.ts +103 -0
  339. package/src/components/content/api-page.ts +248 -0
  340. package/src/components/content/mermaid-element.ts +15 -1
  341. package/src/components/content/view-switcher.ts +71 -0
  342. package/src/components/index.ts +2 -0
  343. package/src/components/islands/Assistant.astro +10 -1
  344. package/src/components/islands/assistant.tsx +104 -14
  345. package/src/components/islands/code-question.ts +62 -0
  346. package/src/components/islands/hooks.ts +136 -21
  347. package/src/components/islands/support-link.ts +68 -0
  348. package/src/components/layout/Analytics.astro +11 -2
  349. package/src/components/layout/Banner.astro +14 -10
  350. package/src/components/layout/Breadcrumbs.astro +6 -2
  351. package/src/components/layout/ConsentBanner.astro +62 -0
  352. package/src/components/layout/ConsentHead.astro +38 -0
  353. package/src/components/layout/Directory.astro +94 -0
  354. package/src/components/layout/DrawerSwitchers.astro +51 -0
  355. package/src/components/layout/Empty.astro +2 -2
  356. package/src/components/layout/Header.astro +122 -80
  357. package/src/components/layout/LanguageSwitcher.astro +48 -15
  358. package/src/components/layout/NarrationPlayer.astro +1085 -0
  359. package/src/components/layout/NavSelector.astro +33 -7
  360. package/src/components/layout/PageActions.astro +139 -24
  361. package/src/components/layout/PageFeedback.astro +54 -49
  362. package/src/components/layout/PageLayout.astro +71 -3
  363. package/src/components/layout/ReferenceLayout.astro +17 -6
  364. package/src/components/layout/RelatedPages.astro +42 -0
  365. package/src/components/layout/RootLayout.astro +318 -47
  366. package/src/components/layout/Search.astro +47 -19
  367. package/src/components/layout/SiteFooter.astro +124 -0
  368. package/src/components/layout/SiteMetatags.astro +16 -0
  369. package/src/components/layout/TableOfContents.astro +24 -17
  370. package/src/components/layout/consent/native.ts +79 -0
  371. package/src/components/layout/consent/types.ts +37 -0
  372. package/src/components/layout/footer-links.ts +53 -0
  373. package/src/components/layout/footer-socials.ts +87 -0
  374. package/src/components/layout/head-scripts.ts +6 -1
  375. package/src/components/layout/locale-redirect.ts +107 -0
  376. package/src/components/layout/nav-utils.ts +83 -0
  377. package/src/components/layout/page-feedback.ts +96 -0
  378. package/src/components/layout/search/algolia.ts +10 -1
  379. package/src/components/layout/search/analytics.ts +126 -0
  380. package/src/components/layout/search/endpoint.ts +48 -5
  381. package/src/components/layout/search/flexsearch.ts +17 -5
  382. package/src/components/layout/search/orama-cloud.ts +26 -14
  383. package/src/components/layout/search/pagefind.ts +114 -5
  384. package/src/components/layout/search/types.ts +32 -0
  385. package/src/components/layout/search/typesense.ts +19 -3
  386. package/src/components/openapi/AsyncApiOperation.astro +14 -7
  387. package/src/components/openapi/Authorization.astro +10 -0
  388. package/src/components/openapi/GraphqlOperation.astro +5 -0
  389. package/src/components/openapi/Operation.astro +184 -13
  390. package/src/components/openapi/ParametersTable.astro +8 -4
  391. package/src/components/openapi/Playground.astro +7 -1
  392. package/src/components/openapi/RequestBody.astro +24 -5
  393. package/src/components/openapi/RequestPanel.astro +59 -7
  394. package/src/components/openapi/Responses.astro +7 -4
  395. package/src/components/openapi/SchemaTable.astro +15 -3
  396. package/src/components/openapi/async-snippets.ts +65 -9
  397. package/src/components/openapi/async.ts +123 -29
  398. package/src/components/openapi/code-samples.ts +97 -0
  399. package/src/components/openapi/graphql-helpers.ts +35 -12
  400. package/src/components/openapi/helpers.ts +36 -2
  401. package/src/components/openapi/message-model.ts +54 -11
  402. package/src/components/openapi/message.ts +14 -4
  403. package/src/components/openapi/operation-model.ts +16 -7
  404. package/src/components/openapi/playground-client.ts +24 -5
  405. package/src/components/openapi/playground-schema.ts +1 -1
  406. package/src/components/openapi/schema-tree.ts +44 -14
  407. package/src/components/openapi/security.ts +16 -3
  408. package/src/components/openapi/snippets.ts +873 -10
  409. package/src/components/openapi/validate-json.ts +4 -3
  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/builtin-tags.ts +5 -0
  421. package/src/core/changelog-index.ts +31 -1
  422. package/src/core/client-address.ts +19 -0
  423. package/src/core/component-diagnostics.ts +53 -0
  424. package/src/core/component-overrides.ts +32 -0
  425. package/src/core/config-input.ts +225 -23
  426. package/src/core/data.ts +46 -4
  427. package/src/core/diagnostics.ts +8 -2
  428. package/src/core/footer.ts +26 -0
  429. package/src/core/graph.ts +39 -36
  430. package/src/core/i18n-ui.ts +84 -2
  431. package/src/core/includes.ts +21 -4
  432. package/src/core/links.ts +63 -19
  433. package/src/core/localizable.ts +26 -0
  434. package/src/core/navigation.ts +43 -12
  435. package/src/core/page-modes.ts +56 -0
  436. package/src/core/probe.ts +26 -6
  437. package/src/core/project-graph.ts +163 -16
  438. package/src/core/redirect-patterns.ts +520 -0
  439. package/src/core/related.ts +131 -0
  440. package/src/core/schema.ts +339 -66
  441. package/src/core/sources/cache.ts +6 -0
  442. package/src/core/sources/contentful-rich-text.ts +29 -6
  443. package/src/core/sources/contentful.ts +135 -25
  444. package/src/core/sources/filesystem.ts +26 -4
  445. package/src/core/sources/github-releases.ts +12 -1
  446. package/src/core/sources/lexical.ts +15 -10
  447. package/src/core/sources/mdx-remote.ts +62 -10
  448. package/src/core/sources/normalize.ts +228 -41
  449. package/src/core/sources/notion.ts +7 -1
  450. package/src/core/sources/obsidian.ts +16 -3
  451. package/src/core/sources/payload.ts +38 -15
  452. package/src/core/sources/read.ts +8 -3
  453. package/src/core/sources/remote.ts +72 -15
  454. package/src/core/sources/resolve.ts +11 -1
  455. package/src/core/sources/sanity.ts +43 -25
  456. package/src/core/sources/strapi.ts +2 -2
  457. package/src/core/types.ts +19 -1
  458. package/src/core/ui-packs/ar.ts +44 -0
  459. package/src/core/ui-packs/bg.ts +46 -0
  460. package/src/core/ui-packs/bn.ts +44 -0
  461. package/src/core/ui-packs/ca.ts +47 -0
  462. package/src/core/ui-packs/cs.ts +45 -0
  463. package/src/core/ui-packs/da.ts +46 -0
  464. package/src/core/ui-packs/de.ts +47 -0
  465. package/src/core/ui-packs/el.ts +46 -0
  466. package/src/core/ui-packs/es.ts +47 -0
  467. package/src/core/ui-packs/fa.ts +46 -0
  468. package/src/core/ui-packs/fi.ts +46 -0
  469. package/src/core/ui-packs/fr.ts +47 -0
  470. package/src/core/ui-packs/he.ts +44 -0
  471. package/src/core/ui-packs/hi.ts +45 -0
  472. package/src/core/ui-packs/hr.ts +46 -0
  473. package/src/core/ui-packs/hu.ts +45 -0
  474. package/src/core/ui-packs/id.ts +46 -0
  475. package/src/core/ui-packs/it.ts +46 -0
  476. package/src/core/ui-packs/ja.ts +46 -0
  477. package/src/core/ui-packs/ko.ts +45 -0
  478. package/src/core/ui-packs/nl.ts +47 -0
  479. package/src/core/ui-packs/no.ts +45 -0
  480. package/src/core/ui-packs/pl.ts +46 -0
  481. package/src/core/ui-packs/pt-br.ts +47 -0
  482. package/src/core/ui-packs/pt.ts +46 -0
  483. package/src/core/ui-packs/ro.ts +46 -0
  484. package/src/core/ui-packs/ru.ts +46 -0
  485. package/src/core/ui-packs/sk.ts +45 -0
  486. package/src/core/ui-packs/sr.ts +46 -0
  487. package/src/core/ui-packs/sv.ts +45 -0
  488. package/src/core/ui-packs/th.ts +44 -0
  489. package/src/core/ui-packs/tr.ts +46 -0
  490. package/src/core/ui-packs/uk.ts +46 -0
  491. package/src/core/ui-packs/vi.ts +46 -0
  492. package/src/core/ui-packs/zh-tw.ts +44 -0
  493. package/src/core/ui-packs/zh.ts +44 -0
  494. package/src/core/variables.ts +103 -0
  495. package/src/deploy/adapter-output.ts +7 -2
  496. package/src/deploy/adapters/cloudflare.ts +7 -5
  497. package/src/deploy/adapters/node.ts +47 -9
  498. package/src/deploy/adapters/types.ts +7 -3
  499. package/src/deploy/artifacts.ts +79 -9
  500. package/src/deploy/cloudflare-negotiation.ts +35 -5
  501. package/src/deploy/headers.ts +2 -1
  502. package/src/deploy/node-headers.ts +63 -9
  503. package/src/deploy/platforms/cloudflare.ts +45 -9
  504. package/src/deploy/platforms/netlify.ts +35 -2
  505. package/src/deploy/platforms/node.ts +12 -2
  506. package/src/deploy/platforms/types.ts +21 -7
  507. package/src/deploy/platforms/vercel.ts +149 -17
  508. package/src/deploy/redirects.ts +151 -24
  509. package/src/deploy/rss.ts +3 -1
  510. package/src/deploy/sitemap.ts +39 -5
  511. package/src/deploy/vercel-negotiation.ts +326 -102
  512. package/src/eval/agents.ts +18 -1
  513. package/src/eval/prompts.ts +6 -3
  514. package/src/eval/report.ts +34 -13
  515. package/src/eval/run.ts +5 -0
  516. package/src/markdown/api-rail.ts +259 -0
  517. package/src/markdown/code-title.ts +35 -9
  518. package/src/markdown/features.ts +3 -2
  519. package/src/markdown/fence-meta.ts +2 -0
  520. package/src/markdown/include.ts +11 -1
  521. package/src/markdown/index.ts +28 -5
  522. package/src/markdown/math.ts +4 -4
  523. package/src/markdown/relative-links.ts +3 -2
  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 +7 -6
  531. package/src/openapi/checks.ts +273 -0
  532. package/src/openapi/graphql-build.ts +21 -0
  533. package/src/openapi/graphql.ts +11 -0
  534. package/src/openapi/model.ts +72 -27
  535. package/src/openapi/overlay.ts +256 -0
  536. package/src/openapi/parse.ts +182 -4
  537. package/src/openapi/references.ts +64 -8
  538. package/src/openapi/render-mdx.ts +5 -1
  539. package/src/openapi/scalar.ts +40 -6
  540. package/src/openapi/source.ts +40 -15
  541. package/src/ratelimit/cloudflare.ts +54 -0
  542. package/src/ratelimit/index.ts +30 -0
  543. package/src/ratelimit/memory.ts +45 -0
  544. package/src/ratelimit/runtime.ts +247 -0
  545. package/src/ratelimit/schema.ts +28 -0
  546. package/src/ratelimit/upstash.ts +60 -0
  547. package/src/ratelimit/wrangler.ts +70 -0
  548. package/src/reference/asyncapi.ts +2 -2
  549. package/src/reference/graphql.ts +20 -3
  550. package/src/reference/index.ts +1 -0
  551. package/src/reference/openapi.ts +21 -7
  552. package/src/reference/options.ts +80 -8
  553. package/src/reference/scalar.ts +33 -8
  554. package/src/registry/eject.ts +23 -5
  555. package/src/registry/registry.ts +7 -0
  556. package/src/search/adapters/locale-scope.ts +30 -0
  557. package/src/search/adapters/typesense.ts +14 -4
  558. package/src/search/build.ts +21 -3
  559. package/src/search/documents.ts +26 -2
  560. package/src/search/orama-index.ts +191 -34
  561. package/src/search/pagefind-ranking.ts +39 -0
  562. package/src/search/source-pages.ts +29 -0
  563. package/src/search/sync/algolia.ts +111 -10
  564. package/src/search/sync/index.ts +52 -13
  565. package/src/search/sync/orama-cloud.ts +3 -0
  566. package/src/search/sync/typesense.ts +76 -26
  567. package/src/seo/metatags.ts +67 -0
  568. package/src/sources/contentful.ts +12 -0
  569. package/src/sources/github-releases.ts +7 -1
  570. package/src/sources/shared.ts +3 -0
  571. package/src/theme/chrome-icons.ts +1 -0
  572. package/src/theme/color.ts +500 -0
  573. package/src/theme/entry.ts +85 -6
  574. package/src/theme/palette.ts +121 -45
  575. package/src/translate/prompts.ts +1 -0
  576. package/src/translate/validate.ts +31 -3
  577. package/src/upgrade/upgrade.ts +4 -171
  578. package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
  579. package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
  580. package/dist/cli/chunk-35d4wj9f.js.map +0 -15
  581. package/dist/cli/chunk-364znk6q.js +0 -75
  582. package/dist/cli/chunk-364znk6q.js.map +0 -11
  583. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  584. package/dist/cli/chunk-8ktnccpt.js.map +0 -10
  585. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  586. package/dist/cli/chunk-b07cmahc.js +0 -145
  587. package/dist/cli/chunk-b07cmahc.js.map +0 -11
  588. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  589. package/dist/cli/chunk-e04dxsz1.js +0 -39
  590. package/dist/cli/chunk-e04dxsz1.js.map +0 -10
  591. package/dist/cli/chunk-j85vccga.js.map +0 -36
  592. package/dist/cli/chunk-p3v96n38.js.map +0 -10
  593. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  594. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  595. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  596. package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
  597. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  598. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  599. package/docs/content/sources.mdx +0 -334
  600. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
  601. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
  602. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
  603. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
  604. /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
  605. /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
  606. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
  607. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
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,
@@ -15,6 +15,8 @@ const uiStringsObject = z.object({
15
15
  .object({
16
16
  addToCursor: z.string().default("Add to Cursor"),
17
17
  addToVscode: z.string().default("Add to VS Code"),
18
+ // A code block's button that opens the assistant with the code attached.
19
+ askAboutCode: z.string().default("Ask about this code"),
18
20
  connectMcp: z.string().default("Connect to MCP"),
19
21
  copied: z.string().default("Copied!"),
20
22
  copyClaudeCode: z.string().default("Copy Claude Code command"),
@@ -47,11 +49,32 @@ const uiStringsObject = z.object({
47
49
  copy: z.string().default("Copy conversation"),
48
50
  empty: z.string().default("Ask a question about the docs."),
49
51
  error: z.string().default("Sorry, something went wrong."),
52
+ // The question sent about an attached code block when the reader
53
+ // writes none.
54
+ explainCode: z.string().default("Explain this code."),
50
55
  label: z.string().default("Ask a question"),
56
+ // The header trigger's tooltip.
57
+ open: z.string().default("Open assistant"),
51
58
  placeholder: z.string().default("Ask a question…"),
59
+ // The answer when the rate limit (`rateLimit`) turns a question away.
60
+ rateLimited: z
61
+ .string()
62
+ .default(
63
+ "You've asked a lot of questions. Try again in a few minutes."
64
+ ),
65
+ // The attached code block's dismiss button.
66
+ removeCode: z.string().default("Remove code"),
52
67
  send: z.string().default("Send"),
68
+ // The link to your support channel (`ai.assistant.support`), and the
69
+ // subject of an email it starts.
70
+ support: z.string().default("Contact support"),
71
+ supportSubject: z.string().default("Question from the docs"),
53
72
  tip: z.string().default("Tip: You can open and close chat with"),
54
73
  title: z.string().default("Assistant"),
74
+ // The answer when the bot check (`ai.assistant.captcha`) fails.
75
+ verifyFailed: z
76
+ .string()
77
+ .default("We couldn't check that you're human. Try again."),
55
78
  you: z.string().default("You"),
56
79
  })
57
80
  .prefault({}),
@@ -67,9 +90,28 @@ const uiStringsObject = z.object({
67
90
  .default(
68
91
  "Product updates, new features, and fixes from every release."
69
92
  ),
93
+ // What the index says when it has no entries to list.
94
+ empty: z.string().default("No changelog entries yet."),
70
95
  title: z.string().default("Changelog"),
71
96
  })
72
97
  .prefault({}),
98
+ // The built-in cookie consent banner (`consent: native()`) and the footer
99
+ // link that reopens a consent manager's preferences.
100
+ consent: z
101
+ .object({
102
+ accept: z.string().default("Accept"),
103
+ decline: z.string().default("Decline"),
104
+ // The banner's accessible name.
105
+ label: z.string().default("Cookie consent"),
106
+ message: z
107
+ .string()
108
+ .default(
109
+ "We'd like to use cookies to understand how these docs are used."
110
+ ),
111
+ policy: z.string().default("Privacy policy"),
112
+ settings: z.string().default("Cookie settings"),
113
+ })
114
+ .prefault({}),
73
115
  content: z
74
116
  .object({
75
117
  // `<Component>`'s source tab.
@@ -89,7 +131,12 @@ const uiStringsObject = z.object({
89
131
  prop: z.string().default("Prop"),
90
132
  // `<Tabs dropdown>`: the accessible name of the tab picker.
91
133
  selectTab: z.string().default("Select tab"),
92
- // `<Expandable>`'s toggle when it sets no `title`.
134
+ // `<View>`: the accessible name of the page's view picker.
135
+ selectView: z.string().default("Select view"),
136
+ // An expanded `expandable` code block's toggle.
137
+ showLess: z.string().default("Show less"),
138
+ // `<Expandable>`'s toggle when it sets no `title`, and a collapsed
139
+ // `expandable` code block's.
93
140
  showMore: z.string().default("Show more"),
94
141
  stars: z.string().default("Stars"),
95
142
  // An untitled tab's label; `{n}` is replaced with its position.
@@ -102,8 +149,11 @@ const uiStringsObject = z.object({
102
149
  .prefault({}),
103
150
  feedback: z
104
151
  .object({
152
+ // The written comment box after the rating (`feedback.comments`).
153
+ comment: z.string().default("Tell us more (optional)"),
105
154
  no: z.string().default("No"),
106
155
  question: z.string().default("Was this page helpful?"),
156
+ send: z.string().default("Send"),
107
157
  thanks: z.string().default("Thanks for your feedback!"),
108
158
  yes: z.string().default("Yes"),
109
159
  })
@@ -114,6 +164,36 @@ const uiStringsObject = z.object({
114
164
  untranslated: z.string().default("Not translated"),
115
165
  })
116
166
  .prefault({}),
167
+ narration: z
168
+ .object({
169
+ // Spoken, not shown: the cues that introduce a callout of each type, a
170
+ // step (`{n}` is its number), a tab (`{title}` is its label), and a
171
+ // collapsible section. End each with a full stop so it reads as its own
172
+ // sentence.
173
+ cueDanger: z.string().default("Danger."),
174
+ cueInfo: z.string().default("Info."),
175
+ cueNote: z.string().default("Note."),
176
+ cueSection: z.string().default("Expandable section."),
177
+ cueStep: z.string().default("Step {n}."),
178
+ cueSuccess: z.string().default("Success."),
179
+ cueTab: z.string().default("{title} tab."),
180
+ cueTip: z.string().default("Tip."),
181
+ cueWarning: z.string().default("Warning."),
182
+ error: z.string().default("The narration couldn't load."),
183
+ // Resumes following the sentence being read after the reader scrolls away.
184
+ follow: z.string().default("Follow along"),
185
+ label: z.string().default("Listen to this page"),
186
+ // The estimated listening time; `{n}` is whole minutes.
187
+ minutes: z.string().default("{n} min"),
188
+ next: z.string().default("Next sentence"),
189
+ pause: z.string().default("Pause"),
190
+ play: z.string().default("Play"),
191
+ previous: z.string().default("Previous sentence"),
192
+ progress: z.string().default("Narration progress"),
193
+ speed: z.string().default("Playback speed"),
194
+ stop: z.string().default("Stop listening"),
195
+ })
196
+ .prefault({}),
117
197
  nav: z
118
198
  .object({
119
199
  back: z.string().default("Back"),
@@ -128,7 +208,7 @@ const uiStringsObject = z.object({
128
208
  primary: z.string().default("Primary"),
129
209
  sections: z.string().default("Sections"),
130
210
  toggleNavigation: z.string().default("Toggle navigation"),
131
- toggleTheme: z.string().default("Toggle color theme"),
211
+ toggleTheme: z.string().default("Toggle theme"),
132
212
  })
133
213
  .prefault({}),
134
214
  notFound: z
@@ -154,6 +234,8 @@ const uiStringsObject = z.object({
154
234
  next: z.string().default("Next"),
155
235
  pagination: z.string().default("Pagination"),
156
236
  previous: z.string().default("Previous"),
237
+ // The heading over a page's `related` links.
238
+ related: z.string().default("Related pages"),
157
239
  skipToContent: z.string().default("Skip to content"),
158
240
  })
159
241
  .prefault({}),
@@ -7,6 +7,7 @@ import type { FenceState } from "./code-fences.ts";
7
7
  import matter from "./frontmatter.ts";
8
8
  import { INLINE_CODE, MD_IMAGE, targetOffsetIn } from "./sources/normalize.ts";
9
9
  import type { Diagnostic } from "./types.ts";
10
+ import { substituteVariables } from "./variables.ts";
10
11
 
11
12
  /**
12
13
  * Content includes: `<include>./relative.mdx</include>` on a line of its own
@@ -98,6 +99,12 @@ export interface IncludeAttributes {
98
99
  lang?: string;
99
100
  /** Fence meta string in code-block mode (e.g. `title="lib.ts"`). */
100
101
  meta?: string;
102
+ /**
103
+ * Every other attribute with a value: the snippet's own variables.
104
+ * `<include plan="Pro">` makes `{{plan}}` in the included file read `Pro`,
105
+ * ahead of any site-wide variable of the same name.
106
+ */
107
+ props?: Record<string, string>;
101
108
  }
102
109
 
103
110
  /** A parsed include statement. */
@@ -108,8 +115,8 @@ export interface IncludeStatement {
108
115
 
109
116
  /**
110
117
  * Parse one trimmed line as an include statement, or `null` when it isn't
111
- * one. Only `lang` and `meta` are meaningful; unknown attributes parse and
112
- * are ignored so a future attribute degrades gracefully on older versions.
118
+ * one. `lang` and `meta` shape a code include; every other attribute with a
119
+ * value becomes a prop, a variable scoped to the included file.
113
120
  */
114
121
  export const parseIncludeStatement = (
115
122
  line: string
@@ -128,6 +135,9 @@ export const parseIncludeStatement = (
128
135
  if (name === "meta" && value) {
129
136
  attributes.meta = value;
130
137
  }
138
+ if (name && name !== "lang" && name !== "meta" && value !== undefined) {
139
+ attributes.props = { ...attributes.props, [name]: value };
140
+ }
131
141
  }
132
142
  return { attributes, target: match.groups.target ?? "" };
133
143
  };
@@ -232,7 +242,7 @@ export const malformedIncludeDiagnostic = (
232
242
  "This <include> isn't a statement Blume can read, so the page shows it as written.",
233
243
  severity: "warning",
234
244
  suggestion:
235
- "Write <include>./path.mdx</include> on a line of its own, or wrap it with the path on its own line and no blank lines; only lang and meta attributes are read.",
245
+ 'Write <include>./path.mdx</include> on a line of its own, or wrap it with the path on its own line and no blank lines. Attributes take lowercase names and quoted values (plan="Pro"): lang and meta shape a code include, and any other is a prop the included file reads as {{name}}.',
236
246
  });
237
247
 
238
248
  /**
@@ -519,8 +529,15 @@ const expandStatement = async (
519
529
  lines.shift();
520
530
  origins.shift();
521
531
  }
532
+ // Props replace their `{{name}}` references line by line, so every line
533
+ // keeps its origin; they reach the file's own nested includes too.
534
+ const { props } = statement.attributes;
522
535
  return {
523
- lines: rebaseImages(lines, dirname(path), dirname(filePath)),
536
+ lines: rebaseImages(
537
+ props ? lines.map((text) => substituteVariables(text, props)) : lines,
538
+ dirname(path),
539
+ dirname(filePath)
540
+ ),
524
541
  origins,
525
542
  };
526
543
  };
package/src/core/links.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  import type { LocaleRouting } from "./i18n.ts";
11
11
  import { localizeLinkPath } from "./locale-links.ts";
12
12
  import { gradeExternal, probeAll } from "./probe.ts";
13
+ import { isPatternPath, pathsUnderPattern } from "./redirect-patterns.ts";
13
14
  import type {
14
15
  ContentGraph,
15
16
  Diagnostic,
@@ -87,6 +88,8 @@ interface LinkContext {
87
88
  publicDir: string | null;
88
89
  /** Normalized `redirect.from` paths — valid targets that resolve at runtime. */
89
90
  redirects: Set<string>;
91
+ /** Pattern `redirect.from`s (`/beta/:slug*`), based like `redirects`. */
92
+ redirectPatterns: string[];
90
93
  routes: Set<string>;
91
94
  }
92
95
 
@@ -309,10 +312,13 @@ const checkPathLink = (
309
312
  ): LinkResult => {
310
313
  // Page routes carry the site-wide base; an absolute author path is written
311
314
  // as if mounted at root, so base it for the route lookup (idempotent — a
312
- // relative link already resolved against the based `page.route`). A real
313
- // route always wins over the asset-extension heuristic, so a dotted route
314
- // (e.g. `/releases/v1.0`) isn't misread as a missing asset.
315
- const authoredRoute = toRoute(withBasePath(ctx.basePath, resolved));
315
+ // relative link already resolved against the based `page.route`). A raw
316
+ // `<a href>` ships unbased, so it's looked up as written. A real route
317
+ // always wins over the asset-extension heuristic, so a dotted route (e.g.
318
+ // `/releases/v1.0`) isn't misread as a missing asset.
319
+ const authoredRoute = toRoute(
320
+ link.raw ? resolved : withBasePath(ctx.basePath, resolved)
321
+ );
316
322
  const route = servedRoute(authoredRoute, page, ctx);
317
323
  if (ctx.routes.has(route)) {
318
324
  return fragment ? checkAnchor(route, fragment, site, ctx, via) : null;
@@ -322,10 +328,16 @@ const checkPathLink = (
322
328
  if (ctx.extraRoutes.has(route)) {
323
329
  return null;
324
330
  }
325
- // A configured `redirect.from` resolves at runtime, so it's a valid target.
331
+ // A configured `redirect.from`, or a path a pattern `from` covers, resolves
332
+ // at runtime, so it's a valid target.
326
333
  // Its destination (and any anchor there) is validated on its own page, so we
327
334
  // don't follow the redirect to check the fragment here.
328
- if (ctx.redirects.has(route)) {
335
+ if (
336
+ ctx.redirects.has(route) ||
337
+ ctx.redirectPatterns.some(
338
+ (from) => pathsUnderPattern(from, [route]).length > 0
339
+ )
340
+ ) {
329
341
  return null;
330
342
  }
331
343
 
@@ -427,6 +439,12 @@ export interface FileRouteIndex {
427
439
  * into the reader's locale onto the fallback copy.
428
440
  */
429
441
  byDefaultNavPath: Map<string, string>;
442
+ /**
443
+ * A staged page's entry id (`<source>/<ref>`) → its route. A remote
444
+ * source's files have no path on disk, so a file link between two of them
445
+ * (`./02-errors.mdx`) resolves by entry id, as the rendered link does.
446
+ */
447
+ byEntryId: Map<string, string>;
430
448
  }
431
449
 
432
450
  /** The linking side of a file link: where the page's source lives. */
@@ -441,12 +459,19 @@ export const buildFileRouteIndex = (
441
459
  ): FileRouteIndex => {
442
460
  const bySource = new Map<string, string>();
443
461
  const byDefaultNavPath = new Map<string, string>();
462
+ const byEntryId = new Map<string, string>();
444
463
  for (const page of pages) {
445
- const { sourcePath } = page;
446
- if (!sourcePath || page.fallback) {
464
+ const { entryId, sourcePath } = page;
465
+ if (page.fallback) {
447
466
  continue;
448
467
  }
449
468
  const isDefault = page.locale === i18n?.defaultLocale;
469
+ if (entryId && (!byEntryId.has(entryId) || isDefault)) {
470
+ byEntryId.set(entryId, page.route);
471
+ }
472
+ if (!sourcePath) {
473
+ continue;
474
+ }
450
475
  if (!bySource.has(sourcePath) || isDefault) {
451
476
  bySource.set(sourcePath, page.route);
452
477
  }
@@ -454,7 +479,7 @@ export const buildFileRouteIndex = (
454
479
  byDefaultNavPath.set(normalize(page.navPath), page.route);
455
480
  }
456
481
  }
457
- return { byDefaultNavPath, bySource };
482
+ return { byDefaultNavPath, byEntryId, bySource };
458
483
  };
459
484
 
460
485
  /**
@@ -483,11 +508,15 @@ const relativeTarget = (
483
508
  ctx: LinkContext
484
509
  ): string => {
485
510
  const base = { isIndex: isIndexPage(page), route: page.route };
486
- const { navPath, sourcePath } = page;
487
- const resolveFile = sourcePath
488
- ? (path: string) =>
489
- routeOfLinkedFile(ctx.fileRoutes, { navPath, sourcePath }, path)
490
- : undefined;
511
+ const { entryId, navPath, sourcePath } = page;
512
+ let resolveFile: ((path: string) => string | undefined) | undefined;
513
+ if (sourcePath) {
514
+ resolveFile = (path) =>
515
+ routeOfLinkedFile(ctx.fileRoutes, { navPath, sourcePath }, path);
516
+ } else if (entryId) {
517
+ resolveFile = (path) =>
518
+ ctx.fileRoutes.byEntryId.get(normalize(join(dirname(entryId), path)));
519
+ }
491
520
  return (
492
521
  resolveRelativeHref(rawPath, base, resolveFile, (route) =>
493
522
  ctx.routes.has(route)
@@ -546,9 +575,14 @@ const classifyLink = (
546
575
  return fragment ? checkAnchor(page.route, fragment, site, ctx, via) : null;
547
576
  }
548
577
 
549
- const resolved = rawPath.startsWith("/")
550
- ? rawPath
551
- : relativeTarget(page, rawPath, ctx);
578
+ let resolved = rawPath;
579
+ if (!rawPath.startsWith("/")) {
580
+ // A raw `<a href>` isn't rewritten, so the browser resolves it against
581
+ // the page's slashless URL: its parent directory, even on an index page.
582
+ resolved = link.raw
583
+ ? resolveRelative(page.route, rawPath, false)
584
+ : relativeTarget(page, rawPath, ctx);
585
+ }
552
586
  return checkPathLink(resolved, fragment, page, link, site, ctx, via);
553
587
  };
554
588
 
@@ -573,6 +607,8 @@ export const validateLinks = async (
573
607
  i18n?: LocaleRouting | null;
574
608
  publicDir: string | null;
575
609
  checkExternal?: boolean;
610
+ /** External URLs not to request (`--ignore`). Internal links are always checked. */
611
+ ignore?: (url: string) => boolean;
576
612
  /** Configured redirects; their `from` paths count as valid link targets. */
577
613
  redirects?: { from: string }[];
578
614
  }
@@ -585,6 +621,11 @@ export const validateLinks = async (
585
621
  fileRoutes: buildFileRouteIndex(graph.pages, options.i18n ?? null),
586
622
  i18n: options.i18n ?? null,
587
623
  publicDir: options.publicDir,
624
+ redirectPatterns: (options.redirects ?? []).flatMap((redirect) =>
625
+ isPatternPath(redirect.from)
626
+ ? [withBasePath(basePath, redirect.from)]
627
+ : []
628
+ ),
588
629
  redirects: new Set(
589
630
  (options.redirects ?? []).map((redirect) =>
590
631
  toRoute(withBasePath(basePath, redirect.from))
@@ -615,8 +656,11 @@ export const validateLinks = async (
615
656
  });
616
657
  }
617
658
 
618
- if (options.checkExternal && external.length > 0) {
619
- diagnostics.push(...(await checkExternalLinks(external)));
659
+ // `--ignore` names URLs that can't be checked from where this runs: a
660
+ // placeholder host, a local server, a site that turns bots away.
661
+ const probed = external.filter((ref) => !options.ignore?.(ref.url));
662
+ if (options.checkExternal && probed.length > 0) {
663
+ diagnostics.push(...(await checkExternalLinks(probed)));
620
664
  }
621
665
 
622
666
  // A partial spliced into several pages (or every locale of one) yields the
@@ -0,0 +1,26 @@
1
+ import type { LocalizableLabel } from "./schema.ts";
2
+
3
+ /** A label is either one string for every locale or a per-locale map. */
4
+ const isSingleLabel = (label: LocalizableLabel): label is string =>
5
+ typeof label === "string";
6
+
7
+ /**
8
+ * Resolve a possibly-per-locale label to the string a locale renders: the
9
+ * active locale's entry, else the default locale's, else the map's first
10
+ * entry (which is also what a single-locale site gets).
11
+ */
12
+ export const resolveLocalizable = (
13
+ label: LocalizableLabel,
14
+ locale?: string,
15
+ defaultLocale?: string
16
+ ): string => {
17
+ if (isSingleLabel(label)) {
18
+ return label;
19
+ }
20
+ return (
21
+ (locale === undefined ? undefined : label[locale]) ??
22
+ (defaultLocale === undefined ? undefined : label[defaultLocale]) ??
23
+ Object.values(label)[0] ??
24
+ ""
25
+ );
26
+ };
@@ -3,6 +3,7 @@ import { extname } from "pathe";
3
3
  import { stripBasePath, withBasePath } from "./base-path.ts";
4
4
  import { orderingPrefix, stripOrderingPrefix } from "./ordering-prefix.ts";
5
5
  import type {
6
+ DirectoryMode,
6
7
  FolderMeta,
7
8
  SidebarDisplay,
8
9
  SidebarItemConfig,
@@ -202,6 +203,7 @@ interface MutableGroup {
202
203
  label: string;
203
204
  icon?: string;
204
205
  collapsed?: boolean;
206
+ directory?: DirectoryMode;
205
207
  display?: SidebarDisplay;
206
208
  order: number;
207
209
  /** Whether `order` is the node's position in its folder meta `pages` list. */
@@ -305,9 +307,19 @@ interface FallbackFolderMeta {
305
307
  meta: (path: string) => FolderMeta | undefined;
306
308
  }
307
309
 
310
+ /** A folder meta's own fields onto its group, over what the folder implied. */
311
+ const applyMetaFields = (group: MutableGroup, meta: FolderMeta): void => {
312
+ group.label = meta.title ?? group.label;
313
+ group.icon = meta.icon ?? group.icon;
314
+ group.order = meta.order ?? group.order;
315
+ group.collapsed = meta.collapsed ?? group.collapsed;
316
+ group.directory = meta.directory ?? group.directory;
317
+ };
318
+
308
319
  /**
309
- * Apply folder meta (title/order/icon/collapsed/display and explicit page
310
- * order), plus the index-frontmatter display sugar collected per folder path.
320
+ * Apply folder meta (title/order/icon/collapsed/display/directory and
321
+ * explicit page order), plus the index-frontmatter display sugar collected
322
+ * per folder path.
311
323
  */
312
324
  const applyFolderMeta = (
313
325
  group: MutableGroup,
@@ -346,10 +358,7 @@ const applyFolderMeta = (
346
358
  (mirrored ? fallback.indexDisplay.get(group.path) : undefined) ??
347
359
  meta?.display;
348
360
  if (meta) {
349
- group.label = meta.title ?? group.label;
350
- group.icon = meta.icon ?? group.icon;
351
- group.order = meta.order ?? group.order;
352
- group.collapsed = meta.collapsed ?? group.collapsed;
361
+ applyMetaFields(group, meta);
353
362
  }
354
363
 
355
364
  for (const child of group.children) {
@@ -630,7 +639,15 @@ const hoistTabSections = (
630
639
  }
631
640
  };
632
641
 
633
- const toNavNode = (node: MutableNode, display: SidebarDisplay): NavNode => {
642
+ /**
643
+ * A group's directory mode: its own, else the nearest one set above it (see
644
+ * `directory` in `schema.ts`).
645
+ */
646
+ const toNavNode = (
647
+ node: MutableNode,
648
+ display: SidebarDisplay,
649
+ inheritedDirectory?: DirectoryMode
650
+ ): NavNode => {
634
651
  if (node.kind === "page") {
635
652
  return {
636
653
  badge: node.badge,
@@ -643,9 +660,13 @@ const toNavNode = (node: MutableNode, display: SidebarDisplay): NavNode => {
643
660
  route: node.route,
644
661
  };
645
662
  }
663
+ const directory = node.directory ?? inheritedDirectory;
646
664
  return {
647
- children: node.children.map((child) => toNavNode(child, display)),
665
+ children: node.children.map((child) =>
666
+ toNavNode(child, display, directory)
667
+ ),
648
668
  collapsed: node.collapsed,
669
+ directory,
649
670
  display: node.display ?? display,
650
671
  icon: node.icon,
651
672
  kind: "group",
@@ -805,7 +826,9 @@ const buildFileSystemSidebar = (
805
826
  sortNodes(root.children, diagnostics);
806
827
  hoistPages(root.children, display, true);
807
828
  hoistTabSections(root.children, tabPaths, display);
808
- return root.children.map((child) => toNavNode(child, display));
829
+ return root.children.map((child) =>
830
+ toNavNode(child, display, root.directory)
831
+ );
809
832
  };
810
833
 
811
834
  /**
@@ -932,16 +955,24 @@ const buildConfigSidebar = (
932
955
  items: SidebarItemConfig[],
933
956
  byRoute: Map<string, PageRecord>,
934
957
  display: SidebarDisplay,
935
- basePath: string
958
+ basePath: string,
959
+ inheritedDirectory?: DirectoryMode
936
960
  ): NavNode[] => {
937
961
  const nodes: NavNode[] = [];
938
962
  for (const item of items) {
939
963
  if (!isPageRef(item) && item.items) {
964
+ const directory = item.directory ?? inheritedDirectory;
940
965
  nodes.push({
941
966
  badge: item.badge,
942
- children: buildConfigSidebar(item.items, byRoute, display, basePath),
967
+ children: buildConfigSidebar(
968
+ item.items,
969
+ byRoute,
970
+ display,
971
+ basePath,
972
+ directory
973
+ ),
943
974
  collapsed: item.collapsed,
944
- directory: item.directory,
975
+ directory,
945
976
  display: item.display ?? display,
946
977
  icon: item.icon,
947
978
  kind: "group",