@lupinum/ginko-docs 0.4.0-rc.8 → 0.4.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/CHANGELOG.md +78 -0
- package/LICENSE +1 -1
- package/README.md +206 -54
- package/app/app.config.ts +42 -55
- package/app/assets/docs.css +555 -0
- package/app/components/DocsBanner.vue +29 -0
- package/app/components/DocsExample.vue +43 -0
- package/app/components/DocsFooter.vue +19 -0
- package/app/components/DocsHeader.vue +112 -0
- package/app/components/DocsLanding.vue +55 -0
- package/app/components/DocsLanguage.vue +29 -0
- package/app/components/DocsNavTree.vue +38 -0
- package/app/components/DocsPage.vue +84 -0
- package/app/components/DocsPageActions.vue +47 -0
- package/app/components/DocsPost.vue +58 -0
- package/app/components/DocsSearch.vue +134 -0
- package/app/components/DocsShell.vue +20 -0
- package/app/components/DocsSidebar.vue +18 -0
- package/app/components/DocsTable.vue +11 -0
- package/app/components/DocsThemeToggle.vue +45 -0
- package/app/components/DocsToc.vue +73 -0
- package/app/composables/useDocs.ts +52 -0
- package/app/composables/useDocsBlog.ts +32 -0
- package/app/composables/useDocsCopy.ts +17 -0
- package/app/composables/useDocsPopover.ts +36 -0
- package/app/composables/useDocsSite.ts +10 -0
- package/app/docs-pages/blog.vue +47 -0
- package/app/docs-pages/changelog.vue +27 -0
- package/app/docs-pages/doc.vue +14 -0
- package/app/docs-pages/docs.vue +9 -0
- package/app/docs-pages/index.vue +12 -0
- package/app/docs-pages/post.vue +8 -0
- package/app/error.vue +23 -115
- package/app/layouts/default.vue +25 -7
- package/app/lib/blogTypes.ts +8 -0
- package/app/lib/docsFirst.ts +11 -0
- package/app/lib/docsInit.ts +18 -0
- package/app/lib/docsNav.ts +36 -0
- package/app/lib/docsPage.ts +44 -0
- package/app/lib/markdownComponents.ts +16 -0
- package/app/lib/previewToc.ts +18 -0
- package/dist/config.d.mts +232 -0
- package/dist/config.d.ts +232 -0
- package/dist/config.mjs +114 -0
- package/dist/pages.d.mts +5 -0
- package/dist/pages.d.ts +5 -0
- package/dist/pages.mjs +36 -0
- package/dist/shared/ginko-docs.BwbF7JBR.mjs +4 -0
- package/i18n/de.json +1 -0
- package/i18n/en.json +1 -0
- package/icons/README.md +2 -0
- package/icons/bluesky.svg +1 -0
- package/icons/claude.svg +1 -0
- package/icons/discord.svg +1 -0
- package/icons/github.svg +1 -0
- package/icons/linkedin.svg +1 -0
- package/icons/mastodon.svg +1 -0
- package/icons/openai.svg +1 -0
- package/icons/x.svg +1 -0
- package/icons/youtube.svg +1 -0
- package/nuxt.config.ts +34 -209
- package/package.json +43 -76
- package/public/_docs/fonts/LICENSE +188 -0
- package/public/_docs/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/public/_docs/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/public/_docs/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
- package/server/feeds/blog-locale.get.ts +9 -0
- package/server/feeds/blog.get.ts +4 -0
- package/server/lib/blogFeed.ts +35 -0
- package/server/lib/rss.ts +57 -0
- package/server/mcp/handler.ts +108 -0
- package/server/routes/__ginko/docs-mcp.ts +35 -0
- package/shared/paths.ts +4 -0
- package/app/app.vue +0 -98
- package/app/assets/css/prose.css +0 -3240
- package/app/assets/css/tailwind.css +0 -394
- package/app/assets/css/theme-palettes.css +0 -309
- package/app/assets/css/theme-presets.css +0 -141
- package/app/components/OgImage/GinkoDocs.satori.vue +0 -238
- package/app/components/content/DocumentPageShell.vue +0 -55
- package/app/components/content/Feedback.vue +0 -102
- package/app/components/content/ImageZoomDialog.vue +0 -84
- package/app/components/content/PageMarkdownCopy.vue +0 -104
- package/app/components/content/imageZoom.ts +0 -25
- package/app/components/mdc/MdcAccordion.vue +0 -67
- package/app/components/mdc/MdcAccordionItem.vue +0 -42
- package/app/components/mdc/MdcApi.vue +0 -159
- package/app/components/mdc/MdcAside.vue +0 -23
- package/app/components/mdc/MdcCallout.vue +0 -61
- package/app/components/mdc/MdcCard.vue +0 -175
- package/app/components/mdc/MdcCards.vue +0 -30
- package/app/components/mdc/MdcCenter.vue +0 -71
- package/app/components/mdc/MdcCodeGroup.vue +0 -197
- package/app/components/mdc/MdcCodeTree.vue +0 -234
- package/app/components/mdc/MdcCollapse.vue +0 -61
- package/app/components/mdc/MdcColumn.vue +0 -70
- package/app/components/mdc/MdcDropcap.vue +0 -84
- package/app/components/mdc/MdcError.vue +0 -23
- package/app/components/mdc/MdcExcerpt.vue +0 -26
- package/app/components/mdc/MdcFigure.vue +0 -85
- package/app/components/mdc/MdcFiles.vue +0 -120
- package/app/components/mdc/MdcIdea.vue +0 -17
- package/app/components/mdc/MdcInfo.vue +0 -17
- package/app/components/mdc/MdcInlineToc.vue +0 -51
- package/app/components/mdc/MdcKbd.vue +0 -10
- package/app/components/mdc/MdcLayout.vue +0 -57
- package/app/components/mdc/MdcNote.vue +0 -17
- package/app/components/mdc/MdcQuiz.vue +0 -241
- package/app/components/mdc/MdcQuizQuestion.vue +0 -221
- package/app/components/mdc/MdcReadMore.vue +0 -70
- package/app/components/mdc/MdcSteps.vue +0 -59
- package/app/components/mdc/MdcSuccess.vue +0 -23
- package/app/components/mdc/MdcTab.vue +0 -17
- package/app/components/mdc/MdcTabs.vue +0 -150
- package/app/components/mdc/MdcTimeline.vue +0 -17
- package/app/components/mdc/MdcTimelineItem.vue +0 -40
- package/app/components/mdc/MdcWarning.vue +0 -23
- package/app/components/mdc/api.utils.ts +0 -132
- package/app/components/mdc/code-tree.utils.ts +0 -22
- package/app/components/mdc/icons.ts +0 -10
- package/app/components/mdc/quiz.utils.ts +0 -38
- package/app/components/prose/ProseA.vue +0 -27
- package/app/components/prose/ProseBlockquote.vue +0 -30
- package/app/components/prose/ProseCode.vue +0 -14
- package/app/components/prose/ProseEm.vue +0 -14
- package/app/components/prose/ProseH1.vue +0 -18
- package/app/components/prose/ProseH2.vue +0 -29
- package/app/components/prose/ProseH3.vue +0 -29
- package/app/components/prose/ProseH4.vue +0 -29
- package/app/components/prose/ProseH5.vue +0 -15
- package/app/components/prose/ProseH6.vue +0 -15
- package/app/components/prose/ProseHr.vue +0 -12
- package/app/components/prose/ProseImg.vue +0 -55
- package/app/components/prose/ProseLi.vue +0 -14
- package/app/components/prose/ProseOl.vue +0 -14
- package/app/components/prose/ProseP.vue +0 -14
- package/app/components/prose/ProsePre.vue +0 -180
- package/app/components/prose/ProseStrong.vue +0 -14
- package/app/components/prose/ProseTable.vue +0 -18
- package/app/components/prose/ProseTbody.vue +0 -14
- package/app/components/prose/ProseTd.vue +0 -14
- package/app/components/prose/ProseTh.vue +0 -14
- package/app/components/prose/ProseThead.vue +0 -14
- package/app/components/prose/ProseTr.vue +0 -14
- package/app/components/prose/ProseUl.vue +0 -14
- package/app/components/site/ModeToggle.vue +0 -81
- package/app/components/site/SiteBanner.vue +0 -97
- package/app/components/site/SiteFooter.vue +0 -90
- package/app/components/site/SiteHeader.vue +0 -371
- package/app/components/site/SiteHeroCode.vue +0 -173
- package/app/components/site/SiteInteractionLayer.vue +0 -42
- package/app/components/site/SiteLocaleSwitcher.vue +0 -190
- package/app/components/site/SiteLogoMark.vue +0 -12
- package/app/components/site/SiteSkipLink.vue +0 -14
- package/app/components/site/SiteSocialLinks.vue +0 -33
- package/app/components/site/header-utils.ts +0 -3
- package/app/components/site/mode-toggle.utils.ts +0 -18
- package/app/components/ui/badge/Badge.vue +0 -28
- package/app/components/ui/badge/index.ts +0 -24
- package/app/components/ui/button/Button.vue +0 -31
- package/app/components/ui/button/index.ts +0 -35
- package/app/components/ui/collapsible/Collapsible.vue +0 -15
- package/app/components/ui/collapsible/CollapsibleContent.vue +0 -25
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +0 -12
- package/app/components/ui/collapsible/index.ts +0 -3
- package/app/components/ui/dropdown-menu/DropdownMenu.vue +0 -15
- package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +0 -50
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +0 -41
- package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +0 -15
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +0 -37
- package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +0 -23
- package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +0 -14
- package/app/components/ui/dropdown-menu/index.ts +0 -7
- package/app/components/ui/kbd/Kbd.vue +0 -23
- package/app/components/ui/kbd/index.ts +0 -21
- package/app/components/ui/scroll-area/ScrollArea.vue +0 -39
- package/app/components/ui/scroll-area/ScrollBar.vue +0 -33
- package/app/components/ui/scroll-area/index.ts +0 -2
- package/app/components/ui/separator/Separator.vue +0 -27
- package/app/components/ui/separator/index.ts +0 -1
- package/app/components/ui/sheet/Sheet.vue +0 -15
- package/app/components/ui/sheet/SheetContent.vue +0 -59
- package/app/components/ui/sheet/SheetDescription.vue +0 -21
- package/app/components/ui/sheet/SheetOverlay.vue +0 -26
- package/app/components/ui/sheet/SheetTitle.vue +0 -21
- package/app/components/ui/sheet/SheetTrigger.vue +0 -12
- package/app/components/ui/sheet/index.ts +0 -5
- package/app/components/ui/tabs/Tabs.vue +0 -24
- package/app/components/ui/tabs/TabsList.vue +0 -26
- package/app/components/ui/tabs/TabsTrigger.vue +0 -28
- package/app/components/ui/tabs/index.ts +0 -3
- package/app/composables/site-navigation.utils.ts +0 -133
- package/app/composables/useCanonicalUrl.ts +0 -9
- package/app/composables/useContentRouteAlternates.ts +0 -35
- package/app/composables/useGinkoAnalytics.ts +0 -52
- package/app/composables/useGinkoDocsConfig.ts +0 -6
- package/app/composables/useGinkoOgImage.ts +0 -20
- package/app/composables/useLocalizedPath.ts +0 -12
- package/app/composables/useLocalizedRouteSwitch.ts +0 -31
- package/app/composables/useMetaKey.ts +0 -18
- package/app/composables/usePageMarkdownActions.ts +0 -79
- package/app/composables/useProseAppearance.ts +0 -24
- package/app/composables/useSchemaJsonLd.ts +0 -25
- package/app/composables/useSiteNavigation.ts +0 -64
- package/app/config/site.utils.ts +0 -36
- package/app/features/docs/components/DocsBreadcrumb.vue +0 -37
- package/app/features/docs/components/DocsContributeLinks.vue +0 -75
- package/app/features/docs/components/DocsMobileToc.vue +0 -108
- package/app/features/docs/components/DocsPageContent.vue +0 -234
- package/app/features/docs/components/DocsPageNav.vue +0 -57
- package/app/features/docs/components/DocsSidebar.vue +0 -145
- package/app/features/docs/components/DocsSidebarDropdown.vue +0 -70
- package/app/features/docs/components/DocsSidebarItem.vue +0 -122
- package/app/features/docs/components/DocsSidebarList.vue +0 -35
- package/app/features/docs/components/DocsSidebarRow.vue +0 -27
- package/app/features/docs/components/DocsSidebarTabs.vue +0 -59
- package/app/features/docs/components/DocsToc.vue +0 -106
- package/app/features/docs/composables/useDocsNavigation.ts +0 -29
- package/app/features/docs/composables/useDocsNavigationData.ts +0 -18
- package/app/features/docs/composables/useRevealActive.ts +0 -58
- package/app/features/docs/composables/useScrollspy.ts +0 -135
- package/app/features/docs/docs-navigation.ts +0 -157
- package/app/features/docs/toc-context.ts +0 -6
- package/app/features/search/command-center.ts +0 -110
- package/app/features/search/components/SiteCommandCenter.vue +0 -359
- package/app/features/search/components/SiteSearchPageHighlight.vue +0 -126
- package/app/features/search/useCommandCenter.ts +0 -292
- package/app/layouts/blog.vue +0 -15
- package/app/layouts/docs.vue +0 -20
- package/app/lib/errors.ts +0 -37
- package/app/lib/schema-org.ts +0 -38
- package/app/pages/blog/[slug].vue +0 -228
- package/app/pages/blog/index.vue +0 -130
- package/app/pages/docs/[...slug].vue +0 -10
- package/app/pages/docs/index.vue +0 -19
- package/app/pages/index.vue +0 -278
- package/app/plugins/ginko-docs-theme.ts +0 -14
- package/app/plugins/ssr-width.ts +0 -6
- package/app/utils/content.ts +0 -67
- package/app/utils/file-icons.ts +0 -90
- package/app/utils/index.ts +0 -7
- package/app/utils/repository.ts +0 -20
- package/components.ts +0 -9
- package/content-collections.ts +0 -89
- package/content.js +0 -200
- package/content.ts +0 -104
- package/i18n/i18n.config.ts +0 -10
- package/i18n/locales.ts +0 -81
- package/i18n/messages/global/banner.ts +0 -8
- package/i18n/messages/global/blog.ts +0 -18
- package/i18n/messages/global/command.ts +0 -49
- package/i18n/messages/global/docs.ts +0 -36
- package/i18n/messages/global/errors.ts +0 -36
- package/i18n/messages/global/feedback.ts +0 -11
- package/i18n/messages/global/index.ts +0 -21
- package/i18n/messages/global/nav.ts +0 -26
- package/i18n/messages/global/site.ts +0 -4
- package/i18n/messages/global/theme.ts +0 -9
- package/i18n/messages/index.ts +0 -20
- package/i18n/messages/localize.ts +0 -35
- package/i18n/routes.ts +0 -33
- package/icon-bundle.ts +0 -116
- package/index.d.ts +0 -37
- package/modules/analytics-boundary.ts +0 -23
- package/modules/feature-routing.ts +0 -68
- package/modules/syntax-highlighting.ts +0 -120
- package/public/fonts/public-sans/LICENSE +0 -93
- package/public/fonts/public-sans/README.md +0 -12
- package/public/fonts/public-sans/public-sans-400-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-500-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-600-normal-latin.woff +0 -0
- package/public/fonts/public-sans/public-sans-700-normal-latin.woff +0 -0
- package/runtime/generate-favicons.mjs +0 -124
- package/runtime/server/agent-markdown.ts +0 -19
- package/server/api/_ginko-docs/redirects.get.ts +0 -28
- package/server/mcp/tools/get-page.ts +0 -14
- package/server/mcp/tools/list-pages.ts +0 -10
- package/server/middleware/redirects.ts +0 -22
- package/server/plugins/agent-markdown.ts +0 -6
- package/server/plugins/sitemap-policy.ts +0 -12
- package/server/routes/.well-known/ai-catalog.json.get.ts +0 -10
- package/server/routes/[locale]/blog/rss.xml.get.ts +0 -13
- package/server/routes/blog/rss.xml.get.ts +0 -5
- package/server/routes/mcp/server-card.get.ts +0 -14
- package/server/utils/blog-feed.ts +0 -51
- package/server/utils/feed.ts +0 -70
- package/server/utils/mcp-server-card.ts +0 -45
- package/server/utils/redirects.ts +0 -38
- package/server/utils/redirects.utils.ts +0 -58
- package/server/utils/sitemap.ts +0 -45
- package/shared/route-slugs.ts +0 -5
- package/shared/theme-palettes.ts +0 -37
- package/shared/theme-presets.ts +0 -3
- package/shared/types/app-config.ts +0 -172
- package/shared/types/nuxt-config.ts +0 -14
- package/tags.ts +0 -192
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
filterTocByDepth,
|
|
4
|
-
flattenTocLinks,
|
|
5
|
-
formatContentDate,
|
|
6
|
-
getMarkdownTocLinks,
|
|
7
|
-
} from "#ginko-docs/utils/content";
|
|
8
|
-
import ContentFeedback from "#ginko-docs/components/content/Feedback.vue";
|
|
9
|
-
import DocsBreadcrumb from "./DocsBreadcrumb.vue";
|
|
10
|
-
import DocsMobileToc from "./DocsMobileToc.vue";
|
|
11
|
-
import DocsPageNav from "./DocsPageNav.vue";
|
|
12
|
-
import DocsToc from "./DocsToc.vue";
|
|
13
|
-
import DocsContributeLinks from "./DocsContributeLinks.vue";
|
|
14
|
-
import { computed, provide, ref, watch } from "vue";
|
|
15
|
-
import { docsTocKey } from "#ginko-docs/features/docs/toc-context";
|
|
16
|
-
import {
|
|
17
|
-
createError,
|
|
18
|
-
useAppConfig,
|
|
19
|
-
useContentPage,
|
|
20
|
-
useHead,
|
|
21
|
-
useI18n,
|
|
22
|
-
useRoute,
|
|
23
|
-
useSeoMeta,
|
|
24
|
-
} from "#imports";
|
|
25
|
-
import { useCanonicalUrl } from "#ginko-docs/composables/useCanonicalUrl";
|
|
26
|
-
import { useGinkoOgImage } from "#ginko-docs/composables/useGinkoOgImage";
|
|
27
|
-
import { useScrollspy } from "#ginko-docs/features/docs/composables/useScrollspy";
|
|
28
|
-
import { useRevealActive } from "#ginko-docs/features/docs/composables/useRevealActive";
|
|
29
|
-
import PageMarkdownCopy from "#ginko-docs/components/content/PageMarkdownCopy.vue";
|
|
30
|
-
import { Separator } from "#ginko-docs/components/ui/separator";
|
|
31
|
-
import { getLocalizedSiteText } from "#ginko-docs/config/site.utils";
|
|
32
|
-
import { useSchemaJsonLd } from "#ginko-docs/composables/useSchemaJsonLd";
|
|
33
|
-
import { useLocalizedPath } from "#ginko-docs/composables/useLocalizedPath";
|
|
34
|
-
import { useDocsNavigation } from "#ginko-docs/features/docs/composables/useDocsNavigation";
|
|
35
|
-
import { useContentRouteAlternates } from "#ginko-docs/composables/useContentRouteAlternates";
|
|
36
|
-
|
|
37
|
-
type DocsNavLink = {
|
|
38
|
-
path: string;
|
|
39
|
-
title: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
function toDocsNavLink(
|
|
43
|
-
item: { path?: string; title?: string } | null | undefined,
|
|
44
|
-
): DocsNavLink | null {
|
|
45
|
-
return item?.path && item?.title ? { path: item.path, title: item.title } : null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { locale, t } = useI18n();
|
|
49
|
-
const config = useAppConfig().ginkoDocs;
|
|
50
|
-
const route = useRoute();
|
|
51
|
-
const localizedPath = useLocalizedPath();
|
|
52
|
-
const canonicalUrl = useCanonicalUrl();
|
|
53
|
-
const { sync: syncContentRouteAlternates } = useContentRouteAlternates();
|
|
54
|
-
const contentPageResult = useContentPage("docs", {
|
|
55
|
-
fallback: true,
|
|
56
|
-
surround: true,
|
|
57
|
-
});
|
|
58
|
-
const docsNavigationResult = useDocsNavigation();
|
|
59
|
-
const [{ page, previous, next: nextContent, error }, { trail }] = await Promise.all([
|
|
60
|
-
contentPageResult,
|
|
61
|
-
docsNavigationResult,
|
|
62
|
-
]);
|
|
63
|
-
if (error.value) throw error.value;
|
|
64
|
-
if (!page.value) throw createError({ statusCode: 404, statusMessage: "Not Found" });
|
|
65
|
-
syncContentRouteAlternates(page);
|
|
66
|
-
|
|
67
|
-
const siteName = computed(() => getLocalizedSiteText(config.site.name, locale.value));
|
|
68
|
-
const pageTitle = computed(() => page.value?.title ?? t("docs.fallbackTitle"));
|
|
69
|
-
const pageDescription = computed(() => page.value?.description ?? t("docs.fallbackDescription"));
|
|
70
|
-
const tocItems = computed(() =>
|
|
71
|
-
filterTocByDepth(flattenTocLinks(getMarkdownTocLinks(page.value?.body)), config.toc?.depth ?? 3),
|
|
72
|
-
);
|
|
73
|
-
const prev = computed(() => toDocsNavLink(previous.value));
|
|
74
|
-
const next = computed(() => toDocsNavLink(nextContent.value));
|
|
75
|
-
const visibleTrail = computed(() => trail.value.slice(0, -1));
|
|
76
|
-
const schemaBreadcrumbs = computed(() => [
|
|
77
|
-
{ name: siteName.value, path: localizedPath("home") },
|
|
78
|
-
...trail.value
|
|
79
|
-
.filter((item) => item.path)
|
|
80
|
-
.map((item) => ({ name: item.title, path: item.path! })),
|
|
81
|
-
]);
|
|
82
|
-
provide(docsTocKey, tocItems);
|
|
83
|
-
const { activeIds, refresh } = useScrollspy(computed(() => tocItems.value.map((item) => item.id)));
|
|
84
|
-
|
|
85
|
-
watch(tocItems, () => {
|
|
86
|
-
void refresh();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Keep the active TOC region visible when a long TOC overflows the sticky
|
|
90
|
-
// aside.
|
|
91
|
-
const tocAside = ref<HTMLElement | null>(null);
|
|
92
|
-
const { reveal: revealActiveTocLink } = useRevealActive(tocAside, "[data-toc-active]", {
|
|
93
|
-
topPad: 16,
|
|
94
|
-
bottomPad: 44,
|
|
95
|
-
});
|
|
96
|
-
watch(activeIds, () => {
|
|
97
|
-
requestAnimationFrame(revealActiveTocLink);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
useSeoMeta({
|
|
101
|
-
title: computed(() => `${pageTitle.value} - ${siteName.value}`),
|
|
102
|
-
description: pageDescription,
|
|
103
|
-
ogTitle: computed(() => `${pageTitle.value} - ${siteName.value}`),
|
|
104
|
-
ogDescription: pageDescription,
|
|
105
|
-
ogUrl: canonicalUrl,
|
|
106
|
-
twitterCard: "summary_large_image",
|
|
107
|
-
twitterTitle: computed(() => `${pageTitle.value} - ${siteName.value}`),
|
|
108
|
-
twitterDescription: pageDescription,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Social card with the raw page title (no site-name suffix baked in).
|
|
112
|
-
useGinkoOgImage({
|
|
113
|
-
title: pageTitle.value,
|
|
114
|
-
description: pageDescription.value,
|
|
115
|
-
locale: locale.value,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
useSchemaJsonLd(() => [
|
|
119
|
-
{
|
|
120
|
-
"@type": "TechArticle",
|
|
121
|
-
headline: pageTitle.value,
|
|
122
|
-
description: pageDescription.value,
|
|
123
|
-
url: canonicalUrl.value,
|
|
124
|
-
inLanguage: locale.value,
|
|
125
|
-
dateModified: page.value?.updated,
|
|
126
|
-
isPartOf: { "@type": "WebSite", name: siteName.value, url: config.site.url },
|
|
127
|
-
publisher: { "@type": "Organization", name: siteName.value, url: config.site.url },
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"@type": "BreadcrumbList",
|
|
131
|
-
itemListElement: schemaBreadcrumbs.value.map((item, index) => ({
|
|
132
|
-
"@type": "ListItem",
|
|
133
|
-
position: index + 1,
|
|
134
|
-
name: item.name,
|
|
135
|
-
item: new URL(item.path, config.site.url).toString(),
|
|
136
|
-
})),
|
|
137
|
-
},
|
|
138
|
-
]);
|
|
139
|
-
|
|
140
|
-
useHead(() => ({
|
|
141
|
-
link: [{ key: "canonical", rel: "canonical", href: canonicalUrl.value }],
|
|
142
|
-
meta: [{ property: "og:type", content: "article" }],
|
|
143
|
-
}));
|
|
144
|
-
|
|
145
|
-
function scrollToTop() {
|
|
146
|
-
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
147
|
-
window.scrollTo({ top: 0, behavior: reducedMotion ? "auto" : "smooth" });
|
|
148
|
-
}
|
|
149
|
-
</script>
|
|
150
|
-
|
|
151
|
-
<template>
|
|
152
|
-
<div class="flex min-w-0 flex-1 flex-col xl:flex-row">
|
|
153
|
-
<div class="flex min-w-0 flex-1 flex-col">
|
|
154
|
-
<DocsMobileToc
|
|
155
|
-
class="order-1"
|
|
156
|
-
:items="tocItems"
|
|
157
|
-
:active-ids="activeIds"
|
|
158
|
-
:page-title="pageTitle"
|
|
159
|
-
/>
|
|
160
|
-
|
|
161
|
-
<main
|
|
162
|
-
id="main-content"
|
|
163
|
-
class="order-2 mx-auto w-full max-w-[72ch] flex-1 px-4 py-8 md:px-6 xl:py-14"
|
|
164
|
-
>
|
|
165
|
-
<article v-if="page">
|
|
166
|
-
<div class="flex items-start justify-between gap-4 sm:gap-6">
|
|
167
|
-
<div class="min-w-0">
|
|
168
|
-
<DocsBreadcrumb class="mb-2" :items="visibleTrail" />
|
|
169
|
-
<h1
|
|
170
|
-
class="font-heading text-[27px] leading-[1.15] font-semibold tracking-[-0.02em] text-balance text-foreground"
|
|
171
|
-
>
|
|
172
|
-
{{ page.title }}
|
|
173
|
-
</h1>
|
|
174
|
-
<p
|
|
175
|
-
v-if="page.description"
|
|
176
|
-
class="mt-2 text-base leading-[1.55] text-muted-foreground"
|
|
177
|
-
>
|
|
178
|
-
{{ page.description }}
|
|
179
|
-
</p>
|
|
180
|
-
</div>
|
|
181
|
-
<PageMarkdownCopy />
|
|
182
|
-
</div>
|
|
183
|
-
<Separator class="my-8" />
|
|
184
|
-
|
|
185
|
-
<div class="content-prose content-prose-docs">
|
|
186
|
-
<ContentRenderer :value="page" />
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
<footer class="mt-12 border-t border-border pt-6">
|
|
190
|
-
<p v-if="page.updated" class="mb-4 text-sm text-muted-foreground">
|
|
191
|
-
{{ t("docs.lastUpdated") }}:
|
|
192
|
-
<time :datetime="page.updated">{{ formatContentDate(page.updated, locale) }}</time>
|
|
193
|
-
</p>
|
|
194
|
-
<div class="flex flex-wrap items-center justify-between gap-x-6 gap-y-4">
|
|
195
|
-
<ContentFeedback :label="t('feedback.label')" />
|
|
196
|
-
<DocsContributeLinks
|
|
197
|
-
class="xl:hidden"
|
|
198
|
-
:stem="page.stem"
|
|
199
|
-
:extension="page.extension"
|
|
200
|
-
:title="pageTitle"
|
|
201
|
-
/>
|
|
202
|
-
</div>
|
|
203
|
-
<DocsPageNav class="mt-8" :prev="prev" :next="next" />
|
|
204
|
-
</footer>
|
|
205
|
-
</article>
|
|
206
|
-
</main>
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
<aside
|
|
210
|
-
ref="tocAside"
|
|
211
|
-
class="sticky top-[var(--site-header-height)] hidden h-[calc(100dvh-var(--site-header-height)-var(--site-banner-height,0px))] w-[var(--docs-toc-width)] shrink-0 flex-col overflow-y-auto pt-10 pr-4 pb-6 xl:flex"
|
|
212
|
-
:aria-label="t('docs.toc')"
|
|
213
|
-
>
|
|
214
|
-
<DocsToc class="mb-6" :items="tocItems" :active-ids="activeIds" />
|
|
215
|
-
|
|
216
|
-
<div class="mt-auto flex flex-col gap-2.5 border-t border-border/70 pt-5">
|
|
217
|
-
<DocsContributeLinks
|
|
218
|
-
variant="rail"
|
|
219
|
-
:stem="page?.stem"
|
|
220
|
-
:extension="page?.extension"
|
|
221
|
-
:title="pageTitle"
|
|
222
|
-
/>
|
|
223
|
-
<button
|
|
224
|
-
type="button"
|
|
225
|
-
class="inline-flex items-center gap-1.5 self-start text-sm font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:rounded-sm focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
|
226
|
-
@click="scrollToTop"
|
|
227
|
-
>
|
|
228
|
-
<Icon name="lucide:arrow-up" class="size-4" aria-hidden="true" />
|
|
229
|
-
{{ t("docs.backToTop") }}
|
|
230
|
-
</button>
|
|
231
|
-
</div>
|
|
232
|
-
</aside>
|
|
233
|
-
</div>
|
|
234
|
-
</template>
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { useI18n } from "#imports";
|
|
3
|
-
|
|
4
|
-
defineProps<{
|
|
5
|
-
prev?: { path?: string; title?: string } | null;
|
|
6
|
-
next?: { path?: string; title?: string } | null;
|
|
7
|
-
}>();
|
|
8
|
-
const { t } = useI18n();
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<template>
|
|
12
|
-
<nav
|
|
13
|
-
v-if="prev || next"
|
|
14
|
-
class="grid grid-cols-1 gap-3 sm:grid-cols-2"
|
|
15
|
-
:aria-label="t('docs.pageNavigation')"
|
|
16
|
-
>
|
|
17
|
-
<!-- Previous -->
|
|
18
|
-
<NuxtLink
|
|
19
|
-
v-if="prev"
|
|
20
|
-
:to="prev.path"
|
|
21
|
-
class="group flex min-h-20 flex-col gap-1.5 rounded-xl border border-border bg-card px-5 py-4 transition-[border-color,background-color] duration-200 hover:border-foreground/20 hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:col-start-1"
|
|
22
|
-
>
|
|
23
|
-
<span
|
|
24
|
-
class="flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors group-hover:text-foreground"
|
|
25
|
-
>
|
|
26
|
-
<Icon
|
|
27
|
-
name="lucide:chevron-left"
|
|
28
|
-
class="size-3.5 transition-transform group-hover:-translate-x-0.5"
|
|
29
|
-
aria-hidden="true"
|
|
30
|
-
/>
|
|
31
|
-
{{ t("docs.previous") }}
|
|
32
|
-
</span>
|
|
33
|
-
<span class="text-sm leading-snug font-semibold text-foreground">{{ prev.title }}</span>
|
|
34
|
-
</NuxtLink>
|
|
35
|
-
<div v-else class="hidden sm:block" />
|
|
36
|
-
|
|
37
|
-
<!-- Next -->
|
|
38
|
-
<NuxtLink
|
|
39
|
-
v-if="next"
|
|
40
|
-
:to="next.path"
|
|
41
|
-
class="group flex min-h-20 flex-col items-end gap-1.5 rounded-xl border border-border bg-card px-5 py-4 text-right transition-[border-color,background-color] duration-200 hover:border-foreground/20 hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:col-start-2"
|
|
42
|
-
>
|
|
43
|
-
<span
|
|
44
|
-
class="flex items-center gap-1 text-xs font-medium text-muted-foreground transition-colors group-hover:text-foreground"
|
|
45
|
-
>
|
|
46
|
-
{{ t("docs.next") }}
|
|
47
|
-
<Icon
|
|
48
|
-
name="lucide:chevron-right"
|
|
49
|
-
class="size-3.5 transition-transform group-hover:translate-x-0.5"
|
|
50
|
-
aria-hidden="true"
|
|
51
|
-
/>
|
|
52
|
-
</span>
|
|
53
|
-
<span class="text-sm leading-snug font-semibold text-foreground">{{ next.title }}</span>
|
|
54
|
-
</NuxtLink>
|
|
55
|
-
<div v-else class="hidden sm:block" />
|
|
56
|
-
</nav>
|
|
57
|
-
</template>
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { cn } from "#ginko-docs/utils";
|
|
3
|
-
import { useDocsNavigation } from "#ginko-docs/features/docs/composables/useDocsNavigation";
|
|
4
|
-
import {
|
|
5
|
-
docsNavigationSectionContainsPath,
|
|
6
|
-
getDocsNavigationGroups,
|
|
7
|
-
resolveDocsSectionTargetPath,
|
|
8
|
-
type DocsNavigationSection,
|
|
9
|
-
} from "#ginko-docs/features/docs/docs-navigation";
|
|
10
|
-
import { ScrollArea } from "#ginko-docs/components/ui/scroll-area";
|
|
11
|
-
import { useRevealActive } from "#ginko-docs/features/docs/composables/useRevealActive";
|
|
12
|
-
import DocsSidebarItem from "./DocsSidebarItem.vue";
|
|
13
|
-
import DocsSidebarDropdown from "./DocsSidebarDropdown.vue";
|
|
14
|
-
import DocsSidebarList from "./DocsSidebarList.vue";
|
|
15
|
-
import DocsSidebarTabs from "./DocsSidebarTabs.vue";
|
|
16
|
-
import { computed, nextTick, onMounted, ref, watch } from "vue";
|
|
17
|
-
import { navigateTo, useAppConfig, useI18n, useRoute } from "#imports";
|
|
18
|
-
|
|
19
|
-
const props = withDefaults(
|
|
20
|
-
defineProps<{
|
|
21
|
-
variant?: "desktop" | "drawer";
|
|
22
|
-
}>(),
|
|
23
|
-
{
|
|
24
|
-
variant: "desktop",
|
|
25
|
-
},
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
const { t } = useI18n();
|
|
29
|
-
const route = useRoute();
|
|
30
|
-
const sidebarSwitcher = useAppConfig().ginkoDocs.site.docsSidebarSwitcher;
|
|
31
|
-
const { sections } = await useDocsNavigation();
|
|
32
|
-
const switcherSections = computed(() =>
|
|
33
|
-
sections.value.filter((section): section is DocsNavigationSection & { title: string } =>
|
|
34
|
-
Boolean(section.title),
|
|
35
|
-
),
|
|
36
|
-
);
|
|
37
|
-
const selectedSectionId = ref("");
|
|
38
|
-
const routeSection = computed(() =>
|
|
39
|
-
sections.value.find((section) => docsNavigationSectionContainsPath(section, route.path)),
|
|
40
|
-
);
|
|
41
|
-
const activeSectionId = computed(
|
|
42
|
-
() => selectedSectionId.value || routeSection.value?.id || sections.value[0]?.id || "",
|
|
43
|
-
);
|
|
44
|
-
const activeSection = computed(
|
|
45
|
-
() => sections.value.find((section) => section.id === activeSectionId.value) ?? sections.value[0],
|
|
46
|
-
);
|
|
47
|
-
const groups = computed(() =>
|
|
48
|
-
activeSection.value ? getDocsNavigationGroups(activeSection.value) : [],
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
watch(
|
|
52
|
-
() => route.path,
|
|
53
|
-
() => {
|
|
54
|
-
selectedSectionId.value = "";
|
|
55
|
-
},
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
function setActiveSection(id: string) {
|
|
59
|
-
// Re-selecting the active section must not navigate away from the current page.
|
|
60
|
-
if (id === activeSectionId.value) return;
|
|
61
|
-
selectedSectionId.value = id;
|
|
62
|
-
const section = sections.value.find((entry) => entry.id === id);
|
|
63
|
-
const path = section ? resolveDocsSectionTargetPath(section) : undefined;
|
|
64
|
-
if (path) void navigateTo(path);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// The reduced top padding only applies under the section switcher; without it
|
|
68
|
-
// the scroll mask would fade into the first group label.
|
|
69
|
-
const scrollViewportClass = computed(() =>
|
|
70
|
-
cn(
|
|
71
|
-
"size-full rounded-[inherit] p-4 overscroll-contain [mask-image:linear-gradient(to_bottom,transparent,white_12px,white_calc(100%-12px),transparent)]",
|
|
72
|
-
switcherSections.value.length > 1 && "pt-2",
|
|
73
|
-
),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Deep links can land on an item far outside the visible band — center it
|
|
77
|
-
// once on mount and on section switches.
|
|
78
|
-
const scrollArea = ref<{ $el?: HTMLElement } | null>(null);
|
|
79
|
-
|
|
80
|
-
const { reveal: revealActiveItem } = useRevealActive(
|
|
81
|
-
() => scrollArea.value?.$el?.querySelector<HTMLElement>("[data-slot='scroll-area-viewport']"),
|
|
82
|
-
"[data-active='true']",
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
onMounted(() => {
|
|
86
|
-
void nextTick(revealActiveItem);
|
|
87
|
-
});
|
|
88
|
-
watch(activeSectionId, () => {
|
|
89
|
-
void nextTick(revealActiveItem);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const asideClass = computed(() =>
|
|
93
|
-
cn(
|
|
94
|
-
"flex h-full w-full flex-col text-sm transition-[border-color] duration-[250ms] ease-out *:w-full",
|
|
95
|
-
props.variant === "desktop"
|
|
96
|
-
? "absolute inset-y-0 start-0 items-end border-e border-border"
|
|
97
|
-
: "relative min-h-0 items-stretch border-e-0",
|
|
98
|
-
),
|
|
99
|
-
);
|
|
100
|
-
</script>
|
|
101
|
-
|
|
102
|
-
<template>
|
|
103
|
-
<aside :data-variant="variant" :aria-label="t('docs.label')" :class="asideClass">
|
|
104
|
-
<div v-if="switcherSections.length > 1" class="flex flex-col gap-3 p-4 pb-2">
|
|
105
|
-
<DocsSidebarTabs
|
|
106
|
-
v-if="sidebarSwitcher === 'tabs'"
|
|
107
|
-
:sections="switcherSections"
|
|
108
|
-
:active-id="activeSectionId"
|
|
109
|
-
@update:active-id="setActiveSection"
|
|
110
|
-
/>
|
|
111
|
-
<DocsSidebarDropdown
|
|
112
|
-
v-else-if="sidebarSwitcher === 'dropdown'"
|
|
113
|
-
:sections="switcherSections"
|
|
114
|
-
:active-id="activeSectionId"
|
|
115
|
-
@update:active-id="setActiveSection"
|
|
116
|
-
/>
|
|
117
|
-
<DocsSidebarList
|
|
118
|
-
v-else
|
|
119
|
-
:sections="switcherSections"
|
|
120
|
-
:active-id="activeSectionId"
|
|
121
|
-
@update:active-id="setActiveSection"
|
|
122
|
-
/>
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
<ScrollArea ref="scrollArea" class="min-h-0 flex-1" :viewport-class="scrollViewportClass">
|
|
126
|
-
<div class="flex min-w-full flex-col gap-0.5">
|
|
127
|
-
<template v-for="group in groups" :key="group.id">
|
|
128
|
-
<p
|
|
129
|
-
v-if="group.title"
|
|
130
|
-
data-slot="docs-sidebar-group-title"
|
|
131
|
-
class="mt-5 mb-1.5 inline-flex items-center gap-2 px-2 text-[11px] font-semibold tracking-wider text-muted-foreground uppercase first:mt-0"
|
|
132
|
-
>
|
|
133
|
-
{{ group.title }}
|
|
134
|
-
</p>
|
|
135
|
-
<DocsSidebarItem
|
|
136
|
-
v-for="item in group.items"
|
|
137
|
-
:key="item.path ?? item.title"
|
|
138
|
-
:item="item"
|
|
139
|
-
:depth="0"
|
|
140
|
-
/>
|
|
141
|
-
</template>
|
|
142
|
-
</div>
|
|
143
|
-
</ScrollArea>
|
|
144
|
-
</aside>
|
|
145
|
-
</template>
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import type { DocsNavigationSection } from "#ginko-docs/features/docs/docs-navigation";
|
|
4
|
-
import {
|
|
5
|
-
DropdownMenu,
|
|
6
|
-
DropdownMenuContent,
|
|
7
|
-
DropdownMenuRadioGroup,
|
|
8
|
-
DropdownMenuRadioItem,
|
|
9
|
-
DropdownMenuTrigger,
|
|
10
|
-
} from "#ginko-docs/components/ui/dropdown-menu";
|
|
11
|
-
|
|
12
|
-
const defaultIcon = "lucide:book-open";
|
|
13
|
-
|
|
14
|
-
const props = defineProps<{
|
|
15
|
-
sections: Array<DocsNavigationSection & { title: string }>;
|
|
16
|
-
activeId: string;
|
|
17
|
-
}>();
|
|
18
|
-
|
|
19
|
-
const emit = defineEmits<{
|
|
20
|
-
"update:activeId": [id: string];
|
|
21
|
-
}>();
|
|
22
|
-
|
|
23
|
-
const activeSection = computed(
|
|
24
|
-
() => props.sections.find((section) => section.id === props.activeId) ?? props.sections[0],
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
function selectSection(value: unknown) {
|
|
28
|
-
if (typeof value === "string") emit("update:activeId", value);
|
|
29
|
-
}
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<template>
|
|
33
|
-
<DropdownMenu v-if="activeSection">
|
|
34
|
-
<DropdownMenuTrigger as-child>
|
|
35
|
-
<button
|
|
36
|
-
type="button"
|
|
37
|
-
class="flex h-10 w-full items-center gap-2 rounded-lg border border-border bg-background px-3 text-start text-foreground transition-colors outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring"
|
|
38
|
-
data-slot="docs-sidebar-dropdown"
|
|
39
|
-
>
|
|
40
|
-
<Icon
|
|
41
|
-
:name="activeSection.icon ?? defaultIcon"
|
|
42
|
-
class="size-4 shrink-0"
|
|
43
|
-
aria-hidden="true"
|
|
44
|
-
/>
|
|
45
|
-
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ activeSection.title }}</span>
|
|
46
|
-
<Icon
|
|
47
|
-
name="lucide:chevrons-up-down"
|
|
48
|
-
class="size-4 shrink-0 text-muted-foreground"
|
|
49
|
-
aria-hidden="true"
|
|
50
|
-
/>
|
|
51
|
-
</button>
|
|
52
|
-
</DropdownMenuTrigger>
|
|
53
|
-
<DropdownMenuContent align="start" class="w-(--reka-dropdown-menu-trigger-width)">
|
|
54
|
-
<DropdownMenuRadioGroup :model-value="activeId" @update:model-value="selectSection">
|
|
55
|
-
<DropdownMenuRadioItem
|
|
56
|
-
v-for="section in sections"
|
|
57
|
-
:key="section.id"
|
|
58
|
-
:value="section.id"
|
|
59
|
-
class="gap-2"
|
|
60
|
-
>
|
|
61
|
-
<template #indicator-icon>
|
|
62
|
-
<Icon name="lucide:check" class="size-4" aria-hidden="true" />
|
|
63
|
-
</template>
|
|
64
|
-
<Icon :name="section.icon ?? defaultIcon" class="size-4 shrink-0" aria-hidden="true" />
|
|
65
|
-
<span class="truncate">{{ section.title }}</span>
|
|
66
|
-
</DropdownMenuRadioItem>
|
|
67
|
-
</DropdownMenuRadioGroup>
|
|
68
|
-
</DropdownMenuContent>
|
|
69
|
-
</DropdownMenu>
|
|
70
|
-
</template>
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, watch } from "vue";
|
|
3
|
-
import { useRoute } from "vue-router";
|
|
4
|
-
import {
|
|
5
|
-
navigationItemContainsPath,
|
|
6
|
-
normalizeNavigationPath,
|
|
7
|
-
} from "@lupinum/ginko-content/navigation";
|
|
8
|
-
import type { DocsNavigationItem } from "#ginko-docs/features/docs/docs-navigation";
|
|
9
|
-
import { Button } from "#ginko-docs/components/ui/button";
|
|
10
|
-
import DocsSidebarRow from "./DocsSidebarRow.vue";
|
|
11
|
-
import {
|
|
12
|
-
Collapsible,
|
|
13
|
-
CollapsibleContent,
|
|
14
|
-
CollapsibleTrigger,
|
|
15
|
-
} from "#ginko-docs/components/ui/collapsible";
|
|
16
|
-
import { cn } from "#ginko-docs/utils";
|
|
17
|
-
|
|
18
|
-
defineOptions({
|
|
19
|
-
name: "DocsSidebarItem",
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const props = defineProps<{
|
|
23
|
-
item: DocsNavigationItem;
|
|
24
|
-
depth: number;
|
|
25
|
-
}>();
|
|
26
|
-
|
|
27
|
-
const route = useRoute();
|
|
28
|
-
|
|
29
|
-
function itemIsExactActive(path: string, item: DocsNavigationItem): boolean {
|
|
30
|
-
return Boolean(item.path && normalizeNavigationPath(path) === normalizeNavigationPath(item.path));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function folderShouldBeOpen(path: string, item: DocsNavigationItem): boolean {
|
|
34
|
-
return item.children.length > 0 && navigationItemContainsPath(item, path);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const isExactActive = computed(() => itemIsExactActive(route.path, props.item));
|
|
38
|
-
|
|
39
|
-
const paddingClass = computed(() => {
|
|
40
|
-
const steps = ["ps-[13px]", "ps-[25px]", "ps-[37px]"] as const;
|
|
41
|
-
return steps[Math.min(Math.max(props.depth - 1, 0), steps.length - 1)]!;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Depth 0: compact pill rows. Depth > 0: each row carries the rail as its own
|
|
45
|
-
// start border so the active segment recolors it — aligned by construction.
|
|
46
|
-
const linkRowClass = computed(() =>
|
|
47
|
-
cn(
|
|
48
|
-
"group relative flex w-full flex-row items-center gap-2 text-start [overflow-wrap:anywhere] text-muted-foreground transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
49
|
-
"[&_svg]:size-4 [&_svg]:shrink-0",
|
|
50
|
-
"hover:bg-accent/50 hover:text-accent-foreground/80 hover:transition-none",
|
|
51
|
-
"data-[active=true]:bg-muted data-[active=true]:text-foreground data-[active=true]:font-medium data-[active=true]:hover:bg-muted data-[active=true]:hover:transition-colors",
|
|
52
|
-
props.depth === 0
|
|
53
|
-
? "rounded-[7px] px-2 py-[5.5px] text-[13px]"
|
|
54
|
-
: cn(
|
|
55
|
-
"rounded-e-[7px] border-s border-border py-[5px] pe-2 text-[12.5px]",
|
|
56
|
-
"hover:border-s-muted-foreground/40",
|
|
57
|
-
"data-[active=true]:border-s-brand",
|
|
58
|
-
paddingClass.value,
|
|
59
|
-
),
|
|
60
|
-
),
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
const folderOpen = ref(folderShouldBeOpen(route.path, props.item));
|
|
64
|
-
|
|
65
|
-
watch(
|
|
66
|
-
() => route.path,
|
|
67
|
-
() => {
|
|
68
|
-
if (folderShouldBeOpen(route.path, props.item)) {
|
|
69
|
-
folderOpen.value = true;
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// No gap between child rows: their start borders stack into one continuous
|
|
75
|
-
// rail. Deeper levels indent via padding only, sharing the same rail.
|
|
76
|
-
const contentRailClass = computed(() =>
|
|
77
|
-
props.depth === 0 ? "ms-[15.5px] mt-0.5 mb-1 flex flex-col" : "flex flex-col",
|
|
78
|
-
);
|
|
79
|
-
</script>
|
|
80
|
-
|
|
81
|
-
<template>
|
|
82
|
-
<Collapsible v-if="item.children.length > 0" v-model:open="folderOpen" :unmount-on-hide="false">
|
|
83
|
-
<CollapsibleTrigger v-if="item.path" as-child>
|
|
84
|
-
<NuxtLink
|
|
85
|
-
:to="item.path"
|
|
86
|
-
:class="linkRowClass"
|
|
87
|
-
:aria-current="isExactActive ? 'page' : undefined"
|
|
88
|
-
:data-active="isExactActive ? 'true' : 'false'"
|
|
89
|
-
>
|
|
90
|
-
<DocsSidebarRow :item="item" show-chevron />
|
|
91
|
-
</NuxtLink>
|
|
92
|
-
</CollapsibleTrigger>
|
|
93
|
-
<CollapsibleTrigger v-else as-child>
|
|
94
|
-
<Button
|
|
95
|
-
variant="ghost"
|
|
96
|
-
:class="linkRowClass"
|
|
97
|
-
class="h-auto justify-start"
|
|
98
|
-
:data-active="isExactActive ? 'true' : 'false'"
|
|
99
|
-
>
|
|
100
|
-
<DocsSidebarRow :item="item" show-chevron />
|
|
101
|
-
</Button>
|
|
102
|
-
</CollapsibleTrigger>
|
|
103
|
-
|
|
104
|
-
<CollapsibleContent :class="contentRailClass">
|
|
105
|
-
<DocsSidebarItem
|
|
106
|
-
v-for="child in item.children"
|
|
107
|
-
:key="child.path ?? child.title"
|
|
108
|
-
:item="child"
|
|
109
|
-
:depth="depth + 1"
|
|
110
|
-
/>
|
|
111
|
-
</CollapsibleContent>
|
|
112
|
-
</Collapsible>
|
|
113
|
-
<NuxtLink
|
|
114
|
-
v-else-if="item.path"
|
|
115
|
-
:to="item.path"
|
|
116
|
-
:aria-current="isExactActive ? 'page' : undefined"
|
|
117
|
-
:class="linkRowClass"
|
|
118
|
-
:data-active="isExactActive ? 'true' : 'false'"
|
|
119
|
-
>
|
|
120
|
-
<DocsSidebarRow :item="item" />
|
|
121
|
-
</NuxtLink>
|
|
122
|
-
</template>
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { DocsNavigationSection } from "#ginko-docs/features/docs/docs-navigation";
|
|
3
|
-
|
|
4
|
-
const defaultIcon = "lucide:book-open";
|
|
5
|
-
|
|
6
|
-
defineProps<{
|
|
7
|
-
sections: Array<DocsNavigationSection & { title: string }>;
|
|
8
|
-
activeId: string;
|
|
9
|
-
}>();
|
|
10
|
-
|
|
11
|
-
const emit = defineEmits<{
|
|
12
|
-
"update:activeId": [id: string];
|
|
13
|
-
}>();
|
|
14
|
-
</script>
|
|
15
|
-
|
|
16
|
-
<template>
|
|
17
|
-
<ul class="flex flex-col gap-0.5" data-slot="docs-sidebar-list">
|
|
18
|
-
<li v-for="section in sections" :key="section.id">
|
|
19
|
-
<button
|
|
20
|
-
type="button"
|
|
21
|
-
class="flex min-h-9 w-full items-center gap-2 rounded-lg px-2 text-start text-sm transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
22
|
-
:class="
|
|
23
|
-
activeId === section.id
|
|
24
|
-
? 'bg-muted font-medium text-foreground'
|
|
25
|
-
: 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
|
|
26
|
-
"
|
|
27
|
-
:aria-pressed="activeId === section.id"
|
|
28
|
-
@click="emit('update:activeId', section.id)"
|
|
29
|
-
>
|
|
30
|
-
<Icon :name="section.icon ?? defaultIcon" class="size-4 shrink-0" aria-hidden="true" />
|
|
31
|
-
<span class="truncate">{{ section.title }}</span>
|
|
32
|
-
</button>
|
|
33
|
-
</li>
|
|
34
|
-
</ul>
|
|
35
|
-
</template>
|