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
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ const BaseSection = z.object({
3
+ icon: z.string().optional(),
4
+ title: z.string(),
5
+ prefix: z.string(),
6
+ position: z.enum(["header", "sidebar"]).default("header"),
7
+ });
8
+ export const ArticleSection = BaseSection.extend({
9
+ type: z.literal("article"),
10
+ });
11
+ export const BlogSection = BaseSection.extend({
12
+ type: z.literal("blog"),
13
+ });
14
+ export const ChangelogSection = BaseSection.extend({
15
+ type: z.literal("changelog"),
16
+ source: z.string(),
17
+ version: z.boolean().optional(),
18
+ });
19
+ export const Section = z.preprocess(input => {
20
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
21
+ return input;
22
+ }
23
+ const record = input;
24
+ return "type" in record ? record : { ...record, type: "article" };
25
+ }, z.discriminatedUnion("type", [ArticleSection, BlogSection, ChangelogSection]));
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL21vZGVscy9zZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUE7QUFFdkIsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUMzQixJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUMzQixLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRTtJQUNqQixNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRTtJQUNsQixRQUFRLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7Q0FDMUQsQ0FBQyxDQUFBO0FBSUYsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUM7SUFDL0MsSUFBSSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDO0NBQzNCLENBQUMsQ0FBQTtBQUlGLE1BQU0sQ0FBQyxNQUFNLFdBQVcsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDO0lBQzVDLElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztDQUN4QixDQUFDLENBQUE7QUFJRixNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxXQUFXLENBQUMsTUFBTSxDQUFDO0lBQ2pELElBQUksRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztJQUM1QixNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRTtJQUNsQixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUNoQyxDQUFDLENBQUE7QUFRRixNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLFVBQVUsQ0FDakMsS0FBSyxDQUFDLEVBQUU7SUFDTixJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4RSxPQUFPLEtBQUssQ0FBQTtJQUNkLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxLQUFnQyxDQUFBO0lBQy9DLE9BQU8sTUFBTSxJQUFJLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsTUFBTSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQTtBQUNuRSxDQUFDLEVBQ0QsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLE1BQU0sRUFBRSxDQUFDLGNBQWMsRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQyxDQUM5RSxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgeiB9IGZyb20gXCJ6b2RcIlxuXG5jb25zdCBCYXNlU2VjdGlvbiA9IHoub2JqZWN0KHtcbiAgaWNvbjogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICB0aXRsZTogei5zdHJpbmcoKSxcbiAgcHJlZml4OiB6LnN0cmluZygpLFxuICBwb3NpdGlvbjogei5lbnVtKFtcImhlYWRlclwiLCBcInNpZGViYXJcIl0pLmRlZmF1bHQoXCJoZWFkZXJcIiksXG59KVxuXG4vKiogRGVmYXVsdCBzZWN0aW9uOiByZW5kZXJzIGFydGljbGVzLiAqL1xuZXhwb3J0IHR5cGUgQXJ0aWNsZVNlY3Rpb24gPSB6LmluZmVyPHR5cGVvZiBBcnRpY2xlU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBBcnRpY2xlU2VjdGlvbiA9IEJhc2VTZWN0aW9uLmV4dGVuZCh7XG4gIHR5cGU6IHoubGl0ZXJhbChcImFydGljbGVcIiksXG59KVxuXG4vKiogQmxvZyBzZWN0aW9uOiBkYXRlLXNvcnRlZCBwb3N0cyB3aXRoIHRhZ3MgYW5kIGluZGV4LiAqL1xuZXhwb3J0IHR5cGUgQmxvZ1NlY3Rpb24gPSB6LmluZmVyPHR5cGVvZiBCbG9nU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBCbG9nU2VjdGlvbiA9IEJhc2VTZWN0aW9uLmV4dGVuZCh7XG4gIHR5cGU6IHoubGl0ZXJhbChcImJsb2dcIiksXG59KVxuXG4vKiogQ2hhbmdlbG9nIHNlY3Rpb246IHNpbmdsZSBmaWxlIG9yIEdpdEh1YiByZWxlYXNlcywgc3BsaXQgcGVyIHZlcnNpb24uICovXG5leHBvcnQgdHlwZSBDaGFuZ2Vsb2dTZWN0aW9uID0gei5pbmZlcjx0eXBlb2YgQ2hhbmdlbG9nU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBDaGFuZ2Vsb2dTZWN0aW9uID0gQmFzZVNlY3Rpb24uZXh0ZW5kKHtcbiAgdHlwZTogei5saXRlcmFsKFwiY2hhbmdlbG9nXCIpLFxuICBzb3VyY2U6IHouc3RyaW5nKCksXG4gIHZlcnNpb246IHouYm9vbGVhbigpLm9wdGlvbmFsKCksXG59KVxuXG4vKipcbiAqIEEgc2l0ZSBzZWN0aW9uLiBEaXNjcmltaW5hdGVkIG9uIGB0eXBlYC5cbiAqIFdoZW4gYHR5cGVgIGlzIG9taXR0ZWQsIGl0IGRlZmF1bHRzIHRvIFwiYXJ0aWNsZVwiIHNvIHBsYWluXG4gKiBgeyB0aXRsZSwgcHJlZml4IH1gIGVudHJpZXMga2VlcCB3b3JraW5nLlxuICovXG5leHBvcnQgdHlwZSBTZWN0aW9uID0gei5pbmZlcjx0eXBlb2YgU2VjdGlvbj5cbmV4cG9ydCBjb25zdCBTZWN0aW9uID0gei5wcmVwcm9jZXNzKFxuICBpbnB1dCA9PiB7XG4gICAgaWYgKGlucHV0ID09PSBudWxsIHx8IHR5cGVvZiBpbnB1dCAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGlucHV0KSkge1xuICAgICAgcmV0dXJuIGlucHV0XG4gICAgfVxuICAgIGNvbnN0IHJlY29yZCA9IGlucHV0IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+XG4gICAgcmV0dXJuIFwidHlwZVwiIGluIHJlY29yZCA/IHJlY29yZCA6IHsgLi4ucmVjb3JkLCB0eXBlOiBcImFydGljbGVcIiB9XG4gIH0sXG4gIHouZGlzY3JpbWluYXRlZFVuaW9uKFwidHlwZVwiLCBbQXJ0aWNsZVNlY3Rpb24sIEJsb2dTZWN0aW9uLCBDaGFuZ2Vsb2dTZWN0aW9uXSksXG4pXG4iXX0=
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ /** Light syntax highlighting theme (Shiki) */
3
+ export declare const CodeThemeLight: z.ZodEnum<{
4
+ "ayu-light": "ayu-light";
5
+ "catppuccin-latte": "catppuccin-latte";
6
+ "everforest-light": "everforest-light";
7
+ "github-light": "github-light";
8
+ "github-light-default": "github-light-default";
9
+ "github-light-high-contrast": "github-light-high-contrast";
10
+ "gruvbox-light-hard": "gruvbox-light-hard";
11
+ "gruvbox-light-medium": "gruvbox-light-medium";
12
+ "gruvbox-light-soft": "gruvbox-light-soft";
13
+ "horizon-bright": "horizon-bright";
14
+ "kanagawa-lotus": "kanagawa-lotus";
15
+ "light-plus": "light-plus";
16
+ "material-theme-lighter": "material-theme-lighter";
17
+ "min-light": "min-light";
18
+ "night-owl-light": "night-owl-light";
19
+ "one-light": "one-light";
20
+ "rose-pine-dawn": "rose-pine-dawn";
21
+ "slack-ochin": "slack-ochin";
22
+ "snazzy-light": "snazzy-light";
23
+ "solarized-light": "solarized-light";
24
+ "vitesse-light": "vitesse-light";
25
+ }>;
26
+ /** Dark syntax highlighting theme (Shiki) */
27
+ export declare const CodeThemeDark: z.ZodEnum<{
28
+ andromeeda: "andromeeda";
29
+ "aurora-x": "aurora-x";
30
+ "ayu-dark": "ayu-dark";
31
+ "ayu-mirage": "ayu-mirage";
32
+ "catppuccin-frappe": "catppuccin-frappe";
33
+ "catppuccin-macchiato": "catppuccin-macchiato";
34
+ "catppuccin-mocha": "catppuccin-mocha";
35
+ "dark-plus": "dark-plus";
36
+ dracula: "dracula";
37
+ "dracula-soft": "dracula-soft";
38
+ "everforest-dark": "everforest-dark";
39
+ "github-dark": "github-dark";
40
+ "github-dark-default": "github-dark-default";
41
+ "github-dark-dimmed": "github-dark-dimmed";
42
+ "github-dark-high-contrast": "github-dark-high-contrast";
43
+ "gruvbox-dark-hard": "gruvbox-dark-hard";
44
+ "gruvbox-dark-medium": "gruvbox-dark-medium";
45
+ "gruvbox-dark-soft": "gruvbox-dark-soft";
46
+ horizon: "horizon";
47
+ houston: "houston";
48
+ "kanagawa-dragon": "kanagawa-dragon";
49
+ "kanagawa-wave": "kanagawa-wave";
50
+ laserwave: "laserwave";
51
+ "material-theme": "material-theme";
52
+ "material-theme-darker": "material-theme-darker";
53
+ "material-theme-ocean": "material-theme-ocean";
54
+ "material-theme-palenight": "material-theme-palenight";
55
+ "min-dark": "min-dark";
56
+ monokai: "monokai";
57
+ "night-owl": "night-owl";
58
+ nord: "nord";
59
+ "one-dark-pro": "one-dark-pro";
60
+ plastic: "plastic";
61
+ poimandres: "poimandres";
62
+ red: "red";
63
+ "rose-pine": "rose-pine";
64
+ "rose-pine-moon": "rose-pine-moon";
65
+ "slack-dark": "slack-dark";
66
+ "solarized-dark": "solarized-dark";
67
+ "synthwave-84": "synthwave-84";
68
+ "tokyo-night": "tokyo-night";
69
+ vesper: "vesper";
70
+ "vitesse-black": "vitesse-black";
71
+ "vitesse-dark": "vitesse-dark";
72
+ }>;
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ /** Light syntax highlighting theme (Shiki) */
3
+ export const CodeThemeLight = z.enum([
4
+ "ayu-light",
5
+ "catppuccin-latte",
6
+ "everforest-light",
7
+ "github-light",
8
+ "github-light-default",
9
+ "github-light-high-contrast",
10
+ "gruvbox-light-hard",
11
+ "gruvbox-light-medium",
12
+ "gruvbox-light-soft",
13
+ "horizon-bright",
14
+ "kanagawa-lotus",
15
+ "light-plus",
16
+ "material-theme-lighter",
17
+ "min-light",
18
+ "night-owl-light",
19
+ "one-light",
20
+ "rose-pine-dawn",
21
+ "slack-ochin",
22
+ "snazzy-light",
23
+ "solarized-light",
24
+ "vitesse-light",
25
+ ]);
26
+ /** Dark syntax highlighting theme (Shiki) */
27
+ export const CodeThemeDark = z.enum([
28
+ "andromeeda",
29
+ "aurora-x",
30
+ "ayu-dark",
31
+ "ayu-mirage",
32
+ "catppuccin-frappe",
33
+ "catppuccin-macchiato",
34
+ "catppuccin-mocha",
35
+ "dark-plus",
36
+ "dracula",
37
+ "dracula-soft",
38
+ "everforest-dark",
39
+ "github-dark",
40
+ "github-dark-default",
41
+ "github-dark-dimmed",
42
+ "github-dark-high-contrast",
43
+ "gruvbox-dark-hard",
44
+ "gruvbox-dark-medium",
45
+ "gruvbox-dark-soft",
46
+ "horizon",
47
+ "houston",
48
+ "kanagawa-dragon",
49
+ "kanagawa-wave",
50
+ "laserwave",
51
+ "material-theme",
52
+ "material-theme-darker",
53
+ "material-theme-ocean",
54
+ "material-theme-palenight",
55
+ "min-dark",
56
+ "monokai",
57
+ "night-owl",
58
+ "nord",
59
+ "one-dark-pro",
60
+ "plastic",
61
+ "poimandres",
62
+ "red",
63
+ "rose-pine",
64
+ "rose-pine-moon",
65
+ "slack-dark",
66
+ "solarized-dark",
67
+ "synthwave-84",
68
+ "tokyo-night",
69
+ "vesper",
70
+ "vitesse-black",
71
+ "vitesse-dark",
72
+ ]);
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhlbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9tb2RlbHMvdGhlbWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQTtBQUV2Qiw4Q0FBOEM7QUFDOUMsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDbkMsV0FBVztJQUNYLGtCQUFrQjtJQUNsQixrQkFBa0I7SUFDbEIsY0FBYztJQUNkLHNCQUFzQjtJQUN0Qiw0QkFBNEI7SUFDNUIsb0JBQW9CO0lBQ3BCLHNCQUFzQjtJQUN0QixvQkFBb0I7SUFDcEIsZ0JBQWdCO0lBQ2hCLGdCQUFnQjtJQUNoQixZQUFZO0lBQ1osd0JBQXdCO0lBQ3hCLFdBQVc7SUFDWCxpQkFBaUI7SUFDakIsV0FBVztJQUNYLGdCQUFnQjtJQUNoQixhQUFhO0lBQ2IsY0FBYztJQUNkLGlCQUFpQjtJQUNqQixlQUFlO0NBQ2hCLENBQUMsQ0FBQTtBQUVGLDZDQUE2QztBQUM3QyxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUNsQyxZQUFZO0lBQ1osVUFBVTtJQUNWLFVBQVU7SUFDVixZQUFZO0lBQ1osbUJBQW1CO0lBQ25CLHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztJQUNYLFNBQVM7SUFDVCxjQUFjO0lBQ2QsaUJBQWlCO0lBQ2pCLGFBQWE7SUFDYixxQkFBcUI7SUFDckIsb0JBQW9CO0lBQ3BCLDJCQUEyQjtJQUMzQixtQkFBbUI7SUFDbkIscUJBQXFCO0lBQ3JCLG1CQUFtQjtJQUNuQixTQUFTO0lBQ1QsU0FBUztJQUNULGlCQUFpQjtJQUNqQixlQUFlO0lBQ2YsV0FBVztJQUNYLGdCQUFnQjtJQUNoQix1QkFBdUI7SUFDdkIsc0JBQXNCO0lBQ3RCLDBCQUEwQjtJQUMxQixVQUFVO0lBQ1YsU0FBUztJQUNULFdBQVc7SUFDWCxNQUFNO0lBQ04sY0FBYztJQUNkLFNBQVM7SUFDVCxZQUFZO0lBQ1osS0FBSztJQUNMLFdBQVc7SUFDWCxnQkFBZ0I7SUFDaEIsWUFBWTtJQUNaLGdCQUFnQjtJQUNoQixjQUFjO0lBQ2QsYUFBYTtJQUNiLFFBQVE7SUFDUixlQUFlO0lBQ2YsY0FBYztDQUNmLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHogfSBmcm9tIFwiem9kXCJcblxuLyoqIExpZ2h0IHN5bnRheCBoaWdobGlnaHRpbmcgdGhlbWUgKFNoaWtpKSAqL1xuZXhwb3J0IGNvbnN0IENvZGVUaGVtZUxpZ2h0ID0gei5lbnVtKFtcbiAgXCJheXUtbGlnaHRcIixcbiAgXCJjYXRwcHVjY2luLWxhdHRlXCIsXG4gIFwiZXZlcmZvcmVzdC1saWdodFwiLFxuICBcImdpdGh1Yi1saWdodFwiLFxuICBcImdpdGh1Yi1saWdodC1kZWZhdWx0XCIsXG4gIFwiZ2l0aHViLWxpZ2h0LWhpZ2gtY29udHJhc3RcIixcbiAgXCJncnV2Ym94LWxpZ2h0LWhhcmRcIixcbiAgXCJncnV2Ym94LWxpZ2h0LW1lZGl1bVwiLFxuICBcImdydXZib3gtbGlnaHQtc29mdFwiLFxuICBcImhvcml6b24tYnJpZ2h0XCIsXG4gIFwia2FuYWdhd2EtbG90dXNcIixcbiAgXCJsaWdodC1wbHVzXCIsXG4gIFwibWF0ZXJpYWwtdGhlbWUtbGlnaHRlclwiLFxuICBcIm1pbi1saWdodFwiLFxuICBcIm5pZ2h0LW93bC1saWdodFwiLFxuICBcIm9uZS1saWdodFwiLFxuICBcInJvc2UtcGluZS1kYXduXCIsXG4gIFwic2xhY2stb2NoaW5cIixcbiAgXCJzbmF6enktbGlnaHRcIixcbiAgXCJzb2xhcml6ZWQtbGlnaHRcIixcbiAgXCJ2aXRlc3NlLWxpZ2h0XCIsXG5dKVxuXG4vKiogRGFyayBzeW50YXggaGlnaGxpZ2h0aW5nIHRoZW1lIChTaGlraSkgKi9cbmV4cG9ydCBjb25zdCBDb2RlVGhlbWVEYXJrID0gei5lbnVtKFtcbiAgXCJhbmRyb21lZWRhXCIsXG4gIFwiYXVyb3JhLXhcIixcbiAgXCJheXUtZGFya1wiLFxuICBcImF5dS1taXJhZ2VcIixcbiAgXCJjYXRwcHVjY2luLWZyYXBwZVwiLFxuICBcImNhdHBwdWNjaW4tbWFjY2hpYXRvXCIsXG4gIFwiY2F0cHB1Y2Npbi1tb2NoYVwiLFxuICBcImRhcmstcGx1c1wiLFxuICBcImRyYWN1bGFcIixcbiAgXCJkcmFjdWxhLXNvZnRcIixcbiAgXCJldmVyZm9yZXN0LWRhcmtcIixcbiAgXCJnaXRodWItZGFya1wiLFxuICBcImdpdGh1Yi1kYXJrLWRlZmF1bHRcIixcbiAgXCJnaXRodWItZGFyay1kaW1tZWRcIixcbiAgXCJnaXRodWItZGFyay1oaWdoLWNvbnRyYXN0XCIsXG4gIFwiZ3J1dmJveC1kYXJrLWhhcmRcIixcbiAgXCJncnV2Ym94LWRhcmstbWVkaXVtXCIsXG4gIFwiZ3J1dmJveC1kYXJrLXNvZnRcIixcbiAgXCJob3Jpem9uXCIsXG4gIFwiaG91c3RvblwiLFxuICBcImthbmFnYXdhLWRyYWdvblwiLFxuICBcImthbmFnYXdhLXdhdmVcIixcbiAgXCJsYXNlcndhdmVcIixcbiAgXCJtYXRlcmlhbC10aGVtZVwiLFxuICBcIm1hdGVyaWFsLXRoZW1lLWRhcmtlclwiLFxuICBcIm1hdGVyaWFsLXRoZW1lLW9jZWFuXCIsXG4gIFwibWF0ZXJpYWwtdGhlbWUtcGFsZW5pZ2h0XCIsXG4gIFwibWluLWRhcmtcIixcbiAgXCJtb25va2FpXCIsXG4gIFwibmlnaHQtb3dsXCIsXG4gIFwibm9yZFwiLFxuICBcIm9uZS1kYXJrLXByb1wiLFxuICBcInBsYXN0aWNcIixcbiAgXCJwb2ltYW5kcmVzXCIsXG4gIFwicmVkXCIsXG4gIFwicm9zZS1waW5lXCIsXG4gIFwicm9zZS1waW5lLW1vb25cIixcbiAgXCJzbGFjay1kYXJrXCIsXG4gIFwic29sYXJpemVkLWRhcmtcIixcbiAgXCJzeW50aHdhdmUtODRcIixcbiAgXCJ0b2t5by1uaWdodFwiLFxuICBcInZlc3BlclwiLFxuICBcInZpdGVzc2UtYmxhY2tcIixcbiAgXCJ2aXRlc3NlLWRhcmtcIixcbl0pXG4iXX0=
package/build/program.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Command } from "commander";
2
2
  import { build } from "./commands/build.js";
