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
@@ -1,23 +1,42 @@
1
1
  /**
2
2
  * Send a custom analytics event to every analytics platform configured in
3
- * `blume.config.ts`. Mirrors the providers wired by `Analytics.astro`: Vercel
4
- * Web Analytics and PostHog are first-class (Cloudflare Web Analytics is too,
5
- * but has no custom-event API to forward to); any other provider added through
6
- * `analytics.scripts` is reached via best-effort global detection or the
7
- * `blume:track` CustomEvent, which fires unconditionally so a project can bridge
8
- * the event to anything. Every call no-ops cleanly when a provider isn't present
9
- * — for example during `blume dev`, where `Analytics.astro` injects nothing —
10
- * and a provider that throws (a consent shim that stubs `gtag` with a raise, a
11
- * broken snippet) is isolated so it neither starves the providers after it nor
12
- * surfaces in the feature that reported the event.
3
+ * `blume.config.ts`. Mirrors the adapters `Analytics.astro` wires
4
+ * (`src/analytics/*`): each one with a custom-event API is called through the
5
+ * global its snippet defines (Cloudflare and Clearbit have none to forward
6
+ * to), and the `blume:track` CustomEvent fires unconditionally so a project
7
+ * can bridge the event to anything else — a `script()` adapter, say. Every
8
+ * call no-ops cleanly when a provider isn't present — for example during
9
+ * `blume dev`, where `Analytics.astro` injects nothing — and a provider that
10
+ * throws (a consent shim that stubs `gtag` with a raise, a broken snippet) is
11
+ * isolated so it neither starves the providers after it nor surfaces in the
12
+ * feature that reported the event.
13
13
  */
14
14
  import { track as vercelTrack } from "@vercel/analytics";
15
15
 
16
16
  /** Flat, serializable event properties. */
17
17
  export type TrackProps = Record<string, boolean | number | string>;
18
18
 
19
+ /** A Tag Manager data layer: a queue the container drains. */
20
+ interface DataLayer {
21
+ push: (entry: TrackProps & { event: string }) => number;
22
+ }
23
+
19
24
  interface AnalyticsWindow {
25
+ /** The data layer a `googleTagManager()` snippet's container reads. */
26
+ __blumeGtmLayer?: DataLayer;
27
+ _satellite?: { track?: (event: string, props?: TrackProps) => void };
28
+ amplitude?: { track?: (event: string, props?: TrackProps) => void };
29
+ analytics?: { track?: (event: string, props?: TrackProps) => void };
30
+ clarity?: (command: "event", event: string) => void;
31
+ dataLayer?: DataLayer;
32
+ fathom?: { trackEvent?: (event: string) => void };
20
33
  gtag?: (command: "event", event: string, props?: TrackProps) => void;
34
+ heap?: { track?: (event: string, props?: TrackProps) => void };
35
+ hj?: (command: "event", event: string) => void;
36
+ htevents?: { track?: (event: string, props?: TrackProps) => void };
37
+ LogRocket?: { track?: (event: string, props?: TrackProps) => void };
38
+ mixpanel?: { track?: (event: string, props?: TrackProps) => void };
39
+ pirsch?: (event: string, options?: { meta?: TrackProps }) => void;
21
40
  plausible?: (event: string, options?: { props?: TrackProps }) => void;
22
41
  posthog?: { capture?: (event: string, props?: TrackProps) => void };
23
42
  }
@@ -31,6 +50,47 @@ const attempt = (send: () => void): void => {
31
50
  }
32
51
  };
33
52
 
