blume 2.0.3 → 2.1.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 (455) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +1 -1
  4. package/dist/cli/{chunk-1d7ve1dm.js → chunk-0gz36akr.js} +18 -2
  5. package/dist/cli/{chunk-1d7ve1dm.js.map → chunk-0gz36akr.js.map} +3 -3
  6. package/dist/cli/{chunk-2eytanqx.js → chunk-1n228xqb.js} +2 -2
  7. package/dist/cli/{chunk-yfyb25rh.js → chunk-2f3ndkpw.js} +24 -8
  8. package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
  9. package/dist/cli/{chunk-r20tn01b.js → chunk-2f7n4qzt.js} +8 -2
  10. package/dist/cli/{chunk-r20tn01b.js.map → chunk-2f7n4qzt.js.map} +3 -3
  11. package/dist/cli/{chunk-35d4wj9f.js → chunk-2n4ps1b3.js} +7 -7
  12. package/dist/cli/{chunk-j85vccga.js → chunk-43zcf4gz.js} +97 -57
  13. package/dist/cli/{chunk-j85vccga.js.map → chunk-43zcf4gz.js.map} +6 -6
  14. package/dist/cli/{chunk-e04dxsz1.js → chunk-4ejxbzee.js} +1 -1
  15. package/dist/cli/{chunk-5m5nmvyq.js → chunk-4h8z9vv5.js} +233 -39
  16. package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
  17. package/dist/cli/{chunk-5f86nr5m.js → chunk-5cg2rzgf.js} +18 -16
  18. package/dist/cli/{chunk-5f86nr5m.js.map → chunk-5cg2rzgf.js.map} +2 -2
  19. package/dist/cli/{chunk-tzmab476.js → chunk-5ffywsmb.js} +4 -4
  20. package/dist/cli/{chunk-2z928egk.js → chunk-5wj2qwn2.js} +5 -5
  21. package/dist/cli/chunk-6bm0xpgd.js +18 -0
  22. package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
  23. package/dist/cli/{chunk-vg9r4eb9.js → chunk-713mngdq.js} +12 -5
  24. package/dist/cli/{chunk-vg9r4eb9.js.map → chunk-713mngdq.js.map} +3 -3
  25. package/dist/cli/{chunk-5xvm6tfj.js → chunk-9geymqns.js} +17 -15
  26. package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-9geymqns.js.map} +2 -2
  27. package/dist/cli/{chunk-q58y5e6a.js → chunk-9xr4dgee.js} +26 -17
  28. package/dist/cli/chunk-9xr4dgee.js.map +10 -0
  29. package/dist/cli/{chunk-b07cmahc.js → chunk-amrb6hgh.js} +2 -2
  30. package/dist/cli/chunk-cm89scdv.js +443 -0
  31. package/dist/cli/chunk-cm89scdv.js.map +11 -0
  32. package/dist/cli/{chunk-akbpwfxc.js → chunk-cmtcnh9f.js} +2 -2
  33. package/dist/cli/chunk-cy5bm0j0.js +104 -0
  34. package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
  35. package/dist/cli/{chunk-crgn1q09.js → chunk-e7vvk5ya.js} +4 -4
  36. package/dist/cli/{chunk-pehfxfta.js → chunk-g2z9x01v.js} +6 -98
  37. package/dist/cli/chunk-g2z9x01v.js.map +10 -0
  38. package/dist/cli/{chunk-tkacnehg.js → chunk-gp84za0h.js} +2 -2
  39. package/dist/cli/{chunk-wrr3j9w9.js → chunk-h2a48yhd.js} +186 -63
  40. package/dist/cli/chunk-h2a48yhd.js.map +36 -0
  41. package/dist/cli/{chunk-hm3vjy5s.js → chunk-jnwj5kjj.js} +14 -9
  42. package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-jnwj5kjj.js.map} +4 -4
  43. package/dist/cli/chunk-kwrg63tm.js +31 -0
  44. package/dist/cli/chunk-kwrg63tm.js.map +10 -0
  45. package/dist/cli/{chunk-2hsdwb9n.js → chunk-nd1821sk.js} +18 -16
  46. package/dist/cli/{chunk-2hsdwb9n.js.map → chunk-nd1821sk.js.map} +2 -2
  47. package/dist/cli/{chunk-6dsbexzp.js → chunk-ntv4qrt7.js} +17 -15
  48. package/dist/cli/{chunk-6dsbexzp.js.map → chunk-ntv4qrt7.js.map} +2 -2
  49. package/dist/cli/{chunk-r9rcc4w7.js → chunk-p804f3pe.js} +7 -7
  50. package/dist/cli/{chunk-ey84smr6.js → chunk-py62fewh.js} +6 -6
  51. package/dist/cli/{chunk-ey84smr6.js.map → chunk-py62fewh.js.map} +1 -1
  52. package/dist/cli/chunk-q3wxxq39.js +126 -0
  53. package/dist/cli/chunk-q3wxxq39.js.map +11 -0
  54. package/dist/cli/{chunk-9t7a85s3.js → chunk-rekqz4qg.js} +23 -13
  55. package/dist/cli/chunk-rekqz4qg.js.map +10 -0
  56. package/dist/cli/{chunk-8ktnccpt.js → chunk-ta27feev.js} +1 -1
  57. package/dist/cli/{chunk-pv29h0wf.js → chunk-xkz5xbtv.js} +3476 -822
  58. package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
  59. package/dist/cli/{chunk-a58773jm.js → chunk-y4ykbark.js} +2 -2
  60. package/dist/cli/{chunk-p3v96n38.js → chunk-y5h4r2y0.js} +4 -4
  61. package/dist/cli/{chunk-acanzt5p.js → chunk-yj17vs39.js} +6 -6
  62. package/dist/cli/{chunk-p73c0m7w.js → chunk-yxmk4pec.js} +16 -14
  63. package/dist/cli/{chunk-p73c0m7w.js.map → chunk-yxmk4pec.js.map} +2 -2
  64. package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
  65. package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
  66. package/dist/cli/{chunk-c8chx29p.js → chunk-zf748avv.js} +384 -207
  67. package/dist/cli/chunk-zf748avv.js.map +37 -0
  68. package/dist/cli/{chunk-g4hq16wv.js → chunk-zhvd6adm.js} +16 -14
  69. package/dist/cli/{chunk-g4hq16wv.js.map → chunk-zhvd6adm.js.map} +2 -2
  70. package/dist/cli/index.js +26 -20
  71. package/dist/cli/index.js.map +5 -5
  72. package/dist/types/ai/agent-surface.d.ts +8 -7
  73. package/dist/types/ai/ai-catalog.d.ts +7 -0
  74. package/dist/types/ai/ask-context.d.ts +22 -0
  75. package/dist/types/ai/ask.d.ts +271 -58
  76. package/dist/types/ai/index.d.ts +2 -2
  77. package/dist/types/ai/site-skill.d.ts +26 -0
  78. package/dist/types/ai/skills.d.ts +16 -0
  79. package/dist/types/astro/integration.d.ts +8 -0
  80. package/dist/types/captcha/hcaptcha.d.ts +27 -0
  81. package/dist/types/captcha/index.d.ts +28 -0
  82. package/dist/types/captcha/schema.d.ts +29 -0
  83. package/dist/types/captcha/turnstile.d.ts +28 -0
  84. package/dist/types/components/content/api-field.d.ts +59 -0
  85. package/dist/types/components/content/api-page.d.ts +95 -0
  86. package/dist/types/components/layout/nav-utils.d.ts +26 -0
  87. package/dist/types/components/openapi/helpers.d.ts +137 -0
  88. package/dist/types/components/openapi/security.d.ts +85 -0
  89. package/dist/types/consent/ethyca.d.ts +48 -0
  90. package/dist/types/consent/index.d.ts +37 -0
  91. package/dist/types/consent/native.d.ts +28 -0
  92. package/dist/types/consent/osano.d.ts +42 -0
  93. package/dist/types/consent/schema.d.ts +110 -0
  94. package/dist/types/core/config-input.d.ts +187 -20
  95. package/dist/types/core/data.d.ts +49 -4
  96. package/dist/types/core/footer.d.ts +8 -0
  97. package/dist/types/core/i18n-ui.d.ts +86 -0
  98. package/dist/types/core/includes.d.ts +8 -2
  99. package/dist/types/core/localizable.d.ts +7 -0
  100. package/dist/types/core/page-modes.d.ts +29 -0
  101. package/dist/types/core/redirect-patterns.d.ts +144 -0
  102. package/dist/types/core/related.d.ts +40 -0
  103. package/dist/types/core/schema.d.ts +551 -56
  104. package/dist/types/core/sources/read.d.ts +5 -0
  105. package/dist/types/core/types.d.ts +1 -1
  106. package/dist/types/core/variables.d.ts +42 -0
  107. package/dist/types/deploy/node-headers.d.ts +13 -2
  108. package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
  109. package/dist/types/deploy/platforms/netlify.d.ts +7 -0
  110. package/dist/types/deploy/platforms/vercel.d.ts +6 -0
  111. package/dist/types/deploy/redirects.d.ts +39 -11
  112. package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
  113. package/dist/types/markdown/code-title.d.ts +17 -0
  114. package/dist/types/markdown/features.d.ts +3 -2
  115. package/dist/types/narration/build.d.ts +70 -0
  116. package/dist/types/narration/provider.d.ts +26 -0
  117. package/dist/types/narration/script.d.ts +154 -0
  118. package/dist/types/openapi/model.d.ts +12 -6
  119. package/dist/types/openapi/overlay.d.ts +54 -0
  120. package/dist/types/openapi/parse.d.ts +13 -0
  121. package/dist/types/openapi/references.d.ts +14 -4
  122. package/dist/types/ratelimit/cloudflare.d.ts +39 -0
  123. package/dist/types/ratelimit/index.d.ts +27 -0
  124. package/dist/types/ratelimit/memory.d.ts +36 -0
  125. package/dist/types/ratelimit/schema.d.ts +111 -0
  126. package/dist/types/ratelimit/upstash.d.ts +21 -0
  127. package/dist/types/ratelimit/wrangler.d.ts +23 -0
  128. package/dist/types/reference/asyncapi.d.ts +10 -10
  129. package/dist/types/reference/graphql.d.ts +13 -10
  130. package/dist/types/reference/openapi.d.ts +33 -12
  131. package/dist/types/reference/options.d.ts +45 -5
  132. package/dist/types/reference/scalar.d.ts +13 -0
  133. package/dist/types/reference/schema.d.ts +60 -36
  134. package/dist/types/search/documents.d.ts +12 -1
  135. package/dist/types/search/orama-index.d.ts +4 -0
  136. package/dist/types/seo/metatags.d.ts +20 -0
  137. package/docs/02-deployment.mdx +41 -5
  138. package/docs/03-upgrading.mdx +4 -4
  139. package/docs/04-migrating.mdx +1 -1
  140. package/docs/advanced/changelog.mdx +2 -2
  141. package/docs/advanced/skills.mdx +14 -0
  142. package/docs/cli/audit.mdx +21 -1
  143. package/docs/cli/index.mdx +1 -0
  144. package/docs/configuration/analytics.mdx +3 -1
  145. package/docs/configuration/assistant.mdx +188 -36
  146. package/docs/configuration/consent.mdx +100 -0
  147. package/docs/configuration/customization.mdx +4 -4
  148. package/docs/configuration/index.mdx +63 -5
  149. package/docs/configuration/meta.ts +3 -0
  150. package/docs/configuration/narration.mdx +119 -0
  151. package/docs/configuration/rate-limiting.mdx +69 -0
  152. package/docs/configuration/search.mdx +29 -1
  153. package/docs/configuration/theming.mdx +3 -1
  154. package/docs/content/components.mdx +106 -0
  155. package/docs/content/frontmatter.mdx +76 -0
  156. package/docs/content/i18n.mdx +38 -3
  157. package/docs/content/includes.mdx +16 -0
  158. package/docs/content/meta.mdx +1 -0
  159. package/docs/content/meta.ts +1 -0
  160. package/docs/content/navigation.mdx +28 -6
  161. package/docs/content/sources/contentful.mdx +32 -0
  162. package/docs/content/sources/custom.mdx +39 -0
  163. package/docs/content/sources/github-releases.mdx +31 -0
  164. package/docs/content/sources/index.mdx +91 -0
  165. package/docs/content/sources/meta.ts +16 -0
  166. package/docs/content/sources/notion.mdx +32 -0
  167. package/docs/content/sources/obsidian.mdx +37 -0
  168. package/docs/content/sources/payload.mdx +30 -0
  169. package/docs/content/sources/remote-mdx.mdx +40 -0
  170. package/docs/content/sources/sanity.mdx +29 -0
  171. package/docs/content/sources/strapi.mdx +31 -0
  172. package/docs/content/syntax.mdx +67 -2
  173. package/docs/content/variables.mdx +41 -0
  174. package/docs/discoverability/agent-discovery.mdx +29 -2
  175. package/docs/discoverability/index.mdx +1 -1
  176. package/docs/discoverability/llms-txt.mdx +1 -1
  177. package/docs/discoverability/metadata.mdx +17 -0
  178. package/docs/references/api-pages.mdx +74 -0
  179. package/docs/references/asyncapi.mdx +1 -1
  180. package/docs/references/graphql.mdx +2 -2
  181. package/docs/references/meta.ts +1 -1
  182. package/docs/references/openapi.mdx +102 -3
  183. package/docs/references/scalar.mdx +3 -1
  184. package/package.json +36 -1
  185. package/skills/blume/SKILL.md +3 -0
  186. package/skills/blume-migrate/SKILL.md +5 -5
  187. package/skills/blume-migrate/references/docusaurus.md +3 -3
  188. package/skills/blume-migrate/references/mintlify.md +37 -19
  189. package/skills/blume-migrate/references/nextra.md +2 -2
  190. package/skills/blume-migrate/references/starlight.md +2 -1
  191. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
  192. package/skills/blume-write-skill/SKILL.md +82 -0
  193. package/src/ai/agent-readability.ts +2 -1
  194. package/src/ai/agent-surface.ts +9 -7
  195. package/src/ai/ai-catalog.ts +6 -3
  196. package/src/ai/ask-context.ts +73 -20
  197. package/src/ai/ask-data.ts +6 -0
  198. package/src/ai/ask-tools.ts +142 -0
  199. package/src/ai/ask.ts +354 -76
  200. package/src/ai/component-markdown.ts +75 -0
  201. package/src/ai/cors.ts +2 -0
  202. package/src/ai/index.ts +12 -0
  203. package/src/ai/llms.ts +6 -2
  204. package/src/ai/markdown.ts +2 -1
  205. package/src/ai/mcp/data.ts +6 -0
  206. package/src/ai/openapi-components.ts +9 -3
  207. package/src/ai/site-skill.ts +274 -0
  208. package/src/ai/skills.ts +35 -2
  209. package/src/ai/write-skill.ts +97 -0
  210. package/src/astro/generate.ts +132 -18
  211. package/src/astro/index.ts +1 -0
  212. package/src/astro/integration.ts +35 -0
  213. package/src/astro/module-types.ts +6 -0
  214. package/src/astro/templates.ts +283 -30
  215. package/src/astro/variables.ts +31 -0
  216. package/src/audit/catalog.ts +19 -0
  217. package/src/audit/checks/contrast.ts +183 -0
  218. package/src/audit/checks/i18n.ts +2 -3
  219. package/src/audit/checks/indexability.ts +2 -3
  220. package/src/audit/checks/links.ts +5 -12
  221. package/src/audit/checks/sitemap.ts +2 -3
  222. package/src/audit/redirects.ts +46 -15
  223. package/src/audit/run.ts +2 -0
  224. package/src/audit/types.ts +2 -1
  225. package/src/blume-modules.d.ts +7 -0
  226. package/src/captcha/client.ts +152 -0
  227. package/src/captcha/hcaptcha.ts +36 -0
  228. package/src/captcha/index.ts +28 -0
  229. package/src/captcha/schema.ts +32 -0
  230. package/src/captcha/turnstile.ts +38 -0
  231. package/src/captcha/verify.ts +71 -0
  232. package/src/cli/command-meta.ts +5 -0
  233. package/src/cli/commands/doctor.ts +22 -16
  234. package/src/cli/commands/skill.ts +96 -0
  235. package/src/cli/index.ts +5 -0
  236. package/src/cli/required-secrets.ts +71 -29
  237. package/src/components/BlumePage.astro +2 -0
  238. package/src/components/content/ApiEndpoint.astro +18 -0
  239. package/src/components/content/ApiField.astro +79 -0
  240. package/src/components/content/ApiPlayground.astro +62 -0
  241. package/src/components/content/ApiRail.astro +19 -0
  242. package/src/components/content/Color.astro +1 -0
  243. package/src/components/content/FileTree.astro +1 -0
  244. package/src/components/content/GithubInfo.astro +1 -0
  245. package/src/components/content/Panel.astro +1 -0
  246. package/src/components/content/ParamField.astro +25 -0
  247. package/src/components/content/RequestExample.astro +17 -0
  248. package/src/components/content/ResponseExample.astro +17 -0
  249. package/src/components/content/ResponseField.astro +32 -0
  250. package/src/components/content/Step.astro +1 -0
  251. package/src/components/content/Tile.astro +1 -0
  252. package/src/components/content/Tooltip.astro +1 -0
  253. package/src/components/content/Tree.astro +1 -0
  254. package/src/components/content/TypeTable.astro +4 -1
  255. package/src/components/content/View.astro +21 -0
  256. package/src/components/content/ViewSwitcher.astro +168 -0
  257. package/src/components/content/api-field.ts +100 -0
  258. package/src/components/content/api-page.ts +224 -0
  259. package/src/components/content/view-switcher.ts +71 -0
  260. package/src/components/index.ts +2 -0
  261. package/src/components/islands/Assistant.astro +10 -1
  262. package/src/components/islands/assistant.tsx +104 -14
  263. package/src/components/islands/code-question.ts +62 -0
  264. package/src/components/islands/hooks.ts +110 -21
  265. package/src/components/islands/support-link.ts +68 -0
  266. package/src/components/layout/Analytics.astro +11 -2
  267. package/src/components/layout/Banner.astro +14 -10
  268. package/src/components/layout/Breadcrumbs.astro +6 -2
  269. package/src/components/layout/ConsentBanner.astro +62 -0
  270. package/src/components/layout/ConsentHead.astro +38 -0
  271. package/src/components/layout/Directory.astro +94 -0
  272. package/src/components/layout/DrawerSwitchers.astro +51 -0
  273. package/src/components/layout/Empty.astro +2 -2
  274. package/src/components/layout/Header.astro +122 -80
  275. package/src/components/layout/LanguageSwitcher.astro +48 -15
  276. package/src/components/layout/NarrationPlayer.astro +1085 -0
  277. package/src/components/layout/NavSelector.astro +33 -7
  278. package/src/components/layout/PageFeedback.astro +54 -49
  279. package/src/components/layout/PageLayout.astro +71 -3
  280. package/src/components/layout/ReferenceLayout.astro +17 -6
  281. package/src/components/layout/RelatedPages.astro +42 -0
  282. package/src/components/layout/RootLayout.astro +308 -45
  283. package/src/components/layout/Search.astro +31 -17
  284. package/src/components/layout/SiteFooter.astro +124 -0
  285. package/src/components/layout/SiteMetatags.astro +16 -0
  286. package/src/components/layout/consent/native.ts +79 -0
  287. package/src/components/layout/consent/types.ts +37 -0
  288. package/src/components/layout/footer-links.ts +53 -0
  289. package/src/components/layout/footer-socials.ts +87 -0
  290. package/src/components/layout/locale-redirect.ts +107 -0
  291. package/src/components/layout/nav-utils.ts +83 -0
  292. package/src/components/layout/page-feedback.ts +96 -0
  293. package/src/components/layout/search/analytics.ts +98 -0
  294. package/src/components/layout/search/flexsearch.ts +17 -5
  295. package/src/components/layout/search/orama-cloud.ts +26 -14
  296. package/src/components/layout/search/types.ts +20 -0
  297. package/src/components/layout/search/typesense.ts +5 -1
  298. package/src/components/openapi/Operation.astro +161 -13
  299. package/src/components/openapi/RequestBody.astro +6 -4
  300. package/src/components/openapi/RequestPanel.astro +59 -7
  301. package/src/components/openapi/Responses.astro +6 -4
  302. package/src/components/openapi/async-snippets.ts +4 -4
  303. package/src/components/openapi/code-samples.ts +97 -0
  304. package/src/components/openapi/security.ts +6 -3
  305. package/src/components/openapi/snippets.ts +869 -8
  306. package/src/components/props.ts +6 -0
  307. package/src/consent/client.ts +95 -0
  308. package/src/consent/clients.ts +19 -0
  309. package/src/consent/ethyca.ts +71 -0
  310. package/src/consent/head.ts +24 -0
  311. package/src/consent/index.ts +37 -0
  312. package/src/consent/init.ts +29 -0
  313. package/src/consent/native.ts +36 -0
  314. package/src/consent/osano.ts +59 -0
  315. package/src/consent/schema.ts +30 -0
  316. package/src/core/builtin-tags.ts +5 -0
  317. package/src/core/client-address.ts +19 -0
  318. package/src/core/config-input.ts +194 -20
  319. package/src/core/data.ts +44 -4
  320. package/src/core/diagnostics.ts +3 -2
  321. package/src/core/footer.ts +26 -0
  322. package/src/core/graph.ts +39 -36
  323. package/src/core/i18n-ui.ts +82 -2
  324. package/src/core/includes.ts +20 -3
  325. package/src/core/links.ts +16 -2
  326. package/src/core/localizable.ts +26 -0
  327. package/src/core/navigation.ts +43 -12
  328. package/src/core/page-modes.ts +56 -0
  329. package/src/core/project-graph.ts +111 -5
  330. package/src/core/redirect-patterns.ts +491 -0
  331. package/src/core/related.ts +131 -0
  332. package/src/core/schema.ts +318 -64
  333. package/src/core/sources/normalize.ts +2 -1
  334. package/src/core/sources/read.ts +8 -3
  335. package/src/core/types.ts +1 -1
  336. package/src/core/ui-packs/ar.ts +43 -0
  337. package/src/core/ui-packs/bg.ts +45 -0
  338. package/src/core/ui-packs/bn.ts +43 -0
  339. package/src/core/ui-packs/ca.ts +46 -0
  340. package/src/core/ui-packs/cs.ts +44 -0
  341. package/src/core/ui-packs/da.ts +45 -0
  342. package/src/core/ui-packs/de.ts +46 -0
  343. package/src/core/ui-packs/el.ts +45 -0
  344. package/src/core/ui-packs/es.ts +46 -0
  345. package/src/core/ui-packs/fa.ts +45 -0
  346. package/src/core/ui-packs/fi.ts +45 -0
  347. package/src/core/ui-packs/fr.ts +46 -0
  348. package/src/core/ui-packs/he.ts +43 -0
  349. package/src/core/ui-packs/hi.ts +44 -0
  350. package/src/core/ui-packs/hr.ts +45 -0
  351. package/src/core/ui-packs/hu.ts +44 -0
  352. package/src/core/ui-packs/id.ts +45 -0
  353. package/src/core/ui-packs/it.ts +45 -0
  354. package/src/core/ui-packs/ja.ts +45 -0
  355. package/src/core/ui-packs/ko.ts +44 -0
  356. package/src/core/ui-packs/nl.ts +46 -0
  357. package/src/core/ui-packs/no.ts +44 -0
  358. package/src/core/ui-packs/pl.ts +45 -0
  359. package/src/core/ui-packs/pt-br.ts +46 -0
  360. package/src/core/ui-packs/pt.ts +45 -0
  361. package/src/core/ui-packs/ro.ts +45 -0
  362. package/src/core/ui-packs/ru.ts +45 -0
  363. package/src/core/ui-packs/sk.ts +44 -0
  364. package/src/core/ui-packs/sr.ts +45 -0
  365. package/src/core/ui-packs/sv.ts +44 -0
  366. package/src/core/ui-packs/th.ts +43 -0
  367. package/src/core/ui-packs/tr.ts +45 -0
  368. package/src/core/ui-packs/uk.ts +45 -0
  369. package/src/core/ui-packs/vi.ts +45 -0
  370. package/src/core/ui-packs/zh-tw.ts +43 -0
  371. package/src/core/ui-packs/zh.ts +43 -0
  372. package/src/core/variables.ts +103 -0
  373. package/src/deploy/artifacts.ts +77 -8
  374. package/src/deploy/cloudflare-negotiation.ts +35 -5
  375. package/src/deploy/headers.ts +2 -1
  376. package/src/deploy/node-headers.ts +63 -9
  377. package/src/deploy/platforms/cloudflare.ts +32 -0
  378. package/src/deploy/platforms/netlify.ts +33 -1
  379. package/src/deploy/platforms/vercel.ts +37 -2
  380. package/src/deploy/redirects.ts +91 -24
  381. package/src/deploy/vercel-negotiation.ts +36 -0
  382. package/src/markdown/api-rail.ts +259 -0
  383. package/src/markdown/code-title.ts +35 -9
  384. package/src/markdown/features.ts +3 -2
  385. package/src/markdown/fence-meta.ts +2 -0
  386. package/src/markdown/include.ts +11 -1
  387. package/src/markdown/index.ts +28 -5
  388. package/src/markdown/math.ts +4 -4
  389. package/src/markdown/variables.ts +56 -0
  390. package/src/markdown/views.ts +82 -0
  391. package/src/narration/build.ts +412 -0
  392. package/src/narration/player.ts +249 -0
  393. package/src/narration/provider.ts +29 -0
  394. package/src/narration/script.ts +592 -0
  395. package/src/og/card.ts +7 -6
  396. package/src/openapi/model.ts +57 -24
  397. package/src/openapi/overlay.ts +256 -0
  398. package/src/openapi/parse.ts +55 -2
  399. package/src/openapi/references.ts +21 -5
  400. package/src/openapi/render-mdx.ts +5 -1
  401. package/src/openapi/scalar.ts +40 -6
  402. package/src/openapi/source.ts +1 -1
  403. package/src/ratelimit/cloudflare.ts +54 -0
  404. package/src/ratelimit/index.ts +30 -0
  405. package/src/ratelimit/memory.ts +45 -0
  406. package/src/ratelimit/runtime.ts +229 -0
  407. package/src/ratelimit/schema.ts +28 -0
  408. package/src/ratelimit/upstash.ts +30 -0
  409. package/src/ratelimit/wrangler.ts +70 -0
  410. package/src/reference/asyncapi.ts +2 -2
  411. package/src/reference/graphql.ts +5 -2
  412. package/src/reference/openapi.ts +21 -7
  413. package/src/reference/options.ts +51 -7
  414. package/src/reference/scalar.ts +33 -8
  415. package/src/registry/eject.ts +20 -4
  416. package/src/registry/registry.ts +7 -0
  417. package/src/search/documents.ts +26 -2
  418. package/src/search/orama-index.ts +28 -3
  419. package/src/search/pagefind-ranking.ts +39 -0
  420. package/src/search/sync/algolia.ts +15 -4
  421. package/src/search/sync/orama-cloud.ts +3 -0
  422. package/src/search/sync/typesense.ts +5 -0
  423. package/src/seo/metatags.ts +67 -0
  424. package/src/theme/chrome-icons.ts +1 -0
  425. package/src/theme/color.ts +500 -0
  426. package/src/theme/entry.ts +56 -3
  427. package/src/theme/palette.ts +121 -45
  428. package/src/translate/prompts.ts +1 -0
  429. package/src/translate/validate.ts +8 -2
  430. package/src/upgrade/upgrade.ts +4 -171
  431. package/dist/cli/chunk-364znk6q.js +0 -75
  432. package/dist/cli/chunk-364znk6q.js.map +0 -11
  433. package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
  434. package/dist/cli/chunk-9t7a85s3.js.map +0 -10
  435. package/dist/cli/chunk-c8chx29p.js.map +0 -36
  436. package/dist/cli/chunk-pehfxfta.js.map +0 -10
  437. package/dist/cli/chunk-pv29h0wf.js.map +0 -190
  438. package/dist/cli/chunk-q58y5e6a.js.map +0 -10
  439. package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
  440. package/dist/cli/chunk-yfyb25rh.js.map +0 -14
  441. package/docs/content/sources.mdx +0 -334
  442. /package/dist/cli/{chunk-2eytanqx.js.map → chunk-1n228xqb.js.map} +0 -0
  443. /package/dist/cli/{chunk-35d4wj9f.js.map → chunk-2n4ps1b3.js.map} +0 -0
  444. /package/dist/cli/{chunk-e04dxsz1.js.map → chunk-4ejxbzee.js.map} +0 -0
  445. /package/dist/cli/{chunk-tzmab476.js.map → chunk-5ffywsmb.js.map} +0 -0
  446. /package/dist/cli/{chunk-2z928egk.js.map → chunk-5wj2qwn2.js.map} +0 -0
  447. /package/dist/cli/{chunk-b07cmahc.js.map → chunk-amrb6hgh.js.map} +0 -0
  448. /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-cmtcnh9f.js.map} +0 -0
  449. /package/dist/cli/{chunk-crgn1q09.js.map → chunk-e7vvk5ya.js.map} +0 -0
  450. /package/dist/cli/{chunk-tkacnehg.js.map → chunk-gp84za0h.js.map} +0 -0
  451. /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-p804f3pe.js.map} +0 -0
  452. /package/dist/cli/{chunk-8ktnccpt.js.map → chunk-ta27feev.js.map} +0 -0
  453. /package/dist/cli/{chunk-a58773jm.js.map → chunk-y4ykbark.js.map} +0 -0
  454. /package/dist/cli/{chunk-p3v96n38.js.map → chunk-y5h4r2y0.js.map} +0 -0
  455. /package/dist/cli/{chunk-acanzt5p.js.map → chunk-yj17vs39.js.map} +0 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The assistant's handoff to your support channel (`ai.assistant.support`).