3
+ import { escape } from "./commands/escape.js";
3
4
  import { preview } from "./commands/preview.js";
4
5
  import { start } from "./commands/start.js";
5
6
  import { helpConfiguration } from "./helpers/program.js";
@@ -10,5 +11,6 @@ export const program = new Command()
10
11
  .configureHelp(helpConfiguration)
11
12
  .addCommand(start)
12
13
  .addCommand(build)
13
- .addCommand(preview);
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZ3JhbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3Byb2dyYW0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUNuQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHVCQUF1QixDQUFBO0FBQy9DLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUMzQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQTtBQUV4RCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsSUFBSSxPQUFPLEVBQUU7S0FDakMsSUFBSSxDQUFDLFVBQVUsQ0FBQztLQUNoQixXQUFXLENBQUMsZ0NBQWdDLENBQUM7S0FDN0MsTUFBTSxDQUFDLHFCQUFxQixFQUFFLHFCQUFxQixDQUFDO0tBQ3BELGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQztLQUNoQyxVQUFVLENBQUMsS0FBSyxDQUFDO0tBQ2pCLFVBQVUsQ0FBQyxLQUFLLENBQUM7S0FDakIsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tbWFuZCB9IGZyb20gXCJjb21tYW5kZXJcIlxuaW1wb3J0IHsgYnVpbGQgfSBmcm9tIFwiLi9jb21tYW5kcy9idWlsZC50c1wiXG5pbXBvcnQgeyBwcmV2aWV3IH0gZnJvbSBcIi4vY29tbWFuZHMvcHJldmlldy50c1wiXG5pbXBvcnQgeyBzdGFydCB9IGZyb20gXCIuL2NvbW1hbmRzL3N0YXJ0LnRzXCJcbmltcG9ydCB7IGhlbHBDb25maWd1cmF0aW9uIH0gZnJvbSBcIi4vaGVscGVycy9wcm9ncmFtLnRzXCJcblxuZXhwb3J0IGNvbnN0IHByb2dyYW0gPSBuZXcgQ29tbWFuZCgpXG4gIC5uYW1lKFwibGl2ZW1hcmtcIilcbiAgLmRlc2NyaXB0aW9uKFwiTGl2ZW1hcmsgc3RhdGljIHNpdGUgZ2VuZXJhdG9yXCIpXG4gIC5vcHRpb24oXCItYywgLS1jb25maWcgPHBhdGg+XCIsIFwicGF0aCB0byBjb25maWcgZmlsZVwiKVxuICAuY29uZmlndXJlSGVscChoZWxwQ29uZmlndXJhdGlvbilcbiAgLmFkZENvbW1hbmQoc3RhcnQpXG4gIC5hZGRDb21tYW5kKGJ1aWxkKVxuICAuYWRkQ29tbWFuZChwcmV2aWV3KVxuIl19
14
+ .addCommand(preview)
15
+ .addCommand(escape);
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvZ3JhbS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3Byb2dyYW0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUNuQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0MsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQzdDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQTtBQUMvQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDM0MsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sc0JBQXNCLENBQUE7QUFFeEQsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLElBQUksT0FBTyxFQUFFO0tBQ2pDLElBQUksQ0FBQyxVQUFVLENBQUM7S0FDaEIsV0FBVyxDQUFDLGdDQUFnQyxDQUFDO0tBQzdDLE1BQU0sQ0FBQyxxQkFBcUIsRUFBRSxxQkFBcUIsQ0FBQztLQUNwRCxhQUFhLENBQUMsaUJBQWlCLENBQUM7S0FDaEMsVUFBVSxDQUFDLEtBQUssQ0FBQztLQUNqQixVQUFVLENBQUMsS0FBSyxDQUFDO0tBQ2pCLFVBQVUsQ0FBQyxPQUFPLENBQUM7S0FDbkIsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tbWFuZCB9IGZyb20gXCJjb21tYW5kZXJcIlxuaW1wb3J0IHsgYnVpbGQgfSBmcm9tIFwiLi9jb21tYW5kcy9idWlsZC50c1wiXG5pbXBvcnQgeyBlc2NhcGUgfSBmcm9tIFwiLi9jb21tYW5kcy9lc2NhcGUudHNcIlxuaW1wb3J0IHsgcHJldmlldyB9IGZyb20gXCIuL2NvbW1hbmRzL3ByZXZpZXcudHNcIlxuaW1wb3J0IHsgc3RhcnQgfSBmcm9tIFwiLi9jb21tYW5kcy9zdGFydC50c1wiXG5pbXBvcnQgeyBoZWxwQ29uZmlndXJhdGlvbiB9IGZyb20gXCIuL2hlbHBlcnMvcHJvZ3JhbS50c1wiXG5cbmV4cG9ydCBjb25zdCBwcm9ncmFtID0gbmV3IENvbW1hbmQoKVxuICAubmFtZShcImxpdmVtYXJrXCIpXG4gIC5kZXNjcmlwdGlvbihcIkxpdmVtYXJrIHN0YXRpYyBzaXRlIGdlbmVyYXRvclwiKVxuICAub3B0aW9uKFwiLWMsIC0tY29uZmlnIDxwYXRoPlwiLCBcInBhdGggdG8gY29uZmlnIGZpbGVcIilcbiAgLmNvbmZpZ3VyZUhlbHAoaGVscENvbmZpZ3VyYXRpb24pXG4gIC5hZGRDb21tYW5kKHN0YXJ0KVxuICAuYWRkQ29tbWFuZChidWlsZClcbiAgLmFkZENvbW1hbmQocHJldmlldylcbiAgLmFkZENvbW1hbmQoZXNjYXBlKVxuIl19
@@ -1,2 +1,2 @@
1
- declare const _default: UserConfig;
1
+ declare const _default: import("vite-plus").UserConfig;
2
2
  export default _default;
