blume 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/cli/{chunk-vtk4a6dg.js → chunk-00gs3wqs.js} +1 -1
  3. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  4. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  5. package/dist/cli/{chunk-yw7dm696.js → chunk-3yce002v.js} +21 -8
  6. package/dist/cli/{chunk-yw7dm696.js.map → chunk-3yce002v.js.map} +3 -3
  7. package/dist/cli/{chunk-yt5n7ppj.js → chunk-4e9b9ra6.js} +17 -7
  8. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  9. package/dist/cli/{chunk-fs23ddbb.js → chunk-5r8g91qn.js} +352 -676
  10. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  11. package/dist/cli/{chunk-6vm74dry.js → chunk-6dtt0zfn.js} +8 -8
  12. package/dist/cli/{chunk-qwsrynx5.js → chunk-6k4ftwze.js} +38 -19
  13. package/dist/cli/{chunk-qwsrynx5.js.map → chunk-6k4ftwze.js.map} +4 -4
  14. package/dist/cli/{chunk-qs4q5p4e.js → chunk-7mbqtmgb.js} +16 -7
  15. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  16. package/dist/cli/{chunk-q5163e60.js → chunk-7vtckvaw.js} +21 -19
  17. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  18. package/dist/cli/{chunk-kdp5q7ke.js → chunk-8g8ytmgx.js} +17 -18
  19. package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-8g8ytmgx.js.map} +2 -2
  20. package/dist/cli/{chunk-epjnccmv.js → chunk-91ws1n6j.js} +18 -15
  21. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  22. package/dist/cli/{chunk-fxypxtvm.js → chunk-bbnwccaz.js} +2 -2
  23. package/dist/cli/{chunk-m3vmjgmq.js → chunk-bfwp9vp6.js} +16 -8
  24. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  25. package/dist/cli/{chunk-f2z5v128.js → chunk-d1v5rhy0.js} +14 -15
  26. package/dist/cli/{chunk-f2z5v128.js.map → chunk-d1v5rhy0.js.map} +2 -2
  27. package/dist/cli/{chunk-6crbhc3x.js → chunk-ddndchfr.js} +21 -6
  28. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  29. package/dist/cli/{chunk-ce574jw2.js → chunk-esh98wmb.js} +1 -1
  30. package/dist/cli/{chunk-zxcczpyx.js → chunk-fsmrqk8a.js} +1 -1
  31. package/dist/cli/{chunk-hdpx1tax.js → chunk-g698a744.js} +5 -5
  32. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  33. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  34. package/dist/cli/{chunk-zxh4d9vy.js → chunk-h2ez8dzb.js} +4 -4
  35. package/dist/cli/{chunk-5shv93fd.js → chunk-h7k3nq3v.js} +2 -2
  36. package/dist/cli/{chunk-79jhk4py.js → chunk-hqp2ajnh.js} +251 -122
  37. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  38. package/dist/cli/{chunk-2hn4b8z7.js → chunk-hr8ne106.js} +109 -40
  39. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  40. package/dist/cli/{chunk-6hsn950k.js → chunk-j85scx15.js} +62 -18
  41. package/dist/cli/chunk-j85scx15.js.map +10 -0
  42. package/dist/cli/{chunk-s1p84fyh.js → chunk-k7pj68a8.js} +63 -22
  43. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  44. package/dist/cli/{chunk-ah61y8py.js → chunk-mqc662a6.js} +2 -2
  45. package/dist/cli/{chunk-ch6g3ar0.js → chunk-n1yg3tj3.js} +4 -4
  46. package/dist/cli/{chunk-mb2919y2.js → chunk-nfcyttvj.js} +17 -6
  47. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  48. package/dist/cli/{chunk-kpf8rrjc.js → chunk-pbg5a4s3.js} +56 -24
  49. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  50. package/dist/cli/{chunk-jwyddg7y.js → chunk-ppzjqwx2.js} +21 -14
  51. package/dist/cli/{chunk-jwyddg7y.js.map → chunk-ppzjqwx2.js.map} +4 -4
  52. package/dist/cli/{chunk-dh8cwk36.js → chunk-qkb5a8sa.js} +24 -9
  53. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  54. package/dist/cli/{chunk-wm7js3j9.js → chunk-sqw4ekg1.js} +2 -2
  55. package/dist/cli/{chunk-qkqwkpte.js → chunk-v6ya5kcb.js} +3083 -953
  56. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  57. package/dist/cli/{chunk-fz5wtpmh.js → chunk-w4bxdvsa.js} +18 -15
  58. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  59. package/dist/cli/{chunk-27g6wdth.js → chunk-wdrt2k2v.js} +2 -2
  60. package/dist/cli/{chunk-wgm7m9qk.js → chunk-xh43dwgw.js} +158 -43
  61. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  62. package/dist/cli/{chunk-s6jhgk0q.js → chunk-zp79m0ts.js} +2 -2
  63. package/dist/cli/index.js +160 -35
  64. package/dist/cli/index.js.map +4 -4
  65. package/dist/types/ai/agent-surface.d.ts +32 -0
  66. package/dist/types/ai/api-catalog.d.ts +7 -1
  67. package/dist/types/ai/ask-context.d.ts +7 -0
  68. package/dist/types/ai/component-markdown.d.ts +4 -4
  69. package/dist/types/ai/relative-links.d.ts +9 -4
  70. package/dist/types/ai/static-expression.d.ts +28 -0
  71. package/dist/types/analytics/databuddy.d.ts +43 -0
  72. package/dist/types/analytics/index.d.ts +2 -0
  73. package/dist/types/analytics/schema.d.ts +14 -0
  74. package/dist/types/cli/env.d.ts +5 -0
  75. package/dist/types/cli/init/scaffold.d.ts +19 -3
  76. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  77. package/dist/types/core/base-path.d.ts +9 -0
  78. package/dist/types/core/config-input.d.ts +4 -4
  79. package/dist/types/core/config.d.ts +2 -2
  80. package/dist/types/core/graph.d.ts +2 -0
  81. package/dist/types/core/i18n-ui.d.ts +31 -0
  82. package/dist/types/core/i18n.d.ts +7 -1
  83. package/dist/types/core/links.d.ts +3 -1
  84. package/dist/types/core/load-module.d.ts +10 -0
  85. package/dist/types/core/locale-links.d.ts +12 -2
  86. package/dist/types/core/meta.d.ts +5 -1
  87. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  88. package/dist/types/core/navigation.d.ts +38 -0
  89. package/dist/types/core/ordering-prefix.d.ts +4 -0
  90. package/dist/types/core/safe-links.d.ts +3 -1
  91. package/dist/types/core/schema.d.ts +52 -13
  92. package/dist/types/core/sources/github-releases.d.ts +5 -0
  93. package/dist/types/core/sources/lower.d.ts +14 -2
  94. package/dist/types/core/sources/normalize.d.ts +10 -1
  95. package/dist/types/core/sources/remote.d.ts +11 -1
  96. package/dist/types/core/sources/resolve.d.ts +12 -0
  97. package/dist/types/core/sources/types.d.ts +31 -0
  98. package/dist/types/core/types.d.ts +9 -0
  99. package/dist/types/deploy/adapters/node.d.ts +5 -2
  100. package/dist/types/deploy/adapters/types.d.ts +7 -0
  101. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  102. package/dist/types/deploy/headers.d.ts +31 -7
  103. package/dist/types/deploy/node-headers.d.ts +43 -8
  104. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  105. package/dist/types/deploy/platforms/node.d.ts +6 -5
  106. package/dist/types/deploy/platforms/types.d.ts +7 -0
  107. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  108. package/dist/types/deploy/redirects.d.ts +7 -2
  109. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  110. package/dist/types/openapi/model.d.ts +20 -6
  111. package/dist/types/search/sync/algolia.d.ts +3 -1
  112. package/docs/01-quickstart.mdx +3 -2
  113. package/docs/02-deployment.mdx +17 -8
  114. package/docs/08-faq.mdx +9 -2
  115. package/docs/advanced/changelog.mdx +1 -1
  116. package/docs/advanced/custom-pages.mdx +4 -2
  117. package/docs/cli/audit.mdx +2 -2
  118. package/docs/cli/doctor.mdx +2 -2
  119. package/docs/cli/evals.mdx +2 -2
  120. package/docs/cli/index.mdx +4 -1
  121. package/docs/cli/translate.mdx +1 -1
  122. package/docs/configuration/analytics.mdx +23 -2
  123. package/docs/configuration/assistant.mdx +1 -1
  124. package/docs/configuration/customization.mdx +4 -3
  125. package/docs/configuration/index.mdx +5 -3
  126. package/docs/configuration/search.mdx +1 -1
  127. package/docs/content/components.mdx +1 -1
  128. package/docs/content/frontmatter.mdx +5 -1
  129. package/docs/content/i18n.mdx +1 -1
  130. package/docs/content/index.mdx +4 -2
  131. package/docs/content/meta.mdx +5 -3
  132. package/docs/content/navigation.mdx +29 -4
  133. package/docs/content/sources.mdx +18 -12
  134. package/docs/content/versioning.mdx +1 -0
  135. package/docs/discoverability/agent-discovery.mdx +21 -7
  136. package/docs/discoverability/index.mdx +2 -2
  137. package/docs/discoverability/llms-txt.mdx +2 -5
  138. package/docs/discoverability/markdown.mdx +5 -3
  139. package/docs/discoverability/mcp.mdx +4 -0
  140. package/docs/discoverability/metadata.mdx +3 -2
  141. package/docs/discoverability/open-graph.mdx +6 -4
  142. package/docs/discoverability/rss.mdx +3 -1
  143. package/docs/references/asyncapi.mdx +1 -1
  144. package/docs/references/graphql.mdx +2 -2
  145. package/docs/references/openapi.mdx +3 -3
  146. package/package.json +1 -1
  147. package/skills/blume-migrate/SKILL.md +6 -6
  148. package/skills/blume-migrate/references/docusaurus.md +6 -6
  149. package/skills/blume-migrate/references/mintlify.md +3 -3
  150. package/skills/blume-migrate/references/monorepo.md +1 -1
  151. package/skills/blume-migrate/references/nextra.md +1 -1
  152. package/skills/blume-migrate/references/starlight.md +5 -5
  153. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  154. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  155. package/src/ai/agent-surface.ts +56 -0
  156. package/src/ai/api/spec.ts +15 -2
  157. package/src/ai/api-catalog.ts +7 -1
  158. package/src/ai/ask-context.ts +14 -2
  159. package/src/ai/ask-data.ts +26 -12
  160. package/src/ai/component-markdown.ts +43 -37
  161. package/src/ai/link-headers.ts +8 -3
  162. package/src/ai/llms.ts +4 -3
  163. package/src/ai/markdown.ts +23 -11
  164. package/src/ai/mcp/server.ts +78 -7
  165. package/src/ai/relative-links.ts +78 -10
  166. package/src/ai/static-expression.ts +416 -0
  167. package/src/ai/visibility.ts +45 -14
  168. package/src/analytics/databuddy.ts +67 -0
  169. package/src/analytics/head.ts +4 -0
  170. package/src/analytics/index.ts +2 -0
  171. package/src/analytics/posthog.ts +21 -3
  172. package/src/analytics/schema.ts +2 -0
  173. package/src/astro/generate.ts +71 -27
  174. package/src/astro/templates.ts +61 -23
  175. package/src/audit/catalog.ts +2 -2
  176. package/src/audit/checks/assets.ts +25 -5
  177. package/src/audit/checks/content.ts +20 -2
  178. package/src/audit/checks/i18n.ts +46 -8
  179. package/src/audit/checks/indexability.ts +39 -19
  180. package/src/audit/checks/links.ts +14 -0
  181. package/src/audit/checks/llms.ts +6 -3
  182. package/src/audit/checks/network.ts +3 -1
  183. package/src/audit/checks/og-image.ts +10 -0
  184. package/src/audit/checks/robots.ts +6 -1
  185. package/src/audit/checks/sitemap.ts +56 -31
  186. package/src/audit/checks/social.ts +21 -2
  187. package/src/audit/crawl.ts +88 -15
  188. package/src/audit/report.ts +54 -17
  189. package/src/audit/run.ts +1 -0
  190. package/src/audit/types.ts +18 -0
  191. package/src/audit/url.ts +37 -6
  192. package/src/cli/build-failure.ts +50 -0
  193. package/src/cli/commands/audit.ts +7 -0
  194. package/src/cli/commands/build.ts +19 -5
  195. package/src/cli/commands/check.ts +2 -0
  196. package/src/cli/commands/eval.ts +1 -7
  197. package/src/cli/commands/init.ts +15 -5
  198. package/src/cli/commands/preview.ts +15 -0
  199. package/src/cli/commands/sync.ts +2 -0
  200. package/src/cli/commands/translate.ts +9 -6
  201. package/src/cli/commands/upgrade.ts +11 -0
  202. package/src/cli/eject-scripts.ts +32 -7
  203. package/src/cli/env.ts +12 -1
  204. package/src/cli/init/scaffold.ts +117 -11
  205. package/src/cli/init/starter-spec.ts +235 -0
  206. package/src/components/content/AccordionItem.astro +26 -22
  207. package/src/components/content/Badge.astro +2 -9
  208. package/src/components/content/Card.astro +2 -2
  209. package/src/components/content/Component.astro +9 -3
  210. package/src/components/content/Expandable.astro +5 -1
  211. package/src/components/content/Frame.astro +2 -7
  212. package/src/components/content/GithubInfo.astro +12 -2
  213. package/src/components/content/Prompt.astro +2 -7
  214. package/src/components/content/Tabs.astro +54 -5
  215. package/src/components/content/Tile.astro +1 -1
  216. package/src/components/content/Tooltip.astro +69 -7
  217. package/src/components/content/Tree.astro +7 -2
  218. package/src/components/content/TypeTable.astro +10 -5
  219. package/src/components/content/Update.astro +8 -2
  220. package/src/components/content/auto-type-table.ts +4 -1
  221. package/src/components/content/badge-color.ts +17 -0
  222. package/src/components/content/base-href.ts +18 -3
  223. package/src/components/content/inline-markdown.ts +27 -7
  224. package/src/components/copy-feedback.ts +35 -8
  225. package/src/components/islands/assistant.tsx +16 -2
  226. package/src/components/islands/hooks.ts +7 -2
  227. package/src/components/islands/webmcp.ts +12 -8
  228. package/src/components/layout/Banner.astro +23 -4
  229. package/src/components/layout/Header.astro +22 -9
  230. package/src/components/layout/Logo.astro +5 -0
  231. package/src/components/layout/NavSelector.astro +6 -1
  232. package/src/components/layout/NavTabMenu.astro +133 -0
  233. package/src/components/layout/NavTree.astro +15 -6
  234. package/src/components/layout/NavTreeCache.astro +5 -2
  235. package/src/components/layout/NavTreeScript.astro +45 -5
  236. package/src/components/layout/PageLayout.astro +22 -8
  237. package/src/components/layout/ReferenceLayout.astro +4 -1
  238. package/src/components/layout/RootLayout.astro +21 -8
  239. package/src/components/layout/Search.astro +5 -1
  240. package/src/components/layout/analytics-client.ts +2 -0
  241. package/src/components/openapi/GraphqlType.astro +11 -3
  242. package/src/components/openapi/MessageComposer.astro +1 -1
  243. package/src/components/openapi/Operation.astro +21 -5
  244. package/src/components/openapi/PanelTabs.astro +4 -1
  245. package/src/components/openapi/Playground.astro +8 -2
  246. package/src/components/openapi/RequestPanel.astro +9 -5
  247. package/src/components/openapi/SchemaProperty.astro +11 -33
  248. package/src/components/openapi/SchemaTable.astro +27 -52
  249. package/src/components/openapi/helpers.ts +90 -12
  250. package/src/components/openapi/message-composer.ts +8 -0
  251. package/src/components/openapi/message-model.ts +12 -2
  252. package/src/components/openapi/message.ts +4 -1
  253. package/src/components/openapi/operation-model.ts +40 -8
  254. package/src/components/openapi/panel.ts +29 -5
  255. package/src/components/openapi/playground-client.ts +37 -7
  256. package/src/components/openapi/playground-schema.ts +25 -6
  257. package/src/components/openapi/request.ts +2 -0
  258. package/src/components/openapi/schema-tree.ts +203 -0
  259. package/src/components/openapi/snippets.ts +30 -15
  260. package/src/components/openapi/validate-json.ts +1 -1
  261. package/src/core/base-path.ts +15 -0
  262. package/src/core/config-input.ts +4 -4
  263. package/src/core/config.ts +18 -4
  264. package/src/core/diagnostics.ts +217 -30
  265. package/src/core/graph.ts +120 -11
  266. package/src/core/i18n-ui.ts +39 -2
  267. package/src/core/i18n.ts +13 -2
  268. package/src/core/links.ts +7 -4
  269. package/src/core/load-module.ts +20 -0
  270. package/src/core/locale-links.ts +17 -17
  271. package/src/core/manifest.ts +3 -2
  272. package/src/core/meta.ts +69 -6
  273. package/src/core/nav-diagnostics.ts +56 -1
  274. package/src/core/navigation.ts +245 -76
  275. package/src/core/ordering-prefix.ts +27 -0
  276. package/src/core/project-graph.ts +23 -1
  277. package/src/core/safe-href.ts +53 -1
  278. package/src/core/safe-links.ts +11 -2
  279. package/src/core/schema.ts +70 -14
  280. package/src/core/sources/assets.ts +83 -41
  281. package/src/core/sources/contentful-rich-text.ts +3 -2
  282. package/src/core/sources/contentful.ts +25 -12
  283. package/src/core/sources/filesystem.ts +5 -1
  284. package/src/core/sources/github-releases.ts +106 -5
  285. package/src/core/sources/lexical.ts +9 -4
  286. package/src/core/sources/lower.ts +79 -26
  287. package/src/core/sources/mdx-remote.ts +1 -0
  288. package/src/core/sources/normalize.ts +132 -30
  289. package/src/core/sources/notion.ts +26 -10
  290. package/src/core/sources/obsidian.ts +17 -3
  291. package/src/core/sources/payload.ts +1 -0
  292. package/src/core/sources/portable-text.ts +63 -44
  293. package/src/core/sources/remote.ts +18 -2
  294. package/src/core/sources/resolve.ts +52 -33
  295. package/src/core/sources/sanity.ts +1 -0
  296. package/src/core/sources/strapi-blocks.ts +9 -4
  297. package/src/core/sources/strapi.ts +1 -0
  298. package/src/core/sources/types.ts +31 -0
  299. package/src/core/types.ts +9 -0
  300. package/src/core/ui-packs/ar.ts +13 -0
  301. package/src/core/ui-packs/bg.ts +13 -0
  302. package/src/core/ui-packs/bn.ts +13 -0
  303. package/src/core/ui-packs/ca.ts +13 -0
  304. package/src/core/ui-packs/cs.ts +13 -0
  305. package/src/core/ui-packs/da.ts +13 -0
  306. package/src/core/ui-packs/de.ts +13 -0
  307. package/src/core/ui-packs/el.ts +13 -0
  308. package/src/core/ui-packs/es.ts +13 -0
  309. package/src/core/ui-packs/fa.ts +13 -0
  310. package/src/core/ui-packs/fi.ts +13 -0
  311. package/src/core/ui-packs/fr.ts +13 -0
  312. package/src/core/ui-packs/he.ts +13 -0
  313. package/src/core/ui-packs/hi.ts +13 -0
  314. package/src/core/ui-packs/hr.ts +13 -0
  315. package/src/core/ui-packs/hu.ts +13 -0
  316. package/src/core/ui-packs/id.ts +13 -0
  317. package/src/core/ui-packs/it.ts +13 -0
  318. package/src/core/ui-packs/ja.ts +13 -0
  319. package/src/core/ui-packs/ko.ts +13 -0
  320. package/src/core/ui-packs/nl.ts +13 -0
  321. package/src/core/ui-packs/no.ts +13 -0
  322. package/src/core/ui-packs/pl.ts +13 -0
  323. package/src/core/ui-packs/pt-br.ts +13 -0
  324. package/src/core/ui-packs/pt.ts +13 -0
  325. package/src/core/ui-packs/ro.ts +13 -0
  326. package/src/core/ui-packs/ru.ts +13 -0
  327. package/src/core/ui-packs/sk.ts +13 -0
  328. package/src/core/ui-packs/sr.ts +13 -0
  329. package/src/core/ui-packs/sv.ts +13 -0
  330. package/src/core/ui-packs/th.ts +13 -0
  331. package/src/core/ui-packs/tr.ts +13 -0
  332. package/src/core/ui-packs/uk.ts +13 -0
  333. package/src/core/ui-packs/vi.ts +13 -0
  334. package/src/core/ui-packs/zh-tw.ts +13 -0
  335. package/src/core/ui-packs/zh.ts +13 -0
  336. package/src/core/version-cut.ts +17 -2
  337. package/src/core/versions.ts +4 -1
  338. package/src/deploy/adapters/node.ts +5 -2
  339. package/src/deploy/adapters/registry.ts +2 -1
  340. package/src/deploy/adapters/types.ts +13 -1
  341. package/src/deploy/artifacts.ts +25 -5
  342. package/src/deploy/cloudflare-negotiation.ts +23 -4
  343. package/src/deploy/headers.ts +67 -51
  344. package/src/deploy/node-headers.ts +148 -27
  345. package/src/deploy/platforms/cloudflare.ts +1 -0
  346. package/src/deploy/platforms/netlify.ts +82 -5
  347. package/src/deploy/platforms/node.ts +7 -5
  348. package/src/deploy/platforms/static.ts +1 -0
  349. package/src/deploy/platforms/types.ts +7 -0
  350. package/src/deploy/platforms/vercel.ts +9 -3
  351. package/src/deploy/redirects.ts +14 -3
  352. package/src/deploy/vercel-negotiation.ts +35 -3
  353. package/src/eval/agents.ts +10 -2
  354. package/src/eval/run.ts +25 -0
  355. package/src/markdown/base-links.ts +55 -8
  356. package/src/markdown/index.ts +6 -3
  357. package/src/markdown/relative-links.ts +3 -23
  358. package/src/markdown/route-snapshot.ts +37 -0
  359. package/src/og/card.ts +20 -4
  360. package/src/og/derive.ts +145 -4
  361. package/src/openapi/graphql-build.ts +28 -2
  362. package/src/openapi/model.ts +77 -13
  363. package/src/openapi/render-mdx.ts +10 -1
  364. package/src/registry/eject.ts +119 -22
  365. package/src/search/documents.ts +22 -1
  366. package/src/search/sync/algolia.ts +36 -2
  367. package/src/sources/registry.ts +5 -0
  368. package/src/theme/entry.ts +11 -4
  369. package/src/translate/agents.ts +6 -1
  370. package/src/translate/ledger.ts +26 -3
  371. package/src/translate/meta.ts +11 -3
  372. package/src/translate/run.ts +11 -5
  373. package/src/translate/validate.ts +10 -1
  374. package/src/translate/work-list.ts +36 -3
  375. package/src/upgrade/upgrade.ts +36 -4
  376. package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
  377. package/dist/cli/chunk-6crbhc3x.js.map +0 -14
  378. package/dist/cli/chunk-6hsn950k.js.map +0 -10
  379. package/dist/cli/chunk-79jhk4py.js.map +0 -35
  380. package/dist/cli/chunk-82bbrxdn.js +0 -51
  381. package/dist/cli/chunk-82bbrxdn.js.map +0 -10
  382. package/dist/cli/chunk-abh8yjkn.js +0 -31
  383. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  384. package/dist/cli/chunk-dh8cwk36.js.map +0 -10
  385. package/dist/cli/chunk-epjnccmv.js.map +0 -10
  386. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  387. package/dist/cli/chunk-fs23ddbb.js.map +0 -35
  388. package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
  389. package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
  390. package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
  391. package/dist/cli/chunk-mb2919y2.js.map +0 -10
  392. package/dist/cli/chunk-q5163e60.js.map +0 -11
  393. package/dist/cli/chunk-qkqwkpte.js.map +0 -182
  394. package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
  395. package/dist/cli/chunk-s1p84fyh.js.map +0 -10
  396. package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
  397. package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
  398. /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-00gs3wqs.js.map} +0 -0
  399. /package/dist/cli/{chunk-6vm74dry.js.map → chunk-6dtt0zfn.js.map} +0 -0
  400. /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-bbnwccaz.js.map} +0 -0
  401. /package/dist/cli/{chunk-ce574jw2.js.map → chunk-esh98wmb.js.map} +0 -0
  402. /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-fsmrqk8a.js.map} +0 -0
  403. /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-g698a744.js.map} +0 -0
  404. /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-h2ez8dzb.js.map} +0 -0
  405. /package/dist/cli/{chunk-5shv93fd.js.map → chunk-h7k3nq3v.js.map} +0 -0
  406. /package/dist/cli/{chunk-ah61y8py.js.map → chunk-mqc662a6.js.map} +0 -0
  407. /package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-n1yg3tj3.js.map} +0 -0
  408. /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-sqw4ekg1.js.map} +0 -0
  409. /package/dist/cli/{chunk-27g6wdth.js.map → chunk-wdrt2k2v.js.map} +0 -0
  410. /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-zp79m0ts.js.map} +0 -0