3
+ * A `mailto:` link starts an email with the conversation as its body, so the
4
+ * reader doesn't have to repeat themselves; any other link gets the
5
+ * conversation's `thread` id in its query, the same id the assistant's
6
+ * analytics events carry, so a support team can find the conversation.
7
+ */
8
+
9
+ /** One turn of the conversation. */
10
+ export interface SupportMessage {
11
+ content: string;
12
+ role: "assistant" | "user";
13
+ }
14
+
15
+ /** What the link is built from. */
16
+ export interface SupportContext {
17
+ /** The label the transcript gives the assistant's turns. */
18
+ ai: string;
19
+ messages: readonly SupportMessage[];
20
+ /** The email's subject. */
21
+ subject: string;
22
+ thread: string;
23
+ /** The label the transcript gives the reader's turns. */
24
+ you: string;
25
+ }
26
+
27
+ /**
28
+ * How much conversation an email carries: mail clients cut long `mailto:`
29
+ * links, so a long conversation is trimmed from the start, keeping the
30
+ * latest turns.
31
+ */
32
+ export const MAX_TRANSCRIPT_CHARS = 1500;
33
+
34
+ const transcriptOf = (context: SupportContext): string => {
35
+ const full = context.messages
36
+ .map(
37
+ (message) =>
38
+ `${message.role === "user" ? context.you : context.ai}: ${message.content}`
39
+ )
40
+ .join("\n\n");
41
+ return full.length > MAX_TRANSCRIPT_CHARS
42
+ ? `…${full.slice(-MAX_TRANSCRIPT_CHARS)}`
43
+ : full;
44
+ };
45
+
46
+ /** The support link for this conversation. */
47
+ export const supportHref = (
48
+ support: string,
49
+ context: SupportContext
50
+ ): string => {
51
+ if (support.startsWith("mailto:")) {
52
+ // `mailto:` wants %20, not the `+` URLSearchParams writes for a space.
53
+ const body = `${transcriptOf(context)}\n\n(${context.thread})`;
54
+ const separator = support.includes("?") ? "&" : "?";
55
+ return `${support}${separator}subject=${encodeURIComponent(context.subject)}&body=${encodeURIComponent(body)}`;
56
+ }
57
+ const url = new URL(support, "https://blume.invalid");
58
+ url.searchParams.set("thread", context.thread);
59
+ return url.origin === "https://blume.invalid"
60
+ ? `${url.pathname}${url.search}${url.hash}`
61
+ : url.href;
62
+ };
63
+
64
+ /** A fresh conversation id: 16 hex characters. */
65
+ export const newThreadId = (): string =>
66
+ Array.from(crypto.getRandomValues(new Uint8Array(8)), (byte) =>
67
+ byte.toString(16).padStart(2, "0")
68
+ ).join("");
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import VercelAnalytics from "@vercel/analytics/astro";
3
+ import data from "blume:data";
3
4
 
