@lupinum/ginko-docs 0.2.2

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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/app/app.config.ts +49 -0
  4. package/app/app.vue +71 -0
  5. package/app/assets/css/prose.css +1154 -0
  6. package/app/assets/css/tailwind.css +365 -0
  7. package/app/components/OgImage/GinkoDocs.satori.vue +117 -0
  8. package/app/components/content/DocumentPageShell.vue +55 -0
  9. package/app/components/content/Feedback.vue +97 -0
  10. package/app/components/content/PageMarkdownCopy.vue +102 -0
  11. package/app/components/mdc/MdcAccordion.vue +64 -0
  12. package/app/components/mdc/MdcAccordionItem.vue +51 -0
  13. package/app/components/mdc/MdcAlert.vue +34 -0
  14. package/app/components/mdc/MdcAside.vue +27 -0
  15. package/app/components/mdc/MdcCallout.vue +62 -0
  16. package/app/components/mdc/MdcCard.vue +186 -0
  17. package/app/components/mdc/MdcCardGroup.vue +47 -0
  18. package/app/components/mdc/MdcCenter.vue +71 -0
  19. package/app/components/mdc/MdcCodeGroup.vue +193 -0
  20. package/app/components/mdc/MdcColumn.vue +70 -0
  21. package/app/components/mdc/MdcDocImg.vue +25 -0
  22. package/app/components/mdc/MdcDropcap.vue +84 -0
  23. package/app/components/mdc/MdcError.vue +16 -0
  24. package/app/components/mdc/MdcField.vue +62 -0
  25. package/app/components/mdc/MdcFieldGroup.vue +18 -0
  26. package/app/components/mdc/MdcFigure.vue +51 -0
  27. package/app/components/mdc/MdcIdea.vue +16 -0
  28. package/app/components/mdc/MdcInfo.vue +16 -0
  29. package/app/components/mdc/MdcLayout.vue +57 -0
  30. package/app/components/mdc/MdcNote.vue +16 -0
  31. package/app/components/mdc/MdcPassage.vue +32 -0
  32. package/app/components/mdc/MdcQuiz.vue +281 -0
  33. package/app/components/mdc/MdcQuizOption.vue +91 -0
  34. package/app/components/mdc/MdcQuizQuestion.vue +226 -0
  35. package/app/components/mdc/MdcReadMore.vue +90 -0
  36. package/app/components/mdc/MdcReadMoreGroup.vue +18 -0
  37. package/app/components/mdc/MdcShortcut.vue +46 -0
  38. package/app/components/mdc/MdcStep.vue +42 -0
  39. package/app/components/mdc/MdcSteps.vue +25 -0
  40. package/app/components/mdc/MdcSuccess.vue +16 -0
  41. package/app/components/mdc/MdcTab.vue +17 -0
  42. package/app/components/mdc/MdcTabs.vue +132 -0
  43. package/app/components/mdc/MdcTimeline.vue +14 -0
  44. package/app/components/mdc/MdcTimelineItem.vue +49 -0
  45. package/app/components/mdc/MdcWarning.vue +16 -0
  46. package/app/components/mdc/icons.ts +10 -0
  47. package/app/components/prose/ProseA.vue +27 -0
  48. package/app/components/prose/ProseBlockquote.vue +14 -0
  49. package/app/components/prose/ProseCode.vue +14 -0
  50. package/app/components/prose/ProseEm.vue +14 -0
  51. package/app/components/prose/ProseH1.vue +18 -0
  52. package/app/components/prose/ProseH2.vue +24 -0
  53. package/app/components/prose/ProseH3.vue +24 -0
  54. package/app/components/prose/ProseH4.vue +14 -0
  55. package/app/components/prose/ProseHr.vue +12 -0
  56. package/app/components/prose/ProseImg.vue +120 -0
  57. package/app/components/prose/ProseLi.vue +14 -0
  58. package/app/components/prose/ProseOl.vue +14 -0
  59. package/app/components/prose/ProseP.vue +14 -0
  60. package/app/components/prose/ProsePre.vue +217 -0
  61. package/app/components/prose/ProseStrong.vue +14 -0
  62. package/app/components/prose/ProseTable.vue +16 -0
  63. package/app/components/prose/ProseTbody.vue +14 -0
  64. package/app/components/prose/ProseTd.vue +14 -0
  65. package/app/components/prose/ProseTh.vue +14 -0
  66. package/app/components/prose/ProseThead.vue +14 -0
  67. package/app/components/prose/ProseTr.vue +14 -0
  68. package/app/components/prose/ProseUl.vue +14 -0
  69. package/app/components/site/ModeToggle.vue +123 -0
  70. package/app/components/site/SiteBanner.vue +56 -0
  71. package/app/components/site/SiteFooter.vue +80 -0
  72. package/app/components/site/SiteHeader.vue +250 -0
  73. package/app/components/site/SiteInteractionLayer.vue +35 -0
  74. package/app/components/site/SiteLocaleSwitcher.vue +268 -0
  75. package/app/components/site/SiteLogoMark.vue +12 -0
  76. package/app/components/site/SiteSkipLink.vue +14 -0
  77. package/app/components/ui/accordion/Accordion.vue +15 -0
  78. package/app/components/ui/accordion/AccordionContent.vue +23 -0
  79. package/app/components/ui/accordion/AccordionItem.vue +24 -0
  80. package/app/components/ui/accordion/AccordionTrigger.vue +35 -0
  81. package/app/components/ui/accordion/index.ts +4 -0
  82. package/app/components/ui/alert/Alert.vue +21 -0
  83. package/app/components/ui/alert/AlertDescription.vue +22 -0
  84. package/app/components/ui/alert/AlertTitle.vue +22 -0
  85. package/app/components/ui/alert/index.ts +34 -0
  86. package/app/components/ui/badge/Badge.vue +28 -0
  87. package/app/components/ui/badge/index.ts +24 -0
  88. package/app/components/ui/button/Button.vue +31 -0
  89. package/app/components/ui/button/index.ts +35 -0
  90. package/app/components/ui/card/Card.vue +16 -0
  91. package/app/components/ui/card/CardAction.vue +17 -0
  92. package/app/components/ui/card/CardContent.vue +14 -0
  93. package/app/components/ui/card/CardDescription.vue +14 -0
  94. package/app/components/ui/card/CardFooter.vue +14 -0
  95. package/app/components/ui/card/CardHeader.vue +22 -0
  96. package/app/components/ui/card/CardTitle.vue +14 -0
  97. package/app/components/ui/card/card-variants.ts +15 -0
  98. package/app/components/ui/card/index.ts +8 -0
  99. package/app/components/ui/checkbox/Checkbox.vue +35 -0
  100. package/app/components/ui/checkbox/index.ts +1 -0
  101. package/app/components/ui/collapsible/Collapsible.vue +15 -0
  102. package/app/components/ui/collapsible/CollapsibleContent.vue +25 -0
  103. package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  104. package/app/components/ui/collapsible/index.ts +3 -0
  105. package/app/components/ui/dialog/Dialog.vue +15 -0
  106. package/app/components/ui/dialog/DialogClose.vue +12 -0
  107. package/app/components/ui/dialog/DialogContent.vue +69 -0
  108. package/app/components/ui/dialog/DialogDescription.vue +21 -0
  109. package/app/components/ui/dialog/DialogFooter.vue +15 -0
  110. package/app/components/ui/dialog/DialogHeader.vue +12 -0
  111. package/app/components/ui/dialog/DialogOverlay.vue +26 -0
  112. package/app/components/ui/dialog/DialogPortal.vue +12 -0
  113. package/app/components/ui/dialog/DialogTitle.vue +21 -0
  114. package/app/components/ui/dialog/DialogTrigger.vue +12 -0
  115. package/app/components/ui/dialog/index.ts +10 -0
  116. package/app/components/ui/dropdown-menu/DropdownMenu.vue +15 -0
  117. package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +36 -0
  118. package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +40 -0
  119. package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  120. package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
  121. package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +25 -0
  122. package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
  123. package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +37 -0
  124. package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  125. package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  126. package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +15 -0
  127. package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +29 -0
  128. package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
  129. package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +14 -0
  130. package/app/components/ui/dropdown-menu/index.ts +16 -0
  131. package/app/components/ui/input/Input.vue +23 -0
  132. package/app/components/ui/input/index.ts +1 -0
  133. package/app/components/ui/kbd/Kbd.vue +23 -0
  134. package/app/components/ui/kbd/KbdGroup.vue +14 -0
  135. package/app/components/ui/kbd/index.ts +22 -0
  136. package/app/components/ui/label/Label.vue +14 -0
  137. package/app/components/ui/label/index.ts +1 -0
  138. package/app/components/ui/scroll-area/ScrollArea.vue +39 -0
  139. package/app/components/ui/scroll-area/ScrollBar.vue +33 -0
  140. package/app/components/ui/scroll-area/index.ts +2 -0
  141. package/app/components/ui/separator/Separator.vue +27 -0
  142. package/app/components/ui/separator/index.ts +1 -0
  143. package/app/components/ui/sheet/Sheet.vue +15 -0
  144. package/app/components/ui/sheet/SheetClose.vue +12 -0
  145. package/app/components/ui/sheet/SheetContent.vue +59 -0
  146. package/app/components/ui/sheet/SheetDescription.vue +21 -0
  147. package/app/components/ui/sheet/SheetFooter.vue +12 -0
  148. package/app/components/ui/sheet/SheetHeader.vue +12 -0
  149. package/app/components/ui/sheet/SheetOverlay.vue +26 -0
  150. package/app/components/ui/sheet/SheetTitle.vue +21 -0
  151. package/app/components/ui/sheet/SheetTrigger.vue +12 -0
  152. package/app/components/ui/sheet/index.ts +8 -0
  153. package/app/components/ui/switch/Switch.vue +39 -0
  154. package/app/components/ui/switch/index.ts +1 -0
  155. package/app/components/ui/tabs/Tabs.vue +24 -0
  156. package/app/components/ui/tabs/TabsContent.vue +21 -0
  157. package/app/components/ui/tabs/TabsList.vue +26 -0
  158. package/app/components/ui/tabs/TabsTrigger.vue +28 -0
  159. package/app/components/ui/tabs/index.ts +4 -0
  160. package/app/components/ui/textarea/Textarea.vue +31 -0
  161. package/app/components/ui/textarea/index.ts +1 -0
  162. package/app/composables/site-navigation.utils.ts +80 -0
  163. package/app/composables/useCanonicalUrl.ts +9 -0
  164. package/app/composables/useContentRouteAlternates.ts +43 -0
  165. package/app/composables/useGinkoAnalytics.ts +37 -0
  166. package/app/composables/useGinkoDocsConfig.ts +6 -0
  167. package/app/composables/useGinkoOgImage.ts +20 -0
  168. package/app/composables/useLocalizedPath.ts +12 -0
  169. package/app/composables/useLocalizedRouteSwitch.ts +31 -0
  170. package/app/composables/usePageMarkdownActions.ts +79 -0
  171. package/app/composables/useSchemaJsonLd.ts +25 -0
  172. package/app/composables/useSiteNavigation.ts +86 -0
  173. package/app/config/site.utils.ts +36 -0
  174. package/app/error.vue +125 -0
  175. package/app/features/docs/components/DocsContributeLinks.vue +65 -0
  176. package/app/features/docs/components/DocsMobileToc.vue +104 -0
  177. package/app/features/docs/components/DocsPageContent.vue +188 -0
  178. package/app/features/docs/components/DocsPageNav.vue +57 -0
  179. package/app/features/docs/components/DocsSidebar.vue +129 -0
  180. package/app/features/docs/components/DocsSidebarDropdown.vue +70 -0
  181. package/app/features/docs/components/DocsSidebarItem.vue +169 -0
  182. package/app/features/docs/components/DocsSidebarList.vue +35 -0
  183. package/app/features/docs/components/DocsSidebarTabs.vue +35 -0
  184. package/app/features/docs/components/DocsToc.vue +60 -0
  185. package/app/features/docs/composables/useDocsEntryPath.ts +13 -0
  186. package/app/features/docs/composables/useDocsNavigation.ts +29 -0
  187. package/app/features/docs/composables/useDocsNavigationData.ts +15 -0
  188. package/app/features/docs/composables/useScrollspy.ts +67 -0
  189. package/app/features/docs/docs-navigation.ts +170 -0
  190. package/app/features/search/command-center.ts +156 -0
  191. package/app/features/search/components/SiteCommandCenter.vue +323 -0
  192. package/app/features/search/components/SiteSearchPageHighlight.vue +124 -0
  193. package/app/features/search/useCommandCenter.ts +300 -0
  194. package/app/layouts/blog.vue +15 -0
  195. package/app/layouts/default.vue +12 -0
  196. package/app/layouts/docs.vue +20 -0
  197. package/app/lib/errors.ts +84 -0
  198. package/app/lib/schema-org.ts +38 -0
  199. package/app/lib/utils.ts +7 -0
  200. package/app/pages/blog/[slug].vue +211 -0
  201. package/app/pages/blog/index.vue +106 -0
  202. package/app/pages/docs/[...slug].vue +10 -0
  203. package/app/pages/docs/index.vue +23 -0
  204. package/app/pages/index.vue +140 -0
  205. package/app/plugins/ssr-width.ts +6 -0
  206. package/app/utils/content.ts +56 -0
  207. package/app/utils/index.ts +1 -0
  208. package/components.ts +9 -0
  209. package/content.js +175 -0
  210. package/content.ts +148 -0
  211. package/i18n/i18n.config.ts +10 -0
  212. package/i18n/locales.ts +81 -0
  213. package/i18n/messages/global/banner.ts +8 -0
  214. package/i18n/messages/global/blog.ts +14 -0
  215. package/i18n/messages/global/command.ts +44 -0
  216. package/i18n/messages/global/docs.ts +32 -0
  217. package/i18n/messages/global/errors.ts +36 -0
  218. package/i18n/messages/global/feedback.ts +11 -0
  219. package/i18n/messages/global/index.ts +21 -0
  220. package/i18n/messages/global/nav.ts +18 -0
  221. package/i18n/messages/global/site.ts +4 -0
  222. package/i18n/messages/global/theme.ts +7 -0
  223. package/i18n/messages/index.ts +20 -0
  224. package/i18n/messages/localize.ts +35 -0
  225. package/i18n/messages/source.ts +5 -0
  226. package/i18n/routes.ts +33 -0
  227. package/icon-bundle.ts +141 -0
  228. package/index.d.ts +13 -0
  229. package/modules/feature-routing.ts +32 -0
  230. package/nuxt.config.ts +194 -0
  231. package/package.json +91 -0
  232. package/runtime/markdown/inline-code-highlight.ts +155 -0
  233. package/runtime/server/agent-markdown.ts +21 -0
  234. package/runtime/utils.ts +7 -0
  235. package/server/mcp/tools/get-page.ts +14 -0
  236. package/server/mcp/tools/list-pages.ts +10 -0
  237. package/server/plugins/agent-markdown.ts +6 -0
  238. package/shared/route-slugs.ts +5 -0
  239. package/shared/types/app-config.ts +100 -0
  240. package/tags.ts +204 -0