package/src/cli/env.ts CHANGED
@@ -19,6 +19,11 @@ import { dirname, join, resolve } from "pathe";
19
19
  * these files at build time, so a value means the same thing to the pre-boot
20
20
  * content scan and the built site — including multi-line double-quoted values
21
21
  * (PEM keys), which a line-based parser silently truncates.
22
+ *
23
+ * `NODE_ENV` is never taken from these files: it picks the mode Vite and Astro
24
+ * run in, so a monorepo root `.env` that sets it for another app would turn
25
+ * every `blume build` into a development build (or `blume dev` into a
26
+ * production one).
22
27
  */
23
28
  export const loadEnvFiles = (startDir: string): void => {
24
29
  const paths: string[] = [];
@@ -31,5 +36,11 @@ export const loadEnvFiles = (startDir: string): void => {
31
36
  done = existsSync(join(dir, ".git")) || parent === dir;
32
37
  dir = parent;
33
38
  }
34
- config({ path: paths, quiet: true });
39
+ const loaded: Record<string, string> = {};
40
+ config({ path: paths, processEnv: loaded, quiet: true });
41
+ for (const [key, value] of Object.entries(loaded)) {
42
+ if (key !== "NODE_ENV" && process.env[key] === undefined) {
43
+ process.env[key] = value;
44
+ }
45
+ }
35
46
  };