4
5
  import { analyticsHead } from "../../analytics/head.ts";
5
6
  import type { BlumeDataConfig } from "../../core/data.ts";
@@ -10,12 +11,20 @@ import type { BlumeDataConfig } from "../../core/data.ts";
10
11
  // component for `vercel()`, one <script> for every other adapter. Loads ONLY
11
12
  // in production builds so `blume dev` stays clean and local traffic never
12
13
  // reaches your analytics.
14
+ //
15
+ // With `consent` configured, every tag is held as inert `text/plain` until
16
+ // the reader allows analytics, when the consent runtime runs it
17
+ // (`consent/client.ts`); Vercel's component sends nothing until then instead
18
+ // (`consent/init.ts`).
13
19
  interface Props {
14
20
  analytics?: BlumeDataConfig["analytics"] | null;
15
21
  }
16
22
 
17
23
  const { analytics } = Astro.props;
18
24
  const nodes = analyticsHead(import.meta.env.PROD ? (analytics ?? []) : []);
25
+ const held = data.config.consent
26
+ ? { "data-blume-consent": "analytics", type: "text/plain" }
27
+ : {};
19
28
  ---
20
29
 
21
30
  {
@@ -24,9 +33,9 @@ const nodes = analyticsHead(import.meta.env.PROD ? (analytics ?? []) : []);
24
33
  return <VercelAnalytics {...node.props} />;
25
34
  }
