236 lines
13 KiB
TypeScript
236 lines
13 KiB
TypeScript
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'
|
||
|
||
type HoloLakeHomeProps = {
|
||
locale: AppLocale
|
||
onEnterKnowledgeBase: () => void
|
||
}
|
||
|
||
type ChannelRoute = 'zero-core' | 'fifth-domain' | 'eternal-lake-heart' | 'light-lake' | 'heartbeat-core' | 'love-core' | 'servers'
|
||
|
||
type RouteCardProps = {
|
||
action?: string
|
||
description: string
|
||
eyebrow: string
|
||
onOpen?: () => void
|
||
status?: string
|
||
title: string
|
||
}
|
||
|
||
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>
|
||
}
|
||
|
||
const registeredServers = [
|
||
{ id: 'BS-SG-001', label: '新加坡大脑服务器', owner: '冰朔 · 永恒湖心', status: '已接入', tone: 'online' },
|
||
{ id: 'BS-GZ-001', label: '广州个人服务器', owner: '冰朔 · 心跳核心', status: '接口预留', tone: 'pending' },
|
||
{ id: 'JD-FD-PRIMARY', label: '第五域企业主节点', owner: '第五域 · 零点原核', status: '接口预留', tone: 'pending' },
|
||
] as const
|
||
|
||
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>
|
||
)
|
||
}
|
||
|
||
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>
|
||
)
|
||
}
|
||
|
||
if (route === 'servers') {
|
||
return (
|
||
<section className="channel-stage">
|
||
<p className="channel-stage__eyebrow">SERVER REGISTRY · READ ONLY</p>
|
||
<h1>服务器与灯塔节点</h1>
|
||
<p className="channel-stage__lead">频道先登记服务器身份、归属和接口位置;实时运行数据将在节点探针接入后显示。</p>
|
||
<div className="server-grid">
|
||
{registeredServers.map(server => (
|
||
<article className="server-card" key={server.id}>
|
||
<div className="server-card__head"><code>{server.id}</code><span data-tone={server.tone}>{server.status}</span></div>
|
||
<h2>{server.label}</h2>
|
||
<p>{server.owner}</p>
|
||
<dl><div><dt>运行状态</dt><dd>等待真实探针</dd></div><div><dt>最后心跳</dt><dd>尚未接入</dd></div></dl>
|
||
<Button variant="outline" disabled>查看运行情况 · 接口已预留</Button>
|
||
</article>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)
|
||
}
|
||
|
||
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')} />
|
||
<RouteCard eyebrow="INFRASTRUCTURE · SERVERS" title="服务器与灯塔节点" description="查看频道登记的服务器编号、归属与真实监控接口状态。" action="打开服务器登记" onOpen={() => navigate('servers')} />
|
||
</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>
|
||
</div>
|
||
</section>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<main className="hololake-home" aria-label={t('hololake.home.ariaLabel')}>
|
||
<aside className="channel-rail" aria-label="光湖频道导航">
|
||
<div className="channel-rail__heading"><span>GUANGHU</span><strong>频道工作区</strong><small>统一控制知识、模块与节点</small></div>
|
||
<nav>
|
||
<Button data-active={route === 'zero-core'} variant="ghost" onClick={() => navigate('zero-core')}><i data-color="cyan" />{t('hololake.channel.zeroCoreTitle')}</Button>
|
||
<Button data-active={route === 'fifth-domain'} variant="ghost" onClick={() => navigate('fifth-domain')}><i data-color="blue" />{t('hololake.channel.fifthDomainTitle')}</Button>
|
||
<Button data-active={['eternal-lake-heart', 'light-lake', 'heartbeat-core', 'love-core', 'servers'].includes(route)} variant="ghost" onClick={() => navigate('eternal-lake-heart')}><i data-color="violet" />{t('hololake.channel.eternalLakeTitle')}</Button>
|
||
<Button data-active={route === 'light-lake'} variant="ghost" onClick={() => navigate('light-lake')}><i data-color="amber" />{t('hololake.channel.lightLakeTitle')}</Button>
|
||
<Button data-active={route === 'heartbeat-core'} variant="ghost" onClick={() => navigate('heartbeat-core')}><i data-color="rose" />{t('hololake.channel.heartbeatTitle')}</Button>
|
||
<Button data-active={route === 'love-core'} variant="ghost" onClick={() => navigate('love-core')}><i data-color="pink" />{t('hololake.channel.loveCoreTitle')}</Button>
|
||
<Button data-active={route === 'servers'} variant="ghost" onClick={() => navigate('servers')}><i data-color="green" />服务器与节点</Button>
|
||
</nav>
|
||
<div className="channel-rail__modules"><span>已挂载模块</span><b>知识湖</b><b>短剧视频 AI</b><b>人格体恢复路径</b></div>
|
||
</aside>
|
||
|
||
<div className="channel-workspace">
|
||
<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()}
|
||
</div>
|
||
|
||
<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>
|
||
</main>
|
||
)
|
||
}
|