blume 1.7.2 → 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 (677) hide show
  1. package/AGENTS.md +19 -0
  2. package/CHANGELOG.md +227 -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-xhtpx3ff.js → chunk-1w8dp3qb.js} +17 -16
  7. package/dist/cli/{chunk-xhtpx3ff.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-9he6crym.js → chunk-6k8vp3ta.js} +25 -13
  15. package/dist/cli/{chunk-9he6crym.js.map → chunk-6k8vp3ta.js.map} +4 -4
  16. package/dist/cli/{chunk-mt76t7dj.js → chunk-79njf86q.js} +133 -54
  17. package/dist/cli/chunk-79njf86q.js.map +11 -0
  18. package/dist/cli/{chunk-3w7b2vcx.js → chunk-7ez8ny0t.js} +2 -2
  19. package/dist/cli/{chunk-688e0dde.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-hs3gbh8p.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-9bkjd11x.js → chunk-bw22s759.js} +15 -5
  33. package/dist/cli/{chunk-9bkjd11x.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-t3tj0dgr.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-exeeb35e.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-12dxjqk7.js → chunk-fh5hj5jt.js} +44 -21
  48. package/dist/cli/chunk-fh5hj5jt.js.map +10 -0
  49. package/dist/cli/{chunk-8cd8tj54.js → chunk-j8mw0za6.js} +86 -57
  50. package/dist/cli/chunk-j8mw0za6.js.map +35 -0
  51. package/dist/cli/chunk-jts8mvcz.js +106 -0
  52. package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
  53. package/dist/cli/{chunk-ejjx8znq.js → chunk-mnqj32sj.js} +505 -536
  54. package/dist/cli/chunk-mnqj32sj.js.map +12 -0
  55. package/dist/cli/{chunk-196vjxp9.js → chunk-mwt1k8n7.js} +100 -372
  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-n9sra6sy.js → chunk-pat2zzwc.js} +10 -14
  60. package/dist/cli/{chunk-n9sra6sy.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-cvky9gb2.js → chunk-sqn5t4q0.js} +81 -87
  64. package/dist/cli/chunk-sqn5t4q0.js.map +10 -0
  65. package/dist/cli/{chunk-eevwt1sc.js → chunk-tzne8qfq.js} +15 -15
  66. package/dist/cli/{chunk-eevwt1sc.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-5n7t497w.js → chunk-z1f5arsg.js} +247 -755
  74. package/dist/cli/chunk-z1f5arsg.js.map +36 -0
  75. package/dist/cli/{chunk-hdm2dkd2.js → chunk-zg2gtj10.js} +1086 -2596
  76. package/dist/cli/chunk-zg2gtj10.js.map +35 -0
  77. package/dist/cli/{chunk-ppfvdcd4.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 +209 -505
  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 +28 -11
  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 +2291 -441
  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 +20 -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 +176 -61
  290. package/docs/configuration/customization.mdx +15 -9
  291. package/docs/configuration/index.mdx +52 -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 +37 -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 +12 -7
  303. package/docs/content/sources.mdx +145 -50
  304. package/docs/content/syntax.mdx +26 -12
  305. package/docs/content/versioning.mdx +3 -3
  306. package/docs/discoverability/agent-discovery.mdx +91 -10
  307. package/docs/discoverability/index.mdx +5 -5
  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 +26 -22
  329. package/src/ai/ai-catalog.ts +259 -0
  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 +19 -6
  338. package/src/ai/llms.ts +26 -15
  339. package/src/ai/markdown.ts +35 -5
  340. package/src/ai/mcp/data.ts +9 -5
  341. package/src/ai/mcp/discovery.ts +1 -1
  342. package/src/ai/openapi-components.ts +4 -1
  343. package/src/ai/relative-links.ts +170 -0
  344. package/src/ai/serializers.ts +2 -2
  345. package/src/ai/skills.ts +1 -1
  346. package/src/ai/web-bot-auth.ts +2 -2
  347. package/src/analytics/adobe.ts +46 -0
  348. package/src/analytics/amplitude.ts +79 -0
  349. package/src/analytics/clarity.ts +46 -0
  350. package/src/analytics/clearbit.ts +47 -0
  351. package/src/analytics/cloudflare.ts +67 -0
  352. package/src/analytics/fathom.ts +64 -0
  353. package/src/analytics/google-analytics.ts +84 -0
  354. package/src/analytics/google-tag-manager.ts +61 -0
  355. package/src/analytics/head.ts +130 -0
  356. package/src/analytics/heap.ts +65 -0
  357. package/src/analytics/hightouch.ts +77 -0
  358. package/src/analytics/hotjar.ts +47 -0
  359. package/src/analytics/index.ts +71 -0
  360. package/src/analytics/inline.ts +21 -0
  361. package/src/analytics/logrocket.ts +71 -0
  362. package/src/analytics/mixpanel.ts +92 -0
  363. package/src/analytics/pirsch.ts +66 -0
  364. package/src/analytics/plausible.ts +83 -0
  365. package/src/analytics/posthog.ts +78 -0
  366. package/src/analytics/schema.ts +60 -0
  367. package/src/analytics/script.ts +60 -0
  368. package/src/analytics/segment.ts +85 -0
  369. package/src/analytics/vercel.ts +50 -0
  370. package/src/astro/adapter-root.ts +7 -9
  371. package/src/astro/component-slots.ts +131 -91
  372. package/src/astro/generate.ts +114 -345
  373. package/src/astro/integration.ts +2 -6
  374. package/src/astro/pages.ts +13 -101
  375. package/src/astro/render-deps.ts +379 -0
  376. package/src/astro/runtime-deps.ts +201 -0
  377. package/src/astro/templates.ts +554 -541
  378. package/src/audit/agent.ts +24 -0
  379. package/src/audit/catalog.ts +2 -2
  380. package/src/audit/checks/assets.ts +2 -2
  381. package/src/audit/checks/dns-aid.ts +1 -1
  382. package/src/audit/checks/duplicates.ts +3 -1
  383. package/src/audit/checks/i18n.ts +1 -1
  384. package/src/audit/checks/indexability.ts +7 -7
  385. package/src/audit/checks/links.ts +2 -2
  386. package/src/audit/checks/llms.ts +6 -6
  387. package/src/audit/checks/network.ts +3 -3
  388. package/src/audit/checks/og-image.ts +2 -2
  389. package/src/audit/checks/robots.ts +1 -1
  390. package/src/audit/checks/sitemap.ts +2 -2
  391. package/src/audit/checks/social.ts +1 -1
  392. package/src/audit/run.ts +4 -3
  393. package/src/audit/terms.ts +31 -0
  394. package/src/audit/url.ts +13 -13
  395. package/src/cli/command-meta.ts +10 -0
  396. package/src/cli/commands/audit.ts +39 -20
  397. package/src/cli/commands/build.ts +71 -314
  398. package/src/cli/commands/check.ts +1 -0
  399. package/src/cli/commands/dev.ts +40 -1
  400. package/src/cli/commands/doctor.ts +90 -14
  401. package/src/cli/commands/eject.ts +44 -7
  402. package/src/cli/commands/eval.ts +2 -2
  403. package/src/cli/commands/init.ts +161 -41
  404. package/src/cli/commands/migrate.ts +121 -0
  405. package/src/cli/commands/preview.ts +7 -1
  406. package/src/cli/commands/translate.ts +2 -2
  407. package/src/cli/commands/upgrade.ts +141 -0
  408. package/src/cli/commands/version.ts +57 -44
  409. package/src/cli/eject-scripts.ts +121 -6
  410. package/src/cli/index.ts +11 -1
  411. package/src/cli/init/install.ts +70 -0
  412. package/src/cli/init/questions.ts +7 -0
  413. package/src/cli/init/scaffold.ts +459 -75
  414. package/src/cli/lazy-command.ts +37 -1
  415. package/src/cli/prepare.ts +40 -6
  416. package/src/cli/required-secrets.ts +38 -11
  417. package/src/cli/unknown-flags.ts +266 -0
  418. package/src/cli/yarn-pnp.ts +52 -0
  419. package/src/components/content/AccordionItem.astro +7 -1
  420. package/src/components/content/Card.astro +4 -3
  421. package/src/components/content/ColorItem.astro +22 -4
  422. package/src/components/content/GithubInfo.astro +2 -2
  423. package/src/components/content/Prompt.astro +25 -25
  424. package/src/components/content/Tabs.astro +3 -1
  425. package/src/components/content/Tile.astro +2 -3
  426. package/src/components/content/Tooltip.astro +57 -9
  427. package/src/components/content/content-strings.ts +34 -0
  428. package/src/components/content/diff.ts +1 -1
  429. package/src/components/content/mermaid-element.ts +13 -2
  430. package/src/components/content/prompt-markdown.ts +292 -0
  431. package/src/components/content/tooltip-id.ts +41 -0
  432. package/src/components/islands/hooks.ts +75 -3
  433. package/src/components/layout/Analytics.astro +21 -79
  434. package/src/components/layout/Banner.astro +3 -1
  435. package/src/components/layout/DiscoveryLinks.astro +69 -0
  436. package/src/components/layout/Header.astro +83 -20
  437. package/src/components/layout/LanguageSwitcher.astro +4 -1
  438. package/src/components/layout/Logo.astro +23 -2
  439. package/src/components/layout/NavSelector.astro +13 -2
  440. package/src/components/layout/NavTree.astro +12 -3
  441. package/src/components/layout/NavTreeScript.astro +17 -3
  442. package/src/components/layout/PageActions.astro +3 -3
  443. package/src/components/layout/PageFeedback.astro +11 -1
  444. package/src/components/layout/PageLayout.astro +52 -5
  445. package/src/components/layout/ReferenceLayout.astro +21 -12
  446. package/src/components/layout/RootLayout.astro +69 -43
  447. package/src/components/layout/Search.astro +30 -6
  448. package/src/components/layout/WebMcp.astro +1 -1
  449. package/src/components/layout/analytics-client.ts +101 -15
  450. package/src/components/layout/drawer-inert.ts +113 -15
  451. package/src/components/layout/dropdown-clamp.ts +105 -0
  452. package/src/components/layout/head-scripts.ts +15 -6
  453. package/src/components/layout/nav-utils.ts +17 -0
  454. package/src/components/layout/search/algolia.ts +8 -8
  455. package/src/components/layout/search/orama-cloud.ts +9 -7
  456. package/src/components/layout/search/typesense.ts +11 -17
  457. package/src/components/openapi/MessageComposer.astro +2 -2
  458. package/src/components/openapi/Playground.astro +2 -2
  459. package/src/components/openapi/playground-client.ts +79 -10
  460. package/src/core/changelog-index.ts +24 -0
  461. package/src/core/component-overrides.ts +399 -154
  462. package/src/core/config-input.ts +212 -543
  463. package/src/core/config.ts +130 -41
  464. package/src/core/custom-pages.ts +105 -0
  465. package/src/core/data.ts +32 -11
  466. package/src/core/define-components.ts +12 -9
  467. package/src/core/deployment-env.ts +18 -74
  468. package/src/core/diagnostics.ts +14 -7
  469. package/src/core/graph.ts +69 -25
  470. package/src/core/i18n-ui.ts +6 -2
  471. package/src/core/i18n.ts +17 -0
  472. package/src/core/includes.ts +156 -38
  473. package/src/core/last-modified.ts +6 -11
  474. package/src/core/links.ts +166 -2
  475. package/src/core/manifest.ts +10 -3
  476. package/src/core/navigation.ts +137 -19
  477. package/src/core/new-tab.ts +35 -0
  478. package/src/core/node-require.ts +21 -0
  479. package/src/core/project-graph.ts +42 -26
  480. package/src/core/project.ts +9 -4
  481. package/src/core/request-body.ts +61 -0
  482. package/src/core/safe-href.ts +28 -0
  483. package/src/core/safe-links.ts +68 -0
  484. package/src/core/schema.ts +531 -721
  485. package/src/core/server-features.ts +8 -9
  486. package/src/core/sources/assets.ts +47 -11
  487. package/src/core/sources/collection.ts +67 -0
  488. package/src/core/sources/contentful-rich-text.ts +285 -0
  489. package/src/core/sources/contentful.ts +173 -0
  490. package/src/core/sources/github-releases.ts +51 -1
  491. package/src/core/sources/json.ts +71 -0
  492. package/src/core/sources/lexical.ts +195 -0
  493. package/src/core/sources/lower.ts +226 -0
  494. package/src/core/sources/normalize.ts +52 -2
  495. package/src/core/sources/notion.ts +39 -28
  496. package/src/core/sources/payload.ts +135 -0
  497. package/src/core/sources/portable-text.ts +11 -16
  498. package/src/core/sources/remote.ts +226 -0
  499. package/src/core/sources/resolve.ts +104 -166
  500. package/src/core/sources/sanity.ts +16 -49
  501. package/src/core/sources/strapi-blocks.ts +124 -0
  502. package/src/core/sources/strapi.ts +191 -0
  503. package/src/core/sources/types.ts +12 -1
  504. package/src/core/types.ts +20 -1
  505. package/src/core/ui-packs/ar.ts +6 -2
  506. package/src/core/ui-packs/bg.ts +6 -2
  507. package/src/core/ui-packs/bn.ts +6 -2
  508. package/src/core/ui-packs/ca.ts +3 -1
  509. package/src/core/ui-packs/cs.ts +6 -2
  510. package/src/core/ui-packs/da.ts +6 -2
  511. package/src/core/ui-packs/de.ts +6 -2
  512. package/src/core/ui-packs/el.ts +3 -1
  513. package/src/core/ui-packs/es.ts +3 -1
  514. package/src/core/ui-packs/fa.ts +6 -2
  515. package/src/core/ui-packs/fi.ts +6 -2
  516. package/src/core/ui-packs/fr.ts +3 -1
  517. package/src/core/ui-packs/he.ts +6 -2
  518. package/src/core/ui-packs/hi.ts +6 -2
  519. package/src/core/ui-packs/hr.ts +6 -2
  520. package/src/core/ui-packs/hu.ts +6 -2
  521. package/src/core/ui-packs/id.ts +6 -2
  522. package/src/core/ui-packs/it.ts +3 -1
  523. package/src/core/ui-packs/ja.ts +3 -1
  524. package/src/core/ui-packs/ko.ts +3 -1
  525. package/src/core/ui-packs/nl.ts +6 -2
  526. package/src/core/ui-packs/no.ts +6 -2
  527. package/src/core/ui-packs/pl.ts +6 -2
  528. package/src/core/ui-packs/pt-br.ts +3 -1
  529. package/src/core/ui-packs/pt.ts +3 -1
  530. package/src/core/ui-packs/ro.ts +6 -2
  531. package/src/core/ui-packs/ru.ts +6 -2
  532. package/src/core/ui-packs/sk.ts +6 -2
  533. package/src/core/ui-packs/sr.ts +6 -2
  534. package/src/core/ui-packs/sv.ts +6 -2
  535. package/src/core/ui-packs/th.ts +3 -1
  536. package/src/core/ui-packs/tr.ts +6 -2
  537. package/src/core/ui-packs/uk.ts +6 -2
  538. package/src/core/ui-packs/vi.ts +3 -1
  539. package/src/core/ui-packs/zh-tw.ts +3 -1
  540. package/src/core/ui-packs/zh.ts +3 -1
  541. package/src/core/unrecognized-keys.ts +10 -0
  542. package/src/core/version-cut.ts +116 -8
  543. package/src/deploy/adapter-output.ts +57 -97
  544. package/src/deploy/adapters/cloudflare.ts +39 -0
  545. package/src/deploy/adapters/index.ts +41 -0
  546. package/src/deploy/adapters/netlify.ts +34 -0
  547. package/src/deploy/adapters/node.ts +34 -0
  548. package/src/deploy/adapters/registry.ts +127 -0
  549. package/src/deploy/adapters/types.ts +92 -0
  550. package/src/deploy/adapters/vercel.ts +35 -0
  551. package/src/deploy/artifacts.ts +67 -28
  552. package/src/deploy/cloudflare-negotiation.ts +179 -100
  553. package/src/deploy/function-bundle.ts +18 -3
  554. package/src/deploy/headers.ts +129 -22
  555. package/src/deploy/node-headers.ts +198 -0
  556. package/src/deploy/platforms/cloudflare.ts +312 -0
  557. package/src/deploy/platforms/index.ts +67 -0
  558. package/src/deploy/platforms/netlify.ts +52 -0
  559. package/src/deploy/platforms/node.ts +40 -0
  560. package/src/deploy/platforms/paths.ts +42 -0
  561. package/src/deploy/platforms/static.ts +29 -0
  562. package/src/deploy/platforms/types.ts +112 -0
  563. package/src/deploy/platforms/vercel.ts +193 -0
  564. package/src/deploy/redirects.ts +30 -18
  565. package/src/deploy/robots.ts +3 -3
  566. package/src/deploy/rss.ts +2 -2
  567. package/src/deploy/sitemap.ts +2 -2
  568. package/src/deploy/vercel-negotiation.ts +27 -4
  569. package/src/eval/agents.ts +32 -1
  570. package/src/eval/findings.ts +19 -11
  571. package/src/eval/report.ts +10 -2
  572. package/src/markdown/external-links.ts +65 -0
  573. package/src/markdown/include.ts +45 -27
  574. package/src/markdown/index.ts +34 -9
  575. package/src/markdown/inline-code.ts +12 -6
  576. package/src/markdown/relative-links.ts +324 -0
  577. package/src/markdown/themes.ts +3 -3
  578. package/src/migrate/migrate.ts +149 -0
  579. package/src/openapi/parse.ts +64 -16
  580. package/src/openapi/proxy.ts +62 -10
  581. package/src/openapi/references.ts +147 -147
  582. package/src/openapi/render-mdx.ts +32 -10
  583. package/src/openapi/scalar.ts +15 -13
  584. package/src/openapi/sentence.ts +14 -0
  585. package/src/openapi/source.ts +37 -21
  586. package/src/openapi/spec-dependency-error.ts +15 -0
  587. package/src/reference/asyncapi.ts +83 -0
  588. package/src/reference/graphql.ts +89 -0
  589. package/src/reference/index.ts +40 -0
  590. package/src/reference/openapi.ts +83 -0
  591. package/src/reference/options.ts +201 -0
  592. package/src/reference/scalar.ts +136 -0
  593. package/src/reference/schema.ts +88 -0
  594. package/src/registry/eject.ts +97 -42
  595. package/src/search/adapters/algolia.ts +46 -0
  596. package/src/search/adapters/flexsearch.ts +29 -0
  597. package/src/search/adapters/index.ts +39 -0
  598. package/src/search/adapters/mixedbread.ts +39 -0
  599. package/src/search/adapters/orama-cloud.ts +51 -0
  600. package/src/search/adapters/orama.ts +24 -0
  601. package/src/search/adapters/pagefind.ts +27 -0
  602. package/src/search/adapters/registry.ts +131 -0
  603. package/src/search/adapters/types.ts +46 -0
  604. package/src/search/adapters/typesense.ts +57 -0
  605. package/src/search/build.ts +33 -7
  606. package/src/search/documents.ts +6 -0
  607. package/src/search/sync/algolia.ts +12 -11
  608. package/src/search/sync/index.ts +35 -20
  609. package/src/search/sync/orama-cloud.ts +12 -9
  610. package/src/search/sync/typesense.ts +15 -13
  611. package/src/sources/contentful.ts +63 -0
  612. package/src/sources/custom.ts +38 -0
  613. package/src/sources/filesystem.ts +51 -0
  614. package/src/sources/github-releases.ts +52 -0
  615. package/src/sources/index.ts +60 -0
  616. package/src/sources/mdx-remote.ts +76 -0
  617. package/src/sources/notion.ts +63 -0
  618. package/src/sources/obsidian.ts +38 -0
  619. package/src/sources/payload.ts +60 -0
  620. package/src/sources/registry.ts +182 -0
  621. package/src/sources/sanity.ts +66 -0
  622. package/src/sources/shared.ts +52 -0
  623. package/src/sources/strapi.ts +58 -0
  624. package/src/theme/entry.ts +47 -2
  625. package/src/translate/report.ts +40 -7
  626. package/src/upgrade/upgrade.ts +499 -0
  627. package/dist/cli/chunk-12dxjqk7.js.map +0 -10
  628. package/dist/cli/chunk-196vjxp9.js.map +0 -13
  629. package/dist/cli/chunk-2mzebbbz.js +0 -69
  630. package/dist/cli/chunk-2z47ypj8.js.map +0 -11
  631. package/dist/cli/chunk-30e87n55.js +0 -108
  632. package/dist/cli/chunk-30e87n55.js.map +0 -10
  633. package/dist/cli/chunk-450a7rcr.js +0 -185
  634. package/dist/cli/chunk-450a7rcr.js.map +0 -11
  635. package/dist/cli/chunk-5n7t497w.js.map +0 -40
  636. package/dist/cli/chunk-61j18dwk.js +0 -5342
  637. package/dist/cli/chunk-61j18dwk.js.map +0 -58
  638. package/dist/cli/chunk-88by27n5.js +0 -17
  639. package/dist/cli/chunk-88by27n5.js.map +0 -10
  640. package/dist/cli/chunk-8cd8tj54.js.map +0 -34
  641. package/dist/cli/chunk-aztttvb3.js +0 -381
  642. package/dist/cli/chunk-aztttvb3.js.map +0 -12
  643. package/dist/cli/chunk-cvky9gb2.js.map +0 -11
  644. package/dist/cli/chunk-ejjx8znq.js.map +0 -15
  645. package/dist/cli/chunk-fmceyezb.js +0 -1007
  646. package/dist/cli/chunk-fmceyezb.js.map +0 -13
  647. package/dist/cli/chunk-hdm2dkd2.js.map +0 -48
  648. package/dist/cli/chunk-hs3gbh8p.js.map +0 -10
  649. package/dist/cli/chunk-jbj4qhfw.js +0 -30
  650. package/dist/cli/chunk-jbj4qhfw.js.map +0 -10
  651. package/dist/cli/chunk-mqb2ka8m.js +0 -68
  652. package/dist/cli/chunk-mqb2ka8m.js.map +0 -10
  653. package/dist/cli/chunk-mt76t7dj.js.map +0 -11
  654. package/dist/cli/chunk-q4rae3bg.js +0 -60
  655. package/dist/cli/chunk-q4rae3bg.js.map +0 -10
  656. package/dist/cli/chunk-ra1v2nc2.js +0 -35
  657. package/dist/cli/chunk-ra1v2nc2.js.map +0 -10
  658. package/dist/cli/chunk-t3tj0dgr.js.map +0 -15
  659. package/dist/cli/chunk-tqa1s0k8.js +0 -69
  660. package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
  661. package/dist/cli/chunk-vh9w1sgp.js +0 -73
  662. package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
  663. package/dist/cli/chunk-vkrsvbr5.js +0 -107
  664. package/dist/cli/chunk-vkrsvbr5.js.map +0 -11
  665. package/dist/cli/chunk-vrfp10qk.js +0 -81
  666. package/dist/cli/chunk-vrfp10qk.js.map +0 -10
  667. package/dist/cli/chunk-wjt80jps.js +0 -1049
  668. package/dist/cli/chunk-wjt80jps.js.map +0 -24
  669. package/docs/advanced/api-reference.mdx +0 -240
  670. package/docs/reference/cli.mdx +0 -197
  671. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
  672. package/src/components/content/changelog-element.ts +0 -69
  673. package/src/search/providers.ts +0 -91
  674. /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-7ez8ny0t.js.map} +0 -0
  675. /package/dist/cli/{chunk-688e0dde.js.map → chunk-88cpgt6h.js.map} +0 -0
  676. /package/dist/cli/{chunk-exeeb35e.js.map → chunk-f7t03s3g.js.map} +0 -0
  677. /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-zxccj738.js.map} +0 -0
