@lupinum/ginko-docs 0.4.0-rc.9 → 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.
Files changed (296) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/LICENSE +1 -1
  3. package/README.md +206 -54
  4. package/app/app.config.ts +42 -55
  5. package/app/assets/docs.css +555 -0
  6. package/app/components/DocsBanner.vue +29 -0
  7. package/app/components/DocsExample.vue +43 -0
  8. package/app/components/DocsFooter.vue +19 -0
  9. package/app/components/DocsHeader.vue +112 -0
  10. package/app/components/DocsLanding.vue +55 -0
  11. package/app/components/DocsLanguage.vue +29 -0
  12. package/app/components/DocsNavTree.vue +38 -0
  13. package/app/components/DocsPage.vue +84 -0
  14. package/app/components/DocsPageActions.vue +47 -0
  15. package/app/components/DocsPost.vue +58 -0
  16. package/app/components/DocsSearch.vue +134 -0
  17. package/app/components/DocsShell.vue +20 -0
  18. package/app/components/DocsSidebar.vue +18 -0
  19. package/app/components/DocsTable.vue +11 -0
  20. package/app/components/DocsThemeToggle.vue +45 -0
  21. package/app/components/DocsToc.vue +73 -0
  22. package/app/composables/useDocs.ts +52 -0
  23. package/app/composables/useDocsBlog.ts +32 -0
  24. package/app/composables/useDocsCopy.ts +17 -0
  25. package/app/composables/useDocsPopover.ts +36 -0
  26. package/app/composables/useDocsSite.ts +10 -0
  27. package/app/docs-pages/blog.vue +47 -0
  28. package/app/docs-pages/changelog.vue +27 -0
  29. package/app/docs-pages/doc.vue +14 -0
  30. package/app/docs-pages/docs.vue +9 -0
  31. package/app/docs-pages/index.vue +12 -0
  32. package/app/docs-pages/post.vue +8 -0
  33. package/app/error.vue +23 -115
  34. package/app/layouts/default.vue +25 -7
  35. package/app/lib/blogTypes.ts +8 -0
  36. package/app/lib/docsFirst.ts +11 -0
  37. package/app/lib/docsInit.ts +18 -0
  38. package/app/lib/docsNav.ts +36 -0
  39. package/app/lib/docsPage.ts +44 -0
  40. package/app/lib/markdownComponents.ts +16 -0
  41. package/app/lib/previewToc.ts +18 -0
  42. package/dist/config.d.mts +232 -0
  43. package/dist/config.d.ts +232 -0
  44. package/dist/config.mjs +114 -0
  45. package/dist/pages.d.mts +5 -0
  46. package/dist/pages.d.ts +5 -0
  47. package/dist/pages.mjs +36 -0
  48. package/dist/shared/ginko-docs.BwbF7JBR.mjs +4 -0
  49. package/i18n/de.json +1 -0
  50. package/i18n/en.json +1 -0
  51. package/icons/README.md +2 -0
  52. package/icons/bluesky.svg +1 -0
  53. package/icons/claude.svg +1 -0
  54. package/icons/discord.svg +1 -0
  55. package/icons/github.svg +1 -0
  56. package/icons/linkedin.svg +1 -0
  57. package/icons/mastodon.svg +1 -0
  58. package/icons/openai.svg +1 -0
  59. package/icons/x.svg +1 -0
  60. package/icons/youtube.svg +1 -0
  61. package/nuxt.config.ts +34 -209
  62. package/package.json +43 -76
  63. package/public/_docs/fonts/LICENSE +188 -0
  64. package/public/_docs/fonts/inter-latin-wght-normal.woff2 +0 -0
  65. package/public/_docs/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  66. package/public/_docs/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  67. package/server/feeds/blog-locale.get.ts +9 -0
  68. package/server/feeds/blog.get.ts +4 -0
  69. package/server/lib/blogFeed.ts +35 -0
  70. package/server/lib/rss.ts +57 -0
  71. package/server/mcp/handler.ts +108 -0
  72. package/server/routes/__ginko/docs-mcp.ts +35 -0
  73. package/shared/paths.ts +4 -0
  74. package/app/app.vue +0 -98
  75. package/app/assets/css/prose.css +0 -3240
  76. package/app/assets/css/tailwind.css +0 -394
  77. package/app/assets/css/theme-palettes.css +0 -309
  78. package/app/assets/css/theme-presets.css +0 -141
  79. package/app/components/OgImage/GinkoDocs.satori.vue +0 -238
  80. package/app/components/content/DocumentPageShell.vue +0 -55
  81. package/app/components/content/Feedback.vue +0 -102
  82. package/app/components/content/ImageZoomDialog.vue +0 -84
  83. package/app/components/content/PageMarkdownCopy.vue +0 -104
  84. package/app/components/content/imageZoom.ts +0 -25
  85. package/app/components/mdc/MdcAccordion.vue +0 -67
  86. package/app/components/mdc/MdcAccordionItem.vue +0 -42
  87. package/app/components/mdc/MdcApi.vue +0 -159
  88. package/app/components/mdc/MdcAside.vue +0 -23
  89. package/app/components/mdc/MdcCallout.vue +0 -61
  90. package/app/components/mdc/MdcCard.vue +0 -175
  91. package/app/components/mdc/MdcCards.vue +0 -30
  92. package/app/components/mdc/MdcCenter.vue +0 -71
  93. package/app/components/mdc/MdcCodeGroup.vue +0 -197
  94. package/app/components/mdc/MdcCodeTree.vue +0 -234
  95. package/app/components/mdc/MdcCollapse.vue +0 -61
  96. package/app/components/mdc/MdcColumn.vue +0 -70
  97. package/app/components/mdc/MdcDropcap.vue +0 -84
  98. package/app/components/mdc/MdcError.vue +0 -23
  99. package/app/components/mdc/MdcExcerpt.vue +0 -26
  100. package/app/components/mdc/MdcFigure.vue +0 -85
  101. package/app/components/mdc/MdcFiles.vue +0 -120
  102. package/app/components/mdc/MdcIdea.vue +0 -17
  103. package/app/components/mdc/MdcInfo.vue +0 -17
  104. package/app/components/mdc/MdcInlineToc.vue +0 -51
  105. package/app/components/mdc/MdcKbd.vue +0 -10
  106. package/app/components/mdc/MdcLayout.vue +0 -57
  107. package/app/components/mdc/MdcNote.vue +0 -17
  108. package/app/components/mdc/MdcQuiz.vue +0 -241
  109. package/app/components/mdc/MdcQuizQuestion.vue +0 -221
  110. package/app/components/mdc/MdcReadMore.vue +0 -70
  111. package/app/components/mdc/MdcSteps.vue +0 -59
  112. package/app/components/mdc/MdcSuccess.vue +0 -23
  113. package/app/components/mdc/MdcTab.vue +0 -17
  114. package/app/components/mdc/MdcTabs.vue +0 -150
  115. package/app/components/mdc/MdcTimeline.vue +0 -17
  116. package/app/components/mdc/MdcTimelineItem.vue +0 -40
  117. package/app/components/mdc/MdcWarning.vue +0 -23
  118. package/app/components/mdc/api.utils.ts +0 -132
  119. package/app/components/mdc/code-tree.utils.ts +0 -22
  120. package/app/components/mdc/icons.ts +0 -10
  121. package/app/components/mdc/quiz.utils.ts +0 -38
  122. package/app/components/prose/ProseA.vue +0 -27
  123. package/app/components/prose/ProseBlockquote.vue +0 -30
  124. package/app/components/prose/ProseCode.vue +0 -14
  125. package/app/components/prose/ProseEm.vue +0 -14
  126. package/app/components/prose/ProseH1.vue +0 -18
  127. package/app/components/prose/ProseH2.vue +0 -29
  128. package/app/components/prose/ProseH3.vue +0 -29
  129. package/app/components/prose/ProseH4.vue +0 -29
  130. package/app/components/prose/ProseH5.vue +0 -15
  131. package/app/components/prose/ProseH6.vue +0 -15
  132. package/app/components/prose/ProseHr.vue +0 -12
  133. package/app/components/prose/ProseImg.vue +0 -55
  134. package/app/components/prose/ProseLi.vue +0 -14
  135. package/app/components/prose/ProseOl.vue +0 -14
  136. package/app/components/prose/ProseP.vue +0 -14
  137. package/app/components/prose/ProsePre.vue +0 -180
  138. package/app/components/prose/ProseStrong.vue +0 -14
  139. package/app/components/prose/ProseTable.vue +0 -18
  140. package/app/components/prose/ProseTbody.vue +0 -14
  141. package/app/components/prose/ProseTd.vue +0 -14
  142. package/app/components/prose/ProseTh.vue +0 -14
  143. package/app/components/prose/ProseThead.vue +0 -14
  144. package/app/components/prose/ProseTr.vue +0 -14
  145. package/app/components/prose/ProseUl.vue +0 -14
  146. package/app/components/site/ModeToggle.vue +0 -81
  147. package/app/components/site/SiteBanner.vue +0 -97
  148. package/app/components/site/SiteFooter.vue +0 -90
  149. package/app/components/site/SiteHeader.vue +0 -371
  150. package/app/components/site/SiteHeroCode.vue +0 -173
  151. package/app/components/site/SiteInteractionLayer.vue +0 -42
  152. package/app/components/site/SiteLocaleSwitcher.vue +0 -190
  153. package/app/components/site/SiteLogoMark.vue +0 -12
  154. package/app/components/site/SiteSkipLink.vue +0 -14
  155. package/app/components/site/SiteSocialLinks.vue +0 -33
  156. package/app/components/site/header-utils.ts +0 -3
  157. package/app/components/site/mode-toggle.utils.ts +0 -18
  158. package/app/components/ui/badge/Badge.vue +0 -28
  159. package/app/components/ui/badge/index.ts +0 -24
  160. package/app/components/ui/button/Button.vue +0 -31
  161. package/app/components/ui/button/index.ts +0 -35
  162. package/app/components/ui/collapsible/Collapsible.vue +0 -15
  163. package/app/components/ui/collapsible/CollapsibleContent.vue +0 -25
  164. package/app/components/ui/collapsible/CollapsibleTrigger.vue +0 -12
  165. package/app/components/ui/collapsible/index.ts +0 -3
  166. package/app/components/ui/dropdown-menu/DropdownMenu.vue +0 -15
  167. package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +0 -50
  168. package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +0 -41
  169. package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +0 -15
  170. package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +0 -37
  171. package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +0 -23
  172. package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +0 -14
  173. package/app/components/ui/dropdown-menu/index.ts +0 -7
  174. package/app/components/ui/kbd/Kbd.vue +0 -23
  175. package/app/components/ui/kbd/index.ts +0 -21
  176. package/app/components/ui/scroll-area/ScrollArea.vue +0 -39
  177. package/app/components/ui/scroll-area/ScrollBar.vue +0 -33
  178. package/app/components/ui/scroll-area/index.ts +0 -2
  179. package/app/components/ui/separator/Separator.vue +0 -27
  180. package/app/components/ui/separator/index.ts +0 -1
  181. package/app/components/ui/sheet/Sheet.vue +0 -15
  182. package/app/components/ui/sheet/SheetContent.vue +0 -59
  183. package/app/components/ui/sheet/SheetDescription.vue +0 -21
  184. package/app/components/ui/sheet/SheetOverlay.vue +0 -26
  185. package/app/components/ui/sheet/SheetTitle.vue +0 -21
  186. package/app/components/ui/sheet/SheetTrigger.vue +0 -12
  187. package/app/components/ui/sheet/index.ts +0 -5
  188. package/app/components/ui/tabs/Tabs.vue +0 -24
  189. package/app/components/ui/tabs/TabsList.vue +0 -26
  190. package/app/components/ui/tabs/TabsTrigger.vue +0 -28
  191. package/app/components/ui/tabs/index.ts +0 -3
  192. package/app/composables/site-navigation.utils.ts +0 -133
  193. package/app/composables/useCanonicalUrl.ts +0 -9
  194. package/app/composables/useContentRouteAlternates.ts +0 -35
  195. package/app/composables/useGinkoAnalytics.ts +0 -52
  196. package/app/composables/useGinkoDocsConfig.ts +0 -6
  197. package/app/composables/useGinkoOgImage.ts +0 -20
  198. package/app/composables/useLocalizedPath.ts +0 -12
  199. package/app/composables/useLocalizedRouteSwitch.ts +0 -31
  200. package/app/composables/useMetaKey.ts +0 -18
  201. package/app/composables/usePageMarkdownActions.ts +0 -79
  202. package/app/composables/useProseAppearance.ts +0 -24
  203. package/app/composables/useSchemaJsonLd.ts +0 -25
  204. package/app/composables/useSiteNavigation.ts +0 -64
  205. package/app/config/site.utils.ts +0 -36
  206. package/app/features/docs/components/DocsBreadcrumb.vue +0 -37
  207. package/app/features/docs/components/DocsContributeLinks.vue +0 -75
  208. package/app/features/docs/components/DocsMobileToc.vue +0 -108
  209. package/app/features/docs/components/DocsPageContent.vue +0 -234
  210. package/app/features/docs/components/DocsPageNav.vue +0 -57
  211. package/app/features/docs/components/DocsSidebar.vue +0 -145
  212. package/app/features/docs/components/DocsSidebarDropdown.vue +0 -70
  213. package/app/features/docs/components/DocsSidebarItem.vue +0 -122
  214. package/app/features/docs/components/DocsSidebarList.vue +0 -35
  215. package/app/features/docs/components/DocsSidebarRow.vue +0 -27
  216. package/app/features/docs/components/DocsSidebarTabs.vue +0 -59
  217. package/app/features/docs/components/DocsToc.vue +0 -106
  218. package/app/features/docs/composables/useDocsNavigation.ts +0 -29
  219. package/app/features/docs/composables/useDocsNavigationData.ts +0 -18
  220. package/app/features/docs/composables/useRevealActive.ts +0 -58
  221. package/app/features/docs/composables/useScrollspy.ts +0 -135
  222. package/app/features/docs/docs-navigation.ts +0 -157
  223. package/app/features/docs/toc-context.ts +0 -6
  224. package/app/features/search/command-center.ts +0 -110
  225. package/app/features/search/components/SiteCommandCenter.vue +0 -359
  226. package/app/features/search/components/SiteSearchPageHighlight.vue +0 -126
  227. package/app/features/search/useCommandCenter.ts +0 -292
  228. package/app/layouts/blog.vue +0 -15
  229. package/app/layouts/docs.vue +0 -20
  230. package/app/lib/errors.ts +0 -37
  231. package/app/lib/schema-org.ts +0 -38
  232. package/app/pages/blog/[slug].vue +0 -228
  233. package/app/pages/blog/index.vue +0 -130
  234. package/app/pages/docs/[...slug].vue +0 -10
  235. package/app/pages/docs/index.vue +0 -19
  236. package/app/pages/index.vue +0 -278
  237. package/app/plugins/ginko-docs-theme.ts +0 -14
  238. package/app/plugins/ssr-width.ts +0 -6
  239. package/app/utils/content.ts +0 -67
  240. package/app/utils/file-icons.ts +0 -90
  241. package/app/utils/index.ts +0 -7
  242. package/app/utils/repository.ts +0 -20
  243. package/components.ts +0 -9
  244. package/content-collections.ts +0 -89
  245. package/content.js +0 -200
  246. package/content.ts +0 -104
  247. package/i18n/i18n.config.ts +0 -10
  248. package/i18n/locales.ts +0 -81
  249. package/i18n/messages/global/banner.ts +0 -8
  250. package/i18n/messages/global/blog.ts +0 -18
  251. package/i18n/messages/global/command.ts +0 -49
  252. package/i18n/messages/global/docs.ts +0 -36
  253. package/i18n/messages/global/errors.ts +0 -36
  254. package/i18n/messages/global/feedback.ts +0 -11
  255. package/i18n/messages/global/index.ts +0 -21
  256. package/i18n/messages/global/nav.ts +0 -26
  257. package/i18n/messages/global/site.ts +0 -4
  258. package/i18n/messages/global/theme.ts +0 -9
  259. package/i18n/messages/index.ts +0 -20
  260. package/i18n/messages/localize.ts +0 -35
  261. package/i18n/routes.ts +0 -33
  262. package/icon-bundle.ts +0 -116
  263. package/index.d.ts +0 -37
  264. package/modules/analytics-boundary.ts +0 -23
  265. package/modules/feature-routing.ts +0 -68
  266. package/modules/syntax-highlighting.ts +0 -120
  267. package/public/fonts/public-sans/LICENSE +0 -93
  268. package/public/fonts/public-sans/README.md +0 -12
  269. package/public/fonts/public-sans/public-sans-400-normal-latin.woff +0 -0
  270. package/public/fonts/public-sans/public-sans-500-normal-latin.woff +0 -0
  271. package/public/fonts/public-sans/public-sans-600-normal-latin.woff +0 -0
  272. package/public/fonts/public-sans/public-sans-700-normal-latin.woff +0 -0
  273. package/runtime/generate-favicons.mjs +0 -124
  274. package/runtime/server/agent-markdown.ts +0 -19
  275. package/server/api/_ginko-docs/redirects.get.ts +0 -28
  276. package/server/mcp/tools/get-page.ts +0 -14
  277. package/server/mcp/tools/list-pages.ts +0 -10
  278. package/server/middleware/redirects.ts +0 -22
  279. package/server/plugins/agent-markdown.ts +0 -6
  280. package/server/plugins/sitemap-policy.ts +0 -12
  281. package/server/routes/.well-known/ai-catalog.json.get.ts +0 -10
  282. package/server/routes/[locale]/blog/rss.xml.get.ts +0 -13
  283. package/server/routes/blog/rss.xml.get.ts +0 -5
  284. package/server/routes/mcp/server-card.get.ts +0 -14
  285. package/server/utils/blog-feed.ts +0 -51
  286. package/server/utils/feed.ts +0 -70
  287. package/server/utils/mcp-server-card.ts +0 -45
  288. package/server/utils/redirects.ts +0 -38
  289. package/server/utils/redirects.utils.ts +0 -58
  290. package/server/utils/sitemap.ts +0 -45
  291. package/shared/route-slugs.ts +0 -5
  292. package/shared/theme-palettes.ts +0 -37
  293. package/shared/theme-presets.ts +0 -3
  294. package/shared/types/app-config.ts +0 -172
  295. package/shared/types/nuxt-config.ts +0 -14
  296. package/tags.ts +0 -192
@@ -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>
@@ -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>
@@ -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
- });
@@ -1,6 +0,0 @@
1
- import { provideSSRWidth } from "@vueuse/core";
2
- import { defineNuxtPlugin } from "#imports";
3
-
4
- export default defineNuxtPlugin((nuxtApp) => {
5
- provideSSRWidth(1024, nuxtApp.vueApp);
6
- });
@@ -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";
@@ -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
- }
@@ -1,7 +0,0 @@
1
- import type { ClassValue } from "clsx";
2
- import { clsx } from "clsx";
3
- import { twMerge } from "tailwind-merge";
4
-
5
- export function cn(...inputs: ClassValue[]) {
6
- return twMerge(clsx(inputs));
7
- }
@@ -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";