livemark 0.2.0 → 0.7.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 (346) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +5 -1
  3. package/build/actions/changelog/build.d.ts +14 -0
  4. package/build/actions/changelog/build.js +158 -0
  5. package/build/actions/changelog/build.unit.js +66 -0
  6. package/build/actions/config/define.d.ts +6 -0
  7. package/build/actions/config/define.js +8 -0
  8. package/build/actions/config/define.unit.js +29 -0
  9. package/build/actions/config/load.d.ts +5 -0
  10. package/build/actions/config/load.js +25 -0
  11. package/build/actions/config/load.unit.js +23 -0
  12. package/build/commands/build.d.ts +5 -0
  13. package/build/commands/build.js +14 -0
  14. package/build/commands/escape.d.ts +9 -0
  15. package/build/commands/escape.js +56 -0
  16. package/build/commands/preview.d.ts +5 -0
  17. package/build/commands/preview.js +18 -0
  18. package/build/commands/start.d.ts +5 -0
  19. package/build/commands/start.js +19 -0
  20. package/build/docs/markdown/includes/example.d.ts +58 -0
  21. package/build/docs/markdown/includes/example.js +5 -0
  22. package/build/entrypoints/main.d.ts +2 -0
  23. package/build/entrypoints/main.js +12 -0
  24. package/build/helpers/program.d.ts +2 -0
  25. package/build/helpers/program.js +5 -0
  26. package/build/index.d.ts +1 -4
  27. package/build/index.js +2 -5
  28. package/build/livemark.config.d.ts +58 -0
  29. package/build/livemark.config.js +25 -0
  30. package/build/models/config.d.ts +340 -0
  31. package/build/models/config.js +34 -0
  32. package/build/models/link.d.ts +13 -0
  33. package/build/models/link.js +9 -0
  34. package/build/models/patch.d.ts +21 -0
  35. package/build/models/patch.js +7 -0
  36. package/build/models/section.d.ts +75 -0
  37. package/build/models/section.js +26 -0
  38. package/build/models/theme.d.ts +72 -0
  39. package/build/models/theme.js +73 -0
  40. package/build/program.d.ts +2 -0
  41. package/build/program.js +16 -0
  42. package/build/vite.config.d.ts +2 -0
  43. package/build/vite.config.js +48 -0
  44. package/build/website/assets/logo.svg +117 -0
  45. package/build/website/client.tsx +10 -0
  46. package/build/website/components/Abbr.tsx +17 -0
  47. package/build/website/components/AnsiCode.tsx +15 -0
  48. package/build/website/components/Article.tsx +172 -0
  49. package/build/website/components/BackToTop.tsx +32 -0
  50. package/build/website/components/Banner.tsx +32 -0
  51. package/build/website/components/BlogIndex.tsx +121 -0
  52. package/build/website/components/BlogSidebar.tsx +162 -0
  53. package/build/website/components/Breadcrumbs.tsx +22 -0
  54. package/build/website/components/Callout.tsx +72 -0
  55. package/build/website/components/Card.tsx +48 -0
  56. package/build/website/components/Cards.tsx +8 -0
  57. package/build/website/components/ChangelogIndex.tsx +111 -0
  58. package/build/website/components/ChangelogSidebar.tsx +120 -0
  59. package/build/website/components/CodeBlock.tsx +135 -0
  60. package/build/website/components/CodeTabs.tsx +83 -0
  61. package/build/website/components/Column.tsx +4 -0
  62. package/build/website/components/Columns.tsx +12 -0
  63. package/build/website/components/Content.tsx +7 -0
  64. package/build/website/components/ContentTab.tsx +7 -0
  65. package/build/website/components/ContentTabs.tsx +82 -0
  66. package/build/website/components/DefaultCatchBoundary.tsx +54 -0
  67. package/build/website/components/DefinitionList.tsx +21 -0
  68. package/build/website/components/Details.tsx +30 -0
  69. package/build/website/components/FileTree.tsx +52 -0
  70. package/build/website/components/Footer.tsx +16 -0
  71. package/build/website/components/FootnoteRef.tsx +52 -0
  72. package/build/website/components/Heading.tsx +20 -0
  73. package/build/website/components/InlineBadge.tsx +9 -0
  74. package/build/website/components/InlineIcon.tsx +10 -0
  75. package/build/website/components/InlineToc.tsx +31 -0
  76. package/build/website/components/Layout.tsx +190 -0
  77. package/build/website/components/LinkButton.tsx +31 -0
  78. package/build/website/components/Mermaid.tsx +18 -0
  79. package/build/website/components/MermaidRenderer.tsx +37 -0
  80. package/build/website/components/MiniSidebar.tsx +49 -0
  81. package/build/website/components/NotFound.tsx +27 -0
  82. package/build/website/components/PackageTabs.tsx +74 -0
  83. package/build/website/components/PageToolbar.tsx +75 -0
  84. package/build/website/components/PrevNext.tsx +60 -0
  85. package/build/website/components/Search.tsx +28 -0
  86. package/build/website/components/SearchDialog.tsx +245 -0
  87. package/build/website/components/Section.tsx +24 -0
  88. package/build/website/components/SectionHeader.tsx +35 -0
  89. package/build/website/components/Sidebar.tsx +160 -0
  90. package/build/website/components/SidebarLinks.tsx +70 -0
  91. package/build/website/components/SidebarSections.tsx +71 -0
  92. package/build/website/components/SiteTitle.tsx +21 -0
  93. package/build/website/components/SortableTable.tsx +81 -0
  94. package/build/website/components/SoundCloud.tsx +16 -0
  95. package/build/website/components/TagIndex.tsx +120 -0
  96. package/build/website/components/Theme.tsx +39 -0
  97. package/build/website/components/Toc.tsx +226 -0
  98. package/build/website/components/YouTube.tsx +16 -0
  99. package/build/website/components/ZoomImage.tsx +25 -0
  100. package/build/website/content/article.ts +78 -0
  101. package/build/website/content/tag.ts +29 -0
  102. package/build/website/content-collections.ts +208 -0
  103. package/build/website/elements/accordion.tsx +78 -0
  104. package/build/website/elements/alert-dialog.tsx +185 -0
  105. package/build/website/elements/alert.tsx +74 -0
  106. package/build/website/elements/aspect-ratio.tsx +22 -0
  107. package/build/website/elements/avatar.tsx +107 -0
  108. package/build/website/elements/badge.tsx +51 -0
  109. package/build/website/elements/breadcrumb.tsx +120 -0
  110. package/build/website/elements/button-group.tsx +87 -0
  111. package/build/website/elements/button.tsx +57 -0
  112. package/build/website/elements/calendar.tsx +229 -0
  113. package/build/website/elements/card.tsx +102 -0
  114. package/build/website/elements/carousel.tsx +240 -0
  115. package/build/website/elements/chart.tsx +372 -0
  116. package/build/website/elements/checkbox.tsx +26 -0
  117. package/build/website/elements/collapsible.tsx +19 -0
  118. package/build/website/elements/command.tsx +210 -0
  119. package/build/website/elements/context-menu.tsx +270 -0
  120. package/build/website/elements/dialog.tsx +152 -0
  121. package/build/website/elements/direction.tsx +4 -0
  122. package/build/website/elements/drawer.tsx +127 -0
  123. package/build/website/elements/dropdown-menu.tsx +260 -0
  124. package/build/website/elements/empty.tsx +104 -0
  125. package/build/website/elements/field.tsx +235 -0
  126. package/build/website/elements/hover-card.tsx +49 -0
  127. package/build/website/elements/input-group.tsx +171 -0
  128. package/build/website/elements/input-otp.tsx +84 -0
  129. package/build/website/elements/input.tsx +18 -0
  130. package/build/website/elements/item.tsx +199 -0
  131. package/build/website/elements/kbd.tsx +26 -0
  132. package/build/website/elements/label.tsx +17 -0
  133. package/build/website/elements/menubar.tsx +282 -0
  134. package/build/website/elements/native-select.tsx +56 -0
  135. package/build/website/elements/navigation-menu.tsx +171 -0
  136. package/build/website/elements/pagination.tsx +133 -0
  137. package/build/website/elements/popover.tsx +88 -0
  138. package/build/website/elements/progress.tsx +81 -0
  139. package/build/website/elements/radio-group.tsx +36 -0
  140. package/build/website/elements/resizable.tsx +48 -0
  141. package/build/website/elements/scroll-area.tsx +51 -0
  142. package/build/website/elements/select.tsx +200 -0
  143. package/build/website/elements/separator.tsx +22 -0
  144. package/build/website/elements/sheet.tsx +130 -0
  145. package/build/website/elements/sidebar.tsx +717 -0
  146. package/build/website/elements/skeleton.tsx +13 -0
  147. package/build/website/elements/slider.tsx +57 -0
  148. package/build/website/elements/sonner.tsx +40 -0
  149. package/build/website/elements/spinner.tsx +16 -0
  150. package/build/website/elements/switch.tsx +30 -0
  151. package/build/website/elements/table.tsx +112 -0
  152. package/build/website/elements/tabs.tsx +79 -0
  153. package/build/website/elements/textarea.tsx +16 -0
  154. package/build/website/elements/toggle-group.tsx +87 -0
  155. package/build/website/elements/toggle.tsx +43 -0
  156. package/build/website/elements/tooltip.tsx +67 -0
  157. package/build/website/env.d.ts +14 -0
  158. package/build/website/helpers/article-icon.ts +61 -0
  159. package/build/website/helpers/article.ts +90 -0
  160. package/build/website/helpers/changelog-source.ts +16 -0
  161. package/build/website/helpers/changelog-source.unit.ts +38 -0
  162. package/build/website/helpers/dynamic-icon-resolver.tsx +19 -0
  163. package/build/website/helpers/dynamic-icon.tsx +12 -0
  164. package/build/website/helpers/favicon.ts +7 -0
  165. package/build/website/helpers/model.ts +4 -0
  166. package/build/website/helpers/resolve-asset-path.ts +9 -0
  167. package/build/website/helpers/resolve-includes.ts +131 -0
  168. package/build/website/helpers/resolve-includes.unit.ts +126 -0
  169. package/build/website/helpers/search-text.ts +15 -0
  170. package/build/website/helpers/section.ts +50 -0
  171. package/build/website/helpers/section.unit.ts +71 -0
  172. package/build/website/helpers/style.ts +6 -0
  173. package/build/website/helpers/theme.ts +27 -0
  174. package/build/website/helpers/toc-context.ts +10 -0
  175. package/build/website/helpers/toc.ts +46 -0
  176. package/build/website/helpers/toc.unit.ts +74 -0
  177. package/build/website/hooks/in-view.ts +30 -0
  178. package/build/website/hooks/media.ts +19 -0
  179. package/build/website/models/article.ts +55 -0
  180. package/build/website/models/toc.ts +5 -0
  181. package/build/website/plugins/rehype-inline-code.ts +107 -0
  182. package/build/website/plugins/rehype-task-list-a11y.ts +33 -0
  183. package/build/website/plugins/remark-abbr.ts +46 -0
  184. package/build/website/plugins/remark-abbr.unit.ts +99 -0
  185. package/build/website/plugins/remark-ansi.ts +248 -0
  186. package/build/website/plugins/remark-ansi.unit.ts +197 -0
  187. package/build/website/plugins/remark-badge.ts +50 -0
  188. package/build/website/plugins/remark-badge.unit.ts +96 -0
  189. package/build/website/plugins/remark-button.ts +60 -0
  190. package/build/website/plugins/remark-button.unit.ts +147 -0
  191. package/build/website/plugins/remark-callout.ts +57 -0
  192. package/build/website/plugins/remark-card.ts +80 -0
  193. package/build/website/plugins/remark-code-tabs.ts +87 -0
  194. package/build/website/plugins/remark-columns.ts +66 -0
  195. package/build/website/plugins/remark-custom-heading-id.ts +70 -0
  196. package/build/website/plugins/remark-custom-heading-id.unit.ts +131 -0
  197. package/build/website/plugins/remark-deflist.ts +36 -0
  198. package/build/website/plugins/remark-details.ts +49 -0
  199. package/build/website/plugins/remark-filetree.ts +89 -0
  200. package/build/website/plugins/remark-filetree.unit.ts +137 -0
  201. package/build/website/plugins/remark-github-callout.ts +71 -0
  202. package/build/website/plugins/remark-icon.ts +42 -0
  203. package/build/website/plugins/remark-image.ts +34 -0
  204. package/build/website/plugins/remark-media.ts +62 -0
  205. package/build/website/plugins/remark-media.unit.ts +74 -0
  206. package/build/website/plugins/remark-mermaid.ts +23 -0
  207. package/build/website/plugins/remark-npm.ts +52 -0
  208. package/build/website/plugins/remark-steps.ts +36 -0
  209. package/build/website/plugins/remark-steps.unit.ts +97 -0
  210. package/build/website/plugins/remark-tab.ts +100 -0
  211. package/build/website/plugins/remark-tab.unit.ts +181 -0
  212. package/build/website/plugins/remark-toc.ts +39 -0
  213. package/build/website/plugins/remark-toc.unit.ts +82 -0
  214. package/build/website/plugins/shiki-icon.ts +82 -0
  215. package/build/website/plugins/shiki-line-highlight.ts +174 -0
  216. package/build/website/plugins/vite-livemark.ts +91 -0
  217. package/build/website/routeTree.gen.ts +86 -0
  218. package/build/website/router.tsx +34 -0
  219. package/build/website/routes/$.tsx +92 -0
  220. package/build/website/routes/__root.tsx +97 -0
  221. package/build/website/server.ts +7 -0
  222. package/build/website/styles/general.css +163 -0
  223. package/build/website/styles/markdown.css +214 -0
  224. package/build/website/vite.config.ts +71 -0
  225. package/entrypoints/main.ts +12 -0
  226. package/package.json +120 -12
  227. package/build/descriptor/index.d.ts +0 -3
  228. package/build/descriptor/index.js +0 -4
  229. package/build/descriptor/load.d.ts +0 -8
  230. package/build/descriptor/load.js +0 -40
  231. package/build/descriptor/node.d.ts +0 -125
  232. package/build/descriptor/node.js +0 -9
  233. package/build/descriptor/save.d.ts +0 -8
  234. package/build/descriptor/save.js +0 -17
  235. package/build/descriptor/save.spec.js +0 -80
  236. package/build/descriptor/validate.d.ts +0 -9
  237. package/build/descriptor/validate.js +0 -14
  238. package/build/descriptor/validate.spec.js +0 -124
  239. package/build/dialect/Dialect.d.ts +0 -88
  240. package/build/dialect/Dialect.js +0 -2
  241. package/build/dialect/index.d.ts +0 -3
  242. package/build/dialect/index.js +0 -3
  243. package/build/dialect/load.d.ts +0 -4
  244. package/build/dialect/load.js +0 -7
  245. package/build/dialect/save.d.ts +0 -9
  246. package/build/dialect/save.js +0 -13
  247. package/build/dialect/validate.d.ts +0 -1
  248. package/build/dialect/validate.js +0 -4
  249. package/build/metadata/index.d.ts +0 -3
  250. package/build/metadata/index.js +0 -4
  251. package/build/metadata/load.d.ts +0 -10
  252. package/build/metadata/load.js +0 -55
  253. package/build/metadata/node.d.ts +0 -125
  254. package/build/metadata/node.js +0 -9
  255. package/build/metadata/save.d.ts +0 -8
  256. package/build/metadata/save.js +0 -17
  257. package/build/metadata/save.spec.js +0 -80
  258. package/build/metadata/validate.d.ts +0 -9
  259. package/build/metadata/validate.js +0 -14
  260. package/build/metadata/validate.spec.d.ts +0 -1
  261. package/build/metadata/validate.spec.js +0 -124
  262. package/build/package/Contributor.d.ts +0 -21
  263. package/build/package/Contributor.js +0 -2
  264. package/build/package/Package.d.ts +0 -65
  265. package/build/package/Package.js +0 -2
  266. package/build/package/index.d.ts +0 -4
  267. package/build/package/index.js +0 -3
  268. package/build/package/load.d.ts +0 -4
  269. package/build/package/load.js +0 -7
  270. package/build/package/save.d.ts +0 -9
  271. package/build/package/save.js +0 -13
  272. package/build/package/validate.d.ts +0 -1
  273. package/build/package/validate.js +0 -4
  274. package/build/resource/License.d.ts +0 -18
  275. package/build/resource/License.js +0 -2
  276. package/build/resource/Resource.d.ts +0 -67
  277. package/build/resource/Resource.js +0 -2
  278. package/build/resource/Source.d.ts +0 -17
  279. package/build/resource/Source.js +0 -2
  280. package/build/resource/index.d.ts +0 -5
  281. package/build/resource/index.js +0 -2
  282. package/build/resource/load.d.ts +0 -1
  283. package/build/resource/load.js +0 -2
  284. package/build/resource/save.d.ts +0 -9
  285. package/build/resource/save.js +0 -13
  286. package/build/resource/types/index.d.ts +0 -1
  287. package/build/resource/types/index.js +0 -2
  288. package/build/resource/types/table.d.ts +0 -29
  289. package/build/resource/types/table.js +0 -2
  290. package/build/resource/validate.d.ts +0 -1
  291. package/build/resource/validate.js +0 -4
  292. package/build/schema/Field.d.ts +0 -5
  293. package/build/schema/Field.js +0 -2
  294. package/build/schema/Schema.d.ts +0 -55
  295. package/build/schema/Schema.js +0 -2
  296. package/build/schema/fields/any.d.ts +0 -20
  297. package/build/schema/fields/any.js +0 -2
  298. package/build/schema/fields/array.d.ts +0 -32
  299. package/build/schema/fields/array.js +0 -2
  300. package/build/schema/fields/base.d.ts +0 -42
  301. package/build/schema/fields/base.js +0 -2
  302. package/build/schema/fields/boolean.d.ts +0 -28
  303. package/build/schema/fields/boolean.js +0 -2
  304. package/build/schema/fields/date.d.ts +0 -35
  305. package/build/schema/fields/date.js +0 -2
  306. package/build/schema/fields/datetime.d.ts +0 -35
  307. package/build/schema/fields/datetime.js +0 -2
  308. package/build/schema/fields/duration.d.ts +0 -28
  309. package/build/schema/fields/duration.js +0 -2
  310. package/build/schema/fields/geojson.d.ts +0 -26
  311. package/build/schema/fields/geojson.js +0 -2
  312. package/build/schema/fields/geopoint.d.ts +0 -27
  313. package/build/schema/fields/geopoint.js +0 -2
  314. package/build/schema/fields/index.d.ts +0 -16
  315. package/build/schema/fields/index.js +0 -17
  316. package/build/schema/fields/integer.d.ts +0 -37
  317. package/build/schema/fields/integer.js +0 -2
  318. package/build/schema/fields/list.d.ts +0 -36
  319. package/build/schema/fields/list.js +0 -2
  320. package/build/schema/fields/number.d.ts +0 -40
  321. package/build/schema/fields/number.js +0 -2
  322. package/build/schema/fields/object.d.ts +0 -32
  323. package/build/schema/fields/object.js +0 -2
  324. package/build/schema/fields/string.d.ts +0 -40
  325. package/build/schema/fields/string.js +0 -2
  326. package/build/schema/fields/time.d.ts +0 -35
  327. package/build/schema/fields/time.js +0 -2
  328. package/build/schema/fields/year.d.ts +0 -28
  329. package/build/schema/fields/year.js +0 -2
  330. package/build/schema/fields/yearmonth.d.ts +0 -28
  331. package/build/schema/fields/yearmonth.js +0 -2
  332. package/build/schema/index.d.ts +0 -4
  333. package/build/schema/index.js +0 -2
  334. package/build/schema/load.d.ts +0 -1
  335. package/build/schema/load.js +0 -2
  336. package/build/schema/save.d.ts +0 -9
  337. package/build/schema/save.js +0 -13
  338. package/build/schema/validate.d.ts +0 -1
  339. package/build/schema/validate.js +0 -4
  340. package/build/tsconfig.tsbuildinfo +0 -1
  341. package/index.ts +0 -0
  342. package/tsconfig.json +0 -10
  343. package/typedoc.json +0 -4
  344. /package/build/{descriptor/save.spec.d.ts → actions/changelog/build.unit.d.ts} +0 -0
  345. /package/build/{descriptor/validate.spec.d.ts → actions/config/define.unit.d.ts} +0 -0
  346. /package/build/{metadata/save.spec.d.ts → actions/config/load.unit.d.ts} +0 -0