@@ -1,8 +1,11 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
 
4
+ import { loadAll } from "js-yaml";
4
5
  import { detect } from "package-manager-detector/detect";
5
6
  import { basename, dirname, isAbsolute, join, relative } from "pathe";
7
+ import picomatch from "picomatch";
8
+ import { z } from "zod";
6
9
 
7
10
  import { blumePackageJson, toPackageName } from "../../core/package-json.ts";
8
11
  import { contentful } from "../../sources/contentful.ts";
@@ -14,6 +17,7 @@ import { payload } from "../../sources/payload.ts";
14
17
  import type { AnySourceAdapter } from "../../sources/registry.ts";
15
18
  import { sanity } from "../../sources/sanity.ts";
16
19
  import { strapi } from "../../sources/strapi.ts";
20
+ import { STARTER_OPENAPI_JSON } from "./starter-spec.ts";
17
21
 
18
22
  export const TEMPLATES = ["docs", "api", "sdk", "changelog"] as const;
19
23
  export type Template = (typeof TEMPLATES)[number];
@@ -64,6 +68,11 @@ interface Starter {
64
68
  /** Import lines the fragment needs, placed after the `defineConfig` import. */
65
69
  configImports: string[];
66
70
  files: (contentDir: string) => { content: string; path: string }[];
71
+ /**
72
+ * Files the config itself reads (the `api` starter's spec), written beside
73
+ * it whichever content sources are selected.
74
+ */
75
+ projectFiles?: { content: string; path: string }[];
67
76
  }
