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
@@ -0,0 +1,94 @@
1
+ ---
2
+ import AccordionItem from "../content/AccordionItem.astro";
3
+ import Card from "../content/Card.astro";
4
+ import CardGroup from "../content/CardGroup.astro";
5
+ import Icon from "../Icon.astro";
6
+ import { directoryEntries } from "./nav-utils.ts";
7
+ import type { NavGroup } from "./nav-utils.ts";
8
+
9
+ /**
10
+ * A group page's directory: the group's other pages listed below the page's
11
+ * content, as cards or as an accordion whose subgroups open to their pages
12
+ * (the group's `directory`, see `core/schema.ts`).
13
+ */
14
+ interface Props {
15
+ group: NavGroup;
16
+ }
17
+
18
+ const { group } = Astro.props;
19
+ const entries = directoryEntries(group);
20
+ ---
21
+
22
+ {
23
+ entries.length > 0 && (
24
+ <nav
25
+ aria-label={group.label}
26
+ class="mx-auto mt-6 max-w-content"
27
+ data-blume-directory={group.directory}
28
+ >
29
+ {group.directory === "card" ? (
30
+ <CardGroup>
31
+ {entries.map((entry) => (
32
+ <Card href={entry.href} icon={entry.icon} title={entry.label}>
33
+ {entry.description}
34
+ </Card>
35
+ ))}
36
+ </CardGroup>
37
+ ) : (
38
+ <div class="not-prose my-6 divide-y divide-border overflow-hidden rounded-blume border border-border">
39
+ {entries.map((entry, index) =>
40
+ entry.pages ? (
41
+ <AccordionItem
42
+ description={entry.description}
43
+ icon={entry.icon}
44
+ id={`blume-directory-${index + 1}`}
45
+ title={entry.label}
46
+ >
47
+ <ul class="grid gap-2 text-sm">
48
+ {entry.pages.map((page) => (
49
+ <li>
50
+ <a
51
+ class="text-muted-foreground no-underline transition-colors hover:text-foreground"
52
+ href={page.route}
53
+ >
54
+ {page.label}
55
+ </a>
56
+ </li>
57
+ ))}
58
+ </ul>
59
+ </AccordionItem>
60
+ ) : (
61
+ <a
62
+ class="flex items-center justify-between gap-4 p-4 font-medium text-foreground no-underline transition-colors hover:bg-muted/40"
63
+ href={entry.href}
64
+ >
65
+ <span class="flex items-start gap-2">
66
+ {entry.icon && (
67
+ <Icon
68
+ class="mt-0.5 shrink-0 text-muted-foreground"
69
+ name={entry.icon}
70
+ size={16}
71
+ />
72
+ )}
73
+ <span>
74
+ <span class="block">{entry.label}</span>
75
+ {entry.description && (
76
+ <span class="block font-normal text-muted-foreground text-sm">
77
+ {entry.description}
78
+ </span>
79
+ )}
80
+ </span>
81
+ </span>
82
+ <Icon
83
+ class="shrink-0 text-muted-foreground rtl:-scale-x-100"
84
+ name="arrow-right"
85
+ size={16}
86
+ />
87
+ </a>
88
+ )
89
+ )}
90
+ </div>
91
+ )}
92
+ </nav>
93
+ )
94
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  import type { DiscoveryHeadLinks } from "../../core/data.ts";
3
- import { withBase } from "../islands/base-path.ts";
3
+ import { withMountedBase } from "../islands/base-path.ts";
4
4
  // Emits the agent-discovery <link>s for the document <head>, on every page and
5
5
  // not just the root: an agent that enters on a deep page (a search result, a
6
6
  // shared link) never sees the homepage HTTP `Link` header (see
@@ -32,7 +32,7 @@ const { discovery, markdownMirror } = Astro.props;
32
32
  {
33
33
  discovery?.agentReadability && (
34
34
  <link
35
- href={withBase("/agent-readability.json")}
35
+ href={withMountedBase("/agent-readability.json")}
36
36
  rel="describedby"
37
37
  type="application/json"
38
38
  />
@@ -40,7 +40,11 @@ const { discovery, markdownMirror } = Astro.props;
40
40
  }
41
41
  {
42
42
  discovery?.llmsTxt && (
43
- <link href={withBase("/llms.txt")} rel="describedby" type="text/plain" />
43
+ <link
44
+ href={withMountedBase("/llms.txt")}
45
+ rel="describedby"
46
+ type="text/plain"
47
+ />
44
48
  )
45
49
  }
46
50
  {/* The AI Catalog / ARD manifest under both relations its two specs define:
@@ -50,12 +54,12 @@ const { discovery, markdownMirror } = Astro.props;
50
54
  discovery?.aiCatalog && (
51
55
  <>
52
56
  <link
53
- href={withBase("/.well-known/ai-catalog.json")}
57
+ href={withMountedBase("/.well-known/ai-catalog.json")}
54
58
  rel="ai-catalog"
55
59
  type="application/ai-catalog+json"
56
60
  />
57
61
  <link
58
- href={withBase("/.well-known/ard.json")}
62
+ href={withMountedBase("/.well-known/ard.json")}
59
63
  rel="ard"
60
64
  type="application/json"
61
65
  />
@@ -0,0 +1,51 @@
1
+ ---
2
+ // The header's selectors, version selector, and language switcher, as a row
3
+ // at the top of the nav drawer. Below `lg` the sidebar is this drawer, and the
4
+ // header has no room for them beside the logo, tabs, search, and icon
5
+ // buttons, so it hides them whenever there's a drawer to open and this row
6
+ // takes over; from `lg` up the row hides and the header shows them again.
7
+ import type { UIStrings } from "../../core/i18n-ui.ts";
8
+ import type {
9
+ LocaleSwitchOption,
10
+ NavSelector as NavSelectorConfig,
11
+ } from "../../core/types.ts";
12
+ import LanguageSwitcher from "./LanguageSwitcher.astro";
13
+ import NavSelector from "./NavSelector.astro";
14
+
15
+ interface Props {
16
+ selectors: NavSelectorConfig[];
17
+ versionSelector?: NavSelectorConfig | null;
18
+ localeSwitch?: LocaleSwitchOption[];
19
+ route: string;
20
+ switcherStrings?: UIStrings["languageSwitcher"];
21
+ }
22
+
23
+ const { selectors, versionSelector, localeSwitch, route, switcherStrings } =
24
+ Astro.props;
25
+ const hasLocales = (localeSwitch?.length ?? 0) > 1;
26
+ ---
27
+
28
+ {
29
+ (selectors.length > 0 || versionSelector || hasLocales) && (
30
+ <div class="mb-4 flex flex-col gap-2 border-border border-b pb-4 lg:hidden">
31
+ {selectors.map((selector) => (
32
+ <NavSelector route={route} selector={selector} variant="drawer" />
33
+ ))}
34
+ {versionSelector && (
35
+ <NavSelector
36
+ route={route}
37
+ selector={versionSelector}
38
+ variant="drawer"
39
+ />
40
+ )}
41
+ {localeSwitch && hasLocales && (
42
+ <LanguageSwitcher
43
+ label={switcherStrings?.label ?? "Language"}
44
+ options={localeSwitch}
45
+ untranslatedLabel={switcherStrings?.untranslated ?? "Not translated"}
46
+ variant="drawer"
47
+ />
48
+ )}
49
+ </div>
50
+ )
51
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  // A no-op layout slot: renders nothing regardless of props. Used as the built-in
3
- // fallback for the content-injection slots (Footer, PageHeader, PageFooter) that
4
- // have no default, so an unconfigured slot renders empty while `resolveSlot`
3
+ // fallback for the content-injection slots (PageHeader, PageFooter) that have
4
+ // no default, so an unconfigured slot renders empty while `resolveSlot`
5
5
  // still returns a valid component and the generated pages stay type-safe.
6
6
  ---
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  import Ask from "blume:ask";
3
3
  import data from "blume:data";
4
- import { withBase } from "../islands/base-path.ts";
5
- import { NEW_TAB_ATTRS, newTabAttrs } from "../../core/new-tab.ts";
4
+ import { withBase, withMountedBase } from "../islands/base-path.ts";
5
+ import { newTabAttrs } from "../../core/new-tab.ts";
6
6
  import type { ComponentOverride } from "../../core/define-components.ts";
7
7
  import { EN_UI } from "../../core/i18n-ui.ts";
8
8
  import type { UIStrings } from "../../core/i18n-ui.ts";
@@ -11,7 +11,6 @@ import type {
11
11
  Navigation,
12
12
  NavSelector as NavSelectorConfig,
13
13
  } from "../../core/types.ts";
14
- import { GITHUB_MARK } from "../github-mark.ts";
15
14
  import Icon from "../Icon.astro";
16
15
  import LanguageSwitcher from "./LanguageSwitcher.astro";
17
16
  import Logo from "./Logo.astro";
@@ -129,11 +128,19 @@ const activeTab = currentTabForRoute(navigation.tabs, route, navigation.root);
129
128
  // mobile nav below `lg`, so the inline tabs wait until `lg` to avoid duplicating
130
129
  // the drawer's links between `md` and `lg`.
131
130
  const tabsNavClass = hasSidebar
132
- ? "hidden gap-1 md:flex"
133
- : "hidden gap-1 lg:flex";
131
+ ? "hidden shrink-0 gap-1 md:flex"
132
+ : "hidden shrink-0 gap-1 lg:flex";
134
133
 
135
134
  const iconButton =
136
- "inline-flex size-9 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
135
+ "inline-flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
136
+
137
+ // Below `lg`, where the sidebar is a drawer, the selectors, version selector,
138
+ // and language switcher move into a row at the top of it (see
139
+ // DrawerSwitchers.astro): they scope what the sidebar lists, and beside the
140
+ // logo, tabs, search, and icon buttons they'd overflow a phone or tablet
141
+ // header. With no drawer to open they have nowhere else to go, so they stay.
142
+ // Nothing in the header shrinks except the logo, whose title truncates.
143
+ const switcherClass = showNavToggle ? "hidden shrink-0 lg:block" : "shrink-0";
137
144
 
138
145
  // Header links: plain actions are secondary and hide below `sm`, where the
139
146
  // header has room for the logo and the drawer toggle and nothing else. The one
@@ -186,37 +193,44 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
186
193
  ---
187
194
 
188
195
  <header
189
- class="sticky top-0 z-40 flex h-16 items-center gap-3 bg-background/90 px-4 backdrop-blur md:px-6"
196
+ class="sticky top-0 z-40 flex h-16 items-center gap-2 bg-background/90 px-4 backdrop-blur sm:gap-3 md:px-6 lg:grid lg:grid-cols-[minmax(0,1fr)_auto_minmax(max-content,1fr)]"
190
197
  data-blume-header
191
198
  data-blume-header-transparent={transparent ? "" : undefined}
192
199
  >
193
- {
194
- showNavToggle && (
195
- <button
196
- aria-controls="blume-nav-drawer"
197
- aria-expanded="false"
198
- aria-label={n.toggleNavigation}
199
- class={`${iconButton} lg:hidden`}
200
- data-blume-nav-toggle
201
- type="button"
202
- >
203
- <Icon name="menu" size={20} />
204
- </button>
205
- )
206
- }
207
- <LogoSlot locale={locale} logo={logo} site={site} />
208
- {
209
- navigation.selectors.length > 0 && (
210
- <div class="flex items-center gap-1.5">
211
- {navigation.selectors.map((selector) => (
212
- <NavSelector route={route} selector={selector} />
213
- ))}
214
- </div>
215
- )
216
- }
200
+ {/* From `lg` the header is three columns, so the tabs sit at its center:
201
+ the logo side and the controls side share the rest equally. The logo
202
+ side may shrink to nothing (its title truncates, as on phones); the
203
+ controls never shrink, so when they need more than half, the tabs move
204
+ over rather than overlap. Below `lg` it's one row, tabs after the logo. */}
205
+ <div class="flex min-w-0 items-center gap-2 sm:gap-3 lg:col-start-1">
206
+ {
207
+ showNavToggle && (
208
+ <button
209
+ aria-controls="blume-nav-drawer"
210
+ aria-expanded="false"
211
+ aria-label={n.toggleNavigation}
212
+ class={`${iconButton} lg:hidden`}
213
+ data-blume-nav-toggle
214
+ type="button"
215
+ >
216
+ <Icon name="menu" size={20} />
217
+ </button>
218
+ )
219
+ }
220
+ <LogoSlot locale={locale} logo={logo} site={site} />
221
+ {
222
+ navigation.selectors.length > 0 && (
223
+ <div class:list={["flex items-center gap-1.5", switcherClass]}>
224
+ {navigation.selectors.map((selector) => (
225
+ <NavSelector route={route} selector={selector} />
226
+ ))}
227
+ </div>
228
+ )
229
+ }
230
+ </div>
217
231
  {
218
232
  navigation.tabs.length > 0 && (
219
- <nav aria-label={n.sections} class={tabsNavClass}>
233
+ <nav aria-label={n.sections} class:list={[tabsNavClass, "lg:col-start-2"]}>
220
234
  {navigation.tabs.map((tab) =>
221
235
  tab.items && tab.items.length > 0 ? (
222
236
  <NavTabMenu
@@ -229,7 +243,7 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
229
243
  <a
230
244
  aria-current={tab === activeTab ? "page" : undefined}
231
245
  class="inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:text-foreground"
232
- href={withBase(tab.href ?? tab.path)}
246
+ href={withMountedBase(tab.href ?? tab.path)}
233
247
  >
234
248
  {tab.icon && (
235
249
  <Icon class="shrink-0" name={tab.icon} size={14} />
@@ -241,38 +255,31 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
241
255
  </nav>
242
256
  )
243
257
  }
244
- <div class="flex-1"></div>
245
- {
246
- /* Right-aligned next to the language switcher: the spacer absorbs its
247
- width, so pages without a version selector (blog, generated references)
248
- keep the logo, tabs, and the rest of this cluster in place — no layout
249
- shift when crossing into the docs. */
250
- versionSelector && (
251
- <NavSelector align="end" route={route} selector={versionSelector} />
252
- )
253
- }
254
- {
255
- localeSwitch && localeSwitch.length > 1 && (
256
- <LanguageSwitcher
257
- label={switcherStrings?.label ?? "Language"}
258
- options={localeSwitch}
259
- untranslatedLabel={switcherStrings?.untranslated ?? "Not translated"}
260
- />
261
- )
262
- }
263
- {
264
- searchEnabled && (
265
- <SearchSlot
266
- assistantEnabled={assistantEnabled}
267
- locale={searchLocale}
268
- navigation={navigation}
269
- popularPages={data.config.search.popular}
270
- strings={searchStrings}
271
- version={searchVersion}
272
- />
273
- )
274
- }
275
- <div class="flex items-center gap-2">
258
+ <div class="flex-1 lg:hidden"></div>
259
+ {/* Everything on the right sits in one group with a tight gap: the version
260
+ and language pickers, the header links, and the icon buttons. Below `lg`
261
+ the spacer absorbs its width, so pages without a version selector (blog,
262
+ generated references) keep the logo and tabs in place — no layout shift
263
+ when crossing into the docs. */}
264
+ <div class="flex shrink-0 items-center gap-1 lg:col-start-3 lg:justify-self-end">
265
+ {
266
+ versionSelector && (
267
+ <div class={switcherClass}>
268
+ <NavSelector align="end" route={route} selector={versionSelector} />
269
+ </div>
270
+ )
271
+ }
272
+ {
273
+ localeSwitch && localeSwitch.length > 1 && (
274
+ <div class={switcherClass}>
275
+ <LanguageSwitcher
276
+ label={switcherStrings?.label ?? "Language"}
277
+ options={localeSwitch}
278
+ untranslatedLabel={switcherStrings?.untranslated ?? "Not translated"}
279
+ />
280
+ </div>
281
+ )
282
+ }
276
283
  {
277
284
  /* Plain links first, then the one filled button: the reader's eye lands
278
285
  on the button because nothing beside it competes. */
@@ -299,29 +306,24 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
299
306
  )
300
307
  }
301
308
  {
302
- navigation.repoUrl && (
303
- <a
304
- aria-label={n.githubRepository}
305
- class={iconButton}
306
- href={navigation.repoUrl}
307
- {...NEW_TAB_ATTRS}
308
- >
309
- <svg
310
- aria-hidden="true"
311
- fill="currentColor"
312
- height="18"
313
- set:html={GITHUB_MARK}
314
- viewBox="0 0 16 16"
315
- width="18"
316
- xmlns="http://www.w3.org/2000/svg"
317
- />
318
- </a>
309
+ /* The icon buttons, each named by a tooltip. The site's repository and
310
+ social profiles live in the footer. */
311
+ searchEnabled && (
312
+ <SearchSlot
313
+ assistantEnabled={assistantEnabled}
314
+ locale={searchLocale}
315
+ navigation={navigation}
316
+ popularPages={data.config.search.popular}
317
+ strings={searchStrings}
318
+ version={searchVersion}
319
+ />
319
320
  )
320
321
  }
321
322
  <button
322
323
  aria-label={n.toggleTheme}
323
324
  class={iconButton}
324
325
  data-blume-theme-toggle
326
+ data-blume-tooltip={n.toggleTheme}
325
327
  type="button"
326
328
  >
327
329
  <span class="inline-flex dark:hidden"><Icon name="sun" size={18} /></span>
@@ -334,6 +336,46 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
334
336
  {transparent && <script is:inline set:html={transparentScript} />}
335
337
 
336
338
  <style is:global>
339
+ /* The header's icon buttons are named by a tooltip below them, shown on hover
340
+ (where there is one) and on keyboard focus. It hangs from the button's end
341
+ edge, so the last button's tooltip never runs past the viewport, and it
342
+ keeps the page's own colors under a transparent header. */
343
+ [data-blume-tooltip] {
344
+ position: relative;
345
+ }
346
+ [data-blume-tooltip]::after {
347
+ content: attr(data-blume-tooltip);
348
+ position: absolute;
349
+ top: calc(100% + 0.375rem);
350
+ inset-inline-end: 0;
351
+ z-index: 50;
352
+ padding: 0.25rem 0.5rem;
353
+ border-radius: 0.375rem;
354
+ background-color: var(--blume-header-foreground);
355
+ color: var(--blume-header-background);
356
+ font-size: 0.75rem;
357
+ font-weight: 500;
358
+ line-height: 1rem;
359
+ white-space: nowrap;
360
+ pointer-events: none;
361
+ opacity: 0;
362
+ visibility: hidden;
363
+ transition:
364
+ opacity 120ms ease,
365
+ visibility 0s linear 120ms;
366
+ }
367
+ [data-blume-tooltip]:focus-visible::after {
368
+ opacity: 1;
369
+ visibility: visible;
370
+ transition: opacity 120ms ease;
371
+ }
372
+ @media (hover: hover) {
373
+ [data-blume-tooltip]:hover::after {
374
+ opacity: 1;
375
+ visibility: visible;
376
+ transition: opacity 120ms ease 400ms;
377
+ }
378
+ }
337
379
  /* Transparent header (PageLayout `transparentHeader`): at the top of the
338
380
  page the bar has no background and its chrome renders in white so it can
339
381
  sit over a dark hero; once scrolled it is the frosted bar again. The color
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { withBase } from "../islands/base-path.ts";
2
+ import { withMountedBase } from "../islands/base-path.ts";
3
3
  import type { LocaleSwitchOption } from "../../core/types.ts";
4
4
  import Icon from "../Icon.astro";
5
5
 
@@ -7,52 +7,82 @@ interface Props {
7
7
  options: LocaleSwitchOption[];
8
8
  label: string;
9
9
  untranslatedLabel: string;
10
+ /**
11
+ * `bar` (the default) for the header; `drawer` for the top of the nav
12
+ * drawer, where the trigger spans the drawer and opens a panel as wide as
13
+ * itself, so nothing reaches past the drawer edge.
14
+ */
15
+ variant?: "bar" | "drawer";
10
16
  }
11
17
 
12
- const { options, label, untranslatedLabel } = Astro.props;
18
+ const { options, label, untranslatedLabel, variant = "bar" } = Astro.props;
13
19
  const current = options.find((option) => option.current);
14
20
  // The accessible name carries the visible language name too (WCAG 2.5.3,
15
21
  // label in name), so a voice-control user can say "English" to open it.
16
22
  const summaryLabel = current ? `${label}: ${current.label}` : label;
17
23
 
18
- const iconButton =
19
- "inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full border border-border bg-background px-3 text-muted-foreground text-sm transition-colors hover:border-foreground hover:text-foreground";
24
+ // In the header the trigger is styled like the icon buttons beside it: no
25
+ // border, a background on hover and while open. The drawer's spans the
26
+ // drawer, where the border marks it as a control.
27
+ const triggerClass =
28
+ variant === "bar"
29
+ ? "inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full px-3 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground group-open:bg-muted group-open:text-foreground"
30
+ : "inline-flex h-9 w-full cursor-pointer items-center gap-1.5 rounded-full border border-border bg-background px-3 text-muted-foreground text-sm transition-colors hover:border-foreground hover:text-foreground";
20
31
  const menuRowClass =
21
- "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-start text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground";
32
+ "flex w-full items-center gap-4 rounded-md px-2 py-1.5 text-start text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground";
22
33
  ---
23
34
 
24
35
  {
25
36
  options.length > 1 && (
26
- <details class="group relative" data-blume-dropdown>
37
+ <details
38
+ class:list={["group relative", variant === "drawer" && "w-full"]}
39
+ data-blume-dropdown
40
+ >
27
41
  <summary
28
42
  aria-label={summaryLabel}
29
- class={`${iconButton} list-none [&::-webkit-details-marker]:hidden`}
43
+ class:list={[triggerClass, "list-none [&::-webkit-details-marker]:hidden"]}
30
44
  >
31
- <Icon name="globe" size={16} />
32
- {/* Collapses to the globe icon below `lg`, matching the search field. */}
33
- <span class="max-lg:hidden">{current?.label ?? label}</span>
45
+ <span class:list={[variant === "drawer" && "flex-1 text-start"]}>
46
+ {current?.label ?? label}
47
+ </span>
34
48
  <Icon
35
49
  class="transition-transform group-open:rotate-180"
36
50
  name="chevron-down"
37
51
  size={14}
38
52
  />
39
53
  </summary>
40
- <div class="absolute end-0 z-50 mt-2 min-w-44 rounded-blume border border-border bg-background p-1 shadow-xl">
54
+ <div
55
+ class:list={[
56
+ "absolute z-50 mt-2 rounded-blume border border-border bg-background p-1 shadow-xl",
57
+ // As wide as its longest row: the panel hangs from a narrow trigger,
58
+ // so it wouldn't otherwise grow past its minimum.
59
+ variant === "bar"
60
+ ? "end-0 w-max min-w-44 max-w-[calc(100vw-1rem)]"
61
+ : "inset-x-0",
62
+ ]}
63
+ >
41
64
  {options.map((option) => (
42
65
  <a
43
66
  aria-current={option.current ? "true" : undefined}
44
67
  class={menuRowClass}
45
- href={withBase(option.href)}
68
+ data-blume-locale-choice
69
+ href={withMountedBase(option.href)}
46
70
  hreflang={option.code}
47
71
  lang={option.code}
48
72
  >
49
- <span class="flex-1">{option.label}</span>
50
- {option.untranslated && (
51
- <span class="text-muted-foreground text-xs italic">
52
- {untranslatedLabel}
73
+ {/* The name keeps its full width, so the panel grows to fit the
74
+ longest row; the note and check sit at the far end. */}
75
+ <span>{option.label}</span>
76
+ {(option.untranslated || option.current) && (
77
+ <span class="ms-auto flex items-center gap-2.5">
78
+ {option.untranslated && (
79
+ <span class="text-muted-foreground text-xs italic">
80
+ {untranslatedLabel}
81
+ </span>
82
+ )}
83
+ {option.current && <Icon name="check" size={14} />}
53
84
  </span>
54
85
  )}
55
- {option.current && <Icon name="check" size={14} />}
56
86
  </a>
57
87
  ))}
58
88
  </div>
@@ -62,8 +92,11 @@ const menuRowClass =
62
92
 
63
93
  <script>
64
94
  import { installDropdownDismiss } from "../dropdown-dismiss.ts";
95
+ import { rememberLocaleChoice } from "./locale-redirect.ts";
65
96
 
66
97
  // Outside-click / Escape / focus-out dismissal, shared with the nav
67
98
  // selectors and the page actions.
68
99
  installDropdownDismiss();
100
+ // A language picked here stops routing by browser language.
101
+ rememberLocaleChoice(document);
69
102
  </script>