@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.
- package/BUTTON_FIXES.md +59 -0
- package/DOCS_INDEX.md +332 -0
- package/DOCUMENTATION.md +252 -0
- package/DOCUMENTATION_BUILD_SUMMARY.md +376 -0
- package/DOCUMENTATION_COMPLETE.md +311 -0
- package/FEATURES.md +333 -0
- package/Lumina-sdk/src/components/lumina-provider.tsx +46 -0
- package/Lumina-sdk/src/components/transaction-confirm.tsx +242 -0
- package/Lumina-sdk/src/components/wallet-display.tsx +157 -0
- package/Lumina-sdk/src/components/wallet-login.tsx +163 -0
- package/Lumina-sdk/src/hooks/use-mobile.ts +19 -0
- package/Lumina-sdk/src/hooks/use-toast.ts +191 -0
- package/Lumina-sdk/src/index.ts +0 -0
- package/Lumina-sdk/src/lib/api.ts +66 -0
- package/Lumina-sdk/src/lib/utils.ts +6 -0
- package/Lumina-sdk/src/package.json +42 -0
- package/Lumina-sdk/src/tsconfig.json +19 -0
- package/NEW_FILES_MANIFEST.txt +146 -0
- package/README.md +298 -0
- package/app/dashboard/analytics/page.tsx +218 -0
- package/app/dashboard/api-keys/page.tsx +260 -0
- package/app/dashboard/billing/page.tsx +412 -0
- package/app/dashboard/integration/page.tsx +185 -0
- package/app/dashboard/layout.tsx +18 -0
- package/app/dashboard/page.tsx +244 -0
- package/app/dashboard/settings/page.tsx +285 -0
- package/app/dashboard/users/page.tsx +148 -0
- package/app/docs/api/authentication/page.tsx +246 -0
- package/app/docs/api/endpoints/page.tsx +397 -0
- package/app/docs/api/errors/page.tsx +305 -0
- package/app/docs/api/overview/page.tsx +306 -0
- package/app/docs/examples/basic-setup/page.tsx +256 -0
- package/app/docs/examples/multi-chain/page.tsx +331 -0
- package/app/docs/examples/nextjs-full-stack/page.tsx +332 -0
- package/app/docs/getting-started/environment-setup/page.tsx +243 -0
- package/app/docs/getting-started/installation/page.tsx +187 -0
- package/app/docs/getting-started/introduction/page.tsx +178 -0
- package/app/docs/getting-started/quick-start/page.tsx +199 -0
- package/app/docs/guides/nextjs/page.tsx +358 -0
- package/app/docs/guides/react/page.tsx +230 -0
- package/app/docs/guides/security/page.tsx +284 -0
- package/app/docs/layout.tsx +32 -0
- package/app/docs/page.tsx +180 -0
- package/app/docs/sdk/lumina-provider/page.tsx +186 -0
- package/app/docs/sdk/transaction-confirm/page.tsx +331 -0
- package/app/docs/sdk/wallet-display/page.tsx +224 -0
- package/app/docs/sdk/wallet-login/page.tsx +207 -0
- package/app/docs/troubleshooting/common-issues/page.tsx +301 -0
- package/app/docs/troubleshooting/faq/page.tsx +105 -0
- package/app/globals.css +125 -0
- package/app/invite/[token]/page.tsx +78 -0
- package/app/layout.tsx +36 -0
- package/app/login/page.tsx +175 -0
- package/app/page.tsx +336 -0
- package/app/sdk-demo/page.tsx +239 -0
- package/components/dashboard-sidebar.tsx +113 -0
- package/components/docs/breadcrumb.tsx +51 -0
- package/components/docs/callout.tsx +53 -0
- package/components/docs/code-block.tsx +77 -0
- package/components/docs/docs-sidebar.tsx +214 -0
- package/components/docs/table-of-contents.tsx +83 -0
- package/components/sdk/lumina-provider.tsx +46 -0
- package/components/sdk/transaction-confirm.tsx +242 -0
- package/components/sdk/wallet-display.tsx +157 -0
- package/components/sdk/wallet-login.tsx +163 -0
- package/components/theme-provider.tsx +11 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/aspect-ratio.tsx +11 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/breadcrumb.tsx +109 -0
- package/components/ui/button-group.tsx +83 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/calendar.tsx +213 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +351 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +184 -0
- package/components/ui/context-menu.tsx +252 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/drawer.tsx +135 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/empty.tsx +104 -0
- package/components/ui/field.tsx +244 -0
- package/components/ui/form.tsx +167 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-group.tsx +169 -0
- package/components/ui/input-otp.tsx +77 -0
- package/components/ui/input.tsx +21 -0
- package/components/ui/item.tsx +193 -0
- package/components/ui/kbd.tsx +28 -0
- package/components/ui/label.tsx +24 -0
- package/components/ui/menubar.tsx +276 -0
- package/components/ui/navigation-menu.tsx +166 -0
- package/components/ui/pagination.tsx +127 -0
- package/components/ui/popover.tsx +48 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/radio-group.tsx +45 -0
- package/components/ui/resizable.tsx +56 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/select.tsx +185 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/sheet.tsx +139 -0
- package/components/ui/sidebar.tsx +726 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/slider.tsx +59 -0
- package/components/ui/sonner.tsx +25 -0
- package/components/ui/spinner.tsx +16 -0
- package/components/ui/switch.tsx +29 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/toast.tsx +129 -0
- package/components/ui/toaster.tsx +35 -0
- package/components/ui/toggle-group.tsx +73 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/components/ui/use-mobile.tsx +19 -0
- package/components/ui/use-toast.ts +191 -0
- package/components.json +21 -0
- package/hooks/use-mobile.ts +19 -0
- package/hooks/use-toast.ts +191 -0
- package/lib/api.ts +66 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +11 -0
- package/package.json +73 -0
- package/pnpm-workspace.yaml +5 -0
- package/postcss.config.mjs +8 -0
- package/public/apple-icon.png +0 -0
- package/public/fav.jpeg +0 -0
- package/public/fav.png +0 -0
- package/public/icon-dark-32x32.png +0 -0
- package/public/icon-light-32x32.png +0 -0
- package/public/icon.png +0 -0
- package/public/icon.svg +26 -0
- package/public/logo.jpeg +0 -0
- package/public/logo.png +0 -0
- package/public/logo2.jpeg +0 -0
- package/public/logo2.png +0 -0
- package/public/placeholder-logo.png +0 -0
- package/public/placeholder-logo.svg +1 -0
- package/public/placeholder-user.jpg +0 -0
- package/public/placeholder.jpg +0 -0
- package/public/placeholder.svg +1 -0
- package/styles/globals.css +209 -0
- package/tailwind.config.ts +15 -0
- 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
|
+
}
|