hololake-system-architecture/product-source/hololake-platform/src/components/HoloLakeHome.tsx

662 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useCallback, useEffect, useRef, useState } from 'react'
import { isTauri } from '../mock-tauri'
import { fetchFifthDomainDiscovery, type FifthDomainDiscovery } from '../lib/fifthDomainDiscovery'
import type { AppLocale, TranslationKey } from '../lib/i18n'
import { translate } from '../lib/i18n'
import { trackEvent } from '../lib/telemetry'
import { openExternalUrl } from '../utils/url'
import { useGuanghuRouter } from '../hooks/useGuanghuRouter'
import { useGuanghuShanghaiNode } from '../hooks/useGuanghuShanghaiNode'
import { useGuanghuWorldLogin } from '../hooks/useGuanghuWorldLogin'
import { useGuanghuEnterpriseStatus } from '../hooks/useGuanghuEnterpriseStatus'
import type { AiModelTarget } from '../lib/aiTargets'
import {
createDeterministicLivingSystemPlan,
createLivingSystemEvent,
createLivingSystemExecutionReceipt,
type GuanghuChannelRoute,
type GuanghuLivingIntent,
type GuanghuLivingSystemPlan,
} from '../lib/guanghuLivingSystem'
import { GUANGHU_THEMES, type GuanghuTheme } from '../lib/guanghuTheme'
import { planGuanghuLivingSystem } from '../utils/planGuanghuLivingSystem'
import { Button } from './ui/button'
import { HOLOLAKE_REPOSITORY_URL } from '../constants/feedback'
import { isEducationWorkLakePath } from '../lib/educationWorkLake'
import { GuanghuRouterConsole } from './GuanghuRouterConsole'
import { GuanghuWorldMap } from './GuanghuWorldMap'
import { GuanghuWorldLoginGate } from './GuanghuWorldLoginGate'
import { FifthDomainSystems } from './FifthDomainSystems'
import { EternalLakeHeartPage } from './EternalLakeHeartPage'
import { EducationWorkLake } from './EducationWorkLake'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from './ui/dialog'
export const HOLOLAKE_DEVELOPMENT_REPOSITORY_URL = HOLOLAKE_REPOSITORY_URL
export const GUANGHU_THEME_INTENT_EVENT = 'guanghu:living-system-theme-intent'
type HoloLakeHomeProps = {
locale: AppLocale
onEnterKnowledgeBase: () => void
onOpenAiWorkspace?: () => void
onOpenLocalWorkspace?: () => void | Promise<void>
livingSystemTarget?: AiModelTarget | null
}
type ChannelRoute = GuanghuChannelRoute
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>
}
export function HoloLakeHome({
locale,
onEnterKnowledgeBase,
onOpenAiWorkspace,
onOpenLocalWorkspace,
livingSystemTarget,
}: HoloLakeHomeProps) {
const initialRoute: ChannelRoute = isEducationWorkLakePath(window.location.pathname)
? 'education-work-lake'
: 'world'
const [route, setRoute] = useState<ChannelRoute>(initialRoute)
const [livingPlan, setLivingPlan] = useState<GuanghuLivingSystemPlan>(() => (
createDeterministicLivingSystemPlan(createLivingSystemEvent({
currentRoute: initialRoute,
eventId: 'system-start',
intent: 'navigate',
receiptIds: [],
requestedRoute: initialRoute,
worldOpen: false,
}))
))
const [livingKernel, setLivingKernel] = useState<'server' | 'model' | 'fallback' | 'planning'>('fallback')
const [livingReceiptId, setLivingReceiptId] = useState('local-state:system-start')
const [livingServerReceiptId, setLivingServerReceiptId] = useState<string>()
const [architectureOpen, setArchitectureOpen] = useState(false)
const [releaseNotesOpen, setReleaseNotesOpen] = useState(false)
const [fifthDomainConnection, setFifthDomainConnection] = useState<
{ status: 'checking' | 'error' } | { status: 'connected'; discovery: FifthDomainDiscovery }
>({ status: 'checking' })
const router = useGuanghuRouter()
const login = useGuanghuWorldLogin()
const enterprise = useGuanghuEnterpriseStatus()
const shanghai = useGuanghuShanghaiNode()
const restoreAttempted = useRef(false)
const livingRequestSequence = useRef(0)
const worldOpen = login.state.phase === 'online'
const fifthDomainOpen = worldOpen && route !== 'world'
const worldRouterState = router.state
const activeRouterReceipt = worldRouterState.status === 'online'
&& worldRouterState.latestReceipt?.state === 'online'
? worldRouterState.latestReceipt
: null
const t = (key: TranslationKey) => translate(locale, key)
const applyLivingPlan = useCallback((
plan: GuanghuLivingSystemPlan,
source: 'server' | 'model' | 'fallback',
serverReceiptId?: string,
) => {
setLivingPlan(plan)
setLivingKernel(source)
setLivingServerReceiptId(serverReceiptId)
setRoute(plan.navigationAction.route)
}, [])
const recordLivingExecution = useCallback((
plan: GuanghuLivingSystemPlan,
source: 'server' | 'model' | 'fallback',
outcome: 'executed' | 'failed',
evidence: string[],
error?: string,
serverReceiptId?: string,
) => {
const receipt = createLivingSystemExecutionReceipt({
plan,
source,
outcome,
evidence,
error,
})
setLivingReceiptId(receipt.receiptId)
trackEvent('guanghu_living_system_receipt', {
eventId: receipt.eventId,
outcome: receipt.outcome,
planId: receipt.planId,
receiptId: receipt.receiptId,
route: receipt.route,
source: receipt.source,
evidence: receipt.evidence.join(','),
...(serverReceiptId ? { serverReceiptId } : {}),
})
}, [])
const navigate = useCallback((
nextRoute: ChannelRoute,
intent: GuanghuLivingIntent = 'navigate',
onAccepted?: () => void | Promise<void>,
appearanceTheme?: GuanghuTheme,
) => {
trackEvent('guanghu_channel_opened', { intent, route: nextRoute })
const sequence = livingRequestSequence.current + 1
livingRequestSequence.current = sequence
const receiptIds = [worldRouterState.latestReceipt?.receipt_id, login.state.workorderId]
.filter((receiptId): receiptId is string => Boolean(receiptId))
const event = createLivingSystemEvent({
currentRoute: route,
eventId: `ui-${sequence}`,
intent,
receiptIds: [...new Set(receiptIds)],
requestedRoute: nextRoute,
appearanceTheme,
worldOpen,
})
const executeAcceptedPlan = async (
plan: GuanghuLivingSystemPlan,
source: 'server' | 'model' | 'fallback',
serverReceiptId?: string,
) => {
applyLivingPlan(plan, source, serverReceiptId)
const routeAccepted = plan.intent === intent && plan.navigationAction.route === nextRoute
try {
if (routeAccepted && onAccepted) {
await onAccepted()
}
const evidence = [
`ui.route:${plan.navigationAction.route}`,
...(routeAccepted && onAccepted && plan.capabilityCall
? [`capability.${plan.capabilityCall.capabilityId}.result`]
: []),
]
recordLivingExecution(plan, source, 'executed', evidence, undefined, serverReceiptId)
} catch (error) {
recordLivingExecution(
plan,
source,
'failed',
[`ui.route:${plan.navigationAction.route}`],
error instanceof Error ? error.message : 'host_action_failed',
serverReceiptId,
)
}
}
if (!isTauri() && !livingSystemTarget) {
void executeAcceptedPlan(createDeterministicLivingSystemPlan(event), 'fallback')
return
}
setLivingKernel('planning')
void planGuanghuLivingSystem({ event, target: livingSystemTarget }).then(result => {
if (livingRequestSequence.current !== sequence) return
void executeAcceptedPlan(result.plan, result.source, result.serverReceiptId)
})
}, [
applyLivingPlan,
livingSystemTarget,
login.state.workorderId,
recordLivingExecution,
route,
worldOpen,
worldRouterState.latestReceipt?.receipt_id,
])
const openArchitecture = () => {
trackEvent('guanghu_architecture_opened', { route: 'GLS-SYS-ARCH-001' })
setArchitectureOpen(true)
}
const openDevelopmentRepository = () => {
trackEvent('hololake_development_repository_opened', { route: 'REPO-014' })
void openExternalUrl(HOLOLAKE_DEVELOPMENT_REPOSITORY_URL)
}
const openKnowledgeBase = () => {
trackEvent('guanghu_channel_module_opened', { channel: 'heartbeat-core', module: 'knowledge-base' })
navigate(route, 'open-knowledge', onEnterKnowledgeBase)
}
const openLocalWorkspace = () => {
trackEvent('local_computer_workspace_opened', { source: 'heartbeat-core' })
navigate(route, 'open-local-workspace', onOpenLocalWorkspace)
}
const openAiWorkspace = () => {
trackEvent('guanghu_channel_module_opened', { channel: 'heartbeat-core', module: 'persona-ai-workspace' })
navigate(route, 'open-agent-workspace', onOpenAiWorkspace)
}
useEffect(() => {
const handleThemeIntent = (event: Event) => {
const theme = (event as CustomEvent<unknown>).detail
if (
typeof theme !== 'string'
|| !(GUANGHU_THEMES as readonly string[]).includes(theme)
) return
navigate(route, 'apply-theme', undefined, theme as GuanghuTheme)
}
window.addEventListener(GUANGHU_THEME_INTENT_EVENT, handleThemeIntent)
return () => window.removeEventListener(GUANGHU_THEME_INTENT_EVENT, handleThemeIntent)
}, [navigate, route])
useEffect(() => {
if (
!worldOpen
|| restoreAttempted.current
|| router.state.status !== 'offline'
) return
restoreAttempted.current = true
trackEvent('guanghu_world_session_restore_requested', {
node: 'JD-FD-PRIMARY',
source: 'email-authorized-login',
})
void router.connect()
}, [router, worldOpen])
useEffect(() => {
if (route !== 'servers' || fifthDomainConnection.status !== 'checking') return
const controller = new AbortController()
void fetchFifthDomainDiscovery(fetch, controller.signal).then(discovery => {
setFifthDomainConnection({ status: 'connected', discovery })
trackEvent('fifth_domain_connection_checked', { access: discovery.access, result: 'connected' })
}).catch(() => {
if (controller.signal.aborted) return
setFifthDomainConnection({ status: 'error' })
trackEvent('fifth_domain_connection_checked', { access: 'public-read-only', result: 'error' })
})
return () => controller.abort()
}, [fifthDomainConnection.status, route])
const renderRoute = () => {
if (route === 'world') {
return (
<GuanghuWorldMap
enterprise={enterprise.state}
livingScene={livingPlan.uiProjection.scene}
onEnterFifthDomain={() => navigate(worldOpen ? 'fifth-domain' : 'world-login')}
onOpenLibrary={openArchitecture}
onOpenReceipt={() => navigate('servers')}
onOpenZeroCore={() => navigate('zero-core')}
router={worldRouterState}
shanghai={shanghai.state}
worldOpen={worldOpen}
/>
)
}
if (route === 'world-login') {
return (
<GuanghuWorldLoginGate
locale={locale}
onBack={() => navigate('world')}
onCheck={() => void login.claim()}
onLogin={() => worldOpen ? navigate('fifth-domain') : void login.requestLogin()}
state={login.state}
/>
)
}
if (route === 'zero-core') {
return (
<section className="channel-stage channel-stage--root">
<div className="channel-stage__aurora" aria-hidden />
<div className="channel-root-layout">
<div className="channel-root-hero">
<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>
<div className="channel-root-actions">
<Button className="hololake-home__primary" onClick={() => navigate('fifth-domain')}>{t('hololake.channel.enterFifthDomain')} <span></span></Button>
<Button variant="outline" onClick={() => setReleaseNotesOpen(true)}> 0.4.6 </Button>
</div>
</div>
<aside className="channel-root-map" aria-label="光湖频道概览">
<header><span>LIVE CHANNEL MAP</span><b></b><small></small></header>
<div className="channel-root-map__route">
<Button variant="ghost" onClick={() => navigate('fifth-domain')}><i data-color="blue" /><span><small>01</small></span><b></b></Button>
<Button variant="ghost" onClick={() => navigate('eternal-lake-heart')}><i data-color="violet" /><span><small>02</small></span><b></b></Button>
<Button variant="ghost" onClick={() => navigate('heartbeat-core')}><i data-color="rose" /><span><small>03</small></span><b></b></Button>
</div>
<footer><span><i />线</span><code>HL · ERA / 0.4.6</code></footer>
</aside>
</div>
</section>
)
}
if (route === 'fifth-domain') {
return (
<FifthDomainSystems
onEnterEternalLake={() => navigate('eternal-lake-heart')}
onEnterPufferfish={() => navigate('pufferfish')}
/>
)
}
if (route === 'pufferfish') {
return (
<FifthDomainSystems
detail="pufferfish"
onEnterEternalLake={() => navigate('eternal-lake-heart')}
onEnterPufferfish={() => navigate('pufferfish')}
/>
)
}
if (route === 'eternal-lake-heart') {
return (
<EternalLakeHeartPage
onBackToFifthDomain={() => navigate('fifth-domain')}
onEnterHeartbeat={() => navigate('heartbeat-core')}
onEnterLightLake={() => navigate('light-lake')}
onEnterLoveCore={() => navigate('love-core')}
router={worldRouterState}
/>
)
}
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 === 'education-work-lake') {
return <EducationWorkLake locale={locale} />
}
if (route === 'servers') {
return (
<section className="channel-stage channel-stage--node">
<div className="node-page__heading">
<div>
<p className="channel-stage__eyebrow">FIFTH DOMAIN · ACTIVE PATH</p>
<h1></h1>
<p className="channel-stage__lead"></p>
</div>
<div className="world-presence world-presence--large">
<i />
<span>
<strong>{activeRouterReceipt ? '第五域连接已回执' : '光湖身份已授权'}</strong>
<small>{activeRouterReceipt ? `第五域 · ${activeRouterReceipt.node_id}` : '等待真实节点回执'}</small>
</span>
</div>
</div>
<div className="node-page__content">
<article className="node-page__primary">
<span>{activeRouterReceipt ? '当前世界节点' : '登记目标节点'}</span>
<strong>{activeRouterReceipt?.node_id ?? 'JD-FD-PRIMARY'}</strong>
<p>{activeRouterReceipt ? '第五域连接已由节点回执证明' : '尚未把身份授权误报为节点连接'}</p>
<dl>
<div><dt></dt><dd></dd></div>
<div><dt></dt><dd>{worldOpen ? '已授权' : '未授权'}</dd></div>
<div><dt></dt><dd>{activeRouterReceipt ? '已回执' : '未回执'}</dd></div>
<div><dt></dt><dd>{fifthDomainConnection.status === 'connected' ? '可用' : fifthDomainConnection.status === 'error' ? '失败' : '检查中'}</dd></div>
</dl>
<Button variant="outline" onClick={() => {
setRoute('world')
void router.disconnect()
void login.logout()
}}></Button>
</article>
<GuanghuRouterConsole locale={locale} router={router} />
</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="PERSONA · AI AGENT" title="人格体工作舱" description="与当前频道的人格体持续协作;保留多 Agent、模型选择、权限模式、工具调用、执行回执与历史会话。" action="打开人格体工作舱" onOpen={onOpenAiWorkspace ? openAiWorkspace : undefined} />
<RouteCard eyebrow="INDUSTRY · EDUCATION" title="教育工作湖" description="通过语言指令调出并编辑文档、表格、对比图、仪表盘与知识。第一阶段权限保留在应用内。" action="进入教育工作湖" onOpen={() => navigate('education-work-lake')} />
<RouteCard eyebrow="MODULE · KNOWLEDGE" title={t('hololake.home.moduleKnowledgeTitle')} description={t('hololake.home.moduleKnowledgeDescription')} action={t('hololake.channel.openKnowledge')} onOpen={openKnowledgeBase} />
<RouteCard eyebrow="LOCAL COMPUTER · LIVE FOLDER" title="本地电脑工作区" description="选择电脑上的任意项目文件夹。编程 AI 直接在原文件夹写入页面,光湖实时显示,不再复制到旧笔记库。" action="打开本地文件夹" onOpen={onOpenLocalWorkspace ? openLocalWorkspace : undefined} />
<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
aria-label={t('hololake.home.ariaLabel')}
className="hololake-home"
data-living-connection={livingPlan.uiProjection.scene.connectionEmphasis}
data-living-kernel={livingKernel}
data-living-motion={livingPlan.uiProjection.scene.motion}
data-living-receipt={livingReceiptId}
data-living-server-receipt={livingServerReceiptId}
data-living-scene={livingPlan.uiProjection.scene.depth}
data-living-stars={livingPlan.uiProjection.scene.starDensity}
data-route={route}
data-world-open={worldOpen}
>
<aside className="channel-rail" data-locked="false" aria-label="光湖频道导航">
<div className="channel-rail__heading">
<span>{fifthDomainOpen ? 'FIFTH DOMAIN' : 'HOLOLAKE OS'}</span>
<strong>{fifthDomainOpen ? '第五域' : 'HoloLake'}</strong>
<small>{worldOpen ? fifthDomainOpen ? '身份已授权 · 节点状态看回执' : '系统能力与光湖世界入口' : '本地系统可用 · 第五域需授权'}</small>
</div>
<nav>
{!fifthDomainOpen && <>
<Button data-active={route === 'world'} variant="ghost" onClick={() => navigate('world')}><i data-color="cyan" /></Button>
<Button variant="ghost" onClick={openKnowledgeBase}><i data-color="blue" /></Button>
<Button disabled={!onOpenAiWorkspace} variant="ghost" onClick={openAiWorkspace}><i data-color="violet" />Agent </Button>
<Button data-active={route === 'education-work-lake'} variant="ghost" onClick={() => navigate('education-work-lake')}><i data-color="green" /></Button>
<Button disabled={!onOpenLocalWorkspace} variant="ghost" onClick={openLocalWorkspace}><i data-color="amber" /></Button>
</>}
{fifthDomainOpen && <>
<Button variant="ghost" onClick={() => navigate('world')}><i data-color="cyan" /></Button>
<Button data-active={route === 'fifth-domain'} variant="ghost" onClick={() => navigate('fifth-domain')}><i data-color="blue" /></Button>
<Button data-active={route === 'pufferfish'} variant="ghost" onClick={() => navigate('pufferfish')}><i data-color="green" /></Button>
<Button data-active={route === 'zero-core'} variant="ghost" onClick={() => navigate('zero-core')}><i data-color="cyan" />{t('hololake.channel.zeroCoreTitle')}</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>
{fifthDomainOpen
? <div className="channel-rail__modules"><span></span><b></b><b> AI</b><b></b></div>
: <div className="channel-rail__modules"><span></span></div>}
</aside>
<div className="channel-workspace">
<div
aria-live="polite"
className="living-system-status"
data-state={livingKernel}
role="status"
>
<i aria-hidden />
<span>
{livingKernel === 'planning'
? '活系统正在适配'
: livingKernel === 'server'
? '服务器模型已回执'
: livingKernel === 'model'
? '本地模型已适配'
: '安全执行路径'}
</span>
<small>{livingKernel === 'server' ? '真实节点' : livingKernel === 'planning' ? '请稍候' : '可继续操作'}</small>
</div>
{worldOpen && route !== 'world-login' ? <>
<header className="world-session-bar">
<div className="world-presence">
<i />
<span>
<strong>{activeRouterReceipt ? '第五域连接已回执' : '光湖身份已授权'}</strong>
<small>
{activeRouterReceipt
? `第五域 · ${activeRouterReceipt.node_id}`
: fifthDomainOpen
? '域内界面已打开 · 节点未回执'
: '世界入口 · 等待域跳转'}
</small>
</span>
</div>
<div className="world-session-bar__actions">
{fifthDomainOpen && onOpenAiWorkspace
? <Button className="world-session-bar__ai" variant="ghost" size="sm" onClick={openAiWorkspace}></Button>
: null}
<Button variant="ghost" size="sm" onClick={() => navigate(fifthDomainOpen ? 'servers' : 'world')}>{fifthDomainOpen ? '查看连接凭证' : '世界总图'}</Button>
</div>
</header>
{route !== 'world' && <nav className="channel-breadcrumb" aria-label={t('hololake.channel.breadcrumbLabel')}>
<Button variant="ghost" size="sm" onClick={() => navigate('world')}></Button><span></span>
<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', 'pufferfish'].includes(route) && <><span></span><Button variant="ghost" size="sm" onClick={() => navigate('eternal-lake-heart')}>{t('hololake.channel.eternalLakeTitle')}</Button></>}
{route === 'pufferfish' && <><span></span><Button variant="ghost" size="sm" onClick={() => navigate('pufferfish')}></Button></>}
</nav>}
{renderRoute()}
</> : renderRoute()}
</div>
{worldOpen && <nav className="channel-mobile-nav" aria-label="手机频道导航">
<Button data-active={route === 'world'} variant="ghost" onClick={() => navigate('world')}><span></span><small></small></Button>
<Button data-active={route === 'fifth-domain'} variant="ghost" onClick={() => navigate('fifth-domain')}><span></span><small></small></Button>
{fifthDomainOpen && <>
<Button data-active={route === 'heartbeat-core'} variant="ghost" onClick={() => navigate('heartbeat-core')}><span></span><small></small></Button>
<Button data-active={route === 'light-lake'} variant="ghost" onClick={() => navigate('light-lake')}><span></span><small></small></Button>
<Button data-active={route === 'servers'} variant="ghost" onClick={() => navigate('servers')}><span></span><small></small></Button>
</>}
</nav>}
<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>
<Dialog open={releaseNotesOpen} onOpenChange={setReleaseNotesOpen}>
<DialogContent aria-label="HoloLake Era 内测版 0.4.6 更新说明">
<DialogHeader>
<DialogTitle>HoloLake Era 0.4.6 · </DialogTitle>
<DialogDescription> AI HLDP </DialogDescription>
</DialogHeader>
<div className="space-y-4 text-sm leading-6">
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">线</p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">使</p>
</section>
<section>
<h3 className="font-semibold"> Agent</h3>
<p className="text-muted-foreground">AI </p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">AI HTTPS 访</p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground"></p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground"> AI </p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground"></p>
</section>
<section>
<h3 className="font-semibold"> API </h3>
<p className="text-muted-foreground"> OpenAI DeepSeekQwenGeminiOpenRouterAnthropicOllama LM Studio 线</p>
</section>
<section>
<h3 className="font-semibold">HLDP </h3>
<p className="text-muted-foreground">HLDP </p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground"> Agent HLDP </p>
</section>
<section>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">沿</p>
</section>
<p className="rounded-md bg-muted p-3 text-muted-foreground"><strong className="text-foreground"></strong> Agent Agent</p>
</div>
</DialogContent>
</Dialog>
</main>
)
}