53
+ // The SDKs whose loader stub queues calls until the library lands, so the
54
+ // method is there to call from the first paint on.
55
+ const trackQueued = (
56
+ w: AnalyticsWindow,
57
+ event: string,
58
+ props: TrackProps
59
+ ): void => {
60
+ attempt(() => w.posthog?.capture?.(event, props));
61
+ attempt(() => w.mixpanel?.track?.(event, props));
62
+ attempt(() => w.heap?.track?.(event, props));
63
+ attempt(() => w.analytics?.track?.(event, props));
64
+ attempt(() => w.htevents?.track?.(event, props));
65
+ attempt(() => w.amplitude?.track?.(event, props));
66
+ attempt(() => w.LogRocket?.track?.(event, props));
67
+ attempt(() => w._satellite?.track?.(event, props));
68
+ };
69
+
70
+ // The providers reached through a global function or a data layer rather
71
+ // than a queued SDK object.
72
+ const trackGlobals = (
73
+ w: AnalyticsWindow,
74
+ event: string,
75
+ props: TrackProps
76
+ ): void => {
77
+ // Google: `gtag('event')` for the Google tag, and a plain `{ event }` push
78
+ // for a Tag Manager custom-event trigger, into the container's own data
79
+ // layer. Each reads only its own shape, so a site with both configured
80
+ // still sees the event once per product.
81
+ attempt(() => w.gtag?.("event", event, props));
82
+ attempt(() => (w.__blumeGtmLayer ?? w.dataLayer)?.push({ ...props, event }));
83
+ // Privacy-first counters take a name, some with properties.
84
+ attempt(() => w.plausible?.(event, { props }));
85
+ attempt(() => w.fathom?.trackEvent?.(event));
86
+ // Pirsch stringifies `meta` values in place, so it gets its own copy and
87
+ // the `blume:track` listeners still see the original types.
88
+ attempt(() => w.pirsch?.(event, { meta: { ...props } }));
89
+ // Behavior tools take a bare name.
90
+ attempt(() => w.clarity?.("event", event));
91
+ attempt(() => w.hj?.("event", event));
92
+ };
93
+
34
94
  /**
35
95
  * @param event The event name.
36
96
  * @param props Properties every provider receives.
@@ -55,11 +115,8 @@ export const track = (
55
115
 
56
116
  // Vercel Web Analytics — self-gates to a no-op until `window.va` is set up.
57
117
  attempt(() => vercelTrack(event, props));
58
- // PostHog — the injected array.js stub queues calls until the lib loads.
59
- attempt(() => w.posthog?.capture?.(event, props));
60
- // Popular providers wired through `analytics.scripts` (GA4/GTM, Plausible).
61
- attempt(() => w.gtag?.("event", event, props));
62
- attempt(() => w.plausible?.(event, { props }));
118
+ trackQueued(w, event, props);
119
+ trackGlobals(w, event, props);
63
120
  // Universal hook for any other integration.
64
121
  attempt(() =>
65
122
  w.dispatchEvent(
@@ -1,36 +1,134 @@
1
1
  /**
2
- * Keep the mobile nav drawer out of the tab order while it is closed. The
3
- * closed drawer is only translated off-canvas, so its links would otherwise
4
- * stay focusable on every page. Mirrors the header's `data-blume-nav-open`
5
- * toggle into `inert`/`aria-hidden` — but only below `lg` (64rem), where the
6
- * same element isn't the static sidebar (RootLayout) or is display-hidden
7
- * anyway (PageLayout). Shared by both layouts' bundled scripts, which run once
8
- * per real page load; the drawer element is rebuilt by every client-router
9
- * swap, so `sync` re-queries it each time and re-runs on `astro:after-swap`.
2
+ * The mobile nav drawer's accessibility. The header's click script opens and
3
+ * closes the drawer by toggling `data-blume-nav-open` on `<html>`; everything
4
+ * here follows that attribute, only below `lg` (64rem) — at `lg` and up the same
5
+ * element is the static sidebar (RootLayout) or display-hidden (PageLayout):
6
+ *
7
+ * - Closed, the drawer is only translated off-canvas, so its links would stay
8
+ * focusable on every page: it's `inert`/`aria-hidden` instead.
9
+ * - Open, it's a modal panel over the page: the page behind it (everything but
10
+ * the header, which holds the toggle, and the overlay button) goes `inert`,
11
+ * so Tab can't wander into content the overlay covers, and the header's
12
+ * toggle reports `aria-expanded`.
13
+ * - Escape closes it, and closing it with focus inside (or on the overlay's
14
+ * close button, which disappears) returns focus to the toggle.
15
+ *
16
+ * Shared by both layouts' bundled scripts, which run once per real page load;
17
+ * the drawer and the page behind it are rebuilt by every client-router swap, so
18
+ * `sync` re-queries them each time and re-runs on `astro:after-swap`.
10
19
  */
