From 3ff80d583a2b63835ef6d9cbc670f3b1041c580d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=9C=94?= <565183519@qq.com> Date: Tue, 4 Aug 2026 11:15:45 +0800 Subject: [PATCH] fix(product): restore HoloLake world entry hierarchy --- .../hololake-platform/src/App.test.tsx | 12 ++--- product-source/hololake-platform/src/App.tsx | 12 +++-- .../src/components/FifthDomainSystems.tsx | 29 ++++++++---- .../src/components/GuanghuWorldLoginGate.tsx | 35 +++++++++----- .../src/components/HoloLakeHome.test.tsx | 47 ++++++++++++++++++- .../src/components/HoloLakeHome.tsx | 3 ++ 6 files changed, 105 insertions(+), 33 deletions(-) diff --git a/product-source/hololake-platform/src/App.test.tsx b/product-source/hololake-platform/src/App.test.tsx index f495a9a..7ff056e 100644 --- a/product-source/hololake-platform/src/App.test.tsx +++ b/product-source/hololake-platform/src/App.test.tsx @@ -762,7 +762,7 @@ describe('App', () => { }) }) - it('shows the external AI setup dialog from the menu when AI onboarding is active', async () => { + it('keeps external AI setup available from the menu without making it startup', async () => { localStorage.removeItem(AI_AGENTS_ONBOARDING_DISMISSED_STORAGE_NAME) localStorage.removeItem(CLAUDE_CODE_ONBOARDING_DISMISSED_STORAGE_NAME) mockCommandResults.get_ai_agents_status = { @@ -777,8 +777,9 @@ describe('App', () => { render() await waitFor(() => { - expect(screen.getByText('AI is ready')).toBeInTheDocument() + expect(screen.getByText('All Notes')).toBeInTheDocument() }, { timeout: SLOW_APP_READY_TIMEOUT_MS }) + expect(screen.queryByText('AI is ready')).not.toBeInTheDocument() await waitFor(() => { expect(typeof window.__laputaTest?.dispatchBrowserMenuCommand).toBe('function') @@ -792,7 +793,6 @@ describe('App', () => { expect(screen.getByText('Manage External AI Tools')).toBeInTheDocument() }) expect(screen.getByTestId('mcp-setup-dialog')).toBeInTheDocument() - expect(screen.queryByText('AI is ready')).not.toBeInTheDocument() }) it('routes right-panel AI chat messages to the selected default agent', async () => { @@ -903,7 +903,7 @@ describe('App', () => { expect(screen.getByTestId('welcome-open-folder')).toHaveTextContent('Open existing vault') }) - it('persists an existing vault and shows AI onboarding after first-run open', async () => { + it('persists an existing vault without putting optional AI setup in front of HoloLake', async () => { const selectedVaultPath = '/Users/mock/Documents/Work Vault' const saveVaultList = vi.fn() const promptSpy = vi.spyOn(window, 'prompt').mockReturnValue('file:///Users/mock/Documents/Work%20Vault') @@ -947,9 +947,9 @@ describe('App', () => { expect(saveVaultList).toHaveBeenCalledTimes(1) await waitFor(() => { - expect(screen.getByTestId('ai-agents-onboarding-screen')).toBeInTheDocument() + expect(screen.getByText('All Notes')).toBeInTheDocument() }, { timeout: SLOW_APP_READY_TIMEOUT_MS }) - expect(screen.getByText('AI setup is optional')).toBeInTheDocument() + expect(screen.queryByTestId('ai-agents-onboarding-screen')).not.toBeInTheDocument() promptSpy.mockRestore() }) diff --git a/product-source/hololake-platform/src/App.tsx b/product-source/hololake-platform/src/App.tsx index bf27495..7690453 100644 --- a/product-source/hololake-platform/src/App.tsx +++ b/product-source/hololake-platform/src/App.tsx @@ -1424,7 +1424,9 @@ function MainApp({ shouldResumeFreshStartOnboarding, shouldShowStartupScreen, } = useStartupScreenState({ - aiAgentsPromptVisible: aiAgentsOnboarding.showPrompt, + // HoloLake is the human entry into the world. Optional Agent setup belongs + // inside the Agent workspace/settings and must never replace that entry. + aiAgentsPromptVisible: false, isNoteWindow: Boolean(noteWindowParams) || aiWorkspaceWindow, onboardingState: onboarding.state, runtimeMissingVaultPath, @@ -1794,10 +1796,10 @@ function MainApp({ -
- handleSetSelection({ kind: 'filter', filter: 'changes' })} onClickPulse={() => handleSetSelection({ kind: 'filter', filter: 'pulse' })} onCommitPush={handleCommitPush} commitActionPending={commitFlow.isOpeningCommitDialog} gitFeaturesEnabled={gitFeaturesEnabled} onInitializeGit={openGitSetupDialog} isOffline={networkStatus.isOffline} isGitVault={isGitVault} isVaultReloading={vault.isReloading || isVaultContentLoading} syncStatus={autoSync.syncStatus} lastSyncTime={autoSync.lastSyncTime} conflictCount={autoSync.conflictFiles.length} remoteStatus={autoSync.remoteStatus} repositories={gitRepositories} selectedRepositoryPath={gitSurfaces.syncRepositoryPath} onRepositoryChange={gitSurfaces.setSyncRepositoryPath} onTriggerSync={handlePullSelectedRepository} onPullAndPush={handlePullAndPushSelectedRepository} onOpenConflictResolver={conflictFlow.handleOpenConflictResolver} zoomLevel={zoom.zoomLevel} themeMode={documentThemeMode} onZoomReset={zoom.zoomReset} onToggleThemeMode={settingsLoaded ? handleToggleThemeMode : undefined} buildNumber={buildNumber} onCheckForUpdates={handleCheckForUpdates} onRemoveVault={vaultSwitcher.removeVault} onReorderVaults={vaultSwitcher.reorderVaults} onUpdateWorkspaceIdentity={vaultSwitcher.updateWorkspaceIdentity} aiFeaturesEnabled={aiFeaturesEnabled} mcpStatus={mcpSetupDialog.status} onInstallMcp={mcpSetupDialog.openDialog} locale={appLocale} /> -
- {aiFeaturesEnabled && !effectiveShowAIChat ? ( + {!showHoloLakeHome ? ( + handleSetSelection({ kind: 'filter', filter: 'changes' })} onClickPulse={() => handleSetSelection({ kind: 'filter', filter: 'pulse' })} onCommitPush={handleCommitPush} commitActionPending={commitFlow.isOpeningCommitDialog} gitFeaturesEnabled={gitFeaturesEnabled} onInitializeGit={openGitSetupDialog} isOffline={networkStatus.isOffline} isGitVault={isGitVault} isVaultReloading={vault.isReloading || isVaultContentLoading} syncStatus={autoSync.syncStatus} lastSyncTime={autoSync.lastSyncTime} conflictCount={autoSync.conflictFiles.length} remoteStatus={autoSync.remoteStatus} repositories={gitRepositories} selectedRepositoryPath={gitSurfaces.syncRepositoryPath} onRepositoryChange={gitSurfaces.setSyncRepositoryPath} onTriggerSync={handlePullSelectedRepository} onPullAndPush={handlePullAndPushSelectedRepository} onOpenConflictResolver={conflictFlow.handleOpenConflictResolver} zoomLevel={zoom.zoomLevel} themeMode={documentThemeMode} onZoomReset={zoom.zoomReset} onToggleThemeMode={settingsLoaded ? handleToggleThemeMode : undefined} buildNumber={buildNumber} onCheckForUpdates={handleCheckForUpdates} onRemoveVault={vaultSwitcher.removeVault} onReorderVaults={vaultSwitcher.reorderVaults} onUpdateWorkspaceIdentity={vaultSwitcher.updateWorkspaceIdentity} aiFeaturesEnabled={aiFeaturesEnabled} mcpStatus={mcpSetupDialog.status} onInstallMcp={mcpSetupDialog.openDialog} locale={appLocale} /> + ) : null} + {!showHoloLakeHome && aiFeaturesEnabled && !effectiveShowAIChat ? ( void onEnterPufferfish: () => void } @@ -18,6 +19,7 @@ const PUFFERFISH_FACTS = { export function FifthDomainSystems({ detail = 'overview', + evidenceMode = 'receipt', onEnterEternalLake, onEnterPufferfish, }: FifthDomainSystemsProps) { @@ -26,6 +28,9 @@ export function FifthDomainSystems({ ) const pufferfishSelected = selected === 'pufferfish' const eternalLakeSelected = selected === 'eternal-lake-heart' + const isVisualPreview = evidenceMode === 'visual-preview' + const evidenceLabel = isVisualPreview ? '本地拓扑预览' : '真实接入' + const nodeState = isVisualPreview ? '未实时核验' : '在线回执' return (
@@ -38,7 +43,7 @@ export function FifthDomainSystems({ : '不同人类系统通过各自真实服务器接入,同时保持系统归属与主权边界。'}

- 真实接入 + {evidenceLabel}
@@ -53,7 +58,7 @@ export function FifthDomainSystems({
@@ -61,14 +66,14 @@ export function FifthDomainSystems({ 大脑服务器 新加坡大脑服务器 {PUFFERFISH_FACTS.brain} - 在线 + {nodeState}
执行节点 苍耳家庭 Ubuntu {PUFFERFISH_FACTS.node} - 已登记 + {isVisualPreview ? '本地登记' : '登记回执'}
@@ -86,7 +91,7 @@ export function FifthDomainSystems({
@@ -94,7 +99,7 @@ export function FifthDomainSystems({ 接入节点 第五域国内主控 JD-FD-PRIMARY - 在线回执 + {nodeState}
@@ -116,12 +121,12 @@ export function FifthDomainSystems({ ) : <>

{pufferfishSelected ? '胖头鱼语言子系统' : '永恒湖心系统'}

- 真实接入 + {evidenceLabel}
{pufferfishSelected ? ( <>
-
系统状态
真实接入
+
系统状态
{evidenceLabel}
大脑服务器
{PUFFERFISH_FACTS.brain}
执行节点
{PUFFERFISH_FACTS.node}
节点编号
{PUFFERFISH_FACTS.nodeId}
@@ -135,7 +140,7 @@ export function FifthDomainSystems({ ) : ( <>
-
系统状态
真实接入
+
系统状态
{evidenceLabel}
入口节点
JD-FD-PRIMARY
系统归属
冰朔个人系统
下一频道
心跳核心频道
@@ -144,7 +149,11 @@ export function FifthDomainSystems({ )} } -
拓扑信息来自本地系统登记与真实路由事实。
+
+ {isVisualPreview + ? '本页仅展示本地登记拓扑,不能证明节点在线或人格体已恢复。' + : '拓扑信息来自本地登记;在线状态仅以当前会话回执为准。'} +
diff --git a/product-source/hololake-platform/src/components/GuanghuWorldLoginGate.tsx b/product-source/hololake-platform/src/components/GuanghuWorldLoginGate.tsx index 75b10db..96a7a7a 100644 --- a/product-source/hololake-platform/src/components/GuanghuWorldLoginGate.tsx +++ b/product-source/hololake-platform/src/components/GuanghuWorldLoginGate.tsx @@ -44,7 +44,7 @@ const domains: Array<{ description: '光湖世界公共航道与跨域共识入口。', id: 'main', node: 'GL-MAIN', - status: '稳定运行', + status: '等待公共回执', subtitle: '稳定的远洋星系', title: '光湖主域', }, @@ -53,7 +53,7 @@ const domains: Array<{ description: '世界规则、协议与系统校准所在的基础域。', id: 'zero', node: 'GL-ZERO', - status: '已登记', + status: '等待登记回执', subtitle: '精密校准环', title: '光湖零域', }, @@ -62,7 +62,7 @@ const domains: Array<{ description: '由多个真实节点共同组成的协作星群。', id: 'sub', node: 'GL-SUB', - status: '联接中', + status: '等待连接回执', subtitle: '联接的岛屿星群', title: '光湖分域', }, @@ -71,7 +71,7 @@ const domains: Array<{ description: '冰朔的独立语言域,从这里归航至永恒湖心。', id: 'fifth', node: 'JD-FD-PRIMARY', - status: '可授权登录', + status: '可发起授权', subtitle: '深空湖心星系', title: '第五域', }, @@ -86,6 +86,13 @@ const domains: Array<{ }, ] +function loginErrorCopy(error: string): string { + if (error === 'guanghu_world_login_desktop_runtime_required') { + return '当前是本地视觉预览,未向第五域服务器发送登录请求。请从已签名的 HoloLake 桌面端进入。' + } + return error +} + export function GuanghuWorldLoginGate({ locale, onBack, @@ -108,6 +115,12 @@ export function GuanghuWorldLoginGate({ : waiting || requesting ? '授权处理中' : '访客入口 · 未验证' + const isVisualPreview = state.workorderId === 'local-visual-preview' + const worldState = isVisualPreview + ? ['本地视觉预览', '未连接真实服务器'] + : state.phase === 'online' + ? ['世界入口:已验证回执', '语言共识层:已连接'] + : ['世界入口:等待验证', '语言共识层:未建立连接'] const selectDomain = (id: DomainId) => { setSelectedDomainId(id) @@ -125,8 +138,8 @@ export function GuanghuWorldLoginGate({ HoloLake Era · 光湖 OS
- 世界状态:稳定 - 语言共识层:已同步 + {worldState[0]} + {worldState[1]}
@@ -237,7 +250,7 @@ export function GuanghuWorldLoginGate({ {state.error ? (
{t('hololake.login.errorTitle')} - {state.error} + {loginErrorCopy(state.error)} @@ -259,10 +272,10 @@ export function GuanghuWorldLoginGate({
- 世界实况 -

光湖分域 · 涧语群岛新航道「涧光径」完成共识校准

-

光湖零感域 · 安泊港港区协同协议更新至 v7.3

-

第五域 · 星洞回廊回声观测站记录到罕见语义涟漪

+ 世界导览 +

光湖分域 · 涧语群岛视觉叙事 · 非实时状态

+

光湖零感域 · 安泊港等待企业节点接入回执

+

第五域 · 星洞回廊授权后读取真实域内状态

{state.nodeId}
diff --git a/product-source/hololake-platform/src/components/HoloLakeHome.test.tsx b/product-source/hololake-platform/src/components/HoloLakeHome.test.tsx index 58f8766..9f77f05 100644 --- a/product-source/hololake-platform/src/components/HoloLakeHome.test.tsx +++ b/product-source/hololake-platform/src/components/HoloLakeHome.test.tsx @@ -170,7 +170,7 @@ describe('HoloLakeHome', () => { fireEvent.click(screen.getByRole('button', { name: /验证并进入第五域/ })) expect(screen.getByRole('button', { name: /冰朔,访客入口 · 未验证/ })).toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: /光湖主域.*稳定运行/ })) + fireEvent.click(screen.getByRole('button', { name: /光湖主域.*等待公共回执/ })) expect(screen.getByRole('heading', { name: '光湖主域' })).toBeInTheDocument() expect(screen.getByRole('button', { name: '当前入口尚未开放' })).toBeDisabled() expect(screen.queryByRole('button', { name: /给我发送授权链接/ })).not.toBeInTheDocument() @@ -361,6 +361,24 @@ describe('HoloLakeHome', () => { expect(screen.getByRole('heading', { name: '心跳核心频道' })).toBeInTheDocument() }) + it('labels the forced online browser route as a visual preview, not a live receipt', () => { + useGuanghuWorldLoginMock.mockReturnValue(worldLogin('online', { + state: { + ...worldLogin('online').state, + navigation: { domains: 5, map_hash: 'local-visual-preview' }, + workorderId: 'local-visual-preview', + }, + })) + + render() + fireEvent.click(screen.getByRole('button', { name: /进入第五域/ })) + + expect(screen.getAllByText('本地拓扑预览').length).toBeGreaterThan(0) + expect(screen.getAllByText('未实时核验').length).toBeGreaterThan(0) + expect(screen.getByText('本页仅展示本地登记拓扑,不能证明节点在线或人格体已恢复。')).toBeInTheDocument() + expect(screen.queryByText('在线回执')).not.toBeInTheDocument() + }) + it('shows receipt-backed Eternal Lake information without invented resonance scores', () => { useGuanghuRouterMock.mockReturnValue(onlineRouter()) @@ -505,6 +523,33 @@ describe('HoloLakeHome', () => { expect(screen.getByRole('button', { name: '重新尝试' })).toBeInTheDocument() }) + it('translates a browser preview limitation into human recovery guidance', () => { + useGuanghuWorldLoginMock.mockReturnValue(worldLogin('error', { + state: { + ...worldLogin('error').state, + error: 'guanghu_world_login_desktop_runtime_required', + }, + })) + + render() + + fireEvent.click(screen.getByRole('button', { name: /验证并进入第五域/ })) + expect(screen.getByRole('alert')).toHaveTextContent('当前是本地视觉预览,未向第五域服务器发送登录请求。') + expect(screen.getByRole('alert')).toHaveTextContent('请从已签名的 HoloLake 桌面端进入。') + expect(screen.queryByText('guanghu_world_login_desktop_runtime_required')).not.toBeInTheDocument() + }) + + it('does not claim a live world connection before a receipt exists', () => { + useGuanghuWorldLoginMock.mockReturnValue(worldLogin('idle')) + + render() + + fireEvent.click(screen.getByRole('button', { name: /验证并进入第五域/ })) + expect(screen.getByText('世界入口:等待验证')).toBeInTheDocument() + expect(screen.getByText('语言共识层:未建立连接')).toBeInTheDocument() + expect(screen.queryByText('世界状态:稳定')).not.toBeInTheDocument() + }) + it('opens the public development repository from the entered Heartbeat Core channel', () => { useGuanghuRouterMock.mockReturnValue(onlineRouter()) diff --git a/product-source/hololake-platform/src/components/HoloLakeHome.tsx b/product-source/hololake-platform/src/components/HoloLakeHome.tsx index e074fe8..fc4e0a9 100644 --- a/product-source/hololake-platform/src/components/HoloLakeHome.tsx +++ b/product-source/hololake-platform/src/components/HoloLakeHome.tsx @@ -115,6 +115,7 @@ export function HoloLakeHome({ const restoreAttempted = useRef(false) const livingRequestSequence = useRef(0) const worldOpen = login.state.phase === 'online' + const isVisualPreview = login.state.workorderId === 'local-visual-preview' const fifthDomainOpen = worldOpen && route !== 'world' const worldRouterState = worldOpen && router.state.status !== 'online' ? { @@ -325,6 +326,7 @@ export function HoloLakeHome({ if (route === 'fifth-domain') { return ( navigate('eternal-lake-heart')} onEnterPufferfish={() => navigate('pufferfish')} /> @@ -335,6 +337,7 @@ export function HoloLakeHome({ return ( navigate('eternal-lake-heart')} onEnterPufferfish={() => navigate('pufferfish')} />