@jerydam/lumina-sdk 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 (152) hide show
  1. package/BUTTON_FIXES.md +59 -0
  2. package/DOCS_INDEX.md +332 -0
  3. package/DOCUMENTATION.md +252 -0
  4. package/DOCUMENTATION_BUILD_SUMMARY.md +376 -0
  5. package/DOCUMENTATION_COMPLETE.md +311 -0
  6. package/FEATURES.md +333 -0
  7. package/Lumina-sdk/src/components/lumina-provider.tsx +46 -0
  8. package/Lumina-sdk/src/components/transaction-confirm.tsx +242 -0
  9. package/Lumina-sdk/src/components/wallet-display.tsx +157 -0
  10. package/Lumina-sdk/src/components/wallet-login.tsx +163 -0
  11. package/Lumina-sdk/src/hooks/use-mobile.ts +19 -0
  12. package/Lumina-sdk/src/hooks/use-toast.ts +191 -0
  13. package/Lumina-sdk/src/index.ts +0 -0
  14. package/Lumina-sdk/src/lib/api.ts +66 -0
  15. package/Lumina-sdk/src/lib/utils.ts +6 -0
  16. package/Lumina-sdk/src/package.json +42 -0
  17. package/Lumina-sdk/src/tsconfig.json +19 -0
  18. package/NEW_FILES_MANIFEST.txt +146 -0
  19. package/README.md +298 -0
  20. package/app/dashboard/analytics/page.tsx +218 -0
  21. package/app/dashboard/api-keys/page.tsx +260 -0
  22. package/app/dashboard/billing/page.tsx +412 -0
  23. package/app/dashboard/integration/page.tsx +185 -0
  24. package/app/dashboard/layout.tsx +18 -0
  25. package/app/dashboard/page.tsx +244 -0
  26. package/app/dashboard/settings/page.tsx +285 -0
  27. package/app/dashboard/users/page.tsx +148 -0
  28. package/app/docs/api/authentication/page.tsx +246 -0
  29. package/app/docs/api/endpoints/page.tsx +397 -0
  30. package/app/docs/api/errors/page.tsx +305 -0
  31. package/app/docs/api/overview/page.tsx +306 -0
  32. package/app/docs/examples/basic-setup/page.tsx +256 -0
  33. package/app/docs/examples/multi-chain/page.tsx +331 -0
  34. package/app/docs/examples/nextjs-full-stack/page.tsx +332 -0
  35. package/app/docs/getting-started/environment-setup/page.tsx +243 -0
  36. package/app/docs/getting-started/installation/page.tsx +187 -0
  37. package/app/docs/getting-started/introduction/page.tsx +178 -0
  38. package/app/docs/getting-started/quick-start/page.tsx +199 -0
  39. package/app/docs/guides/nextjs/page.tsx +358 -0
  40. package/app/docs/guides/react/page.tsx +230 -0
  41. package/app/docs/guides/security/page.tsx +284 -0
  42. package/app/docs/layout.tsx +32 -0
  43. package/app/docs/page.tsx +180 -0
  44. package/app/docs/sdk/lumina-provider/page.tsx +186 -0
  45. package/app/docs/sdk/transaction-confirm/page.tsx +331 -0
  46. package/app/docs/sdk/wallet-display/page.tsx +224 -0
  47. package/app/docs/sdk/wallet-login/page.tsx +207 -0
  48. package/app/docs/troubleshooting/common-issues/page.tsx +301 -0
  49. package/app/docs/troubleshooting/faq/page.tsx +105 -0
  50. package/app/globals.css +125 -0
  51. package/app/invite/[token]/page.tsx +78 -0
  52. package/app/layout.tsx +36 -0
  53. package/app/login/page.tsx +175 -0
  54. package/app/page.tsx +336 -0
  55. package/app/sdk-demo/page.tsx +239 -0
  56. package/components/dashboard-sidebar.tsx +113 -0
  57. package/components/docs/breadcrumb.tsx +51 -0
  58. package/components/docs/callout.tsx +53 -0
  59. package/components/docs/code-block.tsx +77 -0
  60. package/components/docs/docs-sidebar.tsx +214 -0
  61. package/components/docs/table-of-contents.tsx +83 -0
  62. package/components/sdk/lumina-provider.tsx +46 -0
  63. package/components/sdk/transaction-confirm.tsx +242 -0
  64. package/components/sdk/wallet-display.tsx +157 -0
  65. package/components/sdk/wallet-login.tsx +163 -0
  66. package/components/theme-provider.tsx +11 -0
  67. package/components/ui/accordion.tsx +66 -0
  68. package/components/ui/alert-dialog.tsx +157 -0
  69. package/components/ui/alert.tsx +66 -0
  70. package/components/ui/aspect-ratio.tsx +11 -0
  71. package/components/ui/avatar.tsx +53 -0
  72. package/components/ui/badge.tsx +46 -0
  73. package/components/ui/breadcrumb.tsx +109 -0
  74. package/components/ui/button-group.tsx +83 -0
  75. package/components/ui/button.tsx +60 -0
  76. package/components/ui/calendar.tsx +213 -0
  77. package/components/ui/card.tsx +92 -0
  78. package/components/ui/carousel.tsx +241 -0
  79. package/components/ui/chart.tsx +351 -0
  80. package/components/ui/checkbox.tsx +32 -0
  81. package/components/ui/collapsible.tsx +33 -0
  82. package/components/ui/command.tsx +184 -0
  83. package/components/ui/context-menu.tsx +252 -0
  84. package/components/ui/dialog.tsx +143 -0
  85. package/components/ui/drawer.tsx +135 -0
  86. package/components/ui/dropdown-menu.tsx +257 -0
  87. package/components/ui/empty.tsx +104 -0
  88. package/components/ui/field.tsx +244 -0
  89. package/components/ui/form.tsx +167 -0
  90. package/components/ui/hover-card.tsx +44 -0
  91. package/components/ui/input-group.tsx +169 -0
  92. package/components/ui/input-otp.tsx +77 -0
  93. package/components/ui/input.tsx +21 -0
  94. package/components/ui/item.tsx +193 -0
  95. package/components/ui/kbd.tsx +28 -0
  96. package/components/ui/label.tsx +24 -0
  97. package/components/ui/menubar.tsx +276 -0
  98. package/components/ui/navigation-menu.tsx +166 -0
  99. package/components/ui/pagination.tsx +127 -0
  100. package/components/ui/popover.tsx +48 -0
  101. package/components/ui/progress.tsx +31 -0
  102. package/components/ui/radio-group.tsx +45 -0
  103. package/components/ui/resizable.tsx +56 -0
  104. package/components/ui/scroll-area.tsx +58 -0
  105. package/components/ui/select.tsx +185 -0
  106. package/components/ui/separator.tsx +28 -0
  107. package/components/ui/sheet.tsx +139 -0
  108. package/components/ui/sidebar.tsx +726 -0
  109. package/components/ui/skeleton.tsx +13 -0
  110. package/components/ui/slider.tsx +59 -0
  111. package/components/ui/sonner.tsx +25 -0
  112. package/components/ui/spinner.tsx +16 -0
  113. package/components/ui/switch.tsx +29 -0
  114. package/components/ui/table.tsx +116 -0
  115. package/components/ui/tabs.tsx +66 -0
  116. package/components/ui/textarea.tsx +18 -0
  117. package/components/ui/toast.tsx +129 -0
  118. package/components/ui/toaster.tsx +35 -0
  119. package/components/ui/toggle-group.tsx +73 -0
  120. package/components/ui/toggle.tsx +47 -0
  121. package/components/ui/tooltip.tsx +61 -0
  122. package/components/ui/use-mobile.tsx +19 -0
  123. package/components/ui/use-toast.ts +191 -0
  124. package/components.json +21 -0
  125. package/hooks/use-mobile.ts +19 -0
  126. package/hooks/use-toast.ts +191 -0
  127. package/lib/api.ts +66 -0
  128. package/lib/utils.ts +6 -0
  129. package/next-env.d.ts +6 -0
  130. package/next.config.mjs +11 -0
  131. package/package.json +73 -0
  132. package/pnpm-workspace.yaml +5 -0
  133. package/postcss.config.mjs +8 -0
  134. package/public/apple-icon.png +0 -0
  135. package/public/fav.jpeg +0 -0
  136. package/public/fav.png +0 -0
  137. package/public/icon-dark-32x32.png +0 -0
  138. package/public/icon-light-32x32.png +0 -0
  139. package/public/icon.png +0 -0
  140. package/public/icon.svg +26 -0
  141. package/public/logo.jpeg +0 -0
  142. package/public/logo.png +0 -0
  143. package/public/logo2.jpeg +0 -0
  144. package/public/logo2.png +0 -0
  145. package/public/placeholder-logo.png +0 -0
  146. package/public/placeholder-logo.svg +1 -0
  147. package/public/placeholder-user.jpg +0 -0
  148. package/public/placeholder.jpg +0 -0
  149. package/public/placeholder.svg +1 -0
  150. package/styles/globals.css +209 -0
  151. package/tailwind.config.ts +15 -0
  152. package/tsconfig.json +41 -0
