@baybreezy/docd 0.0.1
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/app/app.config.ts +239 -0
- package/app/app.vue +19 -0
- package/app/assets/css/tailwind.css +335 -0
- package/app/assets/css/theme.css +1171 -0
- package/app/components/OgImage/Docs.takumi.vue +69 -0
- package/app/components/OgImage/Landing.takumi.vue +64 -0
- package/app/components/OgImage/Site.takumi.vue +32 -0
- package/app/components/Ui/Avatar/Avatar.vue +75 -0
- package/app/components/Ui/Avatar/Badge.vue +34 -0
- package/app/components/Ui/Avatar/Fallback.vue +30 -0
- package/app/components/Ui/Avatar/Group.vue +29 -0
- package/app/components/Ui/Avatar/GroupCount.vue +29 -0
- package/app/components/Ui/Avatar/Image.vue +29 -0
- package/app/components/Ui/Badge.vue +86 -0
- package/app/components/Ui/BadgeGroup.vue +314 -0
- package/app/components/Ui/BgPattern/Grid.vue +137 -0
- package/app/components/Ui/BorderBeam.vue +97 -0
- package/app/components/Ui/Breadcrumbs.vue +103 -0
- package/app/components/Ui/Button.vue +169 -0
- package/app/components/Ui/ButtonGroup/ButtonGroup.vue +54 -0
- package/app/components/Ui/ButtonGroup/Separator.vue +38 -0
- package/app/components/Ui/ButtonGroup/Text.vue +33 -0
- package/app/components/Ui/Card/Action.vue +31 -0
- package/app/components/Ui/Card/Card.vue +58 -0
- package/app/components/Ui/Card/Content.vue +34 -0
- package/app/components/Ui/Card/Description.vue +36 -0
- package/app/components/Ui/Card/Footer.vue +31 -0
- package/app/components/Ui/Card/Header.vue +31 -0
- package/app/components/Ui/Card/Title.vue +37 -0
- package/app/components/Ui/Collapsible/Collapsible.vue +16 -0
- package/app/components/Ui/Collapsible/Content.vue +27 -0
- package/app/components/Ui/Collapsible/Trigger.vue +13 -0
- package/app/components/Ui/Command/Command.vue +115 -0
- package/app/components/Ui/Command/Dialog.vue +65 -0
- package/app/components/Ui/Command/Empty.vue +32 -0
- package/app/components/Ui/Command/Footer.vue +40 -0
- package/app/components/Ui/Command/Group.vue +58 -0
- package/app/components/Ui/Command/Input.vue +57 -0
- package/app/components/Ui/Command/Item.vue +102 -0
- package/app/components/Ui/Command/List.vue +33 -0
- package/app/components/Ui/Command/Separator.vue +30 -0
- package/app/components/Ui/Command/Shortcut.vue +28 -0
- package/app/components/Ui/Container.vue +46 -0
- package/app/components/Ui/ContextMenu/Arrow.vue +43 -0
- package/app/components/Ui/ContextMenu/CheckboxItem.vue +42 -0
- package/app/components/Ui/ContextMenu/Content.vue +53 -0
- package/app/components/Ui/ContextMenu/ContextMenu.vue +14 -0
- package/app/components/Ui/ContextMenu/Group.vue +12 -0
- package/app/components/Ui/ContextMenu/Item.vue +65 -0
- package/app/components/Ui/ContextMenu/ItemIndicator.vue +30 -0
- package/app/components/Ui/ContextMenu/Label.vue +35 -0
- package/app/components/Ui/ContextMenu/Portal.vue +12 -0
- package/app/components/Ui/ContextMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/ContextMenu/RadioItem.vue +40 -0
- package/app/components/Ui/ContextMenu/Separator.vue +25 -0
- package/app/components/Ui/ContextMenu/Shortcut.vue +30 -0
- package/app/components/Ui/ContextMenu/Sub.vue +15 -0
- package/app/components/Ui/ContextMenu/SubContent.vue +55 -0
- package/app/components/Ui/ContextMenu/SubTrigger.vue +42 -0
- package/app/components/Ui/ContextMenu/Trigger.vue +12 -0
- package/app/components/Ui/Dialog/Close.vue +12 -0
- package/app/components/Ui/Dialog/Content.vue +79 -0
- package/app/components/Ui/Dialog/Description.vue +29 -0
- package/app/components/Ui/Dialog/Dialog.vue +14 -0
- package/app/components/Ui/Dialog/Footer.vue +32 -0
- package/app/components/Ui/Dialog/Header.vue +32 -0
- package/app/components/Ui/Dialog/Overlay.vue +25 -0
- package/app/components/Ui/Dialog/Portal.vue +12 -0
- package/app/components/Ui/Dialog/Title.vue +29 -0
- package/app/components/Ui/Dialog/Trigger.vue +12 -0
- package/app/components/Ui/Divider.vue +102 -0
- package/app/components/Ui/Drawer/Close.vue +12 -0
- package/app/components/Ui/Drawer/Content.vue +67 -0
- package/app/components/Ui/Drawer/Description.vue +33 -0
- package/app/components/Ui/Drawer/Drawer.vue +17 -0
- package/app/components/Ui/Drawer/Footer.vue +35 -0
- package/app/components/Ui/Drawer/Header.vue +34 -0
- package/app/components/Ui/Drawer/Overlay.vue +20 -0
- package/app/components/Ui/Drawer/Portal.vue +12 -0
- package/app/components/Ui/Drawer/Title.vue +35 -0
- package/app/components/Ui/Drawer/Trigger.vue +12 -0
- package/app/components/Ui/DropdownMenu/Arrow.vue +43 -0
- package/app/components/Ui/DropdownMenu/CheckboxItem.vue +41 -0
- package/app/components/Ui/DropdownMenu/Content.vue +56 -0
- package/app/components/Ui/DropdownMenu/DropdownMenu.vue +15 -0
- package/app/components/Ui/DropdownMenu/Group.vue +12 -0
- package/app/components/Ui/DropdownMenu/Item.vue +64 -0
- package/app/components/Ui/DropdownMenu/ItemIndicator.vue +32 -0
- package/app/components/Ui/DropdownMenu/Label.vue +34 -0
- package/app/components/Ui/DropdownMenu/Portal.vue +12 -0
- package/app/components/Ui/DropdownMenu/RadioGroup.vue +16 -0
- package/app/components/Ui/DropdownMenu/RadioItem.vue +40 -0
- package/app/components/Ui/DropdownMenu/Separator.vue +25 -0
- package/app/components/Ui/DropdownMenu/Shortcut.vue +44 -0
- package/app/components/Ui/DropdownMenu/Sub.vue +14 -0
- package/app/components/Ui/DropdownMenu/SubContent.vue +56 -0
- package/app/components/Ui/DropdownMenu/SubTrigger.vue +47 -0
- package/app/components/Ui/DropdownMenu/Trigger.vue +12 -0
- package/app/components/Ui/FancyIcon.vue +243 -0
- package/app/components/Ui/GradientDivider.vue +26 -0
- package/app/components/Ui/IframeLazy.vue +97 -0
- package/app/components/Ui/Input.vue +97 -0
- package/app/components/Ui/InputGroup/Addon.vue +66 -0
- package/app/components/Ui/InputGroup/Button.vue +50 -0
- package/app/components/Ui/InputGroup/Input.vue +25 -0
- package/app/components/Ui/InputGroup/InputGroup.vue +44 -0
- package/app/components/Ui/InputGroup/Text.vue +33 -0
- package/app/components/Ui/InputGroup/Textarea.vue +25 -0
- package/app/components/Ui/Kbd/Group.vue +32 -0
- package/app/components/Ui/Kbd/Kbd.vue +62 -0
- package/app/components/Ui/Label.vue +40 -0
- package/app/components/Ui/Popover/Anchor.vue +13 -0
- package/app/components/Ui/Popover/Arrow.vue +42 -0
- package/app/components/Ui/Popover/Close.vue +13 -0
- package/app/components/Ui/Popover/Content.vue +55 -0
- package/app/components/Ui/Popover/Popover.vue +15 -0
- package/app/components/Ui/Popover/Portal.vue +13 -0
- package/app/components/Ui/Popover/Trigger.vue +12 -0
- package/app/components/Ui/Popover/X.vue +38 -0
- package/app/components/Ui/ScrollArea/Corner.vue +12 -0
- package/app/components/Ui/ScrollArea/ScrollArea.vue +38 -0
- package/app/components/Ui/ScrollArea/Scrollbar.vue +39 -0
- package/app/components/Ui/ScrollArea/Thumb.vue +39 -0
- package/app/components/Ui/ScrollArea/Viewport.vue +27 -0
- package/app/components/Ui/Scrollspy.vue +61 -0
- package/app/components/Ui/Separator.vue +40 -0
- package/app/components/Ui/Skeleton.vue +39 -0
- package/app/components/Ui/Sonner.vue +25 -0
- package/app/components/Ui/Tabs/Content.vue +27 -0
- package/app/components/Ui/Tabs/Indicator.vue +37 -0
- package/app/components/Ui/Tabs/List.vue +37 -0
- package/app/components/Ui/Tabs/Tabs.vue +25 -0
- package/app/components/Ui/Tabs/Trigger.vue +41 -0
- package/app/components/Ui/Textarea.vue +81 -0
- package/app/components/Ui/Tooltip/Arrow.vue +30 -0
- package/app/components/Ui/Tooltip/Content.vue +50 -0
- package/app/components/Ui/Tooltip/Portal.vue +12 -0
- package/app/components/Ui/Tooltip/Provider.vue +14 -0
- package/app/components/Ui/Tooltip/Tooltip.vue +25 -0
- package/app/components/Ui/Tooltip/Trigger.vue +12 -0
- package/app/components/Ui/Tree/Item.vue +15 -0
- package/app/components/Ui/Tree/Tree.vue +15 -0
- package/app/components/Ui/Tree/Virtualizer.vue +13 -0
- package/app/components/component-api/ComponentApiSection.vue +135 -0
- package/app/components/content/docs/Callout/DocsCallout.vue +99 -0
- package/app/components/content/prose/ProseA.global.vue +31 -0
- package/app/components/content/prose/ProseBlockquote.global.vue +28 -0
- package/app/components/content/prose/ProseCallout.global.vue +306 -0
- package/app/components/content/prose/ProseCard.global.vue +100 -0
- package/app/components/content/prose/ProseCodeCollapse.global.vue +125 -0
- package/app/components/content/prose/ProseCodeCopy.global.vue +67 -0
- package/app/components/content/prose/ProseCodeGroup.global.vue +104 -0
- package/app/components/content/prose/ProseCodeTree.global.vue +277 -0
- package/app/components/content/prose/ProseCollapsible.global.vue +64 -0
- package/app/components/content/prose/ProseColorModeImage.global.vue +58 -0
- package/app/components/content/prose/ProseComponentApi.global.vue +104 -0
- package/app/components/content/prose/ProseComponentEvents.global.vue +27 -0
- package/app/components/content/prose/ProseComponentExposed.global.vue +27 -0
- package/app/components/content/prose/ProseComponentProps.global.vue +27 -0
- package/app/components/content/prose/ProseComponentSlots.global.vue +27 -0
- package/app/components/content/prose/ProseEm.global.vue +5 -0
- package/app/components/content/prose/ProseField.global.vue +118 -0
- package/app/components/content/prose/ProseFieldGroup.global.vue +62 -0
- package/app/components/content/prose/ProseH1.global.vue +58 -0
- package/app/components/content/prose/ProseH2.global.vue +58 -0
- package/app/components/content/prose/ProseH3.global.vue +58 -0
- package/app/components/content/prose/ProseH4.global.vue +58 -0
- package/app/components/content/prose/ProseH5.global.vue +47 -0
- package/app/components/content/prose/ProseH6.global.vue +47 -0
- package/app/components/content/prose/ProseHr.global.vue +33 -0
- package/app/components/content/prose/ProseIconList.global.vue +113 -0
- package/app/components/content/prose/ProseImg.global.vue +119 -0
- package/app/components/content/prose/ProseLead.global.vue +28 -0
- package/app/components/content/prose/ProseLi.global.vue +25 -0
- package/app/components/content/prose/ProseMermaid.global.vue +127 -0
- package/app/components/content/prose/ProseOl.global.vue +25 -0
- package/app/components/content/prose/ProseP.global.vue +25 -0
- package/app/components/content/prose/ProsePmInstall.global.vue +60 -0
- package/app/components/content/prose/ProsePmRun.global.vue +51 -0
- package/app/components/content/prose/ProsePmX.global.vue +56 -0
- package/app/components/content/prose/ProsePre.global.vue +181 -0
- package/app/components/content/prose/ProseShowCase.global.vue +56 -0
- package/app/components/content/prose/ProseSmartIcon.global.vue +54 -0
- package/app/components/content/prose/ProseStep.global.vue +22 -0
- package/app/components/content/prose/ProseSteps.global.vue +22 -0
- package/app/components/content/prose/ProseStrong.global.vue +34 -0
- package/app/components/content/prose/ProseTable.global.vue +34 -0
- package/app/components/content/prose/ProseTabs.global.vue +104 -0
- package/app/components/content/prose/ProseTabsInner.global.vue +274 -0
- package/app/components/content/prose/ProseTbody.global.vue +5 -0
- package/app/components/content/prose/ProseTd.global.vue +25 -0
- package/app/components/content/prose/ProseTh.global.vue +25 -0
- package/app/components/content/prose/ProseThead.global.vue +9 -0
- package/app/components/content/prose/ProseTr.global.vue +35 -0
- package/app/components/content/prose/ProseUl.global.vue +25 -0
- package/app/components/docs/DocsExtraStuff.vue +39 -0
- package/app/components/docs/DocsGithubLink.vue +11 -0
- package/app/components/docs/DocsHeader.vue +3 -0
- package/app/components/docs/DocsLogo.vue +74 -0
- package/app/components/docs/DocsMobileBar.vue +26 -0
- package/app/components/docs/DocsMobileNav.vue +49 -0
- package/app/components/docs/DocsNav.vue +161 -0
- package/app/components/docs/DocsPageTransition.vue +32 -0
- package/app/components/docs/DocsSearch.vue +220 -0
- package/app/components/docs/DocsSearchButton.vue +20 -0
- package/app/components/docs/DocsThemeCustomizer.vue +229 -0
- package/app/components/docs/DocsThemeToggler.vue +98 -0
- package/app/components/docs/DocsTocRail.vue +162 -0
- package/app/components/docs/DocsToclink.vue +52 -0
- package/app/components/docs/DocsZigZagRail.vue +70 -0
- package/app/components/landing/Hero.vue +222 -0
- package/app/components/page/PageFooter.vue +49 -0
- package/app/components/page/PageHeader.vue +68 -0
- package/app/components/page/PageHeaderLinks.vue +138 -0
- package/app/components/page/PageMobileToc.vue +162 -0
- package/app/components/page/PageToc.vue +33 -0
- package/app/composables/useDefaultSlotItems.ts +90 -0
- package/app/composables/useDocPage.ts +66 -0
- package/app/composables/useDocSearch.ts +23 -0
- package/app/composables/useDocd.ts +107 -0
- package/app/composables/useLogoAssets.ts +319 -0
- package/app/composables/usePm.ts +73 -0
- package/app/composables/useProseComponentMeta.ts +30 -0
- package/app/composables/useScrollspy.ts +327 -0
- package/app/composables/useSeo.ts +240 -0
- package/app/composables/useUIConfig.ts +36 -0
- package/app/layouts/default.vue +5 -0
- package/app/layouts/docs.vue +64 -0
- package/app/middleware/colorMode.global.ts +8 -0
- package/app/middleware/doc-page-layout.ts +11 -0
- package/app/middleware/landing-page-layout.ts +11 -0
- package/app/pages/[...slug].vue +63 -0
- package/app/plugins/mermaid.client.ts +35 -0
- package/app/plugins/navigation-direction.client.ts +15 -0
- package/app/plugins/theme-restore.client.ts +7 -0
- package/app/templates/landing.vue +40 -0
- package/app/transitions/presets.ts +71 -0
- package/app/types/prose-component-meta.d.ts +4 -0
- package/app/utils/og.ts +26 -0
- package/app/utils/prerender.ts +9 -0
- package/app/utils/translucent.ts +56 -0
- package/app/utils/tw-helper.ts +4 -0
- package/content.config.ts +81 -0
- package/index.d.ts +25 -0
- package/modules/config.ts +89 -0
- package/modules/custom-icons.ts +17 -0
- package/modules/markdown-rewrite.ts +96 -0
- package/modules/prose-component-meta.ts +150 -0
- package/modules/routing.ts +22 -0
- package/modules/skills/index.ts +167 -0
- package/modules/skills/runtime/server/routes/skills-files.ts +49 -0
- package/modules/skills/runtime/server/routes/skills-index.ts +8 -0
- package/nuxt.config.ts +96 -0
- package/nuxt.schema.ts +288 -0
- package/package.json +95 -0
- package/server/mcp/tools/get-page.ts +66 -0
- package/server/mcp/tools/list-pages.ts +61 -0
- package/server/routes/sitemap.xml.ts +73 -0
- package/server/utils/content.ts +3 -0
- package/tailwind.config.cjs +55 -0
- package/utils/component-api-content.ts +467 -0
- package/utils/component-api-discovery.ts +165 -0
- package/utils/component-api-schema.ts +28 -0
- package/utils/component-api.ts +243 -0
- package/utils/generate-prose-component-meta.ts +303 -0
- package/utils/git.ts +123 -0
- package/utils/meta.ts +40 -0
- package/utils/pages.ts +21 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="not-prose mb-10">
|
|
3
|
+
<p
|
|
4
|
+
v-if="headline && !hasFancyIcon"
|
|
5
|
+
class="mb-4 font-bold tracking-tight text-primary capitalize"
|
|
6
|
+
>
|
|
7
|
+
{{ headline }}
|
|
8
|
+
</p>
|
|
9
|
+
<div class="flex flex-col gap-4">
|
|
10
|
+
<UiFancyIcon
|
|
11
|
+
v-if="page?.fancyIcon && page.fancyIcon.icon"
|
|
12
|
+
:theme="page.fancyIcon.theme ?? 'modern-neue'"
|
|
13
|
+
:color="page.fancyIcon.color ?? 'primary'"
|
|
14
|
+
:size="page.fancyIcon.size ?? 'lg'"
|
|
15
|
+
class="shrink-0 text-foreground/70"
|
|
16
|
+
:icon="page.fancyIcon.icon"
|
|
17
|
+
/>
|
|
18
|
+
<UiFancyIcon
|
|
19
|
+
v-else-if="page?.navigation && page.navigation.icon && typeof page.navigation === 'object'"
|
|
20
|
+
theme="modern-neue"
|
|
21
|
+
size="lg"
|
|
22
|
+
class="shrink-0 text-foreground/70"
|
|
23
|
+
:icon="page.navigation.icon"
|
|
24
|
+
/>
|
|
25
|
+
<div class="w-full grow">
|
|
26
|
+
<div class="flex flex-wrap items-start justify-between gap-4">
|
|
27
|
+
<h1 class="text-4xl leading-none font-bold">{{ page?.title }}</h1>
|
|
28
|
+
<PageHeaderLinks />
|
|
29
|
+
</div>
|
|
30
|
+
<p v-if="page?.description" class="mt-2 mb-4 text-lg text-muted-foreground">
|
|
31
|
+
{{ page?.description }}
|
|
32
|
+
</p>
|
|
33
|
+
<div v-if="page?.links && page?.links?.length" class="not-prose mb-6 flex flex-wrap gap-2">
|
|
34
|
+
<template v-for="(link, i) in page?.links" :key="i">
|
|
35
|
+
<UiBadge
|
|
36
|
+
:href="link?.href"
|
|
37
|
+
target="_blank"
|
|
38
|
+
variant="outline"
|
|
39
|
+
size="md"
|
|
40
|
+
class="transition-colors duration-300"
|
|
41
|
+
>
|
|
42
|
+
<Icon
|
|
43
|
+
v-if="link?.icon"
|
|
44
|
+
:name="link.icon"
|
|
45
|
+
class="size-3.5 shrink-0 text-muted-foreground"
|
|
46
|
+
/>{{ link?.title }}
|
|
47
|
+
</UiBadge>
|
|
48
|
+
</template>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<UiDivider :type="isDashed ? 'dashed' : 'solid'" />
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script lang="ts" setup>
|
|
57
|
+
const { headline, page } = await useDocPage();
|
|
58
|
+
const { isDashed } = useDocd();
|
|
59
|
+
|
|
60
|
+
const hasFancyIcon = computed(() => {
|
|
61
|
+
return (
|
|
62
|
+
(page.value?.fancyIcon && page.value.fancyIcon.icon) ||
|
|
63
|
+
(page.value?.navigation &&
|
|
64
|
+
page.value.navigation.icon &&
|
|
65
|
+
typeof page.value.navigation === "object")
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
</script>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<UiButtonGroup>
|
|
3
|
+
<UiTooltip>
|
|
4
|
+
<UiTooltipTrigger as-child>
|
|
5
|
+
<UiButton variant="secondary" size="sm" :disabled="copied" @click="copyPage()">
|
|
6
|
+
<Icon :name="copied ? 'lucide:check' : 'lucide:copy'" class="size-3.5" />
|
|
7
|
+
<span class="sr-only">{{ copied ? "Copied!" : "Copy Page" }}</span>
|
|
8
|
+
</UiButton>
|
|
9
|
+
</UiTooltipTrigger>
|
|
10
|
+
<UiTooltipContent side="bottom">
|
|
11
|
+
<p class="text-center">
|
|
12
|
+
{{
|
|
13
|
+
copied
|
|
14
|
+
? "Markdown link copied to clipboard!"
|
|
15
|
+
: "Copy the markdown link for this page to your clipboard."
|
|
16
|
+
}}
|
|
17
|
+
</p>
|
|
18
|
+
</UiTooltipContent>
|
|
19
|
+
</UiTooltip>
|
|
20
|
+
<UiButtonGroupSeparator />
|
|
21
|
+
<UiTooltip>
|
|
22
|
+
<UiDropdownMenu>
|
|
23
|
+
<UiTooltipTrigger as-child>
|
|
24
|
+
<UiDropdownMenuTrigger as-child>
|
|
25
|
+
<UiButton variant="secondary" size="icon-sm">
|
|
26
|
+
<Icon name="lucide:chevron-down" />
|
|
27
|
+
<span class="sr-only">More options for header links </span>
|
|
28
|
+
</UiButton>
|
|
29
|
+
</UiDropdownMenuTrigger>
|
|
30
|
+
</UiTooltipTrigger>
|
|
31
|
+
<UiTooltipContent side="bottom">
|
|
32
|
+
<p class="text-center">More options for this page</p>
|
|
33
|
+
</UiTooltipContent>
|
|
34
|
+
<UiDropdownMenuContent align="end" :collision-padding="10" class="w-60" translucent>
|
|
35
|
+
<UiDropdownMenuGroup class="flex flex-col gap-0.5">
|
|
36
|
+
<template v-for="(item, i) in items" :key="i">
|
|
37
|
+
<UiDropdownMenuItem
|
|
38
|
+
v-if="item.action"
|
|
39
|
+
:icon="item.icon"
|
|
40
|
+
:title="item.label"
|
|
41
|
+
@select="item.action()"
|
|
42
|
+
/>
|
|
43
|
+
<UiDropdownMenuItem v-else-if="item.to" :text-value="item.label" as-child>
|
|
44
|
+
<NuxtLink
|
|
45
|
+
:to="item.to"
|
|
46
|
+
target="_blank"
|
|
47
|
+
class="flex w-full cursor-pointer items-center gap-2"
|
|
48
|
+
>
|
|
49
|
+
<Icon :name="item.icon" />
|
|
50
|
+
<p v-html="item.label" />
|
|
51
|
+
<Icon name="lucide:arrow-up-right" class="ml-auto text-muted-foreground" />
|
|
52
|
+
</NuxtLink>
|
|
53
|
+
</UiDropdownMenuItem>
|
|
54
|
+
<UiDropdownMenuSeparator v-else-if="item.separator" />
|
|
55
|
+
</template>
|
|
56
|
+
</UiDropdownMenuGroup>
|
|
57
|
+
</UiDropdownMenuContent>
|
|
58
|
+
</UiDropdownMenu>
|
|
59
|
+
</UiTooltip>
|
|
60
|
+
</UiButtonGroup>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script lang="ts" setup>
|
|
64
|
+
const route = useRoute();
|
|
65
|
+
const appBaseURL = useRuntimeConfig().app?.baseURL || "/";
|
|
66
|
+
|
|
67
|
+
const { copy, copied } = useClipboard();
|
|
68
|
+
const markdownLink = computed(() => `${window?.location?.origin}/raw${route.path}.md`);
|
|
69
|
+
const mcpServerUrl = computed(() => `${window?.location?.origin}${appBaseURL}mcp`);
|
|
70
|
+
|
|
71
|
+
const copyPage = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const response = await $fetch<string>(`/raw/${route.path}.md`);
|
|
74
|
+
if (response) {
|
|
75
|
+
copy(response);
|
|
76
|
+
useSonner.success("Markdown Link Copied", {
|
|
77
|
+
description: "The markdown link for this page has been copied to your clipboard.",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
useSonner.error("Link Unavailable", {
|
|
82
|
+
description: "This page does not have a markdown link available.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const items = [
|
|
88
|
+
{
|
|
89
|
+
label: "Copy Markdown Link",
|
|
90
|
+
icon: "lucide:link",
|
|
91
|
+
action: () => {
|
|
92
|
+
copy(markdownLink.value);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: "Copy MCP Server URL",
|
|
97
|
+
icon: "lucide:cpu",
|
|
98
|
+
action: () => {
|
|
99
|
+
copy(mcpServerUrl.value);
|
|
100
|
+
useSonner.success("MCP URL Copied", {
|
|
101
|
+
description: "The MCP server URL has been copied to your clipboard.",
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{ separator: true },
|
|
106
|
+
{
|
|
107
|
+
label: "View Markdown Page",
|
|
108
|
+
icon: "simple-icons:markdown",
|
|
109
|
+
to: `/raw${route.path}.md`,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "Open in ChatGPT",
|
|
113
|
+
icon: "simple-icons:openai",
|
|
114
|
+
to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: "Open in Claude",
|
|
118
|
+
icon: "simple-icons:claude",
|
|
119
|
+
to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: "Open in T3",
|
|
123
|
+
icon: "lucide:message-circle",
|
|
124
|
+
to: `https://t3.chat/new?q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
|
|
125
|
+
},
|
|
126
|
+
{ separator: true },
|
|
127
|
+
{
|
|
128
|
+
label: `View llms.txt File`,
|
|
129
|
+
icon: "lucide:bot",
|
|
130
|
+
to: `/llms.txt`,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
label: `View llms-full.txt File`,
|
|
134
|
+
icon: "lucide:bot",
|
|
135
|
+
to: `/llms-full.txt`,
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
</script>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="hasToc"
|
|
4
|
+
class="sticky top-[calc(var(--header-height)*2)] z-40 tablet:top-[calc(var(--header-height))] xl:hidden"
|
|
5
|
+
>
|
|
6
|
+
<!-- Always-mounted hidden container — keeps scrollspy alive regardless of popover state -->
|
|
7
|
+
<div
|
|
8
|
+
ref="spyRoot"
|
|
9
|
+
aria-hidden="true"
|
|
10
|
+
class="pointer-events-none absolute size-0 overflow-hidden opacity-0"
|
|
11
|
+
>
|
|
12
|
+
<DocsToclink :links="toc!.links" />
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<UiPopover v-model:open="isOpen">
|
|
16
|
+
<UiPopoverTrigger as-child>
|
|
17
|
+
<button
|
|
18
|
+
class="flex w-full cursor-pointer items-center gap-2.5 border-b bg-background/95 px-4 backdrop-blur-sm transition-colors hover:bg-muted/50"
|
|
19
|
+
:class="isDashed ? 'border-dashed' : ''"
|
|
20
|
+
style="height: 2.5rem"
|
|
21
|
+
>
|
|
22
|
+
<!-- Scroll progress ring -->
|
|
23
|
+
<svg
|
|
24
|
+
width="18"
|
|
25
|
+
height="18"
|
|
26
|
+
viewBox="0 0 18 18"
|
|
27
|
+
class="shrink-0 -rotate-90"
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
>
|
|
30
|
+
<circle
|
|
31
|
+
cx="9"
|
|
32
|
+
cy="9"
|
|
33
|
+
r="7"
|
|
34
|
+
fill="none"
|
|
35
|
+
stroke="currentColor"
|
|
36
|
+
stroke-width="2"
|
|
37
|
+
class="text-border"
|
|
38
|
+
/>
|
|
39
|
+
<circle
|
|
40
|
+
cx="9"
|
|
41
|
+
cy="9"
|
|
42
|
+
r="7"
|
|
43
|
+
fill="none"
|
|
44
|
+
:stroke-dasharray="circumference"
|
|
45
|
+
:stroke-dashoffset="circumference - scrollProgress * circumference"
|
|
46
|
+
stroke-width="2"
|
|
47
|
+
stroke-linecap="round"
|
|
48
|
+
stroke="currentColor"
|
|
49
|
+
class="text-primary transition-[stroke-dashoffset] duration-150 ease-out"
|
|
50
|
+
/>
|
|
51
|
+
</svg>
|
|
52
|
+
|
|
53
|
+
<span class="flex-1 truncate text-left text-sm">
|
|
54
|
+
<span v-if="activeHeadingText" class="font-medium text-foreground">
|
|
55
|
+
{{ activeHeadingText }}
|
|
56
|
+
</span>
|
|
57
|
+
<span v-else class="text-muted-foreground">On this page</span>
|
|
58
|
+
</span>
|
|
59
|
+
|
|
60
|
+
<Icon
|
|
61
|
+
name="lucide:chevron-down"
|
|
62
|
+
class="size-4 shrink-0 text-muted-foreground transition-transform duration-200"
|
|
63
|
+
:class="{ 'rotate-180': isOpen }"
|
|
64
|
+
/>
|
|
65
|
+
</button>
|
|
66
|
+
</UiPopoverTrigger>
|
|
67
|
+
|
|
68
|
+
<UiPopoverContent
|
|
69
|
+
align="start"
|
|
70
|
+
side="bottom"
|
|
71
|
+
:side-offset="0"
|
|
72
|
+
:collision-padding="0"
|
|
73
|
+
:avoid-collisions="false"
|
|
74
|
+
class="w-(--reka-popover-trigger-width) rounded-none border-x-0 border-t-0 p-0 shadow-sm"
|
|
75
|
+
translucent
|
|
76
|
+
:class="isDashed ? 'border-dashed' : ''"
|
|
77
|
+
>
|
|
78
|
+
<!-- Intercept link clicks to close the popover -->
|
|
79
|
+
<div @click.capture="onContentClick">
|
|
80
|
+
<UiScrollArea>
|
|
81
|
+
<div class="max-h-[50dvh] p-4">
|
|
82
|
+
<UiScrollspy smooth mode="multiple" :offset="120">
|
|
83
|
+
<DocsTocRail>
|
|
84
|
+
<DocsToclink :links="toc!.links" />
|
|
85
|
+
</DocsTocRail>
|
|
86
|
+
</UiScrollspy>
|
|
87
|
+
</div>
|
|
88
|
+
</UiScrollArea>
|
|
89
|
+
</div>
|
|
90
|
+
</UiPopoverContent>
|
|
91
|
+
</UiPopover>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<script lang="ts" setup>
|
|
96
|
+
type TocLink = {
|
|
97
|
+
text: string;
|
|
98
|
+
id: string;
|
|
99
|
+
depth: number;
|
|
100
|
+
children?: TocLink[];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const { page } = await useDocPage();
|
|
104
|
+
const { isDashed } = useDocd();
|
|
105
|
+
|
|
106
|
+
const toc = computed(() => page.value?.body?.toc as { links: TocLink[] } | undefined);
|
|
107
|
+
const hasToc = computed(() => !!toc.value?.links?.length && !page.value?.hideToc);
|
|
108
|
+
|
|
109
|
+
// --- Hidden, always-mounted scrollspy root so activeId persists when popover is closed ---
|
|
110
|
+
const spyRoot = ref<HTMLElement | null>(null);
|
|
111
|
+
const { activeId } = useScrollspy({ root: spyRoot, offset: 120, history: false, mode: "single" });
|
|
112
|
+
|
|
113
|
+
// Flatten toc links for lookup
|
|
114
|
+
function flattenLinks(links: TocLink[]): TocLink[] {
|
|
115
|
+
return links.flatMap((l) => [l, ...(l.children ? flattenLinks(l.children) : [])]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const activeHeadingText = computed(() => {
|
|
119
|
+
if (!toc.value?.links) return null;
|
|
120
|
+
const flat = flattenLinks(toc.value.links);
|
|
121
|
+
// Fall back to the first heading when the scrollspy hasn't fired yet
|
|
122
|
+
// (hard reload at top of page, or immediately after navigation).
|
|
123
|
+
const id = activeId.value ?? flat[0]?.id;
|
|
124
|
+
return flat.find((l) => l.id === id)?.text ?? null;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// --- Scroll progress ---
|
|
128
|
+
const scrollProgress = ref(0);
|
|
129
|
+
const circumference = 2 * Math.PI * 7;
|
|
130
|
+
|
|
131
|
+
if (import.meta.client) {
|
|
132
|
+
const updateProgress = () => {
|
|
133
|
+
const docH = document.documentElement.scrollHeight;
|
|
134
|
+
const winH = window.innerHeight;
|
|
135
|
+
const max = docH - winH;
|
|
136
|
+
scrollProgress.value = max > 0 ? Math.min(1, window.scrollY / max) : 0;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
useEventListener(window, "scroll", updateProgress, { passive: true });
|
|
140
|
+
onMounted(updateProgress);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// --- Popover state ---
|
|
144
|
+
const isOpen = ref(false);
|
|
145
|
+
|
|
146
|
+
// Reset on navigation
|
|
147
|
+
const route = useRoute();
|
|
148
|
+
watch(
|
|
149
|
+
() => route.path,
|
|
150
|
+
() => {
|
|
151
|
+
isOpen.value = false;
|
|
152
|
+
scrollProgress.value = 0;
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
function onContentClick(e: MouseEvent) {
|
|
157
|
+
const target = e.target as HTMLElement;
|
|
158
|
+
if (target.closest("a")) {
|
|
159
|
+
isOpen.value = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<aside
|
|
3
|
+
v-if="toc && toc.links && toc.links.length && !page?.hideToc"
|
|
4
|
+
class="sticky top-14 z-20 hidden h-[calc(100dvh-calc(var(--header-height)+20px))] xl:block"
|
|
5
|
+
>
|
|
6
|
+
<UiScrollArea type="auto" class="h-full">
|
|
7
|
+
<div class="flex flex-col gap-5 p-5">
|
|
8
|
+
<div class="flex items-center gap-2.5 text-muted-foreground">
|
|
9
|
+
<Icon :name="tocIcon" class="size-4" />
|
|
10
|
+
<p class="text-sm font-semibold">{{ tocTitle }}</p>
|
|
11
|
+
</div>
|
|
12
|
+
<UiScrollspy smooth mode="multiple" :offset="80">
|
|
13
|
+
<DocsTocRail>
|
|
14
|
+
<DocsToclink :links="toc.links" />
|
|
15
|
+
</DocsTocRail>
|
|
16
|
+
</UiScrollspy>
|
|
17
|
+
|
|
18
|
+
<p class="text-sm font-semibold">Extra stuff</p>
|
|
19
|
+
<DocsExtraStuff />
|
|
20
|
+
</div>
|
|
21
|
+
</UiScrollArea>
|
|
22
|
+
</aside>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script lang="ts" setup>
|
|
26
|
+
const { page } = await useDocPage();
|
|
27
|
+
const { tocTitle, tocIcon } = useDocd();
|
|
28
|
+
|
|
29
|
+
const toc = computed(() => {
|
|
30
|
+
if (!page.value) return;
|
|
31
|
+
return page.value?.body?.toc;
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Comment, computed, Fragment, getCurrentInstance, isVNode } from "vue";
|
|
2
|
+
import type { Slots, VNode, VNodeArrayChildren } from "vue";
|
|
3
|
+
|
|
4
|
+
type SlotMetaContext = {
|
|
5
|
+
vnode: VNode;
|
|
6
|
+
index: number;
|
|
7
|
+
props: Record<string, any>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type SlotItem<TMeta = Record<string, any>> = {
|
|
11
|
+
vnode: VNode;
|
|
12
|
+
index: number;
|
|
13
|
+
key: VNode["key"];
|
|
14
|
+
props: Record<string, any>;
|
|
15
|
+
meta: TMeta;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type UseDefaultSlotItemsOptions<TMeta> = {
|
|
19
|
+
slots?: Slots;
|
|
20
|
+
filter?: (ctx: SlotMetaContext) => boolean;
|
|
21
|
+
mapMeta?: (ctx: SlotMetaContext) => TMeta;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Composable to extract and manage items from the default slot
|
|
26
|
+
*/
|
|
27
|
+
export function useDefaultSlotItems<TMeta = Record<string, any>>(
|
|
28
|
+
options: UseDefaultSlotItemsOptions<TMeta> = {}
|
|
29
|
+
) {
|
|
30
|
+
const instance = getCurrentInstance();
|
|
31
|
+
const slots = options.slots ?? instance?.slots;
|
|
32
|
+
|
|
33
|
+
const items = computed<SlotItem<TMeta>[]>(() => {
|
|
34
|
+
const rawChildren = slots?.default?.() ?? [];
|
|
35
|
+
const nodes = flattenVNodes(rawChildren);
|
|
36
|
+
|
|
37
|
+
return nodes
|
|
38
|
+
.map((vnode, index) => {
|
|
39
|
+
const props = (vnode.props ?? {}) as Record<string, any>;
|
|
40
|
+
const ctx: SlotMetaContext = { vnode, index, props };
|
|
41
|
+
|
|
42
|
+
if (options.filter && !options.filter(ctx)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
vnode,
|
|
48
|
+
index,
|
|
49
|
+
key: vnode.key ?? index,
|
|
50
|
+
props,
|
|
51
|
+
meta: options.mapMeta ? options.mapMeta(ctx) : (props as TMeta),
|
|
52
|
+
};
|
|
53
|
+
})
|
|
54
|
+
.filter(Boolean) as SlotItem<TMeta>[];
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
items,
|
|
59
|
+
first: computed(() => items.value[0]),
|
|
60
|
+
find: (predicate: (item: SlotItem<TMeta>) => boolean) => items.value.find(predicate),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function flattenVNodes(children: VNodeArrayChildren, acc: VNode[] = []): VNode[] {
|
|
65
|
+
children.forEach((child) => {
|
|
66
|
+
if (!child) return;
|
|
67
|
+
|
|
68
|
+
if (Array.isArray(child)) {
|
|
69
|
+
flattenVNodes(child, acc);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!isVNode(child)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (child.type === Comment) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (child.type === Fragment && Array.isArray(child.children)) {
|
|
82
|
+
flattenVNodes(child.children as VNodeArrayChildren, acc);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
acc.push(child);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return acc;
|
|
90
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Collections, ContentNavigationItem } from "@nuxt/content";
|
|
2
|
+
import { findPageHeadline } from "@nuxt/content/utils";
|
|
3
|
+
import { kebabCase } from "lodash-es";
|
|
4
|
+
|
|
5
|
+
export const useSearchModal = () => useState("docSearchModal", () => false);
|
|
6
|
+
|
|
7
|
+
export const useDocPage = async () => {
|
|
8
|
+
const route = useRoute();
|
|
9
|
+
const isLandingRoute = route.path === "/";
|
|
10
|
+
|
|
11
|
+
const [{ data: page }, { data: surround }, { data: navigation }] = await Promise.all([
|
|
12
|
+
useAsyncData(
|
|
13
|
+
kebabCase(route.path),
|
|
14
|
+
() =>
|
|
15
|
+
isLandingRoute
|
|
16
|
+
? queryCollection("landing" as keyof Collections)
|
|
17
|
+
.path(route.path)
|
|
18
|
+
.first()
|
|
19
|
+
: queryCollection("docs" as keyof Collections)
|
|
20
|
+
.path(route.path)
|
|
21
|
+
.first(),
|
|
22
|
+
{ watch: [() => route.path] }
|
|
23
|
+
),
|
|
24
|
+
useAsyncData(
|
|
25
|
+
`${kebabCase(route.path)}-surround`,
|
|
26
|
+
() => {
|
|
27
|
+
if (isLandingRoute) {
|
|
28
|
+
return Promise.resolve(null);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return queryCollectionItemSurroundings("docs" as keyof Collections, route.path, {
|
|
32
|
+
fields: ["title", "description", "path"],
|
|
33
|
+
}).where("path", "NOT LIKE", "%/.navigation");
|
|
34
|
+
},
|
|
35
|
+
{ watch: [() => route.path] }
|
|
36
|
+
),
|
|
37
|
+
useAsyncData(
|
|
38
|
+
() => `navigation_docs`,
|
|
39
|
+
() => queryCollectionNavigation("docs" as keyof Collections),
|
|
40
|
+
{
|
|
41
|
+
watch: [() => route.path],
|
|
42
|
+
transform: (data: ContentNavigationItem[]) =>
|
|
43
|
+
data.find((item) => item.path === "/docs")?.children || data,
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The headline for the current page, derived from the navigation structure.
|
|
50
|
+
*/
|
|
51
|
+
const headline = ref(findPageHeadline(navigation?.value, page.value?.path));
|
|
52
|
+
/**
|
|
53
|
+
* The breadcrumbs for the current page, derived from the navigation structure.
|
|
54
|
+
*/
|
|
55
|
+
const breadcrumbs = computed(() =>
|
|
56
|
+
findPageBreadcrumbs(navigation?.value, page.value?.path || "")
|
|
57
|
+
);
|
|
58
|
+
/**
|
|
59
|
+
* The previous and next pages in the collection, if available.
|
|
60
|
+
*/
|
|
61
|
+
const prev = computed(() => surround.value?.[0]);
|
|
62
|
+
/** The next page in the collection, if available. */
|
|
63
|
+
const next = computed(() => surround.value?.[1]);
|
|
64
|
+
|
|
65
|
+
return { page, surround, navigation, headline, breadcrumbs, prev, next };
|
|
66
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface DocSearchSection {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
titles: string[];
|
|
5
|
+
level: number;
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const SEARCH_SECTION_IGNORED_TAGS = ["code", "pre"];
|
|
10
|
+
|
|
11
|
+
export function useDocSearchSections() {
|
|
12
|
+
return useLazyAsyncData(
|
|
13
|
+
"docd-search-sections",
|
|
14
|
+
() =>
|
|
15
|
+
queryCollectionSearchSections("docs" as never, {
|
|
16
|
+
ignoredTags: SEARCH_SECTION_IGNORED_TAGS,
|
|
17
|
+
}) as Promise<DocSearchSection[]>,
|
|
18
|
+
{
|
|
19
|
+
server: false,
|
|
20
|
+
default: () => [],
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { DocdConfig, DocdGithubConfig } from "../app.config";
|
|
2
|
+
import {
|
|
3
|
+
ANIMATE_IDENTITY,
|
|
4
|
+
transitionPresets,
|
|
5
|
+
type DocdTransitionName,
|
|
6
|
+
type TransitionStates,
|
|
7
|
+
} from "../transitions/presets";
|
|
8
|
+
|
|
9
|
+
export function useDocd() {
|
|
10
|
+
const appConfig = useAppConfig();
|
|
11
|
+
const route = useRoute();
|
|
12
|
+
const direction = useState<"forward" | "back">("nav-direction", () => "forward");
|
|
13
|
+
|
|
14
|
+
/* =====================================
|
|
15
|
+
= UI =
|
|
16
|
+
===================================== */
|
|
17
|
+
const borderType = useUIConfig("borderType");
|
|
18
|
+
const isDashed = computed(() => borderType.value === "dashed");
|
|
19
|
+
const extraLinks = useUIConfig("extraLinks");
|
|
20
|
+
const github = computed(
|
|
21
|
+
() => ((appConfig.docd as DocdConfig | undefined)?.github ?? {}) as DocdGithubConfig
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
/* =====================================
|
|
25
|
+
= Footer =
|
|
26
|
+
===================================== */
|
|
27
|
+
const footer = useUIConfig("footer");
|
|
28
|
+
const hideThemeCustomizer = computed(() => footer.value?.hideThemeCustomizer ?? false);
|
|
29
|
+
const hideLightDarkToggle = computed(() => footer.value?.hideLightDarkToggle ?? false);
|
|
30
|
+
|
|
31
|
+
/* =====================================
|
|
32
|
+
= Header =
|
|
33
|
+
===================================== */
|
|
34
|
+
const header = useUIConfig("header");
|
|
35
|
+
const headerTitle = computed(() => header.value.title ?? "");
|
|
36
|
+
const hideSearch = computed(() => header.value.hideSearch ?? false);
|
|
37
|
+
|
|
38
|
+
/* =====================================
|
|
39
|
+
= Logo =
|
|
40
|
+
===================================== */
|
|
41
|
+
const logo = computed(() => header.value.logo);
|
|
42
|
+
const lightLogo = computed(() => logo.value?.light);
|
|
43
|
+
const darkLogo = computed(() => logo.value?.dark);
|
|
44
|
+
const logoAlt = computed(() => logo.value?.alt ?? "Logo");
|
|
45
|
+
const logoClasses = computed(() => logo.value?.classes ?? "");
|
|
46
|
+
const logoUrl = computed(() => logo.value?.url ?? "/");
|
|
47
|
+
|
|
48
|
+
/* =====================================
|
|
49
|
+
= ToC =
|
|
50
|
+
===================================== */
|
|
51
|
+
const toc = useUIConfig("toc");
|
|
52
|
+
const tocTitle = computed(() => toc.value.title ?? "On this page");
|
|
53
|
+
const tocIcon = computed(() => toc.value.icon ?? "lucide:list");
|
|
54
|
+
|
|
55
|
+
/* =====================================
|
|
56
|
+
= Transition =
|
|
57
|
+
===================================== */
|
|
58
|
+
const transitionConfig = useUIConfig("transition");
|
|
59
|
+
|
|
60
|
+
const pageMeta = computed(() => route.meta?.pageTransition as DocdTransitionName | undefined);
|
|
61
|
+
|
|
62
|
+
const activeName = computed<DocdTransitionName>(
|
|
63
|
+
() => pageMeta.value ?? (transitionConfig.value.name as DocdTransitionName) ?? "fade"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const preset = computed<TransitionStates>(() => {
|
|
67
|
+
const base = transitionPresets[activeName.value] ?? transitionPresets.rightToLeft;
|
|
68
|
+
if (direction.value === "back") {
|
|
69
|
+
return { initial: base.exit, exit: base.initial };
|
|
70
|
+
}
|
|
71
|
+
return base;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const duration = computed(() => transitionConfig.value.duration ?? 0.35);
|
|
75
|
+
const easing = computed(() => transitionConfig.value.easing ?? "easeOut");
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
// ui
|
|
79
|
+
header,
|
|
80
|
+
headerTitle,
|
|
81
|
+
borderType,
|
|
82
|
+
isDashed,
|
|
83
|
+
extraLinks,
|
|
84
|
+
github,
|
|
85
|
+
hideSearch,
|
|
86
|
+
// transition
|
|
87
|
+
activeName,
|
|
88
|
+
preset,
|
|
89
|
+
animate: ANIMATE_IDENTITY,
|
|
90
|
+
duration,
|
|
91
|
+
easing,
|
|
92
|
+
// logo
|
|
93
|
+
logo,
|
|
94
|
+
lightLogo,
|
|
95
|
+
darkLogo,
|
|
96
|
+
logoAlt,
|
|
97
|
+
logoClasses,
|
|
98
|
+
logoUrl,
|
|
99
|
+
// ToC
|
|
100
|
+
toc,
|
|
101
|
+
tocTitle,
|
|
102
|
+
tocIcon,
|
|
103
|
+
// footer
|
|
104
|
+
hideThemeCustomizer,
|
|
105
|
+
hideLightDarkToggle,
|
|
106
|
+
};
|
|
107
|
+
}
|