20
+
21
+ const OPEN_ATTRIBUTE = "data-blume-nav-open";
22
+
23
+ /**
24
+ * Marks what `sync` made inert behind the open drawer, so closing it restores
25
+ * only those — never an element something else made inert.
26
+ */
27
+ const BACKDROP_ATTRIBUTE = "data-blume-drawer-backdrop";
28
+
29
+ /** What stays live beside the open drawer: the header and the overlay. */
30
+ const KEEP_LIVE =
31
+ "[data-blume-header], [data-blume-nav-toggle], script, style, template";
32
+
33
+ const drawerElement = (): HTMLElement | null =>
34
+ document.querySelector<HTMLElement>("[data-blume-nav-drawer]");
35
+
36
+ const headerToggle = (): HTMLElement | null =>
37
+ document.querySelector<HTMLElement>(
38
+ "[data-blume-header] [data-blume-nav-toggle]"
39
+ );
40
+
41
+ /**
42
+ * The page behind the drawer: every sibling along the drawer's path up to
43
+ * `<body>`, minus what stays live.
44
+ */
45
+ const backdropOf = (drawer: Element): Element[] => {
46
+ const backdrop: Element[] = [];
47
+ let node = drawer;
48
+ while (node !== document.body && node.parentElement) {
49
+ for (const sibling of node.parentElement.children) {
50
+ if (sibling !== node && !sibling.matches(KEEP_LIVE)) {
51
+ backdrop.push(sibling);
52
+ }
53
+ }
54
+ node = node.parentElement;
55
+ }
56
+ return backdrop;
57
+ };
58
+
59
+ const restoreBackdrop = (): void => {
60
+ for (const element of document.querySelectorAll(`[${BACKDROP_ATTRIBUTE}]`)) {
61
+ element.removeAttribute("inert");
62
+ element.removeAttribute(BACKDROP_ATTRIBUTE);
63
+ }
64
+ };
65
+
66
+ const inertBackdrop = (drawer: Element): void => {
67
+ for (const element of backdropOf(drawer)) {
68
+ // Already inert for another reason: leave it, and leave it unmarked.
69
+ if (!element.hasAttribute("inert")) {
70
+ element.setAttribute("inert", "");
71
+ element.setAttribute(BACKDROP_ATTRIBUTE, "");
72
+ }
73
+ }
74
+ };
75
+
11
76
  export const syncDrawerInert = (): void => {
12
77
  const desktop = window.matchMedia("(min-width: 64rem)");
78
+ const isOpen = () =>
79
+ !desktop.matches && document.documentElement.hasAttribute(OPEN_ATTRIBUTE);
80
+ let wasOpen = false;
81
+
13
82
  const sync = () => {
14
- const drawer = document.querySelector<HTMLElement>(
15
- "[data-blume-nav-drawer]"
16
- );
83
+ const open = isOpen();
84
+ headerToggle()?.setAttribute("aria-expanded", open ? "true" : "false");
85
+ const drawer = drawerElement();
17
86
  if (!drawer) {
87
+ restoreBackdrop();
88
+ wasOpen = open;
18
89
  return;
19
90
  }
20
- const hidden =
21
- !desktop.matches &&
22
- !Object.hasOwn(document.documentElement.dataset, "blumeNavOpen");
91
+ const hidden = !(desktop.matches || open);
23
92
  drawer.inert = hidden;
24
93
  if (hidden) {
25
94
  drawer.setAttribute("aria-hidden", "true");
26
95
  } else {
27
96
  drawer.removeAttribute("aria-hidden");
28
97
  }
98
+ if (open) {
99
+ inertBackdrop(drawer);
100
+ } else {
101
+ restoreBackdrop();
102
+ const active = document.activeElement;
103
+ if (
104
+ wasOpen &&
105
+ !desktop.matches &&
106
+ active &&
107
+ (drawer.contains(active) || active.matches("[data-blume-nav-toggle]"))
108
+ ) {
109
+ headerToggle()?.focus();
110
+ }
111
+ }
112
+ wasOpen = open;
29
113
  };
114
+
30
115
  sync();
31
116
  desktop.addEventListener("change", sync);
32
117
  new MutationObserver(sync).observe(document.documentElement, {
33
- attributeFilter: ["data-blume-nav-open"],
118
+ attributeFilter: [OPEN_ATTRIBUTE],
34
119
  });
35
120
  document.addEventListener("astro:after-swap", sync);
121
+ document.addEventListener("keydown", (event) => {
122
+ // An open dialog (search, Ask AI) closes itself on Escape first.
123
+ if (
124
+ event.key !== "Escape" ||
125
+ !isOpen() ||
126
+ document.querySelector("dialog[open]")
127
+ ) {
128
+ return;
129
+ }
130
+ document.documentElement.removeAttribute(OPEN_ATTRIBUTE);
131
+ sync();
132
+ headerToggle()?.focus();
133
+ });
36
134
  };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Keep a header selector's dropdown panel inside the viewport. The panel is
