livemark 0.0.0-dev → 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 (94) hide show
  1. package/README.md +5 -1
  2. package/build/actions/changelog/build.d.ts +14 -0
  3. package/build/actions/changelog/build.js +158 -0
  4. package/build/actions/changelog/build.unit.d.ts +1 -0
  5. package/build/actions/changelog/build.unit.js +66 -0
  6. package/build/actions/config/define.unit.js +11 -16
  7. package/build/actions/config/load.js +6 -3
  8. package/build/actions/config/load.unit.js +3 -3
  9. package/build/commands/build.js +4 -3
  10. package/build/commands/escape.d.ts +9 -0
  11. package/build/commands/escape.js +56 -0
  12. package/build/docs/markdown/includes/example.d.ts +53 -4
  13. package/build/docs/markdown/includes/example.js +2 -2
  14. package/build/livemark.config.d.ts +53 -4
  15. package/build/livemark.config.js +22 -2
  16. package/build/models/config.d.ts +315 -8
  17. package/build/models/config.js +25 -5
  18. package/build/models/link.d.ts +13 -0
  19. package/build/models/link.js +9 -0
  20. package/build/models/patch.d.ts +21 -0
  21. package/build/models/patch.js +7 -0
  22. package/build/models/section.d.ts +75 -0
  23. package/build/models/section.js +26 -0
  24. package/build/models/theme.d.ts +72 -0
  25. package/build/models/theme.js +73 -0
  26. package/build/program.js +4 -2
  27. package/build/vite.config.d.ts +1 -1
  28. package/build/website/assets/logo.svg +117 -0
  29. package/build/website/components/Article.tsx +172 -0
  30. package/build/website/components/BackToTop.tsx +32 -0
  31. package/build/website/components/Banner.tsx +2 -2
  32. package/build/website/components/BlogIndex.tsx +121 -0
  33. package/build/website/components/BlogSidebar.tsx +162 -0
  34. package/build/website/components/Breadcrumbs.tsx +5 -2
  35. package/build/website/components/Card.tsx +48 -0
  36. package/build/website/components/Cards.tsx +0 -58
  37. package/build/website/components/ChangelogIndex.tsx +111 -0
  38. package/build/website/components/ChangelogSidebar.tsx +120 -0
  39. package/build/website/components/CodeBlock.tsx +57 -40
  40. package/build/website/components/Column.tsx +4 -0
  41. package/build/website/components/Columns.tsx +0 -5
  42. package/build/website/components/Footer.tsx +12 -5
  43. package/build/website/components/InlineIcon.tsx +2 -13
  44. package/build/website/components/Layout.tsx +155 -185
  45. package/build/website/components/Mermaid.tsx +14 -33
  46. package/build/website/components/MermaidRenderer.tsx +37 -0
  47. package/build/website/components/MiniSidebar.tsx +49 -0
  48. package/build/website/components/PageToolbar.tsx +21 -11
  49. package/build/website/components/PrevNext.tsx +20 -12
  50. package/build/website/components/Search.tsx +28 -0
  51. package/build/website/components/SearchDialog.tsx +10 -10
  52. package/build/website/components/Sidebar.tsx +160 -0
  53. package/build/website/components/SidebarLinks.tsx +70 -0
  54. package/build/website/components/SidebarSections.tsx +71 -0
  55. package/build/website/components/SiteTitle.tsx +13 -8
  56. package/build/website/components/TagIndex.tsx +120 -0
  57. package/build/website/components/Theme.tsx +0 -1
  58. package/build/website/components/Toc.tsx +128 -1
  59. package/build/website/components/ZoomImage.tsx +15 -20
  60. package/build/website/content/article.ts +78 -0
  61. package/build/website/content/tag.ts +29 -0
  62. package/build/website/content-collections.ts +53 -26
  63. package/build/website/elements/sidebar.tsx +4 -4
  64. package/build/website/env.d.ts +4 -0
  65. package/build/website/helpers/article-icon.ts +2 -0
  66. package/build/website/helpers/article.ts +90 -0
  67. package/build/website/helpers/changelog-source.ts +16 -0
  68. package/build/website/helpers/changelog-source.unit.ts +38 -0
  69. package/build/website/helpers/dynamic-icon-resolver.tsx +19 -0
  70. package/build/website/helpers/dynamic-icon.tsx +12 -0
  71. package/build/website/helpers/favicon.ts +7 -0
  72. package/build/website/helpers/model.ts +4 -0
  73. package/build/website/helpers/resolve-asset-path.ts +9 -0
  74. package/build/website/helpers/section.ts +50 -0
  75. package/build/website/helpers/section.unit.ts +71 -0
  76. package/build/website/helpers/toc-context.ts +1 -1
  77. package/build/website/helpers/toc.ts +1 -6
  78. package/build/website/models/article.ts +48 -33
  79. package/build/website/models/toc.ts +5 -0
  80. package/build/website/plugins/rehype-task-list-a11y.ts +33 -0
  81. package/build/website/plugins/remark-ansi.ts +1 -0
  82. package/build/website/plugins/remark-image.ts +22 -13
  83. package/build/website/plugins/vite-livemark.ts +91 -0
  84. package/build/website/routeTree.gen.ts +9 -9
  85. package/build/website/routes/$.tsx +74 -131
  86. package/build/website/routes/__root.tsx +36 -23
  87. package/build/website/styles/general.css +26 -210
  88. package/build/website/styles/markdown.css +214 -0
  89. package/build/website/vite.config.ts +38 -3
  90. package/package.json +25 -16
  91. package/build/website/globals.d.ts +0 -8
  92. package/build/website/helpers/articles.ts +0 -24
  93. package/build/website/public/logo.svg +0 -72
  94. package/build/website/routes/index.tsx +0 -13
