@geenius/docs 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 (139) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  5. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  6. package/.github/dependabot.yml +11 -0
  7. package/.github/workflows/ci.yml +23 -0
  8. package/.github/workflows/release.yml +29 -0
  9. package/.nvmrc +1 -0
  10. package/.project/ACCOUNT.yaml +4 -0
  11. package/.project/IDEAS.yaml +7 -0
  12. package/.project/PROJECT.yaml +11 -0
  13. package/.project/ROADMAP.yaml +15 -0
  14. package/CHANGELOG.md +11 -0
  15. package/CODE_OF_CONDUCT.md +16 -0
  16. package/CONTRIBUTING.md +26 -0
  17. package/LICENSE +21 -0
  18. package/README.md +1 -0
  19. package/SECURITY.md +15 -0
  20. package/SUPPORT.md +8 -0
  21. package/package.json +58 -0
  22. package/packages/convex/README.md +1 -0
  23. package/packages/convex/package.json +12 -0
  24. package/packages/convex/src/convex.config.ts +3 -0
  25. package/packages/convex/src/index.ts +3 -0
  26. package/packages/convex/src/mutations.ts +270 -0
  27. package/packages/convex/src/queries.ts +175 -0
  28. package/packages/convex/src/schema.ts +55 -0
  29. package/packages/react/README.md +1 -0
  30. package/packages/react/package.json +36 -0
  31. package/packages/react/src/DocsLayout.tsx +116 -0
  32. package/packages/react/src/DocsProvider.tsx +93 -0
  33. package/packages/react/src/RouterDocsContent.tsx +148 -0
  34. package/packages/react/src/RouterDocsLayout.tsx +161 -0
  35. package/packages/react/src/components/Breadcrumbs.tsx +34 -0
  36. package/packages/react/src/components/DocPage.tsx +191 -0
  37. package/packages/react/src/components/DocSearch.tsx +140 -0
  38. package/packages/react/src/components/DocSidebar.tsx +86 -0
  39. package/packages/react/src/components/DocsLayout.tsx +62 -0
  40. package/packages/react/src/components/EditButton.tsx +26 -0
  41. package/packages/react/src/components/PageNavigation.tsx +45 -0
  42. package/packages/react/src/components/TableOfContents.tsx +46 -0
  43. package/packages/react/src/components/VersionSelector.tsx +60 -0
  44. package/packages/react/src/components/index.ts +9 -0
  45. package/packages/react/src/hooks/index.ts +8 -0
  46. package/packages/react/src/hooks/useDocSearch.ts +55 -0
  47. package/packages/react/src/hooks/useDocs.ts +57 -0
  48. package/packages/react/src/hooks/useDocsAdmin.ts +151 -0
  49. package/packages/react/src/hooks/useTableOfContents.ts +66 -0
  50. package/packages/react/src/index.ts +38 -0
  51. package/packages/react/src/pages/DocSearchPage.tsx +129 -0
  52. package/packages/react/src/pages/DocViewPage.tsx +158 -0
  53. package/packages/react/src/pages/DocsAdminPage.tsx +330 -0
  54. package/packages/react/src/pages/DocsIndexPage.tsx +172 -0
  55. package/packages/react/src/pages/index.ts +4 -0
  56. package/packages/react/src/useDocs.ts +58 -0
  57. package/packages/react/tsup.config.ts +12 -0
  58. package/packages/react-css/README.md +1 -0
  59. package/packages/react-css/package.json +37 -0
  60. package/packages/react-css/src/DocsLayout.tsx +117 -0
  61. package/packages/react-css/src/DocsProvider.tsx +93 -0
  62. package/packages/react-css/src/RouterDocsContent.tsx +60 -0
  63. package/packages/react-css/src/RouterDocsLayout.tsx +101 -0
  64. package/packages/react-css/src/components/DocPage.tsx +21 -0
  65. package/packages/react-css/src/components/DocSearch.tsx +55 -0
  66. package/packages/react-css/src/components/DocSidebar.tsx +56 -0
  67. package/packages/react-css/src/components/DocsLayout.tsx +28 -0
  68. package/packages/react-css/src/components/common.tsx +93 -0
  69. package/packages/react-css/src/components/index.ts +5 -0
  70. package/packages/react-css/src/hooks/index.ts +2 -0
  71. package/packages/react-css/src/index.ts +6 -0
  72. package/packages/react-css/src/index.tsx +3 -0
  73. package/packages/react-css/src/pages/DocViewPage.tsx +78 -0
  74. package/packages/react-css/src/pages/DocsAdminPage.tsx +101 -0
  75. package/packages/react-css/src/pages/DocsIndexPage.tsx +68 -0
  76. package/packages/react-css/src/pages/index.ts +3 -0
  77. package/packages/react-css/src/styles.css +1271 -0
  78. package/packages/react-css/src/useDocs.ts +58 -0
  79. package/packages/react-css/tsconfig.json +19 -0
  80. package/packages/react-css/tsup.config.ts +10 -0
  81. package/packages/shared/README.md +1 -0
  82. package/packages/shared/package.json +31 -0
  83. package/packages/shared/src/__tests__/docs.test.ts +69 -0
  84. package/packages/shared/src/config.ts +80 -0
  85. package/packages/shared/src/index.ts +179 -0
  86. package/packages/shared/src/providers/astro.ts +94 -0
  87. package/packages/shared/src/providers/fumadocs.ts +116 -0
  88. package/packages/shared/src/providers/internal.ts +80 -0
  89. package/packages/shared/src/types.ts +73 -0
  90. package/packages/shared/tsconfig.json +18 -0
  91. package/packages/shared/tsup.config.ts +12 -0
  92. package/packages/shared/vitest.config.ts +4 -0
  93. package/packages/solidjs/README.md +1 -0
  94. package/packages/solidjs/package.json +33 -0
  95. package/packages/solidjs/src/DocsLayout.tsx +87 -0
  96. package/packages/solidjs/src/DocsProvider.tsx +95 -0
  97. package/packages/solidjs/src/RouterDocsContent.tsx +147 -0
  98. package/packages/solidjs/src/RouterDocsLayout.tsx +161 -0
  99. package/packages/solidjs/src/components/Breadcrumbs.tsx +27 -0
  100. package/packages/solidjs/src/components/DocPage.tsx +110 -0
  101. package/packages/solidjs/src/components/DocSearch.tsx +81 -0
  102. package/packages/solidjs/src/components/DocSidebar.tsx +92 -0
  103. package/packages/solidjs/src/components/DocsLayout.tsx +38 -0
  104. package/packages/solidjs/src/components/EditButton.tsx +15 -0
  105. package/packages/solidjs/src/components/PageNavigation.tsx +31 -0
  106. package/packages/solidjs/src/components/TableOfContents.tsx +41 -0
  107. package/packages/solidjs/src/components/VersionSelector.tsx +30 -0
  108. package/packages/solidjs/src/components/index.ts +9 -0
  109. package/packages/solidjs/src/createDocs.ts +62 -0
  110. package/packages/solidjs/src/index.ts +28 -0
  111. package/packages/solidjs/src/pages/DocSearchPage.tsx +72 -0
  112. package/packages/solidjs/src/pages/DocViewPage.tsx +80 -0
  113. package/packages/solidjs/src/pages/DocsAdminPage.tsx +123 -0
  114. package/packages/solidjs/src/pages/DocsIndexPage.tsx +85 -0
  115. package/packages/solidjs/src/pages/index.ts +4 -0
  116. package/packages/solidjs/src/primitives/createDocSearch.ts +42 -0
  117. package/packages/solidjs/src/primitives/createDocs.ts +35 -0
  118. package/packages/solidjs/src/primitives/createDocsAdmin.ts +63 -0
  119. package/packages/solidjs/src/primitives/createTableOfContents.ts +51 -0
  120. package/packages/solidjs/src/primitives/index.ts +4 -0
  121. package/packages/solidjs/tsup.config.ts +12 -0
  122. package/packages/solidjs-css/README.md +1 -0
  123. package/packages/solidjs-css/package.json +36 -0
  124. package/packages/solidjs-css/src/DocsLayout.tsx +106 -0
  125. package/packages/solidjs-css/src/DocsProvider.tsx +95 -0
  126. package/packages/solidjs-css/src/RouterDocsContent.tsx +54 -0
  127. package/packages/solidjs-css/src/RouterDocsLayout.tsx +104 -0
  128. package/packages/solidjs-css/src/createDocs.ts +62 -0
  129. package/packages/solidjs-css/src/index.ts +7 -0
  130. package/packages/solidjs-css/src/index.tsx +17 -0
  131. package/packages/solidjs-css/src/pages/DocViewPage.tsx +111 -0
  132. package/packages/solidjs-css/src/pages/DocsAdminPage.tsx +332 -0
  133. package/packages/solidjs-css/src/pages/DocsIndexPage.tsx +116 -0
  134. package/packages/solidjs-css/src/pages/index.ts +3 -0
  135. package/packages/solidjs-css/src/primitives/index.ts +1 -0
  136. package/packages/solidjs-css/src/styles.css +1271 -0
  137. package/packages/solidjs-css/tsconfig.json +20 -0
  138. package/packages/solidjs-css/tsup.config.ts +10 -0
  139. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,175 @@