3
+ * absolutely positioned against its trigger (`end-0` for the version
4
+ * selector), which is right on desktop, where the trigger sits at the header's
5
+ * trailing edge — but on a phone the trigger lands mid-header, so a 14rem
6
+ * panel anchored to its end runs off the leading edge (and, mirrored, off the
7
+ * trailing edge in a right-to-left locale). When a panel opens, it is measured
8
+ * and nudged back inside with `translate`, which leaves the anchoring classes
9
+ * — and the desktop layout — untouched.
10
+ *
11
+ * One capture-phase `toggle` listener on `document` (the event doesn't bubble)
12
+ * serves every opted-in dropdown, installed once per real page load: the
13
+ * client router rebuilds the header markup on each swap, and delegation needs
14
+ * no re-binding.
15
+ */
16
+
17
+ /** Gap kept between a clamped panel and the viewport edge, in pixels. */
18
+ const MARGIN = 8;
19
+
20
+ /** The panel an opted-in `<details>` dropdown clamps. */
21
+ export const CLAMPED_PANEL = "[data-blume-dropdown-panel]";
22
+
23
+ /**
24
+ * The horizontal shift that brings a panel spanning `left`…`right` inside a
25
+ * viewport `width` wide, keeping `margin` at each edge. A panel wider than the
26
+ * room available pins its leading edge (the one reading starts from) to the
27
+ * margin.
28
+ */
29
+ export const clampShift = (
30
+ left: number,
31
+ right: number,
32
+ width: number,
33
+ rtl: boolean,
34
+ margin = MARGIN
35
+ ): number => {
36
+ const tooWide = right - left > width - 2 * margin;
37
+ if (tooWide) {
38
+ return rtl ? width - margin - right : margin - left;
39
+ }
40
+ if (left < margin) {
41
+ return margin - left;
42
+ }
43
+ if (right > width - margin) {
44
+ return width - margin - right;
45
+ }
46
+ return 0;
47
+ };
48
+
49
+ /** The slice of a panel element the clamp measures and moves. */
50
+ export interface PanelBox {
51
+ getBoundingClientRect: () => { left: number; right: number };
52
+ style: { translate: string };
53
+ }
54
+
55
+ /** Measure `panel` in place and shift it back inside the viewport. */
56
+ export const clampPanel = (panel: PanelBox): void => {
57
+ panel.style.translate = "";
58
+ const { left, right } = panel.getBoundingClientRect();
59
+ const root = document.documentElement;
60
+ const shift = clampShift(
61
+ left,
62
+ right,
63
+ root.clientWidth,
64
+ root.getAttribute("dir") === "rtl"
65
+ );
66
+ if (shift !== 0) {
67
+ panel.style.translate = `${shift}px 0`;
68
+ }
69
+ };
70
+
71
+ /** The clamped panel of the dropdown a `toggle` event opened, if any. */
72
+ const openedPanel = (target: EventTarget | null): HTMLElement | null =>
73
+ target instanceof HTMLDetailsElement && target.open
74
+ ? target.querySelector<HTMLElement>(CLAMPED_PANEL)
75
+ : null;
76
+
77
+ /** Clamp every open panel again, after the viewport changed size. */
78
+ const reclampOpen = (): void => {
79
+ for (const panel of document.querySelectorAll<HTMLElement>(
80
+ `details[open] > ${CLAMPED_PANEL}`
81
+ )) {
82
+ clampPanel(panel);
83
+ }
84
+ };
85
+
86
+ let installed = false;
87
+
88
+ /** Clamp opted-in dropdown panels on open (and on resize while open). */
89
+ export const installDropdownClamp = (): void => {
90
+ if (installed) {
91
+ return;
92
+ }
93
+ installed = true;
94
+ document.addEventListener(
95
+ "toggle",
96
+ (event) => {
97
+ const panel = openedPanel(event.target);
98
+ if (panel) {
99
+ clampPanel(panel);
100
+ }
101
+ },
102
+ true
103
+ );
104
+ window.addEventListener("resize", reclampOpen);
105
+ };
@@ -39,9 +39,13 @@
39
39
  * the `after-swap` re-apply stays as the fallback that also picks up a
40
40
  * preference changed in another tab.
41
41
  *
42
- * Reads `data-mode` — `"system" | "light" | "dark"`.
42
+ * Reads `data-mode` — `"system" | "light" | "dark"`. Blocked storage (Safari
43
+ * "Block All Cookies", sandboxed iframes) throws on read. That reads as no
44
+ * stored preference instead of aborting before the swap listeners exist, and
45
+ * after a swap the theme the `before-swap` stamp carried over stands, so a
46
+ * toggle the reader made still holds for the rest of their visit.
43
47
  */
44
- export const THEME_INIT_SCRIPT = `(()=>{const m=document.currentScript?.dataset.mode??"system";const apply=()=>{const s=localStorage.getItem("blume-theme");const sys=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";document.documentElement.dataset.theme=s??(m==="system"?sys:m);};apply();document.addEventListener("astro:before-swap",(e)=>{const t=document.documentElement.dataset.theme;if(t){e.newDocument.documentElement.dataset.theme=t;}});document.addEventListener("astro:after-swap",apply);})();`;
48
+ export const THEME_INIT_SCRIPT = `(()=>{const m=document.currentScript?.dataset.mode??"system";const apply=()=>{const r=document.documentElement;let s=null;let blocked=false;try{s=localStorage.getItem("blume-theme");}catch{blocked=true;}const sys=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";r.dataset.theme=s??(blocked&&r.dataset.theme?r.dataset.theme:m==="system"?sys:m);};apply();document.addEventListener("astro:before-swap",(e)=>{const t=document.documentElement.dataset.theme;if(t){e.newDocument.documentElement.dataset.theme=t;}});document.addEventListener("astro:after-swap",apply);})();`;
45
49
 
46
50
  /**
47
51
  * Keep an embedded Scalar reference on Blume's theme. Scalar picks its own
@@ -63,12 +67,17 @@ export const THEME_INIT_SCRIPT = `(()=>{const m=document.currentScript?.dataset.
63
67
  export const SCALAR_THEME_INIT_SCRIPT = `(()=>{const d=document.documentElement;const mode=()=>d.dataset.theme==="dark"?"dark":"light";let pinned=0;for(const el of document.querySelectorAll("[data-scalar-client]")){let c;try{c=JSON.parse(el.dataset.configuration||"{}");}catch{continue;}if("forceDarkModeState" in c||"darkMode" in c){continue;}c.forceDarkModeState=mode();el.dataset.configuration=JSON.stringify(c);pinned+=1;}if(!pinned){return;}new MutationObserver(()=>{const dark=mode()==="dark";document.body.classList.toggle("dark-mode",dark);document.body.classList.toggle("light-mode",!dark);}).observe(d,{attributes:true,attributeFilter:["data-theme"]});})();`;
64
68
 
65
69
  /**
66
- * Hide a previously-dismissed banner before it can flash in — and again after
67
- * every client-router swap, which wipes the `<html>` marker attribute.
70
+ * Hide a previously-dismissed banner before it can flash in — and keep it
71
+ * hidden across client-router swaps, which replace the `<html>` attributes with
72
+ * the incoming page's: `astro:before-swap` carries the marker onto the incoming
73
+ * root (as the theme script does with `data-theme`), and `after-swap` re-reads
74
+ * storage as the fallback.
68
75
  *
69
- * Reads `data-key` — the banner's dismissal key.
76
+ * Reads `data-key` — the banner's dismissal key. A storage read that throws
77
+ * (blocked storage) reads as not dismissed; a dismissal made on this visit
78
+ * still holds across its swaps through the carried-over marker.
70
79
  */
71
- export const BANNER_INIT_SCRIPT = `(()=>{const k=document.currentScript?.dataset.key;if(!k){return;}const apply=()=>{if(localStorage.getItem("blume-banner:"+k)){document.documentElement.setAttribute("data-blume-banner-hidden","");}};apply();document.addEventListener("astro:after-swap",apply);})();`;
80
+ export const BANNER_INIT_SCRIPT = `(()=>{const k=document.currentScript?.dataset.key;if(!k){return;}const apply=()=>{let hidden=null;try{hidden=localStorage.getItem("blume-banner:"+k);}catch{}if(hidden){document.documentElement.setAttribute("data-blume-banner-hidden","");}};apply();document.addEventListener("astro:before-swap",(e)=>{if(document.documentElement.hasAttribute("data-blume-banner-hidden")){e.newDocument.documentElement.setAttribute("data-blume-banner-hidden","");}});document.addEventListener("astro:after-swap",apply);})();`;
72
81
 
73
82
  /**
74
83
  * Keep the page styled across client-router swaps. Astro hoists the CSS of a
@@ -14,6 +14,23 @@ export interface Crumb {
14
14
  route?: string;
15
15
  }
16
16
 
17
+ /**
18
+ * Whether a group's header row reads as the current page. A routed group links
19
+ * to its folder's index page; when that index is also listed as one of the
20
+ * group's own rows (kept under a different label), the row is the more
21
+ * specific indicator, so the header stays quiet rather than lighting up beside
22
+ * it. With the index row hidden the header is the only link and takes the
23
+ * highlight.
24
+ */
25
+ export const isGroupRowCurrent = (
26
+ group: Extract<NavNode, { kind: "group" }>,
27
+ currentRoute: string
28
+ ): boolean =>
29
+ group.route === currentRoute &&
30
+ !group.children.some(
31
+ (child) => child.kind === "page" && child.route === currentRoute
32
+ );
33
+
17
34
  /** Flatten the sidebar tree into ordered internal page links. */
18
35
  export const flattenPages = (nodes: NavNode[]): FlatPage[] => {
19
36
  const out: FlatPage[] = [];
@@ -1,5 +1,6 @@
1
1
  import { liteClient } from "algoliasearch/lite";
2
2
 
3
+ import type { AlgoliaOptions } from "../../../search/adapters/algolia.ts";
3
4
  import { excerptFor, highlight, SEARCH_LIMIT } from "./types.ts";
4
5
  import type { SearchFn } from "./types.ts";
5
6
 
@@ -13,20 +14,19 @@ interface AlgoliaRecord {
13
14
 
14
15
  /**
15
16
  * Algolia: the browser queries the index directly with the public,
16
- * search-only key. Records are uploaded at build time by the sync step.
17
+ * search-only key. Records are uploaded at build time by the sync step. Every
18
+ * adapter option Blume doesn't read (`hosts`, `timeouts`, `baseHeaders`, …) is
19
+ * the site's own client option and goes to `liteClient` untouched.
17
20
  */
18
- export const createSearch = (opts: {
19
- appId: string;
20
- indexName: string;
21
- searchApiKey: string;
22
- }): SearchFn => {
23
- const client = liteClient(opts.appId, opts.searchApiKey);
21
+ export const createSearch = (opts: AlgoliaOptions): SearchFn => {
22
+ const { apiKey, appId, indexName, ...clientOptions } = opts;
23
+ const client = liteClient(appId, apiKey, clientOptions);
24
24
  return async (query, options) => {
25
25
  const { results } = await client.search<AlgoliaRecord>({
26
26
  requests: [
27
27
  {
28
28
  hitsPerPage: SEARCH_LIMIT,
29
- indexName: opts.indexName,
29
+ indexName,
30
30
  query,
31
31
  // The sync uploads `locale` and `version` on every record so a
32
32
  // site can scope hosted results to the active language and the
@@ -1,5 +1,6 @@
1
1
  import { OramaClient } from "@oramacloud/client";
2
2
 
3
+ import type { OramaCloudOptions } from "../../../search/adapters/orama-cloud.ts";
3
4
  import { excerptFor, highlight, SEARCH_LIMIT } from "./types.ts";
4
5
  import type { SearchFn } from "./types.ts";
5
6
 
@@ -12,15 +13,16 @@ interface OramaCloudRecord {
12
13
 
13
14
  /**
14
15
  * Orama Cloud: the browser queries the hosted index directly with the public
15
- * endpoint + API key. Records are pushed at build time by the sync step.
16
+ * endpoint + API key. Records are pushed at build time by the sync step. Every
17
+ * adapter option Blume doesn't read is the site's own client option and goes
18
+ * to `OramaClient` untouched; `indexId` belongs to the sync and stays out.
16
19
  */
17
- export const createSearch = (opts: {
18
- endpoint: string;
19
- apiKey: string;
20
- }): SearchFn => {
20
+ export const createSearch = (opts: OramaCloudOptions): SearchFn => {
21
+ const { apiKey, endpoint, indexId: _syncOnly, ...clientOptions } = opts;
21
22
  const client = new OramaClient({
22
- api_key: opts.apiKey,
23
- endpoint: opts.endpoint,
23
+ ...clientOptions,
24
+ api_key: apiKey,
25
+ endpoint,
24
26
  });
25
27
  return async (query, options) => {
26
28
  const results = await client.search({
@@ -1,5 +1,6 @@
1
1
  import { Client } from "typesense";
2
2
 
3
+ import type { TypesenseOptions } from "../../../search/adapters/typesense.ts";
3
4
  import { excerptFor, highlight, SEARCH_LIMIT } from "./types.ts";
4
5
  import type { SearchFn } from "./types.ts";
5
6
 
@@ -13,28 +14,21 @@ interface TypesenseRecord extends Record<string, unknown> {
13
14
 
14
15
  /**
15
16
  * Typesense: the browser queries the collection directly with the search-only
16
- * key. Documents are imported at build time by the sync step.
17
+ * key. Documents are imported at build time by the sync step. Every adapter
18
+ * option Blume doesn't read (`connectionTimeoutSeconds`, `numRetries`, …) is
19
+ * the site's own client option and goes to the `Client` untouched; the named
20
+ * connection options still decide `apiKey` and `nodes`.
17
21
  */
18
- export const createSearch = (opts: {
19
- collection: string;
20
- host: string;
21
- port?: number;
22
- protocol?: string;
23
- searchApiKey: string;
24
- }): SearchFn => {
22
+ export const createSearch = (opts: TypesenseOptions): SearchFn => {
23
+ const { apiKey, collection, host, port, protocol, ...clientOptions } = opts;
25
24
  const client = new Client({
26
- apiKey: opts.searchApiKey,
27
- nodes: [
28
- {
29
- host: opts.host,
30
- port: opts.port ?? 443,
31
- protocol: opts.protocol ?? "https",
32
- },
33
- ],
25
+ ...clientOptions,
26
+ apiKey,
27
+ nodes: [{ host, port: port ?? 443, protocol: protocol ?? "https" }],
34
28
  });
35
29
  return async (query, options) => {
36
30
  const response = await client
37
- .collections<TypesenseRecord>(opts.collection)
31
+ .collections<TypesenseRecord>(collection)
38
32
  .documents()
39
33
  .search(
40
34
  {
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { randomUUID } from "node:crypto";
2
+ import { stableId } from "../content/tooltip-id.ts";
3
3
 
4
4
  import type { MessageModel } from "./message.ts";
5
5
 
@@ -28,7 +28,7 @@ const modelJson = JSON.stringify(model).replaceAll("<", "\\u003c");
28
28
 
29
29
  // Unique per rendered composer (a page can hold several), so the textarea's
30
30
  // aria-describedby can point at ITS error container and no other.
31
- const payloadErrorsId = `blume-payload-errors-${randomUUID()}`;
31
+ const payloadErrorsId = stableId(Astro.locals, "blume-payload-errors", [modelJson]);
32
32
 
33
33
  const LABEL_ROW =
34
34
  "mb-1 flex flex-wrap items-baseline gap-x-1.5 font-medium text-foreground text-xs";
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { randomUUID } from "node:crypto";
2
+ import { stableId } from "../content/tooltip-id.ts";
3
3
 
4
4
  import { paramKey, type PlaygroundModel } from "./request.ts";
5
5
 
@@ -30,7 +30,7 @@ const modelJson = JSON.stringify(model).replaceAll("<", "\\u003c");
30
30
 
31
31
  // Unique per rendered panel (a page can hold several), so the textarea's
32
32
  // aria-describedby can point at ITS error container and no other.
33
- const bodyErrorsId = `blume-body-errors-${randomUUID()}`;
33
+ const bodyErrorsId = stableId(Astro.locals, "blume-body-errors", [slug, operation]);
34
34
 
35
35
  const LABEL_ROW =
36
36
  "mb-1 flex flex-wrap items-baseline gap-x-1.5 font-medium text-foreground text-xs";