specra 0.1.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 (142) hide show
  1. package/LICENSE.MD +21 -0
  2. package/README.md +157 -0
  3. package/dist/app/api/mdx-watch/route.d.mts +12 -0
  4. package/dist/app/api/mdx-watch/route.d.ts +12 -0
  5. package/dist/app/api/mdx-watch/route.js +98 -0
  6. package/dist/app/api/mdx-watch/route.js.map +1 -0
  7. package/dist/app/api/mdx-watch/route.mjs +71 -0
  8. package/dist/app/api/mdx-watch/route.mjs.map +1 -0
  9. package/dist/app/docs-page.d.mts +32 -0
  10. package/dist/app/docs-page.d.ts +32 -0
  11. package/dist/app/docs-page.js +4072 -0
  12. package/dist/app/docs-page.js.map +1 -0
  13. package/dist/app/docs-page.mjs +14 -0
  14. package/dist/app/docs-page.mjs.map +1 -0
  15. package/dist/app/layout.css +297 -0
  16. package/dist/app/layout.css.map +1 -0
  17. package/dist/app/layout.d.mts +19 -0
  18. package/dist/app/layout.d.ts +19 -0
  19. package/dist/app/layout.js +112 -0
  20. package/dist/app/layout.js.map +1 -0
  21. package/dist/app/layout.mjs +13 -0
  22. package/dist/app/layout.mjs.map +1 -0
  23. package/dist/chunk-DR4EPLMT.mjs +1013 -0
  24. package/dist/chunk-DR4EPLMT.mjs.map +1 -0
  25. package/dist/chunk-INL2EC72.mjs +170 -0
  26. package/dist/chunk-INL2EC72.mjs.map +1 -0
  27. package/dist/chunk-IZFGEAD6.mjs +61 -0
  28. package/dist/chunk-IZFGEAD6.mjs.map +1 -0
  29. package/dist/chunk-KTRWWAGL.mjs +50 -0
  30. package/dist/chunk-KTRWWAGL.mjs.map +1 -0
  31. package/dist/chunk-MZJHJ6BV.mjs +21 -0
  32. package/dist/chunk-MZJHJ6BV.mjs.map +1 -0
  33. package/dist/chunk-NXRIAL7T.mjs +3119 -0
  34. package/dist/chunk-NXRIAL7T.mjs.map +1 -0
  35. package/dist/components/index.d.mts +822 -0
  36. package/dist/components/index.d.ts +822 -0
  37. package/dist/components/index.js +3738 -0
  38. package/dist/components/index.js.map +1 -0
  39. package/dist/components/index.mjs +3627 -0
  40. package/dist/components/index.mjs.map +1 -0
  41. package/dist/index.css +297 -0
  42. package/dist/index.css.map +1 -0
  43. package/dist/index.d.mts +545 -0
  44. package/dist/index.d.ts +545 -0
  45. package/dist/index.js +4648 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/index.mjs +347 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/lib/index.d.mts +798 -0
  50. package/dist/lib/index.d.ts +798 -0
  51. package/dist/lib/index.js +1301 -0
  52. package/dist/lib/index.js.map +1 -0
  53. package/dist/lib/index.mjs +89 -0
  54. package/dist/lib/index.mjs.map +1 -0
  55. package/package.json +119 -0
  56. package/src/app/api/mdx-watch/route.ts +86 -0
  57. package/src/app/docs-page.tsx +212 -0
  58. package/src/app/layout.tsx +74 -0
  59. package/src/components/docs/accordion.tsx +53 -0
  60. package/src/components/docs/api/api-endpoint.tsx +59 -0
  61. package/src/components/docs/api/api-params.tsx +43 -0
  62. package/src/components/docs/api/api-playground.tsx +233 -0
  63. package/src/components/docs/api/api-reference.tsx +291 -0
  64. package/src/components/docs/api/api-response.tsx +48 -0
  65. package/src/components/docs/api/index.ts +5 -0
  66. package/src/components/docs/badge.tsx +22 -0
  67. package/src/components/docs/breadcrumb.tsx +51 -0
  68. package/src/components/docs/callout.tsx +109 -0
  69. package/src/components/docs/card.tsx +84 -0
  70. package/src/components/docs/category-index.tsx +112 -0
  71. package/src/components/docs/code-block.tsx +129 -0
  72. package/src/components/docs/columns.tsx +45 -0
  73. package/src/components/docs/componentTextProps.ts +85 -0
  74. package/src/components/docs/dev-mode-badge.tsx +35 -0
  75. package/src/components/docs/doc-layout-wrapper.tsx +54 -0
  76. package/src/components/docs/doc-layout.tsx +111 -0
  77. package/src/components/docs/doc-loading.tsx +15 -0
  78. package/src/components/docs/doc-metadata.tsx +55 -0
  79. package/src/components/docs/doc-navigation.tsx +62 -0
  80. package/src/components/docs/doc-tags.tsx +25 -0
  81. package/src/components/docs/draft-badge.tsx +10 -0
  82. package/src/components/docs/footer.tsx +47 -0
  83. package/src/components/docs/frame.tsx +22 -0
  84. package/src/components/docs/header.tsx +122 -0
  85. package/src/components/docs/hot-reload-indicator.tsx +77 -0
  86. package/src/components/docs/icon.tsx +70 -0
  87. package/src/components/docs/image-card.tsx +95 -0
  88. package/src/components/docs/image.tsx +73 -0
  89. package/src/components/docs/index.ts +48 -0
  90. package/src/components/docs/math.tsx +46 -0
  91. package/src/components/docs/mdx-components.tsx +166 -0
  92. package/src/components/docs/mdx-hot-reload.tsx +37 -0
  93. package/src/components/docs/mermaid.tsx +77 -0
  94. package/src/components/docs/mobile-doc-layout.tsx +115 -0
  95. package/src/components/docs/not-found-content.tsx +55 -0
  96. package/src/components/docs/search-highlight.tsx +127 -0
  97. package/src/components/docs/search-modal.tsx +223 -0
  98. package/src/components/docs/sidebar-skeleton.tsx +39 -0
  99. package/src/components/docs/sidebar.tsx +323 -0
  100. package/src/components/docs/site-banner.tsx +92 -0
  101. package/src/components/docs/steps.tsx +29 -0
  102. package/src/components/docs/tab-context.tsx +28 -0
  103. package/src/components/docs/tab-groups.tsx +50 -0
  104. package/src/components/docs/table-of-contents.tsx +104 -0
  105. package/src/components/docs/tabs.tsx +63 -0
  106. package/src/components/docs/theme-toggle.tsx +39 -0
  107. package/src/components/docs/tooltip.tsx +37 -0
  108. package/src/components/docs/version-switcher.tsx +52 -0
  109. package/src/components/docs/video.tsx +80 -0
  110. package/src/components/global/index.ts +3 -0
  111. package/src/components/global/version-not-found.tsx +26 -0
  112. package/src/components/index.ts +8 -0
  113. package/src/components/theme-provider.tsx +11 -0
  114. package/src/components/ui/badge.tsx +46 -0
  115. package/src/components/ui/button.tsx +60 -0
  116. package/src/components/ui/dialog.tsx +143 -0
  117. package/src/components/ui/index.ts +6 -0
  118. package/src/components/ui/input.tsx +21 -0
  119. package/src/components/ui/textarea.tsx +18 -0
  120. package/src/index.ts +41 -0
  121. package/src/lib/api-parser.types.ts +78 -0
  122. package/src/lib/api.types.ts +202 -0
  123. package/src/lib/category.ts +71 -0
  124. package/src/lib/config.server.ts +170 -0
  125. package/src/lib/config.ts +20 -0
  126. package/src/lib/config.types.ts +295 -0
  127. package/src/lib/dev-utils.ts +75 -0
  128. package/src/lib/index.ts +27 -0
  129. package/src/lib/mdx-cache.ts +200 -0
  130. package/src/lib/mdx.ts +402 -0
  131. package/src/lib/parsers/base-parser.ts +16 -0
  132. package/src/lib/parsers/index.ts +69 -0
  133. package/src/lib/parsers/openapi-parser.ts +251 -0
  134. package/src/lib/parsers/postman-parser.ts +301 -0
  135. package/src/lib/parsers/specra-parser.ts +24 -0
  136. package/src/lib/redirects.ts +40 -0
  137. package/src/lib/remark-code-meta.ts +23 -0
  138. package/src/lib/sidebar-utils.ts +188 -0
  139. package/src/lib/toc.ts +24 -0
  140. package/src/lib/utils.ts +36 -0
  141. package/src/specra.config.json +124 -0
  142. package/src/styles/globals.css +427 -0
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Unified sidebar sorting and structure building utilities
3
+ * This module provides consistent sidebar logic across the application
4
+ * to ensure ordering is handled the same way everywhere.
5
+ */
6
+
7
+ export interface SidebarGroup {
8
+ label: string
9
+ path: string
10
+ icon?: string
11
+ items: any[]
12
+ position: number
13
+ collapsible: boolean
14
+ defaultCollapsed: boolean
15
+ children: Record<string, SidebarGroup>
16
+ }
17
+
18
+ /**
19
+ * Sort sidebar items by their position.
20
+ * Items with explicit sidebar_position come first (sorted numerically),
21
+ * followed by items without position (sorted by their original order).
22
+ *
23
+ * @param items - Array of items with optional sidebar_position
24
+ * @returns Sorted array of items
25
+ */
26
+ export function sortSidebarItems<T extends { sidebar_position?: number; meta?: any }>(items: T[]): T[] {
27
+ return [...items].sort((a, b) => {
28
+ const posA = a.sidebar_position ?? a.meta?.sidebar_position ?? a.meta?.order ?? 999
29
+ const posB = b.sidebar_position ?? b.meta?.sidebar_position ?? b.meta?.order ?? 999
30
+ return posA - posB
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Sort sidebar groups by their position.
36
+ * Groups with explicit position come first (sorted numerically),
37
+ * followed by groups without position at the end (sorted by their original order).
38
+ *
39
+ * @param groups - Record of group key to group object with position
40
+ * @returns Sorted array of [key, group] tuples
41
+ */
42
+ export function sortSidebarGroups<T extends { position: number }>(
43
+ groups: Record<string, T>
44
+ ): [string, T][] {
45
+ return Object.entries(groups).sort(([, a], [, b]) => {
46
+ const posA = a.position ?? 999
47
+ const posB = b.position ?? 999
48
+ return posA - posB
49
+ })
50
+ }
51
+
52
+ /**
53
+ * Build hierarchical sidebar structure from flat list of documents
54
+ * This is the single source of truth for sidebar structure used by both
55
+ * the sidebar component and navigation (prev/next) links.
56
+ *
57
+ * @param docs - Array of documents with metadata
58
+ * @returns Object containing root groups and standalone items
59
+ */
60
+ export function buildSidebarStructure<T extends {
61
+ filePath: string
62
+ slug: string
63
+ categoryLabel?: string
64
+ categoryPosition?: number
65
+ categoryIcon?: string
66
+ categoryCollapsible?: boolean
67
+ categoryCollapsed?: boolean
68
+ meta: any
69
+ }>(docs: T[]): {
70
+ rootGroups: Record<string, SidebarGroup>
71
+ standalone: T[]
72
+ } {
73
+ const rootGroups: Record<string, SidebarGroup> = {}
74
+ const standalone: T[] = []
75
+
76
+ // First pass: collect category metadata from all docs to build complete folder structure
77
+ const categoryMetadata = new Map<string, {
78
+ label?: string
79
+ position?: number
80
+ icon?: string
81
+ collapsible?: boolean
82
+ collapsed?: boolean
83
+ }>()
84
+
85
+ docs.forEach((doc) => {
86
+ const pathParts = doc.filePath.split("/")
87
+ const folderPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : ""
88
+
89
+ if (folderPath && doc.categoryLabel) {
90
+ categoryMetadata.set(folderPath, {
91
+ label: doc.categoryLabel,
92
+ position: doc.categoryPosition,
93
+ icon: doc.categoryIcon,
94
+ collapsible: doc.categoryCollapsible,
95
+ collapsed: doc.categoryCollapsed
96
+ })
97
+ }
98
+ })
99
+
100
+ docs.forEach((doc) => {
101
+ const pathParts = doc.filePath.split("/")
102
+ const isIndexFile = doc.filePath.endsWith("/index") ||
103
+ doc.filePath === "index" ||
104
+ (pathParts.length > 1 && doc.slug === pathParts.slice(0, -1).join("/"))
105
+
106
+ const customGroup = doc.meta.sidebar || doc.meta.group
107
+
108
+ if (customGroup) {
109
+ const groupName = customGroup.charAt(0).toUpperCase() + customGroup.slice(1)
110
+ if (!rootGroups[groupName]) {
111
+ rootGroups[groupName] = {
112
+ label: groupName,
113
+ path: customGroup,
114
+ items: [],
115
+ position: 999,
116
+ collapsible: doc.categoryCollapsible ?? true,
117
+ defaultCollapsed: doc.categoryCollapsed ?? false,
118
+ children: {}
119
+ }
120
+ }
121
+ if (isIndexFile) {
122
+ // Use categoryPosition if available (from _category_.json), otherwise sidebar_position from frontmatter
123
+ rootGroups[groupName].position = doc.categoryPosition ?? doc.meta.sidebar_position ?? 999
124
+ rootGroups[groupName].icon = doc.categoryIcon
125
+ } else {
126
+ rootGroups[groupName].items.push(doc)
127
+ }
128
+ return
129
+ }
130
+
131
+ if (pathParts.length > 1) {
132
+ const folderParts = pathParts.slice(0, -1)
133
+ let currentLevel = rootGroups
134
+ let currentPath = ""
135
+
136
+ for (let i = 0; i < folderParts.length; i++) {
137
+ const folder = folderParts[i]
138
+ currentPath = currentPath ? `${currentPath}/${folder}` : folder
139
+ const folderLabel = folder.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")
140
+
141
+ // Get metadata for this specific folder path
142
+ const metadata = categoryMetadata.get(currentPath)
143
+
144
+ if (!currentLevel[folder]) {
145
+ currentLevel[folder] = {
146
+ label: metadata?.label ?? folderLabel,
147
+ path: currentPath,
148
+ icon: metadata?.icon,
149
+ items: [],
150
+ position: metadata?.position ?? 999,
151
+ collapsible: metadata?.collapsible ?? true,
152
+ defaultCollapsed: metadata?.collapsed ?? false,
153
+ children: {}
154
+ }
155
+ }
156
+
157
+ if (i === folderParts.length - 1) {
158
+ if (isIndexFile) {
159
+ // Update position, label, and icon if this is the index file for this folder
160
+ currentLevel[folder].position = doc.categoryPosition ?? doc.meta.sidebar_position ?? currentLevel[folder].position
161
+ if (doc.categoryLabel) {
162
+ currentLevel[folder].label = doc.categoryLabel
163
+ }
164
+ if (doc.categoryIcon) {
165
+ currentLevel[folder].icon = doc.categoryIcon
166
+ }
167
+ if (doc.categoryCollapsible !== undefined) {
168
+ currentLevel[folder].collapsible = doc.categoryCollapsible
169
+ }
170
+ if (doc.categoryCollapsed !== undefined) {
171
+ currentLevel[folder].defaultCollapsed = doc.categoryCollapsed
172
+ }
173
+ } else {
174
+ currentLevel[folder].items.push(doc)
175
+ }
176
+ }
177
+
178
+ currentLevel = currentLevel[folder].children
179
+ }
180
+ } else {
181
+ if (!isIndexFile) {
182
+ standalone.push(doc)
183
+ }
184
+ }
185
+ })
186
+
187
+ return { rootGroups, standalone }
188
+ }
package/src/lib/toc.ts ADDED
@@ -0,0 +1,24 @@
1
+ export interface TOCItem {
2
+ id: string
3
+ title: string
4
+ level: number
5
+ }
6
+
7
+ /**
8
+ * Extract headings from HTML string for table of contents
9
+ */
10
+ export function extractHeadings(html: string): TOCItem[] {
11
+ const headingRegex = /<h([2-3])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h\1>/g
12
+ const toc: TOCItem[] = []
13
+ let match
14
+
15
+ while ((match = headingRegex.exec(html)) !== null) {
16
+ const level = Number.parseInt(match[1])
17
+ const id = match[2]
18
+ const title = match[3].replace(/<[^>]*>/g, "") // Strip HTML tags
19
+
20
+ toc.push({ id, title, level })
21
+ }
22
+
23
+ return toc
24
+ }
@@ -0,0 +1,36 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
7
+
8
+
9
+ /**
10
+ * Get the correct asset path based on deployment configuration
11
+ * Handles different deployment scenarios:
12
+ * - Vercel/Node.js hosting (standalone build): No basePath needed
13
+ * - GitHub Pages without custom domain: Uses basePath from config
14
+ * - Static hosting with custom domain: No basePath needed
15
+ *
16
+ * @param path - The asset path (can start with or without '/')
17
+ * @returns The properly formatted asset path
18
+ */
19
+ export function getAssetPath(path: string): string {
20
+ // Get basePath from Next.js config (set during build for static exports)
21
+ const basePath = process.env.NEXT_PUBLIC_BASE_PATH || process.env.__NEXT_ROUTER_BASEPATH || ''
22
+
23
+ // Normalize the input path: ensure it starts with '/'
24
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`
25
+
26
+ // If we have a basePath (GitHub Pages without custom domain), prepend it
27
+ if (basePath) {
28
+ // Normalize basePath: remove trailing slash, ensure leading slash
29
+ const normalizedBase = basePath.startsWith('/') ? basePath : `/${basePath}`
30
+ const cleanBase = normalizedBase.replace(/\/$/, '')
31
+ return `${cleanBase}${normalizedPath}`
32
+ }
33
+
34
+ // Default: return the normalized path (works for Vercel, custom domains, and dev)
35
+ return normalizedPath
36
+ }
@@ -0,0 +1,124 @@
1
+ {
2
+ "$schema": "./lib/config.types.ts",
3
+ "site": {
4
+ "title": "Documentation Library",
5
+ "description": "Comprehensive documentation for your project",
6
+ "url": "https://vercel.com/dalmasontos-projects/v0-documentation-library",
7
+ "baseUrl": "/",
8
+ "language": "en",
9
+ "organizationName": "Your Organization",
10
+ "projectName": "v0-documentation-library",
11
+ "activeVersion": "v1.0.0",
12
+ "favicon": "/icon-light-32x32.png"
13
+ },
14
+ "theme": {
15
+ "defaultMode": "system",
16
+ "respectPrefersColorScheme": true
17
+ },
18
+ "navigation": {
19
+ "showSidebar": true,
20
+ "collapsibleSidebar": true,
21
+ "showBreadcrumbs": true,
22
+ "showTableOfContents": true,
23
+ "tocPosition": "right",
24
+ "tocMaxDepth": 3,
25
+ "tabGroups": [
26
+ {
27
+ "id": "guides",
28
+ "label": "Guides",
29
+ "icon": "book-open"
30
+ },
31
+ {
32
+ "id": "api",
33
+ "label": "API Reference",
34
+ "icon": "zap"
35
+ },
36
+ {
37
+ "id": "components",
38
+ "label": "Components",
39
+ "icon": "layers"
40
+ }
41
+ ]
42
+ },
43
+ "social": {
44
+ "github": "https://github.com/dalmasonto",
45
+ "twitter": "https://twitter.com/dalmasonto",
46
+ "discord": "https://discord.com/invite/dalmasonto",
47
+ "custom": [
48
+ {
49
+ "label": "Website",
50
+ "href": "https://craftfolio.com/dalmasonto"
51
+ }
52
+ ]
53
+ },
54
+ "search": {
55
+ "enabled": true,
56
+ "placeholder": "Search documentation...",
57
+ "provider": "meilisearch",
58
+ "meilisearch": {
59
+ "host": "http://localhost:7700",
60
+ "apiKey": "aSampleMasterKey",
61
+ "indexName": "docs"
62
+ }
63
+ },
64
+ "analytics": {
65
+ "googleAnalytics": "",
66
+ "plausible": ""
67
+ },
68
+ "footer": {
69
+ "copyright": "Copyright © 2024 Dalmasonto. All rights reserved.",
70
+ "links": [
71
+ {
72
+ "title": "Documentation",
73
+ "items": [
74
+ {
75
+ "label": "Getting Started",
76
+ "href": "/docs/v1.0.0/getting-started"
77
+ },
78
+ {
79
+ "label": "API Reference",
80
+ "href": "/docs/v1.0.0/api"
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ "title": "Community",
86
+ "items": [
87
+ {
88
+ "label": "GitHub",
89
+ "href": "https://github.com/yourusername/your-repo"
90
+ },
91
+ {
92
+ "label": "Discord",
93
+ "href": "#"
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ },
99
+ "banner": {
100
+ "enabled": true,
101
+ "message": "🎉 This is a development version. Some features may not work as expected.",
102
+ "type": "error",
103
+ "dismissible": true
104
+ },
105
+ "features": {
106
+ "editUrl": "https://github.com/yourusername/your-repo/edit/main/docs",
107
+ "showLastUpdated": true,
108
+ "showReadingTime": true,
109
+ "showAuthors": false,
110
+ "showTags": true,
111
+ "versioning": true,
112
+ "i18n": false
113
+ },
114
+ "env": {
115
+ "API_BASE_URL": "https://api.example.com",
116
+ "API_VERSION": "v1",
117
+ "CDN_URL": "https://cdn.example.com"
118
+ },
119
+ "deployment": {
120
+ "target": "vercel",
121
+ "basePath": "",
122
+ "customDomain": false
123
+ }
124
+ }