26
35
  return node.content === null ? (
27
- <script is:inline {...node.attributes} />
36
+ <script is:inline {...node.attributes} {...held} />
28
37
  ) : (
29
- <script is:inline set:html={node.content} {...node.attributes} />
38
+ <script is:inline set:html={node.content} {...node.attributes} {...held} />
30
39
  );
31
40
  })
32
41
  }
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  import data from "blume:data";
3
+ import type { BlumeBanner } from "../../core/data.ts";
3
4
  import { EN_UI } from "../../core/i18n-ui.ts";
4
5
  import type { UIStrings } from "../../core/i18n-ui.ts";
6
+ import { resolveLocalizable } from "../../core/localizable.ts";
5
7
  import { withBase } from "../islands/base-path.ts";
6
8
  import Icon from "../Icon.astro";
7
9
 
@@ -9,14 +11,11 @@ import Icon from "../Icon.astro";
9
11
  // every shell shows the same bar. The pre-paint hide for a dismissed banner and
10
12
  // the dismiss click handler live in the layout/Header, not here.
11
13
  interface Props {
12
- banner:
13
- | {
14
- content: string;
15
- link?: { text: string; href: string };
16
- dismissible: boolean;
17
- key: string;
18
- }
19
- | null;
14
+ /**
15
+ * The banner. Its `content` and link `text` may be per-locale maps, shown
16
+ * in `locale` (else the default locale, else the map's first entry).
17
+ */
18
+ banner: BlumeBanner | null;
20
19
  /** Localized banner labels. */
21
20
  strings?: UIStrings["banner"];
22
21
  /**
@@ -40,6 +39,11 @@ const b = { ...EN_UI.banner, ...strings };
40
39
  const navigation =
41
40
  (locale === undefined ? undefined : data.navigationByLocale[locale]) ??
42
41
  data.navigation;
42
+ const defaultLocale = data.config.i18n?.defaultLocale;
43
+ const content =
44
+ banner && resolveLocalizable(banner.content, locale, defaultLocale);
45
+ const linkText =
46
+ banner?.link && resolveLocalizable(banner.link.text, locale, defaultLocale);
43
47
  const linkHref =
44
48
  banner?.link && banner.link.href === data.config.banner?.link?.href
45
49
  ? (navigation.bannerHref ?? banner.link.href)
@@ -55,13 +59,13 @@ const linkHref =
55
59
  >
56
60
  {/* The message is authored in one language, whatever the page's
57
61
  direction: `dir="auto"` keeps its punctuation where it was typed. */}
