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,174 @@
1
+ interface HastText {
2
+ type: "text"
3
+ value: string
4
+ }
5
+
6
+ interface HastElement {
7
+ type: "element"
8
+ properties: Record<string, unknown>
9
+ children: (HastElement | HastText)[]
10
+ }
11
+
12
+ interface ShikiTransformer {
13
+ name: string
14
+ preprocess?: (
15
+ this: { options: { meta?: { __raw?: string } } },
16
+ code: string,
17
+ ) => void
18
+ pre?: (pre: HastElement) => HastElement | void
19
+ line?: (line: HastElement, lineNumber: number) => HastElement | void
20
+ span?: (span: HastElement) => HastElement | void
21
+ }
22
+
23
+ function parseLineRanges(meta: string): Set<number> {
24
+ const lines = new Set<number>()
25
+ const match = meta.match(/\{([\d,\s-]+)\}/)
26
+ if (!match) return lines
27
+
28
+ const parts = match[1]!.split(",")
29
+ for (const part of parts) {
30
+ const trimmed = part.trim()
31
+ const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/)
32
+ if (rangeMatch) {
33
+ const start = Number(rangeMatch[1])
34
+ const end = Number(rangeMatch[2])
35
+ for (let i = start; i <= end; i++) {
36
+ lines.add(i)
37
+ }
38
+ } else {
39
+ const num = Number(trimmed)
40
+ if (!Number.isNaN(num)) {
41
+ lines.add(num)
42
+ }
43
+ }
44
+ }
45
+
46
+ return lines
47
+ }
48
+
49
+ function parseLineNumbers(meta: string): { enabled: boolean; start: number } {
50
+ const match = meta.match(/\blineNumbers(?:=(\d+))?\b/)
51
+ if (!match) return { enabled: false, start: 1 }
52
+ const start = match[1] ? Number(match[1]) : 1
53
+ return { enabled: true, start }
54
+ }
55
+
56
+ function parseWordHighlight(meta: string): string | undefined {
57
+ const match = meta.match(/\{word:([^}]+)\}/)
58
+ return match ? match[1] : undefined
59
+ }
60
+
61
+ function spanContainsWord(span: HastElement, word: string): boolean {
62
+ for (const child of span.children) {
63
+ if (child.type === "text" && child.value.includes(word)) {
64
+ return true
65
+ }
66
+ }
67
+ return false
68
+ }
69
+
70
+ const NOTATION_RE =
71
+ /\/\/\s*\[!code\s+(highlight|focus|\+\+|--|error|warning)\]\s*$/
72
+ const NOTATION_BLOCK_RE =
73
+ /\/\*\s*\[!code\s+(highlight|focus|\+\+|--|error|warning)\]\s*\*\/\s*$/
74
+
75
+ function stripNotationFromLine(line: HastElement): string | undefined {
76
+ for (const child of line.children) {
77
+ if (child.type === "element") {
78
+ const result = stripNotationFromLine(child)
79
+ if (result !== undefined) return result
80
+ }
81
+ if (child.type === "text") {
82
+ const match =
83
+ child.value.match(NOTATION_RE) ?? child.value.match(NOTATION_BLOCK_RE)
84
+ if (match) {
85
+ child.value = child.value.slice(0, match.index).trimEnd()
86
+ return match[1]!
87
+ }
88
+ }
89
+ }
90
+ return undefined
91
+ }
92
+
93
+ /** Shiki transformer that handles comment-based code annotations for highlighting, diffs, and focus effects. */
94
+ export function transformerNotations(): ShikiTransformer {
95
+ let hasFocus = false
96
+ let preElement: HastElement | undefined
97
+
98
+ return {
99
+ name: "livemark:notations",
100
+ preprocess() {
101
+ hasFocus = false
102
+ preElement = undefined
103
+ },
104
+ line(line) {
105
+ const notation = stripNotationFromLine(line)
106
+ if (!notation) return line
107
+
108
+ switch (notation) {
109
+ case "highlight":
110
+ line.properties["data-highlighted"] = ""
111
+ break
112
+ case "++":
113
+ line.properties["data-diff"] = "add"
114
+ break
115
+ case "--":
116
+ line.properties["data-diff"] = "remove"
117
+ break
118
+ case "focus":
119
+ line.properties["data-focus"] = ""
120
+ hasFocus = true
121
+ break
122
+ case "error":
123
+ line.properties["data-line-status"] = "error"
124
+ break
125
+ case "warning":
126
+ line.properties["data-line-status"] = "warning"
127
+ break
128
+ }
129
+
130
+ return line
131
+ },
132
+ pre(pre) {
133
+ preElement = pre
134
+ if (hasFocus && preElement) {
135
+ preElement.properties["data-has-focus"] = ""
136
+ }
137
+ return pre
138
+ },
139
+ }
140
+ }
141
+
142
+ /** Shiki transformer that highlights specific lines and words based on the code block meta string. */
143
+ export function transformerLineHighlight(): ShikiTransformer {
144
+ let highlightedLines = new Set<number>()
145
+ let lineNumbers = { enabled: false, start: 1 }
146
+ let highlightedWord: string | undefined
147
+
148
+ return {
149
+ name: "livemark:line-highlight",
150
+ preprocess() {
151
+ const raw = this.options.meta?.__raw ?? ""
152
+ highlightedLines = parseLineRanges(raw)
153
+ lineNumbers = parseLineNumbers(raw)
154
+ highlightedWord = parseWordHighlight(raw)
155
+ },
156
+ line(line, lineNumber) {
157
+ if (highlightedLines.has(lineNumber)) {
158
+ line.properties["data-highlighted"] = ""
159
+ }
160
+ if (lineNumbers.enabled) {
161
+ line.properties["data-line-number"] = String(
162
+ lineNumber - 1 + lineNumbers.start,
163
+ )
164
+ }
165
+ return line
166
+ },
167
+ span(span) {
168
+ if (highlightedWord && spanContainsWord(span, highlightedWord)) {
169
+ span.properties["data-word-highlighted"] = ""
170
+ }
171
+ return span
172
+ },
173
+ }
174
+ }
@@ -0,0 +1,91 @@
1
+ import { existsSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import type { Plugin } from "vite"
4
+
5
+ /** Sub-directories of `website/` that participate in the override mechanism. */
6
+ export const OVERRIDE_SUBDIRS: readonly string[] = [
7
+ "components",
8
+ "elements",
9
+ "styles",
10
+ ]
11
+
12
+ export interface LivemarkOptions {
13
+ defaultsRoot: string
14
+ overridesRoot: string
15
+ subdirs: string[]
16
+ configPath: string
17
+ }
18
+
19
+ /** Livemark dev/build integration:
20
+ * - Redirects imports that land in `<defaultsRoot>/<subdir>/` to the matching
21
+ * user file under `<overridesRoot>/<subdir>/` when it exists.
22
+ * - Watches `livemark.config.ts` and `.livemark/**` so edits, additions and
23
+ * deletions trigger the right reload without manual restart. */
24
+ export function livemark(opts: LivemarkOptions): Plugin {
25
+ const EXT_RE = /\.(tsx|ts|jsx|js|css)(\?|$)/
26
+ return {
27
+ name: "livemark",
28
+ enforce: "pre",
29
+ async resolveId(id, importer, options) {
30
+ if (!importer || !EXT_RE.test(id)) return null
31
+ const resolved = await this.resolve(id, importer, {
32
+ ...options,
33
+ skipSelf: true,
34
+ })
35
+ if (!resolved) return null
36
+ const queryIdx = resolved.id.indexOf("?")
37
+ const path =
38
+ queryIdx === -1 ? resolved.id : resolved.id.slice(0, queryIdx)
39
+ const query = queryIdx === -1 ? "" : resolved.id.slice(queryIdx)
40
+ for (const subdir of opts.subdirs) {
41
+ const defaultsSubdir = `${join(opts.defaultsRoot, subdir)}/`
42
+ if (!path.startsWith(defaultsSubdir)) continue
43
+ const rel = path.slice(defaultsSubdir.length)
44
+ const userPath = join(opts.overridesRoot, subdir, rel)
45
+ if (existsSync(userPath)) return userPath + query
46
+ return null
47
+ }
48
+ return null
49
+ },
50
+ configureServer(server) {
51
+ server.watcher.add(opts.configPath)
52
+ server.watcher.add(join(opts.overridesRoot, "**/*"))
53
+
54
+ const overrideRoots = opts.subdirs.map(
55
+ subdir => `${join(opts.overridesRoot, subdir)}/`,
56
+ )
57
+ const routesRoot = `${join(opts.overridesRoot, "routes")}/`
58
+
59
+ const onChange = (file: string) => {
60
+ if (file === opts.configPath) {
61
+ server.restart()
62
+ return
63
+ }
64
+
65
+ if (file.startsWith(routesRoot)) {
66
+ server.restart()
67
+ return
68
+ }
69
+
70
+ for (let i = 0; i < opts.subdirs.length; i++) {
71
+ const root = overrideRoots[i]
72
+ if (!root || !file.startsWith(root)) continue
73
+ const rel = file.slice(root.length)
74
+ const subdir = opts.subdirs[i]
75
+ if (!subdir) continue
76
+ const defaultPath = join(opts.defaultsRoot, subdir, rel)
77
+ const mod = server.moduleGraph.getModuleById(defaultPath)
78
+ if (mod) server.moduleGraph.invalidateModule(mod)
79
+ server.ws.send({ type: "full-reload" })
80
+ return
81
+ }
82
+ }
83
+
84
+ server.watcher.on("add", onChange)
85
+ server.watcher.on("unlink", onChange)
86
+ server.watcher.on("change", file => {
87
+ if (file === opts.configPath) server.restart()
88
+ })
89
+ },
90
+ }
91
+ }
@@ -0,0 +1,86 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as splatRouteImport } from './routes/$'
13
+ import { Route as IndexRouteImport } from './../.livemark/routes/index'
14
+
15
+ const splatRoute = splatRouteImport.update({
16
+ id: '/$',
17
+ path: '/$',
18
+ getParentRoute: () => rootRouteImport,
19
+ } as any)
20
+ const IndexRoute = IndexRouteImport.update({
21
+ id: '/',
22
+ path: '/',
23
+ getParentRoute: () => rootRouteImport,
24
+ } as any)
25
+
26
+ export interface FileRoutesByFullPath {
27
+ '/': typeof IndexRoute
28
+ '/$': typeof splatRoute
29
+ }
30
+ export interface FileRoutesByTo {
31
+ '/': typeof IndexRoute
32
+ '/$': typeof splatRoute
33
+ }
34
+ export interface FileRoutesById {
35
+ __root__: typeof rootRouteImport
36
+ '/': typeof IndexRoute
37
+ '/$': typeof splatRoute
38
+ }
39
+ export interface FileRouteTypes {
40
+ fileRoutesByFullPath: FileRoutesByFullPath
41
+ fullPaths: '/' | '/$'
42
+ fileRoutesByTo: FileRoutesByTo
43
+ to: '/' | '/$'
44
+ id: '__root__' | '/' | '/$'
45
+ fileRoutesById: FileRoutesById
46
+ }
47
+ export interface RootRouteChildren {
48
+ IndexRoute: typeof IndexRoute
49
+ splatRoute: typeof splatRoute
50
+ }
51
+
52
+ declare module '@tanstack/react-router' {
53
+ interface FileRoutesByPath {
54
+ '/$': {
55
+ id: '/$'
56
+ path: '/$'
57
+ fullPath: '/$'
58
+ preLoaderRoute: typeof splatRouteImport
59
+ parentRoute: typeof rootRouteImport
60
+ }
61
+ '/': {
62
+ id: '/'
63
+ path: '/'
64
+ fullPath: '/'
65
+ preLoaderRoute: typeof IndexRouteImport
66
+ parentRoute: typeof rootRouteImport
67
+ }
68
+ }
69
+ }
70
+
71
+ const rootRouteChildren: RootRouteChildren = {
72
+ IndexRoute: IndexRoute,
73
+ splatRoute: splatRoute,
74
+ }
75
+ export const routeTree = rootRouteImport
76
+ ._addFileChildren(rootRouteChildren)
77
+ ._addFileTypes<FileRouteTypes>()
78
+
79
+ import type { getRouter } from './router.tsx'
80
+ import type { createStart } from '@tanstack/react-start'
81
+ declare module '@tanstack/react-start' {
82
+ interface Register {
83
+ ssr: true
84
+ router: Awaited<ReturnType<typeof getRouter>>
85
+ }
86
+ }
@@ -0,0 +1,34 @@
1
+ import { QueryClient } from "@tanstack/react-query"
2
+ import { createRouter } from "@tanstack/react-router"
3
+ import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query"
4
+ import { DefaultCatchBoundary } from "./components/DefaultCatchBoundary.tsx"
5
+ import { NotFound } from "./components/NotFound.tsx"
6
+ import { routeTree } from "./routeTree.gen.ts"
7
+
8
+ export function getRouter() {
9
+ const queryClient = new QueryClient()
10
+
11
+ const router = createRouter({
12
+ routeTree,
13
+ context: { queryClient },
14
+ trailingSlash: "always",
15
+ defaultPreload: "intent",
16
+ defaultViewTransition: true,
17
+ defaultErrorComponent: DefaultCatchBoundary,
18
+ defaultNotFoundComponent: () => <NotFound />,
19
+ scrollRestoration: true,
20
+ })
21
+
22
+ setupRouterSsrQueryIntegration({
23
+ router,
24
+ queryClient,
25
+ })
26
+
27
+ return router
28
+ }
29
+
30
+ declare module "@tanstack/react-router" {
31
+ interface Register {
32
+ router: ReturnType<typeof getRouter>
33
+ }
34
+ }
@@ -0,0 +1,92 @@
1
+ import { createFileRoute, notFound, redirect } from "@tanstack/react-router"
2
+ import {
3
+ currentSection,
4
+ firstArticle,
5
+ homeArticle,
6
+ sortedArticles,
7
+ } from "../content/article.ts"
8
+ import { sectionTags } from "../content/tag.ts"
9
+ import { Article } from "../components/Article.tsx"
10
+ import { BlogIndex } from "../components/BlogIndex.tsx"
11
+ import { ChangelogIndex } from "../components/ChangelogIndex.tsx"
12
+ import { TagIndex } from "../components/TagIndex.tsx"
13
+ import markdownCss from "../styles/markdown.css?url"
14
+
15
+ export const Route = createFileRoute("/$")({
16
+ loader: ({ params }) => {
17
+ if (!params._splat) {
18
+ if (homeArticle) return { ...homeArticle, sidebar: homeArticle.sidebar }
19
+ if (!firstArticle) throw notFound()
20
+ throw redirect({
21
+ to: "/$",
22
+ params: { _splat: firstArticle.path.replace(/^\/|\/$/g, "") },
23
+ })
24
+ }
25
+ const splat = `/${params._splat}/`
26
+ const article = sortedArticles.find(a => a.path === splat)
27
+ if (article) return article
28
+
29
+ const section = currentSection(splat)
30
+ if (section?.type === "changelog" && section.prefix === splat) {
31
+ return {
32
+ changelogIndex: true as const,
33
+ sectionPrefix: section.prefix,
34
+ title: section.title,
35
+ sidebar: true,
36
+ }
37
+ }
38
+ if (section?.type === "blog") {
39
+ if (section.prefix === splat) {
40
+ return {
41
+ blogIndex: true as const,
42
+ sectionPrefix: section.prefix,
43
+ title: section.title,
44
+ sidebar: true,
45
+ }
46
+ }
47
+
48
+ const tagMatch = splat.match(
49
+ new RegExp(`^${section.prefix}tags/([^/]+)/$`),
50
+ )
51
+ const tag = tagMatch?.[1]
52
+ if (tag) {
53
+ const tagMap = sectionTags.get(section.prefix)
54
+ if (tagMap?.has(tag)) {
55
+ return {
56
+ tagPage: true as const,
57
+ sectionPrefix: section.prefix,
58
+ tag,
59
+ title: `Posts tagged '${tag}'`,
60
+ sidebar: true,
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ throw notFound()
67
+ },
68
+ head: ({ loaderData }) => ({
69
+ meta: [
70
+ ...(loaderData ? [{ title: loaderData.title }] : []),
71
+ ...("description" in loaderData && loaderData.description
72
+ ? [{ name: "description", content: loaderData.description }]
73
+ : []),
74
+ ],
75
+ links: [{ rel: "stylesheet", href: markdownCss }],
76
+ }),
77
+ component: Component,
78
+ })
79
+
80
+ function Component() {
81
+ const data = Route.useLoaderData()
82
+ if ("blogIndex" in data) {
83
+ return <BlogIndex sectionPrefix={data.sectionPrefix} />
84
+ }
85
+ if ("changelogIndex" in data) {
86
+ return <ChangelogIndex sectionPrefix={data.sectionPrefix} />
87
+ }
88
+ if ("tagPage" in data) {
89
+ return <TagIndex sectionPrefix={data.sectionPrefix} tag={data.tag} />
90
+ }
91
+ return <Article article={data} />
92
+ }
@@ -0,0 +1,97 @@
1
+ import { TanStackDevtools } from "@tanstack/react-devtools"
2
+ import { ReactQueryDevtoolsPanel } from "@tanstack/react-query-devtools"
3
+ import {
4
+ createRootRoute,
5
+ HeadContent,
6
+ Outlet,
7
+ Scripts,
8
+ useMatch,
9
+ } from "@tanstack/react-router"
10
+ import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"
11
+ import type * as React from "react"
12
+ import { Layout } from "../components/Layout.tsx"
13
+ import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary.tsx"
14
+ import { NotFound } from "../components/NotFound.tsx"
15
+ import { Toaster } from "../elements/sonner.tsx"
16
+ import { faviconType } from "../helpers/favicon.ts"
17
+ import defaultFavicon from "../assets/logo.svg"
18
+ import generalCss from "../styles/general.css?url"
19
+
20
+ export const Route = createRootRoute({
21
+ head: () => {
22
+ const favicon =
23
+ import.meta.env.CONFIG.favicon ??
24
+ import.meta.env.CONFIG.logo ??
25
+ defaultFavicon
26
+ return {
27
+ meta: [
28
+ { charSet: "utf-8" },
29
+ { title: import.meta.env.CONFIG.title },
30
+ { name: "description", content: import.meta.env.CONFIG.description },
31
+ { property: "og:title", content: import.meta.env.CONFIG.title },
32
+ {
33
+ property: "og:description",
34
+ content: import.meta.env.CONFIG.description,
35
+ },
36
+ { property: "og:site_name", content: import.meta.env.CONFIG.title },
37
+ { property: "og:type", content: "website" },
38
+ {
39
+ name: "viewport",
40
+ content: "width=device-width, initial-scale=1",
41
+ },
42
+ ],
43
+ links: [
44
+ { rel: "icon", href: favicon, type: faviconType(favicon) },
45
+ { rel: "stylesheet", href: generalCss },
46
+ ],
47
+ }
48
+ },
49
+ notFoundComponent: () => <NotFound />,
50
+ errorComponent: props => {
51
+ return (
52
+ <Document>
53
+ <DefaultCatchBoundary {...props} />
54
+ </Document>
55
+ )
56
+ },
57
+ component: () => {
58
+ return (
59
+ <Document>
60
+ <Outlet />
61
+ </Document>
62
+ )
63
+ },
64
+ })
65
+
66
+ function Document(props: { children: React.ReactNode }) {
67
+ const articleRoute = useMatch({ from: "/$", shouldThrow: false })
68
+ const withSidebar = articleRoute?.loaderData?.sidebar
69
+
70
+ return (
71
+ <html lang="en">
72
+ <head>
73
+ <HeadContent />
74
+ </head>
75
+ <body>
76
+ <Layout withSidebar={withSidebar}>{props.children}</Layout>
77
+ <Toaster position="top-center" />
78
+ <TanStackDevtools
79
+ config={{ hideUntilHover: true, position: "bottom-left" }}
80
+ plugins={[
81
+ {
82
+ name: "TanStack Query",
83
+ render: <ReactQueryDevtoolsPanel />,
84
+ defaultOpen: true,
85
+ },
86
+ {
87
+ name: "TanStack Router",
88
+ render: <TanStackRouterDevtoolsPanel />,
89
+ defaultOpen: false,
90
+ },
91
+ ]}
92
+ />
93
+ <Scripts />
94
+ </body>
95
+ </html>
96
+ )
97
+ }
@@ -0,0 +1,7 @@
1
+ import handler, { createServerEntry } from "@tanstack/react-start/server-entry"
2
+
3
+ export default createServerEntry({
4
+ fetch(request) {
5
+ return handler.fetch(request)
6
+ },
7
+ })