blume 0.0.0 → 0.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 (263) hide show
  1. package/bin/blume.mjs +18 -0
  2. package/dist/cli/index.js +11989 -0
  3. package/dist/cli/index.js.map +141 -0
  4. package/docs/01-quickstart.mdx +99 -0
  5. package/docs/02-deployment.mdx +129 -0
  6. package/docs/advanced/api-reference.mdx +114 -0
  7. package/docs/advanced/blog.mdx +121 -0
  8. package/docs/advanced/changelog.mdx +113 -0
  9. package/docs/advanced/custom-pages.mdx +187 -0
  10. package/docs/advanced/meta.ts +7 -0
  11. package/docs/changelog/v0-1-0.mdx +12 -0
  12. package/docs/changelog/v0-2-0.mdx +16 -0
  13. package/docs/configuration/ai.mdx +228 -0
  14. package/docs/configuration/analytics.mdx +98 -0
  15. package/docs/configuration/customization.mdx +91 -0
  16. package/docs/configuration/export.mdx +70 -0
  17. package/docs/configuration/index.mdx +271 -0
  18. package/docs/configuration/meta.ts +15 -0
  19. package/docs/configuration/search.mdx +172 -0
  20. package/docs/configuration/seo.mdx +196 -0
  21. package/docs/configuration/theming.mdx +178 -0
  22. package/docs/content/components.mdx +565 -0
  23. package/docs/content/i18n.mdx +205 -0
  24. package/docs/content/index.mdx +161 -0
  25. package/docs/content/islands.mdx +94 -0
  26. package/docs/content/meta.mdx +119 -0
  27. package/docs/content/meta.ts +15 -0
  28. package/docs/content/navigation.mdx +168 -0
  29. package/docs/content/sources.mdx +216 -0
  30. package/docs/content/syntax.mdx +445 -0
  31. package/docs/index.mdx +112 -0
  32. package/docs/reference/cli.mdx +43 -0
  33. package/docs/reference/frontmatter.mdx +74 -0
  34. package/docs/reference/meta.ts +7 -0
  35. package/package.json +140 -6
  36. package/src/ai/ask.ts +93 -0
  37. package/src/ai/llms.ts +65 -0
  38. package/src/ai/markdown.ts +31 -0
  39. package/src/ai/mcp/data.ts +74 -0
  40. package/src/ai/mcp/discovery.ts +49 -0
  41. package/src/ai/mcp/server.ts +225 -0
  42. package/src/ai/mcp/tools.ts +47 -0
  43. package/src/assets/icon.png +0 -0
  44. package/src/astro/generate.ts +878 -0
  45. package/src/astro/index.ts +4 -0
  46. package/src/astro/integration.ts +74 -0
  47. package/src/astro/islands.ts +131 -0
  48. package/src/astro/markdown-negotiation.ts +68 -0
  49. package/src/astro/pages.ts +28 -0
  50. package/src/astro/templates.ts +1199 -0
  51. package/src/cli/commands/add.ts +81 -0
  52. package/src/cli/commands/build.ts +103 -0
  53. package/src/cli/commands/dev.ts +108 -0
  54. package/src/cli/commands/doctor.ts +74 -0
  55. package/src/cli/commands/eject.ts +57 -0
  56. package/src/cli/commands/init.ts +98 -0
  57. package/src/cli/commands/migrate.ts +39 -0
  58. package/src/cli/commands/preview.ts +39 -0
  59. package/src/cli/commands/sync.ts +52 -0
  60. package/src/cli/commands/validate.ts +60 -0
  61. package/src/cli/index.ts +35 -0
  62. package/src/cli/log.ts +37 -0
  63. package/src/cli/prepare.ts +80 -0
  64. package/src/components/Icon.astro +99 -0
  65. package/src/components/content/Accordion.astro +8 -0
  66. package/src/components/content/AccordionItem.astro +121 -0
  67. package/src/components/content/AutoTypeTable.astro +51 -0
  68. package/src/components/content/Badge.astro +124 -0
  69. package/src/components/content/Callout.astro +73 -0
  70. package/src/components/content/Card.astro +104 -0
  71. package/src/components/content/CardGroup.astro +14 -0
  72. package/src/components/content/CodeGroup.astro +13 -0
  73. package/src/components/content/Color.astro +15 -0
  74. package/src/components/content/ColorItem.astro +87 -0
  75. package/src/components/content/ColorRow.astro +10 -0
  76. package/src/components/content/Column.astro +6 -0
  77. package/src/components/content/Columns.astro +9 -0
  78. package/src/components/content/Expandable.astro +11 -0
  79. package/src/components/content/FileTree.astro +8 -0
  80. package/src/components/content/Frame.astro +70 -0
  81. package/src/components/content/GithubInfo.astro +110 -0
  82. package/src/components/content/Math.astro +24 -0
  83. package/src/components/content/Panel.astro +20 -0
  84. package/src/components/content/Prompt.astro +129 -0
  85. package/src/components/content/Step.astro +34 -0
  86. package/src/components/content/Steps.astro +20 -0
  87. package/src/components/content/Tab.astro +40 -0
  88. package/src/components/content/Tabs.astro +273 -0
  89. package/src/components/content/Tile.astro +42 -0
  90. package/src/components/content/Tooltip.astro +68 -0
  91. package/src/components/content/Tree.astro +300 -0
  92. package/src/components/content/TreeFile.astro +15 -0
  93. package/src/components/content/TreeFolder.astro +62 -0
  94. package/src/components/content/TypeTable.astro +106 -0
  95. package/src/components/content/Update.astro +66 -0
  96. package/src/components/content/Visibility.astro +12 -0
  97. package/src/components/content/Warning.astro +9 -0
  98. package/src/components/content/auto-type-table.ts +141 -0
  99. package/src/components/content/github-info.ts +79 -0
  100. package/src/components/content/mermaid-element.ts +68 -0
  101. package/src/components/github-mark.ts +9 -0
  102. package/src/components/index.ts +14 -0
  103. package/src/components/islands/AskAI.astro +12 -0
  104. package/src/components/islands/ask-ai.tsx +156 -0
  105. package/src/components/layout/Analytics.astro +63 -0
  106. package/src/components/layout/Banner.astro +50 -0
  107. package/src/components/layout/Breadcrumbs.astro +31 -0
  108. package/src/components/layout/Favicon.astro +15 -0
  109. package/src/components/layout/Fonts.astro +14 -0
  110. package/src/components/layout/Header.astro +188 -0
  111. package/src/components/layout/LanguageSwitcher.astro +56 -0
  112. package/src/components/layout/NavTree.astro +462 -0
  113. package/src/components/layout/PageActions.astro +438 -0
  114. package/src/components/layout/PageFeedback.astro +58 -0
  115. package/src/components/layout/Pagination.astro +56 -0
  116. package/src/components/layout/ReferenceLayout.astro +102 -0
  117. package/src/components/layout/RootLayout.astro +533 -0
  118. package/src/components/layout/Search.astro +608 -0
  119. package/src/components/layout/TableOfContents.astro +68 -0
  120. package/src/components/layout/analytics-client.ts +38 -0
  121. package/src/components/layout/nav-utils.ts +87 -0
  122. package/src/components/layout/overrides.ts +32 -0
  123. package/src/components/layout/search/algolia.ts +43 -0
  124. package/src/components/layout/search/endpoint.ts +22 -0
  125. package/src/components/layout/search/flexsearch.ts +52 -0
  126. package/src/components/layout/search/orama-cloud.ts +41 -0
  127. package/src/components/layout/search/orama.ts +26 -0
  128. package/src/components/layout/search/pagefind.ts +43 -0
  129. package/src/components/layout/search/types.ts +163 -0
  130. package/src/components/layout/search/typesense.ts +60 -0
  131. package/src/components/layout/toc-element.ts +108 -0
  132. package/src/core/bridge.ts +92 -0
  133. package/src/core/config.ts +112 -0
  134. package/src/core/content.ts +50 -0
  135. package/src/core/define-components.ts +34 -0
  136. package/src/core/define-meta.ts +20 -0
  137. package/src/core/deployment-env.ts +73 -0
  138. package/src/core/diagnostics.ts +104 -0
  139. package/src/core/graph.ts +128 -0
  140. package/src/core/i18n-ui.ts +171 -0
  141. package/src/core/i18n.ts +169 -0
  142. package/src/core/last-modified.ts +88 -0
  143. package/src/core/links.ts +336 -0
  144. package/src/core/load-module.ts +15 -0
  145. package/src/core/manifest.ts +126 -0
  146. package/src/core/meta.ts +97 -0
  147. package/src/core/navigation.ts +392 -0
  148. package/src/core/package-root.ts +37 -0
  149. package/src/core/project-graph.ts +153 -0
  150. package/src/core/project.ts +56 -0
  151. package/src/core/schema.ts +1057 -0
  152. package/src/core/server-features.ts +23 -0
  153. package/src/core/sources/assets.ts +77 -0
  154. package/src/core/sources/cache.ts +122 -0
  155. package/src/core/sources/filesystem.ts +99 -0
  156. package/src/core/sources/mdx-remote.ts +216 -0
  157. package/src/core/sources/mintlify.ts +161 -0
  158. package/src/core/sources/normalize.ts +227 -0
  159. package/src/core/sources/notion.ts +440 -0
  160. package/src/core/sources/portable-text.ts +143 -0
  161. package/src/core/sources/read.ts +36 -0
  162. package/src/core/sources/resolve.ts +158 -0
  163. package/src/core/sources/sanity.ts +218 -0
  164. package/src/core/sources/types.ts +105 -0
  165. package/src/core/types.ts +261 -0
  166. package/src/core/ui-packs/ar.ts +47 -0
  167. package/src/core/ui-packs/bg.ts +47 -0
  168. package/src/core/ui-packs/bn.ts +47 -0
  169. package/src/core/ui-packs/ca.ts +47 -0
  170. package/src/core/ui-packs/cs.ts +47 -0
  171. package/src/core/ui-packs/da.ts +47 -0
  172. package/src/core/ui-packs/de.ts +47 -0
  173. package/src/core/ui-packs/el.ts +47 -0
  174. package/src/core/ui-packs/es.ts +47 -0
  175. package/src/core/ui-packs/fa.ts +47 -0
  176. package/src/core/ui-packs/fi.ts +47 -0
  177. package/src/core/ui-packs/fr.ts +47 -0
  178. package/src/core/ui-packs/he.ts +47 -0
  179. package/src/core/ui-packs/hi.ts +47 -0
  180. package/src/core/ui-packs/hr.ts +47 -0
  181. package/src/core/ui-packs/hu.ts +47 -0
  182. package/src/core/ui-packs/id.ts +47 -0
  183. package/src/core/ui-packs/index.ts +87 -0
  184. package/src/core/ui-packs/it.ts +47 -0
  185. package/src/core/ui-packs/ja.ts +47 -0
  186. package/src/core/ui-packs/ko.ts +47 -0
  187. package/src/core/ui-packs/nl.ts +47 -0
  188. package/src/core/ui-packs/no.ts +47 -0
  189. package/src/core/ui-packs/pl.ts +47 -0
  190. package/src/core/ui-packs/pt-br.ts +47 -0
  191. package/src/core/ui-packs/pt.ts +47 -0
  192. package/src/core/ui-packs/ro.ts +47 -0
  193. package/src/core/ui-packs/ru.ts +47 -0
  194. package/src/core/ui-packs/sk.ts +47 -0
  195. package/src/core/ui-packs/sr.ts +47 -0
  196. package/src/core/ui-packs/sv.ts +47 -0
  197. package/src/core/ui-packs/th.ts +47 -0
  198. package/src/core/ui-packs/tr.ts +47 -0
  199. package/src/core/ui-packs/uk.ts +47 -0
  200. package/src/core/ui-packs/vi.ts +47 -0
  201. package/src/core/ui-packs/zh-tw.ts +47 -0
  202. package/src/core/ui-packs/zh.ts +47 -0
  203. package/src/core/version.ts +23 -0
  204. package/src/deploy/robots.ts +20 -0
  205. package/src/deploy/rss.ts +128 -0
  206. package/src/deploy/sitemap.ts +28 -0
  207. package/src/index.ts +27 -0
  208. package/src/markdown/code-title.ts +71 -0
  209. package/src/markdown/directives.ts +83 -0
  210. package/src/markdown/heading-anchors.ts +137 -0
  211. package/src/markdown/index.ts +159 -0
  212. package/src/markdown/inline-code.ts +108 -0
  213. package/src/markdown/language-icon.ts +172 -0
  214. package/src/markdown/math.ts +32 -0
  215. package/src/markdown/mdast.ts +48 -0
  216. package/src/markdown/mermaid.ts +37 -0
  217. package/src/markdown/package-commands.ts +159 -0
  218. package/src/markdown/package-install.ts +40 -0
  219. package/src/migrate/fumadocs/config.ts +106 -0
  220. package/src/migrate/fumadocs/content.ts +365 -0
  221. package/src/migrate/fumadocs/frontmatter.ts +18 -0
  222. package/src/migrate/fumadocs/index.ts +252 -0
  223. package/src/migrate/fumadocs/meta.ts +114 -0
  224. package/src/migrate/migrate.ts +53 -0
  225. package/src/migrate/mintlify/config.ts +1040 -0
  226. package/src/migrate/mintlify/content.ts +98 -0
  227. package/src/migrate/mintlify/frontmatter.ts +126 -0
  228. package/src/migrate/mintlify/i18n.ts +51 -0
  229. package/src/migrate/mintlify/icons.ts +128 -0
  230. package/src/migrate/mintlify/index.ts +266 -0
  231. package/src/migrate/mintlify/snippets.ts +305 -0
  232. package/src/migrate/mintlify/transform.ts +81 -0
  233. package/src/migrate/nextra/content.ts +46 -0
  234. package/src/migrate/nextra/frontmatter.ts +40 -0
  235. package/src/migrate/nextra/index.ts +374 -0
  236. package/src/migrate/nextra/meta.ts +266 -0
  237. package/src/migrate/shared.ts +623 -0
  238. package/src/migrate/starlight/config.ts +459 -0
  239. package/src/migrate/starlight/content.ts +78 -0
  240. package/src/migrate/starlight/frontmatter.ts +111 -0
  241. package/src/migrate/starlight/i18n.ts +54 -0
  242. package/src/migrate/starlight/index.ts +131 -0
  243. package/src/og/card.ts +92 -0
  244. package/src/og/index.ts +2 -0
  245. package/src/openapi/scalar.ts +246 -0
  246. package/src/registry/eject.ts +263 -0
  247. package/src/registry/registry.ts +100 -0
  248. package/src/registry/rewrite-imports.ts +39 -0
  249. package/src/runtime/index.ts +14 -0
  250. package/src/search/build.ts +23 -0
  251. package/src/search/documents.ts +165 -0
  252. package/src/search/orama-index.ts +66 -0
  253. package/src/search/providers.ts +91 -0
  254. package/src/search/sync/algolia.ts +30 -0
  255. package/src/search/sync/index.ts +50 -0
  256. package/src/search/sync/orama-cloud.ts +40 -0
  257. package/src/search/sync/typesense.ts +65 -0
  258. package/src/seo/jsonld.ts +113 -0
  259. package/src/theme/entry.ts +608 -0
  260. package/src/theme/fonts.ts +198 -0
  261. package/src/theme/icons.ts +184 -0
  262. package/src/theme/palette.ts +143 -0
  263. package/src/theme/twoslash.ts +81 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Curated Google Fonts exposed through `theme.fonts`.
