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,52 @@
1
+ import { File, Folder } from "lucide-react"
2
+
3
+ interface TreeItem {
4
+ name: string
5
+ isDir: boolean
6
+ children?: TreeItem[]
7
+ }
8
+
9
+ /** Renders a file/directory tree from a JSON-serialized tree structure */
10
+ export function FileTree(props: { tree: string }) {
11
+ const items: TreeItem[] = JSON.parse(props.tree)
12
+
13
+ return (
14
+ <div className="not-prose my-4 rounded-xl border border-border bg-card p-4 font-mono text-sm">
15
+ <TreeItems items={items} depth={0} />
16
+ </div>
17
+ )
18
+ }
19
+
20
+ function TreeItems(props: { items: TreeItem[]; depth: number }) {
21
+ return (
22
+ <ul className={props.depth > 0 ? "ml-4 border-l border-border pl-2" : ""}>
23
+ {props.items.map((item, index) => (
24
+ <TreeNode
25
+ key={`${item.name}-${index}`}
26
+ item={item}
27
+ depth={props.depth}
28
+ />
29
+ ))}
30
+ </ul>
31
+ )
32
+ }
33
+
34
+ function TreeNode(props: { item: TreeItem; depth: number }) {
35
+ const { item, depth } = props
36
+
37
+ return (
38
+ <li className="py-0.5">
39
+ <span className="inline-flex items-center gap-2">
40
+ {item.isDir ? (
41
+ <Folder className="size-4 shrink-0 text-primary" />
42
+ ) : (
43
+ <File className="size-4 shrink-0 text-muted-foreground" />
44
+ )}
45
+ <span className={item.isDir ? "font-semibold" : ""}>{item.name}</span>
46
+ </span>
47
+ {item.children && item.children.length > 0 && (
48
+ <TreeItems items={item.children} depth={depth + 1} />
49
+ )}
50
+ </li>
51
+ )
52
+ }
@@ -0,0 +1,16 @@
1
+ /** Footer shown below article content */
2
+ export function Footer() {
3
+ return (
4
+ <p className="mt-12 text-center text-sm text-muted-foreground">
5
+ Created with <span className="text-red-500">❤</span> and{" "}
6
+ <a
7
+ href="https://github.com/datisthq/livemark"
8
+ target="_blank"
9
+ rel="noopener noreferrer"
10
+ className="font-medium hover:text-foreground transition-colors"
11
+ >
12
+ Livemark
13
+ </a>
14
+ </p>
15
+ )
16
+ }
@@ -0,0 +1,52 @@
1
+ import { type ComponentProps, useEffect, useState } from "react"
2
+ import {
3
+ HoverCard,
4
+ HoverCardContent,
5
+ HoverCardTrigger,
6
+ } from "../elements/hover-card.tsx"
7
+
8
+ const FOOTNOTE_PREFIX = "#user-content-fn-"
9
+
10
+ /** Wraps footnote reference links in a HoverCard that previews footnote content inline */
11
+ export function FootnoteRef(props: ComponentProps<"a">) {
12
+ const href = props.href ?? ""
13
+ if (!href.startsWith(FOOTNOTE_PREFIX)) {
14
+ return <a {...props} />
15
+ }
16
+
17
+ return <FootnoteHoverCard {...props} href={href} />
18
+ }
19
+
20
+ function FootnoteHoverCard(props: ComponentProps<"a"> & { href: string }) {
21
+ const [footnoteHtml, setFootnoteHtml] = useState("")
22
+
23
+ useEffect(() => {
24
+ const targetId = props.href.slice(1)
25
+ const el = document.getElementById(targetId)
26
+ if (!el) return
27
+
28
+ const wrapper = document.createElement("div")
29
+ wrapper.innerHTML = el.innerHTML
30
+ for (const backref of wrapper.querySelectorAll("[data-footnote-backref]")) {
31
+ backref.remove()
32
+ }
33
+ setFootnoteHtml(wrapper.innerHTML)
34
+ }, [props.href])
35
+
36
+ return (
37
+ <HoverCard>
38
+ <HoverCardTrigger render={<a {...props} />} />
39
+ <HoverCardContent
40
+ className="prose dark:prose-invert text-sm w-80 [&>p]:my-1 [&>p:first-child]:mt-0 [&>p:last-child]:mb-0"
41
+ side="top"
42
+ sideOffset={8}
43
+ >
44
+ {footnoteHtml ? (
45
+ <div dangerouslySetInnerHTML={{ __html: footnoteHtml }} />
46
+ ) : (
47
+ <p className="text-muted-foreground">Footnote not found</p>
48
+ )}
49
+ </HoverCardContent>
50
+ </HoverCard>
51
+ )
52
+ }
@@ -0,0 +1,20 @@
1
+ /** Heading component that wraps content in an anchor link to itself */
2
+ function createHeading(Tag: "h1" | "h2" | "h3" | "h4" | "h5" | "h6") {
3
+ return function Heading(props: React.HTMLAttributes<HTMLHeadingElement>) {
4
+ if (!props.id) return <Tag {...props} />
5
+ return (
6
+ <Tag {...props}>
7
+ <a href={`#${props.id}`}>{props.children}</a>
8
+ </Tag>
9
+ )
10
+ }
11
+ }
12
+
13
+ export const headingComponents = {
14
+ h1: createHeading("h1"),
15
+ h2: createHeading("h2"),
16
+ h3: createHeading("h3"),
17
+ h4: createHeading("h4"),
18
+ h5: createHeading("h5"),
19
+ h6: createHeading("h6"),
20
+ }
@@ -0,0 +1,9 @@
1
+ import { Badge } from "../elements/badge.tsx"
2
+
3
+ /** Inline badge rendered from :badge text directives */
4
+ export function InlineBadge(props: {
5
+ label: string
6
+ variant?: "default" | "secondary" | "destructive" | "outline"
7
+ }) {
8
+ return <Badge variant={props.variant}>{props.label}</Badge>
9
+ }
@@ -0,0 +1,10 @@
1
+ import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
2
+
3
+ /** Renders a Lucide icon inline by name */
4
+ export function InlineIcon(props: { name: string; className?: string }) {
5
+ const className = props.className
6
+ ? `inline-block size-4 align-text-bottom ${props.className}`
7
+ : "inline-block size-4 align-text-bottom"
8
+
9
+ return <DynamicIcon name={props.name} className={className} />
10
+ }
@@ -0,0 +1,31 @@
1
+ import { useTocItems } from "../helpers/toc-context.ts"
2
+
3
+ /** Renders the page's table of contents inline within the content */
4
+ export function InlineToc(props: { maxLevel?: string }) {
5
+ const allItems = useTocItems()
6
+ const maxDepth = props.maxLevel ? Number(props.maxLevel) : 4
7
+ const items = allItems.filter(item => item.depth <= maxDepth)
8
+
9
+ if (items.length === 0) return null
10
+
11
+ return (
12
+ <nav className="not-prose my-8 rounded-xl border border-border bg-card p-6">
13
+ <p className="text-base font-semibold mb-4">Table of Contents</p>
14
+ <ul className="text-base space-y-2.5">
15
+ {items.map(item => (
16
+ <li
17
+ key={item.url}
18
+ style={{ paddingLeft: `${(item.depth - 2) * 1.25}rem` }}
19
+ >
20
+ <a
21
+ href={item.url}
22
+ className="text-muted-foreground hover:text-primary transition-colors"
23
+ >
24
+ {item.title}
25
+ </a>
26
+ </li>
27
+ ))}
28
+ </ul>
29
+ </nav>
30
+ )
31
+ }
@@ -0,0 +1,190 @@
1
+ import { Link, useLocation, useMatch } from "@tanstack/react-router"
2
+ import { useHotkey } from "@tanstack/react-hotkeys"
3
+ import { ExternalLink } from "lucide-react"
4
+ import {
5
+ currentSection,
6
+ sectionFirstArticle,
7
+ sectionFlatArticles,
8
+ sortedArticles,
9
+ } from "../content/article.ts"
10
+ import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
11
+ import { sectionIcon } from "../helpers/section.ts"
12
+ import {
13
+ SidebarInset,
14
+ SidebarProvider,
15
+ SidebarTrigger,
16
+ useSidebar,
17
+ } from "../elements/sidebar.tsx"
18
+ import { BackToTop } from "./BackToTop.tsx"
19
+ import { Banner } from "./Banner.tsx"
20
+ import { BlogSidebar } from "./BlogSidebar.tsx"
21
+ import { ChangelogSidebar } from "./ChangelogSidebar.tsx"
22
+ import { MiniSidebar } from "./MiniSidebar.tsx"
23
+ import { Sidebar } from "./Sidebar.tsx"
24
+ import { SiteTitle } from "./SiteTitle.tsx"
25
+
26
+ function splatFor(pathname: string) {
27
+ return { _splat: pathname.replace(/^\/|\/$/g, "") }
28
+ }
29
+
30
+ export function Layout(props: {
31
+ withSidebar?: boolean
32
+ children?: React.ReactNode
33
+ }) {
34
+ const pathname = useLocation({ select: l => l.pathname })
35
+ const articleRoute = useMatch({ from: "/$", shouldThrow: false })
36
+ const activeSection = articleRoute
37
+ ? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
38
+ : undefined
39
+
40
+ useHotkey("J", () =>
41
+ window.scrollBy({ top: window.innerHeight * 0.8, behavior: "smooth" }),
42
+ )
43
+ useHotkey("K", () =>
44
+ window.scrollBy({ top: -window.innerHeight * 0.8, behavior: "smooth" }),
45
+ )
46
+
47
+ const sections = import.meta.env.CONFIG.sections
48
+
49
+ const header = (
50
+ <header className="sticky top-0 z-20 flex h-16 shrink-0 items-center border-b bg-background">
51
+ <div
52
+ className={`flex items-center self-stretch pl-4 ${props.withSidebar ? "pr-4" : "md:pr-20"}`}
53
+ >
54
+ {props.withSidebar ? (
55
+ <>
56
+ <div className="hidden md:flex">
57
+ <SidebarTrigger />
58
+ </div>
59
+ <Link to="/" className="md:hidden flex items-center gap-2">
60
+ <SiteTitle />
61
+ </Link>
62
+ </>
63
+ ) : (
64
+ <Link to="/" className="flex items-center gap-2">
65
+ <SiteTitle />
66
+ </Link>
67
+ )}
68
+ </div>
69
+ <div className="hidden md:flex flex-1 items-center gap-8 self-stretch px-6 text-sm">
70
+ {sections?.length ? (
71
+ sections
72
+ .filter(section => section.position === "header")
73
+ .map(section => {
74
+ const isActive = activeSection?.prefix === section.prefix
75
+ const target =
76
+ section.type === "blog" || section.type === "changelog"
77
+ ? section.prefix
78
+ : sectionFirstArticle.get(section.prefix)
79
+ const latestVersion =
80
+ section.type === "changelog" && section.version
81
+ ? sortedArticles.find(
82
+ a =>
83
+ a.path === sectionFlatArticles.get(section.prefix)?.[0],
84
+ )?.title
85
+ : undefined
86
+ return (
87
+ <Link
88
+ key={section.prefix}
89
+ to={target === "/" ? "/" : "/$"}
90
+ params={target && target !== "/" ? splatFor(target) : {}}
91
+ className={`inline-flex items-center gap-1.5 text-foreground ${
92
+ isActive
93
+ ? "font-medium border-b-2 border-foreground pb-0.5 -mb-0.5"
94
+ : "opacity-80 hover:opacity-100 transition-opacity"
95
+ }`}
96
+ >
97
+ <DynamicIcon
98
+ name={sectionIcon(section)}
99
+ className="size-3.5"
100
+ />
101
+ <span>
102
+ {section.title}
103
+ {latestVersion && (
104
+ <span className="opacity-80"> ({latestVersion})</span>
105
+ )}
106
+ </span>
107
+ </Link>
108
+ )
109
+ })
110
+ ) : (
111
+ <Link
112
+ to="/"
113
+ className="text-foreground font-medium underline underline-offset-4"
114
+ >
115
+ Docs
116
+ </Link>
117
+ )}
118
+ {import.meta.env.CONFIG.links
119
+ ?.filter(
120
+ link =>
121
+ link.position === "header" &&
122
+ (!link.prefix || link.prefix === activeSection?.prefix),
123
+ )
124
+ .map(link => (
125
+ <a
126
+ key={link.url}
127
+ href={link.url}
128
+ target="_blank"
129
+ rel="noopener noreferrer"
130
+ className="text-foreground opacity-80 hover:opacity-100 transition-opacity"
131
+ >
132
+ {link.icon && (
133
+ <DynamicIcon
134
+ name={link.icon}
135
+ className="inline size-3.5 align-[-0.125em]"
136
+ />
137
+ )}{" "}
138
+ {link.title}{" "}
139
+ <ExternalLink className="inline size-3 align-[-0.125em]" />
140
+ </a>
141
+ ))}
142
+ <Banner />
143
+ </div>
144
+ <div className="md:hidden ml-auto pr-2">
145
+ <SidebarTrigger />
146
+ </div>
147
+ </header>
148
+ )
149
+
150
+ const SidebarComponent = !props.withSidebar
151
+ ? MiniSidebar
152
+ : activeSection?.type === "blog"
153
+ ? BlogSidebar
154
+ : activeSection?.type === "changelog"
155
+ ? ChangelogSidebar
156
+ : Sidebar
157
+
158
+ return (
159
+ <SidebarProvider
160
+ key={props.withSidebar ? "sidebar" : "mini"}
161
+ defaultOpen={!!props.withSidebar}
162
+ >
163
+ <SidebarShell SidebarComponent={SidebarComponent} header={header}>
164
+ {props.children}
165
+ </SidebarShell>
166
+ </SidebarProvider>
167
+ )
168
+ }
169
+
170
+ function SidebarShell(props: {
171
+ SidebarComponent: React.ComponentType
172
+ header: React.ReactNode
173
+ children?: React.ReactNode
174
+ }) {
175
+ const { isMobile, setOpenMobile } = useSidebar()
176
+ const onClick = (event: React.MouseEvent<HTMLDivElement>) => {
177
+ if (!isMobile) return
178
+ if ((event.target as HTMLElement).closest("a")) setOpenMobile(false)
179
+ }
180
+ return (
181
+ <div style={{ display: "contents" }} onClick={onClick}>
182
+ <props.SidebarComponent />
183
+ <SidebarInset>
184
+ {props.header}
185
+ <main className="flex-1">{props.children}</main>
186
+ <BackToTop />
187
+ </SidebarInset>
188
+ </div>
189
+ )
190
+ }
@@ -0,0 +1,31 @@
1
+ import { buttonVariants } from "../elements/button.tsx"
2
+ import { cn } from "../helpers/style.ts"
3
+
4
+ /** Styled link button rendered from ::button leaf directives */
5
+ export function LinkButton(props: {
6
+ href: string
7
+ label: string
8
+ variant?:
9
+ | "default"
10
+ | "outline"
11
+ | "secondary"
12
+ | "ghost"
13
+ | "destructive"
14
+ | "link"
15
+ size?: "default" | "sm" | "lg"
16
+ }) {
17
+ return (
18
+ <a
19
+ href={props.href}
20
+ className={cn(
21
+ buttonVariants({
22
+ variant: props.variant,
23
+ size: props.size,
24
+ }),
25
+ "no-underline",
26
+ )}
27
+ >
28
+ {props.label}
29
+ </a>
30
+ )
31
+ }
@@ -0,0 +1,18 @@
1
+ import { Suspense, lazy } from "react"
2
+
3
+ const MermaidRenderer = lazy(() => import("./MermaidRenderer.tsx"))
4
+
5
+ /** Lazily loads and renders a Mermaid diagram */
6
+ export function Mermaid(props: { children: string }) {
7
+ return (
8
+ <Suspense
9
+ fallback={
10
+ <div className="not-prose mermaid flex justify-center">
11
+ Loading diagram…
12
+ </div>
13
+ }
14
+ >
15
+ <MermaidRenderer>{props.children}</MermaidRenderer>
16
+ </Suspense>
17
+ )
18
+ }
@@ -0,0 +1,37 @@
1
+ import { useEffect, useRef } from "react"
2
+ import mermaid from "mermaid"
3
+
4
+ mermaid.initialize({ startOnLoad: false })
5
+
6
+ /** Renders a Mermaid diagram from a chart definition string */
7
+ export default function MermaidRenderer(props: { children: string }) {
8
+ const ref = useRef<HTMLDivElement>(null)
9
+
10
+ useEffect(() => {
11
+ const el = ref.current
12
+ if (!el) return
13
+
14
+ const render = () => {
15
+ const isDark = document.documentElement.classList.contains("dark")
16
+ mermaid.initialize({
17
+ startOnLoad: false,
18
+ theme: isDark ? "dark" : "default",
19
+ })
20
+ el.textContent = props.children
21
+ el.removeAttribute("data-processed")
22
+ mermaid.run({ nodes: [el] })
23
+ }
24
+
25
+ render()
26
+
27
+ const observer = new MutationObserver(render)
28
+ observer.observe(document.documentElement, {
29
+ attributes: true,
30
+ attributeFilter: ["class"],
31
+ })
32
+
33
+ return () => observer.disconnect()
34
+ }, [props.children])
35
+
36
+ return <div ref={ref} className="not-prose mermaid flex justify-center" />
37
+ }
@@ -0,0 +1,49 @@
1
+ import { Link } from "@tanstack/react-router"
2
+ import { useHotkey } from "@tanstack/react-hotkeys"
3
+ import {
4
+ Sidebar as SidebarRoot,
5
+ SidebarContent,
6
+ SidebarFooter,
7
+ SidebarHeader,
8
+ SidebarMenu,
9
+ SidebarMenuButton,
10
+ SidebarMenuItem,
11
+ SidebarRail,
12
+ useSidebar,
13
+ } from "../elements/sidebar.tsx"
14
+ import { Search } from "./Search.tsx"
15
+ import { SidebarLinks } from "./SidebarLinks.tsx"
16
+ import { SidebarSections } from "./SidebarSections.tsx"
17
+ import { SiteTitle } from "./SiteTitle.tsx"
18
+ import { Theme } from "./Theme.tsx"
19
+
20
+ /** Minimal sidebar used on pages without an article tree. On desktop the
21
+ * SidebarProvider keeps it off-canvas; on mobile it drives the sheet so
22
+ * users can still reach Sections / Links / Search / Theme. */
23
+ export function MiniSidebar() {
24
+ const { toggleSidebar } = useSidebar()
25
+ useHotkey("S", toggleSidebar)
26
+
27
+ return (
28
+ <SidebarRoot>
29
+ <SidebarHeader>
30
+ <SidebarMenu>
31
+ <SidebarMenuItem>
32
+ <SidebarMenuButton size="lg" render={<Link to="/" />}>
33
+ <SiteTitle />
34
+ </SidebarMenuButton>
35
+ </SidebarMenuItem>
36
+ </SidebarMenu>
37
+ </SidebarHeader>
38
+ <SidebarContent>
39
+ <SidebarSections />
40
+ <SidebarLinks />
41
+ </SidebarContent>
42
+ <SidebarFooter className="p-4 space-y-2">
43
+ <Search />
44
+ <Theme />
45
+ </SidebarFooter>
46
+ <SidebarRail />
47
+ </SidebarRoot>
48
+ )
49
+ }
@@ -0,0 +1,27 @@
1
+ import { Link } from "@tanstack/react-router"
2
+
3
+ export function NotFound({ children }: { children?: any }) {
4
+ return (
5
+ <div className="min-h-[60vh] flex flex-col items-center justify-center text-center px-4">
6
+ <h1 className="font-sans text-6xl font-semibold mb-4">404</h1>
7
+ <div className="text-muted-foreground mb-6">
8
+ {children || <p>The page you're looking for doesn't exist.</p>}
9
+ </div>
10
+ <div className="flex items-center gap-4">
11
+ <button
12
+ type="button"
13
+ onClick={() => window.history.back()}
14
+ className="bg-primary text-primary-foreground px-4 py-2 text-sm font-medium hover:bg-primary/80 transition-colors"
15
+ >
16
+ Go Back
17
+ </button>
18
+ <Link
19
+ to="/"
20
+ className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
21
+ >
22
+ Home
23
+ </Link>
24
+ </div>
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,74 @@
1
+ import { Check, Clipboard } from "lucide-react"
2
+ import { useState } from "react"
3
+
4
+ type Manager = "npm" | "pnpm" | "yarn" | "bun"
5
+
6
+ const managers: Manager[] = ["npm", "pnpm", "yarn", "bun"]
7
+
8
+ interface PackageTabsProps {
9
+ npm: string
10
+ pnpm: string
11
+ yarn: string
12
+ bun: string
13
+ }
14
+
15
+ /** Tabbed code block showing a command for each package manager */
16
+ export function PackageTabs(props: PackageTabsProps) {
17
+ const [active, setActive] = useState<Manager>("npm")
18
+ const [copied, setCopied] = useState(false)
19
+
20
+ const commands: Record<Manager, string> = {
21
+ npm: props.npm,
22
+ pnpm: props.pnpm,
23
+ yarn: props.yarn,
24
+ bun: props.bun,
25
+ }
26
+
27
+ const handleCopy = () => {
28
+ navigator.clipboard.writeText(commands[active])
29
+ setCopied(true)
30
+ setTimeout(() => setCopied(false), 2000)
31
+ }
32
+
33
+ return (
34
+ <figure className="not-prose my-4 relative rounded-xl border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
35
+ <div className="flex items-center border-b">
36
+ <div className="flex flex-1 items-center">
37
+ {managers.map(pm => (
38
+ <button
39
+ key={pm}
40
+ type="button"
41
+ onClick={() => setActive(pm)}
42
+ className={`px-3 py-2 text-xs font-medium transition-colors ${
43
+ pm === active
44
+ ? "text-foreground border-b-2 border-foreground"
45
+ : "text-muted-foreground hover:text-foreground"
46
+ }`}
47
+ >
48
+ {pm}
49
+ </button>
50
+ ))}
51
+ </div>
52
+ <button
53
+ type="button"
54
+ onClick={handleCopy}
55
+ className="me-2 p-1 rounded-md text-muted-foreground hover:text-foreground transition-colors"
56
+ aria-label="Copy code"
57
+ >
58
+ {copied ? (
59
+ <Check className="size-3.5" />
60
+ ) : (
61
+ <Clipboard className="size-3.5" />
62
+ )}
63
+ </button>
64
+ </div>
65
+ <div className="text-[0.8125rem] py-3.5 overflow-auto max-h-[600px]">
66
+ <pre className="min-w-full w-max">
67
+ <code className="flex flex-col">
68
+ <span className="px-4">{commands[active]}</span>
69
+ </code>
70
+ </pre>
71
+ </div>
72
+ </figure>
73
+ )
74
+ }