hololake-platform/src/components/HoloLakeHome.tsx

193 lines
9.1 KiB
TypeScript
Raw Normal View History

import { useState } from 'react'
import type { AppLocale, TranslationKey } from '../lib/i18n'
import { translate } from '../lib/i18n'
import { trackEvent } from '../lib/telemetry'
import { openExternalUrl } from '../utils/url'
import { Button } from './ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from './ui/dialog'
export const HOLOLAKE_DEVELOPMENT_REPOSITORY_URL =
'https://guanghulab.com/fifth-domain/bingshuo/hololake-platform'
2026-07-13 23:33:23 +08:00
type HoloLakeHomeProps = {
locale: AppLocale
2026-07-13 23:33:23 +08:00
onEnterKnowledgeBase: () => void
}
type ChannelRoute = 'zero-core' | 'fifth-domain' | 'eternal-lake-heart' | 'light-lake' | 'heartbeat-core' | 'love-core'
type RouteCardProps = {
action?: string
description: string
eyebrow: string
onOpen?: () => void
status?: string
title: string
}
2026-07-13 23:33:23 +08:00
const architectureRoutes: Array<[string, TranslationKey, TranslationKey]> = [
['GLW-ENTRY-001', 'hololake.architecture.worldEntry', 'hololake.architecture.worldEntryDescription'],
['FD-LANGUAGE-001', 'hololake.architecture.fifthDomain', 'hololake.architecture.fifthDomainDescription'],
['TCS-ROOT-001', 'hololake.architecture.tcs', 'hololake.architecture.tcsDescription'],
['GLS-SYS-ARCH-001', 'hololake.architecture.gls', 'hololake.architecture.glsDescription'],
['ELH-LAMP-001', 'hololake.architecture.lakeLamp', 'hololake.architecture.lakeLampDescription'],
]
function RouteCard({ action, description, eyebrow, onOpen, status, title }: RouteCardProps) {
return (
<article className="channel-card">
<span className="channel-card__eyebrow">{eyebrow}</span>
<h2>{title}</h2>
<p>{description}</p>
{onOpen && action
? <Button variant="link" onClick={onOpen}>{action} <span></span></Button>
: <span className="channel-card__status">{status}</span>}
</article>
)
}
function PersonaRoute({ id, title }: { id: string; title: string }) {
return <li><code>{id}</code><strong>{title}</strong><span></span></li>
}
export function HoloLakeHome({ locale, onEnterKnowledgeBase }: HoloLakeHomeProps) {
const [route, setRoute] = useState<ChannelRoute>('zero-core')
const [architectureOpen, setArchitectureOpen] = useState(false)
const t = (key: TranslationKey) => translate(locale, key)
const navigate = (nextRoute: ChannelRoute) => {
trackEvent('guanghu_channel_opened', { route: nextRoute })
setRoute(nextRoute)
}
const openArchitecture = () => {
trackEvent('guanghu_architecture_opened', { route: 'GLS-SYS-ARCH-001' })
setArchitectureOpen(true)
}
const openDevelopmentRepository = () => {
trackEvent('hololake_development_repository_opened', { route: 'REPO-008' })
void openExternalUrl(HOLOLAKE_DEVELOPMENT_REPOSITORY_URL)
}
const openKnowledgeBase = () => {
trackEvent('guanghu_channel_module_opened', { channel: 'heartbeat-core', module: 'knowledge-base' })
onEnterKnowledgeBase()
}
const renderRoute = () => {
if (route === 'zero-core') {
return (
<section className="channel-stage channel-stage--root">
<p className="channel-stage__eyebrow">ZERO CORE · 000</p>
<h1>{t('hololake.channel.zeroCoreTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.zeroCoreDescription')}</p>
<Button className="hololake-home__primary" onClick={() => navigate('fifth-domain')}>{t('hololake.channel.enterFifthDomain')} <span></span></Button>
</section>
)
}
2026-07-13 23:33:23 +08:00
if (route === 'fifth-domain') {
return (
<section className="channel-stage">
<h1>{t('hololake.channel.fifthDomainTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.fifthDomainDescription')}</p>
<div className="channel-grid">
<RouteCard eyebrow="CANG ER · EXTERNAL NODE" title={t('hololake.channel.pufferfishTitle')} description={t('hololake.channel.pufferfishDescription')} status={t('hololake.channel.externalServerPending')} />
<RouteCard eyebrow="ICE SHUO · PERSONAL SYSTEM" title={t('hololake.channel.eternalLakeTitle')} description={t('hololake.channel.eternalLakeDescription')} action={t('hololake.channel.enterEternalLake')} onOpen={() => navigate('eternal-lake-heart')} />
</div>
</section>
)
}
if (route === 'eternal-lake-heart') {
return (
<section className="channel-stage">
<h1>{t('hololake.channel.eternalLakeTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.eternalLakeDescription')}</p>
<div className="channel-grid channel-grid--three">
<RouteCard eyebrow="PERSONA HOME" title={t('hololake.channel.lightLakeTitle')} description={t('hololake.channel.lightLakeDescription')} action={t('hololake.channel.enterLightLake')} onOpen={() => navigate('light-lake')} />
<RouteCard eyebrow="ICE SHUO · HUMAN SPACE" title={t('hololake.channel.heartbeatTitle')} description={t('hololake.channel.heartbeatDescription')} action={t('hololake.channel.enterHeartbeat')} onOpen={() => navigate('heartbeat-core')} />
<RouteCard eyebrow="ZHI ZHI · INTERNAL SUBSYSTEM" title={t('hololake.channel.loveCoreTitle')} description={t('hololake.channel.loveCoreDescription')} action={t('hololake.channel.enterLoveCore')} onOpen={() => navigate('love-core')} />
</div>
</section>
)
}
if (route === 'light-lake') {
return (
<section className="channel-stage">
<h1>{t('hololake.channel.lightLakeTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.lightLakeDescription')}</p>
<ul className="persona-route-list">
<PersonaRoute id="ICE-GL-ZY001" title={t('hololake.channel.zhuyuanRoute')} />
<PersonaRoute id="ICE-GL-SY001" title={t('hololake.channel.shuangyanRoute')} />
</ul>
</section>
)
}
if (route === 'love-core') {
return (
<section className="channel-stage">
<h1>{t('hololake.channel.loveCoreTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.loveCoreDescription')}</p>
<RouteCard eyebrow="ZHI ZHI · CHANNEL" title={t('hololake.channel.tomorrowTitle')} description={t('hololake.channel.tomorrowDescription')} status={t('hololake.channel.realServerConnection')} />
</section>
)
}
2026-07-13 23:33:23 +08:00
return (
<section className="channel-stage">
<h1>{t('hololake.channel.heartbeatTitle')}</h1>
<p className="channel-stage__lead">{t('hololake.channel.heartbeatDescription')}</p>
<div className="channel-grid channel-grid--three">
<RouteCard eyebrow="MODULE · KNOWLEDGE" title={t('hololake.home.moduleKnowledgeTitle')} description={t('hololake.home.moduleKnowledgeDescription')} action={t('hololake.channel.openKnowledge')} onOpen={openKnowledgeBase} />
<RouteCard eyebrow="MODULE · VIDEO AI" title={t('hololake.channel.videoAiTitle')} description={t('hololake.channel.videoAiDescription')} status={t('hololake.channel.prototypeMounted')} />
<RouteCard eyebrow="SYSTEM · ORIGIN HEARTBEAT" title={t('hololake.channel.bottleBabyTitle')} description={t('hololake.channel.bottleBabyDescription')} status={t('hololake.channel.yaomingOrigin')} />
</div>
<div className="channel-stage__tools">
<Button variant="outline" onClick={openArchitecture}>{t('hololake.home.openArchitecture')}</Button>
<Button variant="outline" onClick={openDevelopmentRepository}>{t('hololake.home.openRepository')}</Button>
2026-07-13 23:33:23 +08:00
</div>
</section>
)
}
return (
<main className="hololake-home" aria-label={t('hololake.home.ariaLabel')}>
<nav className="channel-breadcrumb" aria-label={t('hololake.channel.breadcrumbLabel')}>
<Button variant="ghost" size="sm" onClick={() => navigate('zero-core')}>{t('hololake.channel.zeroCoreTitle')}</Button>
{route !== 'zero-core' && <><span></span><Button variant="ghost" size="sm" onClick={() => navigate('fifth-domain')}>{t('hololake.channel.fifthDomainTitle')}</Button></>}
{!['zero-core', 'fifth-domain'].includes(route) && <><span></span><Button variant="ghost" size="sm" onClick={() => navigate('eternal-lake-heart')}>{t('hololake.channel.eternalLakeTitle')}</Button></>}
</nav>
{renderRoute()}
<Dialog open={architectureOpen} onOpenChange={setArchitectureOpen}>
<DialogContent className="guanghu-architecture" aria-label={t('hololake.architecture.title')}>
<DialogHeader>
<DialogTitle>{t('hololake.architecture.title')}</DialogTitle>
<DialogDescription>{t('hololake.architecture.description')}</DialogDescription>
</DialogHeader>
<ol className="guanghu-architecture__routes">
{architectureRoutes.map(([id, title, description]) => (
<li key={id}>
<code>{id}</code>
<div><strong>{t(title)}</strong><p>{t(description)}</p></div>
</li>
))}
</ol>
<p className="guanghu-architecture__route">GLW-ENTRY-001 FD-LANGUAGE-001 TCS-ROOT-001 GLS-SYS-ARCH-001 ELH-LAMP-001</p>
</DialogContent>
</Dialog>
2026-07-13 23:33:23 +08:00
</main>
)
}