livemark 0.25.0 → 0.26.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.
- package/README.md +3 -2
- package/build/actions/config/define.d.ts +6 -0
- package/build/actions/config/define.unit.js +8 -1
- package/build/actions/escape/copy.d.ts +11 -0
- package/build/actions/escape/copy.js +29 -0
- package/build/actions/escape/copy.unit.d.ts +1 -0
- package/build/actions/escape/copy.unit.js +37 -0
- package/build/actions/escape/list.d.ts +12 -0
- package/build/actions/escape/list.js +27 -0
- package/build/actions/escape/list.unit.d.ts +1 -0
- package/build/actions/escape/list.unit.js +36 -0
- package/build/actions/target/build.d.ts +9 -26
- package/build/actions/target/build.js +50 -71
- package/build/actions/target/build.unit.d.ts +1 -0
- package/build/actions/target/build.unit.js +76 -0
- package/build/actions/target/clear.d.ts +4 -0
- package/build/actions/target/clear.js +14 -0
- package/build/actions/target/clear.unit.d.ts +1 -0
- package/build/actions/target/clear.unit.js +23 -0
- package/build/commands/build.js +1 -2
- package/build/commands/clear.d.ts +5 -0
- package/build/commands/clear.js +15 -0
- package/build/commands/escape.d.ts +4 -3
- package/build/commands/escape.js +45 -48
- package/build/commands/preview.js +1 -2
- package/build/commands/start.js +1 -2
- package/build/components/Abbr.d.ts +1 -1
- package/build/components/Abbr.js +2 -4
- package/build/components/AnsiCode.d.ts +1 -1
- package/build/components/AnsiCode.js +2 -2
- package/build/components/Article.d.ts +2 -1
- package/build/components/Article.js +7 -14
- package/build/components/BackToTop.d.ts +1 -1
- package/build/components/Banner.d.ts +2 -2
- package/build/components/Banner.js +7 -3
- package/build/components/BlogIndex.d.ts +1 -1
- package/build/components/BlogIndex.js +2 -11
- package/build/components/Breadcrumbs.d.ts +1 -1
- package/build/components/Breadcrumbs.js +2 -3
- package/build/components/Callout.d.ts +1 -1
- package/build/components/Callout.js +5 -9
- package/build/components/Card.d.ts +1 -1
- package/build/components/Card.js +2 -3
- package/build/components/Cards.d.ts +1 -1
- package/build/components/ChangelogIndex.d.ts +1 -1
- package/build/components/ChangelogIndex.js +2 -11
- package/build/components/ChoiceList.d.ts +15 -0
- package/build/components/ChoiceList.js +13 -0
- package/build/components/CodeBlock.d.ts +1 -1
- package/build/components/CodeBlock.js +3 -16
- package/build/components/CodeTabs.d.ts +1 -1
- package/build/components/CodeTabs.js +2 -3
- package/build/components/Column.d.ts +1 -1
- package/build/components/Columns.d.ts +1 -1
- package/build/components/Content.d.ts +1 -1
- package/build/components/ContentTab.d.ts +1 -1
- package/build/components/ContentTabs.d.ts +1 -1
- package/build/components/ContentTabs.js +2 -3
- package/build/components/DefaultCatchBoundary.d.ts +1 -1
- package/build/components/DefaultCatchBoundary.js +5 -7
- package/build/components/DefinitionList.d.ts +3 -3
- package/build/components/Details.d.ts +1 -1
- package/build/components/Details.js +2 -5
- package/build/components/FileTree.d.ts +1 -1
- package/build/components/FileTree.js +3 -5
- package/build/components/Footer.d.ts +1 -1
- package/build/components/Footer.js +2 -5
- package/build/components/FootnoteRef.d.ts +1 -1
- package/build/components/FootnoteRef.js +2 -4
- package/build/components/Header.d.ts +6 -0
- package/build/components/Header.js +26 -0
- package/build/components/HeaderMenu.d.ts +2 -0
- package/build/components/HeaderMenu.js +27 -0
- package/build/components/HeaderNav.d.ts +6 -0
- package/build/components/HeaderNav.js +74 -0
- package/build/components/HeaderSearch.d.ts +4 -0
- package/build/components/HeaderSearch.js +33 -0
- package/build/components/Heading.d.ts +6 -6
- package/build/components/InlineBadge.d.ts +2 -2
- package/build/components/InlineBadge.js +1 -1
- package/build/components/InlineIcon.d.ts +1 -1
- package/build/components/InlineToc.d.ts +1 -1
- package/build/components/InlineToc.js +2 -4
- package/build/components/Layout.d.ts +2 -1
- package/build/components/Layout.js +5 -58
- package/build/components/LinkButton.d.ts +2 -2
- package/build/components/LinkButton.js +1 -1
- package/build/components/MenuSheet.d.ts +22 -0
- package/build/components/MenuSheet.js +27 -0
- package/build/components/Mermaid.d.ts +1 -1
- package/build/components/MermaidRenderer.d.ts +1 -1
- package/build/components/MermaidRenderer.js +20 -8
- package/build/components/NotFound.d.ts +1 -1
- package/build/components/NotFound.js +3 -6
- package/build/components/PackageTabs.d.ts +1 -1
- package/build/components/PackageTabs.js +3 -7
- package/build/components/PageToolbar.d.ts +1 -1
- package/build/components/PageToolbar.js +2 -10
- package/build/components/PrevNext.d.ts +1 -1
- package/build/components/PrevNext.js +2 -10
- package/build/components/Root.d.ts +4 -0
- package/build/components/Root.js +8 -0
- package/build/components/Search.d.ts +5 -2
- package/build/components/Search.js +11 -9
- package/build/components/SearchDialog.d.ts +1 -1
- package/build/components/SearchDialog.js +10 -133
- package/build/components/Section.d.ts +1 -1
- package/build/components/SectionHeader.d.ts +1 -1
- package/build/components/SheetPanel.d.ts +12 -0
- package/build/components/SheetPanel.js +19 -0
- package/build/components/Sidebar.d.ts +1 -1
- package/build/components/Sidebar.js +12 -14
- package/build/components/SidebarArticles.d.ts +4 -1
- package/build/components/SidebarArticles.js +5 -9
- package/build/components/SidebarLinks.d.ts +4 -1
- package/build/components/SidebarLinks.js +6 -9
- package/build/components/SidebarPosts.d.ts +4 -1
- package/build/components/SidebarPosts.js +7 -23
- package/build/components/SidebarReleases.d.ts +4 -1
- package/build/components/SidebarReleases.js +5 -15
- package/build/components/SidebarSectionPicker.d.ts +8 -0
- package/build/components/SidebarSectionPicker.js +31 -0
- package/build/components/SidebarSections.d.ts +1 -1
- package/build/components/SidebarSections.js +5 -9
- package/build/components/SiteLink.d.ts +1 -1
- package/build/components/SiteTitle.d.ts +1 -1
- package/build/components/SiteTitle.js +2 -6
- package/build/components/SortableTable.d.ts +1 -1
- package/build/components/SoundCloud.d.ts +1 -1
- package/build/components/SoundCloud.js +2 -2
- package/build/components/TagIndex.d.ts +1 -1
- package/build/components/TagIndex.js +2 -11
- package/build/components/Theme.d.ts +2 -1
- package/build/components/Theme.js +35 -15
- package/build/components/Toc.d.ts +5 -4
- package/build/components/Toc.js +35 -35
- package/build/components/YouTube.d.ts +1 -1
- package/build/components/YouTube.js +2 -2
- package/build/components/ZoomImage.d.ts +1 -1
- package/build/content-collections.js +5 -1
- package/build/elements/accordion.d.ts +4 -4
- package/build/elements/accordion.js +2 -3
- package/build/elements/alert-dialog.d.ts +12 -12
- package/build/elements/alert-dialog.js +2 -4
- package/build/elements/alert.d.ts +4 -4
- package/build/elements/alert.js +2 -2
- package/build/elements/aspect-ratio.d.ts +1 -1
- package/build/elements/avatar.d.ts +6 -6
- package/build/elements/badge.d.ts +1 -1
- package/build/elements/badge.js +3 -1
- package/build/elements/breadcrumb.d.ts +6 -6
- package/build/elements/breadcrumb.js +2 -4
- package/build/elements/button-group.d.ts +2 -2
- package/build/elements/button-group.js +5 -5
- package/build/elements/button.d.ts +2 -2
- package/build/elements/button.js +9 -7
- package/build/elements/calendar.d.ts +2 -2
- package/build/elements/calendar.js +2 -2
- package/build/elements/card.d.ts +7 -7
- package/build/elements/card.js +4 -4
- package/build/elements/carousel.d.ts +5 -5
- package/build/elements/carousel.js +3 -7
- package/build/elements/chart.d.ts +6 -6
- package/build/elements/chart.js +5 -11
- package/build/elements/checkbox.d.ts +1 -1
- package/build/elements/collapsible.d.ts +3 -3
- package/build/elements/command.d.ts +9 -9
- package/build/elements/command.js +5 -11
- package/build/elements/context-menu.d.ts +15 -15
- package/build/elements/context-menu.js +6 -9
- package/build/elements/dialog.d.ts +10 -10
- package/build/elements/dialog.js +2 -6
- package/build/elements/drawer.d.ts +14 -12
- package/build/elements/drawer.js +14 -15
- package/build/elements/dropdown-menu.d.ts +15 -15
- package/build/elements/dropdown-menu.js +7 -10
- package/build/elements/empty.d.ts +6 -6
- package/build/elements/empty.js +2 -2
- package/build/elements/field.d.ts +10 -10
- package/build/elements/field.js +3 -4
- package/build/elements/hover-card.d.ts +3 -3
- package/build/elements/hover-card.js +2 -2
- package/build/elements/input-group.d.ts +7 -7
- package/build/elements/input-group.js +5 -5
- package/build/elements/input-otp.d.ts +4 -4
- package/build/elements/input.d.ts +1 -1
- package/build/elements/input.js +2 -2
- package/build/elements/item.d.ts +9 -9
- package/build/elements/item.js +2 -2
- package/build/elements/kbd.d.ts +2 -2
- package/build/elements/label.d.ts +1 -1
- package/build/elements/menubar.d.ts +16 -16
- package/build/elements/menubar.js +9 -11
- package/build/elements/native-select.d.ts +3 -3
- package/build/elements/native-select.js +2 -4
- package/build/elements/navigation-menu.d.ts +8 -8
- package/build/elements/navigation-menu.js +3 -5
- package/build/elements/pagination.d.ts +7 -7
- package/build/elements/pagination.js +4 -10
- package/build/elements/popover.d.ts +6 -6
- package/build/elements/popover.js +2 -2
- package/build/elements/progress.d.ts +5 -5
- package/build/elements/progress.js +2 -3
- package/build/elements/radio-group.d.ts +2 -2
- package/build/elements/resizable.d.ts +3 -3
- package/build/elements/scroll-area.d.ts +2 -2
- package/build/elements/scroll-area.js +2 -4
- package/build/elements/select.d.ts +9 -9
- package/build/elements/select.js +4 -9
- package/build/elements/separator.d.ts +1 -1
- package/build/elements/sheet.d.ts +8 -8
- package/build/elements/sheet.js +2 -6
- package/build/elements/sidebar.d.ts +19 -19
- package/build/elements/sidebar.js +15 -37
- package/build/elements/skeleton.d.ts +1 -1
- package/build/elements/slider.d.ts +2 -1
- package/build/elements/slider.js +2 -3
- package/build/elements/sonner.d.ts +1 -1
- package/build/elements/spinner.d.ts +1 -1
- package/build/elements/switch.d.ts +1 -1
- package/build/elements/table.d.ts +8 -8
- package/build/elements/tabs.d.ts +4 -4
- package/build/elements/tabs.js +3 -3
- package/build/elements/textarea.d.ts +1 -1
- package/build/elements/textarea.js +2 -2
- package/build/elements/toggle-group.d.ts +3 -2
- package/build/elements/toggle-group.js +3 -3
- package/build/elements/toggle.d.ts +1 -1
- package/build/elements/toggle.js +2 -2
- package/build/elements/tooltip.d.ts +4 -4
- package/build/elements/tooltip.js +2 -3
- package/build/head.d.ts +4 -0
- package/build/head.js +6 -0
- package/build/helpers/article-search.d.ts +19 -0
- package/build/helpers/article-search.js +134 -0
- package/build/helpers/dependency-dir.d.ts +5 -0
- package/build/helpers/dependency-dir.js +20 -0
- package/build/helpers/dependency-dir.unit.d.ts +1 -0
- package/build/helpers/dependency-dir.unit.js +25 -0
- package/build/helpers/dynamic-icon-resolver.d.ts +1 -1
- package/build/helpers/dynamic-icon.d.ts +1 -1
- package/build/helpers/head.d.ts +53 -0
- package/build/helpers/head.js +69 -0
- package/build/helpers/head.unit.d.ts +1 -0
- package/build/helpers/head.unit.js +108 -0
- package/build/helpers/nav-items.d.ts +11 -0
- package/build/helpers/nav-items.js +65 -0
- package/build/helpers/render-inline-markdown.d.ts +1 -0
- package/build/helpers/render-inline-markdown.js +34 -0
- package/build/helpers/render-inline-markdown.unit.d.ts +1 -0
- package/build/helpers/render-inline-markdown.unit.js +20 -0
- package/build/helpers/search-highlight.d.ts +8 -0
- package/build/helpers/search-highlight.js +59 -0
- package/build/helpers/search-highlight.unit.d.ts +1 -0
- package/build/helpers/search-highlight.unit.js +36 -0
- package/build/helpers/target-export.d.ts +9 -0
- package/build/helpers/target-export.js +24 -0
- package/build/helpers/target-export.unit.d.ts +1 -0
- package/build/helpers/target-export.unit.js +25 -0
- package/build/helpers/theme.d.ts +6 -2
- package/build/helpers/theme.js +40 -7
- package/build/helpers/theme.unit.d.ts +1 -0
- package/build/helpers/theme.unit.js +30 -0
- package/build/hooks/hydrated.d.ts +2 -0
- package/build/hooks/hydrated.js +15 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -1
- package/build/models/config.d.ts +32 -6
- package/build/models/config.js +10 -1
- package/build/models/section.d.ts +4 -2
- package/build/models/section.js +1 -1
- package/build/plugins/vite-livemark.d.ts +4 -5
- package/build/plugins/vite-livemark.js +56 -90
- package/build/program.js +4 -2
- package/build/routes/$.d.ts +4 -1
- package/build/routes/$.js +20 -9
- package/build/routes/__root.js +29 -63
- package/build/settings.js +2 -1
- package/package.json +68 -66
- package/{.agents/skills → skills}/livemark-authoring/SKILL.md +2 -2
- package/{.agents/skills → skills}/livemark-configuration/SKILL.md +12 -5
- package/skills/livemark-customization/SKILL.md +145 -0
- package/source/actions/config/define.unit.ts +9 -0
- package/source/actions/escape/copy.ts +35 -0
- package/source/actions/escape/copy.unit.ts +51 -0
- package/source/actions/escape/list.ts +30 -0
- package/source/actions/escape/list.unit.ts +42 -0
- package/source/actions/target/build.ts +49 -69
- package/source/actions/target/build.unit.ts +95 -0
- package/source/actions/target/clear.ts +14 -0
- package/source/actions/target/clear.unit.ts +25 -0
- package/source/commands/build.ts +0 -1
- package/source/commands/clear.ts +15 -0
- package/source/commands/escape.ts +42 -59
- package/source/commands/preview.ts +0 -1
- package/source/commands/start.ts +0 -1
- package/source/components/AnsiCode.tsx +1 -1
- package/source/components/Article.tsx +8 -4
- package/source/components/Banner.tsx +12 -2
- package/source/components/BlogIndex.tsx +1 -1
- package/source/components/Callout.tsx +4 -6
- package/source/components/ChangelogIndex.tsx +1 -1
- package/source/components/ChoiceList.tsx +69 -0
- package/source/components/CodeBlock.tsx +6 -6
- package/source/components/CodeTabs.tsx +1 -1
- package/source/components/ContentTabs.tsx +1 -1
- package/source/components/DefaultCatchBoundary.tsx +4 -6
- package/source/components/Details.tsx +1 -1
- package/source/components/FileTree.tsx +1 -1
- package/source/components/Header.tsx +52 -0
- package/source/components/HeaderMenu.tsx +41 -0
- package/source/components/HeaderNav.tsx +145 -0
- package/source/components/HeaderSearch.tsx +77 -0
- package/source/components/InlineBadge.tsx +1 -1
- package/source/components/InlineToc.tsx +1 -1
- package/source/components/Layout.tsx +16 -160
- package/source/components/LinkButton.tsx +9 -1
- package/source/components/MenuSheet.tsx +92 -0
- package/source/components/MermaidRenderer.tsx +19 -8
- package/source/components/NotFound.tsx +3 -5
- package/source/components/PackageTabs.tsx +2 -2
- package/source/components/PageToolbar.tsx +11 -3
- package/source/components/PrevNext.tsx +2 -2
- package/source/components/Root.tsx +13 -0
- package/source/components/Search.tsx +28 -8
- package/source/components/SearchDialog.tsx +15 -189
- package/source/components/SheetPanel.tsx +69 -0
- package/source/components/Sidebar.tsx +27 -14
- package/source/components/SidebarArticles.tsx +4 -3
- package/source/components/SidebarLinks.tsx +3 -2
- package/source/components/SidebarPosts.tsx +3 -2
- package/source/components/SidebarReleases.tsx +3 -2
- package/source/components/SidebarSectionPicker.tsx +70 -0
- package/source/components/SiteTitle.tsx +5 -5
- package/source/components/SoundCloud.tsx +1 -1
- package/source/components/TagIndex.tsx +1 -1
- package/source/components/Theme.tsx +55 -29
- package/source/components/Toc.tsx +76 -55
- package/source/components/YouTube.tsx +1 -1
- package/source/content-collections.ts +4 -0
- package/source/elements/alert-dialog.tsx +1 -1
- package/source/elements/alert.tsx +1 -1
- package/source/elements/badge.tsx +4 -0
- package/source/elements/button-group.tsx +4 -4
- package/source/elements/button.tsx +10 -6
- package/source/elements/calendar.tsx +1 -1
- package/source/elements/card.tsx +3 -3
- package/source/elements/command.tsx +7 -4
- package/source/elements/context-menu.tsx +5 -5
- package/source/elements/dialog.tsx +1 -1
- package/source/elements/drawer.tsx +59 -43
- package/source/elements/dropdown-menu.tsx +6 -6
- package/source/elements/empty.tsx +1 -1
- package/source/elements/field.tsx +1 -1
- package/source/elements/hover-card.tsx +1 -1
- package/source/elements/input-group.tsx +4 -4
- package/source/elements/input.tsx +1 -1
- package/source/elements/item.tsx +1 -1
- package/source/elements/menubar.tsx +8 -8
- package/source/elements/native-select.tsx +1 -1
- package/source/elements/popover.tsx +1 -1
- package/source/elements/select.tsx +3 -3
- package/source/elements/sidebar.tsx +8 -21
- package/source/elements/tabs.tsx +2 -2
- package/source/elements/textarea.tsx +1 -1
- package/source/elements/toggle-group.tsx +2 -2
- package/source/elements/toggle.tsx +1 -1
- package/source/env.d.ts +1 -0
- package/source/head.ts +7 -0
- package/source/helpers/article-search.tsx +192 -0
- package/source/helpers/dependency-dir.ts +20 -0
- package/source/helpers/dependency-dir.unit.ts +39 -0
- package/source/helpers/head.ts +94 -0
- package/source/helpers/head.unit.ts +116 -0
- package/source/helpers/nav-items.tsx +102 -0
- package/source/helpers/render-inline-markdown.ts +44 -0
- package/source/helpers/render-inline-markdown.unit.ts +30 -0
- package/source/helpers/search-highlight.ts +73 -0
- package/source/helpers/search-highlight.unit.ts +42 -0
- package/source/helpers/target-export.ts +28 -0
- package/source/helpers/target-export.unit.ts +37 -0
- package/source/helpers/theme.ts +40 -6
- package/source/helpers/theme.unit.ts +32 -0
- package/source/hooks/hydrated.ts +18 -0
- package/source/index.ts +1 -0
- package/source/models/config.ts +13 -0
- package/source/models/section.ts +3 -0
- package/source/plugins/vite-livemark.ts +54 -95
- package/source/program.ts +2 -0
- package/source/routes/$.tsx +27 -9
- package/source/routes/__root.tsx +51 -71
- package/source/settings.ts +1 -0
- package/source/styles/general.css +59 -11
- package/source/styles/markdown.css +13 -7
- package/source/styles/shape.css +14 -0
- package/.agents/skills/change-review/SKILL.md +0 -47
- package/.agents/skills/livemark-customization/SKILL.md +0 -118
|
@@ -1,86 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from "node:fs"
|
|
2
|
+
import { basename, dirname, join, relative, resolve } from "node:path"
|
|
3
|
+
import { resolveDependencyDir } from "../../helpers/dependency-dir.ts"
|
|
4
4
|
import { ESCAPABLE_FILES, ESCAPABLE_FOLDERS } from "../../settings.ts"
|
|
5
5
|
|
|
6
|
-
// This file ships at <livemark-pkg>/source/actions/target/build.ts AND
|
|
7
|
-
// gets copied into <livemark-pkg>/targets/<hash>/actions/target/build.ts
|
|
8
|
-
// as part of every per-consumer mount. content-collections loads the
|
|
9
|
-
// COPIED version, so `import.meta.dirname` can land inside `targets/`.
|
|
10
|
-
// Walk up to the first directory holding livemark's `package.json` so
|
|
11
|
-
// SOURCE_DIR / TARGETS_ROOT always anchor at the package, never at a
|
|
12
|
-
// per-consumer dir.
|
|
13
|
-
const LIVEMARK_PKG = findLivemarkPkg(import.meta.dirname)
|
|
14
|
-
const SOURCE_DIR = join(LIVEMARK_PKG, "source")
|
|
15
|
-
const TARGETS_ROOT = join(LIVEMARK_PKG, "targets")
|
|
16
|
-
|
|
17
|
-
function findLivemarkPkg(start: string) {
|
|
18
|
-
let cur = start
|
|
19
|
-
while (cur !== dirname(cur)) {
|
|
20
|
-
if (existsSync(join(cur, "package.json"))) return cur
|
|
21
|
-
cur = dirname(cur)
|
|
22
|
-
}
|
|
23
|
-
throw new Error(`Could not locate livemark package root from ${start}`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
6
|
/**
|
|
27
|
-
* Synthesize
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* overlaying any matching files from the consumer's `.livemark/`. Every
|
|
32
|
-
* file lives at its target path as a real file — no symlinks — so
|
|
33
|
-
* Vite's relative-import and bare-specifier resolution all anchor
|
|
34
|
-
* inside the target dir, where node_modules walks reach livemark's pnpm
|
|
35
|
-
* subtree natively.
|
|
36
|
-
*
|
|
37
|
-
* Behavior:
|
|
38
|
-
* - First run (target dir missing): full source copy + overlay.
|
|
39
|
-
* - Subsequent runs: skip the source copy. Source is immutable in
|
|
40
|
-
* installed mode; in livemark's own dev the watcher keeps the target
|
|
41
|
-
* in sync. Only the `.livemark/` overlay is re-applied each time —
|
|
42
|
-
* cheap, and may have changed between runs.
|
|
43
|
-
* - With `--clear` in argv: wipe the entire target dir (including
|
|
44
|
-
* downstream caches like `.vite/`, `.content-collections/`,
|
|
45
|
-
* `cache/`, `routeTree.gen.ts`) and rebuild from scratch. Use this
|
|
46
|
-
* to recover from a stale or corrupted cache.
|
|
47
|
-
*
|
|
48
|
-
* Returns the absolute path to the target dir.
|
|
7
|
+
* Synthesize the project's Vite mount at `.livemark/.target/`: livemark's `source/` with
|
|
8
|
+
* the project's `.livemark/` escapes copied over it, plus a `node_modules` link to
|
|
9
|
+
* livemark's dependencies. Escapable folders are rebuilt on every run, so a removed escape
|
|
10
|
+
* never keeps rendering. Returns the target dir.
|
|
49
11
|
*/
|
|
50
|
-
export function buildTarget(configPath: string) {
|
|
51
|
-
const targetDir = targetDirFor(configPath)
|
|
52
|
-
const overridesRoot =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (clear) rmSync(targetDir, { recursive: true, force: true })
|
|
56
|
-
|
|
57
|
-
if (!existsSync(targetDir)) {
|
|
58
|
-
mkdirSync(targetDir, { recursive: true })
|
|
59
|
-
cpSync(SOURCE_DIR, targetDir, { recursive: true })
|
|
60
|
-
}
|
|
12
|
+
export function buildTarget(props: { configPath: string; sourceDir: string }) {
|
|
13
|
+
const targetDir = targetDirFor(props.configPath)
|
|
14
|
+
const overridesRoot = dirname(targetDir)
|
|
15
|
+
mkdirSync(targetDir, { recursive: true })
|
|
61
16
|
|
|
17
|
+
cpSync(props.sourceDir, targetDir, {
|
|
18
|
+
recursive: true,
|
|
19
|
+
filter: src => isPackageFile(src) && !isEscapableFolder(src, props.sourceDir),
|
|
20
|
+
})
|
|
62
21
|
for (const folder of ESCAPABLE_FOLDERS) {
|
|
63
|
-
const overlay = join(overridesRoot, folder)
|
|
64
|
-
if (!existsSync(overlay)) continue
|
|
65
22
|
const dest = join(targetDir, folder)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
rmSync(dest, { recursive: true, force: true })
|
|
24
|
+
const source = join(props.sourceDir, folder)
|
|
25
|
+
if (existsSync(source))
|
|
26
|
+
cpSync(source, dest, { recursive: true, filter: isPackageFile })
|
|
27
|
+
const overlay = join(overridesRoot, folder)
|
|
28
|
+
if (existsSync(overlay)) cpSync(overlay, dest, { recursive: true })
|
|
70
29
|
}
|
|
71
30
|
for (const file of ESCAPABLE_FILES) {
|
|
72
31
|
const overlay = join(overridesRoot, file)
|
|
73
|
-
if (
|
|
74
|
-
cpSync(overlay, join(targetDir, file), { force: true })
|
|
32
|
+
if (existsSync(overlay)) cpSync(overlay, join(targetDir, file))
|
|
75
33
|
}
|
|
76
34
|
|
|
35
|
+
linkDependencies(targetDir, resolveDependencyDir(props.sourceDir))
|
|
77
36
|
return targetDir
|
|
78
37
|
}
|
|
79
38
|
|
|
80
|
-
/**
|
|
81
|
-
* rebuilding it. Used by code that needs to write into the target
|
|
82
|
-
* (e.g. cache writers) outside of `buildTarget`'s setup phase. */
|
|
39
|
+
/** Locate the project's target dir without building it. */
|
|
83
40
|
export function targetDirFor(configPath: string) {
|
|
84
|
-
|
|
85
|
-
|
|
41
|
+
return join(dirname(configPath), ".livemark", ".target")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isPackageFile(src: string) {
|
|
45
|
+
return !/\.unit\.tsx?$/.test(src) && basename(src) !== "fixtures"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isEscapableFolder(src: string, sourceDir: string) {
|
|
49
|
+
return ESCAPABLE_FOLDERS.includes(relative(sourceDir, src))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function linkDependencies(targetDir: string, dependencyDir: string) {
|
|
53
|
+
const link = join(targetDir, "node_modules")
|
|
54
|
+
if (readLink(link) === resolve(dependencyDir)) return
|
|
55
|
+
// Not recursive: a stale link must go without touching the store it points into.
|
|
56
|
+
rmSync(link, { force: true })
|
|
57
|
+
symlinkSync(dependencyDir, link, "junction")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readLink(path: string) {
|
|
61
|
+
try {
|
|
62
|
+
return resolve(dirname(path), readlinkSync(path))
|
|
63
|
+
} catch {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
86
66
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readlinkSync, realpathSync } from "node:fs"
|
|
2
|
+
import { mkdir, rm, symlink, writeFile } from "node:fs/promises"
|
|
3
|
+
import { dirname, join } from "node:path"
|
|
4
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
5
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
6
|
+
import { buildTarget, targetDirFor } from "./build.ts"
|
|
7
|
+
|
|
8
|
+
describe("buildTarget", () => {
|
|
9
|
+
it("copies the source, overlays escapes and skips unit tests", () =>
|
|
10
|
+
temporaryDirectoryTask(async root => {
|
|
11
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
12
|
+
await put(join(root, "project/.livemark/components/Footer.tsx"), "escaped")
|
|
13
|
+
await put(join(root, "project/.livemark/head.ts"), "escaped head")
|
|
14
|
+
|
|
15
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
16
|
+
|
|
17
|
+
expect(targetDir).toBe(join(root, "project/.livemark/.target"))
|
|
18
|
+
expect(read(targetDir, "components/Footer.tsx")).toBe("escaped")
|
|
19
|
+
expect(read(targetDir, "components/Header.tsx")).toBe("default")
|
|
20
|
+
expect(read(targetDir, "head.ts")).toBe("escaped head")
|
|
21
|
+
expect(read(targetDir, "helpers/util.ts")).toBe("util")
|
|
22
|
+
expect(existsSync(join(targetDir, "helpers/util.unit.ts"))).toBe(false)
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
it("drops escapes removed since the last run", () =>
|
|
26
|
+
temporaryDirectoryTask(async root => {
|
|
27
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
28
|
+
const overrides = join(root, "project/.livemark")
|
|
29
|
+
await put(join(overrides, "components/Footer.tsx"), "escaped")
|
|
30
|
+
await put(join(overrides, "routes/about.tsx"), "about")
|
|
31
|
+
await put(join(overrides, "head.ts"), "escaped head")
|
|
32
|
+
buildTarget({ configPath, sourceDir })
|
|
33
|
+
|
|
34
|
+
await rm(join(overrides, "components"), { recursive: true })
|
|
35
|
+
await rm(join(overrides, "routes"), { recursive: true })
|
|
36
|
+
await rm(join(overrides, "head.ts"))
|
|
37
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
38
|
+
|
|
39
|
+
expect(read(targetDir, "components/Footer.tsx")).toBe("default")
|
|
40
|
+
expect(existsSync(join(targetDir, "routes/about.tsx"))).toBe(false)
|
|
41
|
+
expect(read(targetDir, "head.ts")).toBe("head")
|
|
42
|
+
}))
|
|
43
|
+
|
|
44
|
+
it("keeps generated entries between runs", () =>
|
|
45
|
+
temporaryDirectoryTask(async root => {
|
|
46
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
47
|
+
const targetDir = buildTarget({ configPath, sourceDir })
|
|
48
|
+
await put(join(targetDir, "cache/changelog/entry.md"), "cached")
|
|
49
|
+
|
|
50
|
+
buildTarget({ configPath, sourceDir })
|
|
51
|
+
|
|
52
|
+
expect(read(targetDir, "cache/changelog/entry.md")).toBe("cached")
|
|
53
|
+
}))
|
|
54
|
+
|
|
55
|
+
it("links node_modules to livemark's dependencies and repairs a stale link", () =>
|
|
56
|
+
temporaryDirectoryTask(async root => {
|
|
57
|
+
const { configPath, sourceDir } = await createFixture(root)
|
|
58
|
+
const link = join(targetDirFor(configPath), "node_modules")
|
|
59
|
+
const dependencyDir = realpathSync(join(root, "livemark/node_modules"))
|
|
60
|
+
|
|
61
|
+
buildTarget({ configPath, sourceDir })
|
|
62
|
+
expect(readlinkSync(link)).toBe(dependencyDir)
|
|
63
|
+
|
|
64
|
+
await rm(link)
|
|
65
|
+
await mkdir(join(root, "elsewhere"))
|
|
66
|
+
await symlink(join(root, "elsewhere"), link, "junction")
|
|
67
|
+
buildTarget({ configPath, sourceDir })
|
|
68
|
+
|
|
69
|
+
expect(readlinkSync(link)).toBe(dependencyDir)
|
|
70
|
+
expect(existsSync(join(root, "elsewhere"))).toBe(true)
|
|
71
|
+
}))
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
async function createFixture(root: string) {
|
|
75
|
+
const sourceDir = join(root, "livemark/source")
|
|
76
|
+
await put(join(sourceDir, "components/Footer.tsx"), "default")
|
|
77
|
+
await put(join(sourceDir, "components/Header.tsx"), "default")
|
|
78
|
+
await put(join(sourceDir, "routes/index.tsx"), "index")
|
|
79
|
+
await put(join(sourceDir, "helpers/util.ts"), "util")
|
|
80
|
+
await put(join(sourceDir, "helpers/util.unit.ts"), "test")
|
|
81
|
+
await put(join(sourceDir, "head.ts"), "head")
|
|
82
|
+
await mkdir(join(root, "livemark/node_modules"))
|
|
83
|
+
const configPath = join(root, "project/livemark.config.ts")
|
|
84
|
+
await put(configPath, "export default {}")
|
|
85
|
+
return { configPath, sourceDir }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function put(path: string, content: string) {
|
|
89
|
+
await mkdir(dirname(path), { recursive: true })
|
|
90
|
+
await writeFile(path, content)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function read(dir: string, path: string) {
|
|
94
|
+
return readFileSync(join(dir, path), "utf-8")
|
|
95
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { rmSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { targetDirFor } from "./build.ts"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wipe the project's target dir so the next run rebuilds it from scratch.
|
|
7
|
+
*/
|
|
8
|
+
export function clearTarget(configPath: string) {
|
|
9
|
+
const targetDir = targetDirFor(configPath)
|
|
10
|
+
// Unlink the dependency link first so the recursive remove cannot reach the store.
|
|
11
|
+
rmSync(join(targetDir, "node_modules"), { force: true })
|
|
12
|
+
rmSync(targetDir, { recursive: true, force: true })
|
|
13
|
+
return targetDir
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { mkdir, symlink, writeFile } from "node:fs/promises"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { temporaryDirectoryTask } from "tempy"
|
|
5
|
+
import { describe, expect, it } from "vite-plus/test"
|
|
6
|
+
import { targetDirFor } from "./build.ts"
|
|
7
|
+
import { clearTarget } from "./clear.ts"
|
|
8
|
+
|
|
9
|
+
describe("clearTarget", () => {
|
|
10
|
+
it("removes the target dir without following its dependency link", () =>
|
|
11
|
+
temporaryDirectoryTask(async root => {
|
|
12
|
+
const configPath = join(root, "livemark.config.ts")
|
|
13
|
+
const targetDir = targetDirFor(configPath)
|
|
14
|
+
await mkdir(join(targetDir, "cache"), { recursive: true })
|
|
15
|
+
await writeFile(join(targetDir, "cache", "entry.md"), "cached")
|
|
16
|
+
await mkdir(join(root, "deps"))
|
|
17
|
+
await writeFile(join(root, "deps", "kept.js"), "")
|
|
18
|
+
await symlink(join(root, "deps"), join(targetDir, "node_modules"), "junction")
|
|
19
|
+
|
|
20
|
+
expect(clearTarget(configPath)).toBe(targetDir)
|
|
21
|
+
expect(existsSync(targetDir)).toBe(false)
|
|
22
|
+
expect(existsSync(join(root, "deps", "kept.js"))).toBe(true)
|
|
23
|
+
expect(() => clearTarget(configPath)).not.toThrow()
|
|
24
|
+
}))
|
|
25
|
+
})
|
package/source/commands/build.ts
CHANGED
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const build = new Command("build")
|
|
11
11
|
.description("Build the site for production")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const builder = await createBuilder({ configFile })
|
|
15
14
|
await builder.buildApp()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from "commander"
|
|
2
|
+
import pc from "picocolors"
|
|
3
|
+
import { loadConfig } from "../actions/config/load.ts"
|
|
4
|
+
import { clearTarget } from "../actions/target/clear.ts"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wipe the generated `.livemark/.target/` mount.
|
|
8
|
+
*/
|
|
9
|
+
export const clear = new Command("clear")
|
|
10
|
+
.description("Wipe the generated .livemark/.target/ mount")
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const config = await loadConfig()
|
|
13
|
+
const targetDir = clearTarget(config.configPath)
|
|
14
|
+
console.log(`Cleared ${pc.dim(targetDir)}`)
|
|
15
|
+
})
|
|
@@ -1,79 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
statSync,
|
|
7
|
-
writeFileSync,
|
|
8
|
-
} from "node:fs"
|
|
9
|
-
import { dirname, join } from "node:path"
|
|
1
|
+
import { join } from "node:path"
|
|
2
|
+
import { autocompleteMultiselect, cancel, isCancel } from "@clack/prompts"
|
|
10
3
|
import { Command } from "commander"
|
|
11
4
|
import pc from "picocolors"
|
|
12
|
-
import {
|
|
5
|
+
import { copyEscape } from "../actions/escape/copy.ts"
|
|
6
|
+
import { listEscapes } from "../actions/escape/list.ts"
|
|
13
7
|
|
|
14
|
-
const
|
|
8
|
+
const sourceRoot = join(import.meta.dirname, "..", "..", "source")
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
*
|
|
11
|
+
* Pick, list or copy escapable livemark files into `.livemark/`.
|
|
18
12
|
*
|
|
19
|
-
* Without an argument,
|
|
20
|
-
*
|
|
13
|
+
* Without an argument, opens a searchable picker in a terminal and prints every
|
|
14
|
+
* file you can shadow otherwise (or with `--list`). With paths, copies each
|
|
15
|
+
* default from the livemark package into `.livemark/<path>`; fails if a
|
|
21
16
|
* destination already exists.
|
|
22
17
|
*/
|
|
23
18
|
export const escape = new Command("escape")
|
|
24
|
-
.description("
|
|
19
|
+
.description("Pick, list or copy escapable livemark files into .livemark/")
|
|
25
20
|
.argument(
|
|
26
|
-
"[
|
|
27
|
-
"
|
|
21
|
+
"[paths...]",
|
|
22
|
+
"paths relative to .livemark/ (e.g. components/Footer.tsx, client.tsx)",
|
|
28
23
|
)
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
.option("-l, --list", "print every escapable file instead of opening the picker")
|
|
25
|
+
.action(async (paths: string[], options: { list?: boolean }) => {
|
|
26
|
+
const root = process.cwd()
|
|
27
|
+
if (paths.length) return copyPaths(root, paths)
|
|
28
|
+
|
|
29
|
+
const escapes = listEscapes({ root, sourceRoot })
|
|
30
|
+
if (options.list || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
31
|
+
for (const escape of escapes) console.log(escape.path)
|
|
32
32
|
console.log(pc.dim("\nRun 'livemark escape <path>' to copy a file into .livemark/"))
|
|
33
33
|
return
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const src = join(defaultsRoot, relPath)
|
|
50
|
-
if (!existsSync(src) || !statSync(src).isFile()) {
|
|
51
|
-
console.error(pc.red(`Error: '${relPath}' is not a livemark file`))
|
|
52
|
-
process.exit(1)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const dest = join(process.cwd(), ".livemark", relPath)
|
|
56
|
-
if (existsSync(dest)) {
|
|
57
|
-
console.error(pc.red(`Error: .livemark/${relPath} already exists`))
|
|
58
|
-
process.exit(1)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
mkdirSync(dirname(dest), { recursive: true })
|
|
62
|
-
writeFileSync(dest, readFileSync(src))
|
|
63
|
-
console.log(`Copied ${pc.dim(relPath)} → ${pc.green(`.livemark/${relPath}`)}`)
|
|
36
|
+
const selected = await autocompleteMultiselect({
|
|
37
|
+
message: "Select files to escape into .livemark/",
|
|
38
|
+
placeholder: "Type to search...",
|
|
39
|
+
options: escapes.map(escape => ({
|
|
40
|
+
value: escape.path,
|
|
41
|
+
label: escape.path,
|
|
42
|
+
hint: escape.escaped ? "already escaped" : undefined,
|
|
43
|
+
disabled: escape.escaped,
|
|
44
|
+
})),
|
|
45
|
+
required: false,
|
|
46
|
+
})
|
|
47
|
+
if (isCancel(selected)) return cancel("Nothing copied")
|
|
48
|
+
copyPaths(root, selected)
|
|
64
49
|
})
|
|
65
50
|
|
|
66
|
-
function
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
51
|
+
function copyPaths(root: string, paths: string[]) {
|
|
52
|
+
for (const path of paths) {
|
|
53
|
+
try {
|
|
54
|
+
copyEscape({ root, sourceRoot, path })
|
|
55
|
+
console.log(`Copied ${pc.dim(path)} → ${pc.green(`.livemark/${path}`)}`)
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
58
|
+
console.error(pc.red(`Error: ${message}`))
|
|
59
|
+
process.exitCode = 1
|
|
73
60
|
}
|
|
74
61
|
}
|
|
75
|
-
for (const file of ESCAPABLE_FILES) {
|
|
76
|
-
if (existsSync(join(defaultsRoot, file))) files.push(file)
|
|
77
|
-
}
|
|
78
|
-
return files.sort()
|
|
79
62
|
}
|
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const preview = new Command("preview")
|
|
11
11
|
.description("Preview the production build locally")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const server = await vitePreview({
|
|
15
14
|
configFile,
|
package/source/commands/start.ts
CHANGED
|
@@ -9,7 +9,6 @@ const configFile = join(import.meta.dirname, "..", "..", "source", "vite.config.
|
|
|
9
9
|
*/
|
|
10
10
|
export const start = new Command("start")
|
|
11
11
|
.description("Start a live server")
|
|
12
|
-
.option("--clear", "Wipe and fully rebuild the runtime target dir")
|
|
13
12
|
.action(async () => {
|
|
14
13
|
const server = await createServer({
|
|
15
14
|
configFile,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Renders pre-formatted HTML produced from ANSI escape sequences */
|
|
2
2
|
export function AnsiCode(props: { html: string }) {
|
|
3
3
|
return (
|
|
4
|
-
<figure className="shiki not-prose my-4 relative rounded-
|
|
4
|
+
<figure className="shiki not-prose my-4 relative rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
|
|
5
5
|
<div className="text-[0.8125rem] py-3.5 overflow-auto">
|
|
6
6
|
<pre className="min-w-full w-max">
|
|
7
7
|
<code
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { useRef } from "react"
|
|
1
2
|
import { MDXContent } from "@content-collections/mdx/react"
|
|
2
3
|
import { Calendar, History, User } from "lucide-react"
|
|
3
4
|
import type { ArticleView } from "../models/article.ts"
|
|
5
|
+
import { useSearchHighlight } from "../helpers/search-highlight.ts"
|
|
4
6
|
import { TocContext } from "../helpers/toc-context.ts"
|
|
5
7
|
import { Abbr } from "./Abbr.tsx"
|
|
6
8
|
import { AnsiCode } from "./AnsiCode.tsx"
|
|
@@ -42,8 +44,10 @@ import { ZoomImage } from "./ZoomImage.tsx"
|
|
|
42
44
|
import { Separator } from "../elements/separator.tsx"
|
|
43
45
|
|
|
44
46
|
/** Full-page article layout: MDX content, table of contents, toolbar, prev/next, footer */
|
|
45
|
-
export function Article(props: { article: ArticleView }) {
|
|
47
|
+
export function Article(props: { article: ArticleView; highlight?: string }) {
|
|
46
48
|
const { article } = props
|
|
49
|
+
const prose = useRef<HTMLDivElement>(null)
|
|
50
|
+
useSearchHighlight(prose, article.path, props.highlight)
|
|
47
51
|
const lastUpdated = formatDate(article.lastUpdated)
|
|
48
52
|
const publishedDate = formatDate(article.date)
|
|
49
53
|
const authors = article.author
|
|
@@ -56,7 +60,7 @@ export function Article(props: { article: ArticleView }) {
|
|
|
56
60
|
return (
|
|
57
61
|
<TocContext.Provider value={article.tocItems}>
|
|
58
62
|
{article.toc !== false && (
|
|
59
|
-
<MobileToc items={article.tocItems}>
|
|
63
|
+
<MobileToc items={article.tocItems} title={article.title}>
|
|
60
64
|
<PageToolbar
|
|
61
65
|
file={article.file}
|
|
62
66
|
sourceUrl={article.sourceUrl}
|
|
@@ -67,7 +71,7 @@ export function Article(props: { article: ArticleView }) {
|
|
|
67
71
|
)}
|
|
68
72
|
<div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
|
|
69
73
|
<div className="flex-1 min-w-0 mx-auto max-w-3xl">
|
|
70
|
-
<div className="prose dark:prose-invert max-w-none">
|
|
74
|
+
<div ref={prose} className="prose dark:prose-invert max-w-none">
|
|
71
75
|
<h1 id="top">
|
|
72
76
|
<a href="#top">{article.title}</a>
|
|
73
77
|
</h1>
|
|
@@ -97,7 +101,7 @@ export function Article(props: { article: ArticleView }) {
|
|
|
97
101
|
<img
|
|
98
102
|
src={article.image}
|
|
99
103
|
alt=""
|
|
100
|
-
className="!mt-0 !mb-6 w-full rounded-
|
|
104
|
+
className="!mt-0 !mb-6 w-full rounded-card border shadow-sm"
|
|
101
105
|
/>
|
|
102
106
|
)}
|
|
103
107
|
<MDXContent
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { bannerHtml } from "livemark:virtual"
|
|
2
|
+
|
|
3
|
+
/** Announcement strip above the header, rendered from the `banner` config option. */
|
|
2
4
|
export function Banner() {
|
|
3
|
-
return null
|
|
5
|
+
if (!bannerHtml) return null
|
|
6
|
+
return (
|
|
7
|
+
<div className="bg-banner text-banner-foreground">
|
|
8
|
+
<div
|
|
9
|
+
className="min-w-0 px-4 py-2 text-center font-mono text-xs tracking-wide text-balance sm:px-6 [&_a]:underline [&_a]:underline-offset-4 [&_a:hover]:no-underline [&_code]:font-semibold"
|
|
10
|
+
dangerouslySetInnerHTML={{ __html: bannerHtml }}
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
4
14
|
}
|
|
@@ -40,7 +40,7 @@ export function BlogIndex(props: { sectionPrefix: string }) {
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<TocContext.Provider value={tocItems}>
|
|
43
|
-
<MobileToc items={tocItems}>
|
|
43
|
+
<MobileToc items={tocItems} title={section?.title ?? "Blog"}>
|
|
44
44
|
<PageToolbar content={content} />
|
|
45
45
|
</MobileToc>
|
|
46
46
|
<div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
|
|
@@ -25,20 +25,18 @@ const configs: Record<CalloutType, CalloutConfig> = {
|
|
|
25
25
|
tip: {
|
|
26
26
|
icon: Lightbulb,
|
|
27
27
|
label: "Tip",
|
|
28
|
-
classes:
|
|
29
|
-
"border-green-500/30 bg-green-50/50 text-green-900 dark:bg-green-950/30 dark:text-green-200",
|
|
28
|
+
classes: "border-positive/30 bg-positive-tint/50 text-positive-tint-foreground",
|
|
30
29
|
},
|
|
31
30
|
warning: {
|
|
32
31
|
icon: AlertTriangle,
|
|
33
32
|
label: "Warning",
|
|
34
|
-
classes:
|
|
35
|
-
"border-yellow-500/30 bg-yellow-50/50 text-yellow-900 dark:bg-yellow-950/30 dark:text-yellow-200",
|
|
33
|
+
classes: "border-warning/30 bg-warning-tint/50 text-warning-tint-foreground",
|
|
36
34
|
},
|
|
37
35
|
danger: {
|
|
38
36
|
icon: Flame,
|
|
39
37
|
label: "Danger",
|
|
40
38
|
classes:
|
|
41
|
-
"border-
|
|
39
|
+
"border-destructive/30 bg-destructive-tint/50 text-destructive-tint-foreground",
|
|
42
40
|
},
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -54,7 +52,7 @@ export function Callout(props: {
|
|
|
54
52
|
const title = props.title ?? config.label
|
|
55
53
|
|
|
56
54
|
return (
|
|
57
|
-
<aside className={cn("not-prose my-4 rounded-
|
|
55
|
+
<aside className={cn("not-prose my-4 rounded-card border px-4 py-3", config.classes)}>
|
|
58
56
|
<div className="flex items-center gap-2 font-semibold text-sm mb-2">
|
|
59
57
|
<Icon className="size-4 shrink-0" />
|
|
60
58
|
<span>{title}</span>
|
|
@@ -37,7 +37,7 @@ export function ChangelogIndex(props: { sectionPrefix: string }) {
|
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
39
|
<TocContext.Provider value={tocItems}>
|
|
40
|
-
<MobileToc items={tocItems}>
|
|
40
|
+
<MobileToc items={tocItems} title={section?.title ?? "Changelog"}>
|
|
41
41
|
<PageToolbar content={content} />
|
|
42
42
|
</MobileToc>
|
|
43
43
|
<div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { CheckIcon } from "lucide-react"
|
|
2
|
+
import type { MouseEvent, ReactNode } from "react"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
export type Choice = {
|
|
6
|
+
id: string
|
|
7
|
+
label: ReactNode
|
|
8
|
+
icon?: ReactNode
|
|
9
|
+
href?: string
|
|
10
|
+
/** Indent level below the top one, for a nested choice like a sub-heading. */
|
|
11
|
+
depth?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The rows of a sheet that picks one of a list, the selected one tinted and ticked. */
|
|
15
|
+
export function ChoiceList(props: {
|
|
16
|
+
choices: Choice[]
|
|
17
|
+
selectedId?: string
|
|
18
|
+
onSelect?: (choice: Choice, event: MouseEvent<HTMLElement>) => void
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<ul className="-mx-2 flex flex-col gap-0.5">
|
|
22
|
+
{props.choices.map(choice => {
|
|
23
|
+
const selected = choice.id === props.selectedId
|
|
24
|
+
const className = cn(
|
|
25
|
+
"hover:bg-muted active:bg-muted flex h-10 w-full items-center gap-2.5 rounded-control px-2 text-left text-base outline-hidden focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-[18px] [&_svg]:shrink-0",
|
|
26
|
+
selected && "bg-sidebar-accent text-sidebar-accent-foreground font-medium",
|
|
27
|
+
)
|
|
28
|
+
const content = (
|
|
29
|
+
<>
|
|
30
|
+
{choice.icon}
|
|
31
|
+
<span
|
|
32
|
+
className={cn(
|
|
33
|
+
"flex-1 truncate",
|
|
34
|
+
choice.depth === 1 && "ps-4",
|
|
35
|
+
(choice.depth ?? 0) >= 2 && "ps-8",
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
{choice.label}
|
|
39
|
+
</span>
|
|
40
|
+
{selected && <CheckIcon className="text-primary" />}
|
|
41
|
+
</>
|
|
42
|
+
)
|
|
43
|
+
return (
|
|
44
|
+
<li key={choice.id}>
|
|
45
|
+
{choice.href ? (
|
|
46
|
+
<a
|
|
47
|
+
href={choice.href}
|
|
48
|
+
aria-current={selected || undefined}
|
|
49
|
+
className={className}
|
|
50
|
+
onClick={event => props.onSelect?.(choice, event)}
|
|
51
|
+
>
|
|
52
|
+
{content}
|
|
53
|
+
</a>
|
|
54
|
+
) : (
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
aria-pressed={selected}
|
|
58
|
+
className={className}
|
|
59
|
+
onClick={event => props.onSelect?.(choice, event)}
|
|
60
|
+
>
|
|
61
|
+
{content}
|
|
62
|
+
</button>
|
|
63
|
+
)}
|
|
64
|
+
</li>
|
|
65
|
+
)
|
|
66
|
+
})}
|
|
67
|
+
</ul>
|
|
68
|
+
)
|
|
69
|
+
}
|