@@ -0,0 +1,81 @@
1
+ export const defaultLocale = "en" as const;
2
+
3
+ export const locales = [
4
+ {
5
+ code: "en",
6
+ language: "en-US",
7
+ name: "English",
8
+ nativeName: "English",
9
+ shortLabel: "EN",
10
+ flagIcon: "circle-flags:us",
11
+ },
12
+ {
13
+ code: "de",
14
+ language: "de-DE",
15
+ name: "Deutsch",
16
+ nativeName: "Deutsch",
17
+ shortLabel: "DE",
18
+ flagIcon: "circle-flags:de",
19
+ },
20
+ ] as const;
21
+
22
+ export type LocaleCode = (typeof locales)[number]["code"];
23
+
24
+ export const localeCodes = locales.map((locale) => locale.code) as [LocaleCode, ...LocaleCode[]];
25
+ export const nonDefaultLocaleCodes = localeCodes.filter((locale) => locale !== defaultLocale);
26
+ export const localeIconNames = locales.map((locale) => locale.flagIcon);
27
+
28
+ export function isLocaleCode(value: string): value is LocaleCode {
29
+ return localeCodes.includes(value as LocaleCode);
30
+ }
31
+
32
+ export function isDefaultLocale(
33
+ locale: LocaleCode,
34
+ primaryLocale: LocaleCode = defaultLocale,
35
+ ): boolean {
36
+ return locale === primaryLocale;
37
+ }
38
+
39
+ export function localePrefix(
40
+ locale: LocaleCode,
41
+ primaryLocale: LocaleCode = defaultLocale,
42
+ ): string {
43
+ return isDefaultLocale(locale, primaryLocale) ? "" : `/${locale}`;
44
+ }
45
+
46
+ export function localizedPath(
47
+ locale: LocaleCode,
48
+ path: string,
49
+ primaryLocale: LocaleCode = defaultLocale,
50
+ ): string {
51
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
52
+ const prefix = localePrefix(locale, primaryLocale);
53
+
54
+ if (!prefix) return normalizedPath;
55
+ if (normalizedPath === "/") return prefix;
56
+ if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) return normalizedPath;
57
+ return `${prefix}${normalizedPath}`;
58
+ }
59
+
60
+ export function localeFromPath(
61
+ path: string,
62
+ primaryLocale: LocaleCode = defaultLocale,
63
+ supportedLocales: readonly LocaleCode[] = localeCodes,
64
+ ): LocaleCode {
65
+ const normalizedPath = path.replace(/\/{2,}/g, "/") || "/";
66
+ const routeLocale = supportedLocales.find(
67
+ (locale) => normalizedPath === `/${locale}` || normalizedPath.startsWith(`/${locale}/`),
68
+ );
69
+
70
+ return routeLocale ?? primaryLocale;
71
+ }
72
+
73
+ export function stripLocalePrefix(path: string, locale: LocaleCode): string {
74
+ const normalizedPath = path.replace(/\/{2,}/g, "/") || "/";
75
+ const prefix = `/${locale}`;
76
+
77
+ if (isDefaultLocale(locale)) return normalizedPath;
78
+ if (normalizedPath === prefix) return "/";
79
+ if (normalizedPath.startsWith(`${prefix}/`)) return normalizedPath.slice(prefix.length) || "/";
80
+ return normalizedPath;
81
+ }
@@ -0,0 +1,8 @@
1
+ export const banner = {
2
+ text: {
3
+ de: "Neu: die Ginko-Content-Dokumentationspipeline.",
4
+ en: "Introducing the Ginko Content docs pipeline.",
5
+ },
6
+ linkLabel: { de: "Ankündigung lesen", en: "Read the announcement" },
7
+ dismiss: { de: "Banner schließen", en: "Dismiss banner" },
8
+ } as const;
@@ -0,0 +1,14 @@
1
+ export const blog = {
2
+ title: { de: "Blog", en: "Blog" },
3
+ pageTitle: { de: "Blog", en: "Blog" },
4
+ description: {
5
+ de: "Neuigkeiten, Anleitungen und technische Einblicke.",
6
+ en: "Updates, guides, and technical insights.",
7
+ },
8
+ fallbackTitle: { de: "Blog", en: "Blog" },
9
+ fallbackDescription: { de: "Ein Dokumentationsartikel.", en: "A documentation article." },
10
+ back: { de: "Zurück zum Blog", en: "Back to Blog" },
11
+ toc: { de: "Im Artikel", en: "In this article" },
12
+ readMore: { de: "Weiterlesen", en: "Read more" },
13
+ moreArticles: { de: "Weitere Artikel", en: "More articles" },
14
+ } as const;
@@ -0,0 +1,44 @@
1
+ export const command = {
2
+ title: { de: "Command Center", en: "Command center" },
3
+ description: {
4
+ de: "Navigation, Dokumentation und Aktionen durchsuchen.",
5
+ en: "Search navigation, documentation, and shortcuts.",
6
+ },
7
+ placeholder: { de: "Docs, Seiten, Aktionen suchen...", en: "Search docs, pages, actions..." },
8
+ noResults: { de: 'Keine Ergebnisse für "{query}"', en: 'No results for "{query}"' },
9
+ noResultsHelp: {
10
+ de: "Suche nach einem Seitentitel, Docs-Bereich oder Stichwort.",
11
+ en: "Try searching for a page title, doc section, or keyword.",
12
+ },
13
+ clear: { de: "Suche löschen", en: "Clear search" },
14
+ navigate: { de: "Navigieren", en: "Navigate" },
15
+ open: { de: "Öffnen", en: "Open" },
16
+ close: { de: "Schließen", en: "Close" },
17
+ closeHint: { de: "Esc schließen", en: "Esc closes" },
18
+ groups: {
19
+ recent: { de: "Zuletzt", en: "Recent" },
20
+ pages: { de: "Navigation", en: "Navigation" },
21
+ docs_nav: { de: "Docs-Navigation", en: "Docs Navigation" },
22
+ blog: { de: "Blog-Ergebnisse", en: "Blog Results" },
23
+ docs: { de: "Docs-Ergebnisse", en: "Docs Results" },
24
+ actions: { de: "Aktionen", en: "Actions" },
25
+ },
26
+ pages: {
27
+ home: {
28
+ title: { de: "Start", en: "Home" },
29
+ description: {
30
+ de: "Landingpage und Produktüberblick",
31
+ en: "Landing page and product overview",
32
+ },
33
+ },
34
+ primary: { de: "Hauptnavigation", en: "Primary navigation" },
35
+ secondary: { de: "Weitere Seite", en: "Secondary page" },
36
+ documentation: { de: "Dokumentationsbereich", en: "Documentation section" },
37
+ },
38
+ actions: {
39
+ github: {
40
+ title: { de: "GitHub öffnen", en: "Open GitHub" },
41
+ description: { de: "Zum Projekt-Repository springen", en: "Jump to the project repository" },
42
+ },
43
+ },
44
+ } as const;
@@ -0,0 +1,32 @@
1
+ export const docs = {
2
+ label: { de: "Dokumentation", en: "Documentation" },
3
+ fallbackTitle: { de: "Dokumentation", en: "Documentation" },
4
+ fallbackDescription: { de: "Starter-Dokumentation.", en: "Starter documentation." },
5
+ pageNavigation: { de: "Seitennavigation", en: "Page navigation" },
6
+ breadcrumbs: { de: "Brotkrümelnavigation", en: "Breadcrumbs" },
7
+ previous: { de: "Zurück", en: "Previous" },
8
+ next: { de: "Weiter", en: "Next" },
9
+ toc: { de: "Auf dieser Seite", en: "On this page" },
10
+ mobileToc: { de: "Inhalt", en: "Docs" },
11
+ copyMarkdown: { de: "Markdown kopieren", en: "Copy Markdown" },
12
+ copyText: { de: "Text kopieren", en: "Copy text" },
13
+ copied: { de: "Kopiert", en: "Copied" },
14
+ copiedText: { de: "Text kopiert", en: "Copied text" },
15
+ copyFailed: { de: "Kopieren fehlgeschlagen", en: "Copy failed" },
16
+ codeSample: { de: "Codebeispiel", en: "Code sample" },
17
+ zoomImage: { de: "Bild vergrößern", en: "Zoom image" },
18
+ moreActions: { de: "Weitere Seitenaktionen", en: "More page actions" },
19
+ copyLink: { de: "Markdown-Link kopieren", en: "Copy Markdown link" },
20
+ viewMarkdown: { de: "Als Markdown anzeigen", en: "View as Markdown" },
21
+ askChatGpt: { de: "In ChatGPT öffnen", en: "Open in ChatGPT" },
22
+ askClaude: { de: "In Claude öffnen", en: "Open in Claude" },
23
+ copyMcpUrl: { de: "MCP-Server-URL kopieren", en: "Copy MCP server URL" },
24
+ contribute: { de: "Zu dieser Seite beitragen", en: "Contribute to this page" },
25
+ editPage: { de: "Diese Seite bearbeiten", en: "Edit this page" },
26
+ reportIssue: { de: "Problem melden", en: "Report an issue" },
27
+ issueTitle: { de: "Dokumentation: {title}", en: "Docs: {title}" },
28
+ issueBody: {
29
+ de: "Dokumentationsseite: {path}\n\nProblem beschreiben:",
30
+ en: "Documentation page: {path}\n\nDescribe the issue:",
31
+ },
32
+ } as const;
@@ -0,0 +1,36 @@
1
+ export const errors = {
2
+ accessDenied: {
3
+ title: { de: "Zugriff verweigert", en: "Access denied" },
4
+ description: {
5
+ de: "Diese Seite ist nicht fuer den oeffentlichen Zugriff freigegeben.",
6
+ en: "This page is not available for public access.",
7
+ },
8
+ },
9
+ actions: {
10
+ contact: { de: "Kontakt aufnehmen", en: "Contact us" },
11
+ docs: { de: "Dokumentation lesen", en: "Read the docs" },
12
+ home: { de: "Zur Startseite", en: "Go home" },
13
+ retry: { de: "Erneut versuchen", en: "Try again" },
14
+ },
15
+ notFound: {
16
+ title: { de: "Seite nicht gefunden", en: "Page not found" },
17
+ description: {
18
+ de: "Die angeforderte Seite existiert nicht oder wurde verschoben.",
19
+ en: "The requested page does not exist or has moved.",
20
+ },
21
+ },
22
+ server: {
23
+ title: { de: "Etwas ist schiefgelaufen", en: "Something went wrong" },
24
+ description: {
25
+ de: "Die Seite konnte gerade nicht geladen werden. Bitte versuche es spaeter erneut oder kontaktiere uns.",
26
+ en: "The page could not be loaded right now. Please try again later or contact us.",
27
+ },
28
+ },
29
+ unavailable: {
30
+ title: { de: "Seite nicht verfuegbar", en: "Page unavailable" },
31
+ description: {
32
+ de: "Diese Anfrage konnte gerade nicht verarbeitet werden.",
33
+ en: "This request could not be processed right now.",
34
+ },
35
+ },
36
+ } as const;
@@ -0,0 +1,11 @@
1
+ export const feedback = {
2
+ label: { de: "War diese Seite hilfreich?", en: "Was this page helpful?" },
3
+ articleLabel: { de: "War dieser Artikel hilfreich?", en: "Was this article helpful?" },
4
+ yes: { de: "Ja", en: "Yes" },
5
+ no: { de: "Nein", en: "No" },
6
+ thanks: { de: "Danke für dein Feedback.", en: "Thanks for your feedback." },
7
+ issueTitle: {
8
+ de: "Feedback: {path}",
9
+ en: "Feedback: {path}",
10
+ },
11
+ } as const;
@@ -0,0 +1,21 @@
1
+ import { site } from "./site";
2
+ import { banner } from "./banner";
3
+ import { nav } from "./nav";
4
+ import { docs } from "./docs";
5
+ import { blog } from "./blog";
6
+ import { theme } from "./theme";
7
+ import { command } from "./command";
8
+ import { feedback } from "./feedback";
9
+ import { errors } from "./errors";
10
+
11
+ export const globalMessages = {
12
+ site,
13
+ banner,
14
+ nav,
15
+ docs,
16
+ blog,
17
+ theme,
18
+ command,
19
+ feedback,
20
+ errors,
21
+ } as const;
@@ -0,0 +1,18 @@
1
+ export const nav = {
2
+ skip: { de: "Zum Hauptinhalt springen", en: "Skip to main content" },
3
+ home: { de: "Startseite", en: "Home" },
4
+ main: { de: "Hauptnavigation", en: "Main navigation" },
5
+ mobile: { de: "Mobile Navigation", en: "Mobile navigation" },
6
+ navigationSection: { de: "Navigation", en: "Navigation" },
7
+ settingsHelp: { de: "Einstellungen & Hilfe", en: "Settings & Help" },
8
+ language: { de: "Sprache wechseln", en: "Change language" },
9
+ openMenu: { de: "Menü öffnen", en: "Open menu" },
10
+ closeMenu: { de: "Menü schließen", en: "Close menu" },
11
+ search: { de: "Suchen", en: "Search" },
12
+ resources: { de: "Ressourcen", en: "Resources" },
13
+ company: { de: "Unternehmen", en: "Company" },
14
+ documentation: { de: "Dokumentation", en: "Documentation" },
15
+ blog: { de: "Blog", en: "Blog" },
16
+ github: { de: "GitHub", en: "GitHub" },
17
+ externalLink: { de: "externer Link", en: "external link" },
18
+ } as const;
@@ -0,0 +1,4 @@
1
+ export const site = {
2
+ footer: { de: "Alle Rechte vorbehalten.", en: "All rights reserved." },
3
+ openSourceBy: { de: "Open Source von", en: "Open source by" },
4
+ } as const;
@@ -0,0 +1,7 @@
1
+ export const theme = {
2
+ label: { de: "Darstellung", en: "Appearance" },
3
+ toggle: { de: "Darstellung umschalten", en: "Toggle theme" },
4
+ light: { de: "Hell", en: "Light" },
5
+ dark: { de: "Dunkel", en: "Dark" },
6
+ system: { de: "System", en: "System" },
7
+ } as const;
@@ -0,0 +1,20 @@
1
+ import { type LocaleCode, localeCodes } from "../locales";
2
+ import { localizedRoutes } from "../routes";
3
+ import { localizeMessages } from "./localize";
4
+ import { messageSource } from "./source";
5
+
6
+ export const messages = localeCodes.reduce(
7
+ (localizedMessages, locale) => {
8
+ localizedMessages[locale] = {
9
+ ...localizeMessages(messageSource, locale),
10
+ routes: localizedRoutes[locale],
11
+ };
12
+ return localizedMessages;
13
+ },
14
+ {} as Record<
15
+ LocaleCode,
16
+ ReturnType<typeof localizeMessages<typeof messageSource>> & {
17
+ routes: (typeof localizedRoutes)[LocaleCode];
18
+ }
19
+ >,
20
+ );
@@ -0,0 +1,35 @@
1
+ import type { LocaleCode } from "../locales";
2
+ import { localeCodes } from "../locales";
3
+
4
+ type LocalizedLeaf = Record<LocaleCode, string>;
5
+
6
+ export type LocalizedMessages<T> = T extends LocalizedLeaf
7
+ ? string
8
+ : T extends readonly (infer TItem)[]
9
+ ? LocalizedMessages<TItem>[]
10
+ : T extends object
11
+ ? { [TKey in keyof T]: LocalizedMessages<T[TKey]> }
12
+ : T;
13
+
14
+ function isLocalizedLeaf(value: unknown): value is LocalizedLeaf {
15
+ return (
16
+ !!value &&
17
+ typeof value === "object" &&
18
+ !Array.isArray(value) &&
19
+ Object.keys(value).length === localeCodes.length &&
20
+ localeCodes.every((locale) => typeof (value as Partial<LocalizedLeaf>)[locale] === "string")
21
+ );
22
+ }
23
+
24
+ export function localizeMessages<T>(source: T, locale: LocaleCode): LocalizedMessages<T> {
25
+ if (isLocalizedLeaf(source)) return source[locale] as LocalizedMessages<T>;
26
+ if (Array.isArray(source)) {
27
+ return source.map((item) => localizeMessages(item, locale)) as LocalizedMessages<T>;
28
+ }
29
+ if (source && typeof source === "object") {
30
+ return Object.fromEntries(
31
+ Object.entries(source).map(([key, value]) => [key, localizeMessages(value, locale)]),
32
+ ) as LocalizedMessages<T>;
33
+ }
34
+ return source as LocalizedMessages<T>;
35
+ }
@@ -0,0 +1,5 @@
1
+ import { globalMessages } from "./global";
2
+
3
+ export const messageSource = {
4
+ ...globalMessages,
5
+ } as const;
package/i18n/routes.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { type LocaleCode, localeCodes } from "./locales";
2
+ import { routeSlugs } from "../shared/route-slugs";
3
+
4
+ export const localizedRoutes = {
5
+ en: {
6
+ home: routeSlugs.home.en,
7
+ docs: routeSlugs.docs.en,
8
+ blog: routeSlugs.blog.en,
9
+ },
10
+ de: {
11
+ home: routeSlugs.home.de,
12
+ docs: routeSlugs.docs.de,
13
+ blog: routeSlugs.blog.de,
14
+ },
15
+ } as const satisfies Record<LocaleCode, Record<string, string>>;
16
+
17
+ export type LocalizedRouteKey = keyof (typeof localizedRoutes)[LocaleCode];
18
+
19
+ const catchAllPage = <const T extends string>(route: T): `${T}/[...slug]` => `${route}/[...slug]`;
20
+ const singleSlugPage = <const T extends string>(route: T): `${T}/[slug]` => `${route}/[slug]`;
21
+ const routeMap = <T>(getRoute: (locale: LocaleCode) => T): Record<LocaleCode, T> =>
22
+ Object.fromEntries(localeCodes.map((locale) => [locale, getRoute(locale)])) as Record<
23
+ LocaleCode,
24
+ T
25
+ >;
26
+
27
+ export const i18nPages = {
28
+ index: routeMap((locale) => localizedRoutes[locale].home),
29
+ docs: routeMap((locale) => localizedRoutes[locale].docs),
30
+ "docs-slug": routeMap((locale) => catchAllPage(localizedRoutes[locale].docs)),
31
+ blog: routeMap((locale) => localizedRoutes[locale].blog),
32
+ "blog-slug": routeMap((locale) => singleSlugPage(localizedRoutes[locale].blog)),
33
+ };
package/icon-bundle.ts ADDED
@@ -0,0 +1,141 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ export const layerIconNames = [
4
+ "circle-flags:de",
5
+ "circle-flags:us",
6
+ "logos:bun",
7
+ "logos:npm-icon",
8
+ "logos:nuxt-icon",
9
+ "logos:pnpm",
10
+ "logos:typescript-icon",
11
+ "logos:vue",
12
+ "logos:yarn",
13
+ "lucide:arrow-left",
14
+ "lucide:arrow-right",
15
+ "lucide:arrow-up-right",
16
+ "lucide:atom",
17
+ "lucide:badge-check",
18
+ "lucide:blocks",
19
+ "lucide:book-open",
20
+ "lucide:bookmark",
21
+ "lucide:braces",
22
+ "lucide:building-2",
23
+ "lucide:check",
24
+ "lucide:chevron-down",
25
+ "lucide:chevron-left",
26
+ "lucide:chevron-right",
27
+ "lucide:chevrons-up-down",
28
+ "lucide:circle",
29
+ "lucide:circle-alert",
30
+ "lucide:circle-check",
31
+ "lucide:circle-help",
32
+ "lucide:circle-x",
33
+ "lucide:clipboard",
34
+ "lucide:clock",
35
+ "lucide:code-xml",
36
+ "lucide:copy",
37
+ "lucide:file",
38
+ "lucide:file-code",
39
+ "lucide:file-code-2",
40
+ "lucide:file-cog",
41
+ "lucide:file-json",
42
+ "lucide:file-key",
43
+ "lucide:file-text",
44
+ "lucide:git-branch",
45
+ "lucide:github",
46
+ "lucide:hash",
47
+ "lucide:home",
48
+ "lucide:info",
49
+ "lucide:lightbulb",
50
+ "lucide:link",
51
+ "lucide:linkedin",
52
+ "lucide:list",
53
+ "lucide:menu",
54
+ "lucide:message-circle",
55
+ "lucide:monitor",
56
+ "lucide:moon",
57
+ "lucide:newspaper",
58
+ "lucide:package",
59
+ "lucide:palette",
60
+ "lucide:panel-left-open",
61
+ "lucide:pen-line",
62
+ "lucide:plug",
63
+ "lucide:search",
64
+ "lucide:server",
65
+ "lucide:sparkles",
66
+ "lucide:sun",
67
+ "lucide:table",
68
+ "lucide:terminal",
69
+ "lucide:thumbs-down",
70
+ "lucide:thumbs-up",
71
+ "lucide:triangle-alert",
72
+ "lucide:trophy",
73
+ "lucide:x",
74
+ "lucide:zap",
75
+ ] as const;
76
+
77
+ type IconTransform = {
78
+ width?: number;
79
+ height?: number;
80
+ left?: number;
81
+ top?: number;
82
+ rotate?: number;
83
+ hFlip?: boolean;
84
+ vFlip?: boolean;
85
+ };
86
+ type IconData = IconTransform & { body: string };
87
+ type IconAlias = IconTransform & { parent: string };
88
+ type IconCollection = {
89
+ prefix: string;
90
+ width?: number;
91
+ height?: number;
92
+ icons: Record<string, IconData>;
93
+ aliases?: Record<string, IconAlias>;
94
+ };
95
+
96
+ const require = createRequire(import.meta.url);
97
+ const sourceCollections = new Map<string, IconCollection>(
98
+ ["circle-flags", "logos", "lucide"].map((prefix) => [
99
+ prefix,
100
+ require(`@iconify-json/${prefix}/icons.json`) as IconCollection,
101
+ ]),
102
+ );
103
+
104
+ export const layerIconCollections: IconCollection[] = [...sourceCollections.values()].map(
105
+ ({ prefix, width, height }) => ({ prefix, width, height, icons: {}, aliases: {} }),
106
+ );
107
+
108
+ const bundledCollections = new Map(
109
+ layerIconCollections.map((collection) => [collection.prefix, collection]),
110
+ );
111
+
112
+ function includeIcon(prefix: string, name: string, seen = new Set<string>()) {
113
+ const key = `${prefix}:${name}`;
114
+ if (seen.has(key)) return;
115
+ seen.add(key);
116
+
117
+ const source = sourceCollections.get(prefix);
118
+ const target = bundledCollections.get(prefix);
119
+ if (!source || !target) return;
120
+
121
+ const icon = source.icons[name];
122
+ if (icon) {
123
+ target.icons[name] = icon;
124
+ return;
125
+ }
126
+
127
+ const alias = source.aliases?.[name];
128
+ if (!alias) return;
129
+ target.aliases![name] = alias;
130
+ includeIcon(prefix, alias.parent, seen);
131
+ }
132
+
133
+ export function includeIconNames(names: Iterable<string>) {
134
+ for (const icon of names) {
135
+ const separator = icon.indexOf(":");
136
+ if (separator < 1) continue;
137
+ includeIcon(icon.slice(0, separator), icon.slice(separator + 1));
138
+ }
139
+ }
140
+
141
+ includeIconNames(layerIconNames);
package/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { GinkoDocsAppConfig } from "./shared/types/app-config";
2
+
3
+ declare module "nuxt/schema" {
4
+ interface AppConfig {
5
+ ginkoDocs: GinkoDocsAppConfig;
6
+ }
7
+
8
+ interface AppConfigInput {
9
+ ginkoDocs?: Partial<GinkoDocsAppConfig>;
10
+ }
11
+ }
12
+
13
+ export {};
@@ -0,0 +1,32 @@
1
+ import { defineNuxtModule } from "@nuxt/kit";
2
+ import type {} from "@lupinum/ginko-content";
3
+
4
+ interface PageRoute {
5
+ path: string;
6
+ }
7
+
8
+ export function removeBlogPages(pages: PageRoute[], blogEnabled: boolean) {
9
+ if (blogEnabled) return;
10
+
11
+ for (let index = pages.length - 1; index >= 0; index -= 1) {
12
+ const page = pages[index];
13
+ if (page && (page.path === "/blog" || page.path.startsWith("/blog/"))) {
14
+ pages.splice(index, 1);
15
+ }
16
+ }
17
+ }
18
+
19
+ export default defineNuxtModule({
20
+ meta: { name: "ginko-docs-feature-routing" },
21
+ setup(_options, nuxt) {
22
+ let blogEnabled = false;
23
+
24
+ nuxt.hook("content:context", (context) => {
25
+ blogEnabled = Boolean(context.collections.blog);
26
+ });
27
+
28
+ nuxt.hook("pages:extend", (pages) => {
29
+ removeBlogPages(pages, blogEnabled);
30
+ });
31
+ },
32
+ });