68
77
 
69
78
  const page = (title: string, description: string, body: string): string =>
@@ -77,8 +86,8 @@ export const STARTERS = {
77
86
  route: "/api",
78
87
  sources: [
79
88
  {
80
- label: "Petstore",
81
- spec: "https://petstore3.swagger.io/api/v3/openapi.json",
89
+ label: "Pet Store",
90
+ spec: "./openapi.json",
82
91
  },
83
92
  ],
84
93
  }),
@@ -89,11 +98,12 @@ export const STARTERS = {
89
98
  content: page(
90
99
  "API Reference",
91
100
  "Reference documentation for the API, generated from its OpenAPI spec, with every endpoint, its parameters, request bodies, and responses.",
92
- "Your OpenAPI spec renders at [`/api`](/api). Point `openapi()` at your own spec in `blume.config.ts`."
101
+ "The example spec in `openapi.json` renders at [`/api`](/api). Replace it with your own, or point `openapi()` at your spec in `blume.config.ts`."
93
102
  ),
94
103
  path: join(dir, "index.mdx"),
95
104
  },
96
105
  ],
106
+ projectFiles: [{ content: STARTER_OPENAPI_JSON, path: "openapi.json" }],
97
107
  },
98
108
  changelog: {
99
109
  configExtra: `
@@ -173,6 +183,20 @@ export const commandsFor = (pm: PackageManager) => ({
173
183
  install: `${pm} install`,
174
184
  });
175
185
 
186
+ // A path every shell (POSIX, cmd, PowerShell) takes as one bare word.
187
+ const SHELL_SAFE_PATH = /^[\w./:-]+$/u;
188
+ // What a double-quoted POSIX word still interprets.
189
+ const DOUBLE_QUOTE_SPECIAL = /["$\\`]/gu;
190
+
191
+ /**
192
+ * The `cd` into the new project, quoting a directory a shell would split or
193
+ * expand: `blume init "my docs"` prints `cd "my docs"`, not `cd my docs`.
194
+ */
195
+ export const cdCommand = (directory: string): string =>
196
+ SHELL_SAFE_PATH.test(directory)
197
+ ? `cd ${directory}`
198
+ : `cd "${directory.replaceAll(DOUBLE_QUOTE_SPECIAL, String.raw`\$&`)}"`;
199
+
176
200
  const isPackageManager = (value: string): value is PackageManager =>
