@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,90 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import { useI18n } from "#imports";
|
|
4
|
-
import { useLocalizedPath } from "#ginko-docs/composables/useLocalizedPath";
|
|
5
|
-
import { useSiteNavigation } from "#ginko-docs/composables/useSiteNavigation";
|
|
6
|
-
|
|
7
|
-
const { site, socialLinks } = useSiteNavigation();
|
|
8
|
-
const { t } = useI18n();
|
|
9
|
-
const localizedPath = useLocalizedPath();
|
|
10
|
-
|
|
11
|
-
const currentYearDate = new Date();
|
|
12
|
-
const resources = computed(() => [
|
|
13
|
-
{ label: t("nav.documentation"), href: localizedPath("docs"), external: false },
|
|
14
|
-
...socialLinks.value,
|
|
15
|
-
]);
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<template>
|
|
19
|
-
<footer class="border-t border-border bg-muted/30" :aria-label="t('nav.company')">
|
|
20
|
-
<div class="mx-auto max-w-5xl px-4 py-12 md:px-6 md:py-16">
|
|
21
|
-
<div class="flex flex-col gap-8 md:flex-row md:items-start md:justify-between">
|
|
22
|
-
<div class="max-w-sm">
|
|
23
|
-
<NuxtLink
|
|
24
|
-
:to="localizedPath('home')"
|
|
25
|
-
:aria-label="site.name"
|
|
26
|
-
class="flex items-center gap-2 font-semibold text-foreground"
|
|
27
|
-
>
|
|
28
|
-
<SiteLogoMark />
|
|
29
|
-
</NuxtLink>
|
|
30
|
-
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">
|
|
31
|
-
{{ site.description }}
|
|
32
|
-
</p>
|
|
33
|
-
</div>
|
|
34
|
-
<div>
|
|
35
|
-
<h3 class="mb-3 text-sm font-semibold text-foreground">{{ t("nav.resources") }}</h3>
|
|
36
|
-
<ul class="space-y-2.5">
|
|
37
|
-
<li v-for="item in resources" :key="item.href">
|
|
38
|
-
<NuxtLink
|
|
39
|
-
:to="item.href"
|
|
40
|
-
:target="item.external ? '_blank' : undefined"
|
|
41
|
-
:rel="item.external ? 'noopener noreferrer' : undefined"
|
|
42
|
-
class="inline-flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
|
43
|
-
>
|
|
44
|
-
{{ item.label }}
|
|
45
|
-
<Icon
|
|
46
|
-
v-if="item.external"
|
|
47
|
-
name="lucide:arrow-up-right"
|
|
48
|
-
class="size-3 opacity-60"
|
|
49
|
-
aria-hidden="true"
|
|
50
|
-
/>
|
|
51
|
-
</NuxtLink>
|
|
52
|
-
</li>
|
|
53
|
-
</ul>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<div
|
|
58
|
-
class="mt-10 flex flex-col items-center justify-between gap-3 border-t border-border pt-6 text-sm text-muted-foreground sm:flex-row"
|
|
59
|
-
>
|
|
60
|
-
<p>
|
|
61
|
-
© <NuxtTime :datetime="currentYearDate" year="numeric" /> {{ site.name }}.
|
|
62
|
-
{{ t("site.footer") }}
|
|
63
|
-
</p>
|
|
64
|
-
<div class="flex flex-wrap items-center justify-center gap-x-4 gap-y-2 sm:justify-end">
|
|
65
|
-
<NuxtLink
|
|
66
|
-
v-for="link in site.legalLinks"
|
|
67
|
-
:key="link.href"
|
|
68
|
-
:to="link.href"
|
|
69
|
-
:target="link.external ? '_blank' : undefined"
|
|
70
|
-
:rel="link.external ? 'noopener noreferrer' : undefined"
|
|
71
|
-
class="underline decoration-border underline-offset-4 transition-colors hover:text-foreground hover:decoration-foreground"
|
|
72
|
-
>
|
|
73
|
-
{{ link.label }}
|
|
74
|
-
</NuxtLink>
|
|
75
|
-
<p v-if="site.lupinumAttribution">
|
|
76
|
-
{{ t("site.openSourceBy") }}
|
|
77
|
-
<a
|
|
78
|
-
href="https://lupinum.com"
|
|
79
|
-
target="_blank"
|
|
80
|
-
rel="noopener noreferrer"
|
|
81
|
-
class="font-medium text-foreground underline decoration-border underline-offset-4 transition-colors hover:decoration-foreground"
|
|
82
|
-
>
|
|
83
|
-
Lupinum<span class="sr-only"> ({{ t("nav.externalLink") }})</span> </a
|
|
84
|
-
>.
|
|
85
|
-
</p>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
</footer>
|
|
90
|
-
</template>
|
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { Button } from "#ginko-docs/components/ui/button";
|
|
3
|
-
import { Kbd } from "#ginko-docs/components/ui/kbd";
|
|
4
|
-
import {
|
|
5
|
-
Sheet,
|
|
6
|
-
SheetContent,
|
|
7
|
-
SheetDescription,
|
|
8
|
-
SheetTitle,
|
|
9
|
-
SheetTrigger,
|
|
10
|
-
} from "#ginko-docs/components/ui/sheet";
|
|
11
|
-
import { computed, nextTick, ref, watch } from "vue";
|
|
12
|
-
import {
|
|
13
|
-
findFirstNavigationPage,
|
|
14
|
-
navigationItemContainsPath,
|
|
15
|
-
normalizeNavigationPath,
|
|
16
|
-
} from "@lupinum/ginko-content/navigation";
|
|
17
|
-
import { useI18n, useRoute } from "#imports";
|
|
18
|
-
import { useLocalizedPath } from "#ginko-docs/composables/useLocalizedPath";
|
|
19
|
-
import { useSiteNavigation } from "#ginko-docs/composables/useSiteNavigation";
|
|
20
|
-
import { useCommandCenterState } from "#ginko-docs/features/search/useCommandCenter";
|
|
21
|
-
import { useMetaKey } from "#ginko-docs/composables/useMetaKey";
|
|
22
|
-
import { useDocsNavigationData } from "#ginko-docs/features/docs/composables/useDocsNavigationData";
|
|
23
|
-
import {
|
|
24
|
-
getDocsNavigationSections,
|
|
25
|
-
normalizeDocsNavigationItem,
|
|
26
|
-
} from "#ginko-docs/features/docs/docs-navigation";
|
|
27
|
-
import ModeToggle from "#ginko-docs/components/site/ModeToggle.vue";
|
|
28
|
-
import { useGinkoDocsConfig } from "#ginko-docs/composables/useGinkoDocsConfig";
|
|
29
|
-
|
|
30
|
-
const { mainNav, socialLinks } = useSiteNavigation();
|
|
31
|
-
const { openCommandCenter } = useCommandCenterState();
|
|
32
|
-
const config = useGinkoDocsConfig();
|
|
33
|
-
const { t } = useI18n();
|
|
34
|
-
const route = useRoute();
|
|
35
|
-
const isMobileMenuOpen = ref(false);
|
|
36
|
-
const localizedPath = useLocalizedPath();
|
|
37
|
-
|
|
38
|
-
const isActive = (href: string) => route.path === href || route.path.startsWith(href + "/");
|
|
39
|
-
|
|
40
|
-
const metaKey = useMetaKey();
|
|
41
|
-
const searchShortcut = computed(() => (metaKey.value === "⌘" ? "⌘K" : "Ctrl K"));
|
|
42
|
-
const homePath = computed(() => localizedPath("home"));
|
|
43
|
-
const docsPath = computed(() => localizedPath("docs"));
|
|
44
|
-
|
|
45
|
-
const { data: docsNavData } = useDocsNavigationData();
|
|
46
|
-
const docsSections = computed(() =>
|
|
47
|
-
getDocsNavigationSections(
|
|
48
|
-
(docsNavData.value ?? []).map((item, index) => normalizeDocsNavigationItem(item, index)),
|
|
49
|
-
),
|
|
50
|
-
);
|
|
51
|
-
const docsMenuSections = computed(() => {
|
|
52
|
-
const currentPath = normalizeNavigationPath(route.path);
|
|
53
|
-
|
|
54
|
-
return docsSections.value
|
|
55
|
-
.map((section) => ({
|
|
56
|
-
id: section.id,
|
|
57
|
-
title: section.title,
|
|
58
|
-
links: section.items
|
|
59
|
-
.map((item) => ({
|
|
60
|
-
id: item.id,
|
|
61
|
-
label: item.title,
|
|
62
|
-
href: item.path ?? findFirstNavigationPage(item.children)?.path,
|
|
63
|
-
active: navigationItemContainsPath(item, currentPath),
|
|
64
|
-
}))
|
|
65
|
-
.filter((link): link is typeof link & { href: string } => Boolean(link.href)),
|
|
66
|
-
}))
|
|
67
|
-
.filter((section) => section.links.length > 0);
|
|
68
|
-
});
|
|
69
|
-
const hasDocsLevel = computed(() => docsMenuSections.value.length > 0);
|
|
70
|
-
|
|
71
|
-
const rootNavItems = computed(() =>
|
|
72
|
-
mainNav.value.map((item) => ({
|
|
73
|
-
...item,
|
|
74
|
-
drill: hasDocsLevel.value && item.href === docsPath.value,
|
|
75
|
-
})),
|
|
76
|
-
);
|
|
77
|
-
const docsLevelTitle = computed(
|
|
78
|
-
() => rootNavItems.value.find((item) => item.drill)?.label ?? t("nav.documentation"),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
type MenuLevel = "root" | "docs";
|
|
82
|
-
const menuLevel = ref<MenuLevel>("root");
|
|
83
|
-
const docsDrillButton = ref<HTMLButtonElement | null>(null);
|
|
84
|
-
const docsBackButton = ref<HTMLButtonElement | null>(null);
|
|
85
|
-
|
|
86
|
-
function setDrillButtonRef(el: unknown) {
|
|
87
|
-
docsDrillButton.value = el instanceof HTMLButtonElement ? el : null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function drillIntoDocs() {
|
|
91
|
-
menuLevel.value = "docs";
|
|
92
|
-
await nextTick();
|
|
93
|
-
docsBackButton.value?.focus({ preventScroll: true });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function backToRoot() {
|
|
97
|
-
menuLevel.value = "root";
|
|
98
|
-
await nextTick();
|
|
99
|
-
docsDrillButton.value?.focus({ preventScroll: true });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function closeMenu() {
|
|
103
|
-
isMobileMenuOpen.value = false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function openSearch() {
|
|
107
|
-
isMobileMenuOpen.value = false;
|
|
108
|
-
openCommandCenter();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
watch(isMobileMenuOpen, (open) => {
|
|
112
|
-
if (open) {
|
|
113
|
-
menuLevel.value = hasDocsLevel.value && isActive(docsPath.value) ? "docs" : "root";
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
watch(
|
|
118
|
-
() => route.path,
|
|
119
|
-
() => {
|
|
120
|
-
isMobileMenuOpen.value = false;
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
</script>
|
|
124
|
-
|
|
125
|
-
<template>
|
|
126
|
-
<header
|
|
127
|
-
class="sticky top-0 z-50 flex h-14 w-full shrink-0 items-center border-b border-border bg-background/90 backdrop-blur-md"
|
|
128
|
-
>
|
|
129
|
-
<div
|
|
130
|
-
class="mx-auto flex w-full max-w-screen-2xl min-w-0 flex-1 items-center gap-3 px-4 min-[691px]:gap-4 min-[691px]:px-6"
|
|
131
|
-
>
|
|
132
|
-
<NuxtLink
|
|
133
|
-
:to="homePath"
|
|
134
|
-
class="flex shrink-0 items-center gap-2 font-semibold text-foreground"
|
|
135
|
-
:aria-label="t('nav.home')"
|
|
136
|
-
>
|
|
137
|
-
<SiteLogoMark />
|
|
138
|
-
</NuxtLink>
|
|
139
|
-
|
|
140
|
-
<nav
|
|
141
|
-
class="ml-1 hidden shrink-0 items-center gap-1 min-[691px]:ml-2 min-[691px]:flex"
|
|
142
|
-
:aria-label="t('nav.main')"
|
|
143
|
-
>
|
|
144
|
-
<NuxtLink
|
|
145
|
-
v-for="item in mainNav"
|
|
146
|
-
:key="item.href"
|
|
147
|
-
:to="item.href"
|
|
148
|
-
class="rounded-lg px-3 py-1.5 text-sm font-medium transition-colors"
|
|
149
|
-
:class="
|
|
150
|
-
isActive(item.href)
|
|
151
|
-
? 'bg-muted text-foreground'
|
|
152
|
-
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
153
|
-
"
|
|
154
|
-
>
|
|
155
|
-
{{ item.label }}
|
|
156
|
-
</NuxtLink>
|
|
157
|
-
</nav>
|
|
158
|
-
|
|
159
|
-
<div class="ml-auto flex min-w-0 items-center gap-1 min-[691px]:gap-1.5">
|
|
160
|
-
<Button
|
|
161
|
-
variant="outline"
|
|
162
|
-
class="hidden h-9 w-[22.5rem] min-w-0 max-w-[22.5rem] flex-1 justify-start gap-2 overflow-hidden rounded-lg border-border/70 bg-muted/35 px-3 whitespace-nowrap text-muted-foreground min-[691px]:flex"
|
|
163
|
-
:aria-label="t('nav.searchDocumentation')"
|
|
164
|
-
type="button"
|
|
165
|
-
@click="openSearch"
|
|
166
|
-
>
|
|
167
|
-
<Icon name="lucide:search" class="size-4 shrink-0" aria-hidden="true" />
|
|
168
|
-
<span class="min-w-0 flex-1 truncate text-left" aria-hidden="true">{{
|
|
169
|
-
t("nav.searchDocumentation")
|
|
170
|
-
}}</span>
|
|
171
|
-
<Kbd class="hidden h-5 shrink-0 bg-background text-[10px] min-[900px]:inline-flex">{{
|
|
172
|
-
searchShortcut
|
|
173
|
-
}}</Kbd>
|
|
174
|
-
</Button>
|
|
175
|
-
|
|
176
|
-
<div class="flex shrink-0 items-center gap-0.5">
|
|
177
|
-
<Button
|
|
178
|
-
variant="ghost"
|
|
179
|
-
size="icon"
|
|
180
|
-
class="size-9 rounded-lg min-[691px]:hidden"
|
|
181
|
-
:aria-label="t('nav.searchDocumentation')"
|
|
182
|
-
type="button"
|
|
183
|
-
@click="openSearch"
|
|
184
|
-
>
|
|
185
|
-
<Icon name="lucide:search" class="size-[18px]" aria-hidden="true" />
|
|
186
|
-
</Button>
|
|
187
|
-
|
|
188
|
-
<ClientOnly>
|
|
189
|
-
<ModeToggle class="hidden min-[691px]:inline-flex" />
|
|
190
|
-
|
|
191
|
-
<template #fallback>
|
|
192
|
-
<span
|
|
193
|
-
class="hidden size-9 shrink-0 rounded-lg min-[691px]:inline-flex"
|
|
194
|
-
aria-hidden="true"
|
|
195
|
-
/>
|
|
196
|
-
</template>
|
|
197
|
-
</ClientOnly>
|
|
198
|
-
|
|
199
|
-
<SiteLocaleSwitcher class="hidden min-[691px]:flex" />
|
|
200
|
-
|
|
201
|
-
<SiteSocialLinks v-if="config.nav.socialIcons" />
|
|
202
|
-
|
|
203
|
-
<Sheet v-model:open="isMobileMenuOpen">
|
|
204
|
-
<SheetTrigger as-child>
|
|
205
|
-
<Button
|
|
206
|
-
variant="ghost"
|
|
207
|
-
size="icon"
|
|
208
|
-
class="size-9 rounded-lg min-[691px]:hidden"
|
|
209
|
-
:aria-label="t('nav.openMenu')"
|
|
210
|
-
type="button"
|
|
211
|
-
>
|
|
212
|
-
<Icon name="lucide:menu" class="size-[18px]" aria-hidden="true" />
|
|
213
|
-
</Button>
|
|
214
|
-
</SheetTrigger>
|
|
215
|
-
<SheetContent
|
|
216
|
-
side="top"
|
|
217
|
-
class="inset-0 h-dvh gap-0 overflow-hidden border-b-0 bg-background/95 p-0 backdrop-blur-md min-[691px]:hidden"
|
|
218
|
-
>
|
|
219
|
-
<SheetTitle class="sr-only">{{ t("nav.mobile") }}</SheetTitle>
|
|
220
|
-
<SheetDescription class="sr-only">{{ t("nav.main") }}</SheetDescription>
|
|
221
|
-
<div class="flex h-full min-h-0 flex-col pt-16">
|
|
222
|
-
<div
|
|
223
|
-
class="flex min-h-0 w-[200%] flex-1 transition-transform duration-300 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none"
|
|
224
|
-
:class="menuLevel === 'docs' ? '-translate-x-1/2' : ''"
|
|
225
|
-
>
|
|
226
|
-
<nav
|
|
227
|
-
class="flex min-h-0 w-1/2 flex-col overflow-x-hidden overflow-y-auto px-6"
|
|
228
|
-
:aria-label="t('nav.mobile')"
|
|
229
|
-
:inert="menuLevel !== 'root'"
|
|
230
|
-
>
|
|
231
|
-
<div class="m-auto w-full space-y-8 py-6">
|
|
232
|
-
<template v-for="item in rootNavItems" :key="item.href">
|
|
233
|
-
<button
|
|
234
|
-
v-if="item.drill"
|
|
235
|
-
:ref="setDrillButtonRef"
|
|
236
|
-
type="button"
|
|
237
|
-
class="group block w-full text-start"
|
|
238
|
-
@click="drillIntoDocs"
|
|
239
|
-
>
|
|
240
|
-
<span
|
|
241
|
-
class="flex items-center gap-2.5 text-[2.5rem] leading-[1.05] font-semibold tracking-[-0.03em] text-balance"
|
|
242
|
-
:class="
|
|
243
|
-
isActive(item.href)
|
|
244
|
-
? 'text-foreground'
|
|
245
|
-
: 'text-foreground/70 transition-colors group-hover:text-foreground'
|
|
246
|
-
"
|
|
247
|
-
>
|
|
248
|
-
{{ item.label }}
|
|
249
|
-
<Icon
|
|
250
|
-
name="lucide:chevron-right"
|
|
251
|
-
class="size-6 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5"
|
|
252
|
-
aria-hidden="true"
|
|
253
|
-
/>
|
|
254
|
-
</span>
|
|
255
|
-
<span
|
|
256
|
-
v-if="item.description"
|
|
257
|
-
class="mt-1.5 block text-sm text-muted-foreground"
|
|
258
|
-
>
|
|
259
|
-
{{ item.description }}
|
|
260
|
-
</span>
|
|
261
|
-
</button>
|
|
262
|
-
<NuxtLink v-else :to="item.href" class="group block" @click="closeMenu">
|
|
263
|
-
<span
|
|
264
|
-
class="block text-[2.5rem] leading-[1.05] font-semibold tracking-[-0.03em] text-balance"
|
|
265
|
-
:class="
|
|
266
|
-
isActive(item.href)
|
|
267
|
-
? 'text-foreground'
|
|
268
|
-
: 'text-foreground/70 transition-colors group-hover:text-foreground'
|
|
269
|
-
"
|
|
270
|
-
>
|
|
271
|
-
{{ item.label }}
|
|
272
|
-
</span>
|
|
273
|
-
<span
|
|
274
|
-
v-if="item.description"
|
|
275
|
-
class="mt-1.5 block text-sm text-muted-foreground"
|
|
276
|
-
>
|
|
277
|
-
{{ item.description }}
|
|
278
|
-
</span>
|
|
279
|
-
</NuxtLink>
|
|
280
|
-
</template>
|
|
281
|
-
</div>
|
|
282
|
-
</nav>
|
|
283
|
-
|
|
284
|
-
<nav
|
|
285
|
-
v-if="hasDocsLevel"
|
|
286
|
-
class="flex min-h-0 w-1/2 flex-col px-6"
|
|
287
|
-
:aria-label="docsLevelTitle"
|
|
288
|
-
:inert="menuLevel !== 'docs'"
|
|
289
|
-
>
|
|
290
|
-
<button
|
|
291
|
-
ref="docsBackButton"
|
|
292
|
-
type="button"
|
|
293
|
-
class="flex items-center gap-1 self-start py-2 text-sm font-semibold text-muted-foreground transition-colors hover:text-foreground"
|
|
294
|
-
@click="backToRoot"
|
|
295
|
-
>
|
|
296
|
-
<Icon name="lucide:chevron-left" class="size-4" aria-hidden="true" />
|
|
297
|
-
{{ t("nav.menu") }}
|
|
298
|
-
</button>
|
|
299
|
-
<p
|
|
300
|
-
class="mt-1 text-[2rem] leading-[1.1] font-semibold tracking-[-0.03em] text-foreground"
|
|
301
|
-
>
|
|
302
|
-
{{ docsLevelTitle }}
|
|
303
|
-
</p>
|
|
304
|
-
<div class="-mx-2 mt-3 min-h-0 flex-1 overflow-x-hidden overflow-y-auto pb-4">
|
|
305
|
-
<template v-for="section in docsMenuSections" :key="section.id">
|
|
306
|
-
<p
|
|
307
|
-
v-if="section.title"
|
|
308
|
-
class="mt-6 mb-1 px-2 text-xs font-semibold tracking-[0.08em] text-muted-foreground uppercase first:mt-2"
|
|
309
|
-
>
|
|
310
|
-
{{ section.title }}
|
|
311
|
-
</p>
|
|
312
|
-
<NuxtLink
|
|
313
|
-
v-for="link in section.links"
|
|
314
|
-
:key="link.id"
|
|
315
|
-
:to="link.href"
|
|
316
|
-
class="flex items-center rounded-md px-2 py-2 text-lg font-semibold transition-colors"
|
|
317
|
-
:class="
|
|
318
|
-
link.active
|
|
319
|
-
? 'bg-muted text-foreground'
|
|
320
|
-
: 'text-foreground hover:bg-muted/60'
|
|
321
|
-
"
|
|
322
|
-
:aria-current="link.active ? 'true' : undefined"
|
|
323
|
-
@click="closeMenu"
|
|
324
|
-
>
|
|
325
|
-
{{ link.label }}
|
|
326
|
-
<span
|
|
327
|
-
v-if="link.active"
|
|
328
|
-
class="mr-1 ml-auto size-1.5 rounded-full bg-primary"
|
|
329
|
-
aria-hidden="true"
|
|
330
|
-
/>
|
|
331
|
-
</NuxtLink>
|
|
332
|
-
</template>
|
|
333
|
-
</div>
|
|
334
|
-
</nav>
|
|
335
|
-
</div>
|
|
336
|
-
|
|
337
|
-
<div class="shrink-0 space-y-3 border-t border-border px-6 pt-4 pb-8">
|
|
338
|
-
<ClientOnly>
|
|
339
|
-
<ModeToggle variant="menu-row" />
|
|
340
|
-
<SiteLocaleSwitcher variant="menu-row" @navigate="closeMenu" />
|
|
341
|
-
|
|
342
|
-
<template #fallback>
|
|
343
|
-
<span class="block h-28" aria-hidden="true" />
|
|
344
|
-
</template>
|
|
345
|
-
</ClientOnly>
|
|
346
|
-
<div
|
|
347
|
-
v-if="!config.nav.socialIcons && socialLinks.length"
|
|
348
|
-
class="flex flex-wrap items-center gap-2"
|
|
349
|
-
>
|
|
350
|
-
<NuxtLink
|
|
351
|
-
v-for="link in socialLinks"
|
|
352
|
-
:key="link.href"
|
|
353
|
-
:to="link.href"
|
|
354
|
-
target="_blank"
|
|
355
|
-
rel="noopener noreferrer"
|
|
356
|
-
class="inline-flex h-9 items-center gap-2 rounded-full border border-border px-4 text-sm font-semibold text-foreground transition-colors hover:bg-muted"
|
|
357
|
-
@click="closeMenu"
|
|
358
|
-
>
|
|
359
|
-
<Icon v-if="link.icon" :name="link.icon" class="size-4" aria-hidden="true" />
|
|
360
|
-
{{ link.label }}
|
|
361
|
-
</NuxtLink>
|
|
362
|
-
</div>
|
|
363
|
-
</div>
|
|
364
|
-
</div>
|
|
365
|
-
</SheetContent>
|
|
366
|
-
</Sheet>
|
|
367
|
-
</div>
|
|
368
|
-
</div>
|
|
369
|
-
</div>
|
|
370
|
-
</header>
|
|
371
|
-
</template>
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, ref, useId } from "vue";
|
|
3
|
-
import { useAsyncData, useI18n, useRuntimeConfig } from "#imports";
|
|
4
|
-
import { cn } from "../../utils";
|
|
5
|
-
import { filenameExtension, resolveFileIcon } from "../../utils/file-icons";
|
|
6
|
-
|
|
7
|
-
export type SiteHeroCodeTab = {
|
|
8
|
-
label: string;
|
|
9
|
-
icon?: string;
|
|
10
|
-
filename?: string;
|
|
11
|
-
language?: string;
|
|
12
|
-
code: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const props = defineProps<{ tabs: SiteHeroCodeTab[] }>();
|
|
16
|
-
|
|
17
|
-
const { t } = useI18n();
|
|
18
|
-
const runtimeConfig = useRuntimeConfig();
|
|
19
|
-
const syntaxThemes = computed(() => {
|
|
20
|
-
const themes = runtimeConfig.public.ginkoDocs?.syntaxHighlighting?.themes;
|
|
21
|
-
return {
|
|
22
|
-
light: themes?.light ?? "light-plus",
|
|
23
|
-
dark: themes?.dark ?? "dark-plus",
|
|
24
|
-
};
|
|
25
|
-
});
|
|
26
|
-
const regionId = useId();
|
|
27
|
-
const activeIndex = ref(0);
|
|
28
|
-
|
|
29
|
-
const activeTab = computed(() => props.tabs[activeIndex.value] ?? props.tabs[0]);
|
|
30
|
-
|
|
31
|
-
function tabIcon(tab: SiteHeroCodeTab): string {
|
|
32
|
-
return tab.icon ?? resolveFileIcon(tab.filename);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const HTML_ESCAPES: Record<string, string> = { "&": "&", "<": "<", ">": ">" };
|
|
36
|
-
|
|
37
|
-
function escapeHtml(input: string): string {
|
|
38
|
-
return input.replace(/[&<>]/g, (char) => HTML_ESCAPES[char] ?? char);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function plainLines(code: string): string {
|
|
42
|
-
return code
|
|
43
|
-
.split("\n")
|
|
44
|
-
.map((line) => `<span class="line">${escapeHtml(line)}</span>`)
|
|
45
|
-
.join("");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Config code arrives as raw strings, so it skips the build-time content
|
|
49
|
-
// pipeline; highlight here with the configured Shiki theme pair.
|
|
50
|
-
const { data: highlighted } = await useAsyncData(`site-hero-code-${regionId}`, async () => {
|
|
51
|
-
const { codeToHast, hastToHtml } = await import("shiki");
|
|
52
|
-
const themes = syntaxThemes.value;
|
|
53
|
-
|
|
54
|
-
return Promise.all(
|
|
55
|
-
props.tabs.map(async (tab) => {
|
|
56
|
-
const lang = tab.language ?? filenameExtension(tab.filename) ?? "text";
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const root = await codeToHast(tab.code, {
|
|
60
|
-
lang,
|
|
61
|
-
themes,
|
|
62
|
-
});
|
|
63
|
-
const pre = root.children.find((node) => node.type === "element");
|
|
64
|
-
const code = pre?.children.find((node) => node.type === "element");
|
|
65
|
-
if (!code) return plainLines(tab.code);
|
|
66
|
-
|
|
67
|
-
return hastToHtml({ type: "root" as const, children: code.children });
|
|
68
|
-
} catch {
|
|
69
|
-
return plainLines(tab.code);
|
|
70
|
-
}
|
|
71
|
-
}),
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
</script>
|
|
75
|
-
|
|
76
|
-
<template>
|
|
77
|
-
<div class="site-hero-code content-codegroup not-prose group my-0 shadow-md">
|
|
78
|
-
<div class="content-codegroup-header">
|
|
79
|
-
<div v-if="tabs.length > 1" class="content-codegroup-tabs" role="tablist">
|
|
80
|
-
<button
|
|
81
|
-
v-for="(tab, index) in tabs"
|
|
82
|
-
:key="tab.label"
|
|
83
|
-
type="button"
|
|
84
|
-
role="tab"
|
|
85
|
-
:aria-selected="activeIndex === index ? 'true' : 'false'"
|
|
86
|
-
:class="
|
|
87
|
-
cn('content-codegroup-tab', activeIndex === index && 'content-codegroup-tab-active')
|
|
88
|
-
"
|
|
89
|
-
@click="activeIndex = index"
|
|
90
|
-
>
|
|
91
|
-
<Icon :name="tabIcon(tab)" class="content-codegroup-tab-icon" aria-hidden="true" />
|
|
92
|
-
{{ tab.label }}
|
|
93
|
-
</button>
|
|
94
|
-
</div>
|
|
95
|
-
<div
|
|
96
|
-
v-else-if="activeTab"
|
|
97
|
-
class="flex min-w-0 items-center gap-2 text-sm font-medium text-muted-foreground"
|
|
98
|
-
>
|
|
99
|
-
<Icon :name="tabIcon(activeTab)" class="content-codegroup-tab-icon" aria-hidden="true" />
|
|
100
|
-
<span class="truncate">{{ activeTab.label }}</span>
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
<div class="content-codegroup-panels grid">
|
|
104
|
-
<div
|
|
105
|
-
v-for="(tab, index) in tabs"
|
|
106
|
-
:key="tab.label"
|
|
107
|
-
role="tabpanel"
|
|
108
|
-
class="col-start-1 row-start-1 min-w-0"
|
|
109
|
-
:class="activeIndex === index ? '' : 'invisible'"
|
|
110
|
-
:inert="activeIndex !== index"
|
|
111
|
-
>
|
|
112
|
-
<figure
|
|
113
|
-
dir="ltr"
|
|
114
|
-
data-fd-codeblock
|
|
115
|
-
class="content-codeblock group shiki not-prose content-codeblock-in-group relative"
|
|
116
|
-
>
|
|
117
|
-
<div
|
|
118
|
-
role="region"
|
|
119
|
-
tabindex="0"
|
|
120
|
-
:aria-label="`${t('docs.codeSample')}: ${tab.label} (${regionId}-${index})`"
|
|
121
|
-
class="fd-scroll-container max-h-[600px] overflow-auto focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-inset"
|
|
122
|
-
>
|
|
123
|
-
<!-- Shiki output over site-owner config code, never end-user input. -->
|
|
124
|
-
<!-- eslint-disable vue/no-v-html -->
|
|
125
|
-
<pre
|
|
126
|
-
class="w-max min-w-full"
|
|
127
|
-
><code v-html="highlighted?.[index] ?? plainLines(tab.code)" /></pre>
|
|
128
|
-
<!-- eslint-enable vue/no-v-html -->
|
|
129
|
-
</div>
|
|
130
|
-
</figure>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
</div>
|
|
134
|
-
</template>
|
|
135
|
-
|
|
136
|
-
<style scoped>
|
|
137
|
-
/* Hero scale: same chrome as docs code groups, one step larger. */
|
|
138
|
-
.site-hero-code {
|
|
139
|
-
padding: 0.25rem 0.375rem 0.375rem;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.site-hero-code .content-codegroup-header {
|
|
143
|
-
padding: 0.375rem 0.375rem 0.25rem 0.625rem;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.site-hero-code .content-codegroup-tabs {
|
|
147
|
-
gap: 0.375rem;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.site-hero-code .content-codegroup-tab {
|
|
151
|
-
height: 2.125rem;
|
|
152
|
-
gap: 0.5rem;
|
|
153
|
-
padding: 0 0.625rem 0.4375rem;
|
|
154
|
-
font-size: 0.9375rem;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.site-hero-code .content-codegroup-tab-icon {
|
|
158
|
-
width: 1rem;
|
|
159
|
-
height: 1rem;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.site-hero-code .content-codeblock pre {
|
|
163
|
-
padding-top: 1.125rem;
|
|
164
|
-
padding-bottom: 1.125rem;
|
|
165
|
-
font-size: 0.875rem;
|
|
166
|
-
line-height: 1.75;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.site-hero-code :deep([data-fd-codeblock] pre code .line) {
|
|
170
|
-
padding-right: 1.5rem;
|
|
171
|
-
padding-left: 1.5rem;
|
|
172
|
-
}
|
|
173
|
-
</style>
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { defineAsyncComponent, onBeforeUnmount, onMounted, watch } from "vue";
|
|
3
|
-
import { useRoute } from "#imports";
|
|
4
|
-
import SiteSearchPageHighlight from "#ginko-docs/features/search/components/SiteSearchPageHighlight.vue";
|
|
5
|
-
import { useCommandCenterState } from "#ginko-docs/features/search/useCommandCenter";
|
|
6
|
-
|
|
7
|
-
const SiteCommandCenter = defineAsyncComponent(
|
|
8
|
-
() => import("#ginko-docs/features/search/components/SiteCommandCenter.vue"),
|
|
9
|
-
);
|
|
10
|
-
const route = useRoute();
|
|
11
|
-
const { open, openCommandCenter, closeCommandCenter } = useCommandCenterState();
|
|
12
|
-
|
|
13
|
-
watch(
|
|
14
|
-
() => route.fullPath,
|
|
15
|
-
() => closeCommandCenter(),
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
function handleGlobalShortcut(event: KeyboardEvent) {
|
|
19
|
-
const element = event.target as HTMLElement | null;
|
|
20
|
-
const typing =
|
|
21
|
-
element instanceof HTMLElement &&
|
|
22
|
-
(element.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(element.tagName));
|
|
23
|
-
|
|
24
|
-
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
25
|
-
event.preventDefault();
|
|
26
|
-
openCommandCenter();
|
|
27
|
-
} else if (!typing && event.key === "/") {
|
|
28
|
-
event.preventDefault();
|
|
29
|
-
openCommandCenter();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
onMounted(() => window.addEventListener("keydown", handleGlobalShortcut));
|
|
34
|
-
onBeforeUnmount(() => window.removeEventListener("keydown", handleGlobalShortcut));
|
|
35
|
-
</script>
|
|
36
|
-
|
|
37
|
-
<template>
|
|
38
|
-
<ClientOnly>
|
|
39
|
-
<SiteCommandCenter v-if="open" />
|
|
40
|
-
<SiteSearchPageHighlight />
|
|
41
|
-
</ClientOnly>
|
|
42
|
-
</template>
|