@geenius/docs 0.1.0 → 0.4.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 (158) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +53 -1
  3. package/package.json +101 -13
  4. package/packages/convex/dist/index.d.ts +503 -0
  5. package/packages/convex/dist/index.js +482 -0
  6. package/packages/convex/dist/index.js.map +1 -0
  7. package/packages/react/dist/index.d.ts +439 -0
  8. package/packages/react/dist/index.js +4954 -0
  9. package/packages/react/dist/index.js.map +1 -0
  10. package/packages/react-css/{src/styles.css → dist/index.css} +183 -223
  11. package/packages/react-css/dist/index.css.map +1 -0
  12. package/packages/react-css/dist/index.d.ts +443 -0
  13. package/packages/react-css/dist/index.js +5058 -0
  14. package/packages/react-css/dist/index.js.map +1 -0
  15. package/packages/shared/dist/index.d.ts +684 -0
  16. package/packages/shared/dist/index.js +788 -0
  17. package/packages/shared/dist/index.js.map +1 -0
  18. package/packages/solidjs/dist/index.d.ts +435 -0
  19. package/packages/solidjs/dist/index.js +4584 -0
  20. package/packages/solidjs/dist/index.js.map +1 -0
  21. package/packages/solidjs-css/{src/styles.css → dist/index.css} +183 -223
  22. package/packages/solidjs-css/dist/index.css.map +1 -0
  23. package/packages/solidjs-css/dist/index.d.ts +432 -0
  24. package/packages/solidjs-css/dist/index.js +4934 -0
  25. package/packages/solidjs-css/dist/index.js.map +1 -0
  26. package/.changeset/config.json +0 -11
  27. package/.github/CODEOWNERS +0 -1
  28. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  29. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
  30. package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
  31. package/.github/dependabot.yml +0 -11
  32. package/.github/workflows/ci.yml +0 -23
  33. package/.github/workflows/release.yml +0 -29
  34. package/.nvmrc +0 -1
  35. package/.project/ACCOUNT.yaml +0 -4
  36. package/.project/IDEAS.yaml +0 -7
  37. package/.project/PROJECT.yaml +0 -11
  38. package/.project/ROADMAP.yaml +0 -15
  39. package/CODE_OF_CONDUCT.md +0 -16
  40. package/CONTRIBUTING.md +0 -26
  41. package/SECURITY.md +0 -15
  42. package/SUPPORT.md +0 -8
  43. package/packages/convex/README.md +0 -1
  44. package/packages/convex/package.json +0 -12
  45. package/packages/convex/src/convex.config.ts +0 -3
  46. package/packages/convex/src/index.ts +0 -3
  47. package/packages/convex/src/mutations.ts +0 -270
  48. package/packages/convex/src/queries.ts +0 -175
  49. package/packages/convex/src/schema.ts +0 -55
  50. package/packages/react/README.md +0 -1
  51. package/packages/react/package.json +0 -36
  52. package/packages/react/src/DocsLayout.tsx +0 -116
  53. package/packages/react/src/DocsProvider.tsx +0 -93
  54. package/packages/react/src/RouterDocsContent.tsx +0 -148
  55. package/packages/react/src/RouterDocsLayout.tsx +0 -161
  56. package/packages/react/src/components/Breadcrumbs.tsx +0 -34
  57. package/packages/react/src/components/DocPage.tsx +0 -191
  58. package/packages/react/src/components/DocSearch.tsx +0 -140
  59. package/packages/react/src/components/DocSidebar.tsx +0 -86
  60. package/packages/react/src/components/DocsLayout.tsx +0 -62
  61. package/packages/react/src/components/EditButton.tsx +0 -26
  62. package/packages/react/src/components/PageNavigation.tsx +0 -45
  63. package/packages/react/src/components/TableOfContents.tsx +0 -46
  64. package/packages/react/src/components/VersionSelector.tsx +0 -60
  65. package/packages/react/src/components/index.ts +0 -9
  66. package/packages/react/src/hooks/index.ts +0 -8
  67. package/packages/react/src/hooks/useDocSearch.ts +0 -55
  68. package/packages/react/src/hooks/useDocs.ts +0 -57
  69. package/packages/react/src/hooks/useDocsAdmin.ts +0 -151
  70. package/packages/react/src/hooks/useTableOfContents.ts +0 -66
  71. package/packages/react/src/index.ts +0 -38
  72. package/packages/react/src/pages/DocSearchPage.tsx +0 -129
  73. package/packages/react/src/pages/DocViewPage.tsx +0 -158
  74. package/packages/react/src/pages/DocsAdminPage.tsx +0 -330
  75. package/packages/react/src/pages/DocsIndexPage.tsx +0 -172
  76. package/packages/react/src/pages/index.ts +0 -4
  77. package/packages/react/src/useDocs.ts +0 -58
  78. package/packages/react/tsup.config.ts +0 -12
  79. package/packages/react-css/README.md +0 -1
  80. package/packages/react-css/package.json +0 -37
  81. package/packages/react-css/src/DocsLayout.tsx +0 -117
  82. package/packages/react-css/src/DocsProvider.tsx +0 -93
  83. package/packages/react-css/src/RouterDocsContent.tsx +0 -60
  84. package/packages/react-css/src/RouterDocsLayout.tsx +0 -101
  85. package/packages/react-css/src/components/DocPage.tsx +0 -21
  86. package/packages/react-css/src/components/DocSearch.tsx +0 -55
  87. package/packages/react-css/src/components/DocSidebar.tsx +0 -56
  88. package/packages/react-css/src/components/DocsLayout.tsx +0 -28
  89. package/packages/react-css/src/components/common.tsx +0 -93
  90. package/packages/react-css/src/components/index.ts +0 -5
  91. package/packages/react-css/src/hooks/index.ts +0 -2
  92. package/packages/react-css/src/index.ts +0 -6
  93. package/packages/react-css/src/index.tsx +0 -3
  94. package/packages/react-css/src/pages/DocViewPage.tsx +0 -78
  95. package/packages/react-css/src/pages/DocsAdminPage.tsx +0 -101
  96. package/packages/react-css/src/pages/DocsIndexPage.tsx +0 -68
  97. package/packages/react-css/src/pages/index.ts +0 -3
  98. package/packages/react-css/src/useDocs.ts +0 -58
  99. package/packages/react-css/tsconfig.json +0 -19
  100. package/packages/react-css/tsup.config.ts +0 -10
  101. package/packages/shared/README.md +0 -1
  102. package/packages/shared/package.json +0 -31
  103. package/packages/shared/src/__tests__/docs.test.ts +0 -69
  104. package/packages/shared/src/config.ts +0 -80
  105. package/packages/shared/src/index.ts +0 -179
  106. package/packages/shared/src/providers/astro.ts +0 -94
  107. package/packages/shared/src/providers/fumadocs.ts +0 -116
  108. package/packages/shared/src/providers/internal.ts +0 -80
  109. package/packages/shared/src/types.ts +0 -73
  110. package/packages/shared/tsconfig.json +0 -18
  111. package/packages/shared/tsup.config.ts +0 -12
  112. package/packages/shared/vitest.config.ts +0 -4
  113. package/packages/solidjs/README.md +0 -1
  114. package/packages/solidjs/package.json +0 -33
  115. package/packages/solidjs/src/DocsLayout.tsx +0 -87
  116. package/packages/solidjs/src/DocsProvider.tsx +0 -95
  117. package/packages/solidjs/src/RouterDocsContent.tsx +0 -147
  118. package/packages/solidjs/src/RouterDocsLayout.tsx +0 -161
  119. package/packages/solidjs/src/components/Breadcrumbs.tsx +0 -27
  120. package/packages/solidjs/src/components/DocPage.tsx +0 -110
  121. package/packages/solidjs/src/components/DocSearch.tsx +0 -81
  122. package/packages/solidjs/src/components/DocSidebar.tsx +0 -92
  123. package/packages/solidjs/src/components/DocsLayout.tsx +0 -38
  124. package/packages/solidjs/src/components/EditButton.tsx +0 -15
  125. package/packages/solidjs/src/components/PageNavigation.tsx +0 -31
  126. package/packages/solidjs/src/components/TableOfContents.tsx +0 -41
  127. package/packages/solidjs/src/components/VersionSelector.tsx +0 -30
  128. package/packages/solidjs/src/components/index.ts +0 -9
  129. package/packages/solidjs/src/createDocs.ts +0 -62
  130. package/packages/solidjs/src/index.ts +0 -28
  131. package/packages/solidjs/src/pages/DocSearchPage.tsx +0 -72
  132. package/packages/solidjs/src/pages/DocViewPage.tsx +0 -80
  133. package/packages/solidjs/src/pages/DocsAdminPage.tsx +0 -123
  134. package/packages/solidjs/src/pages/DocsIndexPage.tsx +0 -85
  135. package/packages/solidjs/src/pages/index.ts +0 -4
  136. package/packages/solidjs/src/primitives/createDocSearch.ts +0 -42
  137. package/packages/solidjs/src/primitives/createDocs.ts +0 -35
  138. package/packages/solidjs/src/primitives/createDocsAdmin.ts +0 -63
  139. package/packages/solidjs/src/primitives/createTableOfContents.ts +0 -51
  140. package/packages/solidjs/src/primitives/index.ts +0 -4
  141. package/packages/solidjs/tsup.config.ts +0 -12
  142. package/packages/solidjs-css/README.md +0 -1
  143. package/packages/solidjs-css/package.json +0 -36
  144. package/packages/solidjs-css/src/DocsLayout.tsx +0 -106
  145. package/packages/solidjs-css/src/DocsProvider.tsx +0 -95
  146. package/packages/solidjs-css/src/RouterDocsContent.tsx +0 -54
  147. package/packages/solidjs-css/src/RouterDocsLayout.tsx +0 -104
  148. package/packages/solidjs-css/src/createDocs.ts +0 -62
  149. package/packages/solidjs-css/src/index.ts +0 -7
  150. package/packages/solidjs-css/src/index.tsx +0 -17
  151. package/packages/solidjs-css/src/pages/DocViewPage.tsx +0 -111
  152. package/packages/solidjs-css/src/pages/DocsAdminPage.tsx +0 -332
  153. package/packages/solidjs-css/src/pages/DocsIndexPage.tsx +0 -116
  154. package/packages/solidjs-css/src/pages/index.ts +0 -3
  155. package/packages/solidjs-css/src/primitives/index.ts +0 -1
  156. package/packages/solidjs-css/tsconfig.json +0 -20
  157. package/packages/solidjs-css/tsup.config.ts +0 -10
  158. package/pnpm-workspace.yaml +0 -2
