docmk 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 (70) hide show
  1. package/.claude/skills/pdf/SKILL.md +89 -0
  2. package/.claude/skills/web-scraping/SKILL.md +78 -0
  3. package/CLAUDE.md +90 -0
  4. package/bin/docmk.js +3 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +636 -0
  7. package/dist/index.js.map +1 -0
  8. package/final-site/assets/main-B4orIFxK.css +1 -0
  9. package/final-site/assets/main-CSoKXua6.js +25 -0
  10. package/final-site/favicon.svg +4 -0
  11. package/final-site/index.html +26 -0
  12. package/final-site/robots.txt +4 -0
  13. package/final-site/sitemap.xml +14 -0
  14. package/my-docs/api/README.md +152 -0
  15. package/my-docs/api/advanced.md +260 -0
  16. package/my-docs/getting-started/README.md +24 -0
  17. package/my-docs/tutorials/README.md +272 -0
  18. package/my-docs/tutorials/customization.md +492 -0
  19. package/package.json +59 -0
  20. package/postcss.config.js +6 -0
  21. package/site/assets/main-BZUsYUCF.css +1 -0
  22. package/site/assets/main-q6laQtCD.js +114 -0
  23. package/site/favicon.svg +4 -0
  24. package/site/index.html +23 -0
  25. package/site/robots.txt +4 -0
  26. package/site/sitemap.xml +34 -0
  27. package/site-output/assets/main-B4orIFxK.css +1 -0
  28. package/site-output/assets/main-CSoKXua6.js +25 -0
  29. package/site-output/favicon.svg +4 -0
  30. package/site-output/index.html +26 -0
  31. package/site-output/robots.txt +4 -0
  32. package/site-output/sitemap.xml +14 -0
  33. package/src/builder/index.ts +189 -0
  34. package/src/builder/vite-dev.ts +117 -0
  35. package/src/cli/commands/build.ts +48 -0
  36. package/src/cli/commands/dev.ts +53 -0
  37. package/src/cli/commands/preview.ts +57 -0
  38. package/src/cli/index.ts +42 -0
  39. package/src/client/App.vue +15 -0
  40. package/src/client/components/SearchBox.vue +204 -0
  41. package/src/client/components/Sidebar.vue +18 -0
  42. package/src/client/components/SidebarItem.vue +108 -0
  43. package/src/client/index.html +21 -0
  44. package/src/client/layouts/AppLayout.vue +99 -0
  45. package/src/client/lib/utils.ts +6 -0
  46. package/src/client/main.ts +42 -0
  47. package/src/client/pages/Home.vue +279 -0
  48. package/src/client/pages/SkillPage.vue +565 -0
  49. package/src/client/router.ts +16 -0
  50. package/src/client/styles/global.css +92 -0
  51. package/src/client/utils/routes.ts +69 -0
  52. package/src/parser/index.ts +253 -0
  53. package/src/scanner/index.ts +127 -0
  54. package/src/types/index.ts +45 -0
  55. package/tailwind.config.js +65 -0
  56. package/test-build/assets/main-C2ARPC0e.css +1 -0
  57. package/test-build/assets/main-CHIQpV3B.js +25 -0
  58. package/test-build/favicon.svg +4 -0
  59. package/test-build/index.html +47 -0
  60. package/test-build/robots.txt +4 -0
  61. package/test-build/sitemap.xml +19 -0
  62. package/test-dist/assets/main-B4orIFxK.css +1 -0
  63. package/test-dist/assets/main-CSoKXua6.js +25 -0
  64. package/test-dist/favicon.svg +4 -0
  65. package/test-dist/index.html +26 -0
  66. package/test-dist/robots.txt +4 -0
  67. package/test-dist/sitemap.xml +14 -0
  68. package/tsconfig.json +30 -0
  69. package/tsup.config.ts +13 -0
  70. package/vite.config.ts +21 -0
