docus 3.1.0-20250617-135133-677078f → 4.0.0-beta.1

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 (33) hide show
  1. package/README.md +75 -36
  2. package/app/{app/app.config.ts → app.config.ts} +3 -1
  3. package/app/{app/app.vue → app.vue} +3 -1
  4. package/app/{app/components → components}/IconMenuToggle.vue +19 -17
  5. package/app/components/docs/DocsPageHeaderLinks.vue +77 -0
  6. package/app/{app/layouts → layouts}/docs.vue +0 -1
  7. package/app/{app/pages → pages}/[...slug].vue +27 -5
  8. package/app/{app/pages → pages}/index.vue +3 -1
  9. package/app/types/index.d.ts +39 -0
  10. package/app/utils/prerender.ts +12 -0
  11. package/{app/content.config.ts → content.config.ts} +7 -4
  12. package/{app/modules → modules}/default-configs.ts +22 -20
  13. package/{app/nuxt.config.ts → nuxt.config.ts} +13 -7
  14. package/{app/nuxt.schema.ts → nuxt.schema.ts} +16 -53
  15. package/package.json +29 -65
  16. package/{app/server → server}/routes/raw/[...slug].md.get.ts +2 -1
  17. package/{app/app/utils → utils}/git.ts +4 -2
  18. package/LICENSE +0 -21
  19. package/app/app/components/docs/DocsPageHeaderLinks.vue +0 -91
  20. package/dist/main.mjs +0 -100
  21. package/app/{app/assets → assets}/css/main.css +0 -0
  22. package/app/{app/components → components}/OgImage/OgImageDocs.vue +0 -0
  23. package/app/{app/components → components}/OgImage/OgImageLanding.vue +0 -0
  24. package/app/{app/components → components}/app/AppFooter.vue +0 -0
  25. package/app/{app/components → components}/app/AppHeader.vue +0 -0
  26. package/app/{app/components → components}/app/AppHeaderBody.vue +0 -0
  27. package/app/{app/components → components}/app/AppHeaderCTA.vue +0 -0
  28. package/app/{app/components → components}/app/AppHeaderCenter.vue +0 -0
  29. package/app/{app/components → components}/app/AppHeaderLogo.vue +0 -0
  30. package/app/{app/components → components}/docs/DocsAsideLeftTop.vue +0 -0
  31. package/app/{app/components → components}/docs/DocsAsideRightBottom.vue +4 -4
  32. /package/app/{app/error.vue → error.vue} +0 -0
  33. /package/{app/app/utils → utils}/meta.ts +0 -0