@@ -1,21 +0,0 @@
1
- import React from 'react'
2
- import type { DocPage as DocPageType } from '@geenius-docs/shared'
3
- import { slugify } from '@geenius-docs/shared'
4
-
5
- export function DocPage({ page }: { page: DocPageType }) {
6
- const lines = page.content.split('\n'); const parts: string[] = []; let i = 0, inCode = false, codeLang = '', codeLines: string[] = []
7
- const esc = (t: string) => t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
8
-
9
- while (i < lines.length) {
10
- const line = lines[i]
11
- if (line.startsWith('```')) { if (!inCode) { inCode=true; codeLang=line.slice(3).trim(); codeLines=[] } else { parts.push(`<div style="margin:1rem 0"><div style="display:flex;justify-content:space-between;background:oklch(1 0 0/0.05);padding:0.5rem 1rem;border-radius:var(--docs-radius) var(--docs-radius) 0 0;font-size:0.75rem;color:oklch(1 0 0/0.5)">${codeLang||'code'}</div><pre style="overflow-x:auto;background:var(--docs-code-bg);padding:1rem;border-radius:0 0 var(--docs-radius) var(--docs-radius);font-size:0.875rem;line-height:1.65"><code>${esc(codeLines.join('\n'))}</code></pre></div>`); inCode=false }; i++; continue }
12
- if (inCode) { codeLines.push(line); i++; continue }
13
- const hm = line.match(/^(#{1,4})\s+(.+)$/); if (hm) { const lvl=hm[1].length,text=hm[2],id=slugify(text); parts.push(`<h${lvl} id="${id}" style="scroll-margin-top:5rem"><a href="#${id}">${esc(text)}</a></h${lvl}>`); i++; continue }
14
- if (line.startsWith('>')) { const ql:string[]=[]; while(i<lines.length&&lines[i].startsWith('>')){ql.push(lines[i].replace(/^>\s?/,'')); i++}; parts.push(`<blockquote>${esc(ql.join(' '))}</blockquote>`); continue }
15
- if (!line.trim()) { i++; continue }
16
- const fmt=esc(line).replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>').replace(/\*(.+?)\*/g,'<em>$1</em>').replace(/`(.+?)`/g,'<code>$1</code>').replace(/\[(.+?)\]\((.+?)\)/g,'<a href="$2">$1</a>')
17
- parts.push(`<p>${fmt}</p>`); i++
18
- }
19
-
20
- return <article className="docs__content-main" dangerouslySetInnerHTML={{ __html: parts.join('') }} />
21
- }
@@ -1,55 +0,0 @@
1
- import React, { useState, useEffect, useCallback, useRef } from 'react'
2
- import type { SearchResult } from '@geenius-docs/shared'
3
- import { highlightMatch } from '@geenius-docs/shared'
4
-
5
- interface DocSearchProps {
6
- results: SearchResult[]; query: string; onQuery: (q: string) => void
7
- onSelect: (result: SearchResult) => void; isOpen: boolean; onClose: () => void
8
- }
9
-
10
- export function DocSearch({ results, query, onQuery, onSelect, isOpen, onClose }: DocSearchProps) {
11
- const [activeIndex, setActiveIndex] = useState(0)
12
- const inputRef = useRef<HTMLInputElement>(null)
13
- useEffect(() => { if (isOpen) { inputRef.current?.focus(); setActiveIndex(0) } }, [isOpen])
14
- useEffect(() => { setActiveIndex(0) }, [results])
15
-
16
- const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
17
- if (e.key === 'ArrowDown') { e.preventDefault(); setActiveIndex(i => Math.min(i + 1, results.length - 1)) }
18
- else if (e.key === 'ArrowUp') { e.preventDefault(); setActiveIndex(i => Math.max(i - 1, 0)) }
19
- else if (e.key === 'Enter' && results[activeIndex]) { e.preventDefault(); onSelect(results[activeIndex]) }
20
- else if (e.key === 'Escape') { e.preventDefault(); onClose() }
21
- }, [results, activeIndex, onSelect, onClose])
22
-
23
- if (!isOpen) return null
24
-
25
- return (
26
- <div className="docs__search-modal" onClick={onClose}>
27
- <div className="docs__search-backdrop" />
28
- <div className="docs__search-panel" onClick={e => e.stopPropagation()}>
29
- <div className="docs__search-input-wrapper">
30
- <svg style={{ width: 20, height: 20, flexShrink: 0, color: 'oklch(1 0 0/0.3)' }} viewBox="0 0 20 20" fill="currentColor">
31
- <path fillRule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clipRule="evenodd" />
32
- </svg>
33
- <input ref={inputRef} type="text" value={query} onChange={e => onQuery(e.target.value)} onKeyDown={handleKeyDown}
34
- placeholder="Search documentation…" className="docs__search-input" />
35
- <kbd style={{ padding: '2px 6px', border: '1px solid oklch(1 0 0/0.1)', borderRadius: 4, background: 'oklch(1 0 0/0.03)', fontSize: 11, color: 'oklch(1 0 0/0.3)' }}>ESC</kbd>
36
- </div>
37
- <div className="docs__search-results">
38
- {results.length === 0 && query.trim() && <div className="docs__search-empty">No results for "{query}"</div>}
39
- {results.length === 0 && !query.trim() && <div className="docs__search-empty">Start typing to search…</div>}
40
- {results.map((result, idx) => (
41
- <button key={result.pageId} type="button" onClick={() => onSelect(result)}
42
- className={`docs__search-result ${idx === activeIndex ? 'docs__search-result--active' : ''}`}>
43
- <div className="docs__search-result-header">
44
- <span className="docs__search-result-badge">{result.sectionTitle}</span>
45
- <span className="docs__search-result-title">{result.pageTitle}</span>
46
- </div>
47
- <p className="docs__search-result-snippet">{highlightMatch(result.highlight, query)}</p>
48
- </button>
49
- ))}
50
- </div>
51
- <div className="docs__search-footer"><span>↑↓ navigate</span><span>↵ select</span><span>esc close</span></div>
52
- </div>
53
- </div>
54
- )
55
- }
@@ -1,56 +0,0 @@
1
- import React, { useState, useCallback } from 'react'
2
- import type { DocPage, DocSection } from '@geenius-docs/shared'
3
- import './styles.css'
4
-
5
- interface DocSidebarProps {
6
- sections: (DocSection & { pages?: DocPage[]; pageCount?: number })[]
7
- currentPageId?: string
8
- onNavigate: (page: DocPage, section: DocSection) => void
9
- }
10
-
11
- export function DocSidebar({ sections, currentPageId, onNavigate }: DocSidebarProps) {
12
- const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
13
- const toggle = useCallback((id: string) => {
14
- setExpandedIds(prev => { const n = new Set(prev); if (n.has(id)) n.delete(id); else n.add(id); return n })
15
- }, [])
16
-
17
- const topLevel = sections.filter(s => !s.parentId)
18
- const childrenOf = (parentId: string) => sections.filter(s => s.parentId === parentId)
19
-
20
- const renderSection = (section: DocSection & { pages?: DocPage[]; pageCount?: number }, depth: number) => {
21
- const children = childrenOf(section.id)
22
- const isExpanded = expandedIds.has(section.id)
23
- const pages = section.pages ?? []
24
-
25
- return (
26
- <div key={section.id} className={`docs__sidebar-section ${isExpanded ? 'docs__sidebar-section--active' : ''}`} style={{ paddingLeft: depth * 12 }}>
27
- <button type="button" className="docs__sidebar-section-btn" onClick={() => toggle(section.id)}>
28
- {section.icon && <span className="docs__sidebar-section-icon">{section.icon}</span>}
29
- <span className="docs__sidebar-section-title">{section.title}</span>
30
- {section.pageCount != null && <span className="docs__sidebar-section-count">{section.pageCount}</span>}
31
- <svg className={`docs__sidebar-section-chevron ${isExpanded ? 'docs__sidebar-section-chevron--open' : ''}`} viewBox="0 0 16 16">
32
- <path d="M6 4l4 4-4 4" stroke="currentColor" strokeWidth="2" fill="none" />
33
- </svg>
34
- </button>
35
- {isExpanded && (
36
- <div className="docs__sidebar-pages">
37
- {pages.map(page => (
38
- <button key={page.id} type="button" onClick={() => onNavigate(page, section)}
39
- className={`docs__sidebar-page ${currentPageId === page.id ? 'docs__sidebar-page--active' : ''}`}>
40
- {page.title}
41
- </button>
42
- ))}
43
- {children.map(child => renderSection(child, depth + 1))}
44
- </div>
45
- )}
46
- </div>
47
- )
48
- }
49
-
50
- return (
51
- <nav className="docs__sidebar">
52
- <div className="docs__sidebar-header">Documentation</div>
53
- {topLevel.map(s => renderSection(s, 0))}
54
- </nav>
55
- )
56
- }
@@ -1,28 +0,0 @@
1
- import React from 'react'
2
- import type { DocPage, DocSection, TocItem, BreadcrumbItem } from '@geenius-docs/shared'
3
- import { DocSidebar } from './DocSidebar'
4
- import { TableOfContents, Breadcrumbs } from './common'
5
-
6
- interface DocsLayoutProps {
7
- sections: (DocSection & { pages?: DocPage[] })[]
8
- toc?: TocItem[]; activeHeadingId?: string; breadcrumbs?: BreadcrumbItem[]
9
- currentPageId?: string; onNavigate: (page: DocPage, section: DocSection) => void
10
- children: React.ReactNode
11
- }
12
-
13
- export function DocsLayout({ sections, toc, activeHeadingId, breadcrumbs, currentPageId, onNavigate, children }: DocsLayoutProps) {
14
- return (
15
- <div className="docs__layout">
16
- <DocSidebar sections={sections} currentPageId={currentPageId} onNavigate={onNavigate} />
17
- <div className="docs__content">
18
- <div className="docs__content-inner">
19
- <div className="docs__content-main">
20
- {breadcrumbs && breadcrumbs.length > 0 && <Breadcrumbs items={breadcrumbs} />}
21
- {children}
22
- </div>
23
- {toc && toc.length > 0 && <TableOfContents toc={toc} activeId={activeHeadingId} />}
24
- </div>
25
- </div>
26
- </div>
27
- )
28
- }
@@ -1,93 +0,0 @@
1
- import React from 'react'
2
- import type { TocItem, BreadcrumbItem } from '@geenius-docs/shared'
3
-
4
- export function TableOfContents({ toc, activeId }: { toc: TocItem[]; activeId?: string }) {
5
- if (toc.length === 0) return null
6
- return (
7
- <nav className="docs__toc">
8
- <h4 className="docs__toc-heading">On this page</h4>
9
- <div className="docs__toc-list">
10
- {toc.map(item => (
11
- <React.Fragment key={item.id}>
12
- <a href={`#${item.id}`} className={`docs__toc-item ${activeId === item.id ? 'docs__toc-item--active' : ''}`}>{item.text}</a>
13
- {item.children.map(child => (
14
- <a key={child.id} href={`#${child.id}`} className={`docs__toc-item docs__toc-item--h3 ${activeId === child.id ? 'docs__toc-item--active' : ''}`}>{child.text}</a>
15
- ))}
16
- </React.Fragment>
17
- ))}
18
- </div>
19
- </nav>
20
- )
21
- }
22
-
23
- export function Breadcrumbs({ items }: { items: BreadcrumbItem[] }) {
24
- if (items.length === 0) return null
25
- return (
26
- <nav className="docs__breadcrumbs" aria-label="Breadcrumb">
27
- {items.map((item, idx) => (
28
- <React.Fragment key={idx}>
29
- {idx > 0 && (
30
- <svg className="docs__breadcrumb-separator" viewBox="0 0 16 16" fill="none"><path d="M6 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" /></svg>
31
- )}
32
- {idx === items.length - 1
33
- ? <span className="docs__breadcrumb-item docs__breadcrumb-item--current">{item.title}</span>
34
- : <a href={item.href} className="docs__breadcrumb-item">{item.title}</a>
35
- }
36
- </React.Fragment>
37
- ))}
38
- </nav>
39
- )
40
- }
41
-
42
- export function PageNavigation({ prev, next }: { prev?: { title: string; href: string }; next?: { title: string; href: string } }) {
43
- if (!prev && !next) return null
44
- return (
45
- <div className="docs__page-nav">
46
- {prev ? (
47
- <a href={prev.href} className="docs__page-nav-prev">
48
- <span className="docs__page-nav-label">← Previous</span>
49
- <span className="docs__page-nav-title">{prev.title}</span>
50
- </a>
51
- ) : <div style={{ flex: 1 }} />}
52
- {next ? (
53
- <a href={next.href} className="docs__page-nav-next">
54
- <span className="docs__page-nav-label">Next →</span>
55
- <span className="docs__page-nav-title">{next.title}</span>
56
- </a>
57
- ) : <div style={{ flex: 1 }} />}
58
- </div>
59
- )
60
- }
61
-
62
- export function EditButton({ pageSlug, editUrl }: { pageSlug: string; editUrl?: string }) {
63
- if (!editUrl) return null
64
- return (
65
- <a href={`${editUrl.replace(/\/$/, '')}/${pageSlug}.mdx`} target="_blank" rel="noopener noreferrer" className="docs__edit-btn">
66
- <svg className="docs__edit-btn-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor"><path d="M11.5 1.5l3 3-9 9H2.5v-3l9-9z" strokeWidth="1.5" strokeLinejoin="round" /></svg>
67
- Edit this page
68
- </a>
69
- )
70
- }
71
-
72
- export function VersionSelector({ versions, current, onSelect }: { versions: string[]; current: string; onSelect: (v: string) => void }) {
73
- const [isOpen, setIsOpen] = React.useState(false)
74
- if (versions.length <= 1) return null
75
- return (
76
- <div className="docs__version-selector">
77
- <button type="button" className="docs__version-btn" onClick={() => setIsOpen(!isOpen)}>
78
- v{current}
79
- <svg style={{ width: 12, height: 12, transform: isOpen ? 'rotate(180deg)' : '', transition: 'transform 0.2s' }} viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" stroke="currentColor" strokeWidth="2" fill="none" /></svg>
80
- </button>
81
- {isOpen && (
82
- <div className="docs__version-dropdown">
83
- {versions.map(v => (
84
- <button key={v} type="button" className={`docs__version-option ${v === current ? 'docs__version-option--active' : ''}`}
85
- onClick={() => { onSelect(v); setIsOpen(false) }}>
86
- v{v}{v === current && <span style={{ marginLeft: 'auto', fontSize: 10 }}>✓</span>}
87
- </button>
88
- ))}
89
- </div>
90
- )}
91
- </div>
92
- )
93
- }
@@ -1,5 +0,0 @@
1
- export { DocSidebar } from './DocSidebar'
2
- export { DocPage } from './DocPage'
3
- export { DocSearch } from './DocSearch'
4
- export { DocsLayout } from './DocsLayout'
5
- export { TableOfContents, Breadcrumbs, PageNavigation, EditButton, VersionSelector } from './common'
@@ -1,2 +0,0 @@
1
- export { useDocs, useDocSearch, useDocsAdmin, useTableOfContents } from '@geenius-docs/react'
2
- export type { DocsState, DocSearchState, DocsAdminActions, TableOfContentsState } from '@geenius-docs/react'
@@ -1,6 +0,0 @@
1
- export { DocsProvider, useDocs } from './DocsProvider'
2
- export type { DocsProviderProps } from './DocsProvider'
3
-
4
- export { DocsLayout } from './DocsLayout'
5
- export { RouterDocsLayout, type DocItem } from './RouterDocsLayout'
6
- export { RouterDocsContent } from './RouterDocsContent'
@@ -1,3 +0,0 @@
1
- // @geenius-docs/react-css — src/index.tsx
2
- // This file is intentionally empty.
3
- // The canonical package entry point is src/index.ts (tsup entry).
@@ -1,78 +0,0 @@
1
- import React, { useEffect, useMemo } from 'react'
2
- import type { DocPage as DocPageType, DocSection } from '@geenius-docs/shared'
3
- import { buildBreadcrumbs } from '@geenius-docs/shared'
4
- import { useDocs, useTableOfContents } from '../hooks'
5
- import { DocsLayout } from '../components/DocsLayout'
6
- import { DocPage } from '../components/DocPage'
7
- import { EditButton, PageNavigation } from '../components/common'
8
- import '../styles.css'
9
-
10
- interface DocViewPageProps {
11
- tree: (DocSection & { pages: DocPageType[]; pageCount: number })[] | undefined
12
- page: DocPageType | null | undefined
13
- editPageUrl?: string
14
- onNavigate: (page: DocPageType, section: DocSection) => void
15
- onIncrementView?: (pageId: string) => void
16
- }
17
-
18
- export function DocViewPage({ tree, page, editPageUrl, onNavigate, onIncrementView }: DocViewPageProps) {
19
- const docs = useDocs(tree)
20
- const { toc, activeId } = useTableOfContents(page?.content)
21
-
22
- useEffect(() => { if (page?.id && onIncrementView) onIncrementView(page.id) }, [page?.id, onIncrementView])
23
-
24
- const breadcrumbs = useMemo(() => page ? buildBreadcrumbs(docs.sections, page.sectionId, page.slug) : [], [docs.sections, page])
25
-
26
- const { prev, next } = useMemo(() => {
27
- if (!page) return { prev: undefined, next: undefined }
28
- const all = docs.flatPages; const idx = all.findIndex(p => p.id === page.id); const sec = docs.sections.find(s => s.id === page.sectionId)
29
- return {
30
- prev: idx > 0 ? { title: all[idx - 1].title, href: `/docs/${sec?.slug ?? ''}/${all[idx - 1].slug}` } : undefined,
31
- next: idx < all.length - 1 ? { title: all[idx + 1].title, href: `/docs/${sec?.slug ?? ''}/${all[idx + 1].slug}` } : undefined,
32
- }
33
- }, [page, docs.flatPages, docs.sections])
34
-
35
- if (docs.isLoading || page === undefined) return (
36
- <div className="docs__layout">
37
- <div className="docs__sidebar" style={{ padding: '1rem' }}>
38
- {Array.from({ length: 8 }).map((_, i) => <div key={i} className="docs__skeleton" style={{ height: 32, marginBottom: 8 }} />)}
39
- </div>
40
- <div className="docs__content" style={{ padding: '2rem' }}>
41
- <div className="docs__skeleton" style={{ width: 384, height: 40, marginBottom: 16 }} />
42
- {Array.from({ length: 10 }).map((_, i) => <div key={i} className="docs__skeleton" style={{ height: 16, marginBottom: 12, width: `${60 + Math.random() * 40}%` }} />)}
43
- </div>
44
- </div>
45
- )
46
-
47
- if (!page) return (
48
- <div className="docs__empty" style={{ minHeight: '100vh', background: 'var(--docs-bg)' }}>
49
- <div className="docs__empty-icon">🔍</div>
50
- <h2 className="docs__empty-title">Page not found</h2>
51
- </div>
52
- )
53
-
54
- return (
55
- <DocsLayout sections={docs.sections} toc={toc} activeHeadingId={activeId} breadcrumbs={breadcrumbs} currentPageId={page.id} onNavigate={onNavigate}>
56
- <div className="docs__page-header">
57
- <h1 className="docs__page-title">{page.title}</h1>
58
- <div className="docs__reading-meta">
59
- <span className="docs__reading-meta-author">
60
- {page.author.avatar
61
- ? <img src={page.author.avatar} alt="" className="docs__reading-meta-avatar" />
62
- : <span className="docs__reading-meta-avatar-placeholder">{page.author.name[0]}</span>}
63
- {page.author.name}
64
- </span>
65
- {page.readingTime > 0 && <span>{page.readingTime} min read</span>}
66
- {page.viewCount > 0 && <span>{page.viewCount.toLocaleString()} views</span>}
67
- </div>
68
- {page.tags.length > 0 && <div className="docs__tags">{page.tags.map(t => <span key={t} className="docs__tag">{t}</span>)}</div>}
69
- </div>
70
- <DocPage page={page} />
71
- <div style={{ marginTop: '2.5rem', paddingTop: '1.5rem', borderTop: '1px solid var(--docs-border)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
72
- <EditButton pageSlug={page.slug} editUrl={editPageUrl} />
73
- {page.version && <span style={{ fontSize: '0.6875rem', color: 'oklch(1 0 0/0.25)' }}>v{page.version}</span>}
74
- </div>
75
- <PageNavigation prev={prev} next={next} />
76
- </DocsLayout>
77
- )
78
- }
@@ -1,101 +0,0 @@
1
- import React, { useState, useMemo } from 'react'
2
- import type { DocPage, DocSection } from '@geenius-docs/shared'
3
- import type { DocsAdminActions } from '@geenius-docs/react'
4
- import '../styles.css'
5
-
6
- interface DocsAdminPageProps {
7
- tree: (DocSection & { pages: DocPage[]; pageCount: number })[] | undefined
8
- allPages?: DocPage[]
9
- admin: DocsAdminActions
10
- }
11
-
12
- export function DocsAdminPage({ tree, allPages, admin }: DocsAdminPageProps) {
13
- const sections = useMemo(() => tree ?? [], [tree])
14
- const [selectedId, setSelectedId] = useState<string | null>(null)
15
- const [showSF, setShowSF] = useState(false)
16
- const [showPF, setShowPF] = useState(false)
17
- const [sf, setSf] = useState({ title: '', slug: '', description: '', icon: '', access: 'team' as const })
18
- const [pf, setPf] = useState({ title: '', slug: '', content: '', access: 'team' as const, tags: '' })
19
- const selected = sections.find(s => s.id === selectedId)
20
- const pages = useMemo(() => { if (!selected) return []; if (allPages) return allPages.filter(p => p.sectionId === selectedId); return selected.pages ?? [] }, [selected, allPages, selectedId])
21
-
22
- if (tree === undefined) return (
23
- <div style={{ minHeight: '100vh', background: 'var(--docs-bg)', padding: '3rem 1.5rem' }}>
24
- <div style={{ maxWidth: '72rem', margin: '0 auto' }}>
25
- <div className="docs__skeleton" style={{ width: 192, height: 40, marginBottom: 32 }} />
26
- <div className="docs__admin-grid"><div className="docs__skeleton" style={{ height: 384 }} /><div className="docs__skeleton" style={{ height: 384 }} /></div>
27
- </div>
28
- </div>
29
- )
30
-
31
- return (
32
- <div style={{ minHeight: '100vh', background: 'var(--docs-bg)', color: 'var(--docs-text)' }}>
33
- <div style={{ maxWidth: '72rem', margin: '0 auto', padding: '3rem 1.5rem' }}>
34
- <h1 style={{ fontSize: '1.5rem', fontWeight: 700, marginBottom: '2rem' }}>Docs Admin</h1>
35
- <div className="docs__admin-grid">
36
- <div className="docs__admin-panel">
37
- <div className="docs__admin-panel-header">
38
- <h2 className="docs__admin-panel-title">Sections</h2>
39
- <button type="button" className="docs__admin-add-btn" onClick={() => setShowSF(!showSF)}>+ Add</button>
40
- </div>
41
- {showSF && (
42
- <div className="docs__admin-form">
43
- <input className="docs__admin-input" placeholder="Title" value={sf.title} onChange={e => setSf({...sf, title: e.target.value})} />
44
- <input className="docs__admin-input" placeholder="Slug" value={sf.slug} onChange={e => setSf({...sf, slug: e.target.value})} />
45
- <input className="docs__admin-input" placeholder="Icon" value={sf.icon} onChange={e => setSf({...sf, icon: e.target.value})} />
46
- <input className="docs__admin-input" placeholder="Description" value={sf.description} onChange={e => setSf({...sf, description: e.target.value})} />
47
- <div className="docs__admin-form-actions">
48
- <button type="button" className="docs__admin-add-btn" onClick={async () => { await admin.createSection({...sf, order: sections.length, icon: sf.icon||undefined, description: sf.description||undefined}); setSf({title:'',slug:'',description:'',icon:'',access:'team'}); setShowSF(false) }}>Create</button>
49
- <button type="button" style={{padding:'0.375rem 0.75rem',border:'1px solid var(--docs-border)',borderRadius:'var(--docs-radius)',background:'transparent',color:'var(--docs-text)',fontSize:'0.6875rem',cursor:'pointer'}} onClick={() => setShowSF(false)}>Cancel</button>
50
- </div>
51
- </div>
52
- )}
53
- {sections.length === 0 && <p style={{padding:'2rem',textAlign:'center',fontSize:'0.875rem',color:'var(--docs-text-muted)'}}>No sections</p>}
54
- {sections.map(s => (
55
- <div key={s.id} className={`docs__admin-section-item ${selectedId === s.id ? 'docs__admin-section-item--selected' : ''}`} onClick={() => setSelectedId(s.id)}>
56
- {s.icon && <span>{s.icon}</span>}
57
- <span style={{flex:1,fontSize:'0.875rem',fontWeight:500,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{s.title}</span>
58
- <span style={{fontSize:'0.6875rem',opacity:0.4}}>{s.pageCount ?? 0}</span>
59
- <button type="button" style={{background:'transparent',border:'none',color:'oklch(1 0 0/0.2)',cursor:'pointer',padding:4}} onClick={e => { e.stopPropagation(); if (confirm(`Delete "${s.title}"?`)) admin.deleteSection(s.id) }}>✕</button>
60
- </div>
61
- ))}
62
- </div>
63
- <div className="docs__admin-panel">
64
- <div className="docs__admin-panel-header">
65
- <h2 className="docs__admin-panel-title">{selected ? `Pages — ${selected.title}` : 'Pages'}</h2>
66
- {selected && <button type="button" className="docs__admin-add-btn" onClick={() => setShowPF(!showPF)}>+ Add</button>}
67
- </div>
68
- {!selected && <p style={{padding:'4rem',textAlign:'center',fontSize:'0.875rem',color:'var(--docs-text-muted)'}}>Select a section</p>}
69
- {showPF && selected && (
70
- <div className="docs__admin-form">
71
- <input className="docs__admin-input" placeholder="Title" value={pf.title} onChange={e => setPf({...pf, title: e.target.value})} />
72
- <input className="docs__admin-input" placeholder="Slug" value={pf.slug} onChange={e => setPf({...pf, slug: e.target.value})} />
73
- <textarea className="docs__admin-input docs__admin-textarea" placeholder="Content (MDX)" value={pf.content} onChange={e => setPf({...pf, content: e.target.value})} />
74
- <input className="docs__admin-input" placeholder="Tags (comma)" value={pf.tags} onChange={e => setPf({...pf, tags: e.target.value})} />
75
- <div className="docs__admin-form-actions">
76
- <button type="button" className="docs__admin-add-btn" onClick={async () => { await admin.createPage({title:pf.title,slug:pf.slug,content:pf.content,sectionId:selectedId!,access:pf.access,tags:pf.tags?pf.tags.split(',').map(t=>t.trim()):[],order:pages.length}); setPf({title:'',slug:'',content:'',access:'team',tags:''}); setShowPF(false) }}>Create</button>
77
- <button type="button" style={{padding:'0.375rem 0.75rem',border:'1px solid var(--docs-border)',borderRadius:'var(--docs-radius)',background:'transparent',color:'var(--docs-text)',fontSize:'0.6875rem',cursor:'pointer'}} onClick={() => setShowPF(false)}>Cancel</button>
78
- </div>
79
- </div>
80
- )}
81
- {selected && pages.map(page => (
82
- <div key={page.id} className="docs__admin-page-item">
83
- <div className="docs__admin-page-info">
84
- <p className="docs__admin-page-title">{page.title}</p>
85
- <p className="docs__admin-page-slug">/{page.slug}</p>
86
- </div>
87
- <span className={`docs__status-badge docs__status-badge--${page.status}`}>{page.status}</span>
88
- <div className="docs__admin-actions">
89
- {page.status === 'draft' && <button type="button" className="docs__admin-action-btn docs__admin-action-btn--publish" onClick={() => admin.publishPage(page.id)}>Publish</button>}
90
- {page.status === 'published' && <button type="button" className="docs__admin-action-btn docs__admin-action-btn--archive" onClick={() => admin.archivePage(page.id)}>Archive</button>}
91
- <button type="button" className="docs__admin-action-btn docs__admin-action-btn--delete" onClick={() => { if (confirm(`Delete "${page.title}"?`)) admin.deletePage(page.id) }}>✕</button>
92
- </div>
93
- </div>
94
- ))}
95
- {selected && pages.length === 0 && !showPF && <p style={{padding:'3rem',textAlign:'center',fontSize:'0.875rem',color:'var(--docs-text-muted)'}}>No pages</p>}
96
- </div>
97
- </div>
98
- </div>
99
- </div>
100
- )
101
- }
@@ -1,68 +0,0 @@
1
- import React, { useState, useEffect, useCallback } from 'react'
2
- import type { DocPage, DocSection, SearchResult } from '@geenius-docs/shared'
3
- import { buildDocsIndex, searchDocs } from '@geenius-docs/shared'
4
- import { useDocs, useDocSearch } from '../hooks'
5
- import { DocSearch } from '../components/DocSearch'
6
- import '../styles.css'
7
-
8
- interface DocsIndexPageProps {
9
- tree: (DocSection & { pages: DocPage[]; pageCount: number })[] | undefined
10
- onSelectPage?: (page: DocPage, section: DocSection) => void
11
- }
12
-
13
- export function DocsIndexPage({ tree, onSelectPage }: DocsIndexPageProps) {
14
- const docs = useDocs(tree)
15
- const [isSearchOpen, setIsSearchOpen] = useState(false)
16
- const searchFn = useCallback((q: string): SearchResult[] => { const idx = buildDocsIndex(docs.flatPages, docs.sections); return searchDocs(q, idx) }, [docs.flatPages, docs.sections])
17
- const search = useDocSearch(searchFn)
18
-
19
- useEffect(() => { const h = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); setIsSearchOpen(true) } }; document.addEventListener('keydown', h); return () => document.removeEventListener('keydown', h) }, [])
20
-
21
- if (docs.isLoading) return (
22
- <div style={{ minHeight: '100vh', background: 'var(--docs-bg)', padding: '4rem 1.5rem' }}>
23
- <div style={{ maxWidth: '64rem', margin: '0 auto' }}>
24
- <div className="docs__skeleton" style={{ width: 256, height: 40, marginBottom: 40 }} />
25
- <div className="docs__skeleton" style={{ maxWidth: 560, height: 48, marginBottom: 32 }} />
26
- <div className="docs__section-grid">{Array.from({length: 6}).map((_,i) => <div key={i} className="docs__skeleton" style={{ height: 144 }} />)}</div>
27
- </div>
28
- </div>
29
- )
30
-
31
- if (docs.sections.length === 0) return (
32
- <div className="docs__empty" style={{ minHeight: '100vh', background: 'var(--docs-bg)' }}>
33
- <div className="docs__empty-icon">📚</div>
34
- <h2 className="docs__empty-title">No documentation yet</h2>
35
- <p className="docs__empty-desc">Create your first section and pages to get started.</p>
36
- </div>
37
- )
38
-
39
- return (
40
- <div style={{ minHeight: '100vh', background: 'var(--docs-bg)', color: 'var(--docs-text)' }}>
41
- <div style={{ maxWidth: '64rem', margin: '0 auto', padding: '4rem 1.5rem' }}>
42
- <h1 style={{ fontSize: '2.25rem', fontWeight: 700, letterSpacing: '-0.02em', marginBottom: '0.5rem' }}>Documentation</h1>
43
- <p style={{ fontSize: '1.125rem', color: 'var(--docs-text-muted)', marginBottom: '2.5rem' }}>Browse guides, API references, and tutorials.</p>
44
-
45
- <button type="button" className="docs__search-trigger" onClick={() => setIsSearchOpen(true)}>
46
- <svg className="docs__search-trigger-icon" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clipRule="evenodd" /></svg>
47
- <span className="docs__search-trigger-text">Search documentation…</span>
48
- <kbd className="docs__search-trigger-kbd">⌘K</kbd>
49
- </button>
50
-
51
- <div className="docs__section-grid">
52
- {docs.sections.filter(s => !s.parentId).map(section => (
53
- <button key={section.id} type="button" className="docs__section-card"
54
- onClick={() => { const fp = section.pages?.[0]; if (fp && onSelectPage) onSelectPage(fp, section) }}>
55
- {section.icon && <div className="docs__section-card-icon">{section.icon}</div>}
56
- <h3 className="docs__section-card-title">{section.title}</h3>
57
- {section.description && <p className="docs__section-card-desc">{section.description}</p>}
58
- <div className="docs__section-card-meta">{section.pageCount ?? 0} pages</div>
59
- </button>
60
- ))}
61
- </div>
62
- </div>
63
- <DocSearch results={search.results} query={search.query} onQuery={search.setQuery}
64
- onSelect={r => { setIsSearchOpen(false); search.clearSearch(); const sec = docs.sections.find(s => s.slug === r.sectionSlug); const pg = docs.flatPages.find(p => p.id === r.pageId); if (pg && sec && onSelectPage) onSelectPage(pg, sec) }}
65
- isOpen={isSearchOpen} onClose={() => setIsSearchOpen(false)} />
66
- </div>
67
- )
68
- }
@@ -1,3 +0,0 @@
1
- export { DocsIndexPage } from './DocsIndexPage'
2
- export { DocViewPage } from './DocViewPage'
3
- export { DocsAdminPage } from './DocsAdminPage'
@@ -1,58 +0,0 @@
1
- import { useState, useCallback, useMemo, useEffect } from 'react'
2
- import type { DocsPage, DocsSidebar, DocsSearchResult, DocsProvider } from '@geenius-docs/shared'
3
-
4
- /**
5
- * React hook for documentation rendering and navigation.
6
- * Wraps a DocsProvider with React state management.
7
- */
8
- export function useDocs(provider: DocsProvider) {
9
- const [sidebar, setSidebar] = useState<DocsSidebar | null>(null)
10
- const [currentPage, setCurrentPage] = useState<DocsPage | null>(null)
11
- const [searchResults, setSearchResults] = useState<DocsSearchResult[]>([])
12
- const [isLoading, setIsLoading] = useState(false)
13
- const [searchQuery, setSearchQuery] = useState('')
14
-
15
- // Load sidebar on mount
16
- useEffect(() => {
17
- provider.getSidebar().then(setSidebar)
18
- }, [provider])
19
-
20
- const loadPage = useCallback(async (slug: string) => {
21
- setIsLoading(true)
22
- try {
23
- const page = await provider.getPage(slug)
24
- setCurrentPage(page)
25
- } finally {
26
- setIsLoading(false)
27
- }
28
- }, [provider])
29
-
30
- const search = useCallback(async (query: string) => {
31
- setSearchQuery(query)
32
- if (!query.trim()) {
33
- setSearchResults([])
34
- return
35
- }
36
- const results = await provider.search(query)
37
- setSearchResults(results)
38
- }, [provider])
39
-
40
- const navigation = useMemo(() => {
41
- if (!currentPage) return { prev: null, next: null }
42
- return {
43
- prev: currentPage.prev ?? null,
44
- next: currentPage.next ?? null,
45
- }
46
- }, [currentPage])
47
-
48
- return {
49
- sidebar,
50
- currentPage,
51
- navigation,
52
- isLoading,
53
- searchResults,
54
- searchQuery,
55
- loadPage,
56
- search,
57
- }
58
- }
@@ -1,19 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "jsx": "react-jsx",
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "isolatedModules": true,
12
- "target": "ES2022",
13
- "module": "ESNext",
14
- "moduleResolution": "bundler"
15
- },
16
- "include": [
17
- "src"
18
- ]
19
- }
@@ -1,10 +0,0 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- export default defineConfig({
4
- entry: ['src/index.ts'],
5
- format: ['cjs', 'esm'],
6
- dts: true,
7
- clean: true,
8
- sourcemap: true,
9
- external: ['react', '@tanstack/react-router'],
10
- })
@@ -1 +0,0 @@
1
- # ✦ @geenius-docs/shared\n\n> Framework-agnostic docs types, providers, and utilities\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/shared\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius-docs/shared';\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