@@ -0,0 +1,248 @@
1
+ import type { Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+
4
+ interface AnsiState {
5
+ bold: boolean
6
+ dim: boolean
7
+ italic: boolean
8
+ underline: boolean
9
+ strikethrough: boolean
10
+ fg: string | undefined
11
+ bg: string | undefined
12
+ }
13
+
14
+ const STANDARD_COLORS: ReadonlyArray<string> = [
15
+ "#4f5b66", // 0 black
16
+ "#bf616a", // 1 red
17
+ "#a3be8c", // 2 green
18
+ "#ebcb8b", // 3 yellow
19
+ "#8fa1b3", // 4 blue
20
+ "#b48ead", // 5 magenta
21
+ "#96b5b4", // 6 cyan
22
+ "#c0c5ce", // 7 white
23
+ ]
24
+
25
+ const BRIGHT_COLORS: ReadonlyArray<string> = [
26
+ "#65737e", // 8 bright black
27
+ "#bf616a", // 9 bright red
28
+ "#a3be8c", // 10 bright green
29
+ "#ebcb8b", // 11 bright yellow
30
+ "#8fa1b3", // 12 bright blue
31
+ "#b48ead", // 13 bright magenta
32
+ "#96b5b4", // 14 bright cyan
33
+ "#eff1f5", // 15 bright white
34
+ ]
35
+
36
+ function color256(n: number): string | undefined {
37
+ if (n < 8) return STANDARD_COLORS[n]
38
+ if (n < 16) return BRIGHT_COLORS[n - 8]
39
+ if (n < 232) {
40
+ const idx = n - 16
41
+ const r = Math.floor(idx / 36)
42
+ const g = Math.floor((idx % 36) / 6)
43
+ const b = idx % 6
44
+ const toHex = (v: number) =>
45
+ (v === 0 ? 0 : 55 + v * 40).toString(16).padStart(2, "0")
46
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`
47
+ }
48
+ if (n < 256) {
49
+ const v = (8 + (n - 232) * 10).toString(16).padStart(2, "0")
50
+ return `#${v}${v}${v}`
51
+ }
52
+ return undefined
53
+ }
54
+
55
+ function escapeHtml(text: string): string {
56
+ return text
57
+ .replace(/&/g, "&amp;")
58
+ .replace(/</g, "&lt;")
59
+ .replace(/>/g, "&gt;")
60
+ .replace(/"/g, "&quot;")
61
+ }
62
+
63
+ function buildStyle(state: AnsiState): string {
64
+ const parts: string[] = []
65
+ if (state.fg) parts.push(`color:${state.fg}`)
66
+ if (state.bg) parts.push(`background-color:${state.bg}`)
67
+ if (state.bold) parts.push("font-weight:bold")
68
+ if (state.dim) parts.push("opacity:0.7")
69
+ if (state.italic) parts.push("font-style:italic")
70
+ if (state.underline) parts.push("text-decoration:underline")
71
+ if (state.strikethrough) parts.push("text-decoration:line-through")
72
+ return parts.join(";")
73
+ }
74
+
75
+ function makeDefaultState(): AnsiState {
76
+ return {
77
+ bold: false,
78
+ dim: false,
79
+ italic: false,
80
+ underline: false,
81
+ strikethrough: false,
82
+ fg: undefined,
83
+ bg: undefined,
84
+ }
85
+ }
86
+
87
+ function applyParams(state: AnsiState, params: ReadonlyArray<number>) {
88
+ let i = 0
89
+ while (i < params.length) {
90
+ const code = params[i]!
91
+ switch (code) {
92
+ case 0: {
93
+ const reset = makeDefaultState()
94
+ state.bold = reset.bold
95
+ state.dim = reset.dim
96
+ state.italic = reset.italic
97
+ state.underline = reset.underline
98
+ state.strikethrough = reset.strikethrough
99
+ state.fg = reset.fg
100
+ state.bg = reset.bg
101
+ break
102
+ }
103
+ case 1:
104
+ state.bold = true
105
+ break
106
+ case 2:
107
+ state.dim = true
108
+ break
109
+ case 3:
110
+ state.italic = true
111
+ break
112
+ case 4:
113
+ state.underline = true
114
+ break
115
+ case 9:
116
+ state.strikethrough = true
117
+ break
118
+ case 22:
119
+ state.bold = false
120
+ state.dim = false
121
+ break
122
+ case 23:
123
+ state.italic = false
124
+ break
125
+ case 24:
126
+ state.underline = false
127
+ break
128
+ case 29:
129
+ state.strikethrough = false
130
+ break
131
+ case 39:
132
+ state.fg = undefined
133
+ break
134
+ case 49:
135
+ state.bg = undefined
136
+ break
137
+ default:
138
+ if (code >= 30 && code <= 37) {
139
+ state.fg = STANDARD_COLORS[code - 30]
140
+ } else if (code >= 40 && code <= 47) {
141
+ state.bg = STANDARD_COLORS[code - 40]
142
+ } else if (code >= 90 && code <= 97) {
143
+ state.fg = BRIGHT_COLORS[code - 90]
144
+ } else if (code >= 100 && code <= 107) {
145
+ state.bg = BRIGHT_COLORS[code - 100]
146
+ } else if (code === 38 || code === 48) {
147
+ const kind = params[i + 1]
148
+ if (kind === 5) {
149
+ const colorIdx = params[i + 2]
150
+ if (colorIdx !== undefined) {
151
+ const c = color256(colorIdx)
152
+ if (code === 38) state.fg = c
153
+ else state.bg = c
154
+ }
155
+ i += 2
156
+ } else if (kind === 2) {
157
+ const r = params[i + 2] ?? 0
158
+ const g = params[i + 3] ?? 0
159
+ const b = params[i + 4] ?? 0
160
+ const toHex = (v: number) => v.toString(16).padStart(2, "0")
161
+ const c = `#${toHex(r)}${toHex(g)}${toHex(b)}`
162
+ if (code === 38) state.fg = c
163
+ else state.bg = c
164
+ i += 4
165
+ }
166
+ }
167
+ break
168
+ }
169
+ i++
170
+ }
171
+ }
172
+
173
+ /** Convert a string containing ANSI escape sequences into HTML with inline styles */
174
+ export function ansiToHtml(input: string): string {
175
+ // oxlint-disable-next-line no-control-regex -- ANSI escape sequences start with U+001B
176
+ const sgr = /\u001b\[([0-9;]*)m/g
177
+ const state = makeDefaultState()
178
+ const parts: string[] = []
179
+ let lastIndex = 0
180
+
181
+ let match = sgr.exec(input)
182
+ while (match !== null) {
183
+ const textBefore = input.slice(lastIndex, match.index)
184
+ if (textBefore) {
185
+ const style = buildStyle(state)
186
+ if (style) {
187
+ parts.push(`<span style="${style}">${escapeHtml(textBefore)}</span>`)
188
+ } else {
189
+ parts.push(escapeHtml(textBefore))
190
+ }
191
+ }
192
+
193
+ const rawParams = match[1]!
194
+ const params =
195
+ rawParams === ""
196
+ ? [0]
197
+ : rawParams.split(";").map(s => Number.parseInt(s, 10))
198
+ applyParams(state, params)
199
+ lastIndex = match.index + match[0].length
200
+ match = sgr.exec(input)
201
+ }
202
+
203
+ const trailing = input.slice(lastIndex)
204
+ if (trailing) {
205
+ const style = buildStyle(state)
206
+ if (style) {
207
+ parts.push(`<span style="${style}">${escapeHtml(trailing)}</span>`)
208
+ } else {
209
+ parts.push(escapeHtml(trailing))
210
+ }
211
+ }
212
+
213
+ return parts.join("")
214
+ }
215
+
216
+ /** Walk a mdast tree and convert ansi code blocks into AnsiCode JSX elements */
217
+ export function transformAnsiCodeBlocks(tree: Root) {
218
+ const children = tree.children
219
+ for (let i = 0; i < children.length; i++) {
220
+ const node = children[i]
221
+ if (node?.type !== "code" || node.lang !== "ansi") continue
222
+
223
+ const html = ansiToHtml(node.value)
224
+
225
+ const jsxNode = {
226
+ type: "mdxJsxFlowElement" as const,
227
+ name: "AnsiCode",
228
+ attributes: [
229
+ {
230
+ type: "mdxJsxAttribute" as const,
231
+ name: "html",
232
+ value: html,
233
+ },
234
+ ],
235
+ children: [],
236
+ }
237
+
238
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
239
+ children[i] = jsxNode
240
+ }
241
+ }
242
+
243
+ /** Remark plugin that converts ansi code blocks into colored HTML output */
244
+ export const remarkAnsi: Plugin<[], Root> = () => {
245
+ return tree => {
246
+ transformAnsiCodeBlocks(tree)
247
+ }
248
+ }
@@ -0,0 +1,197 @@
1
+ import type { Root } from "mdast"
2
+ import { describe, expect, it } from "vite-plus/test"
3
+ import { ansiToHtml, transformAnsiCodeBlocks } from "./remark-ansi.ts"
4
+
5
+ function makeTree(...children: Root["children"]): Root {
6
+ return { type: "root", children }
7
+ }
8
+
9
+ describe("ansiToHtml", () => {
10
+ it("should return plain text unchanged", () => {
11
+ expect(ansiToHtml("hello world")).toBe("hello world")
12
+ })
13
+
14
+ it("should escape HTML characters", () => {
15
+ expect(ansiToHtml("<div>&</div>")).toBe("&lt;div&gt;&amp;&lt;/div&gt;")
16
+ })
17
+
18
+ it("should render standard foreground colors", () => {
19
+ const input = "\x1b[31mred text\x1b[0m"
20
+ expect(ansiToHtml(input)).toBe(
21
+ '<span style="color:#bf616a">red text</span>',
22
+ )
23
+ })
24
+
25
+ it("should render bold text", () => {
26
+ const input = "\x1b[1mbold\x1b[0m"
27
+ expect(ansiToHtml(input)).toBe('<span style="font-weight:bold">bold</span>')
28
+ })
29
+
30
+ it("should render italic text", () => {
31
+ const input = "\x1b[3mitalic\x1b[0m"
32
+ expect(ansiToHtml(input)).toBe(
33
+ '<span style="font-style:italic">italic</span>',
34
+ )
35
+ })
36
+
37
+ it("should render underline text", () => {
38
+ const input = "\x1b[4munderline\x1b[0m"
39
+ expect(ansiToHtml(input)).toBe(
40
+ '<span style="text-decoration:underline">underline</span>',
41
+ )
42
+ })
43
+
44
+ it("should render dim text", () => {
45
+ const input = "\x1b[2mdim\x1b[0m"
46
+ expect(ansiToHtml(input)).toBe('<span style="opacity:0.7">dim</span>')
47
+ })
48
+
49
+ it("should render strikethrough text", () => {
50
+ const input = "\x1b[9mstruck\x1b[0m"
51
+ expect(ansiToHtml(input)).toBe(
52
+ '<span style="text-decoration:line-through">struck</span>',
53
+ )
54
+ })
55
+
56
+ it("should render background colors", () => {
57
+ const input = "\x1b[42mgreen bg\x1b[0m"
58
+ expect(ansiToHtml(input)).toBe(
59
+ '<span style="background-color:#a3be8c">green bg</span>',
60
+ )
61
+ })
62
+
63
+ it("should combine foreground and background", () => {
64
+ const input = "\x1b[31;42mred on green\x1b[0m"
65
+ expect(ansiToHtml(input)).toBe(
66
+ '<span style="color:#bf616a;background-color:#a3be8c">red on green</span>',
67
+ )
68
+ })
69
+
70
+ it("should handle bright foreground colors", () => {
71
+ const input = "\x1b[91mbright red\x1b[0m"
72
+ expect(ansiToHtml(input)).toBe(
73
+ '<span style="color:#bf616a">bright red</span>',
74
+ )
75
+ })
76
+
77
+ it("should handle bright background colors", () => {
78
+ const input = "\x1b[102mbright green bg\x1b[0m"
79
+ expect(ansiToHtml(input)).toBe(
80
+ '<span style="background-color:#a3be8c">bright green bg</span>',
81
+ )
82
+ })
83
+
84
+ it("should handle 256-color foreground", () => {
85
+ const input = "\x1b[38;5;196mcolor\x1b[0m"
86
+ expect(ansiToHtml(input)).toBe('<span style="color:#ff0000">color</span>')
87
+ })
88
+
89
+ it("should handle 256-color background", () => {
90
+ const input = "\x1b[48;5;21mbg\x1b[0m"
91
+ expect(ansiToHtml(input)).toBe(
92
+ '<span style="background-color:#0000ff">bg</span>',
93
+ )
94
+ })
95
+
96
+ it("should handle 24-bit RGB foreground", () => {
97
+ const input = "\x1b[38;2;255;128;0mrgb\x1b[0m"
98
+ expect(ansiToHtml(input)).toBe('<span style="color:#ff8000">rgb</span>')
99
+ })
100
+
101
+ it("should handle 24-bit RGB background", () => {
102
+ const input = "\x1b[48;2;0;128;255mbg\x1b[0m"
103
+ expect(ansiToHtml(input)).toBe(
104
+ '<span style="background-color:#0080ff">bg</span>',
105
+ )
106
+ })
107
+
108
+ it("should handle reset via empty escape", () => {
109
+ const input = "\x1b[31mred\x1b[mnormal"
110
+ expect(ansiToHtml(input)).toBe(
111
+ '<span style="color:#bf616a">red</span>normal',
112
+ )
113
+ })
114
+
115
+ it("should handle multiple segments", () => {
116
+ const input = "start \x1b[32mgreen\x1b[0m middle \x1b[34mblue\x1b[0m end"
117
+ expect(ansiToHtml(input)).toBe(
118
+ 'start <span style="color:#a3be8c">green</span> middle <span style="color:#8fa1b3">blue</span> end',
119
+ )
120
+ })
121
+
122
+ it("should handle combined bold and color", () => {
123
+ const input = "\x1b[1;33mbold yellow\x1b[0m"
124
+ expect(ansiToHtml(input)).toBe(
125
+ '<span style="color:#ebcb8b;font-weight:bold">bold yellow</span>',
126
+ )
127
+ })
128
+
129
+ it("should handle 256-color grayscale", () => {
130
+ const input = "\x1b[38;5;232mblack\x1b[0m"
131
+ expect(ansiToHtml(input)).toBe('<span style="color:#080808">black</span>')
132
+ })
133
+
134
+ it("should reset individual attributes", () => {
135
+ const input = "\x1b[1;3mbold italic\x1b[23mnot italic\x1b[0m"
136
+ expect(ansiToHtml(input)).toBe(
137
+ '<span style="font-weight:bold;font-style:italic">bold italic</span><span style="font-weight:bold">not italic</span>',
138
+ )
139
+ })
140
+ })
141
+
142
+ describe("transformAnsiCodeBlocks", () => {
143
+ function process(tree: Root) {
144
+ transformAnsiCodeBlocks(tree)
145
+ return tree
146
+ }
147
+
148
+ it("should convert ansi code block to AnsiCode JSX element", () => {
149
+ const tree = process(
150
+ makeTree({ type: "code", lang: "ansi", value: "\x1b[31mhello\x1b[0m" }),
151
+ )
152
+
153
+ const node = tree.children[0]
154
+ expect(node).toMatchObject({
155
+ type: "mdxJsxFlowElement",
156
+ name: "AnsiCode",
157
+ attributes: [
158
+ {
159
+ type: "mdxJsxAttribute",
160
+ name: "html",
161
+ value: '<span style="color:#bf616a">hello</span>',
162
+ },
163
+ ],
164
+ })
165
+ })
166
+
167
+ it("should ignore non-ansi code blocks", () => {
168
+ const tree = process(
169
+ makeTree({ type: "code", lang: "javascript", value: "const x = 1" }),
170
+ )
171
+
172
+ const node = tree.children[0]
173
+ expect(node).toMatchObject({
174
+ type: "code",
175
+ lang: "javascript",
176
+ })
177
+ })
178
+
179
+ it("should handle plain text in ansi code block", () => {
180
+ const tree = process(
181
+ makeTree({ type: "code", lang: "ansi", value: "no escapes here" }),
182
+ )
183
+
184
+ const node = tree.children[0]
185
+ expect(node).toMatchObject({
186
+ type: "mdxJsxFlowElement",
187
+ name: "AnsiCode",
188
+ attributes: [
189
+ {
190
+ type: "mdxJsxAttribute",
191
+ name: "html",
192
+ value: "no escapes here",
193
+ },
194
+ ],
195
+ })
196
+ })
197
+ })
@@ -0,0 +1,50 @@
1
+ import type { Root } from "mdast"
2
+ import type { TextDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit } from "unist-util-visit"
5
+
6
+ /** Walk a mdast tree and convert :badge text directives into InlineBadge JSX elements */
7
+ export function transformBadgeDirectives(tree: Root) {
8
+ visit(tree, "textDirective", (node: TextDirective, index, parent) => {
9
+ if (node.name !== "badge" || index === undefined || !parent) return
10
+
11
+ const label = node.children
12
+ .map(c => ("value" in c ? String(c.value) : ""))
13
+ .join("")
14
+
15
+ const variant = node.attributes?.variant
16
+
17
+ const jsxNode = {
18
+ type: "mdxJsxTextElement" as const,
19
+ name: "InlineBadge",
20
+ attributes: [
21
+ {
22
+ type: "mdxJsxAttribute" as const,
23
+ name: "label",
24
+ value: label,
25
+ },
26
+ ...(variant
27
+ ? [
28
+ {
29
+ type: "mdxJsxAttribute" as const,
30
+ name: "variant",
31
+ value: variant,
32
+ },
33
+ ]
34
+ : []),
35
+ ],
36
+ children: [],
37
+ data: { _mdxExplicitJsx: true },
38
+ }
39
+
40
+ // @ts-expect-error mdxJsxTextElement is not in mdast types
41
+ parent.children.splice(index, 1, jsxNode)
42
+ })
43
+ }
44
+
45
+ /** Remark plugin that converts :badge text directives into InlineBadge JSX elements */
46
+ export const remarkBadge: Plugin<[], Root> = () => {
47
+ return tree => {
48
+ transformBadgeDirectives(tree)
49
+ }
50
+ }
@@ -0,0 +1,96 @@
1
+ import type { Root } from "mdast"
2
+ import type { TextDirective } from "mdast-util-directive"
3
+ import { describe, expect, it } from "vite-plus/test"
4
+ import { transformBadgeDirectives } from "./remark-badge.ts"
5
+
6
+ function makeTextDirective(
7
+ name: string,
8
+ text: string,
9
+ attributes?: Record<string, string>,
10
+ ): TextDirective {
11
+ return {
12
+ type: "textDirective",
13
+ name,
14
+ attributes: attributes ?? {},
15
+ children: [{ type: "text", value: text }],
16
+ }
17
+ }
18
+
19
+ function makeTree(...children: Root["children"]): Root {
20
+ return { type: "root", children }
21
+ }
22
+
23
+ describe("transformBadgeDirectives", () => {
24
+ function process(tree: Root) {
25
+ transformBadgeDirectives(tree)
26
+ return tree
27
+ }
28
+
29
+ it("should convert :badge directive to InlineBadge JSX element", () => {
30
+ const directive = makeTextDirective("badge", "Beta")
31
+ const tree = process(
32
+ makeTree({
33
+ type: "paragraph",
34
+ children: [directive],
35
+ }),
36
+ )
37
+
38
+ const paragraph = tree.children[0]
39
+ if (!paragraph || paragraph.type !== "paragraph")
40
+ throw new Error("Expected paragraph")
41
+ const node = paragraph.children[0]
42
+ expect(node).toMatchObject({
43
+ type: "mdxJsxTextElement",
44
+ name: "InlineBadge",
45
+ attributes: [{ type: "mdxJsxAttribute", name: "label", value: "Beta" }],
46
+ children: [],
47
+ })
48
+ })
49
+
50
+ it("should pass variant attribute when provided", () => {
51
+ const directive = makeTextDirective("badge", "Deprecated", {
52
+ variant: "destructive",
53
+ })
54
+ const tree = process(
55
+ makeTree({
56
+ type: "paragraph",
57
+ children: [directive],
58
+ }),
59
+ )
60
+
61
+ const paragraph = tree.children[0]
62
+ if (!paragraph || paragraph.type !== "paragraph")
63
+ throw new Error("Expected paragraph")
64
+ const node = paragraph.children[0]
65
+ expect(node).toMatchObject({
66
+ type: "mdxJsxTextElement",
67
+ name: "InlineBadge",
68
+ attributes: [
69
+ { type: "mdxJsxAttribute", name: "label", value: "Deprecated" },
70
+ {
71
+ type: "mdxJsxAttribute",
72
+ name: "variant",
73
+ value: "destructive",
74
+ },
75
+ ],
76
+ })
77
+ })
78
+
79
+ it("should ignore non-badge text directives", () => {
80
+ const directive = makeTextDirective("other", "Text")
81
+ const tree = process(
82
+ makeTree({
83
+ type: "paragraph",
84
+ children: [directive],
85
+ }),
86
+ )
87
+
88
+ const paragraph = tree.children[0]
89
+ if (!paragraph || paragraph.type !== "paragraph")
90
+ throw new Error("Expected paragraph")
91
+ expect(paragraph.children[0]).toMatchObject({
92
+ type: "textDirective",
93
+ name: "other",
94
+ })
95
+ })
96
+ })
@@ -0,0 +1,60 @@
1
+ import type { Root } from "mdast"
2
+ import type { LeafDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit } from "unist-util-visit"
5
+
6
+ /** Walk a mdast tree and convert ::button leaf directives into LinkButton JSX elements */
7
+ export function transformButtonDirectives(tree: Root) {
8
+ visit(tree, "leafDirective", (node: LeafDirective, index, parent) => {
9
+ if (node.name !== "button" || index === undefined || !parent) return
10
+
11
+ const label =
12
+ node.children.map(c => ("value" in c ? String(c.value) : "")).join("") ||
13
+ node.attributes?.label
14
+
15
+ const href = node.attributes?.href
16
+ if (!href || !label) return
17
+
18
+ const variant = node.attributes?.variant
19
+ const size = node.attributes?.size
20
+
21
+ const jsxNode = {
22
+ type: "mdxJsxFlowElement" as const,
23
+ name: "LinkButton",
24
+ attributes: [
25
+ { type: "mdxJsxAttribute" as const, name: "href", value: href },
26
+ { type: "mdxJsxAttribute" as const, name: "label", value: label },
27
+ ...(variant
28
+ ? [
29
+ {
30
+ type: "mdxJsxAttribute" as const,
31
+ name: "variant",
32
+ value: variant,
33
+ },
34
+ ]
35
+ : []),
36
+ ...(size
37
+ ? [
38
+ {
39
+ type: "mdxJsxAttribute" as const,
40
+ name: "size",
41
+ value: size,
42
+ },
43
+ ]
44
+ : []),
45
+ ],
46
+ children: [],
47
+ data: { _mdxExplicitJsx: true },
48
+ }
49
+
50
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
51
+ parent.children.splice(index, 1, jsxNode)
52
+ })
53
+ }
54
+
55
+ /** Remark plugin that converts ::button leaf directives into LinkButton JSX elements */
56
+ export const remarkButton: Plugin<[], Root> = () => {
57
+ return tree => {
58
+ transformButtonDirectives(tree)
59
+ }
60
+ }