blume 1.7.3 → 2.0.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 (673) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +217 -0
  3. package/README.md +34 -21
  4. package/dist/cli/chunk-11j0384y.js +148 -0
  5. package/dist/cli/chunk-11j0384y.js.map +10 -0
  6. package/dist/cli/{chunk-rqy0s5wh.js → chunk-1w8dp3qb.js} +17 -16
  7. package/dist/cli/{chunk-rqy0s5wh.js.map → chunk-1w8dp3qb.js.map} +3 -3
  8. package/dist/cli/chunk-2q1dwty4.js +75 -0
  9. package/dist/cli/chunk-2q1dwty4.js.map +11 -0
  10. package/dist/cli/chunk-41za066z.js +122 -0
  11. package/dist/cli/chunk-41za066z.js.map +11 -0
  12. package/dist/cli/chunk-5a2z0198.js +133 -0
  13. package/dist/cli/chunk-5a2z0198.js.map +10 -0
  14. package/dist/cli/{chunk-5g0w1e2c.js → chunk-6k8vp3ta.js} +25 -13
  15. package/dist/cli/{chunk-5g0w1e2c.js.map → chunk-6k8vp3ta.js.map} +4 -4
  16. package/dist/cli/{chunk-5qk08vmp.js → chunk-79njf86q.js} +133 -54
  17. package/dist/cli/chunk-79njf86q.js.map +11 -0
  18. package/dist/cli/{chunk-e7f42gdj.js → chunk-7ez8ny0t.js} +2 -2
  19. package/dist/cli/{chunk-nn13znc2.js → chunk-88cpgt6h.js} +1 -1
  20. package/dist/cli/chunk-a9kptbw5.js +361 -0
  21. package/dist/cli/chunk-a9kptbw5.js.map +14 -0
  22. package/dist/cli/chunk-abh8yjkn.js +31 -0
  23. package/dist/cli/chunk-abh8yjkn.js.map +10 -0
  24. package/dist/cli/chunk-b5aj94ah.js +91 -0
  25. package/dist/cli/chunk-b5aj94ah.js.map +10 -0
  26. package/dist/cli/{chunk-b27xqwn9.js → chunk-bctazmbk.js} +9 -5
  27. package/dist/cli/chunk-bctazmbk.js.map +10 -0
  28. package/dist/cli/chunk-beat36xx.js +279 -0
  29. package/dist/cli/chunk-beat36xx.js.map +10 -0
  30. package/dist/cli/chunk-bnbmcwfb.js +145 -0
  31. package/dist/cli/chunk-bnbmcwfb.js.map +11 -0
  32. package/dist/cli/{chunk-0xjyb285.js → chunk-bw22s759.js} +15 -5
  33. package/dist/cli/{chunk-0xjyb285.js.map → chunk-bw22s759.js.map} +4 -4
  34. package/dist/cli/chunk-by2290sx.js +39 -0
  35. package/dist/cli/chunk-by2290sx.js.map +10 -0
  36. package/dist/cli/chunk-d1tadaw7.js +79 -0
  37. package/dist/cli/chunk-d1tadaw7.js.map +10 -0
  38. package/dist/cli/{chunk-ahnw3kxw.js → chunk-d80hr03s.js} +24 -19
  39. package/dist/cli/chunk-d80hr03s.js.map +15 -0
  40. package/dist/cli/chunk-ernrthtr.js +97 -0
  41. package/dist/cli/chunk-ernrthtr.js.map +10 -0
  42. package/dist/cli/chunk-f2972sbt.js +374 -0
  43. package/dist/cli/chunk-f2972sbt.js.map +10 -0
  44. package/dist/cli/{chunk-vacwm2hv.js → chunk-f7t03s3g.js} +2 -2
  45. package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
  46. package/dist/cli/chunk-fa25z98p.js.map +11 -0
  47. package/dist/cli/{chunk-cjtn640a.js → chunk-fh5hj5jt.js} +43 -20
  48. package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
  49. package/dist/cli/{chunk-yg63d42r.js → chunk-j8mw0za6.js} +86 -57
  50. package/dist/cli/chunk-j8mw0za6.js.map +35 -0
  51. package/dist/cli/{chunk-ct47dqpx.js → chunk-jts8mvcz.js} +67 -7
  52. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  53. package/dist/cli/{chunk-ps4m1xh4.js → chunk-mnqj32sj.js} +505 -542
  54. package/dist/cli/chunk-mnqj32sj.js.map +12 -0
  55. package/dist/cli/{chunk-8cjtbafj.js → chunk-mwt1k8n7.js} +100 -374
  56. package/dist/cli/chunk-mwt1k8n7.js.map +10 -0
  57. package/dist/cli/chunk-nk3ts2xk.js +51 -0
  58. package/dist/cli/chunk-nk3ts2xk.js.map +10 -0
  59. package/dist/cli/{chunk-3r45185y.js → chunk-pat2zzwc.js} +9 -11
  60. package/dist/cli/{chunk-3r45185y.js.map → chunk-pat2zzwc.js.map} +2 -2
  61. package/dist/cli/chunk-pnnvybbk.js +176 -0
  62. package/dist/cli/chunk-pnnvybbk.js.map +11 -0
  63. package/dist/cli/{chunk-4x36ddpw.js → chunk-sqn5t4q0.js} +81 -87
  64. package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
  65. package/dist/cli/{chunk-bvwwhd84.js → chunk-tzne8qfq.js} +15 -15
  66. package/dist/cli/{chunk-bvwwhd84.js.map → chunk-tzne8qfq.js.map} +1 -1
  67. package/dist/cli/chunk-xaz13gwg.js +12449 -0
  68. package/dist/cli/chunk-xaz13gwg.js.map +182 -0
  69. package/dist/cli/chunk-y3e45rc8.js +102 -0
  70. package/dist/cli/chunk-y3e45rc8.js.map +10 -0
  71. package/dist/cli/chunk-z01ze5c1.js +261 -0
  72. package/dist/cli/chunk-z01ze5c1.js.map +10 -0
  73. package/dist/cli/{chunk-k79xp7av.js → chunk-z1f5arsg.js} +219 -689
  74. package/dist/cli/chunk-z1f5arsg.js.map +36 -0
  75. package/dist/cli/{chunk-1jefwnfs.js → chunk-zg2gtj10.js} +1076 -2592
  76. package/dist/cli/chunk-zg2gtj10.js.map +35 -0
  77. package/dist/cli/{chunk-dwgcp5sm.js → chunk-zxccj738.js} +1 -1
  78. package/dist/cli/index.js +214 -57
  79. package/dist/cli/index.js.map +8 -7
  80. package/dist/types/ai/agent-readability.d.ts +52 -0
  81. package/dist/types/ai/ai-catalog.d.ts +42 -0
  82. package/dist/types/ai/api/paths.d.ts +17 -0
  83. package/dist/types/ai/api-catalog.d.ts +18 -0
  84. package/dist/types/ai/ask.d.ts +368 -0
  85. package/dist/types/ai/changelog-markdown.d.ts +2 -0
  86. package/dist/types/ai/component-markdown.d.ts +2 -2
  87. package/dist/types/ai/index.d.ts +24 -0
  88. package/dist/types/ai/link-headers.d.ts +24 -0
  89. package/dist/types/ai/llms.d.ts +25 -0
  90. package/dist/types/ai/markdown.d.ts +45 -0
  91. package/dist/types/ai/mcp/discovery.d.ts +68 -0
  92. package/dist/types/ai/mcp/tools.d.ts +16 -0
  93. package/dist/types/ai/openapi-components.d.ts +43 -0
  94. package/dist/types/ai/relative-links.d.ts +26 -0
  95. package/dist/types/ai/serializers.d.ts +15 -0
  96. package/dist/types/ai/skills.d.ts +42 -0
  97. package/dist/types/ai/tar.d.ts +25 -0
  98. package/dist/types/ai/visibility.d.ts +17 -0
  99. package/dist/types/ai/web-bot-auth.d.ts +16 -0
  100. package/dist/types/analytics/adobe.d.ts +35 -0
  101. package/dist/types/analytics/amplitude.d.ts +50 -0
  102. package/dist/types/analytics/clarity.d.ts +31 -0
  103. package/dist/types/analytics/clearbit.d.ts +29 -0
  104. package/dist/types/analytics/cloudflare.d.ts +41 -0
  105. package/dist/types/analytics/fathom.d.ts +41 -0
  106. package/dist/types/analytics/google-analytics.d.ts +44 -0
  107. package/dist/types/analytics/google-tag-manager.d.ts +40 -0
  108. package/dist/types/analytics/head.d.ts +30 -0
  109. package/dist/types/analytics/heap.d.ts +40 -0
  110. package/dist/types/analytics/hightouch.d.ts +44 -0
  111. package/dist/types/analytics/hotjar.d.ts +33 -0
  112. package/dist/types/analytics/index.d.ts +61 -0
  113. package/dist/types/analytics/inline.d.ts +17 -0
  114. package/dist/types/analytics/logrocket.d.ts +44 -0
  115. package/dist/types/analytics/mixpanel.d.ts +67 -0
  116. package/dist/types/analytics/pirsch.d.ts +42 -0
  117. package/dist/types/analytics/plausible.d.ts +56 -0
  118. package/dist/types/analytics/posthog.d.ts +45 -0
  119. package/dist/types/analytics/schema.d.ts +320 -0
  120. package/dist/types/analytics/script.d.ts +46 -0
  121. package/dist/types/analytics/segment.d.ts +50 -0
  122. package/dist/types/analytics/vercel.d.ts +48 -0
  123. package/dist/types/astro/integration.d.ts +76 -0
  124. package/dist/types/astro/markdown-negotiation.d.ts +23 -0
  125. package/dist/types/astro/module-types.d.ts +14 -0
  126. package/dist/types/astro/pages.d.ts +44 -0
  127. package/dist/types/cli/env.d.ts +12 -0
  128. package/dist/types/cli/init/scaffold.d.ts +154 -0
  129. package/dist/types/components/layout/nav-utils.d.ts +11 -0
  130. package/dist/types/core/adapter.d.ts +47 -0
  131. package/dist/types/core/api-name.d.ts +7 -0
  132. package/dist/types/core/changelog-index.d.ts +13 -0
  133. package/dist/types/core/config-input.d.ts +186 -515
  134. package/dist/types/core/config.d.ts +60 -34
  135. package/dist/types/core/content-assets.d.ts +76 -0
  136. package/dist/types/core/custom-pages.d.ts +33 -0
  137. package/dist/types/core/data.d.ts +27 -12
  138. package/dist/types/core/define-components.d.ts +12 -9
  139. package/dist/types/core/deployment-env.d.ts +6 -11
  140. package/dist/types/core/frontmatter.d.ts +10 -0
  141. package/dist/types/core/graph.d.ts +18 -0
  142. package/dist/types/core/heading-markers.d.ts +54 -0
  143. package/dist/types/core/i18n-ui.d.ts +6 -2
  144. package/dist/types/core/i18n.d.ts +87 -0
  145. package/dist/types/core/includes.d.ts +138 -0
  146. package/dist/types/core/last-modified.d.ts +47 -0
  147. package/dist/types/core/links.d.ts +95 -0
  148. package/dist/types/core/locale-links.d.ts +60 -0
  149. package/dist/types/core/manifest.d.ts +17 -0
  150. package/dist/types/core/meta.d.ts +38 -0
  151. package/dist/types/core/nav-diagnostics.d.ts +26 -0
  152. package/dist/types/core/navigation.d.ts +6 -0
  153. package/dist/types/core/node-require.d.ts +19 -0
  154. package/dist/types/core/package-json.d.ts +13 -0
  155. package/dist/types/core/probe.d.ts +42 -0
  156. package/dist/types/core/project-graph.d.ts +51 -0
  157. package/dist/types/core/project.d.ts +2 -0
  158. package/dist/types/core/safe-href.d.ts +2 -0
  159. package/dist/types/core/safe-links.d.ts +26 -0
  160. package/dist/types/core/schema.d.ts +2282 -452
  161. package/dist/types/core/site-url.d.ts +16 -0
  162. package/dist/types/core/sources/assets.d.ts +36 -0
  163. package/dist/types/core/sources/cache.d.ts +37 -0
  164. package/dist/types/core/sources/collection.d.ts +28 -0
  165. package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
  166. package/dist/types/core/sources/contentful.d.ts +40 -0
  167. package/dist/types/core/sources/filesystem.d.ts +24 -0
  168. package/dist/types/core/sources/github-releases.d.ts +31 -0
  169. package/dist/types/core/sources/json.d.ts +30 -0
  170. package/dist/types/core/sources/lexical.d.ts +19 -0
  171. package/dist/types/core/sources/lower.d.ts +75 -0
  172. package/dist/types/core/sources/mdx-remote.d.ts +28 -0
  173. package/dist/types/core/sources/normalize.d.ts +147 -0
  174. package/dist/types/core/sources/notion.d.ts +131 -0
  175. package/dist/types/core/sources/obsidian.d.ts +46 -0
  176. package/dist/types/core/sources/payload.d.ts +39 -0
  177. package/dist/types/core/sources/portable-text.d.ts +42 -0
  178. package/dist/types/core/sources/read.d.ts +23 -0
  179. package/dist/types/core/sources/remote.d.ts +74 -0
  180. package/dist/types/core/sources/resolve.d.ts +19 -0
  181. package/dist/types/core/sources/sanity.d.ts +43 -0
  182. package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
  183. package/dist/types/core/sources/strapi.d.ts +33 -0
  184. package/dist/types/core/sources/types.d.ts +7 -0
  185. package/dist/types/core/sources/watch.d.ts +45 -0
  186. package/dist/types/core/text-width.d.ts +11 -0
  187. package/dist/types/core/types.d.ts +15 -1
  188. package/dist/types/core/unrecognized-keys.d.ts +7 -0
  189. package/dist/types/core/versions.d.ts +72 -0
  190. package/dist/types/core/yaml.d.ts +9 -0
  191. package/dist/types/deploy/adapter-output.d.ts +45 -0
  192. package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
  193. package/dist/types/deploy/adapters/index.d.ts +29 -0
  194. package/dist/types/deploy/adapters/netlify.d.ts +37 -0
  195. package/dist/types/deploy/adapters/node.d.ts +37 -0
  196. package/dist/types/deploy/adapters/registry.d.ts +133 -0
  197. package/dist/types/deploy/adapters/types.d.ts +71 -0
  198. package/dist/types/deploy/adapters/vercel.d.ts +38 -0
  199. package/dist/types/deploy/artifacts.d.ts +65 -0
  200. package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
  201. package/dist/types/deploy/function-bundle.d.ts +80 -0
  202. package/dist/types/deploy/headers.d.ts +50 -0
  203. package/dist/types/deploy/node-headers.d.ts +42 -0
  204. package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
  205. package/dist/types/deploy/platforms/index.d.ts +18 -0
  206. package/dist/types/deploy/platforms/netlify.d.ts +13 -0
  207. package/dist/types/deploy/platforms/node.d.ts +11 -0
  208. package/dist/types/deploy/platforms/paths.d.ts +27 -0
  209. package/dist/types/deploy/platforms/static.d.ts +10 -0
  210. package/dist/types/deploy/platforms/types.d.ts +104 -0
  211. package/dist/types/deploy/platforms/vercel.d.ts +32 -0
  212. package/dist/types/deploy/redirects.d.ts +53 -0
  213. package/dist/types/deploy/robots.d.ts +8 -0
  214. package/dist/types/deploy/rss.d.ts +31 -0
  215. package/dist/types/deploy/sitemap.d.ts +21 -0
  216. package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
  217. package/dist/types/markdown/code-title.d.ts +32 -0
  218. package/dist/types/markdown/fence-meta.d.ts +23 -0
  219. package/dist/types/markdown/themes.d.ts +3 -3
  220. package/dist/types/openapi/asyncapi.d.ts +129 -0
  221. package/dist/types/openapi/graphql-build.d.ts +8 -0
  222. package/dist/types/openapi/graphql.d.ts +122 -0
  223. package/dist/types/openapi/model.d.ts +158 -0
  224. package/dist/types/openapi/parse.d.ts +57 -0
  225. package/dist/types/openapi/references.d.ts +37 -25
  226. package/dist/types/openapi/render-mdx.d.ts +33 -0
  227. package/dist/types/openapi/sentence.d.ts +7 -0
  228. package/dist/types/openapi/signature.d.ts +10 -0
  229. package/dist/types/openapi/source.d.ts +22 -0
  230. package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
  231. package/dist/types/reference/asyncapi.d.ts +166 -0
  232. package/dist/types/reference/graphql.d.ts +181 -0
  233. package/dist/types/reference/index.d.ts +32 -0
  234. package/dist/types/reference/openapi.d.ts +165 -0
  235. package/dist/types/reference/options.d.ts +157 -0
  236. package/dist/types/reference/scalar.d.ts +136 -0
  237. package/dist/types/reference/schema.d.ts +630 -0
  238. package/dist/types/search/adapters/algolia.d.ts +32 -0
  239. package/dist/types/search/adapters/flexsearch.d.ts +12 -0
  240. package/dist/types/search/adapters/index.d.ts +31 -0
  241. package/dist/types/search/adapters/mixedbread.d.ts +22 -0
  242. package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
  243. package/dist/types/search/adapters/orama.d.ts +13 -0
  244. package/dist/types/search/adapters/pagefind.d.ts +12 -0
  245. package/dist/types/search/adapters/registry.d.ts +228 -0
  246. package/dist/types/search/adapters/types.d.ts +34 -0
  247. package/dist/types/search/adapters/typesense.d.ts +42 -0
  248. package/dist/types/search/build.d.ts +23 -0
  249. package/dist/types/search/documents.d.ts +90 -0
  250. package/dist/types/search/facets.d.ts +3 -0
  251. package/dist/types/search/sync/algolia.d.ts +14 -0
  252. package/dist/types/search/sync/index.d.ts +14 -0
  253. package/dist/types/search/sync/orama-cloud.d.ts +10 -0
  254. package/dist/types/search/sync/typesense.d.ts +14 -0
  255. package/dist/types/sources/contentful.d.ts +68 -0
  256. package/dist/types/sources/custom.d.ts +20 -0
  257. package/dist/types/sources/filesystem.d.ts +42 -0
  258. package/dist/types/sources/github-releases.d.ts +46 -0
  259. package/dist/types/sources/index.d.ts +48 -0
  260. package/dist/types/sources/mdx-remote.d.ts +63 -0
  261. package/dist/types/sources/notion.d.ts +65 -0
  262. package/dist/types/sources/obsidian.d.ts +34 -0
  263. package/dist/types/sources/payload.d.ts +68 -0
  264. package/dist/types/sources/registry.d.ts +1414 -0
  265. package/dist/types/sources/sanity.d.ts +69 -0
  266. package/dist/types/sources/shared.d.ts +46 -0
  267. package/dist/types/sources/strapi.d.ts +66 -0
  268. package/dist/types/theme/icon-kind.d.ts +11 -0
  269. package/dist/types/theme/icons.d.ts +20 -0
  270. package/docs/01-quickstart.mdx +18 -18
  271. package/docs/02-deployment.mdx +50 -26
  272. package/docs/03-upgrading.mdx +351 -0
  273. package/docs/04-migrating.mdx +58 -0
  274. package/docs/08-faq.mdx +3 -3
  275. package/docs/advanced/blog.mdx +13 -6
  276. package/docs/advanced/changelog.mdx +23 -35
  277. package/docs/advanced/custom-pages.mdx +20 -8
  278. package/docs/advanced/meta.ts +1 -8
  279. package/docs/advanced/skills.mdx +8 -0
  280. package/docs/cli/audit.mdx +646 -0
  281. package/docs/cli/doctor.mdx +29 -0
  282. package/docs/{reference/eval.mdx → cli/evals.mdx} +9 -8
  283. package/docs/cli/index.mdx +105 -0
  284. package/docs/cli/meta.ts +7 -0
  285. package/docs/{reference → cli}/translate.mdx +1 -1
  286. package/docs/cli/validate.mdx +41 -0
  287. package/docs/cli/version.mdx +40 -0
  288. package/docs/configuration/analytics.mdx +350 -59
  289. package/docs/configuration/ask-ai.mdx +162 -61
  290. package/docs/configuration/customization.mdx +15 -9
  291. package/docs/configuration/index.mdx +50 -31
  292. package/docs/configuration/search.mdx +75 -54
  293. package/docs/configuration/theming.mdx +24 -15
  294. package/docs/content/components.mdx +21 -5
  295. package/docs/{reference → content}/frontmatter.mdx +35 -1
  296. package/docs/content/i18n.mdx +8 -6
  297. package/docs/content/includes.mdx +2 -4
  298. package/docs/content/index.mdx +1 -1
  299. package/docs/content/islands.mdx +10 -5
  300. package/docs/content/meta.mdx +1 -1
  301. package/docs/content/meta.ts +1 -0
  302. package/docs/content/navigation.mdx +9 -7
  303. package/docs/content/sources.mdx +145 -50
  304. package/docs/content/syntax.mdx +16 -12
  305. package/docs/content/versioning.mdx +3 -3
  306. package/docs/discoverability/agent-discovery.mdx +11 -11
  307. package/docs/discoverability/index.mdx +4 -4
  308. package/docs/discoverability/json-api.mdx +4 -4
  309. package/docs/discoverability/llms-txt.mdx +6 -6
  310. package/docs/discoverability/markdown.mdx +4 -4
  311. package/docs/discoverability/mcp.mdx +11 -11
  312. package/docs/discoverability/sitemap-and-robots.mdx +3 -3
  313. package/docs/index.mdx +2 -2
  314. package/docs/references/asyncapi.mdx +59 -0
  315. package/docs/{advanced → references}/graphql.mdx +45 -32
  316. package/docs/{reference → references}/meta.ts +2 -2
  317. package/docs/references/openapi.mdx +171 -0
  318. package/docs/references/scalar.mdx +64 -0
  319. package/package.json +42 -8
  320. package/skills/blume/SKILL.md +21 -8
  321. package/skills/blume-migrate/SKILL.md +22 -21
  322. package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
  323. package/skills/blume-migrate/references/docusaurus.md +5 -4
  324. package/skills/blume-migrate/references/fumadocs.md +4 -4
  325. package/skills/blume-migrate/references/mintlify.md +23 -8
  326. package/skills/blume-migrate/references/monorepo.md +6 -6
  327. package/skills/blume-migrate/references/starlight.md +4 -4
  328. package/src/ai/agent-readability.ts +21 -22
  329. package/src/ai/ai-catalog.ts +50 -32
  330. package/src/ai/api/handlers.ts +46 -11
  331. package/src/ai/api-catalog.ts +8 -8
  332. package/src/ai/ask-data.ts +1 -1
  333. package/src/ai/ask.ts +624 -100
  334. package/src/ai/changelog-markdown.ts +91 -0
  335. package/src/ai/component-markdown.ts +328 -12
  336. package/src/ai/index.ts +45 -0
  337. package/src/ai/link-headers.ts +7 -6
  338. package/src/ai/llms.ts +20 -15
  339. package/src/ai/markdown.ts +35 -5
  340. package/src/ai/mcp/data.ts +9 -5
  341. package/src/ai/openapi-components.ts +4 -1
  342. package/src/ai/relative-links.ts +170 -0
  343. package/src/ai/serializers.ts +2 -2
  344. package/src/ai/skills.ts +1 -1
  345. package/src/ai/web-bot-auth.ts +2 -2
  346. package/src/analytics/adobe.ts +46 -0
  347. package/src/analytics/amplitude.ts +79 -0
  348. package/src/analytics/clarity.ts +46 -0
  349. package/src/analytics/clearbit.ts +47 -0
  350. package/src/analytics/cloudflare.ts +67 -0
  351. package/src/analytics/fathom.ts +64 -0
  352. package/src/analytics/google-analytics.ts +84 -0
  353. package/src/analytics/google-tag-manager.ts +61 -0
  354. package/src/analytics/head.ts +130 -0
  355. package/src/analytics/heap.ts +65 -0
  356. package/src/analytics/hightouch.ts +77 -0
  357. package/src/analytics/hotjar.ts +47 -0
  358. package/src/analytics/index.ts +71 -0
  359. package/src/analytics/inline.ts +21 -0
  360. package/src/analytics/logrocket.ts +71 -0
  361. package/src/analytics/mixpanel.ts +92 -0
  362. package/src/analytics/pirsch.ts +66 -0
  363. package/src/analytics/plausible.ts +83 -0
  364. package/src/analytics/posthog.ts +78 -0
  365. package/src/analytics/schema.ts +60 -0
  366. package/src/analytics/script.ts +60 -0
  367. package/src/analytics/segment.ts +85 -0
  368. package/src/analytics/vercel.ts +50 -0
  369. package/src/astro/adapter-root.ts +7 -9
  370. package/src/astro/component-slots.ts +131 -91
  371. package/src/astro/generate.ts +112 -345
  372. package/src/astro/integration.ts +2 -6
  373. package/src/astro/pages.ts +13 -101
  374. package/src/astro/render-deps.ts +379 -0
  375. package/src/astro/runtime-deps.ts +201 -0
  376. package/src/astro/templates.ts +554 -541
  377. package/src/audit/agent.ts +24 -0
  378. package/src/audit/catalog.ts +2 -2
  379. package/src/audit/checks/assets.ts +2 -2
  380. package/src/audit/checks/dns-aid.ts +1 -1
  381. package/src/audit/checks/duplicates.ts +3 -1
  382. package/src/audit/checks/i18n.ts +1 -1
  383. package/src/audit/checks/indexability.ts +7 -7
  384. package/src/audit/checks/links.ts +2 -2
  385. package/src/audit/checks/llms.ts +6 -6
  386. package/src/audit/checks/network.ts +3 -3
  387. package/src/audit/checks/og-image.ts +2 -2
  388. package/src/audit/checks/robots.ts +1 -1
  389. package/src/audit/checks/sitemap.ts +2 -2
  390. package/src/audit/checks/social.ts +1 -1
  391. package/src/audit/run.ts +4 -3
  392. package/src/audit/terms.ts +31 -0
  393. package/src/audit/url.ts +13 -13
  394. package/src/cli/command-meta.ts +10 -0
  395. package/src/cli/commands/audit.ts +39 -20
  396. package/src/cli/commands/build.ts +71 -316
  397. package/src/cli/commands/check.ts +1 -0
  398. package/src/cli/commands/dev.ts +40 -1
  399. package/src/cli/commands/doctor.ts +90 -14
  400. package/src/cli/commands/eject.ts +44 -7
  401. package/src/cli/commands/eval.ts +2 -2
  402. package/src/cli/commands/init.ts +161 -41
  403. package/src/cli/commands/migrate.ts +121 -0
  404. package/src/cli/commands/preview.ts +7 -1
  405. package/src/cli/commands/translate.ts +2 -2
  406. package/src/cli/commands/upgrade.ts +141 -0
  407. package/src/cli/commands/version.ts +57 -44
  408. package/src/cli/eject-scripts.ts +121 -6
  409. package/src/cli/index.ts +11 -1
  410. package/src/cli/init/install.ts +70 -0
  411. package/src/cli/init/questions.ts +7 -0
  412. package/src/cli/init/scaffold.ts +459 -75
  413. package/src/cli/lazy-command.ts +37 -1
  414. package/src/cli/prepare.ts +40 -6
  415. package/src/cli/required-secrets.ts +38 -11
  416. package/src/cli/unknown-flags.ts +266 -0
  417. package/src/cli/yarn-pnp.ts +52 -0
  418. package/src/components/content/AccordionItem.astro +7 -1
  419. package/src/components/content/Card.astro +4 -3
  420. package/src/components/content/ColorItem.astro +22 -4
  421. package/src/components/content/GithubInfo.astro +2 -2
  422. package/src/components/content/Prompt.astro +25 -25
  423. package/src/components/content/Tabs.astro +3 -1
  424. package/src/components/content/Tile.astro +2 -3
  425. package/src/components/content/Tooltip.astro +57 -9
  426. package/src/components/content/content-strings.ts +34 -0
  427. package/src/components/content/diff.ts +1 -1
  428. package/src/components/content/mermaid-element.ts +13 -2
  429. package/src/components/content/prompt-markdown.ts +292 -0
  430. package/src/components/content/tooltip-id.ts +41 -0
  431. package/src/components/islands/hooks.ts +25 -2
  432. package/src/components/layout/Analytics.astro +21 -79
  433. package/src/components/layout/Banner.astro +3 -1
  434. package/src/components/layout/DiscoveryLinks.astro +69 -0
  435. package/src/components/layout/Header.astro +83 -20
  436. package/src/components/layout/LanguageSwitcher.astro +4 -1
  437. package/src/components/layout/Logo.astro +23 -2
  438. package/src/components/layout/NavSelector.astro +13 -2
  439. package/src/components/layout/NavTree.astro +12 -3
  440. package/src/components/layout/NavTreeScript.astro +17 -3
  441. package/src/components/layout/PageActions.astro +3 -3
  442. package/src/components/layout/PageFeedback.astro +11 -1
  443. package/src/components/layout/PageLayout.astro +52 -5
  444. package/src/components/layout/ReferenceLayout.astro +21 -12
  445. package/src/components/layout/RootLayout.astro +69 -66
  446. package/src/components/layout/Search.astro +30 -6
  447. package/src/components/layout/WebMcp.astro +1 -1
  448. package/src/components/layout/analytics-client.ts +72 -15
  449. package/src/components/layout/drawer-inert.ts +113 -15
  450. package/src/components/layout/dropdown-clamp.ts +105 -0
  451. package/src/components/layout/head-scripts.ts +15 -6
  452. package/src/components/layout/nav-utils.ts +17 -0
  453. package/src/components/layout/search/algolia.ts +8 -8
  454. package/src/components/layout/search/orama-cloud.ts +9 -7
  455. package/src/components/layout/search/typesense.ts +11 -17
  456. package/src/components/openapi/MessageComposer.astro +2 -2
  457. package/src/components/openapi/Playground.astro +2 -2
  458. package/src/components/openapi/playground-client.ts +79 -10
  459. package/src/core/changelog-index.ts +24 -0
  460. package/src/core/component-overrides.ts +399 -154
  461. package/src/core/config-input.ts +188 -553
  462. package/src/core/config.ts +130 -41
  463. package/src/core/custom-pages.ts +105 -0
  464. package/src/core/data.ts +31 -12
  465. package/src/core/define-components.ts +12 -9
  466. package/src/core/deployment-env.ts +18 -74
  467. package/src/core/diagnostics.ts +14 -7
  468. package/src/core/graph.ts +69 -25
  469. package/src/core/i18n-ui.ts +6 -2
  470. package/src/core/i18n.ts +17 -0
  471. package/src/core/includes.ts +156 -38
  472. package/src/core/last-modified.ts +6 -11
  473. package/src/core/links.ts +166 -2
  474. package/src/core/manifest.ts +10 -3
  475. package/src/core/navigation.ts +115 -16
  476. package/src/core/new-tab.ts +35 -0
  477. package/src/core/node-require.ts +21 -0
  478. package/src/core/project-graph.ts +42 -26
  479. package/src/core/project.ts +9 -4
  480. package/src/core/request-body.ts +61 -0
  481. package/src/core/safe-href.ts +28 -0
  482. package/src/core/safe-links.ts +68 -0
  483. package/src/core/schema.ts +517 -739
  484. package/src/core/server-features.ts +8 -9
  485. package/src/core/sources/assets.ts +47 -11
  486. package/src/core/sources/collection.ts +67 -0
  487. package/src/core/sources/contentful-rich-text.ts +285 -0
  488. package/src/core/sources/contentful.ts +173 -0
  489. package/src/core/sources/github-releases.ts +51 -1
  490. package/src/core/sources/json.ts +71 -0
  491. package/src/core/sources/lexical.ts +195 -0
  492. package/src/core/sources/lower.ts +226 -0
  493. package/src/core/sources/normalize.ts +52 -2
  494. package/src/core/sources/notion.ts +39 -28
  495. package/src/core/sources/payload.ts +135 -0
  496. package/src/core/sources/portable-text.ts +11 -16
  497. package/src/core/sources/remote.ts +226 -0
  498. package/src/core/sources/resolve.ts +104 -166
  499. package/src/core/sources/sanity.ts +16 -49
  500. package/src/core/sources/strapi-blocks.ts +124 -0
  501. package/src/core/sources/strapi.ts +191 -0
  502. package/src/core/sources/types.ts +12 -1
  503. package/src/core/types.ts +15 -1
  504. package/src/core/ui-packs/ar.ts +6 -2
  505. package/src/core/ui-packs/bg.ts +6 -2
  506. package/src/core/ui-packs/bn.ts +6 -2
  507. package/src/core/ui-packs/ca.ts +3 -1
  508. package/src/core/ui-packs/cs.ts +6 -2
  509. package/src/core/ui-packs/da.ts +6 -2
  510. package/src/core/ui-packs/de.ts +6 -2
  511. package/src/core/ui-packs/el.ts +3 -1
  512. package/src/core/ui-packs/es.ts +3 -1
  513. package/src/core/ui-packs/fa.ts +6 -2
  514. package/src/core/ui-packs/fi.ts +6 -2
  515. package/src/core/ui-packs/fr.ts +3 -1
  516. package/src/core/ui-packs/he.ts +6 -2
  517. package/src/core/ui-packs/hi.ts +6 -2
  518. package/src/core/ui-packs/hr.ts +6 -2
  519. package/src/core/ui-packs/hu.ts +6 -2
  520. package/src/core/ui-packs/id.ts +6 -2
  521. package/src/core/ui-packs/it.ts +3 -1
  522. package/src/core/ui-packs/ja.ts +3 -1
  523. package/src/core/ui-packs/ko.ts +3 -1
  524. package/src/core/ui-packs/nl.ts +6 -2
  525. package/src/core/ui-packs/no.ts +6 -2
  526. package/src/core/ui-packs/pl.ts +6 -2
  527. package/src/core/ui-packs/pt-br.ts +3 -1
  528. package/src/core/ui-packs/pt.ts +3 -1
  529. package/src/core/ui-packs/ro.ts +6 -2
  530. package/src/core/ui-packs/ru.ts +6 -2
  531. package/src/core/ui-packs/sk.ts +6 -2
  532. package/src/core/ui-packs/sr.ts +6 -2
  533. package/src/core/ui-packs/sv.ts +6 -2
  534. package/src/core/ui-packs/th.ts +3 -1
  535. package/src/core/ui-packs/tr.ts +6 -2
  536. package/src/core/ui-packs/uk.ts +6 -2
  537. package/src/core/ui-packs/vi.ts +3 -1
  538. package/src/core/ui-packs/zh-tw.ts +3 -1
  539. package/src/core/ui-packs/zh.ts +3 -1
  540. package/src/core/unrecognized-keys.ts +10 -0
  541. package/src/core/version-cut.ts +116 -8
  542. package/src/deploy/adapter-output.ts +57 -97
  543. package/src/deploy/adapters/cloudflare.ts +39 -0
  544. package/src/deploy/adapters/index.ts +41 -0
  545. package/src/deploy/adapters/netlify.ts +34 -0
  546. package/src/deploy/adapters/node.ts +34 -0
  547. package/src/deploy/adapters/registry.ts +127 -0
  548. package/src/deploy/adapters/types.ts +92 -0
  549. package/src/deploy/adapters/vercel.ts +35 -0
  550. package/src/deploy/artifacts.ts +55 -27
  551. package/src/deploy/cloudflare-negotiation.ts +179 -100
  552. package/src/deploy/function-bundle.ts +18 -3
  553. package/src/deploy/headers.ts +124 -23
  554. package/src/deploy/node-headers.ts +198 -0
  555. package/src/deploy/platforms/cloudflare.ts +312 -0
  556. package/src/deploy/platforms/index.ts +67 -0
  557. package/src/deploy/platforms/netlify.ts +52 -0
  558. package/src/deploy/platforms/node.ts +40 -0
  559. package/src/deploy/platforms/paths.ts +42 -0
  560. package/src/deploy/platforms/static.ts +29 -0
  561. package/src/deploy/platforms/types.ts +112 -0
  562. package/src/deploy/platforms/vercel.ts +193 -0
  563. package/src/deploy/redirects.ts +30 -18
  564. package/src/deploy/robots.ts +3 -3
  565. package/src/deploy/rss.ts +2 -2
  566. package/src/deploy/sitemap.ts +2 -2
  567. package/src/deploy/vercel-negotiation.ts +2 -2
  568. package/src/eval/agents.ts +32 -1
  569. package/src/eval/findings.ts +19 -11
  570. package/src/eval/report.ts +10 -2
  571. package/src/markdown/external-links.ts +65 -0
  572. package/src/markdown/include.ts +45 -27
  573. package/src/markdown/index.ts +34 -9
  574. package/src/markdown/inline-code.ts +12 -6
  575. package/src/markdown/relative-links.ts +324 -0
  576. package/src/markdown/themes.ts +3 -3
  577. package/src/migrate/migrate.ts +149 -0
  578. package/src/openapi/parse.ts +64 -16
  579. package/src/openapi/proxy.ts +62 -10
  580. package/src/openapi/references.ts +147 -147
  581. package/src/openapi/render-mdx.ts +32 -10
  582. package/src/openapi/scalar.ts +15 -13
  583. package/src/openapi/sentence.ts +14 -0
  584. package/src/openapi/source.ts +37 -21
  585. package/src/openapi/spec-dependency-error.ts +15 -0
  586. package/src/reference/asyncapi.ts +83 -0
  587. package/src/reference/graphql.ts +89 -0
  588. package/src/reference/index.ts +40 -0
  589. package/src/reference/openapi.ts +83 -0
  590. package/src/reference/options.ts +201 -0
  591. package/src/reference/scalar.ts +136 -0
  592. package/src/reference/schema.ts +88 -0
  593. package/src/registry/eject.ts +97 -42
  594. package/src/search/adapters/algolia.ts +46 -0
  595. package/src/search/adapters/flexsearch.ts +29 -0
  596. package/src/search/adapters/index.ts +39 -0
  597. package/src/search/adapters/mixedbread.ts +39 -0
  598. package/src/search/adapters/orama-cloud.ts +51 -0
  599. package/src/search/adapters/orama.ts +24 -0
  600. package/src/search/adapters/pagefind.ts +27 -0
  601. package/src/search/adapters/registry.ts +131 -0
  602. package/src/search/adapters/types.ts +46 -0
  603. package/src/search/adapters/typesense.ts +57 -0
  604. package/src/search/build.ts +8 -4
  605. package/src/search/documents.ts +6 -0
  606. package/src/search/sync/algolia.ts +12 -11
  607. package/src/search/sync/index.ts +35 -20
  608. package/src/search/sync/orama-cloud.ts +12 -9
  609. package/src/search/sync/typesense.ts +15 -13
  610. package/src/sources/contentful.ts +63 -0
  611. package/src/sources/custom.ts +38 -0
  612. package/src/sources/filesystem.ts +51 -0
  613. package/src/sources/github-releases.ts +52 -0
  614. package/src/sources/index.ts +60 -0
  615. package/src/sources/mdx-remote.ts +76 -0
  616. package/src/sources/notion.ts +63 -0
  617. package/src/sources/obsidian.ts +38 -0
  618. package/src/sources/payload.ts +60 -0
  619. package/src/sources/registry.ts +182 -0
  620. package/src/sources/sanity.ts +66 -0
  621. package/src/sources/shared.ts +52 -0
  622. package/src/sources/strapi.ts +58 -0
  623. package/src/theme/entry.ts +24 -2
  624. package/src/translate/report.ts +40 -7
  625. package/src/upgrade/upgrade.ts +499 -0
  626. package/dist/cli/chunk-0qymqwzz.js +0 -164
  627. package/dist/cli/chunk-0qymqwzz.js.map +0 -15
  628. package/dist/cli/chunk-1jefwnfs.js.map +0 -48
  629. package/dist/cli/chunk-2mzebbbz.js +0 -69
  630. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  631. package/dist/cli/chunk-4x36ddpw.js.map +0 -11
  632. package/dist/cli/chunk-5093q3n7.js +0 -68
  633. package/dist/cli/chunk-5093q3n7.js.map +0 -10
  634. package/dist/cli/chunk-5qk08vmp.js.map +0 -11
  635. package/dist/cli/chunk-7s8hm3b6.js +0 -5347
  636. package/dist/cli/chunk-7s8hm3b6.js.map +0 -58
  637. package/dist/cli/chunk-8cjtbafj.js.map +0 -13
  638. package/dist/cli/chunk-97r59kpr.js +0 -381
  639. package/dist/cli/chunk-97r59kpr.js.map +0 -12
  640. package/dist/cli/chunk-ahnw3kxw.js.map +0 -15
  641. package/dist/cli/chunk-b27xqwn9.js.map +0 -10
  642. package/dist/cli/chunk-bf6bt1xt.js +0 -185
  643. package/dist/cli/chunk-bf6bt1xt.js.map +0 -11
  644. package/dist/cli/chunk-cjtn640a.js.map +0 -10
  645. package/dist/cli/chunk-ct47dqpx.js.map +0 -11
  646. package/dist/cli/chunk-esphfr8p.js +0 -107
  647. package/dist/cli/chunk-esphfr8p.js.map +0 -11
  648. package/dist/cli/chunk-ex56aa81.js +0 -1016
  649. package/dist/cli/chunk-ex56aa81.js.map +0 -13
  650. package/dist/cli/chunk-garjf5z9.js +0 -30
  651. package/dist/cli/chunk-garjf5z9.js.map +0 -10
  652. package/dist/cli/chunk-js7saxwm.js +0 -1045
  653. package/dist/cli/chunk-js7saxwm.js.map +0 -22
  654. package/dist/cli/chunk-k79xp7av.js.map +0 -39
  655. package/dist/cli/chunk-ps4m1xh4.js.map +0 -15
  656. package/dist/cli/chunk-q4rae3bg.js +0 -60
  657. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  658. package/dist/cli/chunk-rz9jmfhz.js +0 -108
  659. package/dist/cli/chunk-rz9jmfhz.js.map +0 -10
  660. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  661. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  662. package/dist/cli/chunk-vrfp10qk.js +0 -81
  663. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  664. package/dist/cli/chunk-yg63d42r.js.map +0 -34
  665. package/docs/advanced/api-reference.mdx +0 -240
  666. package/docs/reference/cli.mdx +0 -197
  667. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  668. package/src/components/content/changelog-element.ts +0 -69
  669. package/src/search/providers.ts +0 -91
  670. /package/dist/cli/{chunk-e7f42gdj.js.map → chunk-7ez8ny0t.js.map} +0 -0
  671. /package/dist/cli/{chunk-nn13znc2.js.map → chunk-88cpgt6h.js.map} +0 -0
  672. /package/dist/cli/{chunk-vacwm2hv.js.map → chunk-f7t03s3g.js.map} +0 -0
  673. /package/dist/cli/{chunk-dwgcp5sm.js.map → chunk-zxccj738.js.map} +0 -0