@@ -5,6 +5,7 @@ import {
5
5
  HeadContent,
6
6
  Outlet,
7
7
  Scripts,
8
+ useMatch,
8
9
  } from "@tanstack/react-router"
9
10
  import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"
10
11
  import type * as React from "react"
@@ -12,30 +13,39 @@ import { Layout } from "../components/Layout.tsx"
12
13
  import { DefaultCatchBoundary } from "../components/DefaultCatchBoundary.tsx"
13
14
  import { NotFound } from "../components/NotFound.tsx"
14
15
  import { Toaster } from "../elements/sonner.tsx"
16
+ import { faviconType } from "../helpers/favicon.ts"
17
+ import defaultFavicon from "../assets/logo.svg"
15
18
  import generalCss from "../styles/general.css?url"
16
19
 
17
20
  export const Route = createRootRoute({
18
- head: () => ({
19
- meta: [
20
- { charSet: "utf-8" },
21
- { title: "Title" },
22
- { name: "description", content: "Description" },
23
- { property: "og:title", content: "Title" },
24
- { property: "og:description", content: "Description" },
25
- { property: "og:site_name", content: "Title" },
26
- { property: "og:type", content: "website" },
27
- {
28
- name: "viewport",
29
- content: "width=device-width, initial-scale=1",
30
- },
31
- ],
32
- links: [
33
- { rel: "icon", href: "/logo.svg", type: "image/svg+xml" },
34
- { rel: "stylesheet", href: generalCss },
35
- // TODO: recover
36
- // { rel: "canonical", href: settings.HOST },
37
- ],
38
- }),
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
+ },
39
49
  notFoundComponent: () => <NotFound />,
40
50
  errorComponent: props => {
41
51
  return (
@@ -54,16 +64,19 @@ export const Route = createRootRoute({
54
64
  })
55
65
 
56
66
  function Document(props: { children: React.ReactNode }) {
67
+ const articleRoute = useMatch({ from: "/$", shouldThrow: false })
68
+ const withSidebar = articleRoute?.loaderData?.sidebar
69
+
57
70
  return (
58
71
  <html lang="en">
59
72
  <head>
60
73
  <HeadContent />
61
74
  </head>
62
75
  <body>
63
- <Layout>{props.children}</Layout>
76
+ <Layout withSidebar={withSidebar}>{props.children}</Layout>
64
77
  <Toaster position="top-center" />
65
78
  <TanStackDevtools
66
- config={{ hideUntilHover: true }}
79
+ config={{ hideUntilHover: true, position: "bottom-left" }}
67
80
  plugins={[
68
81
  {
69
82
  name: "TanStack Query",
@@ -1,6 +1,5 @@
1
1
  @import "tailwindcss";
2
2
  @import "tw-animate-css";
3
- @import "katex/dist/katex.min.css";
4
3
  @plugin "@tailwindcss/typography";
5
4
 
6
5
  @custom-variant dark (&:is(.dark *));
@@ -12,12 +11,12 @@
12
11
  --card-foreground: oklch(0.145 0 0);
13
12
  --popover: oklch(1 0 0);
14
13
  --popover-foreground: oklch(0.145 0 0);
15
- --primary: oklch(62.3% 0.214 259.815);
14
+ --primary: oklch(54.6% 0.245 262.881);
16
15
  --primary-foreground: oklch(0.985 0 0);
17
16
  --secondary: oklch(89.4% 0.057 293.283);
18
17
  --secondary-foreground: oklch(0.205 0 0);
19
18
  --muted: oklch(0.97 0 0);
20
- --muted-foreground: oklch(0.556 0 0);
19
+ --muted-foreground: oklch(0.5 0 0);
21
20
  --accent: oklch(0.97 0 0);
22
21
  --accent-foreground: oklch(0.205 0 0);
23
22
  --destructive: oklch(0.577 0.245 27.325);
@@ -48,8 +47,8 @@
48
47
  --card-foreground: oklch(0.985 0 0);
49
48
  --popover: oklch(0.145 0 0);
50
49
  --popover-foreground: oklch(0.985 0 0);
51
- --primary: oklch(0.985 0 0);
52
- --primary-foreground: oklch(0.205 0 0);
50
+ --primary: oklch(0.623 0.214 259.815);
51
+ --primary-foreground: oklch(0.985 0 0);
53
52
  --secondary: oklch(0.269 0 0);
54
53
  --secondary-foreground: oklch(0.985 0 0);
55
54
  --muted: oklch(0.269 0 0);
@@ -126,222 +125,39 @@
126
125
  html {
127
126
  scroll-behavior: smooth;
128
127
  scrollbar-color: #ddd transparent;
128
+ scrollbar-gutter: stable;
129
129
  }
130
130
 
131
131
  button:not([disabled]),
132
132
  [role="button"]:not([disabled]) {
133
133
  cursor: pointer;
134
134
  }
135
- }
136
-
137
- @layer components {
138
- .prose :is(h1, h2, h3, h4, h5, h6) {
139
- @apply scroll-mt-20;
140
- }
141
-
142
- .prose :is(h1, h2, h3, h4, h5, h6)[id] {
143
- position: relative;
144
- }
145
-
146
- .prose :is(h1, h2, h3, h4, h5, h6)[id]::after {
147
- content: "";
148
- display: inline-block;
149
- width: 0.6em;
150
- height: 0.6em;
151
- margin-left: 0.4em;
152
- vertical-align: middle;
153
- opacity: 0;
154
- transition: opacity 0.15s;
155
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/svg%3E");
156
- background-size: contain;
157
- background-repeat: no-repeat;
158
- }
159
-
160
- .prose :is(h1, h2, h3, h4, h5, h6)[id]:hover::after {
161
- opacity: 0.5;
162
- }
163
-
164
- .prose :is(h1, h2, h3, h4, h5, h6)[id] a,
165
- .prose :is(h1, h2, h3, h4, h5, h6)[id] a:hover {
166
- color: inherit !important;
167
- text-decoration: none !important;
168
- }
169
-
170
- .prose pre code .line {
171
- @apply px-4;
172
- }
173
-
174
- .prose pre code .line[data-highlighted] {
175
- @apply bg-primary/10;
176
- }
177
-
178
- .prose pre code .line[data-diff="add"] {
179
- @apply bg-green-500/15;
180
- }
181
-
182
- .prose pre code .line[data-diff="remove"] {
183
- @apply bg-red-500/15;
184
- }
185
-
186
- .prose pre code .line[data-diff="add"]::before {
187
- content: "+";
188
- @apply text-green-600 absolute left-1;
189
- }
190
135
 
191
- .prose pre code .line[data-diff="remove"]::before {
192
- content: "-";
193
- @apply text-red-600 absolute left-1;
136
+ [data-slot="sidebar-content"] {
137
+ scrollbar-width: thin;
138
+ scrollbar-color: transparent transparent;
139
+ transition: scrollbar-color 0.2s;
194
140
  }
195
-
196
- .prose pre code .line[data-line-status="error"] {
197
- @apply bg-red-500/15;
198
- }
199
-
200
- .prose pre code .line[data-line-status="warning"] {
201
- @apply bg-yellow-500/15;
202
- }
203
-
204
- .prose pre[data-has-focus] code .line:not([data-focus]) {
205
- @apply opacity-30;
206
- }
207
-
208
- .prose pre[data-has-focus] code .line[data-focus] {
209
- @apply opacity-100;
210
- }
211
-
212
- .prose pre code [data-word-highlighted] {
213
- @apply bg-primary/20 rounded px-0.5 py-0.5;
214
- }
215
-
216
- .prose pre code .line[data-line-number]::before {
217
- content: attr(data-line-number);
218
- display: inline-block;
219
- width: 2rem;
220
- margin-right: 1rem;
221
- text-align: right;
222
- color: var(--color-muted-foreground);
223
- opacity: 0.5;
224
- user-select: none;
225
- }
226
-
227
- .prose pre code .twoslash-popup-container {
228
- @apply absolute z-10 translate-y-1 rounded-md border border-border bg-popover px-3 py-1.5 text-xs shadow-md;
229
- }
230
-
231
- .prose pre code .twoslash-popup-type {
232
- @apply font-mono text-muted-foreground;
233
- }
234
-
235
- .prose pre code .twoslash-error-line {
236
- @apply bg-red-500/10;
237
- }
238
-
239
- .prose pre code .twoslash-tag-line {
240
- @apply opacity-50;
241
- }
242
-
243
- .prose pre code .twoslash-completion-list {
244
- @apply absolute z-10 translate-y-1 rounded-md border border-border bg-popover p-1 text-xs shadow-md;
141
+ [data-slot="sidebar-content"]:hover,
142
+ [data-slot="sidebar-content"]:focus-within {
143
+ scrollbar-color: color-mix(
144
+ in oklch,
145
+ var(--sidebar-foreground) 20%,
146
+ transparent
147
+ )
148
+ transparent;
245
149
  }
246
150
 
247
- .prose pre code .twoslash-completion-list .twoslash-completion-item {
248
- @apply px-2 py-0.5;
151
+ [data-slot="sidebar-content"]::-webkit-scrollbar {
152
+ width: 8px;
249
153
  }
250
-
251
- .prose pre code .twoslash-hover .twoslash-popup-container {
252
- @apply hidden;
253
- }
254
-
255
- .prose pre code .twoslash-hover:hover .twoslash-popup-container {
256
- @apply block;
257
- }
258
-
259
- .prose :not(pre) > code {
260
- @apply bg-muted/70 border border-border rounded-md px-1.5 py-0.5 text-sm font-mono;
261
- }
262
-
263
- .prose :not(pre) > code::before,
264
- .prose :not(pre) > code::after {
265
- content: none !important;
266
- }
267
-
268
- .prose code.shiki-inline {
269
- @apply bg-muted/70 border border-border rounded-md px-1.5 py-0.5 text-sm font-mono;
270
- }
271
-
272
- .prose code.shiki-inline .line {
273
- display: inline;
274
- }
275
-
276
- .dark .shiki span {
277
- color: var(--shiki-dark) !important;
278
- }
279
-
280
- .katex-display {
281
- @apply overflow-x-auto overflow-y-hidden;
282
- }
283
-
284
- .katex {
285
- @apply overflow-x-auto overflow-y-hidden;
286
- }
287
-
288
- .prose ul:has(> li > input[type="checkbox"]) {
289
- padding-inline-start: 0.5em;
290
- }
291
-
292
- .prose ul > li:has(> input[type="checkbox"]) {
293
- list-style-type: none;
294
- padding-left: 0;
295
- }
296
-
297
- .prose [data-footnotes] {
298
- @apply mt-8 border-t border-border pt-4 text-sm text-muted-foreground;
299
- }
300
-
301
- .prose [data-footnotes] ol {
302
- @apply list-decimal pl-6;
303
- }
304
-
305
- .prose sup a[data-footnote-ref] {
306
- @apply ml-0.5 px-0.5 text-primary text-xs no-underline font-medium;
307
- }
308
-
309
- .prose [data-footnotes] a[data-footnote-backref] {
310
- @apply text-primary no-underline;
311
- }
312
-
313
- .prose dl {
314
- @apply my-4;
315
- }
316
-
317
- .prose dt {
318
- @apply font-semibold mt-4 first:mt-0;
319
- }
320
-
321
- .prose dd {
322
- @apply ml-6 mt-1 text-muted-foreground;
323
- }
324
-
325
- .prose {
326
- counter-reset: step;
327
- }
328
-
329
- .prose [data-step] {
330
- counter-increment: step;
331
- }
332
-
333
- .prose [data-step]::before {
334
- content: counter(step);
335
- display: inline-flex;
336
- align-items: center;
337
- justify-content: center;
338
- width: 1.5rem;
339
- height: 1.5rem;
154
+ [data-slot="sidebar-content"]::-webkit-scrollbar-thumb {
155
+ background: transparent;
340
156
  border-radius: 9999px;
341
- background: var(--color-primary);
342
- color: white;
343
- font-size: 0.75rem;
344
- font-weight: 600;
345
- margin-right: 0.5rem;
157
+ transition: background 0.2s;
158
+ }
159
+ [data-slot="sidebar-content"]:hover::-webkit-scrollbar-thumb,
160
+ [data-slot="sidebar-content"]:focus-within::-webkit-scrollbar-thumb {
161
+ background: color-mix(in oklch, var(--sidebar-foreground) 20%, transparent);
346
162
  }
347
163
  }
@@ -0,0 +1,214 @@
1
+ @import "katex/dist/katex.min.css";
2
+ @reference "./general.css";
3
+
4
+ @layer components {
5
+ .prose :is(h1, h2, h3, h4, h5, h6) {
6
+ @apply scroll-mt-28 md:scroll-mt-20;
7
+ }
8
+
9
+ .prose :is(h1, h2, h3, h4, h5, h6)[id] {
10
+ position: relative;
11
+ }
12
+
13
+ .prose :is(h1, h2, h3, h4, h5, h6)[id]::after {
14
+ content: "";
15
+ display: inline-block;
16
+ width: 0.6em;
17
+ height: 0.6em;
18
+ margin-left: 0.4em;
19
+ vertical-align: middle;
20
+ opacity: 0;
21
+ transition: opacity 0.15s;
22
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71'/%3E%3C/svg%3E");
23
+ background-size: contain;
24
+ background-repeat: no-repeat;
25
+ }
26
+
27
+ .prose :is(h1, h2, h3, h4, h5, h6)[id]:hover::after {
28
+ opacity: 0.5;
29
+ }
30
+
31
+ .prose :is(h1, h2, h3, h4, h5, h6)[id] a,
32
+ .prose :is(h1, h2, h3, h4, h5, h6)[id] a:hover {
33
+ color: inherit !important;
34
+ text-decoration: none !important;
35
+ }
36
+
37
+ .prose pre code .line {
38
+ @apply px-4;
39
+ }
40
+
41
+ .prose pre code .line[data-highlighted] {
42
+ @apply bg-primary/10;
43
+ }
44
+
45
+ .prose pre code .line[data-diff="add"] {
46
+ @apply bg-green-500/15;
47
+ }
48
+
49
+ .prose pre code .line[data-diff="remove"] {
50
+ @apply bg-red-500/15;
51
+ }
52
+
53
+ .prose pre code .line[data-diff="add"]::before {
54
+ content: "+";
55
+ @apply text-green-600 absolute left-1;
56
+ }
57
+
58
+ .prose pre code .line[data-diff="remove"]::before {
59
+ content: "-";
60
+ @apply text-red-600 absolute left-1;
61
+ }
62
+
63
+ .prose pre code .line[data-line-status="error"] {
64
+ @apply bg-red-500/15;
65
+ }
66
+
67
+ .prose pre code .line[data-line-status="warning"] {
68
+ @apply bg-yellow-500/15;
69
+ }
70
+
71
+ .prose pre[data-has-focus] code .line:not([data-focus]) {
72
+ @apply opacity-30;
73
+ }
74
+
75
+ .prose pre[data-has-focus] code .line[data-focus] {
76
+ @apply opacity-100;
77
+ }
78
+
79
+ .prose pre code [data-word-highlighted] {
80
+ @apply bg-primary/20 rounded px-0.5 py-0.5;
81
+ }
82
+
83
+ .prose pre code .line[data-line-number]::before {
84
+ content: attr(data-line-number);
85
+ display: inline-block;
86
+ width: 2rem;
87
+ margin-right: 1rem;
88
+ text-align: right;
89
+ color: var(--color-muted-foreground);
90
+ opacity: 0.5;
91
+ user-select: none;
92
+ }
93
+
94
+ .prose pre code .twoslash-popup-container {
95
+ @apply absolute z-10 translate-y-1 rounded-md border border-border bg-popover px-3 py-1.5 text-xs shadow-md;
96
+ }
97
+
98
+ .prose pre code .twoslash-popup-type {
99
+ @apply font-mono text-muted-foreground;
100
+ }
101
+
102
+ .prose pre code .twoslash-error-line {
103
+ @apply bg-red-500/10;
104
+ }
105
+
106
+ .prose pre code .twoslash-tag-line {
107
+ @apply opacity-50;
108
+ }
109
+
110
+ .prose pre code .twoslash-completion-list {
111
+ @apply absolute z-10 translate-y-1 rounded-md border border-border bg-popover p-1 text-xs shadow-md;
112
+ }
113
+
114
+ .prose pre code .twoslash-completion-list .twoslash-completion-item {
115
+ @apply px-2 py-0.5;
116
+ }
117
+
118
+ .prose pre code .twoslash-hover .twoslash-popup-container {
119
+ @apply hidden;
120
+ }
121
+
122
+ .prose pre code .twoslash-hover:hover .twoslash-popup-container {
123
+ @apply block;
124
+ }
125
+
126
+ .prose :not(pre) > code {
127
+ @apply bg-muted/70 border border-border rounded-md px-1.5 py-0.5 text-sm font-mono;
128
+ }
129
+
130
+ .prose :not(pre) > code::before,
131
+ .prose :not(pre) > code::after {
132
+ content: none !important;
133
+ }
134
+
135
+ .prose code.shiki-inline {
136
+ @apply bg-muted/70 border border-border rounded-md px-1.5 py-0.5 text-sm font-mono;
137
+ }
138
+
139
+ .prose code.shiki-inline .line {
140
+ display: inline;
141
+ }
142
+
143
+ .dark .shiki span {
144
+ color: var(--shiki-dark) !important;
145
+ }
146
+
147
+ .katex-display {
148
+ @apply overflow-x-auto overflow-y-hidden;
149
+ }
150
+
151
+ .katex {
152
+ @apply overflow-x-auto overflow-y-hidden;
153
+ }
154
+
155
+ .prose ul:has(> li > input[type="checkbox"]) {
156
+ padding-inline-start: 0.5em;
157
+ }
158
+
159
+ .prose ul > li:has(> input[type="checkbox"]) {
160
+ list-style-type: none;
161
+ padding-left: 0;
162
+ }
163
+
164
+ .prose [data-footnotes] {
165
+ @apply mt-8 border-t border-border pt-4 text-sm text-muted-foreground;
166
+ }
167
+
168
+ .prose [data-footnotes] ol {
169
+ @apply list-decimal pl-6;
170
+ }
171
+
172
+ .prose sup a[data-footnote-ref] {
173
+ @apply ml-0.5 px-0.5 text-primary text-xs no-underline font-medium;
174
+ }
175
+
176
+ .prose [data-footnotes] a[data-footnote-backref] {
177
+ @apply text-primary no-underline;
178
+ }
179
+
180
+ .prose dl {
181
+ @apply my-4;
182
+ }
183
+
184
+ .prose dt {
185
+ @apply font-semibold mt-4 first:mt-0;
186
+ }
187
+
188
+ .prose dd {
189
+ @apply ml-6 mt-1 text-muted-foreground;
190
+ }
191
+
192
+ .prose {
193
+ counter-reset: step;
194
+ }
195
+
196
+ .prose [data-step] {
197
+ counter-increment: step;
198
+ }
199
+
200
+ .prose [data-step]::before {
201
+ content: counter(step);
202
+ display: inline-flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ width: 1.5rem;
206
+ height: 1.5rem;
207
+ border-radius: 9999px;
208
+ background: var(--color-primary);
209
+ color: white;
210
+ font-size: 0.75rem;
211
+ font-weight: 600;
212
+ margin-right: 0.5rem;
213
+ }
214
+ }
@@ -1,26 +1,54 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs"
1
2
  import { join, relative } from "node:path"
2
3
  import contentCollections from "@content-collections/vite"
3
4
  import tailwind from "@tailwindcss/vite"
4
5
  import { devtools } from "@tanstack/devtools-vite"
5
6
  import { tanstackStart } from "@tanstack/react-start/plugin/vite"
7
+ import {
8
+ physical,
9
+ rootRoute,
10
+ route,
11
+ type VirtualRouteNode,
12
+ } from "@tanstack/virtual-file-routes"
6
13
  import react from "@vitejs/plugin-react"
7
14
  import { defineConfig } from "vite"
8
15
  import svgr from "vite-plugin-svgr"
9
16
  import { loadConfig } from "../actions/config/load.ts"
17
+ import { WebsiteConfig } from "../models/config.ts"
18
+ import { livemark, OVERRIDE_SUBDIRS } from "./plugins/vite-livemark.ts"
10
19
 
11
20
  const config = await loadConfig()
12
21
  const websiteDir = import.meta.dirname
13
22
  const websiteRelative = relative(config.root, websiteDir)
23
+ const overridesRoot = join(config.root, ".livemark")
24
+
25
+ // Seed `.livemark/.gitignore` on first run so Vite's build/cache artifacts
26
+ // never get committed by accident.
27
+ const gitignorePath = join(overridesRoot, ".gitignore")
28
+ if (!existsSync(gitignorePath)) {
29
+ mkdirSync(overridesRoot, { recursive: true })
30
+ writeFileSync(gitignorePath, "/build\n/cache\n")
31
+ }
32
+
33
+ const routeChildren: VirtualRouteNode[] = [route("/$", "$.tsx")]
34
+ if (existsSync(join(overridesRoot, "routes"))) {
35
+ routeChildren.push(physical("../../.livemark/routes"))
36
+ }
14
37
 
15
38
  export default defineConfig({
16
39
  root: config.root,
17
- publicDir: join(websiteDir, "public"),
40
+ publicDir: join(config.root, ".livemark", "public"),
18
41
  build: { outDir: ".livemark/build" },
19
42
  define: {
20
- "import.meta.env.SITE_TITLE": JSON.stringify(config.title),
21
- "import.meta.env.SITE_DESCRIPTION": JSON.stringify(config.description),
43
+ "import.meta.env.CONFIG": JSON.stringify(WebsiteConfig.parse(config)),
22
44
  },
23
45
  plugins: [
46
+ livemark({
47
+ defaultsRoot: websiteDir,
48
+ overridesRoot,
49
+ subdirs: [...OVERRIDE_SUBDIRS],
50
+ configPath: config.configPath,
51
+ }),
24
52
  devtools(),
25
53
  tailwind(),
26
54
  contentCollections({
@@ -28,7 +56,14 @@ export default defineConfig({
28
56
  }),
29
57
  tanstackStart({
30
58
  srcDirectory: websiteRelative,
59
+ pages: [{ path: "/" }],
31
60
  prerender: { enabled: true, crawlLinks: true },
61
+ sitemap: config.site
62
+ ? { enabled: true, host: config.site }
63
+ : { enabled: false },
64
+ router: {
65
+ virtualRouteConfig: rootRoute("__root.tsx", routeChildren),
66
+ },
32
67
  }),
33
68
  react(),
34
69
  svgr(),