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