@@ -0,0 +1,204 @@
1
+ <template>
2
+ <div class="search-box">
3
+ <div class="relative w-full">
4
+ <svg class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
5
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
6
+ </svg>
7
+ <input
8
+ v-model="searchQuery"
9
+ type="text"
10
+ placeholder="Search documentation..."
11
+ class="w-full h-9 pl-9 pr-4 text-sm bg-secondary border border-border rounded-md placeholder-muted-foreground focus-outline"
12
+ @input="handleSearch"
13
+ @focus="showResults = true"
14
+ @blur="hideResults"
15
+ />
16
+ <kbd class="absolute right-3 top-1/2 -translate-y-1/2 hidden sm:inline-flex h-5 items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-xs text-muted-foreground">
17
+ <span>⌘</span>K
18
+ </kbd>
19
+ </div>
20
+
21
+ <!-- Search Results -->
22
+ <div v-if="showResults && (searchResults.length > 0 || searchQuery)" class="absolute top-full left-0 right-0 mt-2 bg-background border border-border rounded-lg shadow-lg z-50 max-h-96 overflow-y-auto">
23
+ <div v-if="searchResults.length === 0 && searchQuery" class="p-4 text-center text-muted-foreground text-sm">
24
+ No results found for "{{ searchQuery }}"
25
+ </div>
26
+ <div v-else>
27
+ <router-link
28
+ v-for="result in searchResults.slice(0, 8)"
29
+ :key="result.path"
30
+ :to="result.route"
31
+ class="block p-3 hover:bg-accent transition-colors border-b border-border last:border-b-0"
32
+ @click="hideResults"
33
+ >
34
+ <div class="font-medium text-foreground text-sm mb-1">{{ result.title }}</div>
35
+ <div v-if="result.description" class="text-xs text-muted-foreground mb-1 line-clamp-2">
36
+ {{ result.description }}
37
+ </div>
38
+ <div class="text-xs text-muted-foreground font-mono">{{ result.pathDisplay }}</div>
39
+ </router-link>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+ import { ref, computed } from 'vue'
47
+ import { useRouter } from 'vue-router'
48
+ import { SkillFile } from '../../types/index.js'
49
+ import { getFileRoute, getPathDisplay } from '../utils/routes.js'
50
+
51
+ const props = defineProps<{
52
+ files: SkillFile[]
53
+ }>()
54
+
55
+ const router = useRouter()
56
+ const searchQuery = ref('')
57
+ const showResults = ref(false)
58
+
59
+ interface SearchResult {
60
+ path: string
61
+ route: string
62
+ title: string
63
+ description?: string
64
+ pathDisplay: string
65
+ score: number
66
+ }
67
+
68
+ const searchResults = computed(() => {
69
+ if (!searchQuery.value.trim()) return []
70
+
71
+ const query = searchQuery.value.toLowerCase().trim()
72
+ const results: SearchResult[] = []
73
+
74
+ for (const file of props.files) {
75
+ const title = file.title || file.name
76
+ const description = file.description || ''
77
+ const content = file.content || ''
78
+ const path = file.path
79
+
80
+ let score = 0
81
+
82
+ // Title match (highest priority)
83
+ if (title.toLowerCase().includes(query)) {
84
+ score += 10
85
+ }
86
+
87
+ // Description match
88
+ if (description.toLowerCase().includes(query)) {
89
+ score += 5
90
+ }
91
+
92
+ // Content match
93
+ if (content.toLowerCase().includes(query)) {
94
+ score += 1
95
+ }
96
+
97
+ // Path match
98
+ if (path.toLowerCase().includes(query)) {
99
+ score += 2
100
+ }
101
+
102
+ if (score > 0) {
103
+ results.push({
104
+ path,
105
+ route: getFileRoute(file),
106
+ title,
107
+ description,
108
+ pathDisplay: getPathDisplay(path),
109
+ score
110
+ })
111
+ }
112
+ }
113
+
114
+ return results.sort((a, b) => b.score - a.score)
115
+ })
116
+
117
+ // Route generation functions moved to utils/routes.ts
118
+
119
+ function handleSearch() {
120
+ showResults.value = true
121
+ }
122
+
123
+ function hideResults() {
124
+ setTimeout(() => {
125
+ showResults.value = false
126
+ }, 200) // Delay to allow clicks
127
+ }
128
+ </script>
129
+
130
+ <style scoped>
131
+ .search-box {
132
+ position: relative;
133
+ width: 100%;
134
+ }
135
+
136
+ .relative { position: relative; }
137
+ .absolute { position: absolute; }
138
+ .w-full { width: 100%; }
139
+ .h-4 { height: 1rem; }
140
+ .w-4 { width: 1rem; }
141
+ .h-5 { height: 1.25rem; }
142
+ .h-9 { height: 2.25rem; }
143
+ .left-3 { left: 0.75rem; }
144
+ .right-3 { right: 0.75rem; }
145
+ .top-1\/2 { top: 50%; }
146
+ .top-full { top: 100%; }
147
+ .left-0 { left: 0; }
148
+ .right-0 { right: 0; }
149
+ .-translate-y-1\/2 { transform: translateY(-50%); }
150
+ .pl-9 { padding-left: 2.25rem; }
151
+ .pr-4 { padding-right: 1rem; }
152
+ .px-1\.5 { padding-left: 0.375rem; padding-right: 0.375rem; }
153
+ .p-3 { padding: 0.75rem; }
154
+ .p-4 { padding: 1rem; }
155
+ .mt-2 { margin-top: 0.5rem; }
156
+ .mb-1 { margin-bottom: 0.25rem; }
157
+ .gap-1 { gap: 0.25rem; }
158
+ .text-sm { font-size: 0.875rem; }
159
+ .text-xs { font-size: 0.75rem; }
160
+ .font-medium { font-weight: 500; }
161
+ .font-mono { font-family: var(--font-mono); }
162
+ .text-muted-foreground { color: var(--color-muted-foreground); }
163
+ .text-foreground { color: var(--color-foreground); }
164
+ .placeholder-muted-foreground::placeholder { color: var(--color-muted-foreground); }
165
+ .bg-secondary { background-color: var(--color-secondary); }
166
+ .bg-muted { background-color: var(--color-muted); }
167
+ .bg-card { background-color: var(--color-card); }
168
+ .bg-background { background-color: hsl(var(--background)); }
169
+ .bg-accent { background-color: var(--color-accent); }
170
+ .border { border-width: 1px; }
171
+ .border-b { border-bottom-width: 1px; }
172
+ .border-border { border-color: var(--color-border); }
173
+ .rounded-md { border-radius: var(--radius-md); }
174
+ .rounded-lg { border-radius: var(--radius-lg); }
175
+ .rounded { border-radius: var(--radius-sm); }
176
+ .shadow-lg { box-shadow: var(--shadow-lg); }
177
+ .z-50 { z-index: 50; }
178
+ .max-h-96 { max-height: 24rem; }
179
+ .overflow-y-auto { overflow-y: auto; }
180
+ .items-center { align-items: center; }
181
+ .text-center { text-align: center; }
182
+ .block { display: block; }
183
+ .hidden { display: none; }
184
+ .inline-flex { display: inline-flex; }
185
+ .transition-colors { transition: color 0.2s ease, background-color 0.2s ease; }
186
+ .hover\:bg-accent:hover { background-color: var(--color-accent); }
187
+ .last\:border-b-0:last-child { border-bottom-width: 0; }
188
+ .line-clamp-2 {
189
+ display: -webkit-box;
190
+ -webkit-line-clamp: 2;
191
+ -webkit-box-orient: vertical;
192
+ overflow: hidden;
193
+ }
194
+
195
+ .focus-outline:focus {
196
+ outline: none;
197
+ border-color: var(--color-ring);
198
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
199
+ }
200
+
201
+ @media (min-width: 640px) {
202
+ .sm\:inline-flex { display: inline-flex; }
203
+ }
204
+ </style>
@@ -0,0 +1,18 @@
1
+ <template>
2
+ <nav class="space-y-1">
3
+ <SidebarItem
4
+ v-for="item in navigation"
5
+ :key="item.text"
6
+ :item="item"
7
+ />
8
+ </nav>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ import { Navigation } from '../../types/index.js'
13
+ import SidebarItem from './SidebarItem.vue'
14
+
15
+ defineProps<{
16
+ navigation: Navigation[]
17
+ }>()
18
+ </script>
@@ -0,0 +1,108 @@
1
+ <template>
2
+ <div class="sidebar-item">
3
+ <!-- Main Item (Parent with Children) -->
4
+ <div v-if="item.children && item.children.length > 0">
5
+ <button
6
+ @click="toggleExpanded"
7
+ class="flex items-center justify-between w-full px-3 py-2 text-sm font-medium text-foreground hover:bg-accent rounded-md transition-colors"
8
+ >
9
+ <div class="flex items-center gap-2">
10
+ <!-- Fallback icon logic: check item text or use generic -->
11
+ <component :is="getIcon(item.text)" class="h-4 w-4 text-muted-foreground" />
12
+ {{ item.text }}
13
+ </div>
14
+ <component
15
+ :is="isExpanded ? ChevronDown : ChevronRight"
16
+ class="h-4 w-4 text-muted-foreground"
17
+ />
18
+ </button>
19
+
20
+ <!-- Children -->
21
+ <div
22
+ v-if="isExpanded"
23
+ class="ml-6 mt-1 space-y-1 border-l border-border pl-3"
24
+ >
25
+ <SidebarItem
26
+ v-for="child in item.children"
27
+ :key="child.text"
28
+ :item="child"
29
+ />
30
+ </div>
31
+ </div>
32
+
33
+ <!-- Leaf Item (Link) -->
34
+ <router-link
35
+ v-else-if="item.link"
36
+ :to="item.link"
37
+ class="block px-3 py-1.5 text-sm rounded-md transition-colors"
38
+ :class="[
39
+ isActive(item.link)
40
+ ? 'text-primary font-medium bg-primary/5'
41
+ : 'text-muted-foreground hover:text-foreground hover:bg-accent'
42
+ ]"
43
+ >
44
+ {{ item.text }}
45
+ </router-link>
46
+
47
+ <!-- Leaf Item (Text only, rare case) -->
48
+ <span v-else class="block px-3 py-1.5 text-sm text-muted-foreground">
49
+ {{ item.text }}
50
+ </span>
51
+ </div>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import { ref, computed } from 'vue'
56
+ import { useRoute } from 'vue-router'
57
+ import { Navigation } from '../../types/index.js'
58
+ import {
59
+ ChevronDown,
60
+ ChevronRight,
61
+ BookOpen,
62
+ Layers,
63
+ Code,
64
+ FileText,
65
+ Folder
66
+ } from 'lucide-vue-next'
67
+
68
+ const props = defineProps<{
69
+ item: Navigation
70
+ }>()
71
+
72
+ const route = useRoute()
73
+ const isExpanded = ref(false)
74
+
75
+ // Simple icon mapping based on text (Acme Docs style)
76
+ const getIcon = (text: string) => {
77
+ const lower = text.toLowerCase()
78
+ if (lower.includes('getting started') || lower.includes('introduction')) return BookOpen
79
+ if (lower.includes('core') || lower.includes('concept')) return Layers
80
+ if (lower.includes('api') || lower.includes('reference')) return Code
81
+ if (lower.includes('guide') || lower.includes('tutorial')) return FileText
82
+ return Folder // Default
83
+ }
84
+
85
+ // Check if active
86
+ const isActive = (link?: string) => {
87
+ if (!link) return false
88
+ return route.path === link
89
+ }
90
+
91
+ // Auto-expand if current route is within this section
92
+ const shouldAutoExpand = computed(() => {
93
+ if (!props.item.children) return false
94
+ return props.item.children.some(child =>
95
+ (child.link && isActive(child.link)) ||
96
+ (child.children && child.children.some(c => c.link && isActive(c.link)))
97
+ )
98
+ })
99
+
100
+ // Initialize expansion state
101
+ if (shouldAutoExpand.value) {
102
+ isExpanded.value = true
103
+ }
104
+
105
+ function toggleExpanded() {
106
+ isExpanded.value = !isExpanded.value
107
+ }
108
+ </script>
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <meta name="description" content="Modern documentation site">
8
+ <title>Documentation</title>
9
+ </head>
10
+ <body>
11
+ <div id="app">
12
+ <div class="loading">
13
+ <div style="text-align: center;">
14
+ <div style="font-size: 1.5rem; margin-bottom: 0.5rem;">📚</div>
15
+ <div>Loading Documentation...</div>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <script type="module" src="/main.ts"></script>
20
+ </body>
21
+ </html>
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <div class="min-h-screen bg-background font-sans antialiased">
3
+ <!-- Header -->
4
+ <header class="sticky top-0 z-50 w-full border-b border-border bg-background/95 backdrop-blur">
5
+ <div class="flex h-14 items-center px-4 lg:px-6">
6
+ <!-- Mobile Menu Toggle -->
7
+ <button
8
+ class="inline-flex items-center justify-center rounded-md p-2 mr-2 md:hidden hover:bg-accent"
9
+ @click="toggleMobileMenu"
10
+ >
11
+ <Menu v-if="!mobileMenuOpen" class="h-5 w-5" />
12
+ <X v-else class="h-5 w-5" />
13
+ </button>
14
+
15
+ <!-- Logo -->
16
+ <router-link to="/" class="flex items-center space-x-2">
17
+ <Zap class="h-6 w-6" />
18
+ <span class="font-bold">{{ config?.siteConfig.title || 'Docs' }}</span>
19
+ </router-link>
20
+
21
+ <!-- Search -->
22
+ <div class="flex-1 mx-4 lg:mx-8 max-w-md">
23
+ <SearchBox v-if="config" :files="config.files" />
24
+ </div>
25
+
26
+ <!-- GitHub -->
27
+ <a
28
+ href="https://github.com"
29
+ target="_blank"
30
+ rel="noopener noreferrer"
31
+ class="p-2 hover:bg-accent rounded-md"
32
+ >
33
+ <Github class="h-5 w-5" />
34
+ </a>
35
+ </div>
36
+ </header>
37
+
38
+ <div class="flex">
39
+ <!-- Mobile Sidebar Overlay -->
40
+ <div
41
+ v-if="mobileMenuOpen"
42
+ class="fixed inset-0 z-40 bg-background/80 backdrop-blur-sm md:hidden"
43
+ @click="toggleMobileMenu"
44
+ />
45
+
46
+ <!-- Sidebar -->
47
+ <aside
48
+ :class="[
49
+ 'fixed md:sticky top-14 z-40 h-[calc(100vh-3.5rem)] w-64 border-r border-border bg-background overflow-y-auto transition-transform md:translate-x-0',
50
+ mobileMenuOpen ? 'translate-x-0' : '-translate-x-full'
51
+ ]"
52
+ >
53
+ <div class="p-4">
54
+ <Sidebar v-if="config" :navigation="config.navigation" />
55
+ </div>
56
+ </aside>
57
+
58
+ <!-- Main Content -->
59
+ <main class="flex-1 min-w-0">
60
+ <div class="mx-auto px-6 py-10 lg:px-12 xl:px-16 max-w-[1400px]">
61
+ <router-view v-if="config" :config="config" />
62
+ <div v-else class="flex h-[50vh] items-center justify-center text-muted-foreground">
63
+ Loading...
64
+ </div>
65
+ </div>
66
+ </main>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <script setup lang="ts">
72
+ import { ref, onMounted } from 'vue'
73
+ import { DocGenConfig } from '../../types/index.js'
74
+ import Sidebar from '../components/Sidebar.vue'
75
+ import SearchBox from '../components/SearchBox.vue'
76
+ import { Menu, X, Github, Zap } from 'lucide-vue-next'
77
+
78
+ const config = ref<DocGenConfig | null>(null)
79
+ const mobileMenuOpen = ref(false)
80
+
81
+ const toggleMobileMenu = () => {
82
+ mobileMenuOpen.value = !mobileMenuOpen.value
83
+ }
84
+
85
+ onMounted(async () => {
86
+ try {
87
+ if (typeof globalThis.__DOCGEN_CONFIG__ !== 'undefined' && globalThis.__DOCGEN_CONFIG__) {
88
+ config.value = globalThis.__DOCGEN_CONFIG__
89
+ } else {
90
+ const response = await fetch('/api/config')
91
+ if (response.ok) {
92
+ config.value = await response.json()
93
+ }
94
+ }
95
+ } catch (error) {
96
+ console.error('Failed to load configuration:', error)
97
+ }
98
+ })
99
+ </script>
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,42 @@
1
+ import { createApp } from 'vue'
2
+ import { createRouter, createWebHistory } from 'vue-router'
3
+ import App from './App.vue'
4
+ import { routes } from './router.js'
5
+ import './styles/global.css'
6
+
7
+ // Load config from API in dev mode
8
+ async function loadConfig() {
9
+ if (!globalThis.__DOCGEN_CONFIG__) {
10
+ try {
11
+ const response = await fetch('/api/config')
12
+ if (response.ok) {
13
+ globalThis.__DOCGEN_CONFIG__ = await response.json()
14
+ }
15
+ } catch (error) {
16
+ console.error('Failed to load config from API:', error)
17
+ }
18
+ }
19
+ }
20
+
21
+ // Initialize app
22
+ async function init() {
23
+ await loadConfig()
24
+
25
+ // Create router
26
+ const router = createRouter({
27
+ history: createWebHistory(),
28
+ routes
29
+ })
30
+
31
+ // Create and mount app
32
+ const app = createApp(App)
33
+ app.use(router)
34
+ app.mount('#app')
35
+
36
+ // Global error handler
37
+ app.config.errorHandler = (err, instance, info) => {
38
+ console.error('Vue Error:', err, info)
39
+ }
40
+ }
41
+
42
+ init()