livemark 0.0.0-dev

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 (187) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +3 -0
  3. package/build/actions/config/define.d.ts +6 -0
  4. package/build/actions/config/define.js +8 -0
  5. package/build/actions/config/define.unit.d.ts +1 -0
  6. package/build/actions/config/define.unit.js +34 -0
  7. package/build/actions/config/load.d.ts +5 -0
  8. package/build/actions/config/load.js +22 -0
  9. package/build/actions/config/load.unit.d.ts +1 -0
  10. package/build/actions/config/load.unit.js +23 -0
  11. package/build/commands/build.d.ts +5 -0
  12. package/build/commands/build.js +13 -0
  13. package/build/commands/preview.d.ts +5 -0
  14. package/build/commands/preview.js +18 -0
  15. package/build/commands/start.d.ts +5 -0
  16. package/build/commands/start.js +19 -0
  17. package/build/docs/markdown/includes/example.d.ts +9 -0
  18. package/build/docs/markdown/includes/example.js +5 -0
  19. package/build/entrypoints/main.d.ts +2 -0
  20. package/build/entrypoints/main.js +12 -0
  21. package/build/helpers/program.d.ts +2 -0
  22. package/build/helpers/program.js +5 -0
  23. package/build/index.d.ts +1 -0
  24. package/build/index.js +2 -0
  25. package/build/livemark.config.d.ts +9 -0
  26. package/build/livemark.config.js +5 -0
  27. package/build/models/config.d.ts +33 -0
  28. package/build/models/config.js +14 -0
  29. package/build/program.d.ts +2 -0
  30. package/build/program.js +14 -0
  31. package/build/vite.config.d.ts +2 -0
  32. package/build/vite.config.js +48 -0
  33. package/build/website/client.tsx +10 -0
  34. package/build/website/components/Abbr.tsx +17 -0
  35. package/build/website/components/AnsiCode.tsx +15 -0
  36. package/build/website/components/Banner.tsx +32 -0
  37. package/build/website/components/Breadcrumbs.tsx +19 -0
  38. package/build/website/components/Callout.tsx +72 -0
  39. package/build/website/components/Cards.tsx +66 -0
  40. package/build/website/components/CodeBlock.tsx +118 -0
  41. package/build/website/components/CodeTabs.tsx +83 -0
  42. package/build/website/components/Columns.tsx +17 -0
  43. package/build/website/components/Content.tsx +7 -0
  44. package/build/website/components/ContentTab.tsx +7 -0
  45. package/build/website/components/ContentTabs.tsx +82 -0
  46. package/build/website/components/DefaultCatchBoundary.tsx +54 -0
  47. package/build/website/components/DefinitionList.tsx +21 -0
  48. package/build/website/components/Details.tsx +30 -0
  49. package/build/website/components/FileTree.tsx +52 -0
  50. package/build/website/components/Footer.tsx +9 -0
  51. package/build/website/components/FootnoteRef.tsx +52 -0
  52. package/build/website/components/Heading.tsx +20 -0
  53. package/build/website/components/InlineBadge.tsx +9 -0
  54. package/build/website/components/InlineIcon.tsx +21 -0
  55. package/build/website/components/InlineToc.tsx +31 -0
  56. package/build/website/components/Layout.tsx +220 -0
  57. package/build/website/components/LinkButton.tsx +31 -0
  58. package/build/website/components/Mermaid.tsx +37 -0
  59. package/build/website/components/NotFound.tsx +27 -0
  60. package/build/website/components/PackageTabs.tsx +74 -0
  61. package/build/website/components/PageToolbar.tsx +65 -0
  62. package/build/website/components/PrevNext.tsx +52 -0
  63. package/build/website/components/SearchDialog.tsx +245 -0
  64. package/build/website/components/Section.tsx +24 -0
  65. package/build/website/components/SectionHeader.tsx +35 -0
  66. package/build/website/components/SiteTitle.tsx +16 -0
  67. package/build/website/components/SortableTable.tsx +81 -0
  68. package/build/website/components/SoundCloud.tsx +16 -0
  69. package/build/website/components/Theme.tsx +40 -0
  70. package/build/website/components/Toc.tsx +99 -0
  71. package/build/website/components/YouTube.tsx +16 -0
  72. package/build/website/components/ZoomImage.tsx +30 -0
  73. package/build/website/content-collections.ts +181 -0
  74. package/build/website/elements/accordion.tsx +78 -0
  75. package/build/website/elements/alert-dialog.tsx +185 -0
  76. package/build/website/elements/alert.tsx +74 -0
  77. package/build/website/elements/aspect-ratio.tsx +22 -0
  78. package/build/website/elements/avatar.tsx +107 -0
  79. package/build/website/elements/badge.tsx +51 -0
  80. package/build/website/elements/breadcrumb.tsx +120 -0
  81. package/build/website/elements/button-group.tsx +87 -0
  82. package/build/website/elements/button.tsx +57 -0
  83. package/build/website/elements/calendar.tsx +229 -0
  84. package/build/website/elements/card.tsx +102 -0
  85. package/build/website/elements/carousel.tsx +240 -0
  86. package/build/website/elements/chart.tsx +372 -0
  87. package/build/website/elements/checkbox.tsx +26 -0
  88. package/build/website/elements/collapsible.tsx +19 -0
  89. package/build/website/elements/command.tsx +210 -0
  90. package/build/website/elements/context-menu.tsx +270 -0
  91. package/build/website/elements/dialog.tsx +152 -0
  92. package/build/website/elements/direction.tsx +4 -0
  93. package/build/website/elements/drawer.tsx +127 -0
  94. package/build/website/elements/dropdown-menu.tsx +260 -0
  95. package/build/website/elements/empty.tsx +104 -0
  96. package/build/website/elements/field.tsx +235 -0
  97. package/build/website/elements/hover-card.tsx +49 -0
  98. package/build/website/elements/input-group.tsx +171 -0
  99. package/build/website/elements/input-otp.tsx +84 -0
  100. package/build/website/elements/input.tsx +18 -0
  101. package/build/website/elements/item.tsx +199 -0
  102. package/build/website/elements/kbd.tsx +26 -0
  103. package/build/website/elements/label.tsx +17 -0
  104. package/build/website/elements/menubar.tsx +282 -0
  105. package/build/website/elements/native-select.tsx +56 -0
  106. package/build/website/elements/navigation-menu.tsx +171 -0
  107. package/build/website/elements/pagination.tsx +133 -0
  108. package/build/website/elements/popover.tsx +88 -0
  109. package/build/website/elements/progress.tsx +81 -0
  110. package/build/website/elements/radio-group.tsx +36 -0
  111. package/build/website/elements/resizable.tsx +48 -0
  112. package/build/website/elements/scroll-area.tsx +51 -0
  113. package/build/website/elements/select.tsx +200 -0
  114. package/build/website/elements/separator.tsx +22 -0
  115. package/build/website/elements/sheet.tsx +130 -0
  116. package/build/website/elements/sidebar.tsx +717 -0
  117. package/build/website/elements/skeleton.tsx +13 -0
  118. package/build/website/elements/slider.tsx +57 -0
  119. package/build/website/elements/sonner.tsx +40 -0
  120. package/build/website/elements/spinner.tsx +16 -0
  121. package/build/website/elements/switch.tsx +30 -0
  122. package/build/website/elements/table.tsx +112 -0
  123. package/build/website/elements/tabs.tsx +79 -0
  124. package/build/website/elements/textarea.tsx +16 -0
  125. package/build/website/elements/toggle-group.tsx +87 -0
  126. package/build/website/elements/toggle.tsx +43 -0
  127. package/build/website/elements/tooltip.tsx +67 -0
  128. package/build/website/env.d.ts +10 -0
  129. package/build/website/globals.d.ts +8 -0
  130. package/build/website/helpers/article-icon.ts +59 -0
  131. package/build/website/helpers/articles.ts +24 -0
  132. package/build/website/helpers/resolve-includes.ts +131 -0
  133. package/build/website/helpers/resolve-includes.unit.ts +126 -0
  134. package/build/website/helpers/search-text.ts +15 -0
  135. package/build/website/helpers/style.ts +6 -0
  136. package/build/website/helpers/theme.ts +27 -0
  137. package/build/website/helpers/toc-context.ts +10 -0
  138. package/build/website/helpers/toc.ts +51 -0
  139. package/build/website/helpers/toc.unit.ts +74 -0
  140. package/build/website/hooks/in-view.ts +30 -0
  141. package/build/website/hooks/media.ts +19 -0
  142. package/build/website/models/article.ts +40 -0
  143. package/build/website/plugins/rehype-inline-code.ts +107 -0
  144. package/build/website/plugins/remark-abbr.ts +46 -0
  145. package/build/website/plugins/remark-abbr.unit.ts +99 -0
  146. package/build/website/plugins/remark-ansi.ts +247 -0
  147. package/build/website/plugins/remark-ansi.unit.ts +197 -0
  148. package/build/website/plugins/remark-badge.ts +50 -0
  149. package/build/website/plugins/remark-badge.unit.ts +96 -0
  150. package/build/website/plugins/remark-button.ts +60 -0
  151. package/build/website/plugins/remark-button.unit.ts +147 -0
  152. package/build/website/plugins/remark-callout.ts +57 -0
  153. package/build/website/plugins/remark-card.ts +80 -0
  154. package/build/website/plugins/remark-code-tabs.ts +87 -0
  155. package/build/website/plugins/remark-columns.ts +66 -0
  156. package/build/website/plugins/remark-custom-heading-id.ts +70 -0
  157. package/build/website/plugins/remark-custom-heading-id.unit.ts +131 -0
  158. package/build/website/plugins/remark-deflist.ts +36 -0
  159. package/build/website/plugins/remark-details.ts +49 -0
  160. package/build/website/plugins/remark-filetree.ts +89 -0
  161. package/build/website/plugins/remark-filetree.unit.ts +137 -0
  162. package/build/website/plugins/remark-github-callout.ts +71 -0
  163. package/build/website/plugins/remark-icon.ts +42 -0
  164. package/build/website/plugins/remark-image.ts +25 -0
  165. package/build/website/plugins/remark-media.ts +62 -0
  166. package/build/website/plugins/remark-media.unit.ts +74 -0
  167. package/build/website/plugins/remark-mermaid.ts +23 -0
  168. package/build/website/plugins/remark-npm.ts +52 -0
  169. package/build/website/plugins/remark-steps.ts +36 -0
  170. package/build/website/plugins/remark-steps.unit.ts +97 -0
  171. package/build/website/plugins/remark-tab.ts +100 -0
  172. package/build/website/plugins/remark-tab.unit.ts +181 -0
  173. package/build/website/plugins/remark-toc.ts +39 -0
  174. package/build/website/plugins/remark-toc.unit.ts +82 -0
  175. package/build/website/plugins/shiki-icon.ts +82 -0
  176. package/build/website/plugins/shiki-line-highlight.ts +174 -0
  177. package/build/website/public/logo.svg +72 -0
  178. package/build/website/routeTree.gen.ts +86 -0
  179. package/build/website/router.tsx +34 -0
  180. package/build/website/routes/$.tsx +149 -0
  181. package/build/website/routes/__root.tsx +84 -0
  182. package/build/website/routes/index.tsx +13 -0
  183. package/build/website/server.ts +7 -0
  184. package/build/website/styles/general.css +347 -0
  185. package/build/website/vite.config.ts +36 -0
  186. package/entrypoints/main.ts +12 -0
  187. package/package.json +120 -0