177
201
  PACKAGE_MANAGERS.some((pm) => pm === value);
178
202
 
@@ -525,6 +549,70 @@ const readText = (path: string): string => {
525
549
  const pnpmWorkspaceRoot = (root: string): string | null =>
526
550
  nearestAncestor(root, (dir) => existsSync(join(dir, "pnpm-workspace.yaml")));
527
551
 
552
+ /** The slice of `pnpm-workspace.yaml` the membership check reads. */
553
+ const pnpmWorkspaceSchema = z.looseObject({
554
+ packages: z.array(z.string()).default([]),
555
+ });
556
+
557
+ // A glob's leading `./` and trailing `/`, which pnpm ignores.
558
+ const GLOB_EDGES = /^\.\/|\/+$/gu;
559
+
560
+ /** Whether any of `globs` matches the workspace-relative path `at`. */
561
+ const matchesAny = (globs: string[], at: string): boolean =>
562
+ globs.length > 0 && picomatch(globs)(at);
563
+
564
+ /**
565
+ * Whether the pnpm workspace at `workspace` lists `root` as one of its
566
+ * packages: the workspace root itself, or a folder its `packages` globs match
567
+ * and no `!` glob excludes. pnpm installs only those, so an install run in any
568
+ * other folder beneath it succeeds without installing that folder's
569
+ * dependencies. A workspace file that doesn't parse counts as listing it, so
570
+ * `init` never claims what it can't tell.
571
+ */
572
+ const listsPackage = (workspace: string, root: string): boolean => {
573
+ const at = relative(workspace, root);
574
+ if (at === "") {
575
+ return true;
576
+ }
577
+ let parsed: ReturnType<typeof pnpmWorkspaceSchema.safeParse>;
578
+ try {
579
+ // `loadAll`, not `load`: js-yaml 5 throws on a file with no document (empty,
580
+ // or comments only), which pnpm reads as a workspace of the root alone.
581
+ const [document] = loadAll(
582
+ readText(join(workspace, "pnpm-workspace.yaml"))
583
+ );
584
+ parsed = pnpmWorkspaceSchema.safeParse(document ?? {});
585
+ } catch {
586
+ return true;
587
+ }
588
+ if (!parsed.success) {
589
+ return true;
590
+ }
591
+ const globs = parsed.data.packages;
592
+ const pattern = (glob: string): string => glob.replaceAll(GLOB_EDGES, "");
593
+ const included = globs.filter((glob) => !glob.startsWith("!")).map(pattern);
594
+ const excluded = globs
595
+ .filter((glob) => glob.startsWith("!"))
596
+ .map((glob) => pattern(glob.slice(1)));
597
+ return matchesAny(included, at) && !matchesAny(excluded, at);
598
+ };
599
+
600
+ /**
601
+ * Whether `init` installs with pnpm into a folder that sits inside a pnpm
602
+ * workspace without being one of its packages. pnpm would install that
603
+ * workspace's own packages from there and exit 0, leaving `blume` uninstalled.
604
+ */
605
+ export const outsidePnpmWorkspace = (
606
+ root: string,
607
+ answers: InitAnswers
608
+ ): boolean => {
609
+ if (answers.packageManager !== "pnpm") {
610
+ return false;
611
+ }
612
+ const workspace = pnpmWorkspaceRoot(root);
613
+ return workspace !== null && !listsPackage(workspace, root);
614
+ };
615
+
528
616
  /**
529
617
  * The Yarn project enclosing `root`: a directory holding a lockfile, a
530
618
  * `.yarnrc.yml`, or a `package.json` that declares workspaces.
@@ -585,10 +673,13 @@ const packageManagerConfigFor = (
585
673
  return [];
586
674
  };
587
675
 
676
+ const ESBUILD_APPROVAL = " allowBuilds:\n esbuild: true";
677
+
588
678
  /**
589
- * What the project still needs from a workspace it joins, when `init` left
590
- * that workspace's config alone: pnpm's approval for esbuild's build script
591
- * (pnpm 10 and later fail the install without it), or Yarn Berry's
679
+ * What the project still needs from a workspace it sits in, when `init` left
680
+ * that workspace's config alone: a place in a pnpm workspace's `packages`
681
+ * (pnpm skips a folder it doesn't list), pnpm's approval for esbuild's build
682
+ * script (pnpm 10 and later fail the install without it), or Yarn Berry's
592
683
  * `node_modules` linker. Undefined when the workspace already has it, or the
593
684
  * project doesn't sit in one.
594
685
  */
@@ -599,11 +690,21 @@ export const workspaceNote = (
599
690
  ): string | undefined => {
600
691
  if (answers.packageManager === "pnpm") {
601
692
  const workspace = pnpmWorkspaceRoot(root);
602
- const file = workspace && join(workspace, "pnpm-workspace.yaml");
603
- if (!file || readText(file).includes("esbuild")) {
693
+ if (!workspace) {
694
+ return;
695
+ }
696
+ const file = join(workspace, "pnpm-workspace.yaml");
697
+ const approved = readText(file).includes("esbuild");
698
+ if (!listsPackage(workspace, root)) {
699
+ const entry = ` packages:\n - ${relative(workspace, root)}`;
700
+ return approved
701
+ ? `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing:\n\n${entry}`
702
+ : `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing, and approve esbuild's build script, or pnpm 10 and later stop the install:\n\n${entry}\n${ESBUILD_APPROVAL}`;
703
+ }
704
+ if (approved) {
604
705
  return;
605
706
  }
606
- return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm 10 and later stop the install:\n\n allowBuilds:\n esbuild: true`;
707
+ return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm 10 and later stop the install:\n\n${ESBUILD_APPROVAL}`;
607
708
  }
608
709
  if (answers.packageManager !== "yarn" || yarnMajor(env.userAgent) === 1) {
609
710
  return;
@@ -622,6 +723,7 @@ export const buildPlan = (
622
723
  answers: InitAnswers,
623
724
  env: PlanEnvironment = {}
624
725
  ): ScaffoldFile[] => {
726
+ const starter: Starter = STARTERS[answers.template];
625
727
  const files: ScaffoldFile[] = [
626
728
  {
627
729
  content: blumePackageJson(
@@ -632,11 +734,15 @@ export const buildPlan = (
632
734
  },
633
735
  ...packageManagerConfigFor(root, answers, env),
634
736
  { content: buildConfig(answers), path: join(root, "blume.config.ts") },
737
+ ...(starter.projectFiles ?? []).map((file) => ({
738
+ ...file,
739
+ path: join(root, file.path),
740
+ })),
635
741
  ];
636
742
  // Seed pages only make sense when a local filesystem source will read them.
637
743
  if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
638
744
  files.push(
639
- ...STARTERS[answers.template]
745
+ ...starter
640
746
  .files(answers.contentDir)
641
747
  .map((file) => ({ ...file, path: join(root, file.path) }))
642
748
  );
@@ -771,7 +877,7 @@ export const nextSteps = (
771
877
  const commands = commandsFor(answers.packageManager);
772
878
  const lines: string[] = [];
773
879
  if (answers.directory !== ".") {
774
- lines.push(`cd ${answers.directory}`);
880
+ lines.push(cdCommand(answers.directory));
775
881
  }
776
882
  if (needsInstall) {
777
883
  lines.push(commands.install);
@@ -0,0 +1,235 @@
1
+ /**
2
+ * The OpenAPI document the `api` starter writes beside `blume.config.ts`, so a
3
+ * new project's first build reads a local file instead of depending on a
4
+ * third-party server. Small, but it exercises what a real reference renders:
5
+ * path and query parameters, request bodies, shared schemas, error responses,
6
+ * and bearer auth. Each operation's description is sized so its page's meta
7
+ * description, with the endpoint sentence Blume appends, fits the range
8
+ * `blume audit` checks.
9
+ */
10
+
11
+ const petId = {
12
+ description: "The pet's ID.",
13
+ in: "path",
14
+ name: "petId",
15
+ required: true,
16
+ schema: { type: "string" },
17
+ };
18
+
19
+ const errorResponse = (description: string) => ({
20
+ content: {
21
+ "application/json": { schema: { $ref: "#/components/schemas/Error" } },
22
+ },
23
+ description,
24
+ });
25
+
26
+ const petResponse = (description: string) => ({
27
+ content: {
28
+ "application/json": { schema: { $ref: "#/components/schemas/Pet" } },
29
+ },
30
+ description,
31
+ });
32
+
33
+ // Built from entries rather than a literal so the written file keeps the
34
+ // conventional section order (`openapi`, `info`, …) instead of the sorted one.
35
+ const STARTER_SPEC = Object.fromEntries([
36
+ ["openapi", "3.1.0"],
37
+ [
38
+ "info",
39
+ {
40
+ description:
41
+ "A small example API for a pet store: list, create, read, update, and delete pets. Replace openapi.json with your own spec to document your API.",
42
+ title: "Pet Store API",
43
+ version: "1.0.0",
44
+ },
45
+ ],
46
+ ["servers", [{ url: "https://api.example.com/v1" }]],
47
+ ["security", [{ bearerAuth: [] }]],
48
+ [
49
+ "tags",
50
+ [
51
+ {
52
+ description:
53
+ "Every pet in the store, with its name, species, and status.",
54
+ name: "Pets",
55
+ },
56
+ ],
57
+ ],
58
+ [
59
+ "paths",
60
+ {
61
+ "/pets": {
62
+ get: {
63
+ description:
64
+ "Returns the pets in the store, newest first, filtered by status and paged with a cursor.",
65
+ operationId: "listPets",
66
+ parameters: [
67
+ {
68
+ description: "Only return pets with this status.",
69
+ in: "query",
70
+ name: "status",
71
+ schema: { $ref: "#/components/schemas/Status" },
72
+ },
73
+ {
74
+ description: "The most pets to return.",
75
+ in: "query",
76
+ name: "limit",
77
+ schema: {
78
+ default: 20,
79
+ maximum: 100,
80
+ minimum: 1,
81
+ type: "integer",
82
+ },
83
+ },
84
+ {
85
+ description: "The `nextCursor` from the previous page.",
86
+ in: "query",
87
+ name: "cursor",
88
+ schema: { type: "string" },
89
+ },
90
+ ],
91
+ responses: {
92
+ "200": {
93
+ content: {
94
+ "application/json": {
95
+ schema: { $ref: "#/components/schemas/PetList" },
96
+ },
97
+ },
98
+ description: "A page of pets.",
99
+ },
100
+ },
101
+ summary: "List pets",
102
+ tags: ["Pets"],
103
+ },
104
+ post: {
105
+ description:
106
+ "Adds a pet to the store and returns it with its new ID and an available status.",
107
+ operationId: "createPet",
108
+ requestBody: {
109
+ content: {
110
+ "application/json": {
111
+ schema: { $ref: "#/components/schemas/NewPet" },
112
+ },
113
+ },
114
+ required: true,
115
+ },
116
+ responses: {
117
+ "201": petResponse("The new pet."),
118
+ "422": errorResponse("The pet's fields are invalid."),
119
+ },
120
+ summary: "Create a pet",
121
+ tags: ["Pets"],
122
+ },
123
+ },
124
+ "/pets/{petId}": {
125
+ delete: {
126
+ description:
127
+ "Removes a pet from the store for good, or returns a 404 error when no pet has that ID.",
128
+ operationId: "deletePet",
129
+ parameters: [petId],
130
+ responses: {
131
+ "204": { description: "The pet was deleted." },
132
+ "404": errorResponse("No pet has that ID."),
133
+ },
134
+ summary: "Delete a pet",
135
+ tags: ["Pets"],
136
+ },
137
+ get: {
138
+ description:
139
+ "Returns a single pet by its ID, or a 404 error when no pet has that ID.",
140
+ operationId: "getPet",
141
+ parameters: [petId],
142
+ responses: {
143
+ "200": petResponse("The pet."),
144
+ "404": errorResponse("No pet has that ID."),
145
+ },
146
+ summary: "Get a pet",
147
+ tags: ["Pets"],
148
+ },
149
+ patch: {
150
+ description:
151
+ "Changes the fields you send on an existing pet and leaves the rest as they were.",
152
+ operationId: "updatePet",
153
+ parameters: [petId],
154
+ requestBody: {
155
+ content: {
156
+ "application/json": {
157
+ schema: { $ref: "#/components/schemas/PetUpdate" },
158
+ },
159
+ },
160
+ required: true,
161
+ },
162
+ responses: {
163
+ "200": petResponse("The updated pet."),
164
+ "404": errorResponse("No pet has that ID."),
165
+ },
166
+ summary: "Update a pet",
167
+ tags: ["Pets"],
168
+ },
169
+ },
170
+ },
171
+ ],
172
+ [
173
+ "components",
174
+ {
175
+ schemas: {
176
+ Error: {
177
+ properties: {
178
+ code: { example: "not_found", type: "string" },
179
+ message: { example: "No pet has that ID.", type: "string" },
180
+ },
181
+ required: ["code", "message"],
182
+ type: "object",
183
+ },
184
+ NewPet: {
185
+ properties: {
186
+ name: { example: "Mochi", type: "string" },
187
+ species: { $ref: "#/components/schemas/Species" },
188
+ },
189
+ required: ["name", "species"],
190
+ type: "object",
191
+ },
192
+ Pet: {
193
+ properties: {
194
+ createdAt: { format: "date-time", type: "string" },
195
+ id: { example: "pet_01h2x", type: "string" },
196
+ name: { example: "Mochi", type: "string" },
197
+ species: { $ref: "#/components/schemas/Species" },
198
+ status: { $ref: "#/components/schemas/Status" },
199
+ },
200
+ required: ["id", "name", "species", "status", "createdAt"],
201
+ type: "object",
202
+ },
203
+ PetList: {
204
+ properties: {
205
+ data: {
206
+ items: { $ref: "#/components/schemas/Pet" },
207
+ type: "array",
208
+ },
209
+ nextCursor: {
210
+ description: "Pass as `cursor` to fetch the next page.",
211
+ type: ["string", "null"],
212
+ },
213
+ },
214
+ required: ["data", "nextCursor"],
215
+ type: "object",
216
+ },
217
+ PetUpdate: {
218
+ properties: {
219
+ name: { type: "string" },
220
+ status: { $ref: "#/components/schemas/Status" },
221
+ },
222
+ type: "object",
223
+ },
224
+ Species: { enum: ["cat", "dog", "bird", "rabbit"], type: "string" },
225
+ Status: { enum: ["available", "pending", "adopted"], type: "string" },
226
+ },
227
+ securitySchemes: {
228
+ bearerAuth: { scheme: "bearer", type: "http" },
229
+ },
230
+ },
231
+ ],
232
+ ]);
233
+
234
+ /** The starter spec as the `openapi.json` text `init` writes. */
235
+ export const STARTER_OPENAPI_JSON = `${JSON.stringify(STARTER_SPEC, null, 2)}\n`;
@@ -97,29 +97,33 @@ const accordionId = id ?? componentSlug(title);
97
97
  });
98
98
  };
99
99
 
100
- document.addEventListener(
101
- "toggle",
102
- (event) => {
103
- const accordion = event.target;
104
- if (
105
- !(accordion instanceof HTMLDetailsElement) ||
106
- !accordion.matches("[data-blume-accordion]") ||
107
- !accordion.open ||
108
- !accordion.id
109
- ) {
110
- return;
111
- }
100
+ // Only the reader opening an item points the hash at it, so this keys on
101
+ // the summary's click (pointer, Enter, or Space) rather than `toggle`: a
102
+ // `defaultOpen` item a client-router swap inserts fires `toggle` too, and
103
+ // that must not overwrite the hash the reader arrived with.
104
+ document.addEventListener("click", (event) => {
105
+ const summary =
106
+ event.target instanceof Element ? event.target.closest("summary") : null;
107
+ const accordion = summary?.parentElement;
108
+ if (
109
+ event.defaultPrevented ||
110
+ !(accordion instanceof HTMLDetailsElement) ||
111
+ !accordion.matches("[data-blume-accordion]") ||
112
+ // Open now means this click closes it.
113
+ accordion.open ||
114
+ !accordion.id
115
+ ) {
116
+ return;
117
+ }
112
118
 
113
- // Keep the entry's state: the client router ignores a popstate whose
114
- // state is null, so Back to this page would change only the URL.
115
- history.replaceState(
116
- history.state,
117
- "",
118
- `#${encodeURIComponent(accordion.id)}`
119
- );
120
- },
121
- true
122
- );
119
+ // Keep the entry's state: the client router ignores a popstate whose
120
+ // state is null, so Back to this page would change only the URL.
121
+ history.replaceState(
122
+ history.state,
123
+ "",
124
+ `#${encodeURIComponent(accordion.id)}`
125
+ );
126
+ });
123
127
 
124
128
  const init = () => {
125
129
  dedupeAccordionIds();
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { STROKE, TINT } from "../colors.ts";
3
3
  import Icon from "../Icon.astro";
4
+ import { badgeColorName } from "./badge-color.ts";
4
5
 
5
6
  type BadgeVariant = "default" | "accent" | "success" | "warning" | "danger";
6
7
  type BadgeSize = "xs" | "sm" | "md" | "lg";
@@ -34,15 +35,7 @@ const {
34
35
 
35
36
  const activeStroke = stroke === true || stroke === "true";
36
37
  const isDisabled = disabled === true || disabled === "true";
37
- const colorName =
38
- color ??
39
- {
40
- accent: "blue",
41
- danger: "red",
42
- default: "gray",
43
- success: "green",
44
- warning: "orange",
45
- }[variant];
38
+ const colorName = badgeColorName(variant, color);
46
39
  const customColor = colorName.startsWith("#") ? colorName : null;
47
40
 
48
41
  // The hue rows come from the shared table (see colors.ts for the contrast
@@ -80,7 +80,7 @@ const variantClass = {
80
80
  <p class="mt-0! mb-1.5! flex items-center gap-1.5 font-medium text-foreground">
81
81
  {title}
82
82
  {showArrow && (
83
- <Icon class="text-muted-foreground" name="arrow-right" size={14} />
83
+ <Icon class="text-muted-foreground rtl:-scale-x-100" name="arrow-right" size={14} />
84
84
  )}
85
85
  </p>
86
86
  )
@@ -92,7 +92,7 @@ const variantClass = {
92
92
  cta && (
93
93
  <p class="mt-4! mb-0! flex items-center gap-1.5 font-medium text-accent text-sm">
94
94
  {cta}
95
- {showArrow && <Icon name="arrow-right" size={14} />}
95
+ {showArrow && <Icon class="rtl:-scale-x-100" name="arrow-right" size={14} />}
96
96
  </p>
97
97
  )
98
98
  }
@@ -14,6 +14,7 @@ import { CODE_PADDING_BLOCK_REM } from "../../theme/code-block-padding.ts";
14
14
 
15
15
  import { highlightCode } from "../../markdown/index.ts";
16
16
  import { withBase } from "../islands/base-path.ts";
17
+ import { contentStrings } from "./content-strings.ts";
17
18
  import { EXAMPLE_PANE_MIN_PX } from "./example-pane.ts";
18
19
  import Tab from "./Tab.astro";
19
20
  import Tabs from "./Tabs.astro";
@@ -26,6 +27,7 @@ interface Props {
26
27
 
27
28
  const { path } = Astro.props;
28
29
  const entry = examples[path];
30
+ const { content: strings } = contentStrings(data, Astro.currentLocale);
29
31
 
30
32
  // The live preview renders in its own generated route, embedded as an iframe:
31
33
  // the frame boundary is what keeps the docs CSS (prose typography, component
@@ -103,17 +105,21 @@ const paneClass =
103
105
  <Tab
104
106
  class={`overflow-hidden p-0! ${paneClass}`}
105
107
  style={paneStyle}
106
- title="Preview"
108
+ title={strings.preview}
107
109
  >
108
110
  <iframe
109
111
  class="h-full w-full"
110
112
  data-blume-example-frame
111
113
  loading="lazy"
112
114
  src={previewSrc}
113
- title={`Preview of ${path}`}
115
+ title={`${strings.preview}: ${path}`}
114
116
  />
115
117
  </Tab>
116
- <Tab class={`overflow-hidden ${paneClass}`} style={paneStyle} title="Code">
118
+ <Tab
119
+ class={`overflow-hidden ${paneClass}`}
120
+ style={paneStyle}
121
+ title={strings.code}
122
+ >
117
123
  <Fragment set:html={codeHtml} />
118
124
  </Tab>
119
125
  </Tabs>
@@ -1,7 +1,11 @@
1
1
  ---
2
+ import data from "blume:data";
3
+
2
4
  import AccordionItem from "./AccordionItem.astro";
5
+ import { contentStrings } from "./content-strings.ts";
3
6
 
4
- const { defaultOpen = false, title = "Show more" } = Astro.props;
7
+ const { content: strings } = contentStrings(data, Astro.currentLocale);
8
+ const { defaultOpen = false, title = strings.showMore } = Astro.props;
5
9
  ---
6
10
 
7
11
  <div class="not-prose my-4 overflow-hidden rounded-blume border border-border">
@@ -1,7 +1,5 @@
1
1
  ---
2
- import { createSatteriMarkdownProcessor } from "@astrojs/markdown-satteri";
3
-
4
- import { escapeRawHtml, unwrapParagraph } from "./inline-markdown.ts";
2
+ import { renderInlineMarkdown } from "./inline-markdown.ts";
5
3
 
6
4
  interface Props {
7
5
  caption?: string;
@@ -10,10 +8,7 @@ interface Props {
10
8
 
11
9
  const { caption, hint } = Astro.props;
12
10
 
13
- const markdownProcessor = await createSatteriMarkdownProcessor();
14
- const captionHtml = caption
15
- ? unwrapParagraph((await markdownProcessor.render(escapeRawHtml(caption))).code)
16
- : undefined;
11
+ const captionHtml = caption ? await renderInlineMarkdown(caption) : undefined;
17
12
  ---
18
13
 
19
14
  <blume-frame class="not-prose my-6 block">