@@ -34,7 +34,12 @@ const buttonClass =
34
34
  {f.no}
35
35
  </button>
36
36
  </div>
37
- <p class="hidden text-muted-foreground text-sm" data-feedback-thanks>
37
+ <p
38
+ class="hidden text-muted-foreground text-sm focus:outline-none"
39
+ data-feedback-thanks
40
+ role="status"
41
+ tabindex="-1"
42
+ >
38
43
  {f.thanks}
39
44
  </p>
40
45
  </section>
@@ -61,6 +66,11 @@ const buttonClass =
61
66
  });
62
67
  actions?.classList.add("hidden");
63
68
  thanks?.classList.remove("hidden");
69
+ // The clicked button just disappeared under focus; hand focus to the
70
+ // (live) thanks line so the reader isn't dropped at the top of the page.
71
+ if (thanks instanceof HTMLElement) {
72
+ thanks.focus();
73
+ }
64
74
  });
65
75
  }
66
76
  };
@@ -23,10 +23,12 @@ import type {
23
23
  BlumeDataConfig,
24
24
  BlumeFavicon,
25
25
  BlumeLogo,
26
+ DiscoveryHeadLinks,
26
27
  } from "../../core/data.ts";
27
28
  import data from "blume:data";
28
29
  import { EN_UI } from "../../core/i18n-ui.ts";
