@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
package/app/pages/blog/index.vue
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { Badge } from "#ginko-docs/components/ui/badge";
|
|
3
|
-
import { many } from "@lupinum/ginko-content/client";
|
|
4
|
-
import { formatContentDate } from "#ginko-docs/utils/content";
|
|
5
|
-
import { getLocalizedSiteText } from "#ginko-docs/config/site.utils";
|
|
6
|
-
import { computed } from "vue";
|
|
7
|
-
import { definePageMeta, useAppConfig, useAsyncData, useHead, useI18n, useSeoMeta } from "#imports";
|
|
8
|
-
import { useCanonicalUrl } from "#ginko-docs/composables/useCanonicalUrl";
|
|
9
|
-
import { useLocalizedPath } from "#ginko-docs/composables/useLocalizedPath";
|
|
10
|
-
|
|
11
|
-
const MAX_BLOG_POSTS = 50;
|
|
12
|
-
|
|
13
|
-
definePageMeta({ layout: "blog" });
|
|
14
|
-
|
|
15
|
-
const { locale, t } = useI18n();
|
|
16
|
-
const postsKey = computed(() => `blog-posts:${locale.value}`);
|
|
17
|
-
const canonicalUrl = useCanonicalUrl();
|
|
18
|
-
const localizedPath = useLocalizedPath();
|
|
19
|
-
const config = useAppConfig().ginkoDocs;
|
|
20
|
-
const siteName = computed(() => getLocalizedSiteText(config.site.name, locale.value));
|
|
21
|
-
const fullTitle = computed(() => `${t("blog.pageTitle")} - ${siteName.value}`);
|
|
22
|
-
|
|
23
|
-
useSeoMeta({
|
|
24
|
-
title: fullTitle,
|
|
25
|
-
description: computed(() => t("blog.description")),
|
|
26
|
-
ogTitle: fullTitle,
|
|
27
|
-
ogDescription: computed(() => t("blog.description")),
|
|
28
|
-
ogUrl: canonicalUrl,
|
|
29
|
-
twitterCard: "summary_large_image",
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
useHead(() => ({
|
|
33
|
-
link: [
|
|
34
|
-
{ key: "canonical", rel: "canonical", href: canonicalUrl.value },
|
|
35
|
-
{
|
|
36
|
-
key: "rss",
|
|
37
|
-
rel: "alternate",
|
|
38
|
-
type: "application/rss+xml",
|
|
39
|
-
title: fullTitle.value,
|
|
40
|
-
href: `${localizedPath("blog")}/rss.xml`,
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
const { data: queriedPosts, error } = await useAsyncData(
|
|
46
|
-
postsKey,
|
|
47
|
-
() =>
|
|
48
|
-
many("blog", {
|
|
49
|
-
locale: locale.value,
|
|
50
|
-
fallback: true,
|
|
51
|
-
populate: { author: "authors" },
|
|
52
|
-
sort: { date: "desc" },
|
|
53
|
-
limit: MAX_BLOG_POSTS + 1,
|
|
54
|
-
}),
|
|
55
|
-
{ watch: [locale] },
|
|
56
|
-
);
|
|
57
|
-
if (error.value) throw error.value;
|
|
58
|
-
if ((queriedPosts.value?.length ?? 0) > MAX_BLOG_POSTS) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`Blog supports at most ${MAX_BLOG_POSTS} posts; add pagination before publishing more`,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
const posts = computed(() => queriedPosts.value ?? []);
|
|
64
|
-
</script>
|
|
65
|
-
|
|
66
|
-
<template>
|
|
67
|
-
<div class="mx-auto max-w-3xl">
|
|
68
|
-
<div class="mb-12">
|
|
69
|
-
<h1 class="text-3xl font-semibold tracking-tight text-foreground">{{ t("blog.title") }}</h1>
|
|
70
|
-
<p class="mt-2 text-muted-foreground">{{ t("blog.description") }}</p>
|
|
71
|
-
</div>
|
|
72
|
-
|
|
73
|
-
<p v-if="posts.length === 0" class="py-12 text-center text-sm text-muted-foreground">
|
|
74
|
-
{{ t("blog.empty") }}
|
|
75
|
-
</p>
|
|
76
|
-
<div v-else class="divide-y divide-border">
|
|
77
|
-
<article v-for="post in posts" :key="post.route.resolvedPath" class="group py-8">
|
|
78
|
-
<NuxtLink
|
|
79
|
-
:to="post.route.resolvedPath"
|
|
80
|
-
class="flex flex-col gap-6 sm:flex-row sm:items-start"
|
|
81
|
-
>
|
|
82
|
-
<div class="min-w-0 flex-1">
|
|
83
|
-
<div class="mb-3 flex items-center gap-2">
|
|
84
|
-
<Badge v-if="post.badge" variant="secondary" class="text-xs">{{ post.badge }}</Badge>
|
|
85
|
-
<span class="text-xs text-muted-foreground"
|
|
86
|
-
>{{ formatContentDate(post.date, locale)
|
|
87
|
-
}}<template v-if="post.readingTime"> · {{ post.readingTime }}</template></span
|
|
88
|
-
>
|
|
89
|
-
</div>
|
|
90
|
-
<div v-if="post.author" class="mb-3 flex items-center gap-2">
|
|
91
|
-
<img
|
|
92
|
-
v-if="post.author.avatar"
|
|
93
|
-
:src="post.author.avatar"
|
|
94
|
-
:alt="post.author.name"
|
|
95
|
-
class="size-5 shrink-0 rounded-full border border-border object-cover"
|
|
96
|
-
loading="lazy"
|
|
97
|
-
/>
|
|
98
|
-
<p class="text-xs font-medium text-muted-foreground">
|
|
99
|
-
{{ post.author.name }} · {{ post.author.role }}
|
|
100
|
-
</p>
|
|
101
|
-
</div>
|
|
102
|
-
<h2
|
|
103
|
-
class="mb-2 text-xl font-semibold text-foreground transition-colors group-hover:text-primary"
|
|
104
|
-
>
|
|
105
|
-
{{ post.title }}
|
|
106
|
-
</h2>
|
|
107
|
-
<p class="text-sm leading-relaxed text-muted-foreground">{{ post.description }}</p>
|
|
108
|
-
<div class="mt-4 flex items-center gap-1 text-sm font-medium text-primary">
|
|
109
|
-
{{ t("blog.readMore") }}
|
|
110
|
-
<Icon
|
|
111
|
-
name="lucide:arrow-right"
|
|
112
|
-
class="size-4 transition-transform group-hover:translate-x-0.5"
|
|
113
|
-
aria-hidden="true"
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
<NuxtImg
|
|
118
|
-
v-if="post.image"
|
|
119
|
-
:src="post.image"
|
|
120
|
-
:alt="post.title"
|
|
121
|
-
class="aspect-[16/10] w-full shrink-0 rounded-lg border border-border object-cover sm:w-52"
|
|
122
|
-
width="416"
|
|
123
|
-
height="260"
|
|
124
|
-
loading="lazy"
|
|
125
|
-
/>
|
|
126
|
-
</NuxtLink>
|
|
127
|
-
</article>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</template>
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import DocsPageContent from "#ginko-docs/features/docs/components/DocsPageContent.vue";
|
|
3
|
-
import { definePageMeta } from "#imports";
|
|
4
|
-
|
|
5
|
-
definePageMeta({ layout: "docs" });
|
|
6
|
-
</script>
|
|
7
|
-
|
|
8
|
-
<template>
|
|
9
|
-
<DocsPageContent :key="$route.path" />
|
|
10
|
-
</template>
|
package/app/pages/docs/index.vue
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { findFirstNavigationPage } from "@lupinum/ginko-content/navigation";
|
|
3
|
-
import { createError, definePageMeta, navigateTo } from "#imports";
|
|
4
|
-
import { useDocsNavigationData } from "#ginko-docs/features/docs/composables/useDocsNavigationData";
|
|
5
|
-
|
|
6
|
-
definePageMeta({ layout: "docs" });
|
|
7
|
-
|
|
8
|
-
const { data, error } = await useDocsNavigationData();
|
|
9
|
-
if (error.value) throw error.value;
|
|
10
|
-
const entryPath = findFirstNavigationPage(data.value ?? undefined)?.path;
|
|
11
|
-
if (!entryPath) {
|
|
12
|
-
throw createError({ statusCode: 404, statusMessage: "Documentation is empty" });
|
|
13
|
-
}
|
|
14
|
-
await navigateTo(entryPath, { redirectCode: 302, replace: true });
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<template>
|
|
18
|
-
<main id="main-content" />
|
|
19
|
-
</template>
|
package/app/pages/index.vue
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
import { useClipboard } from "@vueuse/core";
|
|
4
|
-
import { useI18n, useSeoMeta } from "#imports";
|
|
5
|
-
import { getLocalizedSiteText } from "#ginko-docs/config/site.utils";
|
|
6
|
-
import { useGinkoDocsConfig } from "#ginko-docs/composables/useGinkoDocsConfig";
|
|
7
|
-
import SiteHeroCode from "#ginko-docs/components/site/SiteHeroCode.vue";
|
|
8
|
-
|
|
9
|
-
const config = useGinkoDocsConfig();
|
|
10
|
-
const { locale, t } = useI18n();
|
|
11
|
-
|
|
12
|
-
type LocalizableText = string | { en: string; de?: string };
|
|
13
|
-
const localize = (value: LocalizableText) => getLocalizedSiteText(value, locale.value);
|
|
14
|
-
const localizeLink = (link: { label: LocalizableText; to: LocalizableText }) => ({
|
|
15
|
-
label: localize(link.label),
|
|
16
|
-
to: localize(link.to),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const landing = computed(() => ({
|
|
20
|
-
title: localize(config.landing.title),
|
|
21
|
-
description: localize(config.landing.description),
|
|
22
|
-
primary: localizeLink(config.landing.primary),
|
|
23
|
-
secondary: config.landing.secondary ? localizeLink(config.landing.secondary) : undefined,
|
|
24
|
-
install: config.landing.install,
|
|
25
|
-
features: config.landing.features.map((feature) => ({
|
|
26
|
-
...feature,
|
|
27
|
-
title: localize(feature.title),
|
|
28
|
-
description: localize(feature.description),
|
|
29
|
-
})),
|
|
30
|
-
heroMedia: config.landing.hero?.media,
|
|
31
|
-
agent: config.landing.agent
|
|
32
|
-
? {
|
|
33
|
-
title: localize(config.landing.agent.title),
|
|
34
|
-
description: localize(config.landing.agent.description),
|
|
35
|
-
code: config.landing.agent.code,
|
|
36
|
-
}
|
|
37
|
-
: undefined,
|
|
38
|
-
cta: config.landing.cta
|
|
39
|
-
? {
|
|
40
|
-
title: localize(config.landing.cta.title),
|
|
41
|
-
primary: config.landing.cta.primary
|
|
42
|
-
? localizeLink(config.landing.cta.primary)
|
|
43
|
-
: localizeLink(config.landing.primary),
|
|
44
|
-
secondary: config.landing.cta.secondary
|
|
45
|
-
? localizeLink(config.landing.cta.secondary)
|
|
46
|
-
: undefined,
|
|
47
|
-
}
|
|
48
|
-
: undefined,
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
const heroCodeTabs = computed(() => {
|
|
52
|
-
const media = landing.value.heroMedia;
|
|
53
|
-
|
|
54
|
-
if (media?.type === "code-tabs") {
|
|
55
|
-
return media.tabs.map((tab) => ({ ...tab, label: localize(tab.label) }));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (media?.type === "code") {
|
|
59
|
-
return [
|
|
60
|
-
{
|
|
61
|
-
label: media.filename ?? media.language ?? "Code",
|
|
62
|
-
filename: media.filename,
|
|
63
|
-
language: media.language,
|
|
64
|
-
code: media.code,
|
|
65
|
-
},
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return [];
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const hasWideHeroCode = computed(() => {
|
|
73
|
-
const media = landing.value.heroMedia;
|
|
74
|
-
return media?.type !== "image" && media?.layout === "wide";
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Feature icon tiles cycle through the theme-adaptive hero accent pairs.
|
|
78
|
-
const FEATURE_TINTS = [
|
|
79
|
-
{ tile: "bg-(--hero-blue-muted)", icon: "text-(--hero-blue-dark)" },
|
|
80
|
-
{ tile: "bg-(--hero-mint-muted)", icon: "text-(--hero-mint-text)" },
|
|
81
|
-
{ tile: "bg-(--hero-yellow-muted)", icon: "text-(--hero-yellow-dark)" },
|
|
82
|
-
{ tile: "bg-(--hero-coral-muted)", icon: "text-(--hero-coral-text)" },
|
|
83
|
-
];
|
|
84
|
-
const tint = (index: number) => FEATURE_TINTS[index % FEATURE_TINTS.length]!;
|
|
85
|
-
|
|
86
|
-
type TerminalLine = { kind: "command" | "comment" | "output"; text: string };
|
|
87
|
-
const agentTerminalLines = computed<TerminalLine[]>(() => {
|
|
88
|
-
const code = landing.value.agent?.code;
|
|
89
|
-
if (!code) return [];
|
|
90
|
-
|
|
91
|
-
return code.split("\n").map((line) => ({
|
|
92
|
-
kind: line.startsWith("$")
|
|
93
|
-
? "command"
|
|
94
|
-
: line.trimStart().startsWith("#")
|
|
95
|
-
? "comment"
|
|
96
|
-
: "output",
|
|
97
|
-
text: line,
|
|
98
|
-
}));
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const { copy, copied } = useClipboard();
|
|
102
|
-
async function copyInstallCommand() {
|
|
103
|
-
await copy(landing.value.install?.command ?? "");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const landingTitle = computed(() => localize(config.site.name));
|
|
107
|
-
const landingDescription = computed(() => localize(config.site.description));
|
|
108
|
-
useSeoMeta({
|
|
109
|
-
title: landingTitle,
|
|
110
|
-
description: landingDescription,
|
|
111
|
-
ogTitle: landingTitle,
|
|
112
|
-
ogDescription: landingDescription,
|
|
113
|
-
});
|
|
114
|
-
</script>
|
|
115
|
-
|
|
116
|
-
<template>
|
|
117
|
-
<div class="overflow-hidden">
|
|
118
|
-
<section class="relative border-b border-border">
|
|
119
|
-
<div
|
|
120
|
-
class="relative mx-auto flex max-w-6xl flex-col gap-14 px-5 py-24 sm:px-8 sm:py-32 lg:flex-row lg:items-center"
|
|
121
|
-
>
|
|
122
|
-
<div class="min-w-0 flex-1">
|
|
123
|
-
<h1
|
|
124
|
-
class="max-w-4xl text-5xl leading-[0.98] font-semibold tracking-[-0.035em] text-balance text-foreground sm:text-7xl"
|
|
125
|
-
:class="landing.heroMedia ? 'lg:text-6xl' : 'lg:text-[5.75rem]'"
|
|
126
|
-
>
|
|
127
|
-
{{ landing.title }}
|
|
128
|
-
</h1>
|
|
129
|
-
<p class="mt-7 max-w-2xl text-lg leading-8 text-muted-foreground sm:text-xl">
|
|
130
|
-
{{ landing.description }}
|
|
131
|
-
</p>
|
|
132
|
-
<div class="mt-10 flex flex-wrap items-center gap-3">
|
|
133
|
-
<NuxtLink
|
|
134
|
-
:to="landing.primary.to"
|
|
135
|
-
class="inline-flex h-11 items-center gap-2 rounded-md bg-brand px-5 text-sm font-semibold text-brand-foreground transition-transform hover:-translate-y-0.5"
|
|
136
|
-
>
|
|
137
|
-
{{ landing.primary.label }}
|
|
138
|
-
<Icon name="lucide:arrow-right" class="size-4" aria-hidden="true" />
|
|
139
|
-
</NuxtLink>
|
|
140
|
-
<NuxtLink
|
|
141
|
-
v-if="landing.secondary"
|
|
142
|
-
:to="landing.secondary.to"
|
|
143
|
-
class="inline-flex h-11 items-center gap-2 rounded-md border border-border bg-background px-5 text-sm font-semibold text-foreground transition-colors hover:bg-muted"
|
|
144
|
-
>
|
|
145
|
-
{{ landing.secondary.label }}
|
|
146
|
-
</NuxtLink>
|
|
147
|
-
</div>
|
|
148
|
-
<div v-if="landing.install" class="mt-5">
|
|
149
|
-
<div
|
|
150
|
-
class="inline-flex h-10 max-w-full items-center gap-3 rounded-md border border-border bg-muted/40 pr-1.5 pl-4 font-mono text-[13px] text-foreground/90"
|
|
151
|
-
>
|
|
152
|
-
<span class="text-muted-foreground select-none" aria-hidden="true">$</span>
|
|
153
|
-
<span class="truncate">{{ landing.install.command }}</span>
|
|
154
|
-
<button
|
|
155
|
-
type="button"
|
|
156
|
-
class="content-codeblock-copy-button"
|
|
157
|
-
:aria-label="copied ? t('docs.copiedText') : t('docs.copyText')"
|
|
158
|
-
@click="copyInstallCommand"
|
|
159
|
-
>
|
|
160
|
-
<Icon
|
|
161
|
-
:name="copied ? 'lucide:check' : 'lucide:clipboard'"
|
|
162
|
-
class="size-3.5"
|
|
163
|
-
aria-hidden="true"
|
|
164
|
-
/>
|
|
165
|
-
</button>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
<div
|
|
171
|
-
v-if="landing.heroMedia"
|
|
172
|
-
class="w-full min-w-0 max-w-2xl shrink-0 lg:w-[30rem] lg:max-w-full xl:w-[36rem]"
|
|
173
|
-
>
|
|
174
|
-
<NuxtImg
|
|
175
|
-
v-if="landing.heroMedia.type === 'image'"
|
|
176
|
-
:src="landing.heroMedia.src"
|
|
177
|
-
:alt="landing.heroMedia.alt"
|
|
178
|
-
class="w-full rounded-xl border border-border object-cover shadow-sm"
|
|
179
|
-
width="576"
|
|
180
|
-
loading="lazy"
|
|
181
|
-
/>
|
|
182
|
-
<SiteHeroCode
|
|
183
|
-
v-else-if="heroCodeTabs.length"
|
|
184
|
-
:tabs="heroCodeTabs"
|
|
185
|
-
:class="hasWideHeroCode && 'site-hero-code-wide'"
|
|
186
|
-
/>
|
|
187
|
-
</div>
|
|
188
|
-
</div>
|
|
189
|
-
</section>
|
|
190
|
-
|
|
191
|
-
<section v-if="landing.features.length" class="mx-auto max-w-6xl px-5 py-20 sm:px-8 sm:py-24">
|
|
192
|
-
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
193
|
-
<article
|
|
194
|
-
v-for="(feature, index) in landing.features"
|
|
195
|
-
:key="feature.title"
|
|
196
|
-
class="min-w-0 rounded-lg border border-border bg-card p-6"
|
|
197
|
-
>
|
|
198
|
-
<span
|
|
199
|
-
v-if="feature.icon"
|
|
200
|
-
class="flex size-10 items-center justify-center rounded-md"
|
|
201
|
-
:class="tint(index).tile"
|
|
202
|
-
>
|
|
203
|
-
<Icon
|
|
204
|
-
:name="feature.icon"
|
|
205
|
-
class="size-5"
|
|
206
|
-
:class="tint(index).icon"
|
|
207
|
-
aria-hidden="true"
|
|
208
|
-
/>
|
|
209
|
-
</span>
|
|
210
|
-
<h2 class="mt-5 text-base font-semibold tracking-tight text-foreground">
|
|
211
|
-
{{ feature.title }}
|
|
212
|
-
</h2>
|
|
213
|
-
<p class="mt-2 text-sm leading-6 text-muted-foreground">{{ feature.description }}</p>
|
|
214
|
-
</article>
|
|
215
|
-
</div>
|
|
216
|
-
</section>
|
|
217
|
-
|
|
218
|
-
<section v-if="landing.agent" class="border-y border-border bg-agent-background">
|
|
219
|
-
<div
|
|
220
|
-
class="mx-auto grid max-w-6xl items-center gap-10 px-5 py-16 sm:px-8 sm:py-20 lg:grid-cols-2 lg:gap-16"
|
|
221
|
-
>
|
|
222
|
-
<div class="min-w-0">
|
|
223
|
-
<h2
|
|
224
|
-
class="text-3xl font-semibold tracking-[-0.03em] text-balance text-agent-foreground sm:text-4xl"
|
|
225
|
-
>
|
|
226
|
-
{{ landing.agent.title }}
|
|
227
|
-
</h2>
|
|
228
|
-
<p class="mt-4 max-w-xl text-base leading-7 text-agent-muted">
|
|
229
|
-
{{ landing.agent.description }}
|
|
230
|
-
</p>
|
|
231
|
-
</div>
|
|
232
|
-
<pre
|
|
233
|
-
class="min-w-0 overflow-x-auto rounded-lg border border-white/10 bg-black/30 p-5 font-mono text-[13px] leading-7"
|
|
234
|
-
><code><span
|
|
235
|
-
v-for="(line, index) in agentTerminalLines"
|
|
236
|
-
:key="index"
|
|
237
|
-
class="block min-h-[1.75rem] whitespace-pre"
|
|
238
|
-
><template v-if="line.kind === 'command'"><span class="text-agent-muted/70 select-none">$</span><span class="text-agent-foreground">{{ line.text.slice(1) }}</span></template><span v-else-if="line.kind === 'comment'" class="text-emerald-300/90">{{ line.text }}</span><span v-else class="text-agent-muted">{{ line.text }}</span></span></code></pre>
|
|
239
|
-
</div>
|
|
240
|
-
</section>
|
|
241
|
-
|
|
242
|
-
<section v-if="landing.cta">
|
|
243
|
-
<div class="mx-auto max-w-6xl px-5 py-20 text-center sm:px-8 sm:py-28">
|
|
244
|
-
<h2
|
|
245
|
-
class="text-3xl font-semibold tracking-[-0.03em] text-balance text-foreground sm:text-5xl"
|
|
246
|
-
>
|
|
247
|
-
{{ landing.cta.title }}
|
|
248
|
-
</h2>
|
|
249
|
-
<div class="mt-9 flex flex-wrap items-center justify-center gap-3">
|
|
250
|
-
<NuxtLink
|
|
251
|
-
:to="landing.cta.primary.to"
|
|
252
|
-
class="inline-flex h-11 items-center gap-2 rounded-md bg-brand px-5 text-sm font-semibold text-brand-foreground transition-transform hover:-translate-y-0.5"
|
|
253
|
-
>
|
|
254
|
-
{{ landing.cta.primary.label }}
|
|
255
|
-
<Icon name="lucide:arrow-right" class="size-4" aria-hidden="true" />
|
|
256
|
-
</NuxtLink>
|
|
257
|
-
<NuxtLink
|
|
258
|
-
v-if="landing.cta.secondary"
|
|
259
|
-
:to="landing.cta.secondary.to"
|
|
260
|
-
class="inline-flex h-11 items-center gap-2 rounded-md border border-border bg-background px-5 text-sm font-semibold text-foreground transition-colors hover:bg-muted"
|
|
261
|
-
>
|
|
262
|
-
{{ landing.cta.secondary.label }}
|
|
263
|
-
</NuxtLink>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
266
|
-
</section>
|
|
267
|
-
</div>
|
|
268
|
-
</template>
|
|
269
|
-
|
|
270
|
-
<style scoped>
|
|
271
|
-
/* Keep the hero grid intact; only use spare space toward the viewport edge. */
|
|
272
|
-
@media (min-width: 80rem) {
|
|
273
|
-
.site-hero-code-wide {
|
|
274
|
-
width: min(48.75rem, calc(50vw - 2rem));
|
|
275
|
-
max-width: none;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
</style>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { defineNuxtPlugin, useAppConfig, useHead } from "#imports";
|
|
2
|
-
|
|
3
|
-
export default defineNuxtPlugin(() => {
|
|
4
|
-
const { theme } = useAppConfig().ginkoDocs;
|
|
5
|
-
|
|
6
|
-
useHead({
|
|
7
|
-
htmlAttrs: {
|
|
8
|
-
"data-theme-preset": theme.preset ?? "default",
|
|
9
|
-
"data-neutral": theme.neutral,
|
|
10
|
-
"data-primary": theme.primary,
|
|
11
|
-
"data-code-blocks": theme.codeBlocks,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
});
|
package/app/plugins/ssr-width.ts
DELETED
package/app/utils/content.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
type TocLinkLike = {
|
|
2
|
-
id?: string;
|
|
3
|
-
text?: string;
|
|
4
|
-
label?: string;
|
|
5
|
-
depth?: number;
|
|
6
|
-
children?: TocLinkLike[];
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type FlatTocItem = {
|
|
10
|
-
id: string;
|
|
11
|
-
label: string;
|
|
12
|
-
depth?: number;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export function getMarkdownTocLinks(body: unknown): TocLinkLike[] | undefined {
|
|
16
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) return undefined;
|
|
17
|
-
|
|
18
|
-
const root = body as { type?: unknown; children?: unknown; toc?: unknown };
|
|
19
|
-
if (root.type !== "root" || !Array.isArray(root.children)) return undefined;
|
|
20
|
-
if (!root.toc || typeof root.toc !== "object" || Array.isArray(root.toc)) return undefined;
|
|
21
|
-
|
|
22
|
-
const links = (root.toc as { links?: unknown }).links;
|
|
23
|
-
return Array.isArray(links) ? (links as TocLinkLike[]) : undefined;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function formatContentDate(
|
|
27
|
-
value?: string | Date | null,
|
|
28
|
-
locale: string = defaultLocale,
|
|
29
|
-
): string {
|
|
30
|
-
if (!value) return "";
|
|
31
|
-
|
|
32
|
-
const date = value instanceof Date ? value : new Date(value);
|
|
33
|
-
if (Number.isNaN(date.getTime())) return "";
|
|
34
|
-
|
|
35
|
-
return new Intl.DateTimeFormat(locale, {
|
|
36
|
-
month: "short",
|
|
37
|
-
day: "numeric",
|
|
38
|
-
year: "numeric",
|
|
39
|
-
}).format(date);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function flattenTocLinks(links?: TocLinkLike[] | null): FlatTocItem[] {
|
|
43
|
-
const output: FlatTocItem[] = [];
|
|
44
|
-
|
|
45
|
-
function visit(items: TocLinkLike[] | undefined) {
|
|
46
|
-
for (const item of items ?? []) {
|
|
47
|
-
const label = item.text ?? item.label;
|
|
48
|
-
if (item.id && label) {
|
|
49
|
-
output.push({
|
|
50
|
-
id: item.id,
|
|
51
|
-
label,
|
|
52
|
-
depth: item.depth,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
visit(item.children);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
visit(links ?? undefined);
|
|
61
|
-
return output;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function filterTocByDepth(items: FlatTocItem[], maxDepth: number): FlatTocItem[] {
|
|
65
|
-
return items.filter((item) => (item.depth ?? 2) <= maxDepth);
|
|
66
|
-
}
|
|
67
|
-
import { defaultLocale } from "../../i18n/locales";
|
package/app/utils/file-icons.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
export const FALLBACK_FILE_ICON = "lucide:file";
|
|
2
|
-
|
|
3
|
-
export const FILENAME_ICONS: Record<string, string> = {
|
|
4
|
-
".env": "lucide:file-key",
|
|
5
|
-
".env.example": "lucide:file-key",
|
|
6
|
-
".gitignore": "lucide:git-branch",
|
|
7
|
-
"components.json": "lucide:blocks",
|
|
8
|
-
"eslint.config.cjs": "lucide:badge-check",
|
|
9
|
-
"eslint.config.js": "lucide:badge-check",
|
|
10
|
-
"eslint.config.mjs": "lucide:badge-check",
|
|
11
|
-
"nuxt.config.js": "logos:nuxt-icon",
|
|
12
|
-
"nuxt.config.ts": "logos:nuxt-icon",
|
|
13
|
-
"package.json": "lucide:package",
|
|
14
|
-
"pnpm-lock.yaml": "lucide:package",
|
|
15
|
-
"pnpm-workspace.yaml": "lucide:package",
|
|
16
|
-
"tailwind.config.js": "lucide:palette",
|
|
17
|
-
"tailwind.config.ts": "lucide:palette",
|
|
18
|
-
"tsconfig.json": "lucide:file-cog",
|
|
19
|
-
"vite.config.js": "lucide:zap",
|
|
20
|
-
"vite.config.ts": "lucide:zap",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const EXTENSION_ICONS: Record<string, string> = {
|
|
24
|
-
bash: "lucide:terminal",
|
|
25
|
-
cjs: "lucide:file-json",
|
|
26
|
-
css: "lucide:braces",
|
|
27
|
-
csv: "lucide:table",
|
|
28
|
-
htm: "lucide:code-xml",
|
|
29
|
-
html: "lucide:code-xml",
|
|
30
|
-
js: "lucide:file-json",
|
|
31
|
-
json: "lucide:braces",
|
|
32
|
-
jsx: "lucide:atom",
|
|
33
|
-
md: "lucide:file-text",
|
|
34
|
-
mdc: "lucide:file-text",
|
|
35
|
-
mjs: "lucide:file-json",
|
|
36
|
-
mts: "lucide:file-code",
|
|
37
|
-
scss: "lucide:braces",
|
|
38
|
-
sh: "lucide:terminal",
|
|
39
|
-
ts: "logos:typescript-icon",
|
|
40
|
-
tsx: "lucide:atom",
|
|
41
|
-
txt: "lucide:file-text",
|
|
42
|
-
vue: "logos:vue",
|
|
43
|
-
yaml: "lucide:file-cog",
|
|
44
|
-
yml: "lucide:file-cog",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const LANGUAGE_ICONS: Record<string, string> = {
|
|
48
|
-
bash: "lucide:terminal",
|
|
49
|
-
css: "lucide:braces",
|
|
50
|
-
html: "lucide:code-xml",
|
|
51
|
-
javascript: "lucide:file-json",
|
|
52
|
-
js: "lucide:file-json",
|
|
53
|
-
json: "lucide:braces",
|
|
54
|
-
jsx: "lucide:atom",
|
|
55
|
-
markdown: "lucide:file-text",
|
|
56
|
-
mdc: "lucide:file-text",
|
|
57
|
-
shell: "lucide:terminal",
|
|
58
|
-
sh: "lucide:terminal",
|
|
59
|
-
ts: "logos:typescript-icon",
|
|
60
|
-
tsx: "lucide:atom",
|
|
61
|
-
typescript: "logos:typescript-icon",
|
|
62
|
-
vue: "logos:vue",
|
|
63
|
-
"vue-html": "logos:vue",
|
|
64
|
-
yaml: "lucide:file-cog",
|
|
65
|
-
yml: "lucide:file-cog",
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export function normalizedFilename(filename: string | null | undefined): string | null {
|
|
69
|
-
return filename?.trim().replace(/^\.\//, "").toLowerCase() || null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function filenameExtension(filename: string | null | undefined): string | null {
|
|
73
|
-
const normalized = normalizedFilename(filename);
|
|
74
|
-
const basename = normalized?.split("/").pop();
|
|
75
|
-
const index = basename?.lastIndexOf(".") ?? -1;
|
|
76
|
-
|
|
77
|
-
if (!basename || index < 0 || index === basename.length - 1) return null;
|
|
78
|
-
return basename.slice(index + 1);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Best-effort icon for a file, by exact name first, then extension. */
|
|
82
|
-
export function resolveFileIcon(filename: string | null | undefined): string {
|
|
83
|
-
const normalized = normalizedFilename(filename);
|
|
84
|
-
if (normalized && FILENAME_ICONS[normalized]) return FILENAME_ICONS[normalized];
|
|
85
|
-
|
|
86
|
-
const extension = filenameExtension(filename);
|
|
87
|
-
if (extension && EXTENSION_ICONS[extension]) return EXTENSION_ICONS[extension];
|
|
88
|
-
|
|
89
|
-
return FALLBACK_FILE_ICON;
|
|
90
|
-
}
|
package/app/utils/index.ts
DELETED
package/app/utils/repository.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { GinkoDocsAppConfig } from "../../shared/types/app-config";
|
|
2
|
-
|
|
3
|
-
type RepositoryConfig = NonNullable<GinkoDocsAppConfig["repository"]>;
|
|
4
|
-
|
|
5
|
-
const repoBase = (repository: RepositoryConfig) => repository.url.replace(/\/$/, "");
|
|
6
|
-
|
|
7
|
-
export function buildRepoIssueUrl(
|
|
8
|
-
repository: RepositoryConfig,
|
|
9
|
-
params: { title: string; body: string },
|
|
10
|
-
): string {
|
|
11
|
-
const url = new URL(`${repoBase(repository)}/issues/new`);
|
|
12
|
-
url.searchParams.set("title", params.title);
|
|
13
|
-
url.searchParams.set("body", params.body);
|
|
14
|
-
return url.toString();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function buildRepoEditUrl(repository: RepositoryConfig, sourcePath: string): string {
|
|
18
|
-
const branch = repository.branch || "main";
|
|
19
|
-
return `${repoBase(repository)}/edit/${encodeURIComponent(branch)}/${sourcePath}`;
|
|
20
|
-
}
|
package/components.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
contentComponentNames as ginkoDocsComponentNames,
|
|
3
|
-
contentComponentPolicy as ginkoDocsComponentPolicy,
|
|
4
|
-
contentComponentTags as ginkoDocsComponentTags,
|
|
5
|
-
} from "./tags";
|
|
6
|
-
export type {
|
|
7
|
-
ContentComponentName as GinkoDocsComponentName,
|
|
8
|
-
ContentComponentTag as GinkoDocsComponentTag,
|
|
9
|
-
} from "./tags";
|