@murshisoft/docus 1.0.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -0
  3. package/app/app.config.ts +33 -0
  4. package/app/app.vue +79 -0
  5. package/app/components/IconMenuToggle.vue +83 -0
  6. package/app/components/LanguageSelect.vue +83 -0
  7. package/app/components/OgImage/OgImageDocs.vue +78 -0
  8. package/app/components/OgImage/OgImageLanding.vue +75 -0
  9. package/app/components/app/AppFooter.vue +11 -0
  10. package/app/components/app/AppFooterLeft.vue +5 -0
  11. package/app/components/app/AppFooterRight.vue +30 -0
  12. package/app/components/app/AppHeader.vue +82 -0
  13. package/app/components/app/AppHeaderBody.vue +13 -0
  14. package/app/components/app/AppHeaderCTA.vue +3 -0
  15. package/app/components/app/AppHeaderCenter.vue +10 -0
  16. package/app/components/app/AppHeaderLogo.vue +16 -0
  17. package/app/components/docs/DocsAsideLeftBody.vue +12 -0
  18. package/app/components/docs/DocsAsideLeftTop.vue +3 -0
  19. package/app/components/docs/DocsAsideRightBottom.vue +18 -0
  20. package/app/components/docs/DocsPageHeaderLinks.vue +96 -0
  21. package/app/composables/useDocusI18n.ts +35 -0
  22. package/app/error.vue +79 -0
  23. package/app/layouts/default.vue +5 -0
  24. package/app/layouts/docs.vue +15 -0
  25. package/app/pages/[[lang]]/[...slug].vue +145 -0
  26. package/app/plugins/i18n.ts +40 -0
  27. package/app/templates/landing.vue +44 -0
  28. package/app/types/index.d.ts +42 -0
  29. package/app/utils/navigation.ts +7 -0
  30. package/app/utils/prerender.ts +12 -0
  31. package/content.config.ts +67 -0
  32. package/i18n/locales/ar.json +22 -0
  33. package/i18n/locales/be.json +23 -0
  34. package/i18n/locales/bg.json +22 -0
  35. package/i18n/locales/bn.json +22 -0
  36. package/i18n/locales/ca.json +22 -0
  37. package/i18n/locales/ckb.json +18 -0
  38. package/i18n/locales/cs.json +22 -0
  39. package/i18n/locales/da.json +22 -0
  40. package/i18n/locales/de.json +22 -0
  41. package/i18n/locales/el.json +22 -0
  42. package/i18n/locales/en.json +22 -0
  43. package/i18n/locales/es.json +22 -0
  44. package/i18n/locales/et.json +22 -0
  45. package/i18n/locales/fi.json +22 -0
  46. package/i18n/locales/fr.json +22 -0
  47. package/i18n/locales/he.json +22 -0
  48. package/i18n/locales/hi.json +22 -0
  49. package/i18n/locales/hy.json +22 -0
  50. package/i18n/locales/it.json +22 -0
  51. package/i18n/locales/ja.json +22 -0
  52. package/i18n/locales/kk.json +22 -0
  53. package/i18n/locales/km.json +22 -0
  54. package/i18n/locales/ko.json +22 -0
  55. package/i18n/locales/ky.json +22 -0
  56. package/i18n/locales/lb.json +22 -0
  57. package/i18n/locales/ms.json +22 -0
  58. package/i18n/locales/nb.json +22 -0
  59. package/i18n/locales/nl.json +22 -0
  60. package/i18n/locales/pl.json +23 -0
  61. package/i18n/locales/ro.json +22 -0
  62. package/i18n/locales/ru.json +23 -0
  63. package/i18n/locales/sl.json +22 -0
  64. package/i18n/locales/sv.json +22 -0
  65. package/i18n/locales/uk.json +22 -0
  66. package/i18n/locales/ur.json +22 -0
  67. package/i18n/locales/vi.json +22 -0
  68. package/modules/config.ts +116 -0
  69. package/modules/css.ts +32 -0
  70. package/modules/routing.ts +41 -0
  71. package/nuxt.config.ts +84 -0
  72. package/nuxt.schema.ts +218 -0
  73. package/package.json +55 -0
  74. package/server/mcp/tools/get-page.ts +60 -0
  75. package/server/mcp/tools/list-pages.ts +60 -0
  76. package/server/routes/raw/[...slug].md.get.ts +45 -0
  77. package/server/utils/content.ts +37 -0
  78. package/utils/git.ts +110 -0
  79. package/utils/meta.ts +29 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "atau",