@@ -0,0 +1,113 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import Link from 'next/link'
5
+ import { usePathname } from 'next/navigation'
6
+ import { LayoutDashboard, Users, BarChart3, Key, CreditCard, Settings, Menu, X, Zap } from 'lucide-react'
7
+
8
+ const navItems = [
9
+ { label: 'Dashboard', icon: LayoutDashboard, href: '/dashboard' },
10
+ { label: 'Users & Wallets', icon: Users, href: '/dashboard/users' },
11
+ { label: 'Analytics', icon: BarChart3, href: '/dashboard/analytics' },
12
+ { label: 'API Keys', icon: Key, href: '/dashboard/api-keys' },
13
+ { label: 'Billing', icon: CreditCard, href: '/dashboard/billing' },
14
+ { label: 'Integration', icon: Zap, href: '/dashboard/integration' },
15
+ { label: 'Settings', icon: Settings, href: '/dashboard/settings' },
16
+ ]
17
+
18
+ export function DashboardSidebar() {
19
+ // Hidden by default on mobile, visible on desktop
20
+ const [mobileOpen, setMobileOpen] = useState(false)
21
+ const pathname = usePathname()
22
+
23
+ return (
24
+ <>
25
+ {/* Mobile toggle button — only visible on mobile */}
26
+ <button
27
+ onClick={() => setMobileOpen(true)}
28
+ className="md:hidden fixed top-4 left-4 z-40 p-2 rounded-lg bg-card border border-border hover:bg-card/80 transition"
29
+ aria-label="Open menu"
30
+ >
31
+ <Menu size={22} />
32
+ </button>
33
+
34
+ {/* Mobile overlay — closes sidebar on tap outside */}
35
+ {mobileOpen && (
36
+ <div
37
+ className="fixed inset-0 bg-black/50 z-40 md:hidden"
38
+ onClick={() => setMobileOpen(false)}
39
+ aria-hidden="true"
40
+ />
41
+ )}
42
+
43
+ {/* Sidebar */}
44
+ {/*
45
+ Desktop: static in flow (not fixed), always visible → `md:relative md:translate-x-0 md:flex`
46
+ Mobile: fixed, slides in/out, hidden by default
47
+ */}
48
+ <aside
49
+ className={`
50
+ fixed inset-y-0 left-0 z-50 w-64 flex flex-col
51
+ bg-black/40 backdrop-blur-xl border-r border-white/10
52
+ transition-transform duration-300
53
+ ${mobileOpen ? 'translate-x-0' : '-translate-x-full'}
54
+ md:relative md:inset-auto md:z-auto md:translate-x-0 md:flex md:shrink-0
55
+ `}
56
+ >
57
+ {/* Logo */}
58
+ <div className="p-6 border-b border-border flex items-center justify-between">
59
+ <div className="flex items-center gap-2">
60
+ <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-emerald-500 to-emerald-600 flex items-center justify-center shrink-0">
61
+ <span className="text-white font-bold">L</span>
62
+ </div>
63
+ <div>
64
+ <div className="font-bold">Lumina</div>
65
+ <div className="text-xs text-foreground/50">Dashboard</div>
66
+ </div>
67
+ </div>
68
+ {/* Close button inside sidebar — mobile only */}
69
+ <button
70
+ onClick={() => setMobileOpen(false)}
71
+ className="md:hidden p-1 rounded-lg hover:bg-white/10 transition"
72
+ aria-label="Close menu"
73
+ >
74
+ <X size={20} />
75
+ </button>
76
+ </div>
77
+
78
+ {/* Nav */}
79
+ <nav className="flex-1 p-4 space-y-1 overflow-y-auto">
80
+ {navItems.map((item) => {
81
+ const isActive = pathname === item.href
82
+ return (
83
+ <Link
84
+ key={item.href}
85
+ href={item.href}
86
+ onClick={() => setMobileOpen(false)}
87
+ className={`flex items-center gap-3 px-4 py-3 rounded-lg transition-colors ${
88
+ isActive
89
+ ? 'bg-emerald-600/20 text-emerald-500 border border-emerald-600/30'
90
+ : 'text-foreground/70 hover:bg-white/5 hover:text-foreground'
91
+ }`}
92
+ >
93
+ <item.icon size={20} />
94
+ <span className="font-medium">{item.label}</span>
95
+ </Link>
96
+ )
97
+ })}
98
+ </nav>
99
+
100
+ {/* Footer */}
101
+ <div className="p-4 border-t border-border">
102
+ <div className="glassmorphism-dark p-4 rounded-lg">
103
+ <div className="text-sm font-semibold mb-1">Account Status</div>
104
+ <div className="text-xs text-foreground/60 mb-3">Professional Plan</div>
105
+ <button className="w-full px-3 py-2 text-sm rounded-lg bg-emerald-600 hover:bg-emerald-700 transition">
106
+ Upgrade
107
+ </button>
108
+ </div>
109
+ </div>
110
+ </aside>
111
+ </>
112
+ )
113
+ }
@@ -0,0 +1,51 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { usePathname } from 'next/navigation'
5
+ import { ChevronRight } from 'lucide-react'
6
+
7
+ export function Breadcrumb() {
8
+ const pathname = usePathname()
9
+
10
+ // Skip if not in docs
11
+ if (!pathname.startsWith('/docs')) {
12
+ return null
13
+ }
14
+
15
+ const segments = pathname.split('/').filter(Boolean)
16
+
17
+ // Build breadcrumb items
18
+ const items = segments.map((segment, index) => {
19
+ const href = '/' + segments.slice(0, index + 1).join('/')
20
+ const label = segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
21
+
22
+ return { href, label }
23
+ })
24
+
25
+ return (
26
+ <div className="flex items-center gap-2 text-sm mb-6">
27
+ <Link
28
+ href="/docs"
29
+ className="text-foreground/60 hover:text-emerald-400 transition-colors"
30
+ >
31
+ Docs
32
+ </Link>
33
+
34
+ {items.slice(1).map((item, index) => (
35
+ <div key={item.href} className="flex items-center gap-2">
36
+ <ChevronRight size={16} className="text-foreground/30" />
37
+ <Link
38
+ href={item.href}
39
+ className={`transition-colors ${
40
+ index === items.length - 2
41
+ ? 'text-emerald-400 font-medium'
42
+ : 'text-foreground/60 hover:text-emerald-400'
43
+ }`}
44
+ >
45
+ {item.label}
46
+ </Link>
47
+ </div>
48
+ ))}
49
+ </div>
50
+ )
51
+ }
@@ -0,0 +1,53 @@
1
+ import { AlertCircle, Info, CheckCircle, AlertTriangle } from 'lucide-react'
2
+
3
+ interface CalloutProps {
4
+ type: 'info' | 'warning' | 'error' | 'success'
5
+ title?: string
6
+ children: React.ReactNode
7
+ }
8
+
9
+ const calloutConfig = {
10
+ info: {
11
+ icon: Info,
12
+ bgColor: 'bg-blue-900/20',
13
+ borderColor: 'border-blue-600/30',
14
+ textColor: 'text-blue-400',
15
+ },
16
+ warning: {
17
+ icon: AlertTriangle,
18
+ bgColor: 'bg-amber-900/20',
19
+ borderColor: 'border-amber-600/30',
20
+ textColor: 'text-amber-400',
21
+ },
22
+ error: {
23
+ icon: AlertCircle,
24
+ bgColor: 'bg-red-900/20',
25
+ borderColor: 'border-red-600/30',
26
+ textColor: 'text-red-400',
27
+ },
28
+ success: {
29
+ icon: CheckCircle,
30
+ bgColor: 'bg-emerald-900/20',
31
+ borderColor: 'border-emerald-600/30',
32
+ textColor: 'text-emerald-400',
33
+ },
34
+ }
35
+
36
+ export function Callout({ type, title, children }: CalloutProps) {
37
+ const config = calloutConfig[type]
38
+ const Icon = config.icon
39
+
40
+ return (
41
+ <div
42
+ className={`my-4 p-4 rounded-lg border ${config.bgColor} ${config.borderColor} flex gap-3`}
43
+ >
44
+ <Icon size={20} className={`flex-shrink-0 mt-0.5 ${config.textColor}`} />
45
+ <div className="flex-1">
46
+ {title && (
47
+ <div className={`font-semibold ${config.textColor} mb-1`}>{title}</div>
48
+ )}
49
+ <div className="text-sm text-foreground/80">{children}</div>
50
+ </div>
51
+ </div>
52
+ )
53
+ }
@@ -0,0 +1,77 @@
1
+ 'use client'
2
+
3
+ import { Copy, Check } from 'lucide-react'
4
+ import { useState } from 'react'
5
+
6
+ interface CodeBlockProps {
7
+ code: string
8
+ language: string
9
+ title?: string
10
+ showLineNumbers?: boolean
11
+ }
12
+
13
+ export function CodeBlock({
14
+ code,
15
+ language,
16
+ title,
17
+ showLineNumbers = false,
18
+ }: CodeBlockProps) {
19
+ const [copied, setCopied] = useState(false)
20
+
21
+ const handleCopy = async () => {
22
+ await navigator.clipboard.writeText(code)
23
+ setCopied(true)
24
+ setTimeout(() => setCopied(false), 2000)
25
+ }
26
+
27
+ const lines = code.split('\n')
28
+
29
+ return (
30
+ <div className="my-4 rounded-lg overflow-hidden bg-black/40 border border-white/10">
31
+ {title && (
32
+ <div className="px-4 py-2 bg-white/5 border-b border-white/10 flex items-center justify-between">
33
+ <span className="text-sm font-mono text-foreground/70">{title}</span>
34
+ <span className="text-xs text-foreground/50 bg-white/10 px-2 py-1 rounded">
35
+ {language}
36
+ </span>
37
+ </div>
38
+ )}
39
+
40
+ <div className="overflow-x-auto">
41
+ <pre className="p-4 text-sm leading-relaxed">
42
+ <code className={`language-${language}`}>
43
+ {lines.map((line, index) => (
44
+ <div key={index} className="flex gap-4">
45
+ {showLineNumbers && (
46
+ <span className="text-foreground/30 w-8 text-right select-none">
47
+ {index + 1}
48
+ </span>
49
+ )}
50
+ <span>{line}</span>
51
+ </div>
52
+ ))}
53
+ </code>
54
+ </pre>
55
+ </div>
56
+
57
+ <div className="px-4 py-2 bg-white/5 border-t border-white/10 flex justify-end">
58
+ <button
59
+ onClick={handleCopy}
60
+ className="flex items-center gap-2 px-3 py-1 rounded text-xs text-foreground/70 hover:text-emerald-400 hover:bg-emerald-600/10 transition-colors"
61
+ >
62
+ {copied ? (
63
+ <>
64
+ <Check size={14} />
65
+ Copied
66
+ </>
67
+ ) : (
68
+ <>
69
+ <Copy size={14} />
70
+ Copy
71
+ </>
72
+ )}
73
+ </button>
74
+ </div>
75
+ </div>
76
+ )
77
+ }
@@ -0,0 +1,214 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { usePathname } from 'next/navigation'
5
+ import { ChevronDown, Search, X } from 'lucide-react'
6
+ import { useState } from 'react'
7
+
8
+ interface NavItem {
9
+ title: string
10
+ href: string
11
+ icon?: string
12
+ children?: NavItem[]
13
+ }
14
+
15
+ const navItems: NavItem[] = [
16
+ {
17
+ title: 'Getting Started',
18
+ href: '/docs',
19
+ icon: '🚀',
20
+ children: [
21
+ { title: 'Introduction', href: '/docs/getting-started/introduction' },
22
+ { title: 'Installation', href: '/docs/getting-started/installation' },
23
+ { title: 'Quick Start', href: '/docs/getting-started/quick-start' },
24
+ { title: 'Environment Setup', href: '/docs/getting-started/environment-setup' },
25
+ ],
26
+ },
27
+ {
28
+ title: 'SDK Reference',
29
+ href: '/docs/sdk',
30
+ icon: '⚙️',
31
+ children: [
32
+ { title: 'LuminaProvider', href: '/docs/sdk/lumina-provider' },
33
+ { title: 'WalletLogin', href: '/docs/sdk/wallet-login' },
34
+ { title: 'WalletDisplay', href: '/docs/sdk/wallet-display' },
35
+ { title: 'TransactionConfirm', href: '/docs/sdk/transaction-confirm' },
36
+ { title: 'Hooks', href: '/docs/sdk/hooks' },
37
+ { title: 'Types', href: '/docs/sdk/types' },
38
+ ],
39
+ },
40
+ {
41
+ title: 'API Reference',
42
+ href: '/docs/api',
43
+ icon: '📡',
44
+ children: [
45
+ { title: 'Authentication', href: '/docs/api/authentication' },
46
+ { title: 'Wallets', href: '/docs/api/wallets' },
47
+ { title: 'Transactions', href: '/docs/api/transactions' },
48
+ { title: 'Networks', href: '/docs/api/networks' },
49
+ { title: 'Rate Limits', href: '/docs/api/rate-limits' },
50
+ { title: 'Error Handling', href: '/docs/api/error-handling' },
51
+ ],
52
+ },
53
+ {
54
+ title: 'Guides',
55
+ href: '/docs/guides',
56
+ icon: '📚',
57
+ children: [
58
+ { title: 'Integration Guide', href: '/docs/guides/integration' },
59
+ { title: 'React Integration', href: '/docs/guides/react' },
60
+ { title: 'Next.js Setup', href: '/docs/guides/nextjs' },
61
+ { title: 'Vue.js Setup', href: '/docs/guides/vue' },
62
+ { title: 'Security Best Practices', href: '/docs/guides/security' },
63
+ { title: 'Testing', href: '/docs/guides/testing' },
64
+ ],
65
+ },
66
+ {
67
+ title: 'Examples',
68
+ href: '/docs/examples',
69
+ icon: '💡',
70
+ children: [
71
+ { title: 'Basic Setup', href: '/docs/examples/basic-setup' },
72
+ { title: 'User Authentication', href: '/docs/examples/authentication' },
73
+ { title: 'Send Transaction', href: '/docs/examples/send-transaction' },
74
+ { title: 'Query Balance', href: '/docs/examples/query-balance' },
75
+ { title: 'Multi-Chain Support', href: '/docs/examples/multi-chain' },
76
+ ],
77
+ },
78
+ {
79
+ title: 'Troubleshooting',
80
+ href: '/docs/troubleshooting',
81
+ icon: '🔧',
82
+ children: [
83
+ { title: 'Common Issues', href: '/docs/troubleshooting/common-issues' },
84
+ { title: 'FAQ', href: '/docs/troubleshooting/faq' },
85
+ { title: 'Support', href: '/docs/troubleshooting/support' },
86
+ ],
87
+ },
88
+ ]
89
+
90
+ interface NavItemProps {
91
+ item: NavItem
92
+ level: number
93
+ onNavClick?: () => void
94
+ }
95
+
96
+ function NavItemComponent({ item, level, onNavClick }: NavItemProps) {
97
+ const pathname = usePathname()
98
+ const [isOpen, setIsOpen] = useState(true)
99
+
100
+ const isActive = pathname === item.href || pathname.startsWith(item.href + '/')
101
+ const hasChildren = item.children && item.children.length > 0
102
+
103
+ return (
104
+ <div>
105
+ <Link
106
+ href={item.href}
107
+ className={`flex items-center gap-2 px-3 py-2 rounded-lg transition-colors ${
108
+ isActive
109
+ ? 'bg-emerald-600/20 text-emerald-400 border border-emerald-600/30'
110
+ : 'text-foreground/70 hover:text-foreground hover:bg-white/5'
111
+ }`}
112
+ onClick={() => {
113
+ if (hasChildren) setIsOpen(!isOpen)
114
+ if (!hasChildren && onNavClick) onNavClick()
115
+ }}
116
+ >
117
+ <span className="text-sm">{item.icon}</span>
118
+ <span className="flex-1 text-sm font-medium">{item.title}</span>
119
+ {hasChildren && (
120
+ <ChevronDown
121
+ size={16}
122
+ className={`transition-transform ${isOpen ? 'rotate-180' : ''}`}
123
+ />
124
+ )}
125
+ </Link>
126
+
127
+ {hasChildren && isOpen && (
128
+ <div className="mt-1 ml-2 border-l border-white/10 pl-2 space-y-1">
129
+ {item.children!.map((child) => (
130
+ <Link
131
+ key={child.href}
132
+ href={child.href}
133
+ onClick={onNavClick}
134
+ className={`block px-3 py-2 rounded-lg text-sm transition-colors ${
135
+ pathname === child.href
136
+ ? 'bg-emerald-600/20 text-emerald-400'
137
+ : 'text-foreground/60 hover:text-foreground hover:bg-white/5'
138
+ }`}
139
+ >
140
+ {child.title}
141
+ </Link>
142
+ ))}
143
+ </div>
144
+ )}
145
+ </div>
146
+ )
147
+ }
148
+
149
+ // Reusable sidebar content
150
+ function SidebarContent({ onNavClick }: { onNavClick?: () => void }) {
151
+ return (
152
+ <>
153
+ <div className="sticky top-0 bg-card/80 backdrop-blur-sm p-4 border-b border-white/10">
154
+ <div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-white/5 border border-white/10">
155
+ <Search size={16} className="text-foreground/50" />
156
+ <input
157
+ type="text"
158
+ placeholder="Search..."
159
+ className="bg-transparent text-sm outline-none text-foreground placeholder:text-foreground/50 flex-1"
160
+ />
161
+ </div>
162
+ </div>
163
+ <nav className="p-3 space-y-2">
164
+ {navItems.map((item) => (
165
+ <NavItemComponent key={item.href} item={item} level={0} onNavClick={onNavClick} />
166
+ ))}
167
+ </nav>
168
+ </>
169
+ )
170
+ }
171
+
172
+ interface DocsSidebarProps {
173
+ isOpen: boolean
174
+ onClose: () => void
175
+ }
176
+
177
+ export function DocsSidebar({ isOpen, onClose }: DocsSidebarProps) {
178
+ return (
179
+ <>
180
+ {/* Desktop Sidebar */}
181
+ <aside className="hidden md:flex w-64 flex-col border-r border-white/10 bg-card overflow-y-auto shrink-0">
182
+ <SidebarContent />
183
+ </aside>
184
+
185
+ {/* Mobile Overlay */}
186
+ {isOpen && (
187
+ <div
188
+ className="fixed inset-0 z-40 bg-black/60 backdrop-blur-sm md:hidden"
189
+ onClick={onClose}
190
+ />
191
+ )}
192
+
193
+ {/* Mobile Drawer */}
194
+ <aside
195
+ className={`fixed top-0 left-0 z-50 h-full w-72 bg-card border-r border-white/10 overflow-y-auto transform transition-transform duration-300 ease-in-out md:hidden ${
196
+ isOpen ? 'translate-x-0' : '-translate-x-full'
197
+ }`}
198
+ >
199
+ {/* Close Button */}
200
+ <div className="flex items-center justify-between p-4 border-b border-white/10">
201
+ <span className="font-semibold text-sm">Documentation</span>
202
+ <button
203
+ onClick={onClose}
204
+ className="p-1.5 rounded-md hover:bg-white/10 transition text-foreground/70 hover:text-foreground"
205
+ aria-label="Close menu"
206
+ >
207
+ <X size={18} />
208
+ </button>
209
+ </div>
210
+ <SidebarContent onNavClick={onClose} />
211
+ </aside>
212
+ </>
213
+ )
214
+ }
@@ -0,0 +1,83 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+
5
+ interface Heading {
6
+ id: string
7
+ text: string
8
+ level: number
9
+ }
10
+
11
+ export function TableOfContents() {
12
+ const [headings, setHeadings] = useState<Heading[]>([])
13
+ const [activeId, setActiveId] = useState<string>('')
14
+
15
+ useEffect(() => {
16
+ // Get all headings from the main content area
17
+ const article = document.querySelector('article')
18
+ if (!article) return
19
+
20
+ const headingElements = Array.from(article.querySelectorAll('h2, h3, h4'))
21
+ const extractedHeadings: Heading[] = headingElements.map((element) => {
22
+ let id = element.id
23
+ if (!id) {
24
+ id = element.textContent?.toLowerCase().replace(/\s+/g, '-') || ''
25
+ element.id = id
26
+ }
27
+ return {
28
+ id,
29
+ text: element.textContent || '',
30
+ level: parseInt(element.tagName[1]),
31
+ }
32
+ })
33
+
34
+ setHeadings(extractedHeadings)
35
+ }, [])
36
+
37
+ useEffect(() => {
38
+ // Intersection observer for active heading
39
+ const observer = new IntersectionObserver(
40
+ (entries) => {
41
+ const activeEntry = entries.find((entry) => entry.isIntersecting)
42
+ if (activeEntry) {
43
+ setActiveId(activeEntry.target.id)
44
+ }
45
+ },
46
+ { rootMargin: '-10% 0% -66%' }
47
+ )
48
+
49
+ document.querySelectorAll('h2, h3, h4').forEach((el) => {
50
+ observer.observe(el)
51
+ })
52
+
53
+ return () => observer.disconnect()
54
+ }, [])
55
+
56
+ if (headings.length === 0) return null
57
+
58
+ return (
59
+ <aside className="w-64 hidden lg:block">
60
+ <div className="sticky top-4 space-y-3">
61
+ <div className="text-xs font-semibold text-foreground/50 uppercase tracking-wider px-2">
62
+ On this page
63
+ </div>
64
+ <nav className="space-y-1 text-sm">
65
+ {headings.map((heading) => (
66
+ <a
67
+ key={heading.id}
68
+ href={`#${heading.id}`}
69
+ className={`block transition-colors ${
70
+ activeId === heading.id
71
+ ? 'text-emerald-400 font-medium border-l-2 border-emerald-400 pl-2'
72
+ : 'text-foreground/60 hover:text-foreground border-l-2 border-transparent pl-2'
73
+ }`}
74
+ style={{ paddingLeft: `${(heading.level - 2) * 12 + 8}px` }}
75
+ >
76
+ {heading.text}
77
+ </a>
78
+ ))}
79
+ </nav>
80
+ </div>
81
+ </aside>
82
+ )
83
+ }
@@ -0,0 +1,46 @@
1
+ 'use client'
2
+
3
+ import React, { createContext, useContext, ReactNode } from 'react'
4
+ import { apiClient } from '@/lib/api' // Adjust path as needed
5
+
6
+ interface LuminaConfig {
7
+ apiKey: string
8
+ environment?: 'development' | 'production'
9
+ theme?: 'light' | 'dark'
10
+ }
11
+
12
+ interface LuminaContextType extends LuminaConfig {
13
+ // Expose a pre-configured fetcher for SDK components
14
+ fetchApi: (endpoint: string, options?: RequestInit) => Promise<any>
15
+ }
16
+
17
+ const LuminaContext = createContext<LuminaContextType | undefined>(undefined)
18
+
19
+ export function LuminaProvider({
20
+ children,
21
+ apiKey,
22
+ environment = 'production',
23
+ theme = 'dark',
24
+ }: {
25
+ children: ReactNode
26
+ } & LuminaConfig) {
27
+
28
+ // Wrap the SDK client so components just call fetchApi('/v1/auth/login', {...})
29
+ const fetchApi = async (endpoint: string, options?: RequestInit) => {
30
+ return apiClient.sdk(endpoint, apiKey, options);
31
+ };
32
+
33
+ return (
34
+ <LuminaContext.Provider value={{ apiKey, environment, theme, fetchApi }}>
35
+ {children}
36
+ </LuminaContext.Provider>
37
+ )
38
+ }
39
+
40
+ export function useLumina() {
41
+ const context = useContext(LuminaContext)
42
+ if (!context) {
43
+ throw new Error('useLumina must be used within a LuminaProvider')
44
+ }
45
+ return context
46
+ }