58
- <span dir="auto">{banner.content}</span>
62
+ <span dir="auto">{content}</span>
59
63
  {banner.link && linkHref && (
60
64
  <a
61
65
  class="inline-flex items-center gap-1 font-medium underline underline-offset-2"
62
66
  href={withBase(linkHref)}
63
67
  >
64
- {banner.link.text}
68
+ {linkText}
65
69
  <Icon class="rtl:-scale-x-100" name="arrow-right" size={14} />
66
70
  </a>
67
71
  )}
@@ -11,9 +11,11 @@ interface Props {
11
11
  strings?: UIStrings["nav"];
12
12
  /** Left-align full width (for the wide API layout) instead of the prose measure. */
13
13
  wide?: boolean;
14
+ /** Match a `center` page's wider centered column instead of the prose measure. */
15
+ centered?: boolean;
14
16
  }
15
17
 
16
- const { crumbs, strings, wide = false } = Astro.props;
18
+ const { crumbs, strings, wide = false, centered = false } = Astro.props;
17
19
 
18
20
  // Merge over the English defaults so a label missing from a translation (or
19
21
  // from a not-yet-regenerated snapshot) still renders instead of coming out
@@ -31,7 +33,9 @@ const eyebrowCrumb = crumbs.length > 1 ? crumbs[crumbs.length - 2] : null;
31
33
  aria-label={n.breadcrumb}
