blume 2.0.0 → 2.0.2

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 (454) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +110 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
  5. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  6. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  7. package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
  8. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
  9. package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
  10. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  11. package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
  12. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  13. package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
  14. package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
  15. package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
  16. package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
  17. package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
  18. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  19. package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
  20. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  21. package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
  22. package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
  24. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  25. package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
  26. package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
  27. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  28. package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
  29. package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
  30. package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
  31. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  32. package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
  33. package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
  34. package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
  35. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  36. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  37. package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
  38. package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
  39. package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
  40. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  41. package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
  42. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  43. package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
  44. package/dist/cli/chunk-j85scx15.js.map +10 -0
  45. package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
  46. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  47. package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
  48. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
  49. package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
  50. package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
  51. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  52. package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
  53. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  54. package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
  55. package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
  56. package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
  57. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  58. package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
  59. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
  60. package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
  61. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  62. package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
  63. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  64. package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
  65. package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
  66. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  67. package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
  68. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
  69. package/dist/cli/index.js +160 -35
  70. package/dist/cli/index.js.map +4 -4
  71. package/dist/types/ai/agent-readability.d.ts +1 -1
  72. package/dist/types/ai/agent-surface.d.ts +32 -0
  73. package/dist/types/ai/api/paths.d.ts +1 -1
  74. package/dist/types/ai/api-catalog.d.ts +7 -1
  75. package/dist/types/ai/ask-context.d.ts +14 -7
  76. package/dist/types/ai/ask.d.ts +43 -43
  77. package/dist/types/ai/component-markdown.d.ts +4 -4
  78. package/dist/types/ai/index.d.ts +3 -3
  79. package/dist/types/ai/openapi-components.d.ts +1 -1
  80. package/dist/types/ai/relative-links.d.ts +9 -4
  81. package/dist/types/ai/serializers.d.ts +1 -1
  82. package/dist/types/ai/static-expression.d.ts +28 -0
  83. package/dist/types/ai/visibility.d.ts +1 -1
  84. package/dist/types/analytics/databuddy.d.ts +43 -0
  85. package/dist/types/analytics/index.d.ts +2 -0
  86. package/dist/types/analytics/schema.d.ts +14 -0
  87. package/dist/types/cli/env.d.ts +5 -0
  88. package/dist/types/cli/init/scaffold.d.ts +19 -3
  89. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  90. package/dist/types/core/base-path.d.ts +9 -0
  91. package/dist/types/core/config-input.d.ts +21 -21
  92. package/dist/types/core/config.d.ts +5 -5
  93. package/dist/types/core/data.d.ts +3 -3
  94. package/dist/types/core/graph.d.ts +2 -0
  95. package/dist/types/core/i18n-ui.d.ts +37 -8
  96. package/dist/types/core/i18n.d.ts +7 -1
  97. package/dist/types/core/links.d.ts +3 -1
  98. package/dist/types/core/load-module.d.ts +10 -0
  99. package/dist/types/core/locale-links.d.ts +12 -2
  100. package/dist/types/core/meta.d.ts +5 -1
  101. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  102. package/dist/types/core/navigation.d.ts +38 -0
  103. package/dist/types/core/ordering-prefix.d.ts +4 -0
  104. package/dist/types/core/safe-links.d.ts +3 -1
  105. package/dist/types/core/schema.d.ts +57 -18
  106. package/dist/types/core/sources/github-releases.d.ts +5 -0
  107. package/dist/types/core/sources/lower.d.ts +14 -2
  108. package/dist/types/core/sources/normalize.d.ts +10 -1
  109. package/dist/types/core/sources/remote.d.ts +11 -1
  110. package/dist/types/core/sources/resolve.d.ts +12 -0
  111. package/dist/types/core/sources/types.d.ts +31 -0
  112. package/dist/types/core/types.d.ts +9 -0
  113. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  117. package/dist/types/deploy/headers.d.ts +31 -7
  118. package/dist/types/deploy/node-headers.d.ts +43 -8
  119. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  120. package/dist/types/deploy/platforms/node.d.ts +6 -5
  121. package/dist/types/deploy/platforms/types.d.ts +7 -0
  122. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  123. package/dist/types/deploy/redirects.d.ts +7 -2
  124. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  125. package/dist/types/openapi/model.d.ts +20 -6
  126. package/dist/types/search/documents.d.ts +1 -1
  127. package/dist/types/search/orama-index.d.ts +1 -1
  128. package/dist/types/search/sync/algolia.d.ts +3 -1
  129. package/docs/01-quickstart.mdx +3 -2
  130. package/docs/02-deployment.mdx +21 -12
  131. package/docs/03-upgrading.mdx +22 -9
  132. package/docs/04-migrating.mdx +4 -4
  133. package/docs/08-faq.mdx +12 -5
  134. package/docs/advanced/changelog.mdx +1 -1
  135. package/docs/advanced/custom-pages.mdx +5 -3
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +5 -5
  138. package/docs/cli/doctor.mdx +4 -4
  139. package/docs/cli/evals.mdx +9 -9
  140. package/docs/cli/index.mdx +6 -3
  141. package/docs/cli/translate.mdx +8 -8
  142. package/docs/configuration/analytics.mdx +23 -2
  143. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
  144. package/docs/configuration/customization.mdx +5 -4
  145. package/docs/configuration/index.mdx +7 -5
  146. package/docs/configuration/meta.ts +1 -1
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +1 -1
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -3
  151. package/docs/content/index.mdx +4 -2
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +5 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -12
  156. package/docs/content/versioning.mdx +1 -0
  157. package/docs/discoverability/agent-discovery.mdx +22 -8
  158. package/docs/discoverability/index.mdx +3 -3
  159. package/docs/discoverability/llms-txt.mdx +2 -5
  160. package/docs/discoverability/markdown.mdx +5 -3
  161. package/docs/discoverability/mcp.mdx +4 -0
  162. package/docs/discoverability/metadata.mdx +3 -2
  163. package/docs/discoverability/open-graph.mdx +6 -4
  164. package/docs/discoverability/rss.mdx +3 -1
  165. package/docs/index.mdx +2 -2
  166. package/docs/references/asyncapi.mdx +1 -1
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +3 -3
  169. package/package.json +1 -1
  170. package/skills/blume/SKILL.md +5 -5
  171. package/skills/blume-migrate/SKILL.md +6 -6
  172. package/skills/blume-migrate/references/docusaurus.md +6 -6
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +1 -1
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +4 -4
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/api/paths.ts +1 -1
  182. package/src/ai/api/spec.ts +15 -2
  183. package/src/ai/api-catalog.ts +7 -1
  184. package/src/ai/ask-context.ts +21 -9
  185. package/src/ai/ask-data.ts +28 -14
  186. package/src/ai/ask.ts +84 -71
  187. package/src/ai/component-markdown.ts +43 -37
  188. package/src/ai/cors.ts +3 -3
  189. package/src/ai/index.ts +16 -16
  190. package/src/ai/link-headers.ts +8 -3
  191. package/src/ai/llms.ts +4 -3
  192. package/src/ai/markdown.ts +23 -11
  193. package/src/ai/mcp/server.ts +78 -7
  194. package/src/ai/openapi-components.ts +1 -1
  195. package/src/ai/relative-links.ts +78 -10
  196. package/src/ai/serializers.ts +1 -1
  197. package/src/ai/static-expression.ts +416 -0
  198. package/src/ai/visibility.ts +46 -15
  199. package/src/analytics/databuddy.ts +67 -0
  200. package/src/analytics/head.ts +4 -0
  201. package/src/analytics/index.ts +2 -0
  202. package/src/analytics/posthog.ts +21 -3
  203. package/src/analytics/schema.ts +2 -0
  204. package/src/astro/generate.ts +90 -45
  205. package/src/astro/module-types.ts +1 -1
  206. package/src/astro/runtime-deps.ts +6 -6
  207. package/src/astro/templates.ts +87 -49
  208. package/src/audit/catalog.ts +2 -2
  209. package/src/audit/checks/assets.ts +25 -5
  210. package/src/audit/checks/content.ts +20 -2
  211. package/src/audit/checks/i18n.ts +46 -8
  212. package/src/audit/checks/indexability.ts +39 -19
  213. package/src/audit/checks/links.ts +14 -0
  214. package/src/audit/checks/llms.ts +6 -3
  215. package/src/audit/checks/network.ts +3 -1
  216. package/src/audit/checks/og-image.ts +10 -0
  217. package/src/audit/checks/robots.ts +6 -1
  218. package/src/audit/checks/sitemap.ts +56 -31
  219. package/src/audit/checks/social.ts +21 -2
  220. package/src/audit/crawl.ts +88 -15
  221. package/src/audit/report.ts +54 -17
  222. package/src/audit/run.ts +1 -0
  223. package/src/audit/types.ts +18 -0
  224. package/src/audit/url.ts +37 -6
  225. package/src/blume-modules.d.ts +2 -2
  226. package/src/cli/build-failure.ts +50 -0
  227. package/src/cli/commands/audit.ts +8 -1
  228. package/src/cli/commands/build.ts +19 -5
  229. package/src/cli/commands/check.ts +2 -0
  230. package/src/cli/commands/doctor.ts +7 -5
  231. package/src/cli/commands/eval.ts +4 -10
  232. package/src/cli/commands/init.ts +15 -5
  233. package/src/cli/commands/migrate.ts +2 -2
  234. package/src/cli/commands/preview.ts +15 -0
  235. package/src/cli/commands/sync.ts +2 -0
  236. package/src/cli/commands/translate.ts +12 -9
  237. package/src/cli/commands/upgrade.ts +13 -2
  238. package/src/cli/eject-scripts.ts +32 -7
  239. package/src/cli/env.ts +12 -1
  240. package/src/cli/init/scaffold.ts +117 -11
  241. package/src/cli/init/starter-spec.ts +235 -0
  242. package/src/cli/required-secrets.ts +3 -3
  243. package/src/components/content/AccordionItem.astro +26 -22
  244. package/src/components/content/Badge.astro +2 -9
  245. package/src/components/content/Card.astro +2 -2
  246. package/src/components/content/Component.astro +9 -3
  247. package/src/components/content/Expandable.astro +5 -1
  248. package/src/components/content/Frame.astro +2 -7
  249. package/src/components/content/GithubInfo.astro +12 -2
  250. package/src/components/content/Prompt.astro +2 -7
  251. package/src/components/content/Tabs.astro +54 -5
  252. package/src/components/content/Tile.astro +1 -1
  253. package/src/components/content/Tooltip.astro +69 -7
  254. package/src/components/content/Tree.astro +7 -2
  255. package/src/components/content/TypeTable.astro +10 -5
  256. package/src/components/content/Update.astro +8 -2
  257. package/src/components/content/auto-type-table.ts +4 -1
  258. package/src/components/content/badge-color.ts +17 -0
  259. package/src/components/content/base-href.ts +18 -3
  260. package/src/components/content/inline-markdown.ts +27 -7
  261. package/src/components/copy-feedback.ts +36 -9
  262. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  263. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
  264. package/src/components/islands/hooks.ts +21 -14
  265. package/src/components/islands/webmcp.ts +12 -8
  266. package/src/components/layout/Banner.astro +23 -4
  267. package/src/components/layout/Header.astro +32 -19
  268. package/src/components/layout/Logo.astro +5 -0
  269. package/src/components/layout/NavSelector.astro +6 -1
  270. package/src/components/layout/NavTabMenu.astro +133 -0
  271. package/src/components/layout/NavTree.astro +15 -6
  272. package/src/components/layout/NavTreeCache.astro +5 -2
  273. package/src/components/layout/NavTreeScript.astro +45 -5
  274. package/src/components/layout/PageLayout.astro +28 -14
  275. package/src/components/layout/Pagination.astro +7 -7
  276. package/src/components/layout/ReferenceLayout.astro +5 -2
  277. package/src/components/layout/RootLayout.astro +27 -14
  278. package/src/components/layout/Search.astro +18 -14
  279. package/src/components/layout/analytics-client.ts +3 -1
  280. package/src/components/layout/drawer-inert.ts +1 -1
  281. package/src/components/openapi/GraphqlType.astro +11 -3
  282. package/src/components/openapi/MessageComposer.astro +1 -1
  283. package/src/components/openapi/Operation.astro +21 -5
  284. package/src/components/openapi/PanelTabs.astro +4 -1
  285. package/src/components/openapi/Playground.astro +8 -2
  286. package/src/components/openapi/RequestPanel.astro +9 -5
  287. package/src/components/openapi/SchemaProperty.astro +11 -33
  288. package/src/components/openapi/SchemaTable.astro +27 -52
  289. package/src/components/openapi/description.ts +2 -2
  290. package/src/components/openapi/helpers.ts +90 -12
  291. package/src/components/openapi/message-composer.ts +8 -0
  292. package/src/components/openapi/message-model.ts +12 -2
  293. package/src/components/openapi/message.ts +4 -1
  294. package/src/components/openapi/operation-model.ts +40 -8
  295. package/src/components/openapi/panel.ts +29 -5
  296. package/src/components/openapi/playground-client.ts +37 -7
  297. package/src/components/openapi/playground-schema.ts +25 -6
  298. package/src/components/openapi/request.ts +2 -0
  299. package/src/components/openapi/schema-tree.ts +203 -0
  300. package/src/components/openapi/snippets.ts +30 -15
  301. package/src/components/openapi/validate-json.ts +1 -1
  302. package/src/core/base-path.ts +15 -0
  303. package/src/core/code-fences.ts +1 -1
  304. package/src/core/config-input.ts +23 -23
  305. package/src/core/config.ts +21 -7
  306. package/src/core/data.ts +3 -3
  307. package/src/core/diagnostics.ts +217 -30
  308. package/src/core/graph.ts +120 -11
  309. package/src/core/i18n-ui.ts +74 -11
  310. package/src/core/i18n.ts +13 -2
  311. package/src/core/links.ts +7 -4
  312. package/src/core/load-module.ts +20 -0
  313. package/src/core/locale-links.ts +17 -17
  314. package/src/core/manifest.ts +3 -2
  315. package/src/core/meta.ts +69 -6
  316. package/src/core/nav-diagnostics.ts +56 -1
  317. package/src/core/navigation.ts +245 -76
  318. package/src/core/ordering-prefix.ts +27 -0
  319. package/src/core/project-graph.ts +23 -1
  320. package/src/core/request-body.ts +1 -1
  321. package/src/core/safe-href.ts +53 -1
  322. package/src/core/safe-links.ts +11 -2
  323. package/src/core/schema.ts +95 -34
  324. package/src/core/server-features.ts +2 -2
  325. package/src/core/sources/assets.ts +83 -41
  326. package/src/core/sources/contentful-rich-text.ts +3 -2
  327. package/src/core/sources/contentful.ts +25 -12
  328. package/src/core/sources/filesystem.ts +5 -1
  329. package/src/core/sources/github-releases.ts +106 -5
  330. package/src/core/sources/lexical.ts +9 -4
  331. package/src/core/sources/lower.ts +79 -26
  332. package/src/core/sources/mdx-remote.ts +1 -0
  333. package/src/core/sources/normalize.ts +132 -30
  334. package/src/core/sources/notion.ts +26 -10
  335. package/src/core/sources/obsidian.ts +17 -3
  336. package/src/core/sources/payload.ts +1 -0
  337. package/src/core/sources/portable-text.ts +63 -44
  338. package/src/core/sources/remote.ts +18 -2
  339. package/src/core/sources/resolve.ts +52 -33
  340. package/src/core/sources/sanity.ts +1 -0
  341. package/src/core/sources/strapi-blocks.ts +9 -4
  342. package/src/core/sources/strapi.ts +1 -0
  343. package/src/core/sources/types.ts +31 -0
  344. package/src/core/types.ts +9 -0
  345. package/src/core/ui-packs/ar.ts +17 -5
  346. package/src/core/ui-packs/bg.ts +17 -5
  347. package/src/core/ui-packs/bn.ts +17 -5
  348. package/src/core/ui-packs/ca.ts +17 -5
  349. package/src/core/ui-packs/cs.ts +17 -5
  350. package/src/core/ui-packs/da.ts +17 -5
  351. package/src/core/ui-packs/de.ts +17 -5
  352. package/src/core/ui-packs/el.ts +17 -5
  353. package/src/core/ui-packs/es.ts +17 -5
  354. package/src/core/ui-packs/fa.ts +17 -5
  355. package/src/core/ui-packs/fi.ts +17 -5
  356. package/src/core/ui-packs/fr.ts +17 -5
  357. package/src/core/ui-packs/he.ts +17 -5
  358. package/src/core/ui-packs/hi.ts +17 -5
  359. package/src/core/ui-packs/hr.ts +17 -5
  360. package/src/core/ui-packs/hu.ts +17 -5
  361. package/src/core/ui-packs/id.ts +17 -5
  362. package/src/core/ui-packs/it.ts +17 -5
  363. package/src/core/ui-packs/ja.ts +17 -5
  364. package/src/core/ui-packs/ko.ts +17 -5
  365. package/src/core/ui-packs/nl.ts +17 -5
  366. package/src/core/ui-packs/no.ts +17 -5
  367. package/src/core/ui-packs/pl.ts +17 -5
  368. package/src/core/ui-packs/pt-br.ts +17 -5
  369. package/src/core/ui-packs/pt.ts +17 -5
  370. package/src/core/ui-packs/ro.ts +17 -5
  371. package/src/core/ui-packs/ru.ts +17 -5
  372. package/src/core/ui-packs/sk.ts +17 -5
  373. package/src/core/ui-packs/sr.ts +17 -5
  374. package/src/core/ui-packs/sv.ts +17 -5
  375. package/src/core/ui-packs/th.ts +17 -5
  376. package/src/core/ui-packs/tr.ts +17 -5
  377. package/src/core/ui-packs/uk.ts +17 -5
  378. package/src/core/ui-packs/vi.ts +17 -5
  379. package/src/core/ui-packs/zh-tw.ts +17 -5
  380. package/src/core/ui-packs/zh.ts +17 -5
  381. package/src/core/unrecognized-keys.ts +1 -1
  382. package/src/core/version-cut.ts +17 -2
  383. package/src/core/versions.ts +4 -1
  384. package/src/deploy/adapters/node.ts +5 -2
  385. package/src/deploy/adapters/registry.ts +2 -1
  386. package/src/deploy/adapters/types.ts +13 -1
  387. package/src/deploy/artifacts.ts +25 -5
  388. package/src/deploy/cloudflare-negotiation.ts +23 -4
  389. package/src/deploy/headers.ts +67 -51
  390. package/src/deploy/node-headers.ts +148 -27
  391. package/src/deploy/platforms/cloudflare.ts +1 -0
  392. package/src/deploy/platforms/netlify.ts +82 -5
  393. package/src/deploy/platforms/node.ts +7 -5
  394. package/src/deploy/platforms/static.ts +1 -0
  395. package/src/deploy/platforms/types.ts +7 -0
  396. package/src/deploy/platforms/vercel.ts +9 -3
  397. package/src/deploy/redirects.ts +14 -3
  398. package/src/deploy/vercel-negotiation.ts +35 -3
  399. package/src/eval/agents.ts +10 -2
  400. package/src/eval/run.ts +25 -0
  401. package/src/markdown/base-links.ts +55 -8
  402. package/src/markdown/index.ts +6 -3
  403. package/src/markdown/relative-links.ts +3 -23
  404. package/src/markdown/route-snapshot.ts +37 -0
  405. package/src/og/card.ts +20 -4
  406. package/src/og/derive.ts +145 -4
  407. package/src/openapi/graphql-build.ts +28 -2
  408. package/src/openapi/model.ts +77 -13
  409. package/src/openapi/render-mdx.ts +10 -1
  410. package/src/registry/eject.ts +135 -37
  411. package/src/search/documents.ts +24 -3
  412. package/src/search/orama-index.ts +1 -1
  413. package/src/search/sync/algolia.ts +36 -2
  414. package/src/sources/registry.ts +5 -0
  415. package/src/theme/entry.ts +11 -4
  416. package/src/translate/agents.ts +6 -1
  417. package/src/translate/ledger.ts +26 -3
  418. package/src/translate/meta.ts +11 -3
  419. package/src/translate/report.ts +1 -1
  420. package/src/translate/run.ts +11 -5
  421. package/src/translate/validate.ts +10 -1
  422. package/src/translate/work-list.ts +36 -3
  423. package/src/upgrade/upgrade.ts +37 -5
  424. package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
  425. package/dist/cli/chunk-5a2z0198.js.map +0 -10
  426. package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
  427. package/dist/cli/chunk-79njf86q.js.map +0 -11
  428. package/dist/cli/chunk-a9kptbw5.js.map +0 -14
  429. package/dist/cli/chunk-abh8yjkn.js +0 -31
  430. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  431. package/dist/cli/chunk-bctazmbk.js.map +0 -10
  432. package/dist/cli/chunk-beat36xx.js.map +0 -10
  433. package/dist/cli/chunk-d1tadaw7.js.map +0 -10
  434. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  435. package/dist/cli/chunk-j8mw0za6.js.map +0 -35
  436. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  437. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  438. package/dist/cli/chunk-nk3ts2xk.js +0 -51
  439. package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
  440. package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
  441. package/dist/cli/chunk-tzne8qfq.js.map +0 -10
  442. package/dist/cli/chunk-xaz13gwg.js.map +0 -182
  443. package/dist/cli/chunk-z01ze5c1.js.map +0 -10
  444. package/dist/cli/chunk-z1f5arsg.js.map +0 -36
  445. package/dist/cli/chunk-zg2gtj10.js.map +0 -35
  446. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
  447. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
  448. /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
  449. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
  450. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
  451. /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
  452. /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
  453. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
  454. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