@@ -0,0 +1,36 @@
1
+ import type { Heading, Root } from "mdast"
2
+ import type { Plugin } from "unified"
3
+ import { visit } from "unist-util-visit"
4
+
5
+ const STEP_PATTERN = /\s*\[step\]\s*$/
6
+
7
+ /** Walk a mdast tree, strip `[step]` suffixes from headings, and add a `data-step` attribute */
8
+ export function transformStepHeadings(tree: Root) {
9
+ let stepNumber = 0
10
+
11
+ visit(tree, "heading", (node: Heading) => {
12
+ const lastChild = node.children[node.children.length - 1]
13
+ if (!lastChild || lastChild.type !== "text") return
14
+ if (!STEP_PATTERN.test(lastChild.value)) return
15
+
16
+ stepNumber += 1
17
+ lastChild.value = lastChild.value.replace(STEP_PATTERN, "")
18
+
19
+ if (lastChild.value === "" && node.children.length > 1) {
20
+ node.children.pop()
21
+ }
22
+
23
+ node.data = node.data ?? {}
24
+ node.data.hProperties = {
25
+ ...node.data.hProperties,
26
+ "data-step": stepNumber,
27
+ }
28
+ })
29
+ }
30
+
31
+ /** Remark plugin that converts `[step]` heading suffixes into numbered step markers */
32
+ export const remarkSteps: Plugin<[], Root> = () => {
33
+ return tree => {
34
+ transformStepHeadings(tree)
35
+ }
36
+ }
@@ -0,0 +1,97 @@
1
+ import type { Heading, Root, Text } from "mdast"
2
+ import { describe, expect, it } from "vite-plus/test"
3
+ import { transformStepHeadings } from "./remark-steps.ts"
4
+
5
+ function makeHeading(depth: 1 | 2 | 3 | 4 | 5 | 6, text: string): Heading {
6
+ return { type: "heading", depth, children: [{ type: "text", value: text }] }
7
+ }
8
+
9
+ function makeTree(...children: Root["children"]): Root {
10
+ return { type: "root", children }
11
+ }
12
+
13
+ function findHeading(tree: Root, index: number): Heading {
14
+ const node = tree.children[index]
15
+ if (!node || node.type !== "heading") throw new Error("No heading at index")
16
+ return node
17
+ }
18
+
19
+ function headingText(heading: Heading): string {
20
+ return heading.children
21
+ .filter((c): c is Text => c.type === "text")
22
+ .map(c => c.value)
23
+ .join("")
24
+ }
25
+
26
+ describe("transformStepHeadings", () => {
27
+ function process(tree: Root) {
28
+ transformStepHeadings(tree)
29
+ return tree
30
+ }
31
+
32
+ it("should strip [step] and set data-step attribute", () => {
33
+ const tree = process(
34
+ makeTree(makeHeading(3, "Install Dependencies [step]")),
35
+ )
36
+ const heading = findHeading(tree, 0)
37
+ expect(heading.data?.hProperties).toEqual({ "data-step": 1 })
38
+ expect(headingText(heading)).toBe("Install Dependencies")
39
+ })
40
+
41
+ it("should auto-increment step numbers", () => {
42
+ const tree = process(
43
+ makeTree(
44
+ makeHeading(3, "Step One [step]"),
45
+ makeHeading(3, "Step Two [step]"),
46
+ makeHeading(3, "Step Three [step]"),
47
+ ),
48
+ )
49
+
50
+ expect(findHeading(tree, 0).data?.hProperties).toEqual({ "data-step": 1 })
51
+ expect(findHeading(tree, 1).data?.hProperties).toEqual({ "data-step": 2 })
52
+ expect(findHeading(tree, 2).data?.hProperties).toEqual({ "data-step": 3 })
53
+ })
54
+
55
+ it("should leave headings without [step] unchanged", () => {
56
+ const tree = process(makeTree(makeHeading(2, "Normal Heading")))
57
+ const heading = findHeading(tree, 0)
58
+ expect(heading.data).toBeUndefined()
59
+ expect(headingText(heading)).toBe("Normal Heading")
60
+ })
61
+
62
+ it("should only count [step] headings for numbering", () => {
63
+ const tree = process(
64
+ makeTree(
65
+ makeHeading(3, "First [step]"),
66
+ makeHeading(3, "Not a step"),
67
+ makeHeading(3, "Second [step]"),
68
+ ),
69
+ )
70
+
71
+ expect(findHeading(tree, 0).data?.hProperties).toEqual({ "data-step": 1 })
72
+ expect(findHeading(tree, 1).data).toBeUndefined()
73
+ expect(findHeading(tree, 2).data?.hProperties).toEqual({ "data-step": 2 })
74
+ })
75
+
76
+ it("should handle different heading depths", () => {
77
+ const tree = process(
78
+ makeTree(
79
+ makeHeading(2, "Big Step [step]"),
80
+ makeHeading(4, "Small Step [step]"),
81
+ ),
82
+ )
83
+
84
+ expect(findHeading(tree, 0).data?.hProperties).toEqual({ "data-step": 1 })
85
+ expect(findHeading(tree, 1).data?.hProperties).toEqual({ "data-step": 2 })
86
+ })
87
+
88
+ it("should preserve existing hProperties", () => {
89
+ const heading = makeHeading(3, "Custom [step]")
90
+ heading.data = { hProperties: { id: "custom-id" } }
91
+ const tree = process(makeTree(heading))
92
+ expect(findHeading(tree, 0).data?.hProperties).toEqual({
93
+ id: "custom-id",
94
+ "data-step": 1,
95
+ })
96
+ })
97
+ })
@@ -0,0 +1,100 @@
1
+ import type { Root } from "mdast"
2
+ import type { ContainerDirective } from "mdast-util-directive"
3
+ import type { Plugin } from "unified"
4
+ import { visit, SKIP } from "unist-util-visit"
5
+
6
+ /** Walk a mdast tree and convert :::tab directives into ContentTab/ContentTabs JSX elements */
7
+ export function transformTabDirectives(tree: Root) {
8
+ visit(
9
+ tree,
10
+ "containerDirective",
11
+ (node: ContainerDirective, index, parent) => {
12
+ if (node.name !== "tab" || index === undefined || !parent) return
13
+
14
+ const title = node.attributes?.title ?? ""
15
+ const sync = node.attributes?.sync
16
+
17
+ const attrs: { type: "mdxJsxAttribute"; name: string; value: string }[] =
18
+ [{ type: "mdxJsxAttribute" as const, name: "title", value: title }]
19
+ if (sync) {
20
+ attrs.push({
21
+ type: "mdxJsxAttribute" as const,
22
+ name: "sync",
23
+ value: sync,
24
+ })
25
+ }
26
+
27
+ const jsxNode = {
28
+ type: "mdxJsxFlowElement" as const,
29
+ name: "ContentTab",
30
+ attributes: attrs,
31
+ children: node.children,
32
+ data: { _mdxExplicitJsx: true },
33
+ }
34
+
35
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
36
+ parent.children[index] = jsxNode
37
+ return SKIP
38
+ },
39
+ )
40
+
41
+ const children = tree.children
42
+ let i = 0
43
+ while (i < children.length) {
44
+ const node = children[i]
45
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
46
+ if (node?.type !== "mdxJsxFlowElement" || node.name !== "ContentTab") {
47
+ i++
48
+ continue
49
+ }
50
+
51
+ let j = i + 1
52
+ while (j < children.length) {
53
+ const next = children[j]
54
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
55
+ if (next?.type !== "mdxJsxFlowElement" || next.name !== "ContentTab")
56
+ break
57
+ j++
58
+ }
59
+
60
+ const group = children.slice(i, j)
61
+
62
+ const firstChild = group[0]
63
+ // @ts-expect-error accessing mdxJsx attributes on untyped node
64
+ const syncAttr = firstChild?.attributes?.find(
65
+ (a: { name: string }) => a.name === "sync",
66
+ )
67
+ const wrapperAttrs: {
68
+ type: "mdxJsxAttribute"
69
+ name: string
70
+ value: string
71
+ }[] = syncAttr
72
+ ? [
73
+ {
74
+ type: "mdxJsxAttribute" as const,
75
+ name: "sync",
76
+ value: syncAttr.value,
77
+ },
78
+ ]
79
+ : []
80
+
81
+ const wrapper = {
82
+ type: "mdxJsxFlowElement" as const,
83
+ name: "ContentTabs",
84
+ attributes: wrapperAttrs,
85
+ children: group,
86
+ data: { _mdxExplicitJsx: true },
87
+ }
88
+
89
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
90
+ children.splice(i, j - i, wrapper)
91
+ i++
92
+ }
93
+ }
94
+
95
+ /** Remark plugin that converts :::tab directives into ContentTab/ContentTabs JSX elements */
96
+ export const remarkTab: Plugin<[], Root> = () => {
97
+ return tree => {
98
+ transformTabDirectives(tree)
99
+ }
100
+ }
@@ -0,0 +1,181 @@
1
+ import type { Root } from "mdast"
2
+ import type { ContainerDirective } from "mdast-util-directive"
3
+ import { describe, expect, it } from "vite-plus/test"
4
+ import { transformTabDirectives } from "./remark-tab.ts"
5
+
6
+ function makeContainerDirective(
7
+ name: string,
8
+ attributes?: Record<string, string>,
9
+ ): ContainerDirective {
10
+ return {
11
+ type: "containerDirective",
12
+ name,
13
+ attributes: attributes ?? {},
14
+ children: [
15
+ { type: "paragraph", children: [{ type: "text", value: "content" }] },
16
+ ],
17
+ }
18
+ }
19
+
20
+ function makeTree(...children: Root["children"]): Root {
21
+ return { type: "root", children }
22
+ }
23
+
24
+ describe("transformTabDirectives", () => {
25
+ function process(tree: Root) {
26
+ transformTabDirectives(tree)
27
+ return tree
28
+ }
29
+
30
+ it("should convert :::tab directive to ContentTab JSX element", () => {
31
+ const directive = makeContainerDirective("tab", { title: "React" })
32
+ const tree = process(makeTree(directive))
33
+
34
+ const node = tree.children[0]
35
+ expect(node).toMatchObject({
36
+ type: "mdxJsxFlowElement",
37
+ name: "ContentTabs",
38
+ children: [
39
+ {
40
+ type: "mdxJsxFlowElement",
41
+ name: "ContentTab",
42
+ attributes: [
43
+ { type: "mdxJsxAttribute", name: "title", value: "React" },
44
+ ],
45
+ },
46
+ ],
47
+ })
48
+ })
49
+
50
+ it("should default title to empty string when not provided", () => {
51
+ const directive = makeContainerDirective("tab")
52
+ const tree = process(makeTree(directive))
53
+
54
+ const wrapper = tree.children[0]
55
+ expect(wrapper).toMatchObject({
56
+ type: "mdxJsxFlowElement",
57
+ name: "ContentTabs",
58
+ children: [
59
+ {
60
+ type: "mdxJsxFlowElement",
61
+ name: "ContentTab",
62
+ attributes: [{ type: "mdxJsxAttribute", name: "title", value: "" }],
63
+ },
64
+ ],
65
+ })
66
+ })
67
+
68
+ it("should ignore non-tab container directives", () => {
69
+ const directive = makeContainerDirective("note")
70
+ const tree = process(makeTree(directive))
71
+
72
+ expect(tree.children[0]).toMatchObject({
73
+ type: "containerDirective",
74
+ name: "note",
75
+ })
76
+ })
77
+
78
+ it("should wrap a single ContentTab in ContentTabs", () => {
79
+ const directive = makeContainerDirective("tab", { title: "Only" })
80
+ const tree = process(makeTree(directive))
81
+
82
+ expect(tree.children).toHaveLength(1)
83
+ expect(tree.children[0]).toMatchObject({
84
+ type: "mdxJsxFlowElement",
85
+ name: "ContentTabs",
86
+ children: [
87
+ {
88
+ type: "mdxJsxFlowElement",
89
+ name: "ContentTab",
90
+ },
91
+ ],
92
+ })
93
+ })
94
+
95
+ it("should group consecutive tabs into a single ContentTabs wrapper", () => {
96
+ const tab1 = makeContainerDirective("tab", { title: "React" })
97
+ const tab2 = makeContainerDirective("tab", { title: "Vue" })
98
+ const tab3 = makeContainerDirective("tab", { title: "Svelte" })
99
+ const tree = process(makeTree(tab1, tab2, tab3))
100
+
101
+ expect(tree.children).toHaveLength(1)
102
+ const wrapper = tree.children[0]
103
+ expect(wrapper).toMatchObject({
104
+ type: "mdxJsxFlowElement",
105
+ name: "ContentTabs",
106
+ })
107
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
108
+ expect(wrapper.children).toHaveLength(3)
109
+ })
110
+
111
+ it("should not group tabs separated by other elements", () => {
112
+ const tab1 = makeContainerDirective("tab", { title: "A" })
113
+ const separator = {
114
+ type: "paragraph" as const,
115
+ children: [{ type: "text" as const, value: "break" }],
116
+ }
117
+ const tab2 = makeContainerDirective("tab", { title: "B" })
118
+ const tree = process(makeTree(tab1, separator, tab2))
119
+
120
+ expect(tree.children).toHaveLength(3)
121
+ expect(tree.children[0]).toMatchObject({
122
+ type: "mdxJsxFlowElement",
123
+ name: "ContentTabs",
124
+ })
125
+ expect(tree.children[1]).toMatchObject({ type: "paragraph" })
126
+ expect(tree.children[2]).toMatchObject({
127
+ type: "mdxJsxFlowElement",
128
+ name: "ContentTabs",
129
+ })
130
+ })
131
+
132
+ it("should pass sync attribute to ContentTab when present", () => {
133
+ const directive = makeContainerDirective("tab", {
134
+ title: "npm",
135
+ sync: "pm",
136
+ })
137
+ const tree = process(makeTree(directive))
138
+
139
+ const wrapper = tree.children[0]
140
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
141
+ const tab = wrapper.children[0]
142
+ expect(tab).toMatchObject({
143
+ type: "mdxJsxFlowElement",
144
+ name: "ContentTab",
145
+ attributes: [
146
+ { type: "mdxJsxAttribute", name: "title", value: "npm" },
147
+ { type: "mdxJsxAttribute", name: "sync", value: "pm" },
148
+ ],
149
+ })
150
+ })
151
+
152
+ it("should propagate sync attribute to ContentTabs wrapper", () => {
153
+ const tab1 = makeContainerDirective("tab", {
154
+ title: "npm",
155
+ sync: "pm",
156
+ })
157
+ const tab2 = makeContainerDirective("tab", {
158
+ title: "pnpm",
159
+ sync: "pm",
160
+ })
161
+ const tree = process(makeTree(tab1, tab2))
162
+
163
+ expect(tree.children[0]).toMatchObject({
164
+ type: "mdxJsxFlowElement",
165
+ name: "ContentTabs",
166
+ attributes: [{ type: "mdxJsxAttribute", name: "sync", value: "pm" }],
167
+ })
168
+ })
169
+
170
+ it("should not add sync attribute when not present", () => {
171
+ const tab1 = makeContainerDirective("tab", { title: "A" })
172
+ const tab2 = makeContainerDirective("tab", { title: "B" })
173
+ const tree = process(makeTree(tab1, tab2))
174
+
175
+ expect(tree.children[0]).toMatchObject({
176
+ type: "mdxJsxFlowElement",
177
+ name: "ContentTabs",
178
+ attributes: [],
179
+ })
180
+ })
181
+ })
@@ -0,0 +1,39 @@
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 toc leaf directives into InlineToc JSX elements */
7
+ export function transformTocDirectives(tree: Root) {
8
+ visit(tree, "leafDirective", (node: LeafDirective, index, parent) => {
9
+ if (node.name !== "toc" || index === undefined || !parent) return
10
+
11
+ const maxLevel = node.attributes?.maxLevel
12
+
13
+ const jsxNode = {
14
+ type: "mdxJsxFlowElement" as const,
15
+ name: "InlineToc",
16
+ attributes: maxLevel
17
+ ? [
18
+ {
19
+ type: "mdxJsxAttribute" as const,
20
+ name: "maxLevel",
21
+ value: maxLevel,
22
+ },
23
+ ]
24
+ : [],
25
+ children: [],
26
+ data: { _mdxExplicitJsx: true },
27
+ }
28
+
29
+ // @ts-expect-error mdxJsxFlowElement is not in mdast types
30
+ parent.children.splice(index, 1, jsxNode)
31
+ })
32
+ }
33
+
34
+ /** Remark plugin that converts ::toc leaf directives into InlineToc JSX elements */
35
+ export const remarkToc: Plugin<[], Root> = () => {
36
+ return tree => {
37
+ transformTocDirectives(tree)
38
+ }
39
+ }
@@ -0,0 +1,82 @@
1
+ import type { Root } from "mdast"
2
+ import type { LeafDirective } from "mdast-util-directive"
3
+ import { describe, expect, it } from "vite-plus/test"
4
+ import { transformTocDirectives } from "./remark-toc.ts"
5
+
6
+ function makeLeafDirective(
7
+ name: string,
8
+ attributes: Record<string, string> = {},
9
+ ): LeafDirective {
10
+ return {
11
+ type: "leafDirective" as LeafDirective["type"],
12
+ name,
13
+ attributes,
14
+ children: [],
15
+ }
16
+ }
17
+
18
+ function makeTree(...children: Root["children"]): Root {
19
+ return { type: "root", children }
20
+ }
21
+
22
+ function process(tree: Root) {
23
+ transformTocDirectives(tree)
24
+ return tree
25
+ }
26
+
27
+ describe("transformTocDirectives", () => {
28
+ it("should convert toc directive to InlineToc JSX element", () => {
29
+ const tree = process(makeTree(makeLeafDirective("toc")))
30
+ const node = tree.children[0]
31
+ expect(node).toMatchObject({
32
+ type: "mdxJsxFlowElement",
33
+ name: "InlineToc",
34
+ attributes: [],
35
+ children: [],
36
+ })
37
+ })
38
+
39
+ it("should ignore unrelated leaf directives", () => {
40
+ const tree = process(
41
+ makeTree(makeLeafDirective("video", { type: "youtube" })),
42
+ )
43
+ const node = tree.children[0]
44
+ expect(node).toMatchObject({ type: "leafDirective", name: "video" })
45
+ })
46
+
47
+ it("should handle multiple toc directives in the same tree", () => {
48
+ const tree = process(
49
+ makeTree(
50
+ makeLeafDirective("toc"),
51
+ { type: "paragraph", children: [{ type: "text", value: "hello" }] },
52
+ makeLeafDirective("toc"),
53
+ ),
54
+ )
55
+ expect(tree.children[0]).toMatchObject({
56
+ type: "mdxJsxFlowElement",
57
+ name: "InlineToc",
58
+ })
59
+ expect(tree.children[1]).toMatchObject({ type: "paragraph" })
60
+ expect(tree.children[2]).toMatchObject({
61
+ type: "mdxJsxFlowElement",
62
+ name: "InlineToc",
63
+ })
64
+ })
65
+
66
+ it("should preserve other children when converting toc directive", () => {
67
+ const tree = process(
68
+ makeTree(
69
+ { type: "paragraph", children: [{ type: "text", value: "before" }] },
70
+ makeLeafDirective("toc"),
71
+ { type: "paragraph", children: [{ type: "text", value: "after" }] },
72
+ ),
73
+ )
74
+ expect(tree.children).toHaveLength(3)
75
+ expect(tree.children[0]).toMatchObject({ type: "paragraph" })
76
+ expect(tree.children[1]).toMatchObject({
77
+ type: "mdxJsxFlowElement",
78
+ name: "InlineToc",
79
+ })
80
+ expect(tree.children[2]).toMatchObject({ type: "paragraph" })
81
+ })
82
+ })
@@ -0,0 +1,82 @@
1
+ interface HastElement {
2
+ properties: Record<string, unknown>
3
+ }
4
+
5
+ interface ShikiTransformer {
6
+ name: string
7
+ pre: (
8
+ this: { options: { lang?: string } },
9
+ pre: HastElement,
10
+ ) => HastElement | void
11
+ }
12
+
13
+ type Icon = { viewBox: string; fill: string; d: string }
14
+
15
+ const shortcuts: Record<string, string> = {
16
+ js: "javascript",
17
+ jsx: "react",
18
+ ts: "typescript",
19
+ tsx: "react",
20
+ py: "python",
21
+ bash: "shellscript",
22
+ sh: "shellscript",
23
+ shell: "shellscript",
24
+ zsh: "shellscript",
25
+ }
26
+
27
+ const icons: Record<string, Icon> = {
28
+ typescript: {
29
+ viewBox: "0 0 24 24",
30
+ fill: "currentColor",
31
+ d: "M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z",
32
+ },
33
+ javascript: {
34
+ viewBox: "0 0 24 24",
35
+ fill: "currentColor",
36
+ d: "M0 0h24v24H0V0zm22.034 18.276c-.175-1.095-.888-2.015-3.003-2.873-.736-.345-1.554-.585-1.797-1.14-.091-.33-.105-.51-.046-.705.15-.646.915-.84 1.515-.66.39.12.75.42.976.9 1.034-.676 1.034-.676 1.755-1.125-.27-.42-.405-.6-.586-.78-.63-.705-1.469-1.065-2.834-1.034l-.705.089c-.676.165-1.32.525-1.71 1.005-1.14 1.291-.811 3.541.569 4.471 1.365 1.02 3.361 1.244 3.616 2.205.24 1.17-.87 1.545-1.966 1.41-.811-.18-1.26-.586-1.755-1.336l-1.83 1.051c.21.48.45.689.81 1.109 1.74 1.756 6.09 1.666 6.871-1.004.029-.09.24-.705.074-1.65l.046.067zm-8.983-7.245h-2.248c0 1.938-.009 3.864-.009 5.805 0 1.232.063 2.363-.138 2.711-.33.689-1.18.601-1.566.48-.396-.196-.597-.466-.83-.855-.063-.105-.11-.196-.127-.196l-1.825 1.125c.305.63.75 1.172 1.324 1.517.855.51 2.004.675 3.207.405.783-.226 1.458-.691 1.811-1.411.51-.93.402-2.07.397-3.346.012-2.054 0-4.109 0-6.179l.004-.056z",
37
+ },
38
+ react: {
39
+ viewBox: "0 0 24 24",
40
+ fill: "currentColor",
41
+ d: "M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z",
42
+ },
43
+ python: {
44
+ viewBox: "0 0 24 24",
45
+ fill: "currentColor",
46
+ d: "M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01H8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z",
47
+ },
48
+ rust: {
49
+ viewBox: "0 0 24 24",
50
+ fill: "currentColor",
51
+ d: "M23.8346 11.7033l-1.0073-.6236a13.7268 13.7268 0 00-.0283-.2936l.8656-.8069a.3483.3483 0 00-.1154-.578l-1.1066-.414a8.4958 8.4958 0 00-.087-.2856l.6904-.9587a.3462.3462 0 00-.2257-.5446l-1.1663-.1894a9.3574 9.3574 0 00-.1407-.2622l.4895-1.0761a.3438.3438 0 00-.3300-.4908l-1.1845.0416a6.7444 6.7444 0 00-.1873-.2268l.2723-1.153a.3472.3472 0 00-.4233-.4164l-1.1592.2683a14.1794 14.1794 0 00-.2231-.1817l.0467-1.1872a.3462.3462 0 00-.5765-.322l-1.0907.4878c-.0799-.0499-.1613-.0983-.2449-.1456l-.1787-1.1758a.3456.3456 0 00-.5766-.2329l-.9813.6919a13.596 13.596 0 00-.2571-.1049l-.3982-1.1175a.348.348 0 00-.5815-.1272l-.8352.8738a10.2102 10.2102 0 00-.2606-.0584l-.6082-1.0131a.3459.3459 0 00-.5659-.0197l-.6602 1.0257a6.3249 6.3249 0 00-.2574.0068L8.3862.8721a.3437.3437 0 00-.5368.0921l-.4676 1.1376a10.3014 10.3014 0 00-.2444.0856L6.2788 1.394a.3447.3447 0 00-.4916.2004l-.2615 1.2048a11.5632 11.5632 0 00-.2242.149l-.9215-.5549a.3445.3445 0 00-.4345.2945l-.0488 1.2261a11.0239 11.0239 0 00-.1989.2028l-1.0498-.2744a.3441.3441 0 00-.3684.3752l.1643 1.2026c-.0553.075-.1084.1515-.1598.2293l-1.1374.0132a.3427.3427 0 00-.2938.4422l.3698 1.1347a10.4633 10.4633 0 00-.1162.2474l-1.1817.2923a.3433.3433 0 00-.2134.4944l.5627 1.0262c-.0265.085-.0502.1713-.0713.258l-1.1812.5586a.343.343 0 00-.1299.5316l.7396.8793a13.0102 13.0102 0 00-.0236.264l-1.1355.8086a.3445.3445 0 00-.0429.5513l.8937.6986a9.0776 9.0776 0 00.0235.2643L.3108 14.515a.3455.3455 0 00.0432.5508l1.0185.4941a14.4526 14.4526 0 00.0694.2602l-.6564.9682a.3443.3443 0 00.1273.5483l1.1066.2713c.0437.0833.0891.1653.136.2462l-.4348 1.0848a.3445.3445 0 00.2069.5304l1.1564.0436c.0583.0749.1182.1485.1797.2205l-.2004 1.1603a.348.348 0 00.2824.4902l1.1669-.1838c.0694.064.1404.1265.2127.1874l.0387 1.1912a.3476.3476 0 00.3498.4306l1.1388-.4038a7.4207 7.4207 0 00.2381.1482l.2723 1.1772a.3459.3459 0 00.4074.3543l1.0738-.6104c.084.0393.169.077.2553.113l.4964 1.1178a.3476.3476 0 00.4529.2643l.9744-.7985c.0873.0285.1757.0553.2649.0803l.707 1.015a.3475.3475 0 00.4827.165l.8435-.9633a8.7717 8.7717 0 00.267.0418l.8978.8715a.347.347 0 00.4959.0582l.6863-1.1004c.0877-.0039.1754-.0094.2631-.0166l1.0645.6907a.3478.3478 0 00.4921-.0466l.5043-1.2024c.0829-.0168.1657-.0349.2476-.0546l1.2024.478a.3444.3444 0 00.4721-.1462l.305-1.2685a9.4986 9.4986 0 00.2264-.0963l1.3088.241a.3462.3462 0 00.4373-.2383l.0931-1.2948a12.775 12.775 0 00.1996-.1384l1.3811-.0133a.3442.3442 0 00.3879-.3209l-.1204-1.2792a12.0697 12.0697 0 00.1677-.1767l1.4174-.2612a.3462.3462 0 00.3266-.3935l-.3286-1.222a13.5765 13.5765 0 00.1307-.2093l1.4154-.4985a.3454.3454 0 00.2558-.4536l-.5275-1.1259a8.7256 8.7256 0 00.0899-.2341l1.3741-.7219a.3439.3439 0 00.1787-.4993l-.7113-.9937a9.1373 9.1373 0 00.0449-.2543l1.2944-.9275a.3446.3446 0 00.0963-.5323zM12.0522 4.5705a.703.703 0 01.5765-.098c.3473.0982.6615.2679.9263.4973a.3873.3873 0 01-.5765.098c-.3473-.0982-.6615-.2679-.9263-.4973zm-3.2 7.5508c.2.0 .4-.0 .6-.0l.2 .6h-1l.2-.6zm4.5 2.7l-.4-.3.3-.4c-.3-.2-.7-.3-1.1-.3l-.1.5-.1-.5c-.4.0-.8.1-1.1.3l.3.4-.4.3c-.3-.3-.5-.6-.6-1l.5-.1-.5-.2c0-.4.1-.8.3-1.2l.4.3-.3-.4c.3-.3.6-.5 1-.6l.1.5.1-.5c.4.0.8.2 1.1.4l-.3.4.4-.3c.2.3.3.7.4 1.1l-.5.1.5.2c-.0.4-.2.8-.4 1.1zm3.3-2.1l-.6.0-.2-.6h1l-.2.6z",
52
+ },
53
+ shellscript: {
54
+ viewBox: "0 0 24 24",
55
+ fill: "currentColor",
56
+ d: "M4.814 0h14.371a2.813 2.813 0 0 1 2.814 2.814v18.371A2.813 2.813 0 0 1 19.186 24H4.814A2.813 2.813 0 0 1 2 21.186V2.814A2.813 2.813 0 0 1 4.814 0zM5.8 21.2h12.39v-1.6H5.8zm6.636-4.762L7.394 12.17l1.07-1.258 5.043 4.268zm-1.111-5.889 1.07-1.258 5.043 4.268-5.043 4.268-1.07-1.258 3.972-3.01z",
57
+ },
58
+ default: {
59
+ viewBox: "0 0 24 24",
60
+ fill: "currentColor",
61
+ d: "M 6,1 C 4.354992,1 3,2.354992 3,4 v 16 c 0,1.645008 1.354992,3 3,3 h 12 c 1.645008,0 3,-1.354992 3,-3 V 8 7 A 1.0001,1.0001 0 0 0 20.707031,6.2929687 l -5,-5 A 1.0001,1.0001 0 0 0 15,1 h -1 z m 0,2 h 7 v 3 c 0,1.645008 1.354992,3 3,3 h 3 v 11 c 0,0.564129 -0.435871,1 -1,1 H 6 C 5.4358712,21 5,20.564129 5,20 V 4 C 5,3.4358712 5.4358712,3 6,3 Z M 15,3.4140625 18.585937,7 H 16 C 15.435871,7 15,6.5641288 15,6 Z",
62
+ },
63
+ }
64
+
65
+ /**
66
+ * Shiki transformer that injects a language icon SVG into the pre element.
67
+ */
68
+ export function transformerIcon(): ShikiTransformer {
69
+ return {
70
+ name: "livemark:icon",
71
+ pre(pre) {
72
+ const lang = this.options.lang
73
+ if (!lang) return
74
+
75
+ const iconName = lang in shortcuts ? shortcuts[lang]! : lang
76
+ const icon = iconName in icons ? icons[iconName]! : icons.default!
77
+
78
+ pre.properties.icon = `<svg viewBox="${icon.viewBox}"><path d="${icon.d}" fill="${icon.fill}" /></svg>`
79
+ return pre
80
+ },
81
+ }
82
+ }