Merge remote-tracking branch 'origin/main'

# Conflicts:
#	console/package.json
#	console/pnpm-lock.yaml
#	console/pnpm-workspace.yaml

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-08 03:05:54 -07:00
co-authored by Hanzo Dev
112 changed files with 2166 additions and 671 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ import {
Shield,
Users,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -12,7 +12,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { useAuth, ProtectedRoute, OrgBadge } from "@/lib/auth"
import { Activity, Server, Globe, Users, Database, Zap, Shield, Network, MapPin, Plus, Settings, Trash2, Eye, BarChart3 } from "lucide-react"
import { Activity, Server, Globe, Users, Database, Zap, Shield, Network, MapPin, Plus, Settings, Trash2, Eye, BarChart3 } from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
// Donut Chart Component (Modern replacement for D3 donut)
+1 -1
View File
@@ -16,7 +16,7 @@ import {
Terminal,
Webhook,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
export const metadata: Metadata = {
+1 -1
View File
@@ -11,7 +11,7 @@ import {
BookOpen,
Calendar,
Clock,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
export default function BlogPage() {
+1 -1
View File
@@ -13,7 +13,7 @@ import {
MapPin,
Rocket,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -16,7 +16,7 @@ import {
Globe,
Network,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
const CHAIN_EXPLORERS: Record<string, string> = {
ethereum: "https://etherscan.io",
+1 -1
View File
@@ -19,7 +19,7 @@ import {
Globe,
Search,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
const CATEGORY_ORDER: ChainCategory[] = [
"ethereum-l1",
+1 -1
View File
@@ -14,7 +14,7 @@ import {
Mail,
MessageSquare,
Send,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -19,7 +19,7 @@ import {
RefreshCcw,
TrendingUp,
Globe
} from "lucide-react"
} from "@luxfi/ui/icons"
interface AnalyticsData {
totalRequests: number
+1 -1
View File
@@ -13,7 +13,7 @@ import {
Check,
X,
Loader2,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useApiKeys, useCreateApiKey, useDeleteApiKey } from "@/lib/hooks"
function getProjectId(): string {
+1 -1
View File
@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Image, Search, ExternalLink, Loader2 } from "lucide-react"
import { Image, Search, ExternalLink, Loader2 } from "@luxfi/ui/icons"
interface NFT {
contract: string
+1 -1
View File
@@ -3,7 +3,7 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Coins, Image, Wallet } from "lucide-react"
import { Coins, Image, Wallet } from "@luxfi/ui/icons"
import Link from "next/link"
export default function AssetsPage() {
+1 -1
View File
@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Badge } from "@/components/ui/badge"
import { Coins, Search, Loader2 } from "lucide-react"
import { Coins, Search, Loader2 } from "@luxfi/ui/icons"
interface TokenMetadata {
contract_address: string
+1 -1
View File
@@ -14,7 +14,7 @@ import {
DollarSign,
Zap,
Settings,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useGasPolicies, useCreateGasPolicy, useDeleteGasPolicy, useGasPrices } from "@/lib/hooks"
export default function GasPoliciesPage() {
@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Box, Plus, Loader2, X, Settings, Download, Scale } from "lucide-react"
import { Box, Plus, Loader2, X, Settings, Download, Scale } from "@luxfi/ui/icons"
interface Cluster {
id: string
@@ -7,7 +7,7 @@ import { DOKS_REGIONS, type LuxFleet } from "@/components/lux/region-data"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Globe, Plus, RefreshCw, Zap } from "lucide-react"
import { Globe, Plus, RefreshCw, Zap } from "@luxfi/ui/icons"
/** Convert LuxFleet[] to FleetRegion[] for the map component */
function fleetsToRegions(fleets: LuxFleet[]): FleetRegion[] {
@@ -39,7 +39,7 @@ import {
ChevronRight,
Circle,
AlertTriangle,
} from "lucide-react"
} from "@luxfi/ui/icons"
// ---------------------------------------------------------------------------
// Types
@@ -4,7 +4,7 @@ import { getApiUrl } from "@/lib/api"
import { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Activity, Cpu, HardDrive, MemoryStick, Wifi, WifiOff, Loader2 } from "lucide-react"
import { Activity, Cpu, HardDrive, MemoryStick, Wifi, WifiOff, Loader2 } from "@luxfi/ui/icons"
interface NodeHealth {
id: string
@@ -12,7 +12,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Globe, Loader2, RefreshCcw, ExternalLink, Activity, Zap, CheckCircle, AlertTriangle, XCircle, AlertCircle } from "lucide-react"
import { Globe, Loader2, RefreshCcw, ExternalLink, Activity, Zap, CheckCircle, AlertTriangle, XCircle, AlertCircle } from "@luxfi/ui/icons"
import Link from "next/link"
import { getApiUrl, getAuthHeaders, load, loadError } from "@/lib/api"
@@ -10,7 +10,7 @@ import { Input } from "@/components/ui/input"
import {
Server, ArrowLeft, Play, Square, Trash2, RefreshCcw, Copy, Check,
Activity, HardDrive, Cpu, MemoryStick, Network, Loader2, Terminal
} from "lucide-react"
} from "@luxfi/ui/icons"
interface NodeDetails {
id: string
@@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Select } from "@/components/ui/select"
import { Server, Plus, ExternalLink, Loader2, X, Play, Square, Trash2, RefreshCcw, Activity, AlertTriangle, ChevronRight } from "lucide-react"
import { Server, Plus, ExternalLink, Loader2, X, Play, Square, Trash2, RefreshCcw, Activity, AlertTriangle, ChevronRight, ArrowDown, ArrowUp } from "@luxfi/ui/icons"
import Link from "next/link"
import { getBrand } from "@/lib/brand"
@@ -940,8 +940,8 @@ export default function NodesPage() {
{node.metrics?.network_rx !== undefined && (
<div className="text-right hidden xl:block">
<p className="text-sm font-medium">
<span className="text-green-500">↓</span>{node.metrics.network_rx.toFixed(0)}
<span className="text-blue-500 ml-1">↑</span>{node.metrics.network_tx?.toFixed(0) || 0} MB
<ArrowDown className="inline size-3.5 text-green-500" aria-label="in" />{node.metrics.network_rx.toFixed(0)}
<ArrowUp className="ml-1 inline size-3.5 text-blue-500" aria-label="out" />{node.metrics.network_tx?.toFixed(0) || 0} MB
</p>
<p className="text-xs text-muted-foreground">Network</p>
</div>
@@ -16,7 +16,7 @@ import {
ArrowUpRight,
ArrowDownRight,
Minus,
} from "lucide-react"
} from "@luxfi/ui/icons"
interface HealthOverview {
total_services: number
@@ -5,7 +5,7 @@ import { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Server, Box, HardDrive, Copy, Loader2, Shield } from "lucide-react"
import { Server, Box, HardDrive, Copy, Loader2, Shield } from "@luxfi/ui/icons"
import Link from "next/link"
interface InfraStats {
@@ -24,7 +24,7 @@ import {
CheckCircle2,
XCircle,
Minus,
} from "lucide-react"
} from "@luxfi/ui/icons"
interface ServiceInfo {
name: string
@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Copy, Loader2, X, HardDrive, Camera, CheckCircle, Clock, AlertCircle } from "lucide-react"
import { Copy, Loader2, X, HardDrive, Camera, CheckCircle, Clock, AlertCircle } from "@luxfi/ui/icons"
interface Volume {
id: string
@@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { HardDrive, Plus, Loader2, X, Camera, Link2, Unlink, ArrowUpRight } from "lucide-react"
import { HardDrive, Plus, Loader2, X, Camera, Link2, Unlink, ArrowUpRight } from "@luxfi/ui/icons"
interface Volume {
id: string
+1 -1
View File
@@ -9,7 +9,7 @@ import { Input } from "@/components/ui/input"
import {
TrendingUp, TrendingDown, Server, Coins, Search, ArrowUpRight,
Loader2, Filter, ChevronRight, Zap, Shield, Globe, Star, Lock
} from "lucide-react"
} from "@luxfi/ui/icons"
import Link from "next/link"
interface StakingData {
+1 -1
View File
@@ -24,7 +24,7 @@ import {
Wifi,
XCircle,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getApiUrl, getAuthHeaders } from "@/lib/api"
interface HealthCheck {
@@ -12,7 +12,7 @@ import {
Loader2,
ArrowUpRight,
AlertCircle,
} from "lucide-react"
} from "@luxfi/ui/icons"
interface Tier {
name: string
+1 -1
View File
@@ -5,7 +5,7 @@ import { useEffect, useState } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Building, Users, Settings, Key, Loader2, CreditCard, AlertCircle } from "lucide-react"
import { Building, Users, Settings, Key, Loader2, CreditCard, AlertCircle } from "@luxfi/ui/icons"
import Link from "next/link"
import { useAuth } from "@/lib/auth"
@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Badge } from "@/components/ui/badge"
import { Switch } from "@/components/ui/switch"
import { Building, Globe, Bell, Shield, Loader2, CheckCircle2, AlertCircle } from "lucide-react"
import { Building, Globe, Bell, Shield, Loader2, CheckCircle2, AlertCircle } from "@luxfi/ui/icons"
import { useAuth } from "@/lib/auth"
interface OrgSettings {
@@ -13,7 +13,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { useAuth } from "@/lib/auth"
import { team as teamApi, TeamMember } from "@/lib/api"
import { Users, UserPlus, Mail, Shield, Settings, Trash2, Loader2 } from "lucide-react"
import { Users, UserPlus, Mail, Shield, Settings, Trash2, Loader2 } from "@luxfi/ui/icons"
export default function TeamPage() {
const { user } = useAuth()
+1 -1
View File
@@ -18,7 +18,7 @@ import {
Building,
Loader2,
AlertCircle
} from "lucide-react"
} from "@luxfi/ui/icons"
interface DashboardStats {
api_requests: number
+1 -1
View File
@@ -18,7 +18,7 @@ import {
EyeOff,
LogOut,
User,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useAuth, OrgBadge } from "@/lib/auth"
import { getApiUrl } from "@/lib/api"
+1 -1
View File
@@ -9,7 +9,7 @@ import { Input } from "@/components/ui/input"
import {
Wallet, Key, Shield, Server, Coins, Lock, Unlock, ExternalLink,
Loader2, ChevronRight, Zap, AlertTriangle, Check, Copy, RefreshCcw
} from "lucide-react"
} from "@luxfi/ui/icons"
import Link from "next/link"
interface Validator {
+1 -1
View File
@@ -8,7 +8,7 @@ import {
Globe,
Database,
Server,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useChains } from "@/lib/hooks"
import { getBrand } from "@/lib/brand"
+1 -1
View File
@@ -16,7 +16,7 @@ import {
Shield,
Key,
Users,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useWallets, useCreateWallet } from "@/lib/hooks"
import { getBrand } from "@/lib/brand"
+1 -1
View File
@@ -18,7 +18,7 @@ import {
Send,
ChevronDown,
ChevronUp,
} from "lucide-react"
} from "@luxfi/ui/icons"
import {
useWebhooks,
useCreateWebhook,
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -14,7 +14,7 @@ import {
Terminal,
FileCode,
ArrowRight,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
const sections = [
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -2,7 +2,7 @@ import Link from "next/link"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { DocsLayout } from "@/components/docs-layout"
import { ArrowRight } from "lucide-react"
import { ArrowRight } from "@luxfi/ui/icons"
import { docsConfig } from "@/lib/docs-config"
export const metadata = {
+1 -1
View File
@@ -21,7 +21,7 @@ import {
Wallet,
Webhook,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
export default function HomePage() {
const brand = getBrand()
+1 -1
View File
@@ -20,7 +20,7 @@ import {
HelpCircle,
Minus,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
export const metadata: Metadata = {
+1 -1
View File
@@ -3,7 +3,7 @@ import type { Metadata } from "next"
import { Navbar } from "@/components/navbar"
import { Footer } from "@/components/footer"
import { Badge } from "@/components/ui/badge"
import { Shield } from "lucide-react"
import { Shield } from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -19,7 +19,7 @@ import {
Layers,
Search,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
import { docsConfig } from "@/lib/docs-config"
+1 -1
View File
@@ -20,7 +20,7 @@ import {
BarChart3,
Braces,
Palette,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
export default function ExplorerProductPage() {
+1 -1
View File
@@ -20,7 +20,7 @@ import {
Users,
Wallet,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
export default function MPCProductPage() {
+1 -1
View File
@@ -18,7 +18,7 @@ import {
Shield,
Wifi,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
import { docsConfig } from "@/lib/docs-config"
+1 -1
View File
@@ -21,7 +21,7 @@ import {
Settings,
Wrench,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
import { docsConfig } from "@/lib/docs-config"
+1 -1
View File
@@ -19,7 +19,7 @@ import {
Users,
Wallet,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
import { getSdkPackage } from "@/lib/sdk-package"
import { docsConfig } from "@/lib/docs-config"
+1 -1
View File
@@ -21,7 +21,7 @@ import {
TestTube,
Webhook,
Zap,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
import { docsConfig } from "@/lib/docs-config"
+55 -3
View File
@@ -1,7 +1,50 @@
"use client"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { useState } from "react"
import { AppProvider, IdentityProvider } from "@luxfi/ui"
import { QueryClient } from "@tanstack/react-query"
import { useEffect, useState } from "react"
import { setReturnUrl } from "@/lib/iam"
// Brand + identity, both from the HOST.
//
// `AppProvider` (@luxfi/ui) mounts the @hanzo/gui engine on the host-resolved
// theme row and carries the white-label — the same resolver lib/brand.ts now
// reads its OIDC triple from, so there is one answer to "which brand is this"
// and not two that can disagree.
//
// `IdentityProvider` reads the signed-in account and its org membership from
// IAM. Credentials stay where they already are: lib/iam owns the PKCE flow and
// localStorage["bootnode_token"] the bearer, so the chrome adds no second token
// store and no second refresh timer — it borrows the bearer and two verbs.
const TOKEN_KEY = "bootnode_token"
const readToken = (): string | null => {
if (typeof window === "undefined") return null
try {
return window.localStorage.getItem(TOKEN_KEY)
} catch {
return null
}
}
/** Sign-in goes through /login, which is what drives the SDK's PKCE redirect. */
const signIn = (): void => {
setReturnUrl(window.location.pathname + window.location.search)
window.location.assign("/login")
}
/** Sign-out drops the bearer and returns to the marketing root. */
const signOut = (): void => {
try {
window.localStorage.removeItem(TOKEN_KEY)
window.localStorage.removeItem("bootnode_project_id")
} catch {
/* storage disabled — the redirect below still ends the session in this tab */
}
window.location.assign("/")
}
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
@@ -15,8 +58,17 @@ export function Providers({ children }: { children: React.ReactNode }) {
},
})
)
// `output: "export"` prerenders with no Host header, so the first paint is
// the default row and the real host arrives after hydration — deterministic
// on both sides, so there is no mismatch.
const [host, setHost] = useState<string | undefined>(undefined)
useEffect(() => setHost(window.location.host), [])
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<AppProvider host={host} queryClient={queryClient}>
<IdentityProvider auth={{ token: readToken, signIn, signOut }}>
{children}
</IdentityProvider>
</AppProvider>
)
}
+1 -1
View File
@@ -13,7 +13,7 @@ import {
Network,
Shield,
ShieldCheck,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -3,7 +3,7 @@ import type { Metadata } from "next"
import { Navbar } from "@/components/navbar"
import { Footer } from "@/components/footer"
import { Badge } from "@/components/ui/badge"
import { FileText } from "lucide-react"
import { FileText } from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const brand = getBrand()
+1 -1
View File
@@ -14,7 +14,7 @@ import {
Activity,
Zap,
RotateCw,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useBrand } from "@/components/brand-logo"
import { getBrandKey } from "@/lib/brand"
@@ -1,6 +1,7 @@
"use client"
import { useMemo, useState } from "react"
import { Check } from "@luxfi/ui/icons"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
@@ -100,7 +101,7 @@ export function ChainOnboardingSelector({
active ? "border-blue-600 bg-blue-600 text-white" : "border-zinc-300"
}`}
>
{active ? "✓" : ""}
{active ? <Check className="size-3" /> : null}
</span>
<span className="inline-flex h-6 w-6 items-center justify-center rounded-full bg-zinc-100 text-sm overflow-hidden">
{item.icon.startsWith("/") ? (
+1 -1
View File
@@ -4,7 +4,7 @@ import { useState } from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Check, Copy, Play } from "lucide-react"
import { Check, Copy, Play } from "@luxfi/ui/icons"
import { getBrand } from "@/lib/brand"
const CHAINS = ["Ethereum", "Solana", "Base", "Arbitrum", "Polygon"]
+1 -1
View File
@@ -27,7 +27,7 @@ import {
Sparkles,
ArrowUpRight,
Terminal,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { useBrand } from "@/components/brand-logo"
import { AiChat } from "@/components/ai-chat"
@@ -2,7 +2,7 @@
import { useMemo, useState } from "react"
import { useRouter } from "next/navigation"
import { ArrowRight, Search } from "lucide-react"
import { ArrowRight, Search } from "@luxfi/ui/icons"
import {
Dialog,
+1 -1
View File
@@ -20,7 +20,7 @@ import {
Terminal,
X,
XCircle,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { getApiUrl, getAuthHeaders as getAuth } from "@/lib/api"
// ─── Types ─────────────────────────────────────────────
+1 -1
View File
@@ -13,7 +13,7 @@ import {
Package,
FileCode,
Clock,
} from "lucide-react"
} from "@luxfi/ui/icons"
const sidebarNav = [
{ title: "Documentation", href: "/docs", icon: Book },
+1 -1
View File
@@ -2,7 +2,7 @@
import Link from "next/link"
import { BrandLogo, useBrand } from "@/components/brand-logo"
import { ExternalLink } from "lucide-react"
import { ExternalLink } from "@luxfi/ui/icons"
// Footer, in lux.cloud's chrome: no tinted slab, just a hairline rule over the
// page ground with 13px muted type. The four sitemap columns stay — dropping
+1 -1
View File
@@ -26,7 +26,7 @@ import {
RefreshCcw,
Server,
Users,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { FleetMap, type FleetRegion } from "@/components/lux/fleet-map"
import {
CHAIN_LABEL,
+1 -1
View File
@@ -2,7 +2,7 @@
import { useEffect, useState } from "react"
import Link from "next/link"
import { ArrowRight, Circle } from "lucide-react"
import { ArrowRight, Circle } from "@luxfi/ui/icons"
import {
NETWORK_LABEL,
fetchFleetOverview,
+1 -1
View File
@@ -1,7 +1,7 @@
"use client"
import { useState, useEffect, useRef, ReactNode } from "react"
import { ChevronDown } from "lucide-react"
import { ChevronDown } from "@luxfi/ui/icons"
import { useMediaQuery } from "@/hooks/use-media-query"
import { cn } from "@/lib/utils"
+15 -23
View File
@@ -34,7 +34,9 @@ import {
MessageCircle,
LifeBuoy,
Activity,
} from "lucide-react"
} from "@luxfi/ui/icons"
import { OrgSwitcher, UserMenu } from "@luxfi/ui"
import { BrandLogo } from "@/components/brand-logo"
import { useBrand } from "@/components/brand-provider"
import { CommandMenu } from "@/components/command-menu"
@@ -487,28 +489,18 @@ export function Navbar() {
and a poor thumb target. */}
<div className="flex items-center gap-2.5 max-lg:flex-col max-lg:items-stretch max-lg:gap-2 [&_button]:max-lg:h-10 [&_a]:max-lg:h-10">
<CommandMenu />
{user ? (
<>
{/* Who you are signed in as. The old console header spent a
dropdown on this plus an org switcher whose options changed
nothing and a bell wired to no notification source. */}
<span
className="max-w-44 truncate text-[13px] text-muted-foreground max-lg:max-w-none"
title={user.email}
>
{user.email}
</span>
<Button variant="outline" size="sm" onClick={() => { close(); logout() }}>
Sign out
</Button>
</>
) : (
<Button variant="outline" size="sm" asChild onClick={close}>
{/* Brand-derived: on lux.cloud this reads "Sign in with Lux ID",
on zoo.cloud "Zoo ID" — from the same build. */}
<Link href="/login">Sign in with {brand.iam.idLabel}</Link>
</Button>
)}
{/* The account cluster is the SHARED chrome (@luxfi/ui), not a
third copy of it. What was here was an email in a <span> and a
Sign out button — no menu, no way to see or change which org you
were acting in, and a "sign in" that only ever named the brand.
The switchers below read IAM (get-account, get-organizations)
and are the same controls the MPC dashboard and lux.market show,
including the phone sheet, which this bar had no equivalent of.
They degrade honestly: signed out they offer sign-in rather than
rendering an empty list that reads as an account with no orgs. */}
<OrgSwitcher />
<UserMenu />
<Button size="sm" asChild onClick={close}>
<Link href="/dashboard">Dashboard</Link>
</Button>
+1 -1
View File
@@ -2,7 +2,7 @@
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { X } from "@luxfi/ui/icons"
import { cn } from "@/lib/utils"
+1 -1
View File
@@ -2,7 +2,7 @@
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import { Check, ChevronRight, Circle } from "@luxfi/ui/icons"
import { cn } from "@/lib/utils"
+1 -1
View File
@@ -3,7 +3,7 @@
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { ChevronDown } from "lucide-react"
import { ChevronDown } from "@luxfi/ui/icons"
import { cn } from "@/lib/utils"
const NavigationMenu = React.forwardRef<
+39 -7
View File
@@ -27,6 +27,8 @@
// registering `https://<host>/auth/callback` on that brand's IAM. Nothing else
// in the app carries a brand string, an issuer, or a client id.
import { resolveWhiteLabel } from "@luxfi/ui/white-label"
export type BrandKey = "bootnode" | "lux" | "zoo" | "hanzo" | "pars"
export type BrandConfig = {
@@ -89,7 +91,9 @@ export type BrandConfig = {
const API = "https://api.bootno.de"
const WS = "wss://ws.bootno.de"
const BRANDS: Record<BrandKey, BrandConfig> = {
// Presentation only. The OIDC triple is attached below, from the one shared
// table — see CONSOLE_HOST.
const BRANDS: Record<BrandKey, Omit<BrandConfig, "iam">> = {
// DEFAULT. bootno.de, localhost, and any unmatched host resolve here.
bootnode: {
key: "bootnode",
@@ -113,7 +117,6 @@ const BRANDS: Record<BrandKey, BrandConfig> = {
defaultTheme: "dark",
colors: { primary: "#f5f5f5", primaryForeground: "#000000", accent: "#a8a8ff", accentEnd: "#8bffd1" },
social: { github: "https://github.com/bootnode" },
iam: { url: "https://id.bootno.de", clientId: "bootnode-platform", domain: "id.bootno.de", idLabel: "Bootnode ID" },
footerLinks: [],
ecosystemApps: [],
},
@@ -140,7 +143,6 @@ const BRANDS: Record<BrandKey, BrandConfig> = {
defaultTheme: "dark",
colors: { primary: "#f5f5f5", primaryForeground: "#000000", accent: "#a8a8ff", accentEnd: "#8bffd1" },
social: { github: "https://github.com/luxfi" },
iam: { url: "https://lux.id", clientId: "lux-cloud", domain: "lux.id", idLabel: "Lux ID" },
footerLinks: [],
ecosystemApps: [],
},
@@ -171,7 +173,6 @@ const BRANDS: Record<BrandKey, BrandConfig> = {
defaultTheme: "dark",
colors: { primary: "#f5f5f5", primaryForeground: "#000000", accent: "#8bffd1", accentEnd: "#a8a8ff" },
social: { github: "https://github.com/zooai" },
iam: { url: "https://zoolabs.id", clientId: "zoo-cloud", domain: "zoolabs.id", idLabel: "Zoo ID" },
footerLinks: [],
ecosystemApps: [],
},
@@ -198,7 +199,6 @@ const BRANDS: Record<BrandKey, BrandConfig> = {
defaultTheme: "dark",
colors: { primary: "#f5f5f5", primaryForeground: "#000000", accent: "#a8a8ff", accentEnd: "#ff8bc0" },
social: { github: "https://github.com/hanzoai" },
iam: { url: "https://hanzo.id", clientId: "hanzo-web3", domain: "hanzo.id", idLabel: "Hanzo ID" },
footerLinks: [],
ecosystemApps: [],
},
@@ -225,7 +225,6 @@ const BRANDS: Record<BrandKey, BrandConfig> = {
defaultTheme: "dark",
colors: { primary: "#f5f5f5", primaryForeground: "#000000", accent: "#ffd18b", accentEnd: "#ff8bc0" },
social: { github: "https://github.com/parsdao" },
iam: { url: "https://pars.id", clientId: "pars-app", domain: "pars.id", idLabel: "Pars ID" },
footerLinks: [],
ecosystemApps: [],
},
@@ -247,6 +246,39 @@ const HOSTS: { host: string; key: BrandKey }[] = [
const DEFAULT_KEY: BrandKey = "bootnode"
// The host each brand's console is REGISTERED under — the one key into the
// shared white-label table.
//
// Issuer, client id and IAM host are NOT this file's to know. They are the same
// answer @luxfi/ui/white-label already gives every Lux surface from the Host
// header, and a second copy here is a second thing to get wrong — which it was:
// this table and that one disagreed about Zoo's issuer, and the surfaces on the
// losing side of that disagreement reject every token they are handed.
//
// This file keeps what is genuinely its own: logos, taglines, palettes, footer
// links, the ecosystem launcher, and the API origins that answer.
const CONSOLE_HOST: Record<BrandKey, string> = {
bootnode: "bootno.de",
lux: "lux.cloud",
zoo: "zoo.cloud",
hanzo: "web3.hanzo.ai",
pars: "cloud.pars.network",
}
/** Presentation + the shared OIDC triple, resolved once per brand. */
const RESOLVED = Object.fromEntries(
(Object.keys(BRANDS) as BrandKey[]).map((key) => {
const wl = resolveWhiteLabel(CONSOLE_HOST[key])
return [
key,
{
...BRANDS[key],
iam: { url: wl.issuer, clientId: wl.clientId, domain: wl.iamDomain, idLabel: `${wl.name} ID` },
},
]
})
) as Record<BrandKey, BrandConfig>
/**
* Resolve the brand key for a hostname. Pass one explicitly to resolve for a
* host other than the current page; omit it in the browser to use
@@ -264,5 +296,5 @@ export function getBrandKey(hostname?: string): BrandKey {
/** The active brand. Synchronous and total — every key has a complete entry. */
export function getBrand(hostname?: string): BrandConfig {
return BRANDS[getBrandKey(hostname)]
return RESOLVED[getBrandKey(hostname)]
}
+1 -1
View File
@@ -27,7 +27,7 @@ export interface LuxNode {
pod_index: number
phase: string
ready: boolean
external_ip: string | null
ip: string | null
rpc_reachable: boolean
node_id: string | null
version: string | null
+8 -1
View File
@@ -1,4 +1,5 @@
import type { NextConfig } from "next"
import { withLuxUi } from "@luxfi/ui/next"
const nextConfig: NextConfig = {
// Modern React & Next.js features
@@ -32,4 +33,10 @@ const nextConfig: NextConfig = {
// the legacy /admin -> /dashboard redirect is handled by public/_redirects.
}
export default nextConfig
// `withLuxUi` is the ONE place the @hanzo/gui engine's bundler wiring lives —
// transpile list, react-native → react-native-web alias in both bundlers, the
// platform defines, and the single-copy dedupe. Every Next surface needed the
// identical incantation; the ones that hand-rolled it each transpiled a
// different subset, which is how one app renders a Sheet and the next throws on
// the same component.
export default withLuxUi({ ...nextConfig, dir: __dirname } as never) as NextConfig
+5 -1
View File
@@ -53,7 +53,11 @@
"sonner": "catalog:",
"tailwind-merge": "catalog:",
"tailwindcss-animate": "catalog:",
"zod": "catalog:"
"zod": "catalog:",
"@hanzo/gui": "catalog:",
"@luxfi/ui": "catalog:",
"geist": "catalog:",
"react-native-web": "catalog:"
},
"devDependencies": {
"@tailwindcss/postcss": "catalog:",