29
30
  import type { UIStrings } from "../../core/i18n-ui.ts";
31
+ import { NEW_TAB_HINT_ID } from "../../core/new-tab.ts";
30
32
  import type { FontHead } from "../../theme/fonts.ts";
31
33
  import type { LocaleSwitchOption, Navigation } from "../../core/types.ts";
32
34
  import {
@@ -36,13 +38,14 @@ import {
36
38
  } from "../../og/dimensions.ts";
37
39
  import { buildStructuredData } from "../../seo/jsonld.ts";
38
40
  import { normalizeXHandle } from "../../seo/x-handle.ts";
39
- import { withBase } from "../islands/base-path.ts";
41
+ import { stripBase, withBase } from "../islands/base-path.ts";
40
42
  import { pageDirection, pageLocale } from "./page-locale.ts";
41
43
  import { searchLocaleFor } from "./search-locale.ts";
42
44
  import "blume:theme";
43
45
  import { ClientRouter } from "astro:transitions";
44
46
  import Analytics from "./Analytics.astro";
45
47
  import Banner from "./Banner.astro";
48
+ import DiscoveryLinks from "./DiscoveryLinks.astro";
46
49
  import Favicon from "./Favicon.astro";
47
50
  import Fonts from "./Fonts.astro";
48
51
  import {
@@ -65,7 +68,10 @@ interface Props {
65
68
  /**
66
69
  * Title/description/route for `<title>`, `og:*`, and the header active link.
67
70
  * Unlike RootLayout, `title` is used verbatim (no `- siteTitle` suffix), since
68
- * marketing pages usually set their own complete document title.
71
+ * marketing pages usually set their own complete document title. An omitted
72
+ * `route` is read from the request URL (base-less), so a page that never
73
+ * passes one still gets its own canonical, OG card, and Markdown mirror
74
+ * rather than the homepage's.
69
75
  */
70
76
  page?: { title?: string; description?: string; route?: string };
71
77
  themeMode: "system" | "light" | "dark";
@@ -77,6 +83,13 @@ interface Props {
77
83
  * without wiring anything up.
78
84
  */
79
85
  askEnabled?: boolean;
86
+ /**
87
+ * Start the header see-through, with its chrome in white, so it can sit over
88
+ * a dark hero at the top of the page; it returns to the frosted bar as soon
89
+ * as the page scrolls. The hero has to run under the header for this to
90
+ * show: pull it up by the header's height (`-mt-16`) and pad its top.
91
+ */
92
+ transparentHeader?: boolean;
80
93
  /**
81
94
  * Absolute site URL (`data.config.site`). When set, `canonical` and the
82
95
  * generated `og:image` for this route are derived from it automatically;
@@ -105,6 +118,14 @@ interface Props {
105
118
  * matching RootLayout.
106
119
  */
107
120
  structuredDataEnabled?: boolean;
121
+ /**
122
+ * Which agent-discovery resources exist, advertised as `describedby` head
123
+ * links plus the AI Catalog/ARD pair — the same `DiscoveryLinks` partial
124
+ * RootLayout renders. A custom page gets this from the `blume:data` snapshot
125
+ * without wiring anything up; pass it to override, or `null` to drop the
126
+ * links.
127
+ */
128
+ discovery?: DiscoveryHeadLinks | null;
108
129
  /**
109
130
  * X (Twitter) attribution (`data.config.x`): the site's account and an author
110
131
  * handle, emitted as `twitter:site`/`twitter:creator`.
@@ -143,6 +164,7 @@ const {
143
164
  fontCssVars,
144
165
  searchEnabled,
145
166
  askEnabled,
167
+ transparentHeader,
146
168
  siteUrl,
147
169
  ogEnabled,
148
170
  ogImage,
@@ -150,6 +172,7 @@ const {
150
172
  ogImageSize,
151
173
  canonical,
152
174
  structuredDataEnabled,
175
+ discovery = data.config.discovery,
153
176
  x,
154
177
  noindex,
155
178
  locale: localeProp,
@@ -180,14 +203,30 @@ const navStrings = { ...EN_UI.nav, ...strings.nav };
180
203
  const searchLocale = searchLocaleFor(data.config.i18n, locale);
181
204
  const pageTitle = page?.title ?? site.title;
182
205
  const description = page?.description ?? site.description;
183
- const route = page?.route ?? "/";
206
+ // An omitted `page.route` falls back to the request pathname with the
207
+ // deployment base stripped, not to `/`: a non-root page that copies the
208
+ // homepage example would otherwise claim the homepage's canonical, OG card,
209
+ // and `/index.md` mirror. `trailingSlash: "never"` keeps the pathname bare.
210
+ const route =
211
+ page?.route ??
212
+ stripBase(import.meta.env.BASE_URL ?? "/", Astro.url.pathname);
184
213
  const activeTab = currentTabForRoute(navigation.tabs, route, navigation.root);
185
214
 
215
+ // A custom page has no raw-Markdown mirror — except the homepage: `/index.md`
216
+ // always exists (see `markdownRoutePaths`; a landing page gets the synthesized
217
+ // llms.txt index), and the homepage HTTP `Link` header already advertises it,
218
+ // so the head is its HTML counterpart on hosts that can't set that header.
219
+ // Advertising a mirror for any other custom route would 404.
220
+ const markdownMirror = route === "/" ? withBase("/index.md") : null;
221
+
186
222
  // Derive canonical + og:image from the site URL the same way the catch-all does
187
223
  // for content pages, so a custom page gets both for free. The matching OG card
188
224
  // (`/og/<slug>.png`, `index` for the home) is generated for static custom pages
189
- // when `og.enabled`. Explicit props still win.
225
+ // when `og.enabled` — not for a dynamic route (`[param]`), whose concrete URLs
226
+ // can't be enumerated up front, so such a page advertises no generated card
227
+ // rather than one that 404s. Explicit props still win.
190
228
  const ogSlug = route === "/" ? "index" : route.slice(1);
229
+ const ogCardGenerated = !Astro.routePattern.includes("[");
191
230
  // Absolute URLs carry the deployment base too (`site + base + route`), since the
192
231
  // page is served under it. `withBase` handles a root base as a no-op.
193
232
  const basedRoute = withBase(route);
@@ -211,7 +250,7 @@ const absolutizeOgImage = (value: string): string =>
211
250
  value.startsWith("/") && siteBase ? `${siteBase}${withBase(value)}` : value;
212
251
  const resolvedOgImage = ogImage
213
252
  ? absolutizeOgImage(ogImage)
214
- : ogEnabled && siteBase
253
+ : ogEnabled && ogCardGenerated && siteBase
215
254
  ? `${siteBase}${encodeURI(withBase(`/og/${ogSlug}.png`))}`
216
255
  : null;
217
256
  // Only the generated card has a known size and format, so its dimensions are
@@ -321,6 +360,9 @@ createIconSprite(Astro.locals);
321
360
  {description && <meta content={description} name="twitter:description" />}
322
361
  {xSite && <meta content={xSite} name="twitter:site" />}
323
362
  {xCreator && <meta content={xCreator} name="twitter:creator" />}
363
+ {/* Agent discovery on every page, so a custom page built on PageLayout is
364
+ covered without passing a prop, the same way the docs pages are. */}
365
+ <DiscoveryLinks discovery={discovery} markdownMirror={markdownMirror} />
324
366
  {
325
367
  structuredDataJson && (
326
368
  <script
@@ -343,11 +385,14 @@ createIconSprite(Astro.locals);
343
385
  class="absolute start-[-999px] top-0 z-[100] bg-accent px-4 py-2 text-accent-foreground focus:start-0"
344
386
  href="#blume-content">{strings.page.skipToContent}</a
345
387
  >
388
+ {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
389
+ <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
346
390
  <Banner banner={banner} strings={strings.banner} />
347
391
  <Header
348
392
  askEnabled={askEnabled}
349
393
  askStrings={strings.ask}
350
394
  hasSidebar={false}
395
+ locale={locale}
351
396
  localeSwitch={localeSwitch}
352
397
  logo={logo}
353
398
  navStrings={navStrings}
@@ -358,6 +403,7 @@ createIconSprite(Astro.locals);
358
403
  searchStrings={strings.search}
359
404
  site={site}
360
405
  switcherStrings={strings.languageSwitcher}
406
+ transparent={transparentHeader}
361
407
  />
362
408
  <main id="blume-content"><slot /></main>
363
409
  <slot name="footer" />
@@ -374,6 +420,7 @@ createIconSprite(Astro.locals);
374
420
  aria-label={navStrings.navigation}
375
421
  class="fixed top-[var(--blume-drawer-top,4rem)] start-0 z-[35] h-[calc(100dvh-var(--blume-drawer-top,4rem))] w-64 max-w-[80vw] -translate-x-[105%] overflow-y-auto border-border border-e bg-background px-5 pt-4 pb-6 transition-transform rtl:translate-x-[105%] [:where([data-blume-nav-open])_&]:translate-x-0! lg:hidden"
376
422
  data-blume-nav-drawer
423
+ id="blume-nav-drawer"
377
424
  >
378
425
  <nav aria-label={navStrings.sections}>
379
426
  <ul class="m-0 list-none p-0">
@@ -3,14 +3,20 @@ import IconSprite from "./IconSprite.astro";
3
3
  import { createIconSprite } from "../icon-sprite.ts";
4
4
  import "blume:theme";
5
5
  import data from "blume:data";
6
- import type { BlumeFavicon } from "../../core/data.ts";
6
+ import type {
7
+ BlumeDataConfig,
8
+ BlumeFavicon,
9
+ DiscoveryHeadLinks,
10
+ } from "../../core/data.ts";
7
11
  import { EN_UI } from "../../core/i18n-ui.ts";
8
12
  import type { UIStrings } from "../../core/i18n-ui.ts";
13
+ import { NEW_TAB_HINT_ID } from "../../core/new-tab.ts";
9
14
  import type { FontHead } from "../../theme/fonts.ts";
10
15
  import type { Navigation } from "../../core/types.ts";
11
16
  import Analytics from "./Analytics.astro";
12
17
  import WebMcp from "./WebMcp.astro";
13
18
  import Banner from "./Banner.astro";
19
+ import DiscoveryLinks from "./DiscoveryLinks.astro";
14
20
  import Favicon from "./Favicon.astro";
15
21
  import Fonts from "./Fonts.astro";
16
22
  import {
@@ -50,17 +56,7 @@ interface Props {
50
56
  dismissible: boolean;
51
57
  key: string;
52
58
  } | null;
53
- analytics?: {
54
- cloudflare?: { token: string };
55
- posthog?: { host?: string; key: string };
56
- scripts?: {
57
- attributes?: Record<string, string>;
58
- content?: string;
59
- src?: string;
60
- strategy?: "async" | "defer";
61
- }[];
62
- vercel?: boolean;
63
- } | null;
59
+ analytics?: BlumeDataConfig["analytics"] | null;
64
60
  navigation: Navigation;
65
61
  route: string;
66
62
  themeMode: "system" | "light" | "dark";
@@ -69,6 +65,13 @@ interface Props {
69
65
  pageTitle: string;
70
66
  /** Keep the reference route out of crawler indexes. */
71
67
  noindex?: boolean;
68
+ /**
69
+ * Which agent-discovery resources exist, advertised in the head by the same
70
+ * `DiscoveryLinks` partial the docs shells render; defaults to the
71
+ * `blume:data` snapshot, `null` drops the links. A reference route has no
72
+ * raw-Markdown mirror, so no `alternate` is emitted here.
73
+ */
74
+ discovery?: DiscoveryHeadLinks | null;
72
75
  /**
73
76
  * Active locale code for `<html lang>`. Defaults to the locale Astro
74
77
  * resolved from the URL (`Astro.currentLocale`), then the site default.
@@ -94,6 +97,7 @@ const {
94
97
  searchEnabled,
95
98
  pageTitle,
96
99
  noindex = false,
100
+ discovery = data.config.discovery,
97
101
  locale: localeProp,
98
102
  dir: dirProp,
99
103
  ui,
@@ -106,6 +110,7 @@ const locale = pageLocale(data.config.i18n, localeProp, Astro.currentLocale);
106
110
  const dir = dirProp ?? pageDirection(data.config.i18n, locale);
107
111
 
108
112
  const strings = ui ?? EN_UI;
113
+ const navStrings = { ...EN_UI.nav, ...strings.nav };
109
114
  // Scope search to the reference page's language on a multi-locale site.
110
115
  const searchLocale = searchLocaleFor(data.config.i18n, locale);
111
116
 
@@ -125,6 +130,7 @@ createIconSprite(Astro.locals);
125
130
  <title>{pageTitle}</title>
126
131
  <Favicon favicon={favicon} appleIcon={appleIcon} />
127
132
  <Fonts cssVars={fontCssVars ?? []} />
133
+ <DiscoveryLinks discovery={discovery} />
128
134
  {
129
135
  bannerKey && (
130
136
  <script data-key={bannerKey} is:inline set:html={BANNER_INIT_SCRIPT} />
@@ -138,11 +144,14 @@ createIconSprite(Astro.locals);
138
144
  class="absolute start-[-999px] top-0 z-[100] bg-accent px-4 py-2 text-accent-foreground focus:start-0"
139
145
  href="#blume-content">{strings.page.skipToContent}</a
140
146
  >
147
+ {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
148
+ <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
141
149
  <Banner banner={banner} strings={strings.banner} />
142
150
  <Header
143
151
  askStrings={strings.ask}
144
152
  hasDrawer={false}
145
153
  hasSidebar={false}
154
+ locale={locale}
146
155
  logo={logo}
147
156
  navigation={navigation}
148
157
  route={route}
@@ -7,7 +7,12 @@ import type { UIStrings } from "../../core/i18n-ui.ts";
7
7
  import { resolveDateFormatOptions } from "../../core/date-format.ts";
8
8
  import type { ResolvedDateFormat } from "../../core/schema.ts";
9
9
  import type { OpenInChatProvider } from "../../core/open-in-chat.ts";
10
- import type { BlumeClientData, BlumeFavicon } from "../../core/data.ts";
10
+ import type {
11
+ BlumeClientData,
12
+ BlumeDataConfig,
13
+ BlumeFavicon,
14
+ DiscoveryHeadLinks,
15
+ } from "../../core/data.ts";
11
16
  import type {
12
17
  Heading,
13
18
  LocaleSwitchOption,
@@ -36,11 +41,13 @@ import { buildStructuredData, toIso } from "../../seo/jsonld.ts";
36
41
  import { normalizeXHandle } from "../../seo/x-handle.ts";
37
42
  import { withBase } from "../islands/base-path.ts";
38
43
  import { isExternalUrl } from "../../core/base-path.ts";
44
+ import { NEW_TAB_HINT_ID, newTabAttrs } from "../../core/new-tab.ts";
39
45
  import Analytics from "./Analytics.astro";
40
46
  import WebMcp from "./WebMcp.astro";
41
47
  import Banner from "./Banner.astro";
42
48
  import Breadcrumbs from "./Breadcrumbs.astro";
43
49
  import Empty from "./Empty.astro";
50
+ import DiscoveryLinks from "./DiscoveryLinks.astro";
44
51
  import Favicon from "./Favicon.astro";
45
52
  import Fonts from "./Fonts.astro";
46
53
  import {
@@ -87,17 +94,7 @@ interface Props {
87
94
  dismissible: boolean;
88
95
  key: string;
89
96
  } | null;
90
- analytics?: {
91
- cloudflare?: { token: string };
92
- posthog?: { host?: string; key: string };
93
- scripts?: {
94
- attributes?: Record<string, string>;
95
- content?: string;
96
- src?: string;
97
- strategy?: "async" | "defer";
98
- }[];
99
- vercel?: boolean;
100
- } | null;
97
+ analytics?: BlumeDataConfig["analytics"] | null;
101
98
  navigation: Navigation;
102
99
  mcp?: { name: string; route: string } | null;
103
100
  page: { title: string; description?: string; route: string };
@@ -144,13 +141,11 @@ interface Props {
144
141
  * links on every page — so an agent entering on a deep page (a search
145
142
  * result, a shared link) finds the machine-readable surface without probing
146
143
  * the site root. The HTML counterpart of the homepage-only HTTP `Link`
147
- * header (see `ai/link-headers.ts`).
144
+ * header (see `ai/link-headers.ts`). Defaults to the `blume:data` snapshot,
145
+ * so a custom page on this layout gets the links without passing a prop;
146
+ * `null` drops them for one page.
148
147
  */
149
- discovery?: {
150
- agentReadability: boolean;
151
- aiCatalog: boolean;
152
- llmsTxt: boolean;
153
- } | null;
148
+ discovery?: DiscoveryHeadLinks | null;
154
149
  siteUrl?: string | null;
155
150
  pageType?: string;
156
151
  published?: string | Date | null;
@@ -170,6 +165,13 @@ interface Props {
170
165
  * article so prose punctuation reads correctly; the chrome still mirrors.
171
166
  */
172
167
  contentDir?: "ltr" | "rtl";
168
+ /**
169
+ * Language of the page content itself — the fallback locale on a fallback
170
+ * page (English content at `/ar/…`). Set as the article's `lang` when it
171
+ * differs from the page locale, so assistive tech reads the text in its own
172
+ * language rather than the chrome's.
173
+ */
174
+ contentLocale?: string;
173
175
  /** Resolved UI dictionary; English baseline when omitted. */
174
176
  ui?: UIStrings;
175
177
  /** `hreflang` alternates (absolute URLs) for this page's translations. */
@@ -252,7 +254,7 @@ const {
252
254
  exportEpub,
253
255
  openInChat,
254
256
  feeds,
255
- discovery,
257
+ discovery = data.config.discovery,
256
258
  siteUrl,
257
259
  pageType,
258
260
  published,
@@ -262,6 +264,7 @@ const {
262
264
  locale: localeProp,
263
265
  dir: dirProp,
264
266
  contentDir,
267
+ contentLocale,
265
268
  ui,
266
269
  localeAlternates,
267
270
  xDefault,
@@ -349,7 +352,16 @@ if (isBare) {
349
352
  } else if (isApiOperation) {
350
353
  articleClass = "prose max-w-none";
351
354
  }
352
- const pageTitle = page.title ? `${page.title} - ${site.title}` : site.title;
355
+ // A page titled like the site (a home page often is) takes the bare site title
356
+ // rather than repeating it ("Acme Docs - Acme Docs").
357
+ const pageTitle =
358
+ page.title && page.title !== site.title
359
+ ? `${page.title} - ${site.title}`
360
+ : site.title;
361
+ // The article's own language, when a fallback page renders another locale's
362
+ // content under this one.
363
+ const articleLang =
364
+ contentLocale && contentLocale !== locale ? contentLocale : undefined;
353
365
  const description = page.description ?? site.description;
354
366
  // Blog posts and changelog entries are dated writing, so they take `og:type:
355
367
  // article` — the same split JSON-LD makes when it picks an article @type. Docs
@@ -534,48 +546,7 @@ createIconSprite(Astro.locals);
534
546
  />
535
547
  ))
536
548
  }
537
- {/* Agent discovery on every page, not just the root: an agent that enters
538
- on a deep page (a search result, a shared link) never sees the homepage
539
- HTTP Link header, so the head carries the same describedby links plus
540
- this page's own raw-Markdown mirror. Both rels are IANA-registered. */}
541
- {
542
- discovery?.agentReadability && (
543
- <link
544
- href={withBase("/agent-readability.json")}
545
- rel="describedby"
546
- type="application/json"
547
- />
548
- )
549
- }
550
- {
551
- discovery?.llmsTxt && (
552
- <link href={withBase("/llms.txt")} rel="describedby" type="text/plain" />
553
- )
554
- }
555
- {/* The AI Catalog / ARD manifest under both relations its two specs
556
- define: `ai-catalog` (ai-catalog spec) and `ard` (ARD v0.91), each
557
- pointing at that spec's own well-known path. */}
558
- {
559
- discovery?.aiCatalog && (
560
- <>
561
- <link
562
- href={withBase("/.well-known/ai-catalog.json")}
563
- rel="ai-catalog"
564
- type="application/ai-catalog+json"
565
- />
566
- <link
567
- href={withBase("/.well-known/ard.json")}
568
- rel="ard"
569
- type="application/json"
570
- />
571
- </>
572
- )
573
- }
574
- {
575
- markdownMirror && (
576
- <link href={markdownMirror} rel="alternate" type="text/markdown" />
577
- )
578
- }
549
+ <DiscoveryLinks discovery={discovery} markdownMirror={markdownMirror} />
579
550
  {
580
551
  structuredDataJson && (
581
552
  <script
@@ -606,12 +577,15 @@ createIconSprite(Astro.locals);
606
577
  class="absolute start-[-999px] top-0 z-[100] bg-accent px-4 py-2 text-accent-foreground focus:start-0"
607
578
  href="#blume-content">{strings.page.skipToContent}</a
608
579
  >
580
+ {/* Every new-tab link's aria-describedby target (see core/new-tab.ts). */}
581
+ <span hidden id={NEW_TAB_HINT_ID}>{navStrings.opensInNewTab}</span>
609
582
  <Banner banner={banner} strings={strings.banner} />
610
583
  <VersionBanner notice={versionNotice ?? null} />
611
584
  <HeaderSlot
612
585
  askEnabled={askEnabled}
613
586
  askStrings={strings.ask}
614
587
  layout={layout}
588
+ locale={locale}
615
589
  localeSwitch={localeSwitch}
616
590
  versionSelector={versionSelector}
617
591
  logo={logo}
@@ -632,6 +606,7 @@ createIconSprite(Astro.locals);
632
606
  <aside
633
607
  aria-label={navStrings.primary}
634
608
  data-blume-nav-drawer
609
+ id="blume-nav-drawer"
635
610
  class:list={[
636
611
  "fixed top-[var(--blume-drawer-top,4rem)] start-0 z-[35] h-[calc(100dvh-var(--blume-drawer-top,4rem))] w-64 max-w-[80vw] -translate-x-[105%] overflow-y-auto border-border border-e bg-background px-5 pt-4 pb-6 transition-transform rtl:translate-x-[105%] [:where([data-blume-nav-open])_&]:translate-x-0! lg:sticky lg:top-16 lg:z-auto lg:h-[calc(100dvh-4rem)] lg:w-auto lg:max-w-none lg:translate-x-0! lg:scrollbar-thin lg:scrollbar-thumb-border lg:scrollbar-track-transparent lg:border-e-0 lg:bg-transparent lg:px-4",
637
612
  // A "bare" landing (the changelog index) has no sidebar column on
@@ -657,8 +632,7 @@ createIconSprite(Astro.locals);
657
632
  <a
658
633
  class="block rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground"
659
634
  href={withBase(link.href)}
660
- rel={external ? "noreferrer" : undefined}
661
- target={external ? "_blank" : undefined}
635
+ {...newTabAttrs(link.href)}
662
636
  >
663
637
  <span class="flex items-center gap-2">
664
638
  {link.icon && (
@@ -768,7 +742,18 @@ createIconSprite(Astro.locals);
768
742
  )
769
743
  }
770
744
  <PageHeaderSlot headings={tocHeadings} page={page} route={page.route} />
771
- <article class:list={[articleClass]} dir={contentDir}>
745
+ {/* `data-pagefind-body` scopes Pagefind to the page's own text: without
746
+ it Pagefind indexes the whole <body>, so the header, sidebar, and
747
+ skip link lead every excerpt and match every query. Once any page
748
+ carries it, pages without it (landing pages, the 404, the
749
+ changelog index — none are content routes, which is what site
750
+ search indexes) drop out of the index entirely. */}
751
+ <article
752
+ class:list={[articleClass]}
753
+ data-pagefind-body={indexable && !isBare ? "" : undefined}
754
+ dir={contentDir}
755
+ lang={articleLang}
756
+ >
772
757
  <slot />
773
758
  </article>
774
759
  {
@@ -837,6 +822,7 @@ createIconSprite(Astro.locals);
837
822
  type="button"
838
823
  ></button>
839
824
  <script>
825
+ import type { Zoom } from "medium-zoom";
840
826
  import { copyText, createCopyFlash } from "../copy-feedback.ts";
841
827
  import { syncDrawerInert } from "./drawer-inert.ts";
842
828
  import { chromeIcons as icons } from "../../theme/chrome-icons.ts";
@@ -938,6 +924,13 @@ createIconSprite(Astro.locals);
938
924
  // client-router swap (which replaces the body with server-rendered
939
925
  // markup that has none of this applied). Everything in here binds to
940
926
  // elements the swap just created, so nothing double-attaches.
927
+ // One medium-zoom instance per real page load, re-pointed at each page's
928
+ // images: every instance adds document keyup/scroll and window resize
929
+ // listeners it never removes and holds on to the images it was given, so
930
+ // a fresh instance per client-router swap stacked listeners and kept
931
+ // every earlier page's images alive.
932
+ let zoom: Zoom | undefined;
933
+
941
934
  const initPage = async () => {
942
935
  // Localized copy-button labels, stamped on <body> by the layout markup
943
936
  // (the Search.astro data-attribute channel) since this bundled script
@@ -1056,6 +1049,15 @@ createIconSprite(Astro.locals);
1056
1049
  // via medium-zoom: ESC/scroll/click dismissal, natural-size capping,
1057
1050
  // and the open/close transition races are its problem, not ours.
1058
1051
  // Opt out per-image with `data-no-zoom`.
1052
+ // The previous page's images are gone after a swap; let go of them
1053
+ // before looking at this page. A swap that lands while an image is
1054
+ // zoomed (Back mid-zoom) leaves the instance mid-animation, its overlay
1055
+ // gone with the old body and its close never finishing, so start over
1056
+ // then instead.
1057
+ if (zoom?.getZoomedImage()) {
1058
+ zoom = undefined;
1059
+ }
1060
+ zoom?.detach();
1059
1061
  if (document.body.hasAttribute("data-blume-image-zoom")) {
1060
1062
  const zoomTargets = Array.from(
1061
1063
  document.querySelectorAll<HTMLImageElement>(
@@ -1071,11 +1073,12 @@ createIconSprite(Astro.locals);
1071
1073
  const { default: mediumZoom } = await import(
1072
1074
  "medium-zoom/dist/pure"
1073
1075
  );
1074
- mediumZoom(zoomTargets, {
1076
+ zoom ??= mediumZoom({
1075
1077
  background:
1076
1078
  "color-mix(in oklab, var(--color-background) 80%, transparent)",
1077
1079
  margin: 24,
1078
1080
  });
1081
+ zoom.attach(...zoomTargets);
1079
1082
  }
1080
1083
  }
1081
1084
  };
@@ -84,8 +84,11 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
84
84
  >
85
85
  <Icon name="search" size={16} />
86
86
  <span class="flex-1 text-start max-lg:hidden">{s.button}</span>
87
- <kbd class="font-mono text-[0.7rem] max-lg:hidden" data-blume-search-kbd
88
- >⌘K</kbd
87
+ {/* A key chord reads left to right in every locale ("⌘K", never "K⌘"). */}
88
+ <kbd
89
+ class="font-mono text-[0.7rem] max-lg:hidden"
90
+ data-blume-search-kbd
91
+ dir="ltr">⌘K</kbd
89
92
  >
90
93
  </button>
91
94
 
@@ -195,8 +198,10 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
195
198
  {s.open}
196
199
  </span>
197
200
  <span class="flex items-center gap-1 max-md:hidden">
198
- <kbd class={`${kbd} text-[0.65rem]`} data-blume-search-preview-kbd
199
- >⌘J</kbd
201
+ <kbd
202
+ class={`${kbd} text-[0.65rem]`}
203
+ data-blume-search-preview-kbd
204
+ dir="ltr">⌘J</kbd
200
205
  >
201
206
  {s.preview}
202
207
  </span>
@@ -271,6 +276,26 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
271
276
  }
272
277
  };
273
278
 
279
+ // The generated client and the index it fetches are the same on every
280
+ // page (locale and version filters apply per query), but each client-router
281
+ // swap rebuilds the header, and this element with it. Loading once in
282
+ // module scope — bundled modules survive swaps — keeps the first search
283
+ // after a navigation from refetching the index and rebuilding it. A failed
284
+ // load clears the cache, so the next open retries.
285
+ let searchClient: Promise<SearchFn> | undefined;
286
+ const loadSearchClient = (): Promise<SearchFn> => {
287
+ searchClient ??= (async () => {
288
+ try {
289
+ const { createSearch } = await import("blume:search-client");
290
+ return await createSearch();
291
+ } catch (error) {
292
+ searchClient = undefined;
293
+ throw error;
294
+ }
295
+ })();
296
+ return searchClient;
297
+ };
298
+
274
299
  class BlumeSearch extends HTMLElement {
275
300
  dialog!: HTMLDialogElement;
276
301
  input!: HTMLInputElement;
@@ -481,8 +506,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
481
506
  if (!this.loaded) {
482
507
  this.loading = true;
483
508
  try {
484
- const { createSearch } = await import("blume:search-client");
485
- this.searchFn = await createSearch();
509
+ this.searchFn = await loadSearchClient();
486
510
  // Only latch on success — a transient failure (flaky network
487
511
  // fetching the index) must retry on the next open, not disable
488
512
  // search until a full page reload.
@@ -2,7 +2,7 @@
2
2
  // WebMCP: expose the docs' read-only tools (search, page Markdown, the docs
3
3
  // index) to agentic browsers via the page's model context. Rendered by
4
4
  // PageLayout on every page; the marker element below both gates the script
5
- // (absent when `ai.webmcp` is off, so it returns immediately) and carries the
5
+ // (absent when `agents.webmcp` is off, so it returns immediately) and carries the
6
6
  // two config flags the client needs — importing `blume:data` from the client
7
7
  // script would drag the whole navigation snapshot into the bundle.
8
8
  import data from "blume:data";