1
+ import { v } from "convex/values";
2
+ import { query } from "./_generated/server.js";
3
+
4
+ export const listSections = query({
5
+ args: { access: v.optional(v.string()) },
6
+ returns: v.array(v.any()),
7
+ handler: async (ctx, args) => {
8
+ let sections = await ctx.db
9
+ .query("doc_sections")
10
+ .withIndex("by_order")
11
+ .collect();
12
+
13
+ if (args.access) {
14
+ sections = sections.filter((s) => s.access === args.access);
15
+ }
16
+
17
+ return sections.sort((a, b) => a.order - b.order);
18
+ },
19
+ });
20
+
21
+ export const getSection = query({
22
+ args: { sectionId: v.id("doc_sections") },
23
+ returns: v.any(),
24
+ handler: async (ctx, args) => {
25
+ return await ctx.db.get(args.sectionId);
26
+ },
27
+ });
28
+
29
+ export const listPagesBySection = query({
30
+ args: {
31
+ sectionId: v.id("doc_sections"),
32
+ status: v.optional(v.string()),
33
+ },
34
+ returns: v.array(v.any()),
35
+ handler: async (ctx, args) => {
36
+ let pages = await ctx.db
37
+ .query("doc_pages")
38
+ .withIndex("by_sectionId", (q) => q.eq("sectionId", args.sectionId))
39
+ .collect();
40
+
41
+ if (args.status) {
42
+ pages = pages.filter((p) => p.status === args.status);
43
+ }
44
+
45
+ return pages.sort((a, b) => a.order - b.order);
46
+ },
47
+ });
48
+
49
+ export const getPage = query({
50
+ args: {
51
+ sectionId: v.id("doc_sections"),
52
+ slug: v.string(),
53
+ },
54
+ returns: v.any(),
55
+ handler: async (ctx, args) => {
56
+ const pages = await ctx.db
57
+ .query("doc_pages")
58
+ .withIndex("by_sectionId", (q) => q.eq("sectionId", args.sectionId))
59
+ .collect();
60
+
61
+ return pages.find((p) => p.slug === args.slug) ?? null;
62
+ },
63
+ });
64
+
65
+ export const getPageById = query({
66
+ args: { pageId: v.id("doc_pages") },
67
+ returns: v.any(),
68
+ handler: async (ctx, args) => {
69
+ return await ctx.db.get(args.pageId);
70
+ },
71
+ });
72
+
73
+ export const searchPages = query({
74
+ args: {
75
+ query: v.string(),
76
+ access: v.optional(v.string()),
77
+ },
78
+ returns: v.array(v.any()),
79
+ handler: async (ctx, args) => {
80
+ const q = args.query.toLowerCase();
81
+ if (!q) return [];
82
+
83
+ let pages = await ctx.db.query("doc_pages").collect();
84
+
85
+ if (args.access) {
86
+ pages = pages.filter((p) => p.access === args.access);
87
+ }
88
+
89
+ const results = pages
90
+ .filter(
91
+ (p) =>
92
+ p.title.toLowerCase().includes(q) ||
93
+ p.content.toLowerCase().includes(q) ||
94
+ p.tags.some((t: string) => t.toLowerCase().includes(q))
95
+ )
96
+ .slice(0, 20);
97
+
98
+ return results;
99
+ },
100
+ });
101
+
102
+ export const getFullTree = query({
103
+ args: { access: v.optional(v.string()) },
104
+ returns: v.any(),
105
+ handler: async (ctx, args) => {
106
+ let sections = await ctx.db
107
+ .query("doc_sections")
108
+ .withIndex("by_order")
109
+ .collect();
110
+
111
+ if (args.access) {
112
+ sections = sections.filter((s) => s.access === args.access);
113
+ }
114
+
115
+ sections.sort((a, b) => a.order - b.order);
116
+
117
+ const tree = [];
118
+ for (const section of sections) {
119
+ const pages = await ctx.db
120
+ .query("doc_pages")
121
+ .withIndex("by_sectionId", (q) => q.eq("sectionId", section._id))
122
+ .collect();
123
+
124
+ const publishedPages = pages
125
+ .filter((p) => p.status === "published")
126
+ .sort((a, b) => a.order - b.order);
127
+
128
+ tree.push({
129
+ ...section,
130
+ id: section._id,
131
+ pages: publishedPages.map((p) => ({ ...p, id: p._id })),
132
+ pageCount: publishedPages.length,
133
+ });
134
+ }
135
+
136
+ return tree;
137
+ },
138
+ });
139
+
140
+ export const getRecentPages = query({
141
+ args: {
142
+ limit: v.optional(v.number()),
143
+ access: v.optional(v.string()),
144
+ },
145
+ returns: v.array(v.any()),
146
+ handler: async (ctx, args) => {
147
+ let pages = await ctx.db.query("doc_pages").collect();
148
+
149
+ pages = pages.filter((p) => p.status === "published");
150
+
151
+ if (args.access) {
152
+ pages = pages.filter((p) => p.access === args.access);
153
+ }
154
+
155
+ pages.sort((a, b) => b.updatedAt - a.updatedAt);
156
+
157
+ return pages.slice(0, args.limit ?? 10).map((p) => ({ ...p, id: p._id }));
158
+ },
159
+ });
160
+
161
+ export const getPageVersions = query({
162
+ args: { slug: v.string() },
163
+ returns: v.array(v.any()),
164
+ handler: async (ctx, args) => {
165
+ const pages = await ctx.db
166
+ .query("doc_pages")
167
+ .withIndex("by_slug", (q) => q.eq("slug", args.slug))
168
+ .collect();
169
+
170
+ return pages
171
+ .filter((p) => p.version)
172
+ .sort((a, b) => (b.version ?? "").localeCompare(a.version ?? ""))
173
+ .map((p) => ({ ...p, id: p._id }));
174
+ },
175
+ });
@@ -0,0 +1,55 @@
1
+ import { defineSchema, defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+
4
+ export default defineSchema({
5
+ doc_sections: defineTable({
6
+ title: v.string(),
7
+ slug: v.string(),
8
+ parentId: v.optional(v.id("doc_sections")),
9
+ order: v.number(),
10
+ icon: v.optional(v.string()),
11
+ description: v.optional(v.string()),
12
+ access: v.union(v.literal("public"), v.literal("team"), v.literal("admin")),
13
+ createdAt: v.number(),
14
+ })
15
+ .index("by_slug", ["slug"])
16
+ .index("by_parentId", ["parentId"])
17
+ .index("by_order", ["order"]),
18
+
19
+ doc_pages: defineTable({
20
+ title: v.string(),
21
+ slug: v.string(),
22
+ content: v.string(),
23
+ excerpt: v.optional(v.string()),
24
+ sectionId: v.id("doc_sections"),
25
+ order: v.number(),
26
+ author: v.object({ name: v.string(), avatar: v.optional(v.string()) }),
27
+ lastEditedBy: v.optional(
28
+ v.object({ name: v.string(), editedAt: v.string() })
29
+ ),
30
+ version: v.optional(v.string()),
31
+ access: v.union(v.literal("public"), v.literal("team"), v.literal("admin")),
32
+ tags: v.array(v.string()),
33
+ status: v.union(
34
+ v.literal("draft"),
35
+ v.literal("published"),
36
+ v.literal("archived")
37
+ ),
38
+ createdAt: v.number(),
39
+ updatedAt: v.number(),
40
+ wordCount: v.number(),
41
+ readingTime: v.number(),
42
+ viewCount: v.number(),
43
+ })
44
+ .index("by_sectionId", ["sectionId"])
45
+ .index("by_slug", ["slug"])
46
+ .index("by_status", ["status"])
47
+ .index("by_access", ["access"]),
48
+
49
+ doc_search_index: defineTable({
50
+ pageId: v.id("doc_pages"),
51
+ sectionId: v.id("doc_sections"),
52
+ tokens: v.array(v.string()),
53
+ updatedAt: v.number(),
54
+ }).index("by_pageId", ["pageId"]),
55
+ });
@@ -0,0 +1 @@
1
+ # ✦ @geenius-docs/react\n\n> A premium module for the Geenius Boilerplate Ecosystem.\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius-docs/react\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius-docs/react';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@geenius-docs/react",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "peerDependencies": {
11
+ "react": ">=18.0.0"
12
+ },
13
+ "dependencies": {
14
+ "@geenius-docs/shared": "workspace:*",
15
+ "lucide-react": "^0.577.0",
16
+ "react-markdown": "^10.1.0",
17
+ "remark-gfm": "^4.0.1"
18
+ },
19
+ "devDependencies": {
20
+ "@tanstack/react-router": "^1.168.1",
21
+ "@types/react": "^19.0.0",
22
+ "@types/react-dom": "^19.0.0",
23
+ "typescript": "~6.0.2"
24
+ },
25
+ "scripts": {
26
+ "type-aml": "tsc --noEmit"
27
+ },
28
+ "author": "Antigravity HQ",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">=20.0.0"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }
@@ -0,0 +1,116 @@
1
+ import React from 'react'
2
+ import { useDocs } from './DocsProvider'
3
+
4
+ interface DocsLayoutProps {
5
+ children?: React.ReactNode
6
+ currentPageId?: string
7
+ onSelectPage?: (id: string) => void
8
+ searchQuery?: string
9
+ onSearch?: (query: string) => void
10
+ searchResults?: any[]
11
+ }
12
+
13
+ /**
14
+ * A professional, high-quality layout for Geenius Documentation.
15
+ * Provides a responsive sidebar, searchable navigation, and premium typography.
16
+ */
17
+ export const DocsLayout: React.FC<DocsLayoutProps> = ({
18
+ children,
19
+ currentPageId,
20
+ onSelectPage,
21
+ searchQuery: searchQueryProp,
22
+ onSearch: onSearchProp,
23
+ searchResults: searchResultsProp
24
+ }) => {
25
+ const {
26
+ tree,
27
+ selectPage,
28
+ setSearchQuery,
29
+ searchQuery: contextSearchQuery,
30
+ searchResults: contextSearchResults
31
+ } = useDocs()
32
+
33
+ const q = searchQueryProp ?? contextSearchQuery
34
+ const results = searchResultsProp ?? contextSearchResults
35
+ const onSelect = onSelectPage ?? selectPage
36
+ const onSearch = onSearchProp ?? setSearchQuery
37
+
38
+ return (
39
+ <div className="flex min-h-[calc(100vh-64px)] bg-background">
40
+ {/* Searchable Sidebar */}
41
+ <aside className="w-80 border-r bg-card/10 backdrop-blur-xl flex flex-col shrink-0 overflow-hidden">
42
+ <div className="p-6 space-y-4">
43
+ <div className="relative group">
44
+ <span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors group-focus-within:text-primary">
45
+ <svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
46
+ </span>
47
+ <input
48
+ value={q}
49
+ onChange={(e) => onSearch(e.target.value)}
50
+ placeholder="Search knowledge base..."
51
+ className="w-full pl-10 pr-4 py-2.5 rounded-2xl border bg-background/50 text-sm focus:ring-2 ring-primary/20 outline-none transition-all placeholder:text-muted-foreground/30"
52
+ />
53
+ </div>
54
+ </div>
55
+
56
+ <nav className="flex-1 overflow-y-auto px-4 pb-12 space-y-8 scrollbar-hide">
57
+ {q && results.length > 0 ? (
58
+ <div className="space-y-2 px-2 animate-in fade-in slide-in-from-top-2 duration-300">
59
+ <p className="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-4 opacity-50 px-2">
60
+ Search Results ({results.length})
61
+ </p>
62
+ {results.map(page => (
63
+ <button
64
+ key={page.id}
65
+ onClick={() => { onSelect(page.id); onSearch('') }}
66
+ className="w-full p-4 rounded-2xl border bg-card/30 hover:bg-card hover:border-primary/20 hover:shadow-xl hover:shadow-primary/5 text-left transition-all duration-300 group"
67
+ >
68
+ <h5 className="text-sm font-bold group-hover:text-primary transition-colors">{page.title}</h5>
69
+ {page.description && <p className="text-[10px] text-muted-foreground line-clamp-1 mt-1 opacity-70">{page.description}</p>}
70
+ </button>
71
+ ))}
72
+ </div>
73
+ ) : (
74
+ tree.map(group => (
75
+ <div key={group.id} className="space-y-2">
76
+ <h4 className="px-4 text-[10px] font-bold text-muted-foreground uppercase tracking-widest opacity-40">
77
+ {group.title}
78
+ </h4>
79
+ <div className="space-y-0.5">
80
+ {group.children.map(page => {
81
+ const isActive = page.id === currentPageId
82
+ return (
83
+ <button
84
+ key={page.id}
85
+ onClick={() => onSelect(page.id)}
86
+ className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-medium transition-all group ${isActive ? 'bg-primary/5 text-primary shadow-inner' : 'text-muted-foreground hover:bg-muted/50 hover:text-foreground'}`}
87
+ >
88
+ <span className={`w-1.5 h-1.5 rounded-full transition-all ${isActive ? 'bg-primary scale-125 shadow-[0_0_8px_rgba(var(--primary),0.5)]' : 'bg-transparent group-hover:bg-muted-foreground/20'}`} />
89
+ {page.title}
90
+ </button>
91
+ )
92
+ })}
93
+ </div>
94
+ </div>
95
+ ))
96
+ )}
97
+ </nav>
98
+ </aside>
99
+
100
+ {/* Dynamic Content Area */}
101
+ <main className="flex-1 overflow-y-auto overscroll-contain">
102
+ <div className="max-w-4xl mx-auto px-12 py-16 animate-in fade-in slide-in-from-bottom-8 duration-700">
103
+ {children}
104
+
105
+ <div className="mt-20 pt-8 border-t border-dashed flex items-center justify-between opacity-50">
106
+ <p className="text-xs font-medium text-muted-foreground">© 2024 Geenius. Premium Documentation.</p>
107
+ <div className="flex gap-4">
108
+ <button className="text-xs hover:text-primary transition-colors">Edit on GitHub</button>
109
+ <button className="text-xs hover:text-primary transition-colors">Community Help</button>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </main>
114
+ </div>
115
+ )
116
+ }
@@ -0,0 +1,93 @@
1
+ import React, { createContext, useContext, useState, useMemo, useCallback } from 'react'
2
+
3
+ export interface DocsPage {
4
+ id: string
5
+ title: string
6
+ description?: string
7
+ content: any
8
+ }
9
+
10
+ export interface DocsGroup {
11
+ id: string
12
+ title: string
13
+ children: DocsPage[]
14
+ }
15
+
16
+ export type DocsTree = DocsGroup[]
17
+
18
+ interface DocsContextValue {
19
+ tree: DocsTree
20
+ currentPage: DocsPage | null
21
+ selectPage: (id: string) => void
22
+ searchQuery: string
23
+ setSearchQuery: (query: string) => void
24
+ searchResults: DocsPage[]
25
+ }
26
+
27
+ const DocsContext = createContext<DocsContextValue | null>(null)
28
+
29
+ interface DocsProviderProps {
30
+ tree: DocsTree
31
+ children: React.ReactNode
32
+ }
33
+
34
+ /**
35
+ * DocsProvider — Manages documentation state including current page,
36
+ * search, and navigation through the docs tree.
37
+ */
38
+ export const DocsProvider: React.FC<DocsProviderProps> = ({ tree, children }) => {
39
+ const [currentPageId, setCurrentPageId] = useState<string | null>(tree[0]?.children[0]?.id || null)
40
+ const [searchQuery, setSearchQuery] = useState('')
41
+
42
+ const currentPage = useMemo(() => {
43
+ for (const group of tree) {
44
+ const page = group.children.find(p => p.id === currentPageId)
45
+ if (page) return page
46
+ }
47
+ return null
48
+ }, [tree, currentPageId])
49
+
50
+ const selectPage = useCallback((id: string) => {
51
+ setCurrentPageId(id)
52
+ }, [])
53
+
54
+ const searchResults = useMemo(() => {
55
+ if (!searchQuery) return []
56
+ const results: DocsPage[] = []
57
+ const query = searchQuery.toLowerCase()
58
+
59
+ for (const group of tree) {
60
+ for (const page of group.children) {
61
+ if (
62
+ page.title.toLowerCase().includes(query) ||
63
+ page.description?.toLowerCase().includes(query) ||
64
+ (typeof page.content === 'string' && page.content.toLowerCase().includes(query))
65
+ ) {
66
+ results.push(page)
67
+ }
68
+ }
69
+ }
70
+ return results
71
+ }, [tree, searchQuery])
72
+
73
+ return (
74
+ <DocsContext.Provider value={{
75
+ tree,
76
+ currentPage,
77
+ selectPage,
78
+ searchQuery,
79
+ setSearchQuery,
80
+ searchResults
81
+ }}>
82
+ {children}
83
+ </DocsContext.Provider>
84
+ )
85
+ }
86
+
87
+ export function useDocs() {
88
+ const context = useContext(DocsContext)
89
+ if (!context) {
90
+ throw new Error('useDocs must be used within a DocsProvider')
91
+ }
92
+ return context
93
+ }
@@ -0,0 +1,148 @@
1
+ // src/components/docs/DocsContent.tsx
2
+
3
+ import React from 'react'
4
+ import ReactMarkdown from 'react-markdown'
5
+ import remarkGfm from 'remark-gfm'
6
+ import { Link } from '@tanstack/react-router'
7
+
8
+ interface DocsContentProps {
9
+ content: string
10
+ title: string
11
+ description: string
12
+ }
13
+
14
+ export const DocsContent: React.FC<DocsContentProps> = ({
15
+ content,
16
+ title,
17
+ description,
18
+ }) => {
19
+ return (
20
+ <article className="prose prose-lg dark:prose-invert max-w-none">
21
+ {/* Header */}
22
+ <header className="mb-8 pb-8 border-b border-border not-prose">
23
+ <h1 className="text-4xl font-extrabold text-text-main mb-3">{title}</h1>
24
+ <p className="text-lg text-text-muted">{description}</p>
25
+ </header>
26
+
27
+ {/* Markdown Content */}
28
+ <ReactMarkdown
29
+ remarkPlugins={[remarkGfm]}
30
+ components={{
31
+ // Custom heading styles
32
+ h1: ({ children }) => (
33
+ <h1 className="text-3xl font-bold text-text-main mt-12 mb-6">
34
+ {children}
35
+ </h1>
36
+ ),
37
+ h2: ({ children }) => (
38
+ <h2 className="text-2xl font-bold text-text-main mt-10 mb-4 pb-2 border-b border-border">
39
+ {children}
40
+ </h2>
41
+ ),
42
+ h3: ({ children }) => (
43
+ <h3 className="text-xl font-bold text-text-main mt-8 mb-3">
44
+ {children}
45
+ </h3>
46
+ ),
47
+ h4: ({ children }) => (
48
+ <h4 className="text-lg font-semibold text-text-main mt-6 mb-2">
49
+ {children}
50
+ </h4>
51
+ ),
52
+ // Paragraph
53
+ p: ({ children }) => (
54
+ <p className="text-text-muted leading-relaxed mb-4">{children}</p>
55
+ ),
56
+ // Links
57
+ a: ({ href, children }) => {
58
+ const isInternal = href?.startsWith('/')
59
+ if (isInternal) {
60
+ return (
61
+ <Link
62
+ to={href!}
63
+ className="text-primary hover:underline font-medium"
64
+ >
65
+ {children}
66
+ </Link>
67
+ )
68
+ }
69
+ return (
70
+ <a
71
+ href={href}
72
+ target="_blank"
73
+ rel="noopener noreferrer"
74
+ className="text-primary hover:underline font-medium"
75
+ >
76
+ {children}
77
+ </a>
78
+ )
79
+ },
80
+ // Code blocks
81
+ code: ({ className, children, ...props }) => {
82
+ const isInline = !className
83
+ if (isInline) {
84
+ return (
85
+ <code className="bg-bg-muted px-1.5 py-0.5 rounded text-sm font-mono text-primary">
86
+ {children}
87
+ </code>
88
+ )
89
+ }
90
+ return (
91
+ <code className={`${className} block`} {...props}>
92
+ {children}
93
+ </code>
94
+ )
95
+ },
96
+ pre: ({ children }) => (
97
+ <pre className="bg-[#1e1e2e] text-gray-100 p-4 rounded-xl overflow-x-auto text-sm my-6 border border-border">
98
+ {children}
99
+ </pre>
100
+ ),
101
+ // Lists
102
+ ul: ({ children }) => (
103
+ <ul className="list-disc list-inside space-y-2 text-text-muted mb-4">
104
+ {children}
105
+ </ul>
106
+ ),
107
+ ol: ({ children }) => (
108
+ <ol className="list-decimal list-inside space-y-2 text-text-muted mb-4">
109
+ {children}
110
+ </ol>
111
+ ),
112
+ li: ({ children }) => <li className="text-text-muted">{children}</li>,
113
+ // Tables
114
+ table: ({ children }) => (
115
+ <div className="overflow-x-auto my-6">
116
+ <table className="min-w-full divide-y divide-border border border-border rounded-lg overflow-hidden">
117
+ {children}
118
+ </table>
119
+ </div>
120
+ ),
121
+ thead: ({ children }) => (
122
+ <thead className="bg-bg-muted">{children}</thead>
123
+ ),
124
+ th: ({ children }) => (
125
+ <th className="px-4 py-3 text-left text-sm font-bold text-text-main">
126
+ {children}
127
+ </th>
128
+ ),
129
+ td: ({ children }) => (
130
+ <td className="px-4 py-3 text-sm text-text-muted border-t border-border">
131
+ {children}
132
+ </td>
133
+ ),
134
+ // Blockquotes
135
+ blockquote: ({ children }) => (
136
+ <blockquote className="border-l-4 border-primary pl-4 py-2 my-6 bg-primary/5 rounded-r-lg">
137
+ {children}
138
+ </blockquote>
139
+ ),
140
+ // Horizontal rules
141
+ hr: () => <hr className="border-border my-8" />,
142
+ }}
143
+ >
144
+ {content}
145
+ </ReactMarkdown>
146
+ </article>
147
+ )
148
+ }