@@ -0,0 +1,133 @@
1
+ ---
2
+ // A header tab with dropdown `items` (`navigation.tabs[].items`). In the
3
+ // header's tab bar (`variant="bar"`) it is the nav selectors' floating
4
+ // `<details>` dropdown: the `<summary>` is the disclosure button (the browser
5
+ // exposes its expanded state), the panel a list of links, and the shared
6
+ // light-dismiss closes it on an outside press, Escape (focus returns to the
7
+ // tab), or focus leaving it. In a navigation drawer (`variant="drawer"`) the
8
+ // same items expand in place under the tab, like a sidebar group, open from
9
+ // the start on the tab's own section. Both are server-rendered and the scripts
10
+ // delegate from `document`, so client-router swaps need no re-init.
11
+ import type { NavTab } from "../../core/types.ts";
12
+ import Icon from "../Icon.astro";
13
+ import { withBase } from "../islands/base-path.ts";
14
+
15
+ interface Props {
16
+ tab: NavTab;
17
+ /** The page's route, to mark the item it is on. */
18
+ route: string;
19
+ /** Whether the current route falls under this tab. */
20
+ active: boolean;
21
+ variant: "bar" | "drawer";
22
+ }
23
+
24
+ const { tab, route, active, variant } = Astro.props;
25
+ const items = tab.items ?? [];
26
+ // The tab itself is a button, not a page, so its "you are here" state is
27
+ // `aria-current="true"` (current item of a set); an item is the page.
28
+ const tabCurrent = active ? "true" : undefined;
29
+ const itemCurrent = (path: string): "page" | undefined =>
30
+ path === route ? "page" : undefined;
31
+
32
+ const barRowClass =
33
+ "flex w-full items-start 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=page]:text-foreground";
34
+ const drawerRowClass =
35
+ "flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground";
36
+ ---
37
+
38
+ {
39
+ variant === "bar" ? (
40
+ <details class="group relative" data-blume-dropdown>
41
+ <summary
42
+ aria-current={tabCurrent}
43
+ class="inline-flex cursor-pointer list-none items-center gap-1 rounded-full px-3 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground [&::-webkit-details-marker]:hidden"
44
+ >
45
+ {tab.icon && (
46
+ <Icon class="me-0.5 shrink-0" name={tab.icon} size={14} />
47
+ )}
48
+ {tab.label}
49
+ <Icon
50
+ class="transition-transform group-open:rotate-180"
51
+ name="chevron-down"
52
+ size={14}
53
+ />
54
+ </summary>
55
+ <div
56
+ class="absolute start-0 z-50 mt-2 min-w-56 max-w-[calc(100vw-1rem)] rounded-blume border border-border bg-background p-1 shadow-xl"
57
+ data-blume-dropdown-panel
58
+ >
59
+ {items.map((item) => (
60
+ <a
61
+ aria-current={itemCurrent(item.path)}
62
+ class={barRowClass}
63
+ href={withBase(item.path)}
64
+ >
65
+ {item.icon && <Icon class="mt-0.5" name={item.icon} size={16} />}
66
+ <span class="flex-1">
67
+ <span class="flex items-center gap-2">
68
+ {item.label}
69
+ {item.tag && (
70
+ <span class="rounded-full bg-muted px-1.5 py-0.5 text-[0.65rem] text-muted-foreground">
71
+ {item.tag}
72
+ </span>
73
+ )}
74
+ </span>
75
+ {item.description && (
76
+ <span class="mt-0.5 block text-muted-foreground text-xs">
77
+ {item.description}
78
+ </span>
79
+ )}
80
+ </span>
81
+ </a>
82
+ ))}
83
+ </div>
84
+ </details>
85
+ ) : (
86
+ <details class="group" open={active}>
87
+ <summary
88
+ aria-current={tabCurrent}
89
+ class="flex cursor-pointer list-none items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground [&::-webkit-details-marker]:hidden"
90
+ >
91
+ {tab.icon && <Icon class="shrink-0" name={tab.icon} size={14} />}
92
+ <span class="flex-1 truncate">{tab.label}</span>
93
+ <Icon
94
+ class="shrink-0 transition-transform group-open:rotate-180"
95
+ name="chevron-down"
96
+ size={14}
97
+ />
98
+ </summary>
99
+ <ul class="m-0 ms-2.5 list-none space-y-px border-border border-s p-0 ps-2">
100
+ {items.map((item) => (
101
+ <li>
102
+ <a
103
+ aria-current={itemCurrent(item.path)}
104
+ class={drawerRowClass}
105
+ href={withBase(item.path)}
106
+ >
107
+ {item.icon && (
108
+ <Icon class="shrink-0" name={item.icon} size={14} />
109
+ )}
110
+ <span class="flex-1 truncate">{item.label}</span>
111
+ {item.tag && (
112
+ <span class="shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-[0.65rem] text-muted-foreground">
113
+ {item.tag}
114
+ </span>
115
+ )}
116
+ </a>
117
+ </li>
118
+ ))}
119
+ </ul>
120
+ </details>
121
+ )
122
+ }
123
+
124
+ <script>
125
+ import { installDropdownDismiss } from "../dropdown-dismiss.ts";
126
+ import { installDropdownClamp } from "./dropdown-clamp.ts";
127
+
128
+ // Outside-click / Escape / focus-out dismissal and viewport clamping for
129
+ // the header dropdown, shared with the nav selectors. The drawer variant
130
+ // opts into neither: it is an in-place disclosure, not a floating panel.
131
+ installDropdownDismiss();
132
+ installDropdownClamp();
133
+ </script>
@@ -79,6 +79,9 @@ const fragmentFor = (node: NavNode, id: string): string | undefined =>
79
79
  // blank — the PageActions pattern.