@@ -0,0 +1,117 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ viewBox="0 0 200 200"
4
+ version="1.1"
5
+ id="svg5"
6
+ sodipodi:docname="logo.svg"
7
+ inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
8
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
9
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ xmlns:svg="http://www.w3.org/2000/svg">
12
+ <defs
13
+ id="defs5" />
14
+ <sodipodi:namedview
15
+ id="namedview5"
16
+ pagecolor="#ffffff"
17
+ bordercolor="#000000"
18
+ borderopacity="0.25"
19
+ inkscape:showpageshadow="2"
20
+ inkscape:pageopacity="0.0"
21
+ inkscape:pagecheckerboard="0"
22
+ inkscape:deskcolor="#d1d1d1"
23
+ inkscape:zoom="2.56"
24
+ inkscape:cx="92.1875"
25
+ inkscape:cy="79.492188"
26
+ inkscape:window-width="1920"
27
+ inkscape:window-height="1108"
28
+ inkscape:window-x="0"
29
+ inkscape:window-y="53"
30
+ inkscape:window-maximized="1"
31
+ inkscape:current-layer="g5" />
32
+ <g
33
+ transform="matrix(2.0984308,0,0,2.0984308,83.49896,109.13924)"
34
+ id="g5">
35
+ <rect
36
+ x="-6.633275"
37
+ y="-51.953632"
38
+ width="28.590414"
39
+ height="28.590414"
40
+ rx="3.2988937"
41
+ fill="#93c5fd"
42
+ id="rect1"
43
+ style="stroke-width:1.09963" />
44
+ <rect
45
+ x="-39.414604"
46
+ y="-52.007191"
47
+ width="28.590414"
48
+ height="28.590414"
49
+ rx="3.2988937"
50
+ fill="#93c5fd"
51
+ id="rect1-56"
52
+ style="stroke-width:1.09963" />
53
+ <rect
54
+ x="26.997494"
55
+ y="-51.681351"
56
+ width="28.590414"
57
+ height="28.590414"
58
+ rx="3.2988937"
59
+ fill="#93c5fd"
60
+ id="rect1-5"
61
+ style="stroke-width:1.09963" />
62
+ <rect
63
+ x="-7.0055771"
64
+ y="-18.406252"
65
+ width="28.590414"
66
+ height="28.590414"
67
+ rx="3.2988937"
68
+ fill="#4e94f8"
69
+ id="rect2"
70
+ style="stroke-width:1.09963" />
71
+ <rect
72
+ x="-7.0055771"
73
+ y="14.582686"
74
+ width="28.590414"
75
+ height="28.590414"
76
+ rx="3.2988937"
77
+ fill="#2563eb"
78
+ id="rect3"
79
+ style="stroke-width:1.09963" />
80
+ <rect
81
+ x="-39.689648"
82
+ y="14.813753"
83
+ width="28.590414"
84
+ height="28.590414"
85
+ rx="3.2988937"
86
+ fill="#2563eb"
87
+ id="rect3-2"
88
+ style="stroke-width:1.09963" />
89
+ <rect
90
+ x="26.91572"
91
+ y="14.813753"
92
+ width="28.590414"
93
+ height="28.590414"
94
+ rx="3.2988937"
95
+ fill="#2563eb"
96
+ id="rect3-9"
97
+ style="stroke-width:1.09963" />
98
+ <rect
99
+ x="26.591444"
100
+ y="-17.766558"
101
+ width="28.590414"
102
+ height="28.590414"
103
+ rx="3.2988937"
104
+ fill="#4e94f8"
105
+ id="rect4"
106
+ style="stroke-width:1.09963" />
107
+ <rect
108
+ x="-39.496445"
109
+ y="-18.058704"
110
+ width="28.590414"
111
+ height="28.590414"
112
+ rx="3.2988937"
113
+ fill="#4e94f8"
114
+ id="rect4-6"
115
+ style="stroke-width:1.09963" />
116
+ </g>
117
+ </svg>
@@ -0,0 +1,172 @@
1
+ import { MDXContent } from "@content-collections/mdx/react"
2
+ import { Calendar, History, User } from "lucide-react"
3
+ import type { ArticleView } from "../models/article.ts"
4
+ import { TocContext } from "../helpers/toc-context.ts"
5
+ import { Abbr } from "./Abbr.tsx"
6
+ import { AnsiCode } from "./AnsiCode.tsx"
7
+ import { Callout } from "./Callout.tsx"
8
+ import { Card } from "./Card.tsx"
9
+ import { Cards } from "./Cards.tsx"
10
+ import { CodeBlock } from "./CodeBlock.tsx"
11
+ import { CodeTabs } from "./CodeTabs.tsx"
12
+ import { Column } from "./Column.tsx"
13
+ import { Columns } from "./Columns.tsx"
14
+ import { ContentTab } from "./ContentTab.tsx"
15
+ import { ContentTabs } from "./ContentTabs.tsx"
16
+ import {
17
+ DefinitionDetail,
18
+ DefinitionList,
19
+ DefinitionTerm,
20
+ } from "./DefinitionList.tsx"
21
+ import { Details } from "./Details.tsx"
22
+ import { FileTree } from "./FileTree.tsx"
23
+ import { Footer } from "./Footer.tsx"
24
+ import { FootnoteRef } from "./FootnoteRef.tsx"
25
+ import { headingComponents } from "./Heading.tsx"
26
+ import { InlineBadge } from "./InlineBadge.tsx"
27
+ import { InlineIcon } from "./InlineIcon.tsx"
28
+ import { InlineToc } from "./InlineToc.tsx"
29
+ import { LinkButton } from "./LinkButton.tsx"
30
+ import { Mermaid } from "./Mermaid.tsx"
31
+ import { PackageTabs } from "./PackageTabs.tsx"
32
+ import { PageToolbar } from "./PageToolbar.tsx"
33
+ import { PrevNext } from "./PrevNext.tsx"
34
+ import {
35
+ SortableTable,
36
+ TableBody,
37
+ TableCell,
38
+ TableHead,
39
+ TableHeader,
40
+ TableRow,
41
+ } from "./SortableTable.tsx"
42
+ import { SoundCloud } from "./SoundCloud.tsx"
43
+ import { MobileToc, Toc } from "./Toc.tsx"
44
+ import { YouTube } from "./YouTube.tsx"
45
+ import { ZoomImage } from "./ZoomImage.tsx"
46
+ import { Separator } from "../elements/separator.tsx"
47
+
48
+ /** Full-page article layout: MDX content, table of contents, toolbar, prev/next, footer */
49
+ export function Article(props: { article: ArticleView }) {
50
+ const { article } = props
51
+ const lastUpdated = formatDate(article.lastUpdated)
52
+ const publishedDate = formatDate(article.date)
53
+ const authors = article.author
54
+ ? Array.isArray(article.author)
55
+ ? article.author
56
+ : [article.author]
57
+ : []
58
+ const hasMeta = publishedDate || authors.length > 0 || lastUpdated
59
+
60
+ const H1 = (headingProps: React.HTMLAttributes<HTMLHeadingElement>) => (
61
+ <>
62
+ <h1 {...headingProps}>
63
+ {headingProps.id ? (
64
+ <a href={`#${headingProps.id}`}>{headingProps.children}</a>
65
+ ) : (
66
+ headingProps.children
67
+ )}
68
+ </h1>
69
+ {hasMeta && (
70
+ <p className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-muted-foreground !-mt-4 !mb-6">
71
+ {authors.length > 0 && (
72
+ <span className="flex items-center gap-1.5">
73
+ <User className="size-3.5" />
74
+ {authors.join(", ")}
75
+ </span>
76
+ )}
77
+ {publishedDate && (
78
+ <span className="flex items-center gap-1.5">
79
+ <Calendar className="size-3.5" />
80
+ Published {publishedDate}
81
+ </span>
82
+ )}
83
+ {lastUpdated && (
84
+ <span className="flex items-center gap-1.5">
85
+ <History className="size-3.5" />
86
+ Updated {lastUpdated}
87
+ </span>
88
+ )}
89
+ </p>
90
+ )}
91
+ {article.image && (
92
+ <img
93
+ src={article.image}
94
+ alt=""
95
+ className="!mt-0 !mb-6 w-full rounded-xl border shadow-sm"
96
+ />
97
+ )}
98
+ </>
99
+ )
100
+
101
+ return (
102
+ <TocContext.Provider value={article.tocItems}>
103
+ {article.toc !== false && (
104
+ <MobileToc items={article.tocItems}>
105
+ <PageToolbar file={article.file} content={article.content} />
106
+ </MobileToc>
107
+ )}
108
+ <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
109
+ <div className="flex-1 min-w-0 mx-auto max-w-3xl">
110
+ <div className="prose dark:prose-invert max-w-none">
111
+ <MDXContent
112
+ code={article.mdx}
113
+ components={{
114
+ hr: Separator,
115
+ img: ZoomImage,
116
+ pre: CodeBlock,
117
+ table: SortableTable,
118
+ thead: TableHeader,
119
+ tbody: TableBody,
120
+ tr: TableRow,
121
+ th: TableHead,
122
+ td: TableCell,
123
+ a: FootnoteRef,
124
+ Abbr,
125
+ AnsiCode,
126
+ Callout,
127
+ Card,
128
+ Cards,
129
+ Column,
130
+ Columns,
131
+ CodeTabs,
132
+ ContentTab,
133
+ ContentTabs,
134
+ DefinitionDetail,
135
+ DefinitionList,
136
+ DefinitionTerm,
137
+ Details,
138
+ FileTree,
139
+ InlineBadge,
140
+ InlineIcon,
141
+ InlineToc,
142
+ LinkButton,
143
+ Mermaid,
144
+ PackageTabs,
145
+ SoundCloud,
146
+ YouTube,
147
+ ...headingComponents,
148
+ h1: H1,
149
+ }}
150
+ />
151
+ </div>
152
+ <PrevNext path={article.path} />
153
+ <Footer />
154
+ </div>
155
+ {article.toc !== false && (
156
+ <Toc items={article.tocItems}>
157
+ <PageToolbar file={article.file} content={article.content} />
158
+ </Toc>
159
+ )}
160
+ </div>
161
+ </TocContext.Provider>
162
+ )
163
+ }
164
+
165
+ function formatDate(iso?: string) {
166
+ if (!iso) return undefined
167
+ return new Date(iso).toLocaleDateString("en-US", {
168
+ year: "numeric",
169
+ month: "short",
170
+ day: "numeric",
171
+ })
172
+ }
@@ -0,0 +1,32 @@
1
+ import { ArrowUp } from "lucide-react"
2
+ import { useEffect, useState } from "react"
3
+ import { Button } from "../elements/button.tsx"
4
+
5
+ /** Floating button that scrolls the page back to the top once the user
6
+ * has scrolled past a threshold. */
7
+ export function BackToTop() {
8
+ const [visible, setVisible] = useState(false)
9
+
10
+ useEffect(() => {
11
+ const onScroll = () => setVisible(window.scrollY > 400)
12
+ onScroll()
13
+ window.addEventListener("scroll", onScroll, { passive: true })
14
+ return () => window.removeEventListener("scroll", onScroll)
15
+ }, [])
16
+
17
+ return (
18
+ <Button
19
+ type="button"
20
+ variant="outline"
21
+ aria-label="Back to top"
22
+ onClick={() => window.scrollTo({ top: 0, behavior: "smooth" })}
23
+ className={`fixed bottom-6 right-6 z-30 size-10 rounded-full shadow-md transition-all duration-200 ${
24
+ visible
25
+ ? "opacity-100 translate-y-0 pointer-events-auto"
26
+ : "opacity-0 translate-y-2 pointer-events-none"
27
+ }`}
28
+ >
29
+ <ArrowUp className="size-4" />
30
+ </Button>
31
+ )
32
+ }
@@ -3,8 +3,8 @@ import { Star, MessageCircle } from "lucide-react"
3
3
  /** Technical preview banner displayed in the header */