32
34
  class:list={[
33
35
  "mb-2 text-muted-foreground text-sm",
34
- wide ? "max-w-none" : "mx-auto max-w-content",
36
+ wide
37
+ ? "max-w-none"
38
+ : ["mx-auto", centered ? "max-w-[54rem]" : "max-w-content"],
35
39
  ]}
36
40
  >
37
41
  {eyebrowCrumb.route ? (
@@ -0,0 +1,62 @@
1
+ ---
2
+ import { EN_UI } from "../../core/i18n-ui.ts";
3
+ import type { UIStrings } from "../../core/i18n-ui.ts";
4
+ import { newTabAttrs } from "../../core/new-tab.ts";
5
+ import { withBase } from "../islands/base-path.ts";
6
+
7
+ // The `native()` consent banner: a card at the foot of the viewport asking to
8
+ // use analytics cookies. It renders hidden, and the consent runtime
9
+ // (`consent/client.ts`) shows it until the reader answers, stores the answer,
10
+ // and shows it again from the footer's Cookie settings link. Accept and
11
+ // Decline carry the same weight, so declining is as easy as accepting.
12
+ interface Props {
13
+ /** The privacy policy link: a page route or a URL. */
14
+ policy?: string;
15
+ /** Localized banner labels. */
16
+ strings?: UIStrings["consent"];
17
+ }
18
+
19
+ const { policy, strings } = Astro.props;
20
+ const c = { ...EN_UI.consent, ...strings };
21
+ const buttonClass =
22
+ "flex-1 rounded-blume border border-border px-4 py-2 text-foreground text-sm transition-colors hover:border-foreground";
23
+ ---
24
+
25
+ <div
26
+ aria-label={c.label}
27
+ class="fixed inset-x-4 bottom-4 z-50 rounded-blume border border-border bg-background p-4 shadow-lg sm:start-auto sm:end-4 sm:w-96"
28
+ data-blume-consent-banner
29
+ hidden
30
+ role="region"
31
+ >
32
+ <p class="text-foreground text-sm">
33
+ {c.message}{" "}
34
+ {
35
+ policy && (
36
+ <a
37
+ class="text-muted-foreground underline underline-offset-2 transition-colors hover:text-foreground"
38
+ href={withBase(policy)}
39
+ {...newTabAttrs(policy)}
40
+ >
41
+ {c.policy}
42
+ </a>
43
+ )
44
+ }
45
+ </p>
46
+ <div class="mt-3 flex gap-2">
47
+ <button
48
+ class={buttonClass}
49
+ data-blume-consent-choice="decline"
50
+ type="button"
51
+ >
52
+ {c.decline}
53
+ </button>
54
+ <button
55
+ class={buttonClass}
56
+ data-blume-consent-choice="accept"
57
+ type="button"
58
+ >
59
+ {c.accept}
60
+ </button>
61
+ </div>
62
+ </div>
@@ -0,0 +1,38 @@
1
+ ---
2
+ import { consentHead } from "../../consent/head.ts";
3
+ import { CONSENT_INIT_SCRIPT } from "../../consent/init.ts";
4
+ import type { BlumeDataConfig } from "../../core/data.ts";
5
+
6
+ // The consent layer's <head> output, rendered ahead of `Analytics.astro` by
7
+ // every layout when `consent` is configured: the inline script that creates
8
+ // `window.blumeConsent` (`consent/init.ts`), the adapter's own tags — a
9
+ // hosted consent manager's loader and the bridge that reports its answers —
10
+ // and the runtime that runs the held analytics (`consent/client.ts`), which
11
+ // starts the adapter's browser module, if it has one (`blume:consent-client`,
12
+ // see `consent/clients.ts`). Like analytics, a hosted manager loads in
13
+ // production builds only; a browser module (`native()`'s banner) works in
14
+ // `blume dev` too.
15
+ interface Props {
16
+ consent: NonNullable<BlumeDataConfig["consent"]>;
17
+ }
18
+
19
+ const { consent } = Astro.props;
20
+ const tags = import.meta.env.PROD ? consentHead(consent) : [];
21
+ ---
22
+
23
+ <script data-kind={consent.kind} is:inline set:html={CONSENT_INIT_SCRIPT} />
24
+ {
25
+ tags.map((tag) =>
26
+ tag.content === null ? (
27
+ <script is:inline {...tag.attributes} />
28
+ ) : (
29
+ <script is:inline set:html={tag.content} {...tag.attributes} />
30
+ )
31
+ )
32
+ }
33
+ <script>
34
+ import { startConsentClient } from "blume:consent-client";
35
+ import { startConsent } from "../../consent/client.ts";
36
+
37
+ startConsent(startConsentClient);
38
+ </script>
@@ -0,0 +1,94 @@
1
+ ---
2
+ import AccordionItem from "../content/AccordionItem.astro";
3
+ import Card from "../content/Card.astro";
4
+ import CardGroup from "../content/CardGroup.astro";
5
+ import Icon from "../Icon.astro";
6
+ import { directoryEntries } from "./nav-utils.ts";
7
+ import type { NavGroup } from "./nav-utils.ts";
8
+
9
+ /**
10
+ * A group page's directory: the group's other pages listed below the page's
11
+ * content, as cards or as an accordion whose subgroups open to their pages
12
+ * (the group's `directory`, see `core/schema.ts`).
13
+ */
14
+ interface Props {
15
+ group: NavGroup;
16
+ }
17
+
18
+ const { group } = Astro.props;
19
+ const entries = directoryEntries(group);
20
+ ---
21
+
22
+ {
23
+ entries.length > 0 && (
24
+ <nav
25
+ aria-label={group.label}
26
+ class="mx-auto mt-6 max-w-content"
27
+ data-blume-directory={group.directory}
28
+ >
29
+ {group.directory === "card" ? (
30
+ <CardGroup>
31
+ {entries.map((entry) => (
32
+ <Card href={entry.href} icon={entry.icon} title={entry.label}>
33
+ {entry.description}
34
+ </Card>
35
+ ))}
36
+ </CardGroup>
37
+ ) : (
38
+ <div class="not-prose my-6 divide-y divide-border overflow-hidden rounded-blume border border-border">
39
+ {entries.map((entry, index) =>
40
+ entry.pages ? (
41
+ <AccordionItem
42
+ description={entry.description}
43
+ icon={entry.icon}
44
+ id={`blume-directory-${index + 1}`}
45
+ title={entry.label}
46
+ >
47
+ <ul class="grid gap-2 text-sm">
48
+ {entry.pages.map((page) => (
49
+ <li>
50
+ <a
51
+ class="text-muted-foreground no-underline transition-colors hover:text-foreground"
52
+ href={page.route}
53
+ >
54
+ {page.label}
55
+ </a>
56
+ </li>
57
+ ))}
58
+ </ul>
59
+ </AccordionItem>
60
+ ) : (
61
+ <a
62
+ class="flex items-center justify-between gap-4 p-4 font-medium text-foreground no-underline transition-colors hover:bg-muted/40"
63
+ href={entry.href}
64
+ >
65
+ <span class="flex items-start gap-2">
66
+ {entry.icon && (
67
+ <Icon
68
+ class="mt-0.5 shrink-0 text-muted-foreground"
69
+ name={entry.icon}
70
+ size={16}
71
+ />
72
+ )}
73
+ <span>
74
+ <span class="block">{entry.label}</span>
75
+ {entry.description && (
76
+ <span class="block font-normal text-muted-foreground text-sm">
77
+ {entry.description}
78
+ </span>
79
+ )}
80
+ </span>
81
+ </span>
82
+ <Icon
83
+ class="shrink-0 text-muted-foreground rtl:-scale-x-100"
84
+ name="arrow-right"
85
+ size={16}
86
+ />
87
+ </a>
88
+ )
89
+ )}
90
+ </div>
91
+ )}
92
+ </nav>
93
+ )
94
+ }
@@ -0,0 +1,51 @@
1
+ ---
2
+ // The header's selectors, version selector, and language switcher, as a row
3
+ // at the top of the nav drawer. Below `lg` the sidebar is this drawer, and the
4
+ // header has no room for them beside the logo, tabs, search, and icon
5
+ // buttons, so it hides them whenever there's a drawer to open and this row
6
+ // takes over; from `lg` up the row hides and the header shows them again.
7
+ import type { UIStrings } from "../../core/i18n-ui.ts";
8
+ import type {
9
+ LocaleSwitchOption,
10
+ NavSelector as NavSelectorConfig,
11
+ } from "../../core/types.ts";
12
+ import LanguageSwitcher from "./LanguageSwitcher.astro";
13
+ import NavSelector from "./NavSelector.astro";
14
+
15
+ interface Props {
16
+ selectors: NavSelectorConfig[];
17
+ versionSelector?: NavSelectorConfig | null;
18
+ localeSwitch?: LocaleSwitchOption[];
19
+ route: string;
20
+ switcherStrings?: UIStrings["languageSwitcher"];
21
+ }
22
+
23
+ const { selectors, versionSelector, localeSwitch, route, switcherStrings } =
24
+ Astro.props;
25
+ const hasLocales = (localeSwitch?.length ?? 0) > 1;
26
+ ---
27
+
28
+ {
29
+ (selectors.length > 0 || versionSelector || hasLocales) && (
30
+ <div class="mb-4 flex flex-col gap-2 border-border border-b pb-4 lg:hidden">
31
+ {selectors.map((selector) => (
32
+ <NavSelector route={route} selector={selector} variant="drawer" />
33
+ ))}
34
+ {versionSelector && (
35
+ <NavSelector
36
+ route={route}
37
+ selector={versionSelector}
38
+ variant="drawer"
39
+ />
40
+ )}
41
+ {localeSwitch && hasLocales && (
42
+ <LanguageSwitcher
43
+ label={switcherStrings?.label ?? "Language"}
44
+ options={localeSwitch}
45
+ untranslatedLabel={switcherStrings?.untranslated ?? "Not translated"}
46
+ variant="drawer"
47
+ />
48
+ )}
49
+ </div>
50
+ )
51
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  // A no-op layout slot: renders nothing regardless of props. Used as the built-in
3
- // fallback for the content-injection slots (Footer, PageHeader, PageFooter) that
4
- // have no default, so an unconfigured slot renders empty while `resolveSlot`
3
+ // fallback for the content-injection slots (PageHeader, PageFooter) that have
4
+ // no default, so an unconfigured slot renders empty while `resolveSlot`
5
5
  // still returns a valid component and the generated pages stay type-safe.
6
6
  ---