80
80
  const n = { ...EN_UI.nav, ...strings };
81
81
 
82
+ /** The DOM id of a drill-in panel, which its drill button controls. */
83
+ const navPanelId = (id: string): string => `blume-nav-panel-${id}`;
84
+
82
85
  const badgeBase =
83
86
  "shrink-0 rounded-full px-1.5 py-0.5 font-medium text-[0.65rem] leading-none";
84
87
  // HTTP-method, AsyncAPI-action, and GraphQL operation-kind badges (from a
@@ -165,6 +168,7 @@ const initialId =
165
168
  data-nav-panel={panel.id}
166
169
  data-nav-src={src}
167
170
  hidden={panel.id !== initialId}
171
+ id={navPanelId(panel.id)}
168
172
  >
169
173
  {/* The title is the only sidebar link to the section's own page, so
170
174
  a routed panel keeps it as a link; without a route the whole row
@@ -174,7 +178,7 @@ const initialId =
174
178
  <div class="mb-3 flex items-center gap-0.5">
175
179
  <button
176
180
  aria-label={n.back}
177
- class="-ml-1 flex shrink-0 items-center justify-center self-stretch rounded-[0.65rem] px-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
181
+ class="-ms-1 flex shrink-0 items-center justify-center self-stretch rounded-[0.65rem] px-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
178
182
  data-nav-back={panel.parentId}
179
183
  type="button"
180
184
  >
@@ -195,7 +199,7 @@ const initialId =
195
199
  ) : (
196
200
  <button
197
201
  aria-label={`${n.back}: ${panel.label}`}
198
- class="-ml-1 mb-3 flex w-full items-center gap-1.5 rounded-[0.65rem] p-1 text-left font-semibold text-foreground text-sm transition-colors hover:bg-muted"
202
+ class="-ms-1 mb-3 flex w-full items-center gap-1.5 rounded-[0.65rem] p-1 text-start font-semibold text-foreground text-sm transition-colors hover:bg-muted"
199
203
  data-nav-back={panel.parentId}
200
204
  type="button"
201
205
  >
@@ -293,11 +297,16 @@ const initialId =
293
297
  const spacing =
294
298
  index === 0 ? "mt-0" : display === "flat" ? "mt-6" : "mt-0.5";
295
299
 
296
- // `page`: a single row that drills into this group's panel.
300
+ // `page`: a single row that drills into this group's panel. It
301
+ // controls that panel; NavTreeScript keeps `aria-expanded` in step
302
+ // with the panel shown (static markup, since cached subtrees and
303
+ // fragments can't carry per-page state).
297
304
  if (display === "page") {
298
305
  return (
299
306
  <li class={spacing}>
300
307
  <button
308
+ aria-controls={navPanelId(id)}
309
+ aria-expanded="false"
301
310
  class="blume-nav-drill"
302
311
  data-nav-to={id}
303
312
  type="button"
@@ -377,7 +386,7 @@ const initialId =
377
386
  </span>
378
387
  </summary>
379
388
  <div
380
- class="space-y-0.5 border-border border-l pl-3"
389
+ class="space-y-0.5 border-border border-s ps-3"
381
390
  data-nav-src={open ? undefined : fragmentFor(item, id)}
382
391
  >
383
392
  {/* A closed group's children are deferred (fetched into this
@@ -434,7 +443,7 @@ const initialId =
434
443
  )}
435
444
  <span class="truncate">{item.label}</span>
436
445
  {item.badge && (
437
- <span class={`ml-auto ${badgeClassFor(item.badge)}`}>{item.badge}</span>
446
+ <span class={`ms-auto ${badgeClassFor(item.badge)}`}>{item.badge}</span>
438
447
  )}
439
448
  </a>
440
449
  ) : (
@@ -448,7 +457,7 @@ const initialId =
448
457
  )}
449
458
  <span class="truncate">{item.label}</span>
450
459
  {item.badge && (
451
- <span class={`ml-auto ${badgeClassFor(item.badge)}`}>{item.badge}</span>
460
+ <span class={`ms-auto ${badgeClassFor(item.badge)}`}>{item.badge}</span>
452
461
  )}
453
462
  </>
454
463
  )}
@@ -4,7 +4,10 @@
4
4
  // once per group instead of once per page. Off in dev, where component edits
5
5
  // must show on the next request. The cached HTML references the page's icon
6
6
  // sprite (see icon-sprite.ts), so the symbols it used ride along and are
7
- // registered into whichever page reuses it.
7
+ // registered into whichever page reuses it. A render with no sprite (the
8
+ // `/blume-nav/` fragment route) inlines its icons instead, so it is cached
9
+ // apart: sprite references reused there would point at symbols the page that
10
+ // fetches the fragment may never have rendered.
8
11
  import type { NavNode } from "../../core/types.ts";
9
12
  import { iconSpriteFor, referencedIconSymbols } from "../icon-sprite.ts";
10
13
  import type { IconSymbol } from "../icon-sprite.ts";
@@ -19,7 +22,7 @@ const { node, variant } = Astro.props;
19
22
  const sprite = iconSpriteFor(Astro.locals);
20
23
  const { html, icons } = await cachedNavSubtree(
21
24
  node,
22
- variant,
25
+ `${variant}|${sprite ? "sprite" : "inline"}`,
23
26
  async () => {
24
27
  const rendered = await Astro.slots.render("default");
25
28
  const used: [string, IconSymbol][] = [];
@@ -40,7 +40,12 @@
40
40
  return pending;
41
41
  };
42
42
 
43
- /** Fill a deferred element from its fragment; a no-op once filled. */
43
+ /**
44
+ * Fill a deferred element from its fragment; a no-op once filled. The rows
45
+ * are appended, not swapped in: a drill-in panel keeps its own back row
46
+ * (the Back button and the section's title) above the deferred rows, and a
47
+ * collapsed group's slot is empty anyway.
48
+ */
44
49
  const fillDeferred = async (slot: HTMLElement): Promise<void> => {
45
50
  const src = slot.dataset.navSrc;
46
51
  if (!src) {
@@ -48,8 +53,12 @@
48
53
  }
49
54
  slot.setAttribute("aria-busy", "true");
50
55
  try {
51
- slot.innerHTML = await loadFragment(src);
52
- delete slot.dataset.navSrc;
56
+ const html = await loadFragment(src);
57
+ // Two opens racing on the same fetch fill it once.
58
+ if (slot.dataset.navSrc) {
59
+ slot.insertAdjacentHTML("beforeend", html);
60
+ delete slot.dataset.navSrc;
61
+ }
53
62
  } finally {
54
63
  slot.removeAttribute("aria-busy");
55
64
  }
@@ -179,21 +188,52 @@
179
188
  if (prev === next) {
180
189
  return;
181
190
  }
191
+ // The pressed drill or back button sits in the panel about to hide, so
192
+ // its focus has to move into the panel replacing it — left behind, the
193
+ // next Tab would leave the sidebar.
194
+ const hadFocus = prev?.contains(document.activeElement) ?? false;
182
195
  // Settle any in-flight slide before starting the next one.
183
196
  if (this.finishSlide) {
184
197
  this.finishSlide();
185
198
  }
186
199
  this.active = next;
187
200
  this.dataset.active = next.dataset.navPanel;
201
+ this.syncExpanded(next);
188
202
 
189
203
  if (!(animate && prev) || this.reduced()) {
190
204
  for (const panel of this.querySelectorAll<HTMLElement>("[data-nav-panel]")) {
191
205
  panel.hidden = panel !== next;
192
206
  }
193
- return;
207
+ } else {
208
+ this.slide(prev, next, this.depth(next) > this.depth(prev));
194
209
  }
210
+ if (hadFocus && prev) {
211
+ this.focusInto(next, prev);
212
+ }
213
+ }
214
+
215
+ /** Each drill button is expanded exactly while the panel it opens shows. */
216
+ syncExpanded(shown: HTMLElement): void {
217
+ for (const drill of this.querySelectorAll("[data-nav-to]")) {
218
+ drill.setAttribute(
219
+ "aria-expanded",
220
+ String(drill.getAttribute("data-nav-to") === shown.dataset.navPanel)
221
+ );
222
+ }
223
+ }
195
224
 
196
- this.slide(prev, next, this.depth(next) > this.depth(prev));
225
+ /**
226
+ * Focus `next` after leaving `prev`: going back, the drill button that
227
+ * opened `prev`; drilling in, the new panel's back button (its first
228
+ * control). `preventScroll` keeps the focus from scrolling the clipped
229
+ * container mid-slide.
230
+ */
231
+ focusInto(next: HTMLElement, prev: HTMLElement): void {
232
+ const target =
233
+ next.querySelector<HTMLElement>(
234
+ `[data-nav-to="${prev.dataset.navPanel}"]`
235
+ ) ?? next.querySelector<HTMLElement>("button, a[href]");
236
+ target?.focus({ preventScroll: true });
197
237
  }
198
238
 
199
239
  slide(out: HTMLElement, into: HTMLElement, forward: boolean): void {
@@ -38,6 +38,7 @@ import {
38
38
  } from "../../og/dimensions.ts";
39
39
  import { buildStructuredData } from "../../seo/jsonld.ts";
40
40
  import { normalizeXHandle } from "../../seo/x-handle.ts";
41
+ import Icon from "../Icon.astro";
41
42
  import { stripBase, withBase } from "../islands/base-path.ts";
42
43
  import { pageDirection, pageLocale } from "./page-locale.ts";
43
44
  import { searchLocaleFor } from "./search-locale.ts";
@@ -55,6 +56,7 @@ import {
55
56
  } from "./head-scripts.ts";
56
57
  import Header from "./Header.astro";
57
58
  import { currentTabForRoute } from "./nav-utils.ts";
59
+ import NavTabMenu from "./NavTabMenu.astro";
58
60
  import WebMcp from "./WebMcp.astro";
59
61
 
60
62
  interface Props {
@@ -78,11 +80,11 @@ interface Props {
78
80
  fontCssVars?: (string | FontHead)[];
79
81
  searchEnabled: boolean;
80
82
  /**
81
- * Opt this page out of the header's Ask AI trigger. Defaults to whether Ask
82
- * AI is configured, so a custom page gets the same trigger the docs pages have
83
+ * Opt this page out of the header's assistant trigger. Defaults to whether the
84
+ * assistant is configured, so a custom page gets the same trigger the docs pages have
83
85
  * without wiring anything up.
84
86
  */
85
- askEnabled?: boolean;
87
+ assistantEnabled?: boolean;
86
88
  /**
87
89
  * Start the header see-through, with its chrome in white, so it can sit over
88
90
  * a dark hero at the top of the page; it returns to the frosted bar as soon
@@ -163,7 +165,7 @@ const {
163
165
  themeMode,
164
166
  fontCssVars,
165
167
  searchEnabled,
166
- askEnabled,
168
+ assistantEnabled,
167
169
  transparentHeader,
168
170
  siteUrl,
169
171
  ogEnabled,
@@ -387,10 +389,10 @@ createIconSprite(Astro.locals);
387
389
  >
388
390
  {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
389
391
  <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
390
- <Banner banner={banner} strings={strings.banner} />
392
+ <Banner banner={banner} locale={locale} strings={strings.banner} />
391
393
  <Header
392
- askEnabled={askEnabled}
393
- askStrings={strings.ask}
394
+ assistantEnabled={assistantEnabled}
395
+ assistantStrings={strings.assistant}
394
396
  hasSidebar={false}
395
397
  locale={locale}
396
398
  localeSwitch={localeSwitch}
@@ -426,13 +428,25 @@ createIconSprite(Astro.locals);
426
428
  <ul class="m-0 list-none p-0">
427
429
  {navigation.tabs.map((tab) => (
428
430
  <li>
429
- <a
430
- aria-current={tab === activeTab ? "page" : undefined}
431
- class="block rounded-[0.65rem] px-2.5 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
432
- href={withBase(tab.href ?? tab.path)}
433
- >
434
- {tab.label}
435
- </a>
431
+ {tab.items && tab.items.length > 0 ? (
432
+ <NavTabMenu
433
+ active={tab === activeTab}
434
+ route={route}
435
+ tab={tab}
436
+ variant="drawer"
437
+ />
438
+ ) : (
439
+ <a
440
+ aria-current={tab === activeTab ? "page" : undefined}
441
+ class="flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
442
+ href={withBase(tab.href ?? tab.path)}
443
+ >
444
+ {tab.icon && (
445
+ <Icon class="shrink-0" name={tab.icon} size={14} />
446
+ )}
447
+ <span class="flex-1 truncate">{tab.label}</span>
448
+ </a>
449
+ )}
436
450
  </li>
437
451
  ))}
438
452
  </ul>
@@ -26,19 +26,19 @@ const s = { ...EN_UI.page, ...strings };
26
26
  (prev || next) && (
27
27
  <nav
28
28
  aria-label={s.pagination}
29
- class="mx-auto mt-12 flex max-w-content justify-between gap-4 border-border border-t pt-6 max-md:flex-col"
29
+ class="mx-auto mt-12 flex max-w-content justify-between gap-4 border-border border-t pt-6"
30
30
  >
31
31
  {prev ? (
32
32
  <a
33
- class="flex max-w-[48%] flex-1 items-center gap-2 rounded-blume border border-border px-4 py-3 text-foreground transition-colors hover:border-foreground max-md:max-w-full"
33
+ class="flex max-w-[48%] flex-1 items-center gap-2 rounded-blume border border-border px-4 py-3 text-foreground transition-colors hover:border-foreground"
34
34
  href={withBase(prev.route)}
35
35
  >
36
36
  <Icon class="rtl:-scale-x-100" name="arrow-left" size={16} />
37
37
  <span class="min-w-0">
38
- <span class="block text-muted-foreground text-xs max-md:hidden">
38
+ <span class="block text-muted-foreground text-xs">
39
39
  {s.previous}
40
40
  </span>
41
- <span class="block truncate font-medium">{prev.label}</span>
41
+ <span class="block font-medium max-md:line-clamp-2 max-md:wrap-break-word md:truncate">{prev.label}</span>
42
42
  </span>
43
43
  </a>
44
44
  ) : (
@@ -46,14 +46,14 @@ const s = { ...EN_UI.page, ...strings };
46
46
  )}
47
47
  {next && (
48
48
  <a
49
- class="ms-auto flex max-w-[48%] flex-1 items-center justify-end gap-2 rounded-blume border border-border px-4 py-3 text-end text-foreground transition-colors hover:border-foreground max-md:max-w-full"
49
+ class="ms-auto flex max-w-[48%] flex-1 items-center justify-end gap-2 rounded-blume border border-border px-4 py-3 text-end text-foreground transition-colors hover:border-foreground"
50
50
  href={withBase(next.route)}
51
51
  >
52
52
  <span class="min-w-0">
53
- <span class="block text-muted-foreground text-xs max-md:hidden">
53
+ <span class="block text-muted-foreground text-xs">
54
54
  {s.next}
55
55
  </span>
56
- <span class="block truncate font-medium">{next.label}</span>
56
+ <span class="block font-medium max-md:line-clamp-2 max-md:wrap-break-word md:truncate">{next.label}</span>
57
57
  </span>
58
58
  <Icon class="rtl:-scale-x-100" name="arrow-right" size={16} />
59
59
  </a>
@@ -146,18 +146,21 @@ createIconSprite(Astro.locals);
146
146
  >
147
147
  {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
148
148
  <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
149
- <Banner banner={banner} strings={strings.banner} />
149
+ <Banner banner={banner} locale={locale} strings={strings.banner} />
150
150
  <Header
151
- askStrings={strings.ask}
151
+ assistantStrings={strings.assistant}
152
152
  hasDrawer={false}
153
153
  hasSidebar={false}
154
154
  locale={locale}
155
155
  logo={logo}
156
+ navStrings={navStrings}
156
157
  navigation={navigation}
157
158
  route={route}
158
159
  searchEnabled={searchEnabled}
159
160
  searchLocale={searchLocale}
161
+ searchStrings={strings.search}
160
162
  site={site}
163
+ switcherStrings={strings.languageSwitcher}
161
164
  />
162
165
  <div
163
166
  class="h-[calc(100dvh-4rem)] overflow-auto border-border border-t [&>[data-scalar-client]]:h-full"
@@ -66,6 +66,7 @@ import {
66
66
  navGroupIds,
67
67
  sidebarForRoute,
68
68
  } from "./nav-utils.ts";
69
+ import NavTabMenu from "./NavTabMenu.astro";
69
70
  import NavTree from "./NavTree.astro";
70
71
  import { resolveSlot } from "./overrides.ts";
71
72
  import { pageDirection, pageLocale } from "./page-locale.ts";
@@ -122,10 +123,10 @@ interface Props {
122
123
  canonical?: string | null;
123
124
  editUrl?: string | null;
124
125
  /**
125
- * Opt this page out of the header's Ask AI trigger. Defaults to whether Ask
126
- * AI is configured, so pages never wire the trigger up themselves.
126
+ * Opt this page out of the header's assistant trigger. Defaults to whether the
127
+ * assistant is configured, so pages never wire the trigger up themselves.
127
128
  */
128
- askEnabled?: boolean;
129
+ assistantEnabled?: boolean;
129
130
  /** Show the "Was this page helpful?" rating below the content. */
130
131
  feedback?: boolean;
131
132
  exportPdf?: boolean;
@@ -248,7 +249,7 @@ const {
248
249
  x,
249
250
  canonical,
250
251
  editUrl,
251
- askEnabled,
252
+ assistantEnabled,
252
253
  feedback = false,
253
254
  exportPdf,
254
255
  exportEpub,
@@ -579,11 +580,11 @@ createIconSprite(Astro.locals);
579
580
  >
580
581
  {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
581
582
  <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
582
- <Banner banner={banner} strings={strings.banner} />
583
+ <Banner banner={banner} locale={locale} strings={strings.banner} />
583
584
  <VersionBanner notice={versionNotice ?? null} />
584
585
  <HeaderSlot
585
- askEnabled={askEnabled}
586
- askStrings={strings.ask}
586
+ assistantEnabled={assistantEnabled}
587
+ assistantStrings={strings.assistant}
587
588
  layout={layout}
588
589
  locale={locale}
589
590
  localeSwitch={localeSwitch}
@@ -677,13 +678,25 @@ createIconSprite(Astro.locals);
677
678
  <ul class="m-0 list-none p-0">
678
679
  {navigation.tabs.map((tab) => (
679
680
  <li>
680
- <a
681
- aria-current={tab === activeTab ? "page" : undefined}
682
- class="block rounded-[0.65rem] px-2.5 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
683
- href={withBase(tab.href ?? tab.path)}
684
- >
685
- {tab.label}
686
- </a>
681
+ {tab.items && tab.items.length > 0 ? (
682
+ <NavTabMenu
683
+ active={tab === activeTab}
684
+ route={page.route}
685
+ tab={tab}
686
+ variant="drawer"
687
+ />
688
+ ) : (
689
+ <a
690
+ aria-current={tab === activeTab ? "page" : undefined}
691
+ class="flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
692
+ href={withBase(tab.href ?? tab.path)}
693
+ >
694
+ {tab.icon && (
695
+ <Icon class="shrink-0" name={tab.icon} size={14} />
696
+ )}
697
+ <span class="flex-1 truncate">{tab.label}</span>
698
+ </a>
699
+ )}
687
700
  </li>
688
701
  ))}
689
702
  </ul>