livemark 0.2.0 → 0.7.1

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 (348) 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/package.json +120 -12
  226. package/skills/authoring/SKILL.md +97 -0
  227. package/skills/configuration/SKILL.md +120 -0
  228. package/skills/customization/SKILL.md +118 -0
  229. package/build/descriptor/index.d.ts +0 -3
  230. package/build/descriptor/index.js +0 -4
  231. package/build/descriptor/load.d.ts +0 -8
  232. package/build/descriptor/load.js +0 -40
  233. package/build/descriptor/node.d.ts +0 -125
  234. package/build/descriptor/node.js +0 -9
  235. package/build/descriptor/save.d.ts +0 -8
  236. package/build/descriptor/save.js +0 -17
  237. package/build/descriptor/save.spec.js +0 -80
  238. package/build/descriptor/validate.d.ts +0 -9
  239. package/build/descriptor/validate.js +0 -14
  240. package/build/descriptor/validate.spec.js +0 -124
  241. package/build/dialect/Dialect.d.ts +0 -88
  242. package/build/dialect/Dialect.js +0 -2
  243. package/build/dialect/index.d.ts +0 -3
  244. package/build/dialect/index.js +0 -3
  245. package/build/dialect/load.d.ts +0 -4
  246. package/build/dialect/load.js +0 -7
  247. package/build/dialect/save.d.ts +0 -9
  248. package/build/dialect/save.js +0 -13
  249. package/build/dialect/validate.d.ts +0 -1
  250. package/build/dialect/validate.js +0 -4
  251. package/build/metadata/index.d.ts +0 -3
  252. package/build/metadata/index.js +0 -4
  253. package/build/metadata/load.d.ts +0 -10
  254. package/build/metadata/load.js +0 -55
  255. package/build/metadata/node.d.ts +0 -125
  256. package/build/metadata/node.js +0 -9
  257. package/build/metadata/save.d.ts +0 -8
  258. package/build/metadata/save.js +0 -17
  259. package/build/metadata/save.spec.js +0 -80
  260. package/build/metadata/validate.d.ts +0 -9
  261. package/build/metadata/validate.js +0 -14
  262. package/build/metadata/validate.spec.d.ts +0 -1
  263. package/build/metadata/validate.spec.js +0 -124
  264. package/build/package/Contributor.d.ts +0 -21
  265. package/build/package/Contributor.js +0 -2
  266. package/build/package/Package.d.ts +0 -65
  267. package/build/package/Package.js +0 -2
  268. package/build/package/index.d.ts +0 -4
  269. package/build/package/index.js +0 -3
  270. package/build/package/load.d.ts +0 -4
  271. package/build/package/load.js +0 -7
  272. package/build/package/save.d.ts +0 -9
  273. package/build/package/save.js +0 -13
  274. package/build/package/validate.d.ts +0 -1
  275. package/build/package/validate.js +0 -4
  276. package/build/resource/License.d.ts +0 -18
  277. package/build/resource/License.js +0 -2
  278. package/build/resource/Resource.d.ts +0 -67
  279. package/build/resource/Resource.js +0 -2
  280. package/build/resource/Source.d.ts +0 -17
  281. package/build/resource/Source.js +0 -2
  282. package/build/resource/index.d.ts +0 -5
  283. package/build/resource/index.js +0 -2
  284. package/build/resource/load.d.ts +0 -1
  285. package/build/resource/load.js +0 -2
  286. package/build/resource/save.d.ts +0 -9
  287. package/build/resource/save.js +0 -13
  288. package/build/resource/types/index.d.ts +0 -1
  289. package/build/resource/types/index.js +0 -2
  290. package/build/resource/types/table.d.ts +0 -29
  291. package/build/resource/types/table.js +0 -2
  292. package/build/resource/validate.d.ts +0 -1
  293. package/build/resource/validate.js +0 -4
  294. package/build/schema/Field.d.ts +0 -5
  295. package/build/schema/Field.js +0 -2
  296. package/build/schema/Schema.d.ts +0 -55
  297. package/build/schema/Schema.js +0 -2
  298. package/build/schema/fields/any.d.ts +0 -20
  299. package/build/schema/fields/any.js +0 -2
  300. package/build/schema/fields/array.d.ts +0 -32
  301. package/build/schema/fields/array.js +0 -2
  302. package/build/schema/fields/base.d.ts +0 -42
  303. package/build/schema/fields/base.js +0 -2
  304. package/build/schema/fields/boolean.d.ts +0 -28
  305. package/build/schema/fields/boolean.js +0 -2
  306. package/build/schema/fields/date.d.ts +0 -35
  307. package/build/schema/fields/date.js +0 -2
  308. package/build/schema/fields/datetime.d.ts +0 -35
  309. package/build/schema/fields/datetime.js +0 -2
  310. package/build/schema/fields/duration.d.ts +0 -28
  311. package/build/schema/fields/duration.js +0 -2
  312. package/build/schema/fields/geojson.d.ts +0 -26
  313. package/build/schema/fields/geojson.js +0 -2
  314. package/build/schema/fields/geopoint.d.ts +0 -27
  315. package/build/schema/fields/geopoint.js +0 -2
  316. package/build/schema/fields/index.d.ts +0 -16
  317. package/build/schema/fields/index.js +0 -17
  318. package/build/schema/fields/integer.d.ts +0 -37
  319. package/build/schema/fields/integer.js +0 -2
  320. package/build/schema/fields/list.d.ts +0 -36
  321. package/build/schema/fields/list.js +0 -2
  322. package/build/schema/fields/number.d.ts +0 -40
  323. package/build/schema/fields/number.js +0 -2
  324. package/build/schema/fields/object.d.ts +0 -32
  325. package/build/schema/fields/object.js +0 -2
  326. package/build/schema/fields/string.d.ts +0 -40
  327. package/build/schema/fields/string.js +0 -2
  328. package/build/schema/fields/time.d.ts +0 -35
  329. package/build/schema/fields/time.js +0 -2
  330. package/build/schema/fields/year.d.ts +0 -28
  331. package/build/schema/fields/year.js +0 -2
  332. package/build/schema/fields/yearmonth.d.ts +0 -28
  333. package/build/schema/fields/yearmonth.js +0 -2
  334. package/build/schema/index.d.ts +0 -4
  335. package/build/schema/index.js +0 -2
  336. package/build/schema/load.d.ts +0 -1
  337. package/build/schema/load.js +0 -2
  338. package/build/schema/save.d.ts +0 -9
  339. package/build/schema/save.js +0 -13
  340. package/build/schema/validate.d.ts +0 -1
  341. package/build/schema/validate.js +0 -4
  342. package/build/tsconfig.tsbuildinfo +0 -1
  343. package/index.ts +0 -0
  344. package/tsconfig.json +0 -10
  345. package/typedoc.json +0 -4
  346. /package/build/{descriptor/save.spec.d.ts → actions/changelog/build.unit.d.ts} +0 -0
  347. /package/build/{descriptor/validate.spec.d.ts → actions/config/define.unit.d.ts} +0 -0
  348. /package/build/{metadata/save.spec.d.ts → actions/config/load.unit.d.ts} +0 -0