4
+ "error": {
5
+ "title": "Halaman tidak dijumpai",
6
+ "description": "Kami mohon maaf, tetapi halaman yang anda cari tidak dapat dijumpai."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Salin halaman",
12
+ "link": "Salin halaman Markdown",
13
+ "view": "Lihat sebagai Markdown",
14
+ "gpt": "Buka di ChatGPT",
15
+ "claude": "Buka di Claude"
16
+ },
17
+ "links": "Komuniti",
18
+ "toc": "Dalam halaman ini",
19
+ "report": "Laporkan masalah",
20
+ "edit": "Edit halaman ini"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "eller",
4
+ "error": {
5
+ "title": "Side ikke funnet",
6
+ "description": "Vi beklager, men siden du leter etter kunne ikke finnes."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Kopier side",
12
+ "link": "Kopier Markdown-side",
13
+ "view": "Vis som Markdown",
14
+ "gpt": "Åpne i ChatGPT",
15
+ "claude": "Åpne i Claude"
16
+ },
17
+ "links": "Fellesskap",
18
+ "toc": "På denne siden",
19
+ "report": "Rapporter et problem",
20
+ "edit": "Rediger denne siden"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "of",
4
+ "error": {
5
+ "title": "Pagina niet gevonden",
6
+ "description": "Sorry, maar deze pagina is niet gevonden."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Kopieer pagina",
12
+ "link": "Kopieer Markdown pagina",
13
+ "view": "Bekijk als Markdown",
14
+ "gpt": "Open in ChatGPT",
15
+ "claude": "Open in Claude"
16
+ },
17
+ "links": "Community",
18
+ "toc": "Op deze pagina",
19
+ "report": "Rapporteer een probleem",
20
+ "edit": "Bewerk deze pagina"
21
+ }
22
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "common": {
3
+ "or": "lub",
4
+ "error": {
5
+ "title": "Nie znaleziono strony",
6
+ "description": "Przepraszamy, ale nie znaleziono tej strony."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Skopiuj stronę",
12
+ "link": "Skopiuj stronę Markdown",
13
+ "view": "Wyświetl jako Markdown",
14
+ "gpt": "Otwórz w ChatGPT",
15
+ "claude": "Otwórz w Claude"
16
+ },
17
+ "links": "Społeczność",
18
+ "toc": "Na tej stronie",
19
+ "report": "Zgłoś problem",
20
+ "edit": "Edytuj tę stronę"
21
+ }
22
+ }
23
+
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "sau",
4
+ "error": {
5
+ "title": "Pagina nu a fost găsită",
6
+ "description": "Ne pare rău, dar această pagină nu a putut fi găsită."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Copiază pagina",
12
+ "link": "Copiază pagina în Markdown",
13
+ "view": "Vezi ca Markdown",
14
+ "gpt": "Deschide în ChatGPT",
15
+ "claude": "Deschide în Claude"
16
+ },
17
+ "links": "Comunitate",
18
+ "toc": "Pe această pagină",
19
+ "report": "Raportează o problemă",
20
+ "edit": "Editează această pagină"
21
+ }
22
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "common": {
3
+ "or": "или",
4
+ "error": {
5
+ "title": "Страница не найдена",
6
+ "description": "Извините, но запрашиваемая страница не найдена."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Копировать страницу",
12
+ "link": "Копировать Markdown-страницу",
13
+ "view": "Просмотреть как Markdown",
14
+ "gpt": "Открыть в ChatGPT",
15
+ "claude": "Открыть в Claude"
16
+ },
17
+ "links": "Сообщество",
18
+ "toc": "На этой странице",
19
+ "report": "Сообщить о проблеме",
20
+ "edit": "Редактировать эту страницу"
21
+ }
22
+ }
23
+
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "ali",
4
+ "error": {
5
+ "title": "Stran ni bila najdena",
6
+ "description": "Opravičujemo se, vendar stran, ki jo iščete, ni bila najdena."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Kopiraj stran",
12
+ "link": "Kopiraj Markdown stran",
13
+ "view": "Prikaži kot Markdown",
14
+ "gpt": "Odpri v ChatGPT",
15
+ "claude": "Odpri v Claude"
16
+ },
17
+ "links": "Skupnost",
18
+ "toc": "Na tej strani",
19
+ "report": "Prijavi težavo",
20
+ "edit": "Uredi to stran"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "eller",
4
+ "error": {
5
+ "title": "Sidan hittades inte",
6
+ "description": "Vi ber om ursäkt, men sidan du letar efter kunde inte hittas."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Kopiera sida",
12
+ "link": "Kopiera Markdown-sida",
13
+ "view": "Visa som Markdown",
14
+ "gpt": "Öppna i ChatGPT",
15
+ "claude": "Öppna i Claude"
16
+ },
17
+ "links": "Gemenskap",
18
+ "toc": "På denna sida",
19
+ "report": "Rapportera ett problem",
20
+ "edit": "Redigera denna sida"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "або",
4
+ "error": {
5
+ "title": "Сторінку не знайдено",
6
+ "description": "Вибачте, але сторінку, яку ви шукаєте, не знайдено."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Скопіювати сторінку",
12
+ "link": "Скопіювати Markdown сторінку",
13
+ "view": "Переглянути як Markdown",
14
+ "gpt": "Відкрити в ChatGPT",
15
+ "claude": "Відкрити в Claude"
16
+ },
17
+ "links": "Спільнота",
18
+ "toc": "На цій сторінці",
19
+ "report": "Повідомити про проблему",
20
+ "edit": "Редагувати цю сторінку"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "یا",
4
+ "error": {
5
+ "title": "صفحہ نہیں ملا",
6
+ "description": "ہمیں افسوس ہے، لیکن آپ جو صفحہ تلاش کر رہے ہیں وہ موجود نہیں ہے۔"
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "صفحہ کاپی کریں",
12
+ "link": "Markdown صفحہ کاپی کریں",
13
+ "view": "Markdown کے طور پر دیکھیں",
14
+ "gpt": "ChatGPT میں کھولیں",
15
+ "claude": "Claude میں کھولیں"
16
+ },
17
+ "links": "کمیونٹی",
18
+ "toc": "اس صفحے پر",
19
+ "report": "مسئلہ کی اطلاع دیں",
20
+ "edit": "اس صفحے کو ترمیم کریں"
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "common": {
3
+ "or": "hoặc",
4
+ "error": {
5
+ "title": "Không tìm thấy trang",
6
+ "description": "Chúng tôi xin lỗi, nhưng trang bạn đang tìm kiếm không tồn tại."
7
+ }
8
+ },
9
+ "docs": {
10
+ "copy": {
11
+ "page": "Sao chép trang",
12
+ "link": "Sao chép trang Markdown",
13
+ "view": "Xem dưới dạng Markdown",
14
+ "gpt": "Mở trong ChatGPT",
15
+ "claude": "Mở trong Claude"
16
+ },
17
+ "links": "Cộng đồng",
18
+ "toc": "Trên trang này",
19
+ "report": "Báo cáo sự cố",
20
+ "edit": "Chỉnh sửa trang này"
21
+ }
22
+ }
@@ -0,0 +1,116 @@
1
+ import { createResolver, defineNuxtModule } from '@nuxt/kit'
2
+ import { defu } from 'defu'
3
+ import { existsSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+ import { inferSiteURL, getPackageJsonMetadata } from '../utils/meta'
6
+ import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
7
+
8
+ export default defineNuxtModule({
9
+ meta: {
10
+ name: 'config',
11
+ },
12
+ async setup(_options, nuxt) {
13
+ const dir = nuxt.options.rootDir
14
+ const url = inferSiteURL()
15
+ const meta = await getPackageJsonMetadata(dir)
16
+ const gitInfo = await getLocalGitInfo(dir) || getGitEnv()
17
+ const siteName = nuxt.options?.site?.name || meta.name || gitInfo?.name || ''
18
+
19
+ nuxt.options.llms = defu(nuxt.options.llms, {
20
+ domain: url,
21
+ title: siteName,
22
+ description: meta.description || '',
23
+ full: {
24
+ title: siteName,
25
+ description: meta.description || '',
26
+ },
27
+ })
28
+
29
+ nuxt.options.site = defu(nuxt.options.site, {
30
+ url,
31
+ name: siteName,
32
+ debug: false,
33
+ })
34
+
35
+ nuxt.options.appConfig.header = defu(nuxt.options.appConfig.header, {
36
+ title: siteName,
37
+ })
38
+
39
+ nuxt.options.appConfig.seo = defu(nuxt.options.appConfig.seo, {
40
+ titleTemplate: `%s - ${siteName}`,
41
+ title: siteName,
42
+ description: meta.description || '',
43
+ })
44
+
45
+ nuxt.options.appConfig.github = defu(nuxt.options.appConfig.github, {
46
+ owner: gitInfo?.owner,
47
+ name: gitInfo?.name,
48
+ url: gitInfo?.url,
49
+ branch: getGitBranch(),
50
+ })
51
+
52
+ /*
53
+ ** I18N
54
+ */
55
+ if (nuxt.options.i18n && nuxt.options.i18n.locales) {
56
+ const { resolve } = createResolver(import.meta.url)
57
+
58
+ // Filter locales to only include existing ones
59
+ const filteredLocales = nuxt.options.i18n.locales.filter((locale) => {
60
+ const localeCode = typeof locale === 'string' ? locale : locale.code
61
+
62
+ // Check for JSON locale file
63
+ const localeFilePath = resolve('../i18n/locales', `${localeCode}.json`)
64
+ const hasLocaleFile = existsSync(localeFilePath)
65
+
66
+ // Check for content folder
67
+ const contentPath = join(nuxt.options.rootDir, 'content', localeCode)
68
+ const hasContentFolder = existsSync(contentPath)
69
+
70
+ if (!hasLocaleFile) {
71
+ console.warn(`[Docus] Locale file not found: ${localeCode}.json - skipping locale "${localeCode}"`)
72
+ }
73
+
74
+ if (!hasContentFolder) {
75
+ console.warn(`[Docus] Content folder not found: content/${localeCode}/ - skipping locale "${localeCode}"`)
76
+ }
77
+
78
+ return hasLocaleFile && hasContentFolder
79
+ })
80
+
81
+ // Override strategy to prefix
82
+ nuxt.options.i18n = {
83
+ ...nuxt.options.i18n,
84
+ strategy: 'prefix',
85
+ }
86
+
87
+ // Expose filtered locales
88
+ nuxt.options.runtimeConfig.public.docus = {
89
+ filteredLocales,
90
+ }
91
+
92
+ nuxt.hook('i18n:registerModule', (register) => {
93
+ const langDir = resolve('../i18n/locales')
94
+
95
+ const locales = filteredLocales?.map((locale) => {
96
+ return typeof locale === 'string'
97
+ ? {
98
+ code: locale,
99
+ name: locale,
100
+ file: `${locale}.json`,
101
+ }
102
+ : {
103
+ code: locale.code,
104
+ name: locale.name || locale.code,
105
+ file: `${locale.code}.json`,
106
+ }
107
+ })
108
+
109
+ register({
110
+ langDir,
111
+ locales,
112
+ })
113
+ })
114
+ }
115
+ },
116
+ })
package/modules/css.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { defineNuxtModule, addTemplate, createResolver } from '@nuxt/kit'
2
+ import { joinURL } from 'ufo'
3
+ import { resolveModulePath } from 'exsolve'
4
+
5
+ export default defineNuxtModule({
6
+ meta: {
7
+ name: 'css',
8
+ },
9
+ async setup(_options, nuxt) {
10
+ const dir = nuxt.options.rootDir
11
+ const resolver = createResolver(import.meta.url)
12
+
13
+ const contentDir = joinURL(dir, 'content')
14
+ const uiPath = resolveModulePath('@nuxt/ui', { from: import.meta.url, conditions: ['style'] })
15
+ const tailwindPath = resolveModulePath('tailwindcss', { from: import.meta.url, conditions: ['style'] })
16
+ const layerDir = resolver.resolve('../app')
17
+
18
+ const cssTemplate = addTemplate({
19
+ filename: 'docus.css',
20
+ getContents: () => {
21
+ return `@import ${JSON.stringify(tailwindPath)};
22
+ @import ${JSON.stringify(uiPath)};
23
+
24
+ @source "${contentDir.replace(/\\/g, '/')}/**/*";
25
+ @source "${layerDir.replace(/\\/g, '/')}/**/*";
26
+ @source "../../app.config.ts";`
27
+ },
28
+ })
29
+
30
+ nuxt.options.css.unshift(cssTemplate.dst)
31
+ },
32
+ })
@@ -0,0 +1,41 @@
1
+ import { defineNuxtModule, extendPages, createResolver } from '@nuxt/kit'
2
+
3
+ export default defineNuxtModule({
4
+ meta: {
5
+ name: 'routing',
6
+ },
7
+ async setup(_options, nuxt) {
8
+ const { resolve } = createResolver(import.meta.url)
9
+
10
+ const isI18nEnabled = !!(nuxt.options.i18n && nuxt.options.i18n.locales)
11
+
12
+ // Ensure useDocusI18n is available in the app
13
+ nuxt.hook('imports:extend', (imports) => {
14
+ if (imports.some(i => i.name === 'useDocusI18n')) return
15
+
16
+ imports.push({
17
+ name: 'useDocusI18n',
18
+ from: resolve('../app/composables/useDocusI18n'),
19
+ })
20
+ })
21
+
22
+ extendPages((pages) => {
23
+ const landingTemplate = resolve('../app/templates/landing.vue')
24
+
25
+ if (isI18nEnabled) {
26
+ pages.push({
27
+ name: 'lang-index',
28
+ path: '/:lang?',
29
+ file: landingTemplate,
30
+ })
31
+ }
32
+ else {
33
+ pages.push({
34
+ name: 'index',
35
+ path: '/',
36
+ file: landingTemplate,
37
+ })
38
+ }
39
+ })
40
+ },
41
+ })
package/nuxt.config.ts ADDED
@@ -0,0 +1,84 @@
1
+ import { extendViteConfig, createResolver, useNuxt } from '@nuxt/kit'
2
+
3
+ const { resolve } = createResolver(import.meta.url)
4
+
5
+ export default defineNuxtConfig({
6
+ modules: [
7
+ resolve('./modules/config'),
8
+ resolve('./modules/routing'),
9
+ resolve('./modules/css'),
10
+ '@nuxt/ui',
11
+ '@nuxt/content',
12
+ '@nuxt/image',
13
+ '@nuxtjs/robots',
14
+ '@nuxtjs/mcp-toolkit',
15
+ 'nuxt-og-image',
16
+ 'nuxt-llms',
17
+ () => {
18
+ // Update @nuxt/content optimizeDeps options
19
+ extendViteConfig((config) => {
20
+ config.optimizeDeps ||= {}
21
+ config.optimizeDeps.include ||= []
22
+ config.optimizeDeps.include.push('@nuxt/content > slugify')
23
+ config.optimizeDeps.include = config.optimizeDeps.include
24
+ .map(id => id.replace(/^@nuxt\/content > /, '@vkuttyp/docus > @nuxt/content > '))
25
+ })
26
+ },
27
+ ],
28
+ devtools: {
29
+ enabled: true,
30
+ },
31
+ content: {
32
+ build: {
33
+ markdown: {
34
+ highlight: {
35
+ langs: ['bash', 'diff', 'json', 'js', 'ts', 'html', 'css', 'vue', 'shell', 'mdc', 'md', 'yaml'],
36
+ },
37
+ remarkPlugins: {
38
+ 'remark-mdc': {
39
+ options: {
40
+ autoUnwrap: true,
41
+ },
42
+ },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ experimental: {
48
+ asyncContext: true,
49
+ },
50
+ compatibilityDate: '2025-07-22',
51
+ nitro: {
52
+ prerender: {
53
+ crawlLinks: true,
54
+ failOnError: false,
55
+ autoSubfolderIndex: false,
56
+ },
57
+ compatibilityDate: {
58
+ // Don't generate observability routes for now
59
+ vercel: '2025-07-14',
60
+ },
61
+ },
62
+ hooks: {
63
+ 'nitro:config'(nitroConfig) {
64
+ const nuxt = useNuxt()
65
+
66
+ const i18nOptions = nuxt.options.i18n
67
+
68
+ const routes: string[] = []
69
+ if (!i18nOptions) {
70
+ routes.push('/')
71
+ }
72
+ else {
73
+ routes.push(...(i18nOptions.locales?.map(locale => typeof locale === 'string' ? `/${locale}` : `/${locale.code}`) || []))
74
+ }
75
+
76
+ nitroConfig.prerender = nitroConfig.prerender || {}
77
+ nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
78
+ nitroConfig.prerender.routes.push(...(routes || []))
79
+ },
80
+ },
81
+ icon: {
82
+ provider: 'iconify',
83
+ },
84
+ })