livemark 0.25.0 → 0.27.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 +12 -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/article.js +4 -11
- package/build/content-collections.js +6 -4
- 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/agent-skills.d.ts +22 -0
- package/build/helpers/agent-skills.js +46 -0
- package/build/helpers/agent-skills.unit.d.ts +1 -0
- package/build/helpers/agent-skills.unit.js +49 -0
- package/build/helpers/article-search.d.ts +19 -0
- package/build/helpers/article-search.js +134 -0
- package/build/helpers/article.d.ts +13 -0
- package/build/helpers/article.js +17 -1
- 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 +58 -0
- package/build/helpers/head.js +80 -0
- package/build/helpers/head.unit.d.ts +1 -0
- package/build/helpers/head.unit.js +110 -0
- package/build/helpers/llms.d.ts +33 -0
- package/build/helpers/llms.js +72 -0
- package/build/helpers/llms.unit.d.ts +1 -0
- package/build/helpers/llms.unit.js +98 -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 +52 -6
- package/build/models/config.js +17 -1
- package/build/models/section.d.ts +4 -2
- package/build/models/section.js +1 -1
- package/build/plugins/vite-agents.d.ts +17 -0
- package/build/plugins/vite-agents.js +109 -0
- package/build/plugins/vite-agents.unit.d.ts +1 -0
- package/build/plugins/vite-agents.unit.js +20 -0
- package/build/plugins/vite-livemark.d.ts +4 -5
- package/build/plugins/vite-livemark.js +56 -90
- package/build/plugins/vite-robots.d.ts +4 -2
- package/build/plugins/vite-robots.js +15 -2
- package/build/plugins/vite-robots.unit.js +5 -1
- 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/build/vite.config.js +3 -1
- package/package.json +69 -66
- package/{.agents/skills → skills}/livemark-authoring/SKILL.md +2 -2
- package/{.agents/skills → skills}/livemark-configuration/SKILL.md +16 -5
- package/skills/livemark-customization/SKILL.md +146 -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/article.ts +8 -13
- package/source/content-collections.ts +5 -4
- 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/agent-skills.ts +59 -0
- package/source/helpers/agent-skills.unit.ts +56 -0
- package/source/helpers/article-search.tsx +192 -0
- package/source/helpers/article.ts +17 -0
- package/source/helpers/dependency-dir.ts +20 -0
- package/source/helpers/dependency-dir.unit.ts +39 -0
- package/source/helpers/head.ts +105 -0
- package/source/helpers/head.unit.ts +118 -0
- package/source/helpers/llms.ts +109 -0
- package/source/helpers/llms.unit.ts +115 -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 +24 -0
- package/source/models/section.ts +3 -0
- package/source/plugins/vite-agents.ts +135 -0
- package/source/plugins/vite-agents.unit.ts +26 -0
- package/source/plugins/vite-livemark.ts +54 -95
- package/source/plugins/vite-robots.ts +18 -3
- package/source/plugins/vite-robots.unit.ts +7 -0
- 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/source/vite.config.ts +2 -0
- package/.agents/skills/change-review/SKILL.md +0 -47
- package/.agents/skills/livemark-customization/SKILL.md +0 -118
|
@@ -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
|
+
}
|
|
@@ -28,7 +28,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
|
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
30
|
<figure
|
|
31
|
-
className="shiki not-prose my-4 relative rounded-
|
|
31
|
+
className="shiki not-prose my-4 relative rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar"
|
|
32
32
|
style={{ ...style, backgroundColor: undefined }}
|
|
33
33
|
>
|
|
34
34
|
{title ? (
|
|
@@ -44,7 +44,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
|
|
|
44
44
|
<TooltipTrigger
|
|
45
45
|
onClick={handleWrapToggle}
|
|
46
46
|
aria-label={wrap ? "Disable wrap" : "Enable wrap"}
|
|
47
|
-
className={`-me-2 p-1.5 rounded-
|
|
47
|
+
className={`-me-2 p-1.5 rounded-control hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
|
|
48
48
|
>
|
|
49
49
|
<WrapText className="size-3.5" />
|
|
50
50
|
</TooltipTrigger>
|
|
@@ -54,7 +54,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
|
|
|
54
54
|
<TooltipTrigger
|
|
55
55
|
onClick={handleCopy}
|
|
56
56
|
aria-label={copied ? "Copied" : "Copy code"}
|
|
57
|
-
className="-me-2 p-1.5 rounded-
|
|
57
|
+
className="-me-2 p-1.5 rounded-control hover:text-foreground transition-colors"
|
|
58
58
|
>
|
|
59
59
|
{copied ? (
|
|
60
60
|
<Check className="size-3.5" />
|
|
@@ -66,12 +66,12 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
|
|
|
66
66
|
</Tooltip>
|
|
67
67
|
</div>
|
|
68
68
|
) : (
|
|
69
|
-
<div className="empty:hidden absolute top-2 right-2 z-10 backdrop-blur-lg rounded-
|
|
69
|
+
<div className="empty:hidden absolute top-2 right-2 z-10 backdrop-blur-lg rounded-control text-muted-foreground">
|
|
70
70
|
<Tooltip>
|
|
71
71
|
<TooltipTrigger
|
|
72
72
|
onClick={handleWrapToggle}
|
|
73
73
|
aria-label={wrap ? "Disable wrap" : "Enable wrap"}
|
|
74
|
-
className={`p-1.5 rounded-
|
|
74
|
+
className={`p-1.5 rounded-control hover:text-foreground transition-colors ${wrap ? "text-foreground" : ""}`}
|
|
75
75
|
>
|
|
76
76
|
<WrapText className="size-3.5" />
|
|
77
77
|
</TooltipTrigger>
|
|
@@ -81,7 +81,7 @@ export function CodeBlock(props: React.ComponentProps<"pre">) {
|
|
|
81
81
|
<TooltipTrigger
|
|
82
82
|
onClick={handleCopy}
|
|
83
83
|
aria-label={copied ? "Copied" : "Copy code"}
|
|
84
|
-
className="p-1.5 rounded-
|
|
84
|
+
className="p-1.5 rounded-control hover:text-foreground transition-colors"
|
|
85
85
|
>
|
|
86
86
|
{copied ? (
|
|
87
87
|
<Check className="size-3.5" />
|
|
@@ -47,7 +47,7 @@ export function CodeTabs(props: {
|
|
|
47
47
|
}, [props.sync, names])
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
<div className="not-prose my-4 rounded-
|
|
50
|
+
<div className="not-prose my-4 rounded-card border shadow-sm overflow-hidden text-sm bg-sidebar dark:bg-sidebar">
|
|
51
51
|
<div className="flex border-b">
|
|
52
52
|
{names.map((name, i) => (
|
|
53
53
|
<button
|
|
@@ -45,7 +45,7 @@ export function ContentTabs(props: { sync?: string; children: React.ReactNode })
|
|
|
45
45
|
}, [props.sync, titles])
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<div className="not-prose my-4 rounded-
|
|
48
|
+
<div className="not-prose my-4 rounded-card border bg-card">
|
|
49
49
|
<div className="flex border-b">
|
|
50
50
|
{titles.map((title, i) => (
|
|
51
51
|
<button
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useMatch,
|
|
7
7
|
useRouter,
|
|
8
8
|
} from "@tanstack/react-router"
|
|
9
|
+
import { buttonVariants } from "../elements/button.tsx"
|
|
9
10
|
|
|
10
11
|
export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
|
11
12
|
const router = useRouter()
|
|
@@ -25,21 +26,18 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
|
|
|
25
26
|
onClick={() => {
|
|
26
27
|
router.invalidate()
|
|
27
28
|
}}
|
|
28
|
-
className=
|
|
29
|
+
className={buttonVariants({ size: "lg" })}
|
|
29
30
|
>
|
|
30
31
|
Try Again
|
|
31
32
|
</button>
|
|
32
33
|
{isRoot ? (
|
|
33
|
-
<Link
|
|
34
|
-
to="/"
|
|
35
|
-
className="border border-border px-4 py-2 text-sm font-medium hover:bg-card transition-colors"
|
|
36
|
-
>
|
|
34
|
+
<Link to="/" className={buttonVariants({ variant: "outline", size: "lg" })}>
|
|
37
35
|
Home
|
|
38
36
|
</Link>
|
|
39
37
|
) : (
|
|
40
38
|
<Link
|
|
41
39
|
to="/"
|
|
42
|
-
className=
|
|
40
|
+
className={buttonVariants({ variant: "outline", size: "lg" })}
|
|
43
41
|
onClick={(e: any) => {
|
|
44
42
|
e.preventDefault()
|
|
45
43
|
window.history.back()
|
|
@@ -14,7 +14,7 @@ export function Details(props: { summary: string; children: React.ReactNode }) {
|
|
|
14
14
|
<Collapsible
|
|
15
15
|
open={open}
|
|
16
16
|
onOpenChange={setOpen}
|
|
17
|
-
className="not-prose my-4 rounded-
|
|
17
|
+
className="not-prose my-4 rounded-card border border-border bg-card px-4 py-3"
|
|
18
18
|
>
|
|
19
19
|
<CollapsibleTrigger className="flex w-full cursor-pointer items-center gap-2 text-sm font-semibold select-none">
|
|
20
20
|
<ChevronRight
|
|
@@ -11,7 +11,7 @@ export function FileTree(props: { tree: string }) {
|
|
|
11
11
|
const items: TreeItem[] = JSON.parse(props.tree)
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
|
-
<div className="not-prose my-4 rounded-
|
|
14
|
+
<div className="not-prose my-4 rounded-card border border-border bg-card p-4 font-mono text-sm">
|
|
15
15
|
<TreeItems items={items} depth={0} />
|
|
16
16
|
</div>
|
|
17
17
|
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { config } from "livemark:virtual"
|
|
2
|
+
import { useLocation, useMatch } from "@tanstack/react-router"
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
import { cn } from "cn"
|
|
5
|
+
import { currentSection } from "../content/article.ts"
|
|
6
|
+
import { activeSiteLink } from "../helpers/section.ts"
|
|
7
|
+
import { HeaderMenu } from "./HeaderMenu.tsx"
|
|
8
|
+
import { HeaderNav } from "./HeaderNav.tsx"
|
|
9
|
+
import { Search } from "./Search.tsx"
|
|
10
|
+
import { SiteLink } from "./SiteLink.tsx"
|
|
11
|
+
import { SiteTitle } from "./SiteTitle.tsx"
|
|
12
|
+
import { Theme } from "./Theme.tsx"
|
|
13
|
+
|
|
14
|
+
/** Sticky site header: title, header sections and links, search and theme. `toggle`
|
|
15
|
+
* sits before the title from `lg`; `menu` opens the phone navigation */
|
|
16
|
+
export function Header(props: { toggle?: React.ReactNode; menu?: React.ReactNode }) {
|
|
17
|
+
const pathname = useLocation({ select: l => l.pathname })
|
|
18
|
+
const articleRoute = useMatch({ from: "/$", shouldThrow: false })
|
|
19
|
+
const activeSection = articleRoute
|
|
20
|
+
? currentSection(`/${pathname.replace(/^\/|\/$/g, "")}/`)
|
|
21
|
+
: undefined
|
|
22
|
+
const siteLinkTarget = activeSiteLink(pathname, config.sections ?? [])
|
|
23
|
+
const [compactSearch, setCompactSearch] = useState(false)
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<header className="sticky top-0 z-20 flex has-[[data-header-search]]:z-40 h-16 shrink-0 items-center border-b bg-background">
|
|
27
|
+
<div
|
|
28
|
+
className={cn(
|
|
29
|
+
"flex min-w-0 items-center self-stretch pl-4 lg:shrink-0",
|
|
30
|
+
props.toggle ? "pr-4" : "lg:w-[calc(var(--sidebar-width,16rem)+4rem)]",
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
{props.toggle && <div className="hidden lg:flex">{props.toggle}</div>}
|
|
34
|
+
<SiteLink
|
|
35
|
+
to={siteLinkTarget}
|
|
36
|
+
className={cn("flex min-w-0 items-center gap-2", props.toggle && "lg:hidden")}
|
|
37
|
+
>
|
|
38
|
+
<SiteTitle />
|
|
39
|
+
</SiteLink>
|
|
40
|
+
</div>
|
|
41
|
+
<HeaderNav
|
|
42
|
+
activePrefix={activeSection?.prefix}
|
|
43
|
+
onCompactChange={setCompactSearch}
|
|
44
|
+
/>
|
|
45
|
+
<div className="ml-auto flex shrink-0 items-center gap-1 pr-2 lg:pr-1">
|
|
46
|
+
<Search compact={compactSearch} />
|
|
47
|
+
<Theme />
|
|
48
|
+
<div className="lg:hidden">{props.menu ?? <HeaderMenu />}</div>
|
|
49
|
+
</div>
|
|
50
|
+
</header>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { config } from "livemark:virtual"
|
|
2
|
+
import { useNavigate } from "@tanstack/react-router"
|
|
3
|
+
import { Menu } from "lucide-react"
|
|
4
|
+
import { Button } from "../elements/button.tsx"
|
|
5
|
+
import { DynamicIcon } from "../helpers/dynamic-icon.tsx"
|
|
6
|
+
import { useNavItems } from "../helpers/nav-items.tsx"
|
|
7
|
+
import { prefixUrl } from "../helpers/prefix-url.ts"
|
|
8
|
+
import { MenuSheet } from "./MenuSheet.tsx"
|
|
9
|
+
|
|
10
|
+
/** Phone navigation for a `Header` without a sidebar: the header sections and links */
|
|
11
|
+
export function HeaderMenu() {
|
|
12
|
+
const items = useNavItems()
|
|
13
|
+
const navigate = useNavigate()
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<MenuSheet
|
|
17
|
+
title="Navigation"
|
|
18
|
+
items={items.map(item => ({
|
|
19
|
+
id: item.key,
|
|
20
|
+
label: item.suffix ? `${item.title} (${item.suffix})` : item.title,
|
|
21
|
+
icon: item.icon && <DynamicIcon name={item.icon} className="size-4 shrink-0" />,
|
|
22
|
+
active: item.active,
|
|
23
|
+
onSelect: () => {
|
|
24
|
+
if (item.href) window.location.assign(prefixUrl(item.href, config.base))
|
|
25
|
+
else if (item.target) navigate({ to: item.target })
|
|
26
|
+
},
|
|
27
|
+
}))}
|
|
28
|
+
trigger={
|
|
29
|
+
<Button
|
|
30
|
+
variant="ghost"
|
|
31
|
+
size="icon-sm"
|
|
32
|
+
title="Navigation"
|
|
33
|
+
aria-label="Navigation"
|
|
34
|
+
className="cursor-pointer"
|
|
35
|
+
/>
|
|
36
|
+
}
|
|
37
|
+
>
|
|
38
|
+
<Menu />
|
|
39
|
+
</MenuSheet>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { ChevronDown } from "lucide-react"
|
|
2
|
+
import { useLayoutEffect, useRef, useState } from "react"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
import { type NavItem, renderNavAnchor, useNavItems } from "../helpers/nav-items.tsx"
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuTrigger,
|
|
10
|
+
} from "../elements/dropdown-menu.tsx"
|
|
11
|
+
|
|
12
|
+
// The `lg:w-56` Search field minus its 36px icon-only form
|
|
13
|
+
const SEARCH_COMPACT_SAVING = 188
|
|
14
|
+
|
|
15
|
+
/** Header sections and links from `lg`: once they outgrow the bar, Search turns
|
|
16
|
+
* compact and the rest fold into a "More" menu */
|
|
17
|
+
export function HeaderNav(props: {
|
|
18
|
+
activePrefix?: string
|
|
19
|
+
onCompactChange: (compact: boolean) => void
|
|
20
|
+
}) {
|
|
21
|
+
const items = useNavItems(props.activePrefix)
|
|
22
|
+
const strip = useRef<HTMLElement>(null)
|
|
23
|
+
const measure = useRef<HTMLDivElement>(null)
|
|
24
|
+
const [visibleCount, setVisibleCount] = useState(items.length)
|
|
25
|
+
const onCompactChange = useRef(props.onCompactChange)
|
|
26
|
+
onCompactChange.current = props.onCompactChange
|
|
27
|
+
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
let compact = false
|
|
30
|
+
const fit = () => {
|
|
31
|
+
if (!strip.current || !measure.current) return
|
|
32
|
+
const style = getComputedStyle(strip.current)
|
|
33
|
+
const width =
|
|
34
|
+
strip.current.clientWidth -
|
|
35
|
+
parseFloat(style.paddingLeft) -
|
|
36
|
+
parseFloat(style.paddingRight)
|
|
37
|
+
if (width <= 0) return
|
|
38
|
+
// Measured as if Search were full, so toggling it never feeds back into the fit
|
|
39
|
+
const room = width - (compact ? SEARCH_COMPACT_SAVING : 0)
|
|
40
|
+
const gap = parseFloat(getComputedStyle(measure.current).columnGap) || 0
|
|
41
|
+
const widths = [...measure.current.children].map(
|
|
42
|
+
child => child.getBoundingClientRect().width,
|
|
43
|
+
)
|
|
44
|
+
const more = widths.pop() ?? 0
|
|
45
|
+
const total = widths.reduce((sum, w) => sum + w, 0) + gap * (widths.length - 1)
|
|
46
|
+
|
|
47
|
+
compact = total > room
|
|
48
|
+
let count = widths.length
|
|
49
|
+
if (total > room + SEARCH_COMPACT_SAVING) {
|
|
50
|
+
let used = more
|
|
51
|
+
count = 0
|
|
52
|
+
for (const w of widths) {
|
|
53
|
+
if (used + gap + w > room + SEARCH_COMPACT_SAVING) break
|
|
54
|
+
used += gap + w
|
|
55
|
+
count++
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
onCompactChange.current(compact)
|
|
59
|
+
setVisibleCount(count)
|
|
60
|
+
}
|
|
61
|
+
fit()
|
|
62
|
+
const observer = new ResizeObserver(fit)
|
|
63
|
+
if (strip.current) observer.observe(strip.current)
|
|
64
|
+
if (measure.current) observer.observe(measure.current)
|
|
65
|
+
return () => observer.disconnect()
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
const shown = items.slice(0, visibleCount)
|
|
69
|
+
const folded = items.slice(visibleCount)
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<nav
|
|
73
|
+
ref={strip}
|
|
74
|
+
className="relative hidden w-0 min-w-0 flex-1 items-center gap-6 self-stretch overflow-hidden px-6 text-sm lg:flex xl:gap-8"
|
|
75
|
+
>
|
|
76
|
+
{shown.map(item => (
|
|
77
|
+
<HeaderNavLink key={item.key} item={item} />
|
|
78
|
+
))}
|
|
79
|
+
{folded.length > 0 && <HeaderNavMore items={folded} />}
|
|
80
|
+
<div
|
|
81
|
+
ref={measure}
|
|
82
|
+
aria-hidden
|
|
83
|
+
inert
|
|
84
|
+
className="invisible absolute left-0 flex gap-6 whitespace-nowrap xl:gap-8"
|
|
85
|
+
>
|
|
86
|
+
{items.map(item => (
|
|
87
|
+
<HeaderNavLink key={item.key} item={item} />
|
|
88
|
+
))}
|
|
89
|
+
<HeaderNavMoreTrigger active={false} />
|
|
90
|
+
</div>
|
|
91
|
+
</nav>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function HeaderNavLink(props: { item: NavItem }) {
|
|
96
|
+
return renderNavAnchor(
|
|
97
|
+
props.item,
|
|
98
|
+
cn(
|
|
99
|
+
"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap text-foreground",
|
|
100
|
+
props.item.active ? ACTIVE : IDLE,
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function HeaderNavMore(props: { items: NavItem[] }) {
|
|
106
|
+
return (
|
|
107
|
+
<DropdownMenu>
|
|
108
|
+
<DropdownMenuTrigger
|
|
109
|
+
render={<HeaderNavMoreTrigger active={props.items.some(item => item.active)} />}
|
|
110
|
+
/>
|
|
111
|
+
<DropdownMenuContent align="end" className="w-auto">
|
|
112
|
+
{props.items.map(item => (
|
|
113
|
+
<DropdownMenuItem
|
|
114
|
+
key={item.key}
|
|
115
|
+
className={cn("cursor-pointer", item.active && "font-medium")}
|
|
116
|
+
render={renderNavAnchor(item)}
|
|
117
|
+
/>
|
|
118
|
+
))}
|
|
119
|
+
</DropdownMenuContent>
|
|
120
|
+
</DropdownMenu>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function HeaderNavMoreTrigger(
|
|
125
|
+
props: React.ComponentProps<"button"> & { active: boolean },
|
|
126
|
+
) {
|
|
127
|
+
const { active, className, ...rest } = props
|
|
128
|
+
return (
|
|
129
|
+
<button
|
|
130
|
+
type="button"
|
|
131
|
+
{...rest}
|
|
132
|
+
className={cn(
|
|
133
|
+
"inline-flex shrink-0 cursor-pointer items-center gap-1 whitespace-nowrap text-foreground",
|
|
134
|
+
active ? ACTIVE : IDLE,
|
|
135
|
+
className,
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
More
|
|
139
|
+
<ChevronDown className="size-3.5" />
|
|
140
|
+
</button>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const ACTIVE = "font-medium border-b-2 border-foreground pb-0.5 -mb-0.5"
|
|
145
|
+
const IDLE = "opacity-80 hover:opacity-100 transition-opacity"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useLayoutEffect, useRef, useState } from "react"
|
|
2
|
+
import { X } from "lucide-react"
|
|
3
|
+
import { SearchResultGroups, useArticleSearch } from "../helpers/article-search.tsx"
|
|
4
|
+
import { Command, CommandEmpty, CommandInput, CommandList } from "../elements/command.tsx"
|
|
5
|
+
|
|
6
|
+
/** Search field that takes over the header below `lg`, with results dropping under it */
|
|
7
|
+
export function HeaderSearch(props: { onClose: () => void }) {
|
|
8
|
+
const search = useArticleSearch()
|
|
9
|
+
const bar = useRef<HTMLDivElement>(null)
|
|
10
|
+
const [panelTop, setPanelTop] = useState<number>()
|
|
11
|
+
|
|
12
|
+
// Measured, not `top-16`: while the Banner is in view it pushes the header down.
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
const measure = () => setPanelTop(bar.current?.getBoundingClientRect().bottom)
|
|
15
|
+
measure()
|
|
16
|
+
window.addEventListener("scroll", measure, { passive: true })
|
|
17
|
+
window.addEventListener("resize", measure)
|
|
18
|
+
return () => {
|
|
19
|
+
window.removeEventListener("scroll", measure)
|
|
20
|
+
window.removeEventListener("resize", measure)
|
|
21
|
+
}
|
|
22
|
+
}, [])
|
|
23
|
+
|
|
24
|
+
// `data-header-search` lifts the header over the Toc bar and BackToTop (Layout).
|
|
25
|
+
return (
|
|
26
|
+
<Command
|
|
27
|
+
ref={bar}
|
|
28
|
+
data-header-search
|
|
29
|
+
shouldFilter={false}
|
|
30
|
+
loop
|
|
31
|
+
onKeyDown={event => {
|
|
32
|
+
if (event.key === "Escape") props.onClose()
|
|
33
|
+
}}
|
|
34
|
+
className="absolute inset-0 z-10 overflow-visible rounded-none! bg-background p-0 lg:hidden"
|
|
35
|
+
>
|
|
36
|
+
<div className="flex h-full items-center gap-1 pl-1 pr-2">
|
|
37
|
+
<div className="flex-1 [&_[data-slot=command-input-wrapper]]:p-0 [&_[data-slot=input-group]]:border-transparent! [&_[data-slot=input-group]]:bg-transparent! [&_[data-slot=input-group]]:ring-0!">
|
|
38
|
+
<CommandInput
|
|
39
|
+
autoFocus
|
|
40
|
+
placeholder="Search docs..."
|
|
41
|
+
value={search.query}
|
|
42
|
+
onValueChange={search.searchArticles}
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
onClick={props.onClose}
|
|
48
|
+
aria-label="Close search"
|
|
49
|
+
title="Close search"
|
|
50
|
+
className="flex size-9 shrink-0 items-center justify-center rounded-control text-muted-foreground hover:bg-muted hover:text-foreground transition-colors cursor-pointer"
|
|
51
|
+
>
|
|
52
|
+
<X className="size-5" />
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
<div
|
|
56
|
+
style={{ top: panelTop }}
|
|
57
|
+
className="fixed inset-x-0 bottom-0 flex flex-col border-t bg-background p-1"
|
|
58
|
+
>
|
|
59
|
+
<CommandList className="max-h-none flex-1 overscroll-contain">
|
|
60
|
+
<CommandEmpty>
|
|
61
|
+
{search.query.trim()
|
|
62
|
+
? "No results found."
|
|
63
|
+
: "Type to search across all contents."}
|
|
64
|
+
</CommandEmpty>
|
|
65
|
+
<SearchResultGroups
|
|
66
|
+
query={search.query}
|
|
67
|
+
grouped={search.grouped}
|
|
68
|
+
onSelect={path => {
|
|
69
|
+
props.onClose()
|
|
70
|
+
search.selectArticle(path)
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
</CommandList>
|
|
74
|
+
</div>
|
|
75
|
+
</Command>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Badge } from "../elements/badge.tsx"
|
|
|
3
3
|
/** Inline badge rendered from :badge text directives */
|
|
4
4
|
export function InlineBadge(props: {
|
|
5
5
|
label: string
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline"
|
|
6
|
+
variant?: "default" | "secondary" | "positive" | "warning" | "destructive" | "outline"
|
|
7
7
|
}) {
|
|
8
8
|
return <Badge variant={props.variant}>{props.label}</Badge>
|
|
9
9
|
}
|
|
@@ -9,7 +9,7 @@ export function InlineToc(props: { maxLevel?: string }) {
|
|
|
9
9
|
if (items.length === 0) return null
|
|
10
10
|
|
|
11
11
|
return (
|
|
12
|
-
<nav className="not-prose my-8 rounded-
|
|
12
|
+
<nav className="not-prose my-8 rounded-card border border-border bg-card p-6">
|
|
13
13
|
<p className="text-base font-semibold mb-4">Table of Contents</p>
|
|
14
14
|
<ul className="text-base space-y-2.5">
|
|
15
15
|
{items.map(item => (
|