@@ -8,6 +8,7 @@ import { withBase } from "../islands/base-path.ts";
8
8
  import Self from "./NavTree.astro";
9
9
  import NavTreeCache from "./NavTreeCache.astro";
10
10
  import NavTreeScript from "./NavTreeScript.astro";
11
+ import { isGroupRowCurrent } from "./nav-utils.ts";
11
12
 
12
13
  interface PagePanel {
13
14
  id: string;
@@ -180,7 +181,11 @@ const initialId =
180
181
  <Icon class="rtl:-scale-x-100" name="arrow-left" size={16} />
181
182
  </button>
182
183
  <a
183
- aria-current={panel.route === currentRoute ? "page" : undefined}
184
+ aria-current={
185
+ isGroupRowCurrent(panel.node, currentRoute)
186
+ ? "page"
187
+ : undefined
188
+ }
184
189
  class="flex-1 truncate rounded-[0.65rem] px-1 py-1 font-semibold text-foreground text-sm transition-colors hover:bg-muted"
185
190
  href={withBase(panel.route)}
186
191
  >
@@ -329,7 +334,9 @@ const initialId =
329
334
  {item.route ? (
330
335
  <a
331
336
  aria-current={
332
- item.route === currentRoute ? "page" : undefined
337
+ isGroupRowCurrent(item, currentRoute)
338
+ ? "page"
339
+ : undefined
333
340
  }
334
341
  class="blume-nav-summary-link"
335
342
  href={withBase(item.route)}
@@ -412,7 +419,9 @@ const initialId =
412
419
  <p class="blume-nav-heading">
413
420
  {item.route ? (
414
421
  <a
415
- aria-current={item.route === currentRoute ? "page" : undefined}
422
+ aria-current={
423
+ isGroupRowCurrent(item, currentRoute) ? "page" : undefined
424
+ }
416
425
  class="blume-nav-heading-link"
417
426
  href={withBase(item.route)}
418
427
  >
@@ -77,7 +77,12 @@
77
77
  }
78
78
  const slot = deferredIn(details.querySelector(":scope > [data-nav-src]"));
79
79
  if (slot) {
80
- void fillDeferred(slot);
80
+ // A fragment that can't load (offline, a deploy that dropped it)
81
+ // closes the group again instead of leaving it open and empty; the
82
+ // slot keeps its `data-nav-src`, so the next open retries.
83
+ fillDeferred(slot).catch(() => {
84
+ details.open = false;
85
+ });
81
86
  }
82
87
  },
83
88
  true
@@ -159,8 +164,17 @@
159
164
  if (!next) {
160
165
  return;
161
166
  }
162
- // A deferred panel loads its contents before it slides in.
163
- await fillDeferred(next);
167
+ // A deferred panel loads its contents before it slides in. When its
168
+ // fragment can't load (offline, a deploy that dropped it), the drill-in
169
+ // follows the section's own page link — the panel's title — instead of
170
+ // leaving the click dead. A section with no page stays put, and the next
171
+ // drill-in retries the fetch.
172
+ try {
173
+ await fillDeferred(next);
174
+ } catch {
175
+ next.querySelector<HTMLAnchorElement>("a[href]")?.click();
176
+ return;
177
+ }
164
178
  const prev = this.active;
165
179
  if (prev === next) {
166
180
  return;
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { EN_UI } from "../../core/i18n-ui.ts";
3
3
  import type { UIStrings } from "../../core/i18n-ui.ts";
4
+ import { NEW_TAB_ATTRS } from "../../core/new-tab.ts";
4
5
  import { openInChatProviders } from "../../core/open-in-chat.ts";
5
6
  import type { OpenInChatProvider } from "../../core/open-in-chat.ts";
6
7
  import { GITHUB_MARK } from "../github-mark.ts";
@@ -112,7 +113,7 @@ const menuRowClass =
112
113
  >
113
114
  {
114
115
  editUrl && (
115
- <a class={rowClass} href={editUrl} rel="noreferrer" target="_blank">
116
+ <a class={rowClass} href={editUrl} {...NEW_TAB_ATTRS}>
116
117
  <span set:html={GITHUB_LOGO} />
117
118
  {a.edit}
118
119
  </a>
@@ -192,8 +193,7 @@ const menuRowClass =
192
193
  <a
193
194
  class={menuRowClass}
194
195
  data-open-in={provider.key}
195
- rel="noreferrer"
196
- target="_blank"
196
+ {...NEW_TAB_ATTRS}
197
197
  >
198
198
  <span set:html={LOGOS[provider.key]} />
199
199
  <span class="flex-1">{provider.label}</span>
@@ -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,9 +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?: { agentReadability: boolean; llmsTxt: boolean } | null;
148
+ discovery?: DiscoveryHeadLinks | null;
150
149
  siteUrl?: string | null;
151
150
  pageType?: string;
152
151
  published?: string | Date | null;
@@ -166,6 +165,13 @@ interface Props {
166
165
  * article so prose punctuation reads correctly; the chrome still mirrors.
167
166
  */
168
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;
169
175
  /** Resolved UI dictionary; English baseline when omitted. */
170
176
  ui?: UIStrings;
171
177
  /** `hreflang` alternates (absolute URLs) for this page's translations. */
@@ -248,7 +254,7 @@ const {
248
254
  exportEpub,
249
255
  openInChat,
250
256
  feeds,
251
- discovery,
257
+ discovery = data.config.discovery,
252
258
  siteUrl,
253
259
  pageType,
254
260
  published,
@@ -258,6 +264,7 @@ const {
258
264
  locale: localeProp,
259
265
  dir: dirProp,
260
266
  contentDir,
267
+ contentLocale,
261
268
  ui,
262
269
  localeAlternates,
263
270
  xDefault,
@@ -345,7 +352,16 @@ if (isBare) {
345
352
  } else if (isApiOperation) {
346
353
  articleClass = "prose max-w-none";
347
354
  }
348
- 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;
349
365
  const description = page.description ?? site.description;
350
366
  // Blog posts and changelog entries are dated writing, so they take `og:type:
351
367
  // article` — the same split JSON-LD makes when it picks an article @type. Docs
@@ -530,29 +546,7 @@ createIconSprite(Astro.locals);
530
546
  />
531
547
  ))
532
548
  }
533
- {/* Agent discovery on every page, not just the root: an agent that enters
534
- on a deep page (a search result, a shared link) never sees the homepage
535
- HTTP Link header, so the head carries the same describedby links plus
536
- this page's own raw-Markdown mirror. Both rels are IANA-registered. */}
537
- {
538
- discovery?.agentReadability && (
539
- <link
540
- href={withBase("/agent-readability.json")}
541
- rel="describedby"
542
- type="application/json"
543
- />
544
- )
545
- }
546
- {
547
- discovery?.llmsTxt && (
548
- <link href={withBase("/llms.txt")} rel="describedby" type="text/plain" />
549
- )
550
- }
551
- {
552
- markdownMirror && (
553
- <link href={markdownMirror} rel="alternate" type="text/markdown" />
554
- )
555
- }
549
+ <DiscoveryLinks discovery={discovery} markdownMirror={markdownMirror} />
556
550
  {
557
551
  structuredDataJson && (
558
552
  <script
@@ -583,12 +577,15 @@ createIconSprite(Astro.locals);
583
577
  class="absolute start-[-999px] top-0 z-[100] bg-accent px-4 py-2 text-accent-foreground focus:start-0"
584
578
  href="#blume-content">{strings.page.skipToContent}</a
585
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>
586
582
  <Banner banner={banner} strings={strings.banner} />
587
583
  <VersionBanner notice={versionNotice ?? null} />
588
584
  <HeaderSlot
589
585
  askEnabled={askEnabled}
590
586
  askStrings={strings.ask}
591
587
  layout={layout}
588
+ locale={locale}
592
589
  localeSwitch={localeSwitch}
593
590
  versionSelector={versionSelector}
594
591
  logo={logo}
@@ -609,6 +606,7 @@ createIconSprite(Astro.locals);
609
606
  <aside
610
607
  aria-label={navStrings.primary}
611
608
  data-blume-nav-drawer
609
+ id="blume-nav-drawer"
612
610
  class:list={[
613
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",
614
612
  // A "bare" landing (the changelog index) has no sidebar column on
@@ -634,8 +632,7 @@ createIconSprite(Astro.locals);
634
632
  <a
635
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"
636
634
  href={withBase(link.href)}
637
- rel={external ? "noreferrer" : undefined}
638
- target={external ? "_blank" : undefined}
635
+ {...newTabAttrs(link.href)}
639
636
  >
640
637
  <span class="flex items-center gap-2">
641
638
  {link.icon && (
@@ -745,7 +742,18 @@ createIconSprite(Astro.locals);
745
742
  )
746
743
  }
747
744
  <PageHeaderSlot headings={tocHeadings} page={page} route={page.route} />
748
- <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
+ >
749
757
  <slot />
750
758
  </article>
751
759
  {
@@ -814,6 +822,7 @@ createIconSprite(Astro.locals);
814
822
  type="button"
815
823
  ></button>
816
824
  <script>
825
+ import type { Zoom } from "medium-zoom";
817
826
  import { copyText, createCopyFlash } from "../copy-feedback.ts";
818
827
  import { syncDrawerInert } from "./drawer-inert.ts";
819
828
  import { chromeIcons as icons } from "../../theme/chrome-icons.ts";
@@ -915,6 +924,13 @@ createIconSprite(Astro.locals);
915
924
  // client-router swap (which replaces the body with server-rendered
916
925
  // markup that has none of this applied). Everything in here binds to
917
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
+
918
934
  const initPage = async () => {
919
935
  // Localized copy-button labels, stamped on <body> by the layout markup
920
936
  // (the Search.astro data-attribute channel) since this bundled script
@@ -1033,6 +1049,15 @@ createIconSprite(Astro.locals);
1033
1049
  // via medium-zoom: ESC/scroll/click dismissal, natural-size capping,
1034
1050
  // and the open/close transition races are its problem, not ours.
1035
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();
1036
1061
  if (document.body.hasAttribute("data-blume-image-zoom")) {
1037
1062
  const zoomTargets = Array.from(
1038
1063
  document.querySelectorAll<HTMLImageElement>(
@@ -1048,11 +1073,12 @@ createIconSprite(Astro.locals);
1048
1073
  const { default: mediumZoom } = await import(
1049
1074
  "medium-zoom/dist/pure"
1050
1075
  );
1051
- mediumZoom(zoomTargets, {
1076
+ zoom ??= mediumZoom({
1052
1077
  background:
1053
1078
  "color-mix(in oklab, var(--color-background) 80%, transparent)",
1054
1079
  margin: 24,
1055
1080
  });
1081
+ zoom.attach(...zoomTargets);
1056
1082
  }
1057
1083
  }
1058
1084
  };