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,4 +1,5 @@
|
|
|
1
1
|
import { config } from "livemark:virtual"
|
|
2
|
+
import { useState } from "react"
|
|
2
3
|
import { useLocation, useMatch } from "@tanstack/react-router"
|
|
3
4
|
import { useHotkey } from "@tanstack/react-hotkeys"
|
|
4
5
|
import { currentSection } from "../content/article.ts"
|
|
@@ -6,7 +7,6 @@ import { activeSiteLink } from "../helpers/section.ts"
|
|
|
6
7
|
import {
|
|
7
8
|
Sidebar as SidebarRoot,
|
|
8
9
|
SidebarContent,
|
|
9
|
-
SidebarFooter,
|
|
10
10
|
SidebarHeader,
|
|
11
11
|
SidebarMenu,
|
|
12
12
|
SidebarMenuButton,
|
|
@@ -14,15 +14,15 @@ import {
|
|
|
14
14
|
SidebarRail,
|
|
15
15
|
useSidebar,
|
|
16
16
|
} from "../elements/sidebar.tsx"
|
|
17
|
-
import { Search } from "./Search.tsx"
|
|
18
17
|
import { SidebarArticles } from "./SidebarArticles.tsx"
|
|
19
18
|
import { SidebarLinks } from "./SidebarLinks.tsx"
|
|
20
19
|
import { SidebarPosts } from "./SidebarPosts.tsx"
|
|
21
20
|
import { SidebarReleases } from "./SidebarReleases.tsx"
|
|
21
|
+
import { SidebarSectionPicker } from "./SidebarSectionPicker.tsx"
|
|
22
22
|
import { SidebarSections } from "./SidebarSections.tsx"
|
|
23
23
|
import { SiteLink } from "./SiteLink.tsx"
|
|
24
24
|
import { SiteTitle } from "./SiteTitle.tsx"
|
|
25
|
-
import {
|
|
25
|
+
import type { RoutedSection } from "../models/section.ts"
|
|
26
26
|
|
|
27
27
|
/** Application sidebar shell: header, body slot picked by section.type, footer */
|
|
28
28
|
export function Sidebar(props: { withSidebar?: boolean }) {
|
|
@@ -30,13 +30,19 @@ export function Sidebar(props: { withSidebar?: boolean }) {
|
|
|
30
30
|
const articleRoute = useMatch({ from: "/$", shouldThrow: false })
|
|
31
31
|
const { toggleSidebar, isMobile } = useSidebar()
|
|
32
32
|
|
|
33
|
-
useHotkey("
|
|
33
|
+
useHotkey("L", () => {
|
|
34
34
|
if (props.withSidebar || isMobile) toggleSidebar()
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const [browsed, setBrowsed] = useState<{ section: RoutedSection; pathname: string }>()
|
|
38
|
+
|
|
39
|
+
const routeSection = articleRoute
|
|
38
40
|
? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
39
41
|
: undefined
|
|
42
|
+
const picking = isMobile && routeSection !== undefined
|
|
43
|
+
// Browsing is scoped to the page it started on, so navigating resets it
|
|
44
|
+
const section =
|
|
45
|
+
picking && browsed?.pathname === pathname ? browsed.section : routeSection
|
|
40
46
|
const siteLinkTarget = activeSiteLink(pathname, config.sections ?? [])
|
|
41
47
|
|
|
42
48
|
return (
|
|
@@ -51,18 +57,25 @@ export function Sidebar(props: { withSidebar?: boolean }) {
|
|
|
51
57
|
</SidebarMenu>
|
|
52
58
|
</SidebarHeader>
|
|
53
59
|
<SidebarContent>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
{picking && section ? (
|
|
61
|
+
<SidebarSectionPicker
|
|
62
|
+
section={section}
|
|
63
|
+
onSelect={picked => setBrowsed({ section: picked, pathname })}
|
|
64
|
+
/>
|
|
65
|
+
) : (
|
|
66
|
+
<SidebarSections />
|
|
67
|
+
)}
|
|
68
|
+
{props.withSidebar && section?.type === "blog" && (
|
|
69
|
+
<SidebarPosts section={section} />
|
|
70
|
+
)}
|
|
71
|
+
{props.withSidebar && section?.type === "changelog" && (
|
|
72
|
+
<SidebarReleases section={section} />
|
|
73
|
+
)}
|
|
57
74
|
{props.withSidebar &&
|
|
58
75
|
section?.type !== "blog" &&
|
|
59
|
-
section?.type !== "changelog" && <SidebarArticles />}
|
|
60
|
-
<SidebarLinks />
|
|
76
|
+
section?.type !== "changelog" && <SidebarArticles section={section} />}
|
|
77
|
+
<SidebarLinks section={section} />
|
|
61
78
|
</SidebarContent>
|
|
62
|
-
<SidebarFooter className="p-4 space-y-2">
|
|
63
|
-
<Search />
|
|
64
|
-
<Theme />
|
|
65
|
-
</SidebarFooter>
|
|
66
79
|
<SidebarRail />
|
|
67
80
|
</SidebarRoot>
|
|
68
81
|
)
|
|
@@ -20,13 +20,14 @@ import {
|
|
|
20
20
|
} from "../elements/sidebar.tsx"
|
|
21
21
|
import { resolveArticleIcon } from "../helpers/article-icon.ts"
|
|
22
22
|
import type { ArticleNode } from "../models/article.ts"
|
|
23
|
+
import type { RoutedSection } from "../models/section.ts"
|
|
23
24
|
|
|
24
25
|
/** Article-tree body slot for the Sidebar shell */
|
|
25
|
-
export function SidebarArticles() {
|
|
26
|
+
export function SidebarArticles(props: { section?: RoutedSection }) {
|
|
26
27
|
const pathname = useLocation({ select: l => l.pathname })
|
|
27
28
|
|
|
28
29
|
const configSections = config.sections
|
|
29
|
-
const section = currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
30
|
+
const section = props.section ?? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
30
31
|
const tree = configSections?.length
|
|
31
32
|
? (sectionArticleTrees.get(section?.prefix ?? "__default__") ?? articleTree)
|
|
32
33
|
: articleTree
|
|
@@ -88,7 +89,7 @@ function NavNode(props: { node: ArticleNode; currentPath: string; depth?: number
|
|
|
88
89
|
</Button>
|
|
89
90
|
<CollapsibleTrigger
|
|
90
91
|
aria-label={`Toggle ${node.label ?? node.title} submenu`}
|
|
91
|
-
className="absolute right-1 top-1.5 p-1 rounded-
|
|
92
|
+
className="absolute right-1 top-1.5 p-1 rounded-control hover:bg-sidebar-accent"
|
|
92
93
|
>
|
|
93
94
|
<ChevronRight className="size-3.5 transition-transform group-data-[open]/collapsible:rotate-90" />
|
|
94
95
|
</CollapsibleTrigger>
|
|
@@ -11,13 +11,14 @@ import {
|
|
|
11
11
|
SidebarMenuButton,
|
|
12
12
|
SidebarMenuItem,
|
|
13
13
|
} from "../elements/sidebar.tsx"
|
|
14
|
+
import type { RoutedSection } from "../models/section.ts"
|
|
14
15
|
|
|
15
16
|
/** Shared Links group. Renders sidebar-positioned links always, and
|
|
16
17
|
* header-positioned links only on mobile (mobile collapses all chrome
|
|
17
18
|
* links into the sidebar since the header strip is hidden below `md`). */
|
|
18
|
-
export function SidebarLinks() {
|
|
19
|
+
export function SidebarLinks(props: { section?: RoutedSection }) {
|
|
19
20
|
const pathname = useLocation({ select: l => l.pathname })
|
|
20
|
-
const section = currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
21
|
+
const section = props.section ?? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
21
22
|
const links =
|
|
22
23
|
config.links?.filter(link => !link.prefix || link.prefix === section?.prefix) ?? []
|
|
23
24
|
|
|
@@ -17,12 +17,13 @@ import {
|
|
|
17
17
|
SidebarMenuSubButton,
|
|
18
18
|
SidebarMenuSubItem,
|
|
19
19
|
} from "../elements/sidebar.tsx"
|
|
20
|
+
import type { RoutedSection } from "../models/section.ts"
|
|
20
21
|
|
|
21
22
|
/** Blog body slot for the Sidebar shell: All Posts / Recent Posts / RSS / Tags */
|
|
22
|
-
export function SidebarPosts() {
|
|
23
|
+
export function SidebarPosts(props: { section?: RoutedSection }) {
|
|
23
24
|
const pathname = useLocation({ select: l => l.pathname })
|
|
24
25
|
|
|
25
|
-
const section = currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
26
|
+
const section = props.section ?? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
26
27
|
const flat = section ? (sectionFlatArticles.get(section.prefix) ?? []) : []
|
|
27
28
|
const posts = flat
|
|
28
29
|
.map(p => sortedArticles.find(a => a.path === p))
|
|
@@ -16,12 +16,13 @@ import {
|
|
|
16
16
|
SidebarMenuSubButton,
|
|
17
17
|
SidebarMenuSubItem,
|
|
18
18
|
} from "../elements/sidebar.tsx"
|
|
19
|
+
import type { RoutedSection } from "../models/section.ts"
|
|
19
20
|
|
|
20
21
|
/** Changelog body slot for the Sidebar shell: All Releases / Recent Releases */
|
|
21
|
-
export function SidebarReleases() {
|
|
22
|
+
export function SidebarReleases(props: { section?: RoutedSection }) {
|
|
22
23
|
const pathname = useLocation({ select: l => l.pathname })
|
|
23
24
|
|
|
24
|
-
const section = currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
25
|
+
const section = props.section ?? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
25
26
|
const flat = section ? (sectionFlatArticles.get(section.prefix) ?? []) : []
|
|
26
27
|
const entries = flat
|
|
27
28
|
.map(p => sortedArticles.find(a => a.path === p))
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { config } from "livemark:virtual"
|
|
2
|
+
import { ChevronDownIcon } from "lucide-react"
|
|
3
|
+
import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
|
|
4
|
+
import { prefixUrl } from "../helpers/prefix-url.ts"
|
|
5
|
+
import {
|
|
6
|
+
SidebarGroup,
|
|
7
|
+
SidebarGroupContent,
|
|
8
|
+
SidebarGroupLabel,
|
|
9
|
+
} from "../elements/sidebar.tsx"
|
|
10
|
+
import type { RoutedSection, Section } from "../models/section.ts"
|
|
11
|
+
import { MenuSheet } from "./MenuSheet.tsx"
|
|
12
|
+
|
|
13
|
+
/** Mobile Sections group: a select-like trigger opening a bottom sheet that swaps the
|
|
14
|
+
* sidebar body to another section without navigating; custom sections still navigate
|
|
15
|
+
* to their URL. */
|
|
16
|
+
export function SidebarSectionPicker(props: {
|
|
17
|
+
section: RoutedSection
|
|
18
|
+
onSelect: (section: RoutedSection) => void
|
|
19
|
+
}) {
|
|
20
|
+
const sections = config.sections ?? []
|
|
21
|
+
|
|
22
|
+
const selectSection = (picked: Section) => {
|
|
23
|
+
if (picked.type === "custom") {
|
|
24
|
+
window.location.assign(prefixUrl(picked.url, config.base))
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
props.onSelect(picked)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<SidebarGroup>
|
|
32
|
+
<SidebarGroupLabel className="uppercase font-mono text-xs tracking-widest">
|
|
33
|
+
Sections
|
|
34
|
+
</SidebarGroupLabel>
|
|
35
|
+
<SidebarGroupContent>
|
|
36
|
+
<MenuSheet
|
|
37
|
+
title="Choose section"
|
|
38
|
+
items={sections.map(s => ({
|
|
39
|
+
id: sectionKey(s),
|
|
40
|
+
label: s.title,
|
|
41
|
+
icon: s.icon ? <DynamicIcon name={s.icon} className="size-4" /> : undefined,
|
|
42
|
+
active: s.type !== "custom" && s.prefix === props.section.prefix,
|
|
43
|
+
onSelect: () => selectSection(s),
|
|
44
|
+
}))}
|
|
45
|
+
trigger={
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
aria-label="Section"
|
|
49
|
+
aria-haspopup="dialog"
|
|
50
|
+
className="flex h-9 w-full min-w-0 cursor-pointer items-center gap-2 rounded-control border border-input bg-transparent px-3 text-left text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 dark:bg-input/30 dark:hover:bg-input/50"
|
|
51
|
+
/>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
{props.section.icon && (
|
|
55
|
+
<DynamicIcon name={props.section.icon} className="size-4 shrink-0" />
|
|
56
|
+
)}
|
|
57
|
+
<span className="flex-1 truncate">{props.section.title}</span>
|
|
58
|
+
<ChevronDownIcon
|
|
59
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
/>
|
|
62
|
+
</MenuSheet>
|
|
63
|
+
</SidebarGroupContent>
|
|
64
|
+
</SidebarGroup>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sectionKey(section: Section) {
|
|
69
|
+
return section.type === "custom" ? `url:${section.url}` : section.prefix
|
|
70
|
+
}
|
|
@@ -14,11 +14,11 @@ export function SiteTitle() {
|
|
|
14
14
|
const description = section?.siteDescription ?? config.description
|
|
15
15
|
const logo = prefixUrl(config.logo ?? defaultLogo, config.base)
|
|
16
16
|
return (
|
|
17
|
-
<div className="flex items-end gap-2.5 text-sm">
|
|
18
|
-
<img src={logo} alt={title} className="size-6.5 mb-1.25" />
|
|
19
|
-
<div className="flex flex-col gap-0.5 leading-none">
|
|
20
|
-
<span className="font-semibold">{title}</span>
|
|
21
|
-
<span className="text-xs opacity-80">{description}</span>
|
|
17
|
+
<div className="flex min-w-0 items-end gap-2.5 text-sm">
|
|
18
|
+
<img src={logo} alt={title} className="size-6.5 mb-1.25 shrink-0" />
|
|
19
|
+
<div className="flex min-w-0 flex-col gap-0.5 leading-none">
|
|
20
|
+
<span className="truncate font-semibold">{title}</span>
|
|
21
|
+
<span className="truncate text-xs opacity-80">{description}</span>
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
24
24
|
)
|
|
@@ -3,7 +3,7 @@ export function SoundCloud(props: { url: string }) {
|
|
|
3
3
|
const src = `https://w.soundcloud.com/player/?url=${encodeURIComponent(props.url)}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false&show_teaser=false`
|
|
4
4
|
|
|
5
5
|
return (
|
|
6
|
-
<div className="not-prose my-4 overflow-hidden rounded-
|
|
6
|
+
<div className="not-prose my-4 overflow-hidden rounded-card border">
|
|
7
7
|
<iframe
|
|
8
8
|
className="w-full"
|
|
9
9
|
height={166}
|
|
@@ -39,7 +39,7 @@ export function TagIndex(props: { sectionPrefix: string; tag: string }) {
|
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<TocContext.Provider value={tocItems}>
|
|
42
|
-
<MobileToc items={tocItems}>
|
|
42
|
+
<MobileToc items={tocItems} title={title}>
|
|
43
43
|
<PageToolbar content={content} />
|
|
44
44
|
</MobileToc>
|
|
45
45
|
<div className="flex flex-1 gap-10 p-4 pt-8 md:p-10">
|
|
@@ -1,39 +1,65 @@
|
|
|
1
|
-
import { Moon, Sun } from "lucide-react"
|
|
1
|
+
import { Moon, Sun, SunMoon } from "lucide-react"
|
|
2
2
|
import { useHotkey } from "@tanstack/react-hotkeys"
|
|
3
|
+
import { useState } from "react"
|
|
3
4
|
import { Button } from "../elements/button.tsx"
|
|
4
|
-
import {
|
|
5
|
+
import type { ThemePref } from "../helpers/theme.ts"
|
|
6
|
+
import { getCurrentTheme, getThemePref, setTheme } from "../helpers/theme.ts"
|
|
7
|
+
import { MenuSheet } from "./MenuSheet.tsx"
|
|
5
8
|
|
|
9
|
+
/** Header menu choosing light, dark or system theme; "T" flips light and dark */
|
|
6
10
|
export function Theme() {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
11
|
+
// Read as the menu opens: on the server there is no stored preference to read.
|
|
12
|
+
const [current, setCurrent] = useState<ThemePref>("system")
|
|
13
|
+
|
|
14
|
+
useHotkey("T", () => setTheme(getCurrentTheme() === "light" ? "dark" : "light"))
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
const choose = (pref: ThemePref) => {
|
|
17
|
+
setCurrent(pref)
|
|
18
|
+
setTheme(pref)
|
|
19
|
+
}
|
|
14
20
|
|
|
15
21
|
return (
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
<MenuSheet
|
|
23
|
+
title="Change theme"
|
|
24
|
+
onOpenChange={open => open && setCurrent(getThemePref())}
|
|
25
|
+
items={[
|
|
26
|
+
{
|
|
27
|
+
id: "light",
|
|
28
|
+
label: "Light",
|
|
29
|
+
icon: <Sun className="size-4 shrink-0" />,
|
|
30
|
+
active: current === "light",
|
|
31
|
+
onSelect: () => choose("light"),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "dark",
|
|
35
|
+
label: "Dark",
|
|
36
|
+
icon: <Moon className="size-4 shrink-0" />,
|
|
37
|
+
active: current === "dark",
|
|
38
|
+
onSelect: () => choose("dark"),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "system",
|
|
42
|
+
label: "System",
|
|
43
|
+
icon: <SunMoon className="size-4 shrink-0" />,
|
|
44
|
+
active: current === "system",
|
|
45
|
+
onSelect: () => choose("system"),
|
|
46
|
+
},
|
|
47
|
+
]}
|
|
48
|
+
trigger={
|
|
49
|
+
<Button
|
|
50
|
+
variant="ghost"
|
|
51
|
+
size="icon-sm"
|
|
52
|
+
title="Change theme"
|
|
53
|
+
aria-label="Change theme"
|
|
54
|
+
aria-keyshortcuts="T"
|
|
55
|
+
className="cursor-pointer text-muted-foreground"
|
|
56
|
+
/>
|
|
57
|
+
}
|
|
22
58
|
>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</kbd>
|
|
29
|
-
</div>
|
|
30
|
-
<div className="flex-1 gap-2 items-center hidden dark:flex">
|
|
31
|
-
<Moon className="size-4" />
|
|
32
|
-
<span className="flex-1 text-left">Dark Theme</span>
|
|
33
|
-
<kbd className="rounded border border-border bg-muted px-1.5 py-0.5 text-[10px] font-mono">
|
|
34
|
-
T
|
|
35
|
-
</kbd>
|
|
36
|
-
</div>
|
|
37
|
-
</Button>
|
|
59
|
+
{/* Swapped on the `.dark` class rather than on state, so the server render
|
|
60
|
+
matches: no hydration mismatch, no wrong-icon flash. */}
|
|
61
|
+
<Sun className="size-4 dark:hidden" />
|
|
62
|
+
<Moon className="hidden size-4 dark:block" />
|
|
63
|
+
</MenuSheet>
|
|
38
64
|
)
|
|
39
65
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useEffect, useRef, useState, type ReactNode } from "react"
|
|
1
|
+
import { ChevronUp } from "lucide-react"
|
|
2
|
+
import { useEffect, useRef, useState, type MouseEvent, type ReactNode } from "react"
|
|
3
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../elements/tabs.tsx"
|
|
3
4
|
import type { TocItem } from "../models/toc.ts"
|
|
5
|
+
import { ChoiceList, type Choice } from "./ChoiceList.tsx"
|
|
6
|
+
import { SheetPanel } from "./SheetPanel.tsx"
|
|
4
7
|
|
|
5
8
|
export function Toc(props: { items: TocItem[]; children?: ReactNode }) {
|
|
6
9
|
const activeId = useActiveHeading(props.items)
|
|
@@ -10,7 +13,12 @@ export function Toc(props: { items: TocItem[]; children?: ReactNode }) {
|
|
|
10
13
|
return (
|
|
11
14
|
<aside className="hidden xl:block w-56 shrink-0">
|
|
12
15
|
<div className="sticky top-20 max-h-[calc(100vh-5rem)] overflow-auto [scrollbar-width:none]">
|
|
13
|
-
{props.children
|
|
16
|
+
{props.children && (
|
|
17
|
+
<div className="mb-6">
|
|
18
|
+
<p className="text-sm font-medium mb-3">Actions</p>
|
|
19
|
+
{props.children}
|
|
20
|
+
</div>
|
|
21
|
+
)}
|
|
14
22
|
{props.items.length > 0 && (
|
|
15
23
|
<nav>
|
|
16
24
|
<p className="text-sm font-medium mb-3">Contents</p>
|
|
@@ -34,74 +42,87 @@ export function Toc(props: { items: TocItem[]; children?: ReactNode }) {
|
|
|
34
42
|
)
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
const SHEET_ACTIONS =
|
|
46
|
+
"[&_[data-slot=page-toolbar]]:-mx-2 [&_[data-slot=page-toolbar]]:gap-0.5 [&_[data-slot=page-action]]:h-10 [&_[data-slot=page-action]]:gap-2.5 [&_[data-slot=page-action]]:rounded-control [&_[data-slot=page-action]]:px-2 [&_[data-slot=page-action]]:text-base [&_[data-slot=page-action]]:text-foreground [&_[data-slot=page-action]:hover]:bg-muted [&_[data-slot=page-action]_svg]:size-[18px]"
|
|
47
|
+
|
|
48
|
+
/** Mobile-only sticky secondary bar showing the active heading; it opens a
|
|
49
|
+
* bottom sheet with the Contents and page Actions as two tabs. */
|
|
50
|
+
export function MobileToc(props: {
|
|
51
|
+
items: TocItem[]
|
|
52
|
+
title?: string
|
|
53
|
+
children?: ReactNode
|
|
54
|
+
}) {
|
|
40
55
|
const activeId = useActiveHeading(props.items)
|
|
41
56
|
const [open, setOpen] = useState(false)
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (!open) return
|
|
46
|
-
const onPointerDown = (event: MouseEvent) => {
|
|
47
|
-
if (!wrapperRef.current?.contains(event.target as Node)) setOpen(false)
|
|
48
|
-
}
|
|
49
|
-
const onKey = (event: KeyboardEvent) => {
|
|
50
|
-
if (event.key === "Escape") setOpen(false)
|
|
51
|
-
}
|
|
52
|
-
document.addEventListener("mousedown", onPointerDown)
|
|
53
|
-
document.addEventListener("keydown", onKey)
|
|
54
|
-
return () => {
|
|
55
|
-
document.removeEventListener("mousedown", onPointerDown)
|
|
56
|
-
document.removeEventListener("keydown", onKey)
|
|
57
|
-
}
|
|
58
|
-
}, [open])
|
|
57
|
+
const [tab, setTab] = useState(props.items.length > 0 ? "contents" : "actions")
|
|
58
|
+
const pendingUrl = useRef<string | null>(null)
|
|
59
59
|
|
|
60
60
|
if (props.items.length === 0 && !props.children) return null
|
|
61
61
|
|
|
62
62
|
const active = props.items.find(item => item.url.slice(1) === activeId)
|
|
63
63
|
const title = active?.title ?? props.items[0]?.title ?? "Contents"
|
|
64
|
+
const choices: Choice[] = props.items.map(item => ({
|
|
65
|
+
id: item.url.slice(1),
|
|
66
|
+
label: item.title,
|
|
67
|
+
href: item.url,
|
|
68
|
+
depth: Math.max(0, item.depth - 2),
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
// The sheet locks page scroll until it has closed, which swallows a native hash
|
|
72
|
+
// jump made from inside it; navigate once the close has finished.
|
|
73
|
+
const handleSelect = (choice: Choice, event: MouseEvent<HTMLElement>) => {
|
|
74
|
+
event.preventDefault()
|
|
75
|
+
pendingUrl.current = choice.href ?? null
|
|
76
|
+
setOpen(false)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const handleOpenChangeComplete = (isOpen: boolean) => {
|
|
80
|
+
const url = pendingUrl.current
|
|
81
|
+
if (isOpen || !url) return
|
|
82
|
+
pendingUrl.current = null
|
|
83
|
+
history.pushState(null, "", url)
|
|
84
|
+
document.getElementById(url.slice(1))?.scrollIntoView()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const contents = (
|
|
88
|
+
<ChoiceList choices={choices} selectedId={activeId} onSelect={handleSelect} />
|
|
89
|
+
)
|
|
64
90
|
|
|
65
91
|
return (
|
|
66
|
-
<div
|
|
92
|
+
<div className="xl:hidden sticky top-16 z-20 border-b bg-background">
|
|
67
93
|
<button
|
|
68
94
|
type="button"
|
|
69
|
-
onClick={() => setOpen(
|
|
70
|
-
aria-
|
|
95
|
+
onClick={() => setOpen(true)}
|
|
96
|
+
aria-haspopup="dialog"
|
|
71
97
|
className="flex w-full items-center gap-3 px-4 py-3 text-sm text-muted-foreground"
|
|
72
98
|
>
|
|
73
99
|
<ScrollProgressRing />
|
|
74
100
|
<span className="truncate flex-1 text-left">{title}</span>
|
|
75
|
-
<
|
|
76
|
-
className={`size-4 shrink-0 transition-transform ${open ? "rotate-180" : ""}`}
|
|
77
|
-
/>
|
|
101
|
+
<ChevronUp className="size-4 shrink-0" />
|
|
78
102
|
</button>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
)}
|
|
103
|
+
<SheetPanel
|
|
104
|
+
open={open}
|
|
105
|
+
onOpenChange={setOpen}
|
|
106
|
+
onOpenChangeComplete={handleOpenChangeComplete}
|
|
107
|
+
title={props.title ?? "On this page"}
|
|
108
|
+
>
|
|
109
|
+
{props.children && props.items.length > 0 ? (
|
|
110
|
+
<Tabs value={tab} onValueChange={value => setTab(String(value))}>
|
|
111
|
+
<TabsList className="mb-2 w-full">
|
|
112
|
+
<TabsTrigger value="contents">Contents</TabsTrigger>
|
|
113
|
+
<TabsTrigger value="actions">Actions</TabsTrigger>
|
|
114
|
+
</TabsList>
|
|
115
|
+
<TabsContent value="contents">{contents}</TabsContent>
|
|
116
|
+
<TabsContent value="actions" className={SHEET_ACTIONS}>
|
|
117
|
+
{props.children}
|
|
118
|
+
</TabsContent>
|
|
119
|
+
</Tabs>
|
|
120
|
+
) : props.children ? (
|
|
121
|
+
<div className={SHEET_ACTIONS}>{props.children}</div>
|
|
122
|
+
) : (
|
|
123
|
+
contents
|
|
124
|
+
)}
|
|
125
|
+
</SheetPanel>
|
|
105
126
|
</div>
|
|
106
127
|
)
|
|
107
128
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Renders a responsive YouTube iframe embed */
|
|
2
2
|
export function YouTube(props: { id: string }) {
|
|
3
3
|
return (
|
|
4
|
-
<div className="not-prose my-4 overflow-hidden rounded-
|
|
4
|
+
<div className="not-prose my-4 overflow-hidden rounded-card border">
|
|
5
5
|
<div className="aspect-video">
|
|
6
6
|
<iframe
|
|
7
7
|
className="h-full w-full"
|
|
@@ -183,6 +183,10 @@ const articles = defineCollection({
|
|
|
183
183
|
light: config.codeThemeLight,
|
|
184
184
|
dark: config.codeThemeDark,
|
|
185
185
|
},
|
|
186
|
+
// A fence with no or an unknown lang is otherwise skipped by Shiki and
|
|
187
|
+
// renders without line padding, theme colours or transformers.
|
|
188
|
+
defaultLanguage: "text",
|
|
189
|
+
fallbackLanguage: "text",
|
|
186
190
|
// Darken catppuccin-latte's green (the colour used for strings and
|
|
187
191
|
// some comments) — the theme default #40A02B lands at 3.2:1 on the
|
|
188
192
|
// code-block background, below the WCAG AA 4.5:1 threshold.
|
|
@@ -46,7 +46,7 @@ function AlertDialogContent({
|
|
|
46
46
|
data-slot="alert-dialog-content"
|
|
47
47
|
data-size={size}
|
|
48
48
|
className={cn(
|
|
49
|
-
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-
|
|
49
|
+
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-panel bg-popover p-6 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
50
50
|
className,
|
|
51
51
|
)}
|
|
52
52
|
{...props}
|
|
@@ -2,7 +2,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|
|
2
2
|
import { cn } from "cn"
|
|
3
3
|
|
|
4
4
|
const alertVariants = cva(
|
|
5
|
-
"grid gap-0.5 rounded-
|
|
5
|
+
"grid gap-0.5 rounded-card border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
|
|
6
6
|
{
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
@@ -12,6 +12,10 @@ const badgeVariants = cva(
|
|
|
12
12
|
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
13
13
|
destructive:
|
|
14
14
|
"bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
|
15
|
+
positive:
|
|
16
|
+
"bg-positive/10 [a]:hover:bg-positive/20 focus-visible:ring-positive/20 dark:focus-visible:ring-positive/40 text-positive-tint-foreground dark:bg-positive/20",
|
|
17
|
+
warning:
|
|
18
|
+
"bg-warning/10 [a]:hover:bg-warning/20 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40 text-warning-tint-foreground dark:bg-warning/20",
|
|
15
19
|
outline:
|
|
16
20
|
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
17
21
|
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
@@ -6,14 +6,14 @@ import { cn } from "cn"
|
|
|
6
6
|
import { Separator } from "./separator.tsx"
|
|
7
7
|
|
|
8
8
|
const buttonGroupVariants = cva(
|
|
9
|
-
"flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-
|
|
9
|
+
"flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-control [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
orientation: {
|
|
13
13
|
horizontal:
|
|
14
|
-
"*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-
|
|
14
|
+
"*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-control! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
|
|
15
15
|
vertical:
|
|
16
|
-
"flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-
|
|
16
|
+
"flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-control! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
defaultVariants: {
|
|
@@ -48,7 +48,7 @@ function ButtonGroupText({
|
|
|
48
48
|
props: mergeProps<"div">(
|
|
49
49
|
{
|
|
50
50
|
className: cn(
|
|
51
|
-
"flex items-center gap-2 rounded-
|
|
51
|
+
"flex items-center gap-2 rounded-control border bg-muted px-2.5 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
52
52
|
className,
|
|
53
53
|
),
|
|
54
54
|
},
|