package/README.md CHANGED
@@ -1,52 +1,91 @@
1
- [![docus](https://docus-puce.vercel.app/__og-image__/static/og.png)](https://docus.dev)
1
+ # docus
2
2
 
3
- [![npm version][npm-version-src]][npm-version-href]
4
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![License][license-src]][license-href]
6
- [![Nuxt][nuxt-src]][nuxt-href]
3
+ > A minimal and beautiful Nuxt layer for documentation websites
7
4
 
8
- # Docus
5
+ [![npm version](https://img.shields.io/npm/v/docus.svg)](https://www.npmjs.com/package/docus)
6
+ [![npm downloads](https://img.shields.io/npm/dm/docus.svg)](https://www.npmjs.com/package/docus)
9
7
 
10
- Documentation Theme and CLI to write beautiful docs with Markdown.
8
+ This is the official Nuxt layer for [Docus](https://docus.dev), providing a complete documentation theming. It works with the [Docus CLI](https://github.com/nuxtlabs/docus) for rapid project setup.
11
9
 
12
- Ship fast, flexible, and SEO-optimized documentation with beautiful design out of the box. Docus brings together the best of the Nuxt ecosystem:
13
- - [Nuxt 3](https://nuxt.com)
14
- - [Nuxt Content](https://content.nuxt.com/)
15
- - [Nuxt UI](https://ui.nuxt.com/)
16
- - [Nuxt Image](https://image.nuxt.com/)
17
- - [Nuxt LLMs](https://github.com/nuxtlabs/nuxt-llms)
18
- - [UnJS ecosystem](https://unjs.io/)
19
- - [Nuxt Studio](https://content.nuxt.com/studio)
10
+ ## 🚀 Features
20
11
 
21
- ## Contribution
12
+ - ✨ **Beautiful Design** - Clean, modern documentation theme
13
+ - 📱 **Responsive** - Mobile-first responsive design
14
+ - 🌙 **Dark Mode** - Built-in dark/light mode support
15
+ - 🔍 **Search** - Full-text search functionality
16
+ - 📝 **Markdown Enhanced** - Extended markdown with custom components
17
+ - 🎨 **Customizable** - Easy theming and customization
18
+ - ⚡ **Fast** - Optimized for performance
19
+ - 🔧 **TypeScript** - Full TypeScript support
20
+ - 🛠️ **CLI Integration** - Works with Docus CLI for quick project setup
22
21
 
23
- <details>
24
- <summary>Local development</summary>
22
+ ## 📦 Installation
25
23
 
26
- - Clone this repository
27
- - Install the latest LTS version of [Node.js](https://nodejs.org/en/)
28
- - Install dependencies using `pnpm install`
29
- - Run prepare command using `pnpm run dev:prepare`
30
- - Run dev documentation built on top of Docus using `pnpm run dev`
24
+ ```bash
25
+ npm install docus
26
+ ```
31
27
 
32
- </details>
28
+ ## 🏗️ Quick Setup
33
29
 
34
- ## License
30
+ ### Option 1: Docus CLI (Recommended)
35
31
 
36
- Published under the [MIT](https://github.com/unjs/undocs/blob/main/LICENSE) license.
32
+ The easiest way to get started is using the Docus CLI, which automatically sets up a project with this layer:
37
33
 
38
- Docus v3 has been entirely rewritten from scratch and is inspired and copied from [undocs](https://github.com/unjs/undocs) made by [@pi0](https://github.com/pi0) 💚
34
+ ```bash
35
+ # Create a new documentation project
36
+ npx create docus my-docs
39
37
 
38
+ # Navigate to your project
39
+ cd my-docs
40
40
 
41
- <!-- Badges -->
42
- [npm-version-src]: https://img.shields.io/npm/v/docus/latest.svg?style=flat&colorA=020420&colorB=EEEEEE
43
- [npm-version-href]: https://npmjs.com/package/docus
41
+ # Start development
42
+ npm run dev
43
+ ```
44
44
 
45
- [npm-downloads-src]: https://img.shields.io/npm/dm/docus.svg?style=flat&colorA=020420&colorB=EEEEEE
46
- [npm-downloads-href]: https://npm.chart.dev/docus
45
+ This creates a complete documentation project pre-configured with `docus`.
47
46
 
48
- [license-src]: https://img.shields.io/npm/l/docus.svg?style=flat&colorA=020420&colorB=EEEEEE
49
- [license-href]: https://npmjs.com/package/docus
47
+ ### Option 2: Manual Setup
50
48
 
51
- [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
52
- [nuxt-href]: https://nuxt.com
49
+ #### Option 2a: Nuxt Config (recommended)
50
+
51
+ Add the layer to your `nuxt.config.ts`:
52
+
53
+ ```typescript
54
+ export default defineNuxtConfig({
55
+ extends: ['docus']
56
+ })
57
+ ```
58
+
59
+ #### Option 2b: CLI Usage
60
+
61
+ Use directly with Nuxt CLI:
62
+
63
+ ```bash
64
+ # Development
65
+ nuxt dev --extends docus
66
+
67
+ # Build
68
+ nuxt build --extends docus
69
+ ```
70
+
71
+ ## 🔗 Related Packages
72
+
73
+ - [`create-docus`](https://www.npmjs.com/package/create-docus) - CLI tool to scaffold Docus projects
74
+
75
+ ## 📄 License
76
+
77
+ [MIT License](./LICENSE)
78
+
79
+ ## 🤝 Contributing
80
+
81
+ Contributions are welcome! Please feel free to submit a Pull Request.
82
+
83
+ ## 📞 Support
84
+
85
+ - 📖 [Documentation](https://docus.dev)
86
+ - 🐛 [Issues](https://github.com/nuxtlabs/docus/issues)
87
+ - 💬 [Discussions](https://github.com/nuxtlabs/docus/discussions)
88
+
89
+ ---
90
+
91
+ Made with ❤️ for the Nuxt community
@@ -9,7 +9,6 @@ export default defineAppConfig({
9
9
  contentNavigation: {
10
10
  slots: {
11
11
  linkLeadingIcon: 'size-4 mr-1',
12
- listWithChildren: 'border-(--ui-bg-elevated)',
13
12
  linkTrailing: 'hidden',
14
13
  },
15
14
  variants: {
@@ -19,6 +18,9 @@ export default defineAppConfig({
19
18
  },
20
19
  },
21
20
  },
21
+ defaultVariants: {
22
+ variant: 'link',
23
+ },
22
24
  },
23
25
  pageLinks: {
24
26
  slots: {
@@ -2,7 +2,9 @@
2
2
  const { seo } = useAppConfig()
3
3
  const site = useSiteConfig()
4
4
 
5
- const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
5
+ const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'), {
6
+ transform: data => data.find(item => item.path === '/docs')?.children || data || [],
7
+ })
6
8
  const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
7
9
  server: false,
8
10
  })
@@ -1,31 +1,33 @@
1
1
  <script setup lang="ts">
2
2
  import { motion } from 'motion-v'
3
+ import type { VariantType } from 'motion-v'
3
4
 
4
5
  const props = defineProps<{
5
6
  open: boolean
6
7
  }>()
7
8
 
8
- const lineVariants = {
9
+ const variants: { [k: string]: VariantType | ((custom: unknown) => VariantType) } = {
9
10
  normal: {
10
11
  rotate: 0,
11
12
  y: 0,
12
13
  opacity: 1,
13
14
  },
14
- close: (custom: number) => ({
15
- rotate: custom === 1 ? 45 : custom === 3 ? -45 : 0,
16
- y: custom === 1 ? 6 : custom === 3 ? -6 : 0,
17
- opacity: custom === 2 ? 0 : 1,
18
- transition: {
19
- type: 'spring',
20
- stiffness: 260,
21
- damping: 20,
22
- },
23
- }),
15
+ close: (custom: unknown) => {
16
+ const c = custom as number
17
+ return {
18
+ rotate: c === 1 ? 45 : c === 3 ? -45 : 0,
19
+ y: c === 1 ? 6 : c === 3 ? -6 : 0,
20
+ opacity: c === 2 ? 0 : 1,
21
+ transition: {
22
+ type: 'spring',
23
+ stiffness: 260,
24
+ damping: 20,
25
+ },
26
+ }
27
+ },
24
28
  }
25
29
 
26
- const state = computed(() => {
27
- return props.open ? 'close' : 'normal'
28
- })
30
+ const state = computed(() => props.open ? 'close' : 'normal')
29
31
  </script>
30
32
 
31
33
  <template>
@@ -51,7 +53,7 @@ const state = computed(() => {
51
53
  y1="6"
52
54
  x2="20"
53
55
  y2="6"
54
- :variants="lineVariants"
56
+ :variants="variants"
55
57
  :animate="state"
56
58
  :custom="1"
57
59
  class="outline-none"
@@ -61,7 +63,7 @@ const state = computed(() => {
61
63
  y1="12"
62
64
  x2="20"
63
65
  y2="12"
64
- :variants="lineVariants"
66
+ :variants="variants"
65
67
  :animate="state"
66
68
  :custom="2"
67
69
  class="outline-none"
@@ -71,7 +73,7 @@ const state = computed(() => {
71
73
  y1="18"
72
74
  x2="20"
73
75
  y2="18"
74
- :variants="lineVariants"
76
+ :variants="variants"
75
77
  :animate="state"
76
78
  :custom="3"
77
79
  class="outline-none"
@@ -0,0 +1,77 @@
1
+ <script setup lang="ts">
2
+ import { useClipboard } from '@vueuse/core'
3
+
4
+ const route = useRoute()
5
+ const toast = useToast()
6
+ const { copy, copied } = useClipboard()
7
+
8
+ const markdownLink = computed(() => `${window?.location?.origin}/raw${route.path}.md`)
9
+
10
+ const items = [
11
+ {
12
+ label: 'Copy Markdown link',
13
+ icon: 'i-lucide-link',
14
+ onSelect() {
15
+ copy(markdownLink.value)
16
+
17
+ toast.add({
18
+ title: 'Markdown link copied to clipboard',
19
+ icon: 'i-lucide-check-circle',
20
+ color: 'success',
21
+ })
22
+ },
23
+ },
24
+ {
25
+ label: 'View as Markdown',
26
+ icon: 'i-simple-icons:markdown',
27
+ target: '_blank',
28
+ to: markdownLink.value,
29
+ },
30
+ {
31
+ label: 'Open in ChatGPT',
32
+ icon: 'i-simple-icons:openai',
33
+ target: '_blank',
34
+ to: `https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
35
+ },
36
+ {
37
+ label: 'Open in Claude',
38
+ icon: 'i-simple-icons:anthropic',
39
+ target: '_blank',
40
+ to: `https://claude.ai/new?q=${encodeURIComponent(`Read ${markdownLink.value} so I can ask questions about it.`)}`,
41
+ },
42
+ ]
43
+ </script>
44
+
45
+ <template>
46
+ <UButtonGroup size="sm">
47
+ <UButton
48
+ label="Copy page"
49
+ :icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
50
+ color="neutral"
51
+ variant="outline"
52
+ :ui="{
53
+ leadingIcon: [copied ? 'text-primary' : 'text-neutral', 'size-3.5'],
54
+ }"
55
+ @click="copy(markdownLink)"
56
+ />
57
+
58
+ <UDropdownMenu
59
+ size="sm"
60
+ :items="items"
61
+ :content="{
62
+ align: 'end',
63
+ side: 'bottom',
64
+ sideOffset: 8,
65
+ }"
66
+ :ui="{
67
+ content: 'w-48',
68
+ }"
69
+ >
70
+ <UButton
71
+ icon="i-lucide-chevron-down"
72
+ color="neutral"
73
+ variant="outline"
74
+ />
75
+ </UDropdownMenu>
76
+ </UButtonGroup>
77
+ </template>
@@ -13,7 +13,6 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
13
13
 
14
14
  <UContentNavigation
15
15
  highlight
16
- variant="link"
17
16
  :navigation="navigation"
18
17
  />
19
18
  </UPageAside>
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
- import type { ContentNavigationItem } from '@nuxt/content'
3
2
  import { kebabCase } from 'scule'
4
- import { findPageHeadline } from '#ui-pro/utils/content'
3
+ import type { ContentNavigationItem } from '@nuxt/content'
4
+ import { findPageHeadline } from '@nuxt/content/utils'
5
+ import { addPrerenderPath } from '../utils/prerender'
5
6
 
6
7
  definePageMeta({
7
8
  layout: 'docs',
@@ -24,6 +25,9 @@ if (!page.value) {
24
25
  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
25
26
  }
26
27
 
28
+ // Add the page path to the prerender list
29
+ addPrerenderPath(`/raw${route.path}.md`)
30
+
27
31
  const title = page.value.seo?.title || page.value.title
28
32
  const description = page.value.seo?.description || page.value.description
29
33
 
@@ -34,13 +38,24 @@ useSeoMeta({
34
38
  ogDescription: description,
35
39
  })
36
40
 
37
- const headline = computed(() => findPageHeadline(navigation?.value, page.value))
41
+ const headline = computed(() => findPageHeadline(navigation?.value, page.value?.path))
38
42
  defineOgImageComponent('Docs', {
39
43
  headline: headline.value,
40
44
  })
41
45
 
42
46
  const editLink = computed(() => {
43
- return appConfig.github && `${appConfig.github.url}/edit/${appConfig.github.branch}/content/${page.value?.stem}.${page.value?.extension}`
47
+ if (!appConfig.github) {
48
+ return
49
+ }
50
+
51
+ return [
52
+ appConfig.github.url,
53
+ 'edit',
54
+ appConfig.github.branch,
55
+ appConfig.github.rootDir,
56
+ 'content',
57
+ `${page.value?.stem}.${page.value?.extension}`,
58
+ ].filter(Boolean).join('/')
44
59
  })
45
60
  </script>
46
61
 
@@ -49,13 +64,19 @@ const editLink = computed(() => {
49
64
  <UPageHeader
50
65
  :title="page.title"
51
66
  :description="page.description"
52
- :links="page.links"
53
67
  :headline="headline"
54
68
  :ui="{
55
69
  wrapper: 'flex-row items-center flex-wrap justify-between',
56
70
  }"
57
71
  >
58
72
  <template #links>
73
+ <UButton
74
+ v-for="(link, index) in page.links"
75
+ :key="index"
76
+ size="sm"
77
+ v-bind="link"
78
+ />
79
+
59
80
  <DocsPageHeaderLinks />
60
81
  </template>
61
82
  </UPageHeader>
@@ -102,6 +123,7 @@ const editLink = computed(() => {
102
123
  #right
103
124
  >
104
125
  <UContentToc
126
+ highlight
105
127
  :title="appConfig.toc?.title || 'Table of Contents'"
106
128
  :links="page.body?.toc?.links"
107
129
  >
@@ -4,6 +4,8 @@ if (!page.value) {
4
4
  throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
5
5
  }
6
6
 
7
+ // Reconsider it once this is implemented: https://github.com/nuxt/content/issues/3419
8
+ const prose = page.value.meta.prose as boolean
7
9
  const title = page.value.seo?.title || page.value.title
8
10
  const description = page.value.seo?.description || page.value.description
9
11
 
@@ -32,6 +34,6 @@ else {
32
34
  <ContentRenderer
33
35
  v-if="page"
34
36
  :value="page"
35
- :prose="false"
37
+ :prose="prose || false"
36
38
  />
37
39
  </template>
@@ -0,0 +1,39 @@
1
+ declare module 'nuxt/schema' {
2
+ interface AppConfig {
3
+ seo: {
4
+ titleTemplate: string
5
+ title: string
6
+ description: string
7
+ }
8
+ header: {
9
+ title: string
10
+ logo: {
11
+ light: string
12
+ dark: string
13
+ alt: string
14
+ }
15
+ }
16
+ socials: Record<string, string>
17
+ toc: {
18
+ title: string
19
+ bottom: {
20
+ title: string
21
+ links: {
22
+ icon: string
23
+ label: string
24
+ to: string
25
+ target: string
26
+ }[]
27
+ }
28
+ }
29
+ github: {
30
+ owner: string
31
+ name: string
32
+ url: string
33
+ branch: string
34
+ rootDir?: string
35
+ }
36
+ }
37
+ }
38
+
39
+ export {}
@@ -0,0 +1,12 @@
1
+ export const addPrerenderPath = (path: string) => {
2
+ const event = useRequestEvent()
3
+ if (event) {
4
+ event.node.res.setHeader(
5
+ 'x-nitro-prerender',
6
+ [
7
+ event.node.res.getHeader('x-nitro-prerender'),
8
+ path,
9
+ ].filter(Boolean).join(','),
10
+ )
11
+ }
12
+ }
@@ -1,20 +1,23 @@
1
1
  import { defineContentConfig, defineCollection, z } from '@nuxt/content'
2
+ import { useNuxt } from '@nuxt/kit'
3
+ import { joinURL } from 'ufo'
4
+
5
+ const { options } = useNuxt()
6
+ const cwd = joinURL(options.rootDir, 'content')
2
7
 
3
8
  export default defineContentConfig({
4
9
  collections: {
5
10
  landing: defineCollection({
6
11
  type: 'page',
7
12
  source: {
8
- // @ts-expect-error __DOCS_DIR__ is not defined
9
- cwd: globalThis.__DOCS_DIR__,
13
+ cwd,
10
14
  include: 'index.md',
11
15
  },
12
16
  }),
13
17
  docs: defineCollection({
14
18
  type: 'page',
15
19
  source: {
16
- // @ts-expect-error __DOCS_DIR__ is not defined
17
- cwd: globalThis.__DOCS_DIR__,
20
+ cwd,
18
21
  include: '**',
19
22
  exclude: ['index.md'],
20
23
  },
@@ -1,7 +1,7 @@
1
- import { defineNuxtModule } from 'nuxt/kit'
1
+ import { defineNuxtModule } from '@nuxt/kit'
2
2
  import { defu } from 'defu'
3
- import { inferSiteURL, getPackageJsonMetadata } from '../app/utils/meta'
4
- import { getGitBranch, getGitEnv, getLocalGitInfo } from '../app/utils/git'
3
+ import { inferSiteURL, getPackageJsonMetadata } from '../utils/meta'
4
+ import { getGitBranch, getGitEnv, getLocalGitInfo } from '../utils/git'
5
5
 
6
6
  export default defineNuxtModule({
7
7
  meta: {
@@ -14,7 +14,6 @@ export default defineNuxtModule({
14
14
  const gitInfo = await getLocalGitInfo(dir) || getGitEnv()
15
15
  const siteName = nuxt.options?.site?.name || meta.name || gitInfo?.name || ''
16
16
 
17
- // @ts-expect-error llms is not defined in the schema
18
17
  nuxt.options.llms = defu(nuxt.options.llms, {
19
18
  domain: url,
20
19
  title: siteName,
@@ -31,22 +30,25 @@ export default defineNuxtModule({
31
30
  debug: false,
32
31
  })
33
32
 
34
- nuxt.options.appConfig = defu(nuxt.options.appConfig, {
35
- header: {
36
- title: siteName,
37
- },
38
- github: {
39
- owner: gitInfo?.owner,
40
- name: gitInfo?.name,
41
- url: gitInfo?.url,
42
- branch: getGitBranch(),
43
- },
44
- seo: {
45
- titleTemplate: `%s - ${siteName}`,
46
- title: siteName,
47
- description: meta.description || '',
48
- },
49
- toc: {},
33
+ nuxt.options.appConfig.header = defu(nuxt.options.appConfig.header, {
34
+ title: siteName,
35
+ })
36
+
37
+ nuxt.options.appConfig.seo = defu(nuxt.options.appConfig.seo, {
38
+ titleTemplate: `%s - ${siteName}`,
39
+ title: siteName,
40
+ description: meta.description || '',
41
+ })
42
+
43
+ nuxt.options.appConfig.github = defu(nuxt.options.appConfig.github, {
44
+ owner: gitInfo?.owner,
45
+ name: gitInfo?.name,
46
+ url: gitInfo?.url,
47
+ branch: getGitBranch(),
48
+ })
49
+
50
+ nuxt.options.appConfig.toc = defu(nuxt.options.appConfig.toc, {
51
+ title: 'On this page',
50
52
  })
51
53
  },
52
54
  })
@@ -1,7 +1,6 @@
1
- import { extendViteConfig } from '@nuxt/kit'
1
+ import { extendViteConfig, createResolver } from '@nuxt/kit'
2
2
 
3
- // Flag enabled when developing docs theme
4
- const dev = !!process.env.NUXT_DOCS_DEV
3
+ const { resolve } = createResolver(import.meta.url)
5
4
 
6
5
  export default defineNuxtConfig({
7
6
  modules: [
@@ -23,17 +22,24 @@ export default defineNuxtConfig({
23
22
  },
24
23
  ],
25
24
  devtools: {
26
- enabled: dev,
25
+ enabled: true,
27
26
  },
28
- css: ['../app/assets/css/main.css'],
29
- future: {
30
- compatibilityVersion: 4,
27
+ css: [resolve('./app/assets/css/main.css')],
28
+ content: {
29
+ build: {
30
+ markdown: {
31
+ highlight: {
32
+ langs: ['bash', 'diff', 'json', 'js', 'ts', 'html', 'css', 'vue', 'shell', 'mdc', 'md', 'yaml'],
33
+ },
34
+ },
35
+ },
31
36
  },
32
37
  nitro: {
33
38
  prerender: {
34
39
  routes: ['/'],
35
40
  crawlLinks: true,
36
41
  failOnError: false,
42
+ autoSubfolderIndex: false,
37
43
  },
38
44
  },
39
45
  icon: {
@@ -189,67 +189,30 @@ export default defineNuxtSchema({
189
189
  github: group({
190
190
  title: 'GitHub',
191
191
  description: 'GitHub configuration.',
192
- icon: 'i-lucide-github',
192
+ icon: 'i-simple-icons-github',
193
193
  fields: {
194
194
  url: field({
195
195
  type: 'string',
196
196
  title: 'URL',
197
197
  description: 'GitHub URL.',
198
- icon: 'i-lucide-github',
198
+ icon: 'i-simple-icons-github',
199
+ default: '',
200
+ }),
201
+ branch: field({
202
+ type: 'string',
203
+ title: 'Branch',
204
+ description: 'GitHub branch.',
205
+ icon: 'i-lucide-git-branch',
206
+ default: 'main',
207
+ }),
208
+ rootDir: field({
209
+ type: 'string',
210
+ title: 'Root Directory',
211
+ description: 'Root directory of the GitHub repository.',
212
+ icon: 'i-lucide-folder',
199
213
  default: '',
200
214
  }),
201
215
  },
202
216
  }),
203
217
  },
204
218
  })
205
-
206
- declare module '@nuxt/schema' {
207
- interface AppConfig {
208
- seo: {
209
- titleTemplate: string
210
- title: string
211
- description: string
212
- }
213
- ui: {
214
- colors: {
215
- primary: string
216
- neutral: string
217
- }
218
- icons: {
219
- search: string
220
- dark: string
221
- light: string
222
- external: string
223
- chevron: string
224
- hash: string
225
- } & Record<string, string>
226
- }
227
- header: {
228
- title: string
229
- logo: {
230
- light: string
231
- dark: string
232
- alt: string
233
- }
234
- }
235
- socials: Record<string, string>
236
- toc: {
237
- title: string
238
- bottom: {
239
- title: string
240
- links: {
241
- icon: string
242
- label: string
243
- to: string
244
- target: string
245
- }[]
246
- }
247
- }
248
- github: {
249
- owner: string
250
- name: string
251
- url: string
252
- branch: string
253
- }
254
- }
255
- }
package/package.json CHANGED
@@ -1,77 +1,41 @@
1
1
  {
2
2
  "name": "docus",
3
- "description": "Minimal Documentation theme and CLI for shared usage across Nuxt modules",
4
- "repository": {
5
- "type": "git",
6
- "url": "https://github.com/nuxtlabs/docus"
7
- },
3
+ "description": "Nuxt layer for Docus documentation theme",
4
+ "version": "4.0.0-beta.1",
5
+ "type": "module",
6
+ "main": "./nuxt.config.ts",
8
7
  "private": false,
9
- "version": "3.1.0-20250617-135133-677078f",
10
- "keywords": [],
11
- "license": "MIT",
12
- "bin": {
13
- "docus": "./dist/main.mjs"
14
- },
15
8
  "files": [
16
9
  "app",
17
- "dist",
18
- "!app/**/node_modules",
19
- "!app/**/.nuxt",
20
- "!app/**/tsconfig.json"
10
+ "content.config.ts",
11
+ "modules",
12
+ "nuxt.config.ts",
13
+ "nuxt.schema.ts",
14
+ "server",
15
+ "utils",
16
+ "README.md"
21
17
  ],
22
- "scripts": {
23
- "dev": "npm run docs:dev",
24
- "docs:dev": "NUXT_DOCS_DEV=1 npm run docus dev docs",
25
- "docs:build": "npm run docus build docs",
26
- "dev:prepare": "nuxi prepare app && nuxi prepare docs",
27
- "build": "tsup-node ./cli/main.ts --format esm",
28
- "docus": "tsx ./cli/main.ts",
29
- "test": "echo \"Error: no test specified\"",
30
- "lint": "eslint .",
31
- "release": "npm run lint && npm run test && npm run build && release-it"
32
- },
33
18
  "dependencies": {
34
- "@iconify-json/lucide": "^1.2.47",
35
- "@iconify-json/simple-icons": "^1.2.38",
36
- "@iconify-json/vscode-icons": "^1.2.22",
37
- "@nuxt/content": "^3.6.0",
19
+ "@iconify-json/lucide": "^1.2.57",
20
+ "@iconify-json/simple-icons": "^1.2.43",
21
+ "@iconify-json/vscode-icons": "^1.2.23",
22
+ "@nuxt/content": "^3.6.3",
38
23
  "@nuxt/image": "^1.10.0",
39
- "@nuxt/kit": "^3.17.5",
40
- "@nuxt/ui-pro": "^3.1.3",
41
- "@nuxtjs/mdc": "^0.17.0",
42
- "@nuxtjs/robots": "^5.2.10",
43
- "c12": "^3.0.4",
44
- "citty": "^0.1.6",
45
- "dotenv": "^16.5.0",
46
- "git-url-parse": "^16.1.0",
47
- "minimark": "^0.2.0",
48
- "motion-v": "^1.2.1",
49
- "nuxi": "^3.25.1",
24
+ "@nuxt/kit": "^4.0.0",
25
+ "@nuxt/ui-pro": "^3.2.0",
26
+ "@nuxtjs/mdc": "^0.17.1",
27
+ "@nuxtjs/robots": "^5.4.0",
28
+ "@vueuse/core": "^13.5.0",
29
+ "defu": "^6.1.4",
30
+ "motion-v": "^1.5.0",
50
31
  "nuxt-llms": "^0.1.3",
51
- "nuxt-og-image": "^5.1.6",
52
- "pkg-types": "^2.1.0",
32
+ "nuxt-og-image": "^5.1.9",
53
33
  "scule": "^1.3.0",
54
- "tailwindcss": "^4.1.8",
55
- "ufo": "^1.6.1",
56
- "unctx": "^2.4.1",
57
- "unist-util-visit": "^5.0.0"
58
- },
59
- "devDependencies": {
60
- "@nuxt/eslint-config": "^1.4.1",
61
- "@release-it/conventional-changelog": "^10.0.1",
62
- "@stylistic/eslint-plugin": "^4.4.1",
63
- "@types/node": "^24.0.0",
64
- "@typescript-eslint/parser": "^8.34.0",
65
- "changelogen": "^0.6.1",
66
- "eslint": "^9.28.0",
67
- "release-it": "^19.0.3",
68
- "tsup": "^8.5.0",
69
- "tsx": "^4.19.4",
70
- "typescript": "^5.8.3"
34
+ "tailwindcss": "^4.1.11",
35
+ "ufo": "^1.6.1"
71
36
  },
72
37
  "peerDependencies": {
73
- "better-sqlite3": "11.x",
74
- "nuxt": "3.x"
75
- },
76
- "packageManager": "pnpm@10.12.1"
77
- }
38
+ "better-sqlite3": "12.x",
39
+ "nuxt": "4.x"
40
+ }
41
+ }
@@ -1,5 +1,6 @@
1
- import { stringify } from 'minimark/stringify'
2
1
  import { withLeadingSlash } from 'ufo'
2
+ import { stringify } from 'minimark/stringify'
3
+ import { queryCollection } from '@nuxt/content/nitro'
3
4
 
4
5
  export default eventHandler(async (event) => {
5
6
  const slug = getRouterParams(event)['slug.md']
@@ -29,8 +29,10 @@ export function getGitBranch() {
29
29
  }
30
30
  }
31
31
  catch {
32
- return 'main'
32
+ // Ignore error
33
33
  }
34
+
35
+ return 'main'
34
36
  }
35
37
 
36
38
  export async function getLocalGitInfo(rootDir: string): Promise<GitInfo | undefined> {
@@ -63,7 +65,7 @@ async function getLocalGitRemote(dir: string): Promise<string | undefined> {
63
65
  }
64
66
  }
65
67
 
66
- export function getGitEnv(): GitInfo | undefined {
68
+ export function getGitEnv(): GitInfo {
67
69
  // https://github.com/unjs/std-env/issues/59
68
70
  const envInfo = {
69
71
  // Provider
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) NuxtLabs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,91 +0,0 @@
1
- <template>
2
- <UButtonGroup>
3
- <UButton
4
- label="Copy page"
5
- :icon="copyStatus === 'copied' ? 'i-lucide-copy-check' : 'i-lucide-copy'"
6
- color="neutral"
7
- variant="outline"
8
- :loading="copyStatus === 'copying'"
9
- size="xs"
10
- :ui="{
11
- leadingIcon: [copyStatus === 'copied' ? 'text-primary' : 'text-neutral', 'size-3.5'],
12
- }"
13
- @click="copyPage"
14
- />
15
- <UDropdownMenu
16
- size="sm"
17
- :items="items"
18
- :content="{
19
- align: 'end',
20
- side: 'bottom',
21
- sideOffset: 8,
22
- }"
23
- :ui="{
24
- content: 'w-48',
25
- }"
26
- >
27
- <UButton
28
- icon="i-lucide-chevron-down"
29
- size="sm"
30
- color="neutral"
31
- variant="outline"
32
- />
33
- </UDropdownMenu>
34
- </UButtonGroup>
35
- </template>
36
-
37
- <script setup lang="ts">
38
- const route = useRoute()
39
- const copyStatus = ref<'idle' | 'copying' | 'copied'>('idle')
40
-
41
- const items = [
42
- {
43
- label: 'Copy Markdown link',
44
- icon: 'i-lucide-link',
45
- onSelect() {
46
- navigator.clipboard.writeText(`${window.location.origin}/raw${route.path}.md`)
47
- },
48
- },
49
- {
50
- label: 'View as Markdown',
51
- icon: 'i-simple-icons:markdown',
52
- target: '_blank',
53
- onSelect() {
54
- window.open(`${window.location.origin}/raw${route.path}.md`, '_blank')
55
- },
56
- },
57
- {
58
- label: 'Open in ChatGPT',
59
- icon: 'i-simple-icons:openai',
60
- target: '_blank',
61
- onSelect() {
62
- window.open(`https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${window.location.origin}/raw${route.path}.md so I can ask questions about it.`)}`, '_blank')
63
- },
64
- },
65
- {
66
- label: 'Open in Claude',
67
- icon: 'i-simple-icons:anthropic',
68
- target: '_blank',
69
- onSelect() {
70
- window.open(`https://claude.ai/new?q=${encodeURIComponent(`Read ${window.location.origin}/raw${route.path}.md so I can ask questions about it.`)}`, '_blank')
71
- },
72
- },
73
- ]
74
-
75
- async function copyPage() {
76
- copyStatus.value = 'copying'
77
- const markdown = await $fetch<string>(`${window.location.origin}/raw${route.path}.md`)
78
- copyToClipboard(markdown)
79
- copyStatus.value = 'copied'
80
- setTimeout(() => {
81
- copyStatus.value = 'idle'
82
- }, 2000)
83
- }
84
-
85
- function copyToClipboard(text: string) {
86
- // Fix for iOS Safari: https://stackoverflow.com/questions/62327358/javascript-clipboard-api-safari-ios-notallowederror-message
87
- setTimeout(async () => {
88
- await navigator.clipboard.writeText(text)
89
- }, 0)
90
- }
91
- </script>
package/dist/main.mjs DELETED
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // cli/main.ts
4
- import * as dotenv from "dotenv";
5
-
6
- // cli/cli.ts
7
- import { resolve as resolve2 } from "path";
8
- import { defineCommand, runMain } from "citty";
9
-
10
- // cli/setup.ts
11
- import { fileURLToPath } from "url";
12
- import { resolve } from "path";
13
- var appDir = fileURLToPath(new URL("../app", import.meta.url));
14
- var pkgDir = fileURLToPath(new URL("..", import.meta.url));
15
- async function getNuxtConfig(dir, _opts = {}) {
16
- global.__DOCS_DIR__ = resolve(dir, "content");
17
- return {
18
- compatibilityDate: "2025-06-17",
19
- extends: [appDir],
20
- modulesDir: [resolve(pkgDir, "node_modules"), resolve(appDir, "node_modules")]
21
- };
22
- }
23
-
24
- // cli/cli.ts
25
- function createCLI(opts) {
26
- const sharedArgs = {
27
- dir: {
28
- type: "positional",
29
- description: "Docs directory",
30
- required: true,
31
- default: "."
32
- }
33
- };
34
- const init = defineCommand({
35
- meta: {
36
- name: "init",
37
- description: "Initialize a fresh Docus project"
38
- },
39
- args: { ...sharedArgs },
40
- async setup({ args }) {
41
- const dir = resolve2(args.dir);
42
- const { runCommand } = await import("nuxi");
43
- await runCommand("init", [dir, "-t", "gh:nuxtlabs/docus/.starter", dir]);
44
- }
45
- });
46
- const dev = defineCommand({
47
- meta: {
48
- name: "dev",
49
- description: "Start docs in development mode"
50
- },
51
- args: { ...sharedArgs },
52
- async setup({ args }) {
53
- const dir = resolve2(args.dir);
54
- const nuxtConfig = await getNuxtConfig(dir, {
55
- ...opts.setup,
56
- dev: true
57
- });
58
- const { runCommand } = await import("nuxi");
59
- await runCommand("dev", [dir, "--no-fork", "--port", process.env.PORT || "4000"], { overrides: nuxtConfig });
60
- }
61
- });
62
- const build = defineCommand({
63
- meta: {
64
- name: "build",
65
- description: "Build docs for production"
66
- },
67
- args: { ...sharedArgs },
68
- async setup({ args }) {
69
- const dir = resolve2(args.dir);
70
- const nuxtConfig = await getNuxtConfig(dir, opts.setup);
71
- const { runCommand } = await import("nuxi");
72
- await runCommand("build", [dir], { overrides: nuxtConfig });
73
- }
74
- });
75
- const main = defineCommand({
76
- meta: {
77
- name: opts.name,
78
- description: opts.description
79
- },
80
- subCommands: {
81
- init,
82
- dev,
83
- build
84
- }
85
- });
86
- return {
87
- runMain: () => runMain(main)
88
- };
89
- }
90
-
91
- // cli/main.ts
92
- dotenv.config();
93
- var cli = createCLI({
94
- name: "Docus",
95
- description: "Docus Docs CLI",
96
- setup: {
97
- defaults: {}
98
- }
99
- });
100
- cli.runMain();
File without changes
@@ -1,3 +1,7 @@
1
+ <script setup lang="ts">
2
+ const appConfig = useAppConfig()
3
+ </script>
4
+
1
5
  <template>
2
6
  <div
3
7
  v-if="appConfig.toc?.bottom?.links?.length"
@@ -11,7 +15,3 @@
11
15
  />
12
16
  </div>
13
17
  </template>
14
-
15
- <script setup lang="ts">
16
- const appConfig = useAppConfig()
17
- </script>
File without changes
File without changes