@@ -0,0 +1,111 @@
1
+ import { Link } from "@tanstack/react-router"
2
+ import { History } from "lucide-react"
3
+ import {
4
+ currentSection,
5
+ sectionFlatArticles,
6
+ sortedArticles,
7
+ } from "../content/article.ts"
8
+ import { Separator } from "../elements/separator.tsx"
9
+ import { TocContext } from "../helpers/toc-context.ts"
10
+ import { Footer } from "./Footer.tsx"
11
+ import { PageToolbar } from "./PageToolbar.tsx"
12
+ import { MobileToc, Toc } from "./Toc.tsx"
13
+
14
+ /** Auto-generated changelog index listing all versions by date */
15
+ export function ChangelogIndex(props: { sectionPrefix: string }) {
16
+ const section = currentSection(props.sectionPrefix)
17
+ const flat = sectionFlatArticles.get(props.sectionPrefix) ?? []
18
+ const entries = flat
19
+ .map(p => sortedArticles.find(a => a.path === p))
20
+ .filter(a => a !== undefined)
21
+
22
+ const tocItems = entries.map(entry => ({
23
+ url: `#${slugOf(entry.path)}`,
24
+ title: entry.title,
25
+ depth: 2,
26
+ }))
27
+
28
+ const content = [
29
+ `# ${section?.title ?? "Changelog"}`,
30
+ ...entries.map(entry => {
31
+ const parts = [`## ${entry.title}`]
32
+ if (entry.date) parts.push(formatDate(entry.date))
33
+ if (entry.description) parts.push("", entry.description)
34
+ return parts.join("\n")
35
+ }),
36
+ ].join("\n\n")
37
+
38
+ return (
39
+ <TocContext.Provider value={tocItems}>
40
+ <MobileToc items={tocItems}>
41
+ <PageToolbar content={content} />
42
+ </MobileToc>
43
+ <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
44
+ <div className="flex-1 min-w-0 mx-auto max-w-3xl">
45
+ <div className="prose dark:prose-invert max-w-none mb-8">
46
+ <h1 id="top">
47
+ <a href="#top">{section?.title ?? "Changelog"}</a>
48
+ </h1>
49
+ {entries[0]?.date && (
50
+ <p className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-muted-foreground !-mt-4 !mb-0">
51
+ <span className="flex items-center gap-1.5">
52
+ <History className="size-3.5" />
53
+ Updated {formatDate(entries[0].date)}
54
+ </span>
55
+ </p>
56
+ )}
57
+ </div>
58
+ <div className="space-y-6">
59
+ {entries.map((entry, i) => (
60
+ <article key={entry.path} id={slugOf(entry.path)}>
61
+ {i > 0 && <Separator className="mb-6" />}
62
+ <h2 className="text-2xl">
63
+ <Link
64
+ to="/$"
65
+ params={{ _splat: entry.path.replace(/^\/|\/$/g, "") }}
66
+ className="font-medium hover:text-primary transition-colors"
67
+ >
68
+ {entry.title}
69
+ </Link>
70
+ </h2>
71
+ {entry.date && (
72
+ <p className="text-sm text-muted-foreground mt-1">
73
+ {formatDate(entry.date)}
74
+ </p>
75
+ )}
76
+ {entry.description && (
77
+ <p className="text-muted-foreground mt-2">
78
+ {entry.description}
79
+ </p>
80
+ )}
81
+ </article>
82
+ ))}
83
+ </div>
84
+ <div className="mt-10">
85
+ <Footer />
86
+ </div>
87
+ </div>
88
+ <Toc items={tocItems}>
89
+ <PageToolbar content={content} />
90
+ </Toc>
91
+ </div>
92
+ </TocContext.Provider>
93
+ )
94
+ }
95
+
96
+ function slugOf(path: string) {
97
+ return (
98
+ path
99
+ .replace(/^\/|\/$/g, "")
100
+ .split("/")
101
+ .pop() ?? ""
102
+ )
103
+ }
104
+
105
+ function formatDate(iso: string) {
106
+ return new Date(iso).toLocaleDateString("en-US", {
107
+ year: "numeric",
108
+ month: "short",
109
+ day: "numeric",
110
+ })
111
+ }
@@ -0,0 +1,120 @@
1
+ import { Link, useLocation } from "@tanstack/react-router"
2
+ import { useHotkey } from "@tanstack/react-hotkeys"
3
+ import { Clock, List } from "lucide-react"
4
+ import {
5
+ currentSection,
6
+ sectionFlatArticles,
7
+ sortedArticles,
8
+ } from "../content/article.ts"
9
+ import {
10
+ Sidebar as SidebarRoot,
11
+ SidebarContent,
12
+ SidebarFooter,
13
+ SidebarGroup,
14
+ SidebarGroupContent,
15
+ SidebarGroupLabel,
16
+ SidebarHeader,
17
+ SidebarMenu,
18
+ SidebarMenuButton,
19
+ SidebarMenuItem,
20
+ SidebarMenuSub,
21
+ SidebarMenuSubButton,
22
+ SidebarMenuSubItem,
23
+ SidebarRail,
24
+ useSidebar,
25
+ } from "../elements/sidebar.tsx"
26
+ import { Search } from "./Search.tsx"
27
+ import { SidebarLinks } from "./SidebarLinks.tsx"
28
+ import { SidebarSections } from "./SidebarSections.tsx"
29
+ import { SiteTitle } from "./SiteTitle.tsx"
30
+ import { Theme } from "./Theme.tsx"
31
+
32
+ /** Changelog-specific sidebar with Releases and shared Links */
33
+ export function ChangelogSidebar() {
34
+ const pathname = useLocation({ select: l => l.pathname })
35
+ const { toggleSidebar } = useSidebar()
36
+
37
+ useHotkey("S", toggleSidebar)
38
+
39
+ const section = currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
40
+ const flat = section ? (sectionFlatArticles.get(section.prefix) ?? []) : []
41
+ const entries = flat
42
+ .map(p => sortedArticles.find(a => a.path === p))
43
+ .filter(a => a !== undefined)
44
+ const recent = entries.slice(0, 10)
45
+
46
+ const normalized = `/${pathname.replace(/^\/|\/$/g, "")}/`
47
+ const isIndex = section ? normalized === section.prefix : false
48
+ const sectionPath = (section?.prefix ?? "/changelog/").replace(/^\/|\/$/g, "")
49
+
50
+ return (
51
+ <SidebarRoot>
52
+ <SidebarHeader>
53
+ <SidebarMenu>
54
+ <SidebarMenuItem>
55
+ <SidebarMenuButton size="lg" render={<Link to="/" />}>
56
+ <SiteTitle />
57
+ </SidebarMenuButton>
58
+ </SidebarMenuItem>
59
+ </SidebarMenu>
60
+ </SidebarHeader>
61
+ <SidebarContent>
62
+ <SidebarSections />
63
+ <SidebarGroup>
64
+ <SidebarGroupLabel className="uppercase font-mono text-xs tracking-widest">
65
+ Releases
66
+ </SidebarGroupLabel>
67
+ <SidebarGroupContent>
68
+ <SidebarMenu>
69
+ <SidebarMenuItem>
70
+ <SidebarMenuButton
71
+ isActive={isIndex}
72
+ className={isIndex ? "" : "opacity-75"}
73
+ render={<Link to="/$" params={{ _splat: sectionPath }} />}
74
+ >
75
+ <List className="size-4" />
76
+ <span>All Releases</span>
77
+ </SidebarMenuButton>
78
+ </SidebarMenuItem>
79
+ <SidebarMenuItem>
80
+ <SidebarMenuButton className="opacity-75">
81
+ <Clock className="size-4" />
82
+ <span>Recent Releases</span>
83
+ </SidebarMenuButton>
84
+ <SidebarMenuSub>
85
+ {recent.map(entry => {
86
+ const active = normalized === entry.path
87
+ return (
88
+ <SidebarMenuSubItem key={entry.path}>
89
+ <SidebarMenuSubButton
90
+ isActive={active}
91
+ className={active ? "" : "opacity-75"}
92
+ render={
93
+ <Link
94
+ to="/$"
95
+ params={{
96
+ _splat: entry.path.replace(/^\/|\/$/g, ""),
97
+ }}
98
+ />
99
+ }
100
+ >
101
+ <span>{entry.title}</span>
102
+ </SidebarMenuSubButton>
103
+ </SidebarMenuSubItem>
104
+ )
105
+ })}
106
+ </SidebarMenuSub>
107
+ </SidebarMenuItem>
108
+ </SidebarMenu>
109
+ </SidebarGroupContent>
110
+ </SidebarGroup>
111
+ <SidebarLinks />
112
+ </SidebarContent>
113
+ <SidebarFooter className="p-4 space-y-2">
114
+ <Search />
115
+ <Theme />
116
+ </SidebarFooter>
117
+ <SidebarRail />
118
+ </SidebarRoot>
119
+ )
120
+ }
@@ -0,0 +1,135 @@
1
+ import { Check, ChevronDown, Clipboard, WrapText } from "lucide-react"
2
+ import { useRef, useState } from "react"
3
+ import {
4
+ Tooltip,
5
+ TooltipContent,
6
+ TooltipTrigger,
7
+ } from "../elements/tooltip.tsx"
8
+
9
+ export function CodeBlock(props: React.ComponentProps<"pre">) {
10
+ const [copied, setCopied] = useState(false)
11
+ const [wrap, setWrap] = useState(false)
12
+ const [expanded, setExpanded] = useState(false)
13
+ const preRef = useRef<HTMLPreElement>(null)
14
+ const extra = props as Record<string, unknown>
15
+ const meta = extra["data-meta"] as string | undefined
16
+ const title = meta?.match(/title="([^"]+)"/)?.[1]
17
+ const maxLines = meta?.match(/maxLines=(\d+)/)?.[1]
18
+ const icon = extra.icon as string | undefined
19
+ const style = props.style as Record<string, string> | undefined
20
+ const collapsedHeight = maxLines
21
+ ? `${Number(maxLines) * 1.5 + 1.75}rem`
22
+ : undefined
23
+
24
+ const handleCopy = () => {
25
+ const text = preRef.current?.textContent ?? ""
26
+ navigator.clipboard.writeText(text)
27
+ setCopied(true)
28
+ setTimeout(() => setCopied(false), 2000)
29
+ }
30
+
31
+ const handleWrapToggle = () => {
32
+ setWrap(prev => !prev)
33
+ }
34
+
35
+ return (
36
+ <figure
37
+ className="shiki not-prose my-4 relative rounded-xl border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar"
38
+ style={{ ...style, backgroundColor: undefined }}
39
+ >
40
+ {title ? (
41
+ <div className="flex items-center gap-2 h-9.5 border-b px-4 text-muted-foreground">
42
+ {icon && (
43
+ <span
44
+ className="size-3.5 [&>svg]:size-full"
45
+ dangerouslySetInnerHTML={{ __html: icon }}
46
+ />
47
+ )}
48
+ <span className="flex-1 truncate text-xs font-medium">{title}</span>
49
+ <Tooltip>
50
+ <TooltipTrigger
51
+ onClick={handleWrapToggle}
52
+ aria-label={wrap ? "Disable wrap" : "Enable wrap"}
53
+ className={`-me-2 p-1.5 rounded-md hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
54
+ >
55
+ <WrapText className="size-3.5" />
56
+ </TooltipTrigger>
57
+ <TooltipContent>
58
+ {wrap ? "Disable wrap" : "Enable wrap"}
59
+ </TooltipContent>
60
+ </Tooltip>
61
+ <Tooltip>
62
+ <TooltipTrigger
63
+ onClick={handleCopy}
64
+ aria-label={copied ? "Copied" : "Copy code"}
65
+ className="-me-2 p-1.5 rounded-md hover:text-foreground transition-colors"
66
+ >
67
+ {copied ? (
68
+ <Check className="size-3.5" />
69
+ ) : (
70
+ <Clipboard className="size-3.5" />
71
+ )}
72
+ </TooltipTrigger>
73
+ <TooltipContent>{copied ? "Copied!" : "Copy code"}</TooltipContent>
74
+ </Tooltip>
75
+ </div>
76
+ ) : (
77
+ <div className="empty:hidden absolute top-2 right-2 z-10 backdrop-blur-lg rounded-lg text-muted-foreground">
78
+ <Tooltip>
79
+ <TooltipTrigger
80
+ onClick={handleWrapToggle}
81
+ aria-label={wrap ? "Disable wrap" : "Enable wrap"}
82
+ className={`p-1.5 rounded-md hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
83
+ >
84
+ <WrapText className="size-3.5" />
85
+ </TooltipTrigger>
86
+ <TooltipContent>
87
+ {wrap ? "Disable wrap" : "Enable wrap"}
88
+ </TooltipContent>
89
+ </Tooltip>
90
+ <Tooltip>
91
+ <TooltipTrigger
92
+ onClick={handleCopy}
93
+ aria-label={copied ? "Copied" : "Copy code"}
94
+ className="p-1.5 rounded-md hover:text-foreground transition-colors"
95
+ >
96
+ {copied ? (
97
+ <Check className="size-3.5" />
98
+ ) : (
99
+ <Clipboard className="size-3.5" />
100
+ )}
101
+ </TooltipTrigger>
102
+ <TooltipContent>{copied ? "Copied!" : "Copy code"}</TooltipContent>
103
+ </Tooltip>
104
+ </div>
105
+ )}
106
+ <div
107
+ className="text-[0.8125rem] py-3.5 overflow-auto transition-[max-height] duration-300"
108
+ style={{
109
+ maxHeight: collapsedHeight && !expanded ? collapsedHeight : "600px",
110
+ }}
111
+ >
112
+ <pre
113
+ {...props}
114
+ ref={preRef}
115
+ style={undefined}
116
+ className={
117
+ wrap
118
+ ? "min-w-full whitespace-pre-wrap break-words [&>code]:flex [&>code]:flex-col"
119
+ : "min-w-full w-max [&>code]:flex [&>code]:flex-col"
120
+ }
121
+ />
122
+ </div>
123
+ {collapsedHeight && !expanded && (
124
+ <button
125
+ type="button"
126
+ onClick={() => setExpanded(true)}
127
+ className="flex items-center justify-center gap-1 w-full py-1.5 text-xs text-muted-foreground hover:text-foreground border-t cursor-pointer transition-colors"
128
+ >
129
+ <ChevronDown className="size-3.5" />
130
+ Expand
131
+ </button>
132
+ )}
133
+ </figure>
134
+ )
135
+ }
@@ -0,0 +1,83 @@
1
+ import { Children, useEffect, useState } from "react"
2
+
3
+ /** Tabbed code block component for grouping multiple code blocks with optional sync */
4
+ export function CodeTabs(props: {
5
+ tabs: string
6
+ sync?: string
7
+ children: React.ReactNode
8
+ }) {
9
+ const names = JSON.parse(props.tabs) as string[]
10
+ const children = Children.toArray(props.children)
11
+
12
+ const getInitialIndex = () => {
13
+ if (!props.sync) return 0
14
+ const saved =
15
+ typeof window !== "undefined"
16
+ ? localStorage.getItem("livemark-tab-" + props.sync)
17
+ : null
18
+ if (!saved) return 0
19
+ const idx = names.indexOf(saved)
20
+ return idx >= 0 ? idx : 0
21
+ }
22
+
23
+ const [active, setActive] = useState(getInitialIndex)
24
+
25
+ const handleSelect = (i: number) => {
26
+ setActive(i)
27
+ if (props.sync) {
28
+ localStorage.setItem("livemark-tab-" + props.sync, names[i]!)
29
+ window.dispatchEvent(
30
+ new CustomEvent("livemark-tab-sync", {
31
+ detail: { sync: props.sync, title: names[i] },
32
+ }),
33
+ )
34
+ }
35
+ }
36
+
37
+ useEffect(() => {
38
+ if (!props.sync) return
39
+ const handler = (e: Event) => {
40
+ const { sync, title } = (
41
+ e as CustomEvent<{ sync: string; title: string }>
42
+ ).detail
43
+ if (sync !== props.sync) return
44
+ const idx = names.indexOf(title)
45
+ if (idx >= 0) setActive(idx)
46
+ }
47
+ window.addEventListener("livemark-tab-sync", handler)
48
+ return () => window.removeEventListener("livemark-tab-sync", handler)
49
+ }, [props.sync, names])
50
+
51
+ return (
52
+ <div className="not-prose my-4 rounded-xl border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
53
+ <div className="flex border-b">
54
+ {names.map((name, i) => (
55
+ <button
56
+ key={name}
57
+ type="button"
58
+ onClick={() => handleSelect(i)}
59
+ className={`px-4 py-2 text-xs font-medium transition-colors cursor-pointer ${
60
+ i === active
61
+ ? "text-foreground border-b-2 border-primary -mb-px"
62
+ : "text-muted-foreground hover:text-foreground"
63
+ }`}
64
+ >
65
+ {name}
66
+ </button>
67
+ ))}
68
+ </div>
69
+ {children.map((child, i) => (
70
+ <div
71
+ key={names[i]}
72
+ className={
73
+ i === active
74
+ ? "[&>figure]:my-0 [&>figure]:rounded-none [&>figure]:border-0 [&>figure]:shadow-none"
75
+ : "hidden"
76
+ }
77
+ >
78
+ {child}
79
+ </div>
80
+ ))}
81
+ </div>
82
+ )
83
+ }
@@ -0,0 +1,4 @@
1
+ /** Single column within a Columns grid */
2
+ export function Column(props: { children: React.ReactNode }) {
3
+ return <div className="max-w-none">{props.children}</div>
4
+ }
@@ -0,0 +1,12 @@
1
+ /** Grid layout for arranging content side by side */
2
+ export function Columns(props: { cols?: string; children: React.ReactNode }) {
3
+ const cols = props.cols ? parseInt(props.cols, 10) : undefined
4
+ const style = cols
5
+ ? { gridTemplateColumns: `repeat(${cols}, 1fr)` }
6
+ : undefined
7
+ return (
8
+ <div className="grid gap-6 my-4 sm:grid-cols-2" style={style}>
9
+ {props.children}
10
+ </div>
11
+ )
12
+ }
@@ -0,0 +1,7 @@
1
+ export function Content(props: { children: React.ReactNode }) {
2
+ return (
3
+ <div className="w-full max-w-7xl mx-auto px-4 md:px-10">
4
+ {props.children}
5
+ </div>
6
+ )
7
+ }
@@ -0,0 +1,7 @@
1
+ /** Simple wrapper that provides a title prop for ContentTabs to read */
2
+ export function ContentTab(props: {
3
+ title: string
4
+ children: React.ReactNode
5
+ }) {
6
+ return <div>{props.children}</div>
7
+ }
@@ -0,0 +1,82 @@
1
+ import { Children, isValidElement, useEffect, useState } from "react"
2
+
3
+ /** Tabbed container component for grouping content tabs with optional sync across groups */
4
+ export function ContentTabs(props: {
5
+ sync?: string
6
+ children: React.ReactNode
7
+ }) {
8
+ const children = Children.toArray(props.children)
9
+ const titles = children.map(child =>
10
+ isValidElement<{ title: string }>(child) ? child.props.title : "",
11
+ )
12
+
13
+ const getInitialIndex = () => {
14
+ if (!props.sync) return 0
15
+ const saved =
16
+ typeof window !== "undefined"
17
+ ? localStorage.getItem("livemark-tab-" + props.sync)
18
+ : null
19
+ if (!saved) return 0
20
+ const idx = titles.indexOf(saved)
21
+ return idx >= 0 ? idx : 0
22
+ }
23
+
24
+ const [active, setActive] = useState(getInitialIndex)
25
+
26
+ const handleSelect = (i: number) => {
27
+ setActive(i)
28
+ if (props.sync) {
29
+ localStorage.setItem("livemark-tab-" + props.sync, titles[i]!)
30
+ window.dispatchEvent(
31
+ new CustomEvent("livemark-tab-sync", {
32
+ detail: { sync: props.sync, title: titles[i] },
33
+ }),
34
+ )
35
+ }
36
+ }
37
+
38
+ useEffect(() => {
39
+ if (!props.sync) return
40
+ const handler = (e: Event) => {
41
+ const { sync, title } = (
42
+ e as CustomEvent<{ sync: string; title: string }>
43
+ ).detail
44
+ if (sync !== props.sync) return
45
+ const idx = titles.indexOf(title)
46
+ if (idx >= 0) setActive(idx)
47
+ }
48
+ window.addEventListener("livemark-tab-sync", handler)
49
+ return () => window.removeEventListener("livemark-tab-sync", handler)
50
+ }, [props.sync, titles])
51
+
52
+ return (
53
+ <div className="not-prose my-4 rounded-xl border bg-card">
54
+ <div className="flex border-b">
55
+ {titles.map((title, i) => (
56
+ <button
57
+ key={title}
58
+ type="button"
59
+ onClick={() => handleSelect(i)}
60
+ className={`px-4 py-2 text-sm font-medium transition-colors cursor-pointer ${
61
+ i === active
62
+ ? "text-foreground border-b-2 border-primary -mb-px"
63
+ : "text-muted-foreground hover:text-foreground"
64
+ }`}
65
+ >
66
+ {title}
67
+ </button>
68
+ ))}
69
+ </div>
70
+ {children.map((child, i) => (
71
+ <div
72
+ key={titles[i]}
73
+ className={
74
+ i === active ? "p-4 prose dark:prose-invert max-w-none" : "hidden"
75
+ }
76
+ >
77
+ {child}
78
+ </div>
79
+ ))}
80
+ </div>
81
+ )
82
+ }
@@ -0,0 +1,54 @@
1
+ import type { ErrorComponentProps } from "@tanstack/react-router"
2
+ import {
3
+ ErrorComponent,
4
+ Link,
5
+ rootRouteId,
6
+ useMatch,
7
+ useRouter,
8
+ } from "@tanstack/react-router"
9
+
10
+ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
11
+ const router = useRouter()
12
+ const isRoot = useMatch({
13
+ strict: false,
14
+ select: state => state.id === rootRouteId,
15
+ })
16
+
17
+ console.error(error)
18
+
19
+ return (
20
+ <div className="min-h-[60vh] flex flex-col items-center justify-center gap-6 px-4">
21
+ <ErrorComponent error={error} />
22
+ <div className="flex gap-3 items-center flex-wrap">
23
+ <button
24
+ type="button"
25
+ onClick={() => {
26
+ router.invalidate()
27
+ }}
28
+ className="bg-primary text-primary-foreground px-4 py-2 text-sm font-medium hover:bg-primary/80 transition-colors"
29
+ >
30
+ Try Again
31
+ </button>
32
+ {isRoot ? (
33
+ <Link
34
+ to="/"
35
+ className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
36
+ >
37
+ Home
38
+ </Link>
39
+ ) : (
40
+ <Link
41
+ to="/"
42
+ className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
43
+ onClick={(e: any) => {
44
+ e.preventDefault()
45
+ window.history.back()
46
+ }}
47
+ >
48
+ Go Back
49
+ </Link>
50
+ )}
51
+ </div>
52
+ </div>
53
+ )
54
+ }
@@ -0,0 +1,21 @@
1
+ import type { ComponentProps } from "react"
2
+
3
+ /** Styled wrapper for definition lists produced by remark-definition-list */
4
+ export function DefinitionList(props: ComponentProps<"dl">) {
5
+ return <dl {...props} className="not-prose space-y-4" />
6
+ }
7
+
8
+ /** Styled definition term rendered as bold text */
9
+ export function DefinitionTerm(props: ComponentProps<"dt">) {
10
+ return <dt {...props} className="text-foreground font-semibold" />
11
+ }
12
+
13
+ /** Styled definition detail with a left border accent */
14
+ export function DefinitionDetail(props: ComponentProps<"dd">) {
15
+ return (
16
+ <dd
17
+ {...props}
18
+ className="ml-0 pl-4 border-l-2 border-border text-muted-foreground"
19
+ />
20
+ )
21
+ }
@@ -0,0 +1,30 @@
1
+ import { useState } from "react"
2
+ import { ChevronRight } from "lucide-react"
3
+ import {
4
+ Collapsible,
5
+ CollapsibleTrigger,
6
+ CollapsibleContent,
7
+ } from "../elements/collapsible.tsx"
8
+
9
+ /** Collapsible content section with a summary label */
10
+ export function Details(props: { summary: string; children: React.ReactNode }) {
11
+ const [open, setOpen] = useState(false)
12
+
13
+ return (
14
+ <Collapsible
15
+ open={open}
16
+ onOpenChange={setOpen}
17
+ className="not-prose my-4 rounded-xl border border-border bg-card px-4 py-3"
18
+ >
19
+ <CollapsibleTrigger className="flex w-full cursor-pointer items-center gap-2 text-sm font-semibold select-none">
20
+ <ChevronRight
21
+ className={`size-4 shrink-0 transition-transform ${open ? "rotate-90" : ""}`}
22
+ />
23
+ {props.summary}
24
+ </CollapsibleTrigger>
25
+ <CollapsibleContent className="mt-3 text-sm [&>p]:my-1 [&>p:first-child]:mt-0 [&>p:last-child]:mb-0">
26
+ {props.children}
27
+ </CollapsibleContent>
28
+ </Collapsible>
29
+ )
30
+ }