mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-10-25 02:31:30 +00:00
Remove the hardcoded 5-brand catalog from web/lib/brand.ts; resolve the active brand at runtime by hostname from an external catalog (window.__BRAND_CATALOG__ / /config.json), mirroring the @hanzo/id SPA. Ship one neutral built-in bootnode fallback. Add BrandProvider that loads the per-deploy catalog client-side after hydration. Co-authored-by: Hanzo Dev <[email protected]>
113 lines
2.5 KiB
TypeScript
113 lines
2.5 KiB
TypeScript
"use client"
|
|
|
|
import Image from "next/image"
|
|
import { useEffect, useState } from "react"
|
|
import { useTheme } from "next-themes"
|
|
import { getBrand } from "@/lib/brand"
|
|
import { useBrand } from "@/components/brand-provider"
|
|
|
|
// Re-export the brand hook so existing importers (`@/components/brand-logo`)
|
|
// keep working. Single source of truth lives in brand-provider.tsx.
|
|
export { useBrand }
|
|
|
|
// Brand Logo Component - theme-aware and white-label ready
|
|
export function BrandLogo({
|
|
showText = true,
|
|
size = "default",
|
|
className = ""
|
|
}: {
|
|
showText?: boolean
|
|
size?: "small" | "default" | "large"
|
|
className?: string
|
|
}) {
|
|
const brand = useBrand()
|
|
const { resolvedTheme } = useTheme()
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setMounted(true)
|
|
}, [])
|
|
|
|
const sizeClasses = {
|
|
small: "h-5 w-5",
|
|
default: "h-6 w-6",
|
|
large: "h-8 w-8",
|
|
}
|
|
|
|
const textSizes = {
|
|
small: "text-lg",
|
|
default: "text-xl",
|
|
large: "text-2xl",
|
|
}
|
|
|
|
const pixelSizes = {
|
|
small: 20,
|
|
default: 24,
|
|
large: 32,
|
|
}
|
|
|
|
// Use white logo for dark mode, default logo for light mode
|
|
const logoSrc = mounted && resolvedTheme === "dark" ? brand.logoWhite : brand.logo
|
|
|
|
return (
|
|
<div className={`flex items-center space-x-2 ${className}`}>
|
|
<Image
|
|
src={brand.logoIcon}
|
|
alt={brand.name}
|
|
width={pixelSizes[size]}
|
|
height={pixelSizes[size]}
|
|
className={sizeClasses[size]}
|
|
/>
|
|
{showText && (
|
|
<span className={`font-bold ${textSizes[size]}`}>{brand.name}</span>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Full logo (icon + text as image if available)
|
|
export function BrandLogoFull({
|
|
size = "default",
|
|
className = ""
|
|
}: {
|
|
size?: "small" | "default" | "large"
|
|
className?: string
|
|
}) {
|
|
const brand = useBrand()
|
|
const { resolvedTheme } = useTheme()
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setMounted(true)
|
|
}, [])
|
|
|
|
const heights = {
|
|
small: 24,
|
|
default: 32,
|
|
large: 40,
|
|
}
|
|
|
|
// Use white logo for dark mode
|
|
const logoSrc = mounted && resolvedTheme === "dark" ? brand.logoWhite : brand.logo
|
|
|
|
return (
|
|
<Image
|
|
src={logoSrc}
|
|
alt={brand.name}
|
|
width={heights[size] * 4}
|
|
height={heights[size]}
|
|
className={className}
|
|
/>
|
|
)
|
|
}
|
|
|
|
// Export brand name helper for SSR-safe usage
|
|
export function getBrandName(): string {
|
|
return getBrand().name
|
|
}
|
|
|
|
// Export brand tagline
|
|
export function getBrandTagline(): string {
|
|
return getBrand().tagline
|
|
}
|