4
4
  export function Banner() {
5
5
  return (
6
- <div className="flex flex-1 items-center justify-end">
7
- <div className="rounded-xl border border-primary/20 bg-primary/5 shadow-xs px-3 py-1.5 text-sm text-primary">
6
+ <div className="hidden md:flex flex-1 items-center justify-end">
7
+ <div className="rounded-xl border border-primary/20 bg-primary/2 dark:bg-primary/5 shadow-xs px-3 py-1.5 text-sm text-primary">
8
8
  <span>
9
9
  This project is in technical preview.{" "}
10
10
  <a
@@ -0,0 +1,121 @@
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 blog index listing all posts by date */
15
+ export function BlogIndex(props: { sectionPrefix: string }) {
16
+ const section = currentSection(props.sectionPrefix)
17
+ const flat = sectionFlatArticles.get(props.sectionPrefix) ?? []
18
+ const posts = flat
19
+ .map(p => sortedArticles.find(a => a.path === p))
20
+ .filter(a => a !== undefined)
21
+
22
+ const tocItems = posts.map(post => ({
23
+ url: `#${slugOf(post.path)}`,
24
+ title: post.title,
25
+ depth: 2,
26
+ }))
27
+
28
+ const content = [
29
+ `# ${section?.title ?? "Blog"}`,
30
+ ...posts.map(post => {
31
+ const parts = [`## ${post.title}`]
32
+ if (post.date) parts.push(formatDate(post.date))
33
+ if (post.author) {
34
+ parts.push(
35
+ Array.isArray(post.author) ? post.author.join(", ") : post.author,
36
+ )
37
+ }
38
+ if (post.description) parts.push("", post.description)
39
+ return parts.join("\n")
40
+ }),
41
+ ].join("\n\n")
42
+
43
+ return (
44
+ <TocContext.Provider value={tocItems}>
45
+ <MobileToc items={tocItems}>
46
+ <PageToolbar content={content} />
47
+ </MobileToc>
48
+ <div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
49
+ <div className="flex-1 min-w-0 mx-auto max-w-3xl">
50
+ <div className="prose dark:prose-invert max-w-none mb-8">
51
+ <h1 id="top">
52
+ <a href="#top">{section?.title ?? "Blog"}</a>
53
+ </h1>
54
+ {posts[0]?.date && (
55
+ <p className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-muted-foreground !-mt-4 !mb-0">
56
+ <span className="flex items-center gap-1.5">
57
+ <History className="size-3.5" />
58
+ Updated {formatDate(posts[0].date)}
59
+ </span>
60
+ </p>
61
+ )}
62
+ </div>
63
+ <div className="space-y-6">
64
+ {posts.map((post, i) => (
65
+ <article key={post.path} id={slugOf(post.path)}>
66
+ {i > 0 && <Separator className="mb-6" />}
67
+ <h2 className="text-2xl">
68
+ <Link
69
+ to="/$"
70
+ params={{ _splat: post.path.replace(/^\/|\/$/g, "") }}
71
+ className="font-medium hover:text-primary transition-colors"
72
+ >
73
+ {post.title}
74
+ </Link>
75
+ </h2>
76
+ <p className="text-sm text-muted-foreground mt-1 flex gap-3">
77
+ {post.date && <span>{formatDate(post.date)}</span>}
78
+ {post.author && (
79
+ <span>
80
+ {Array.isArray(post.author)
81
+ ? post.author.join(", ")
82
+ : post.author}
83
+ </span>
84
+ )}
85
+ </p>
86
+ {post.description && (
87
+ <p className="text-muted-foreground mt-2">
88
+ {post.description}
89
+ </p>
90
+ )}
91
+ </article>
92
+ ))}
93
+ </div>
94
+ <div className="mt-10">
95
+ <Footer />
96
+ </div>
97
+ </div>
98
+ <Toc items={tocItems}>
99
+ <PageToolbar content={content} />
100
+ </Toc>
101
+ </div>
102
+ </TocContext.Provider>
103
+ )
104
+ }
105
+
106
+ function slugOf(path: string) {
107
+ return (
108
+ path
109
+ .replace(/^\/|\/$/g, "")
110
+ .split("/")
111
+ .pop() ?? ""
112
+ )
113
+ }
114
+
115
+ function formatDate(iso: string) {
116
+ return new Date(iso).toLocaleDateString("en-US", {
117
+ year: "numeric",
118
+ month: "short",
119
+ day: "numeric",
120
+ })
121
+ }