3
+ *
4
+ * Each slug maps to the official Google family name (Astro's font provider needs
5
+ * the exact name), a docs-appropriate set of weights, and a fallback category.
6
+ * Fonts are self-hosted and optimized by Astro's built-in Fonts API; this module
7
+ * only resolves config slugs into the data that drives it.
8
+ */
9
+
10
+ export type FontCategory = "sans" | "serif" | "mono";
11
+
12
+ /** The three configurable roles in `theme.fonts`. */
13
+ export type FontSlot = "display" | "body" | "mono";
14
+
15
+ interface FontDef {
16
+ category: FontCategory;
17
+ family: string;
18
+ weights: number[];
19
+ }
20
+
21
+ /** Resolved theme fonts (a validated slug per role, all optional). */
22
+ export type FontsConfig = Partial<Record<FontSlot, string>> | undefined;
23
+
24
+ /** A single Astro `fonts:` entry (sans the literal `fontProviders.google()`). */
25
+ export interface FontEntry {
26
+ cssVariable: string;
27
+ fallbacks: string[];
28
+ name: string;
29
+ weights: number[];
30
+ }
31
+
32
+ const FALLBACKS: Record<FontCategory, string[]> = {
33
+ mono: ["ui-monospace", "SF Mono", "Menlo", "monospace"],
34
+ sans: ["ui-sans-serif", "system-ui", "sans-serif"],
35
+ serif: ["ui-serif", "Georgia", "serif"],
36
+ };
37
+
38
+ /** Slug -> Google family + weights + fallback category. Keep keys alphabetical. */
39
+ export const GOOGLE_FONTS = {
40
+ "dm-sans": { category: "sans", family: "DM Sans", weights: [400, 500, 700] },
41
+ figtree: {
42
+ category: "sans",
43
+ family: "Figtree",
44
+ weights: [400, 500, 600, 700],
45
+ },
46
+ "fira-code": {
47
+ category: "mono",
48
+ family: "Fira Code",
49
+ weights: [400, 500, 700],
50
+ },
51
+ geist: { category: "sans", family: "Geist", weights: [400, 500, 600, 700] },
52
+ "geist-mono": {
53
+ category: "mono",
54
+ family: "Geist Mono",
55
+ weights: [400, 500, 600],
56
+ },
57
+ "ibm-plex-mono": {
58
+ category: "mono",
59
+ family: "IBM Plex Mono",
60
+ weights: [400, 500, 600],
61
+ },
62
+ "ibm-plex-sans": {
63
+ category: "sans",
64
+ family: "IBM Plex Sans",
65
+ weights: [400, 500, 600, 700],
66
+ },
67
+ "ibm-plex-serif": {
68
+ category: "serif",
69
+ family: "IBM Plex Serif",
70
+ weights: [400, 500, 600],
71
+ },
72
+ inter: { category: "sans", family: "Inter", weights: [400, 500, 600, 700] },
73
+ "inter-tight": {
74
+ category: "sans",
75
+ family: "Inter Tight",
76
+ weights: [400, 500, 600, 700],
77
+ },
78
+ "jetbrains-mono": {
79
+ category: "mono",
80
+ family: "JetBrains Mono",
81
+ weights: [400, 500, 700],
82
+ },
83
+ lora: { category: "serif", family: "Lora", weights: [400, 500, 600, 700] },
84
+ manrope: {
85
+ category: "sans",
86
+ family: "Manrope",
87
+ weights: [400, 500, 600, 700],
88
+ },
89
+ merriweather: {
90
+ category: "serif",
91
+ family: "Merriweather",
92
+ weights: [400, 700],
93
+ },
94
+ "open-sans": {
95
+ category: "sans",
96
+ family: "Open Sans",
97
+ weights: [400, 600, 700],
98
+ },
99
+ "playfair-display": {
100
+ category: "serif",
101
+ family: "Playfair Display",
102
+ weights: [400, 500, 700],
103
+ },
104
+ "plus-jakarta-sans": {
105
+ category: "sans",
106
+ family: "Plus Jakarta Sans",
107
+ weights: [400, 500, 600, 700],
108
+ },
109
+ roboto: { category: "sans", family: "Roboto", weights: [400, 500, 700] },
110
+ "roboto-mono": {
111
+ category: "mono",
112
+ family: "Roboto Mono",
113
+ weights: [400, 500, 700],
114
+ },
115
+ "source-code-pro": {
116
+ category: "mono",
117
+ family: "Source Code Pro",
118
+ weights: [400, 500, 600],
119
+ },
120
+ "source-sans-3": {
121
+ category: "sans",
122
+ family: "Source Sans 3",
123
+ weights: [400, 600, 700],
124
+ },
125
+ "source-serif-4": {
126
+ category: "serif",
127
+ family: "Source Serif 4",
128
+ weights: [400, 600, 700],
129
+ },
130
+ "space-grotesk": {
131
+ category: "sans",
132
+ family: "Space Grotesk",
133
+ weights: [400, 500, 700],
134
+ },
135
+ "space-mono": { category: "mono", family: "Space Mono", weights: [400, 700] },
136
+ "work-sans": {
137
+ category: "sans",
138
+ family: "Work Sans",
139
+ weights: [400, 500, 600],
140
+ },
141
+ } satisfies Record<string, FontDef>;
142
+
143
+ export type FontSlug = keyof typeof GOOGLE_FONTS;
144
+
145
+ /** All supported slugs, for schema validation and error messages. */
146
+ export const FONT_SLUGS = Object.keys(GOOGLE_FONTS);
147
+
148
+ /** Type guard: is `value` a supported font slug? */
149
+ export const isFontSlug = (value: string): value is FontSlug =>
150
+ Object.hasOwn(GOOGLE_FONTS, value);
151
+
152
+ /** The CSS variable Astro populates for a given font (shared across roles). */
153
+ const fontVar = (slug: string): string => `--blume-ff-${slug}`;
154
+
155
+ const SLOTS: FontSlot[] = ["display", "body", "mono"];
156
+
157
+ /** The unique Astro `fonts:` entries for the configured roles (deduped). */
158
+ export const buildFontEntries = (fonts: FontsConfig): FontEntry[] => {
159
+ if (!fonts) {
160
+ return [];
161
+ }
162
+ const slugs = SLOTS.map((slot) => fonts[slot]).filter(
163
+ (slug): slug is FontSlug => typeof slug === "string" && isFontSlug(slug)
164
+ );
165
+ return [...new Set(slugs)].map((slug) => {
166
+ const def = GOOGLE_FONTS[slug];
167
+ return {
168
+ cssVariable: fontVar(slug),
169
+ fallbacks: FALLBACKS[def.category],
170
+ name: def.family,
171
+ weights: def.weights,
172
+ };
173
+ });
174
+ };
175
+
176
+ /**
177
+ * The config-token CSS that points each role's `--blume-font-<role>-src` at the
178
+ * Astro-populated family variable. Concatenated into the generated entry's
179
+ * config tokens; empty when no fonts are set so defaults stay the system stacks.
180
+ */
181
+ export const buildFontsCss = (fonts: FontsConfig): string => {
182
+ if (!fonts) {
183
+ return "";
184
+ }
185
+ const lines = SLOTS.flatMap((slot) => {
186
+ const slug = fonts[slot];
187
+ return slug && isFontSlug(slug)
188
+ ? [` --blume-font-${slot}-src: var(${fontVar(slug)});`]
189
+ : [];
190
+ });
191
+ return lines.length > 0
192
+ ? `/* Generated by Blume from theme.fonts. */\n:root {\n${lines.join("\n")}\n}\n`
193
+ : "";
194
+ };
195
+
196
+ /** The CSS variables to feed Astro's `<Font>` component in the document head. */
197
+ export const configuredCssVars = (fonts: FontsConfig): string[] =>
198
+ buildFontEntries(fonts).map((entry) => entry.cssVariable);
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Curated inline SVG icon set (Lucide-style, MIT). Stored as inner SVG markup
3
+ * so icons render as zero-JS inline SVG. The set is intentionally small to keep
4
+ * payloads bounded; more icons can be added as components need them.
5
+ */
6
+ export const icons: Record<string, string> = {
7
+ "arrow-left": '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
8
+ "arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
9
+ "arrow-up": '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
10
+ "arrow-up-right": '<path d="M7 7h10v10"/><path d="M7 17 17 7"/>',
11
+ "badge-alert":
12
+ '<path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.78 4.78 4 4 0 0 1-6.74 0 4 4 0 0 1-4.78-4.78 4 4 0 0 1 0-6.75Z"/><path d="M12 8v4"/><path d="M12 16h.01"/>',
13
+ ban: '<circle cx="12" cy="12" r="10"/><path d="m4.93 4.93 14.14 14.14"/>',
14
+ "book-open":
15
+ '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>',
16
+ "book-open-cover":
17
+ '<path d="M12 7v14"/><path d="M3 18a2 2 0 0 1 2-2h7V5H5a2 2 0 0 0-2 2Z"/><path d="M21 18a2 2 0 0 0-2-2h-7V5h7a2 2 0 0 1 2 2Z"/>',
18
+ "brand-x": '<path d="m4 4 11.7 16H20L8.3 4Z"/><path d="M4 20 20 4"/>',
19
+ check: '<path d="M20 6 9 17l-5-5"/>',
20
+ "chevron-down": '<path d="m6 9 6 6 6-6"/>',
21
+ "chevron-right": '<path d="m9 18 6-6-6-6"/>',
22
+ "circle-check": '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
23
+ "circle-x":
24
+ '<circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/>',
25
+ clock: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
26
+ copy: '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
27
+ download:
28
+ '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>',
29
+ "external-link":
30
+ '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
31
+ file: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
32
+ flag: '<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><path d="M4 22V15"/>',
33
+ folder:
34
+ '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/>',
35
+ gear: '<path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/>',
36
+ github:
37
+ '<path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.3-.8 2.1-.36.16-.78.24-1.2.24-1.4 0-2.4-.7-3-2-.3-.6-.8-.9-1.2-.9-.4 0-.8.2-.8.5 0 .5.7.8 1 1.2.7 1.5 2 2.4 4 2.4.43 0 .84-.04 1.2-.13V22"/>',
38
+ globe:
39
+ '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
40
+ info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
41
+ js: '<path d="M8 8v7a2 2 0 1 1-4 0"/><path d="M16 15a2 2 0 1 0 2-2 2 2 0 1 1 2-2"/><path d="M20 8v.01"/>',
42
+ key: '<path d="M21 2 11.4 11.6"/><circle cx="7.5" cy="16.5" r="5.5"/><path d="m15 7 2 2"/><path d="m12 10 2 2"/>',
43
+ leaf: '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12"/>',
44
+ lightbulb:
45
+ '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/>',
46
+ link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
47
+ linkedin:
48
+ '<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/>',
49
+ lock: '<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
50
+ menu: '<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>',
51
+ "message-circle": '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
52
+ moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
53
+ "panel-left":
54
+ '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/>',
55
+ "panel-left-close":
56
+ '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m16 15-3-3 3-3"/>',
57
+ "panel-right":
58
+ '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/>',
59
+ "panel-right-close":
60
+ '<rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/>',
61
+ paperclip:
62
+ '<path d="m16 6-8.41 8.41a2 2 0 0 0 2.83 2.83L18.83 8.83a4 4 0 0 0-5.66-5.66L4.76 11.59a6 6 0 0 0 8.49 8.49L21.66 11.66"/>',
63
+ "puzzle-piece":
64
+ '<path d="M15.39 4.39a2.1 2.1 0 0 0-2.97 0L12 4.82l-.42-.43a2.1 2.1 0 1 0-2.97 2.97l.43.42L7.6 9.22H4.75A1.75 1.75 0 0 0 3 10.97v8.28C3 20.22 3.78 21 4.75 21h8.28c.97 0 1.75-.78 1.75-1.75V16.4l1.44-1.44.42.43a2.1 2.1 0 1 0 2.97-2.97l-.43-.42.43-.42a2.1 2.1 0 1 0-2.97-2.97l-.42.43-1.44-1.44.61-.61a2.1 2.1 0 0 0 0-2.97Z"/>',
65
+ python:
66
+ '<path d="M12 2h4a4 4 0 0 1 4 4v3H8a4 4 0 0 0-4 4v1"/><path d="M12 22H8a4 4 0 0 1-4-4v-3h12a4 4 0 0 0 4-4v-1"/><path d="M9 6h.01"/><path d="M15 18h.01"/>',
67
+ rocket:
68
+ '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
69
+ rss: '<path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/>',
70
+ search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
71
+ sparkles:
72
+ '<path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3Z"/><path d="M5 3v4"/><path d="M3 5h4"/><path d="M19 17v4"/><path d="M17 19h4"/>',
73
+ star: '<path d="m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01Z"/>',
74
+ sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
75
+ "text-align-start":
76
+ '<path d="M4 6h16"/><path d="M4 10h10"/><path d="M4 14h16"/><path d="M4 18h10"/>',
77
+ "thumbs-down":
78
+ '<path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/>',
79
+ "thumbs-up":
80
+ '<path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/>',
81
+ "triangle-alert":
82
+ '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
83
+ x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
84
+ };
85
+
86
+ const iconAliases: Record<string, string> = {
87
+ "alien-8bit": "sparkles",
88
+ "arrow-up-right-from-square": "external-link",
89
+ "book-open-reader": "book-open",
90
+ "circle-info": "info",
91
+ close: "x",
92
+ "external-link-alt": "external-link",
93
+ "fa-github": "github",
94
+ "fa-linkedin": "linkedin",
95
+ "fa-x-twitter": "brand-x",
96
+ "file-lines": "file",
97
+ javascript: "js",
98
+ "panel-left-open": "panel-left",
99
+ "panel-right-open": "panel-right",
100
+ times: "x",
101
+ "x-twitter": "brand-x",
102
+ };
103
+
104
+ const libraryPrefixes = [
105
+ "fa-brands",
106
+ "fa-duotone",
107
+ "fa-light",
108
+ "fa-regular",
109
+ "fa-sharp-solid",
110
+ "fa-solid",
111
+ "fa-thin",
112
+ "fa",
113
+ "fab",
114
+ "fad",
115
+ "fal",
116
+ "far",
117
+ "fas",
118
+ "fat",
119
+ "lucide",
120
+ "tabler",
121
+ "ti",
122
+ ];
123
+
124
+ const normalizedIconName = (name: string): string =>
125
+ name
126
+ .trim()
127
+ .toLowerCase()
128
+ .replaceAll(/[\s_]+/gu, "-");
129
+
130
+ const isString = (value: string | null | undefined): value is string =>
131
+ typeof value === "string";
132
+
133
+ const withoutLibraryPrefix = (name: string): string => {
134
+ let normalized = normalizedIconName(name).replaceAll(/^icon-/gu, "");
135
+ let changed = true;
136
+ while (changed) {
137
+ changed = false;
138
+ for (const prefix of libraryPrefixes) {
139
+ if (normalized.startsWith(`${prefix}-`)) {
140
+ normalized = normalized.slice(prefix.length + 1);
141
+ changed = true;
142
+ }
143
+ if (normalized.startsWith(`${prefix}:`)) {
144
+ normalized = normalized.slice(prefix.length + 1);
145
+ changed = true;
146
+ }
147
+ }
148
+ }
149
+ return normalized;
150
+ };
151
+
152
+ export interface ResolvedIcon {
153
+ name: string;
154
+ markup: string;
155
+ }
156
+
157
+ /** Resolve common Mintlify Font Awesome/Lucide/Tabler names to Blume icons. */
158
+ export const resolveIcon = (
159
+ name: string,
160
+ iconType?: string
161
+ ): ResolvedIcon | null => {
162
+ const normalized = normalizedIconName(name);
163
+ const stripped = withoutLibraryPrefix(name);
164
+ const type = iconType ? normalizedIconName(iconType) : null;
165
+ const candidates = [
166
+ normalized,
167
+ stripped,
168
+ type ? `${type}-${stripped}` : null,
169
+ iconAliases[normalized],
170
+ iconAliases[stripped],
171
+ ].filter(isString);
172
+
173
+ for (const candidate of candidates) {
174
+ const markup = icons[candidate];
175
+ if (markup) {
176
+ return { markup, name: candidate };
177
+ }
178
+ }
179
+ return null;
180
+ };
181
+
182
+ /** Whether a name resolves to a known built-in icon. */
183
+ export const hasIcon = (name: string, iconType?: string): boolean =>
184
+ resolveIcon(name, iconType) !== null;
@@ -0,0 +1,143 @@
1
+ import type { ResolvedConfig } from "../core/schema.ts";
2
+
3
+ /** Named accent presets mapped to OKLCH values. */
4
+ const ACCENTS: Record<string, string> = {
5
+ blue: "oklch(0.62 0.16 250)",
6
+ green: "oklch(0.6 0.16 150)",
7
+ orange: "oklch(0.68 0.17 50)",
8
+ pink: "oklch(0.65 0.2 350)",
9
+ purple: "oklch(0.58 0.2 290)",
10
+ red: "oklch(0.58 0.22 25)",
11
+ teal: "oklch(0.6 0.12 195)",
12
+ };
13
+
14
+ const RADII: Record<ResolvedConfig["theme"]["radius"], string> = {
15
+ lg: "0.75rem",
16
+ md: "0.5rem",
17
+ none: "0",
18
+ sm: "0.25rem",
19
+ };
20
+
21
+ const cssString = (value: string): string => JSON.stringify(value);
22
+
23
+ const backgroundImageCss = (image: string): string =>
24
+ `url(${cssString(image)})`;
25
+
26
+ const cssToken = (name: string, value?: string | null): string[] =>
27
+ value ? [` ${name}: ${value};`] : [];
28
+
29
+ const backgroundDecorationCss = (
30
+ decoration: ResolvedConfig["theme"]["backgroundDecoration"]
31
+ ): string => {
32
+ if (decoration === "gradient") {
33
+ return ` --blume-background-decoration: radial-gradient(circle at top left, color-mix(in oklab, var(--blume-accent) 18%, transparent), transparent 28rem), radial-gradient(circle at top right, color-mix(in oklab, var(--blume-action) 12%, transparent), transparent 24rem);
34
+ --blume-background-decoration-repeat: no-repeat, no-repeat;
35
+ --blume-background-decoration-size: auto, auto;
36
+ `;
37
+ }
38
+ if (decoration === "grid") {
39
+ return ` --blume-background-decoration: linear-gradient(var(--blume-border) 1px, transparent 1px), linear-gradient(90deg, var(--blume-border) 1px, transparent 1px);
40
+ --blume-background-decoration-repeat: repeat, repeat;
41
+ --blume-background-decoration-size: 2rem 2rem, 2rem 2rem;
42
+ `;
43
+ }
44
+ if (decoration === "windows") {
45
+ return ` --blume-background-decoration: linear-gradient(90deg, color-mix(in oklab, var(--blume-border) 70%, transparent) 1px, transparent 1px), linear-gradient(var(--blume-border) 1px, transparent 1px);
46
+ --blume-background-decoration-repeat: repeat, repeat;
47
+ --blume-background-decoration-size: 7rem 4.5rem, 7rem 4.5rem;
48
+ `;
49
+ }
50
+ return "";
51
+ };
52
+
53
+ const themeRootCss = (
54
+ theme: ResolvedConfig["theme"],
55
+ options: {
56
+ accent: string;
57
+ action: string | null;
58
+ backgroundDecoration: string;
59
+ radius: string;
60
+ }
61
+ ): string =>
62
+ [
63
+ ` --blume-accent: ${options.accent};`,
64
+ ...cssToken("--blume-action", options.action),
65
+ ...cssToken(
66
+ "--blume-action-foreground",
67
+ options.action ? "oklch(1 0 0)" : null
68
+ ),
69
+ ...cssToken("--blume-background", theme.background),
70
+ ...cssToken(
71
+ "--blume-background-image",
72
+ theme.backgroundImage ? backgroundImageCss(theme.backgroundImage) : null
73
+ ),
74
+ options.backgroundDecoration.trimEnd(),
75
+ ` --blume-radius: ${options.radius};`,
76
+ ]
77
+ .filter(Boolean)
78
+ .join("\n");
79
+
80
+ const themeDarkCss = (
81
+ theme: ResolvedConfig["theme"],
82
+ accentDark: string | null
83
+ ): string => {
84
+ const tokens = [
85
+ ...cssToken("--blume-accent", accentDark),
86
+ ...cssToken("--blume-background", theme.backgroundDark),
87
+ ...cssToken(
88
+ "--blume-background-image",
89
+ theme.backgroundImageDark
90
+ ? backgroundImageCss(theme.backgroundImageDark)
91
+ : null
92
+ ),
93
+ ];
94
+ if (tokens.length === 0) {
95
+ return "";
96
+ }
97
+ return `:root[data-theme="dark"] {
98
+ ${tokens.join("\n")}
99
+ }
100
+ `;
101
+ };
102
+
103
+ /**
104
+ * Resolve the configured accent to a CSS color. A named accent resolves to its
105
+ * preset; any other value is treated as a raw CSS color so users can pass
106
+ * arbitrary colors without a config change.
107
+ */
108
+ export const resolveAccent = (theme: ResolvedConfig["theme"]): string =>
109
+ ACCENTS[theme.accent] ?? theme.accent;
110
+
111
+ /** Resolve the configured radius preset to a CSS length. */
112
+ export const resolveRadius = (theme: ResolvedConfig["theme"]): string =>
113
+ RADII[theme.radius];
114
+
115
+ /**
116
+ * Compile theme config into CSS custom properties. A named accent resolves to
117
+ * its preset; any other value is treated as a raw CSS color so users can pass
118
+ * arbitrary colors without a config change.
119
+ */
120
+ export const buildThemeCss = (theme: ResolvedConfig["theme"]): string => {
121
+ const accent = ACCENTS[theme.accent] ?? theme.accent;
122
+ const accentDark = theme.accentDark
123
+ ? (ACCENTS[theme.accentDark] ?? theme.accentDark)
124
+ : null;
125
+ const action = theme.action ? (ACCENTS[theme.action] ?? theme.action) : null;
126
+ const backgroundDecoration = backgroundDecorationCss(
127
+ theme.backgroundDecoration
128
+ );
129
+ const radius = RADII[theme.radius];
130
+ const root = themeRootCss(theme, {
131
+ accent,
132
+ action,
133
+ backgroundDecoration,
134
+ radius,
135
+ });
136
+ const dark = themeDarkCss(theme, accentDark);
137
+
138
+ return `/* Generated by Blume from theme config. */
139
+ :root {
140
+ ${root}
141
+ }
142
+ ${dark}`;
143
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * CSS for Twoslash's rich renderer, emitted into the Tailwind entry (Twoslash
3
+ * runs on any fence with the `twoslash` meta). It is the shipped
4
+ * `@shikijs/twoslash` stylesheet plus a Blume-themed override layer:
5
+ *
6
+ * - maps the renderer's `--twoslash-*` variables onto Blume tokens so popups
7
+ * match the active theme (light/dark);
8
+ * - lets popups escape the code block's horizontal scroll container; and
9
+ * - restores backgrounds the theme's `pre.astro-code span { background:
10
+ * transparent !important }` rule would otherwise clear from popup chrome.
11
+ */
12
+
13
+ import { readFileSync } from "node:fs";
14
+ import { createRequire } from "node:module";
15
+
16
+ const require = createRequire(import.meta.url);
17
+
18
+ /** Blume-token theming layered over the shipped rich renderer styles. */
19
+ const OVERRIDES = `
20
+ /* Twoslash: theme the rich renderer with Blume tokens. */
21
+ :root {
22
+ --twoslash-border-color: var(--blume-border);
23
+ --twoslash-popup-bg: var(--blume-code-background);
24
+ --twoslash-popup-color: var(--blume-foreground);
25
+ --twoslash-popup-shadow: 0 6px 24px oklch(0 0 0 / 0.18);
26
+ --twoslash-docs-color: var(--blume-muted-foreground);
27
+ --twoslash-docs-font: var(--font-sans);
28
+ --twoslash-code-font: var(--font-mono);
29
+ --twoslash-matched-color: var(--blume-foreground);
30
+ --twoslash-unmatched-color: var(--blume-muted-foreground);
31
+ --twoslash-cursor-color: var(--blume-muted-foreground);
32
+ }
33
+
34
+ /* Popups are absolutely positioned and must escape the pre's scroll container.
35
+ The base prose pre rule (from the typography layer) wins the cascade here
36
+ despite lower specificity, so !important is needed to force visibility. */
37
+ .prose pre.twoslash {
38
+ overflow: visible !important;
39
+ }
40
+
41
+ /* The rich renderer renders each popup's type signature as a nested Shiki
42
+ pre. Strip the code-block chrome (border, radius, padding, background) so it
43
+ sits flush inside the popup, which owns the frame. */
44
+ .prose pre.twoslash .twoslash-popup-container pre {
45
+ background: transparent !important;
46
+ border: 0 !important;
47
+ border-radius: 0 !important;
48
+ margin: 0 !important;
49
+ padding: 0 !important;
50
+ }
51
+
52
+ /* Re-assert chrome backgrounds the transparent token-span rule would clear. */
53
+ .twoslash .twoslash-popup-container,
54
+ .twoslash .twoslash-popup-arrow,
55
+ .twoslash .twoslash-completion-cursor .twoslash-completion-list {
56
+ background: var(--twoslash-popup-bg) !important;
57
+ }
58
+ .twoslash .twoslash-popup-error {
59
+ background: var(--twoslash-error-bg) !important;
60
+ }
61
+ .twoslash .twoslash-highlighted {
62
+ background: var(--twoslash-highlighted-bg) !important;
63
+ }
64
+
65
+ /* The error squiggle uses a background image the transparent rule clears, so
66
+ draw it as a wavy underline instead. */
67
+ .twoslash .twoslash-error {
68
+ text-decoration: underline wavy var(--twoslash-error-color);
69
+ text-underline-offset: 0.25em;
70
+ }
71
+
72
+ .twoslash-popup-container {
73
+ max-width: min(90vw, 36rem);
74
+ }
75
+ `;
76
+
77
+ /** The full Twoslash stylesheet (shipped rich styles + Blume overrides). */
78
+ export const twoslashCss = (): string => {
79
+ const file = require.resolve("@shikijs/twoslash/style-rich.css");
80
+ return `${readFileSync(file, "utf-8")}\n${OVERRIDES}`;
81
+ };