feat: drive public domain vestibules from validated manifests
This commit is contained in:
parent
7a90fad360
commit
2c50b28557
7 changed files with 297 additions and 40 deletions
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"schema": "gh-aios.hololake-local-stage-receipt/v1",
|
||||||
|
"receipt_id": "GH-HOLOLAKE-PUBLIC-VESTIBULE-DIRECTORY-20260810-001",
|
||||||
|
"result": "PASS_100_LOCAL_PUBLIC_VESTIBULE_DIRECTORY",
|
||||||
|
"development_id": "DEV-20260809-007",
|
||||||
|
"observed_at": "2026-08-10T05:27:00+08:00",
|
||||||
|
"source": {
|
||||||
|
"repository_id": "REPO-014",
|
||||||
|
"base_commit": "7a90fad360085ae4779179df194a7296301c5bf5",
|
||||||
|
"branch": "main"
|
||||||
|
},
|
||||||
|
"public_directory": {
|
||||||
|
"domain_count": 5,
|
||||||
|
"official_order": ["光湖主域", "光湖分域", "光湖零域", "光湖零感域", "第五域 · 光湖本源域"],
|
||||||
|
"enterprise_theme_state": "DOMAIN_TEAM_OWNED_UNPUBLISHED",
|
||||||
|
"fifth_domain_theme_state": "LAKE_REFLECTS_STARS_SELECTED_REFERENCE",
|
||||||
|
"private_resources_exposed": false,
|
||||||
|
"supported_node_types": ["local-terminal", "cloud-resident"]
|
||||||
|
},
|
||||||
|
"acceptance": {
|
||||||
|
"single_validated_public_directory": true,
|
||||||
|
"official_zero_domain_name_fixed": true,
|
||||||
|
"first_four_theme_ownership_preserved": true,
|
||||||
|
"fifth_domain_selected_reference_preserved": true,
|
||||||
|
"public_lighthouse_kept_visually_distinct": true,
|
||||||
|
"runtime_entry_gates_unchanged": true
|
||||||
|
},
|
||||||
|
"verification": {
|
||||||
|
"desktop_test_count": 55,
|
||||||
|
"desktop_test_failures": 0,
|
||||||
|
"production_build": "PASS",
|
||||||
|
"packaged_arm64_app": "PASS",
|
||||||
|
"codesign_deep_strict": "PASS",
|
||||||
|
"installed_binary_sha256": "f1e5474262bf0c64ceec3f912d8b4e9995dfa73612ca3193da93c1508d1709f8",
|
||||||
|
"installed_app_asar_sha256": "fd35a09ac2aff572c83f698a09be9c00d7a53096d7732f202f961cc937b6ceeb",
|
||||||
|
"current_ui_screenshot_sha256": "375a06d805d6ee31a7499bfef0c922229dc1ec6cfa0169ef61251d7189d29f18"
|
||||||
|
},
|
||||||
|
"boundaries": [
|
||||||
|
"These are built-in public vestibule manifests, not signed runtime domain manifests.",
|
||||||
|
"The first four domain themes remain unpublished until their responsible teams provide them.",
|
||||||
|
"The selected Fifth Domain reference is not yet proof of an independently loaded domain runtime theme.",
|
||||||
|
"No private domain resource was loaded before login.",
|
||||||
|
"This receipt does not claim server deployment, runtime health, persona birth or notarization."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -13,9 +13,10 @@ import { ModuleLibrarySheet } from './components/ModuleLibrarySheet';
|
||||||
import { cleanDisplayText } from './presentation';
|
import { cleanDisplayText } from './presentation';
|
||||||
import { WorldEntry } from './components/WorldEntry';
|
import { WorldEntry } from './components/WorldEntry';
|
||||||
import { DomainConnectionSheet } from './components/DomainConnectionSheet';
|
import { DomainConnectionSheet } from './components/DomainConnectionSheet';
|
||||||
|
import type { DomainRouteId } from './public-domain-directory';
|
||||||
|
|
||||||
type View = 'editor' | 'history';
|
type View = 'editor' | 'history';
|
||||||
type RouteId = 'fifth' | 'main' | 'sub' | 'zero' | 'zero-sense';
|
type RouteId = DomainRouteId;
|
||||||
type ModuleId = 'knowledge' | 'education';
|
type ModuleId = 'knowledge' | 'education';
|
||||||
|
|
||||||
interface RepositoryStatus {
|
interface RepositoryStatus {
|
||||||
|
|
@ -65,14 +66,6 @@ function findFirstDocument(nodes: DocTreeNode[]): string | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const routeCopy: Record<RouteId, { title: string; body: string }> = {
|
|
||||||
fifth: { title: '我的第五域', body: '当前账号的个人第五域频道。模块在这里按需挂载、收起和组合。' },
|
|
||||||
main: { title: '光湖主域', body: '公共产品事实、发布与公告入口。当前客户端尚未取得该域的操作权限。' },
|
|
||||||
sub: { title: '光湖分域', body: '行业入口与初始化频道目录。教育行业与网文行业从这里进入。' },
|
|
||||||
zero: { title: '光湖零域', body: '实验、模块试装、对比与质量验证入口。' },
|
|
||||||
'zero-sense': { title: '光湖零感域', body: '治理、部署审批、事故、回滚与审计入口。' },
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const agentApiBase = window.location.protocol === 'file:' ? 'http://127.0.0.1:3890' : '';
|
const agentApiBase = window.location.protocol === 'file:' ? 'http://127.0.0.1:3890' : '';
|
||||||
const [tree, setTree] = useState<DocTreeNode[]>([]);
|
const [tree, setTree] = useState<DocTreeNode[]>([]);
|
||||||
|
|
@ -339,7 +332,6 @@ export default function App() {
|
||||||
}
|
}
|
||||||
}, [openDoc, refreshTree]);
|
}, [openDoc, refreshTree]);
|
||||||
|
|
||||||
const activeRouteCopy = routeCopy[activeRoute];
|
|
||||||
const currentTitle = cleanDisplayText(currentDoc?.meta.title || '知识库');
|
const currentTitle = cleanDisplayText(currentDoc?.meta.title || '知识库');
|
||||||
const knowledgeState = channelState?.modules.find(module => module.id === 'HL-MOD-KNOWLEDGE-001');
|
const knowledgeState = channelState?.modules.find(module => module.id === 'HL-MOD-KNOWLEDGE-001');
|
||||||
const knowledgeInstalled = knowledgeState?.installed !== false;
|
const knowledgeInstalled = knowledgeState?.installed !== false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { getPublicDomainVestibule, type DomainRouteId } from '../public-domain-directory';
|
||||||
|
|
||||||
type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense';
|
type DomainId = Exclude<DomainRouteId, 'fifth'>;
|
||||||
|
|
||||||
interface DomainEntry {
|
interface DomainEntry {
|
||||||
id: DomainId;
|
id: DomainId;
|
||||||
|
|
@ -92,15 +93,15 @@ export function DomainSurface({ activeDomain, educationSelected = false, onOpenS
|
||||||
|
|
||||||
const entry = registry?.domains.find(domain => domain.id === activeDomain);
|
const entry = registry?.domains.find(domain => domain.id === activeDomain);
|
||||||
const liveReadOnly = entry?.live?.access_state === 'ONLINE_READ_ONLY';
|
const liveReadOnly = entry?.live?.access_state === 'ONLINE_READ_ONLY';
|
||||||
const fallbackName = activeDomain === 'main' ? '光湖主域' : activeDomain === 'sub' ? '光湖分域' : activeDomain === 'zero' ? '光湖零域' : '光湖零感域';
|
const publicVestibule = getPublicDomainVestibule(activeDomain);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="domain-surface">
|
<main className="domain-surface">
|
||||||
<header className="domain-surface-header">
|
<header className="domain-surface-header">
|
||||||
<div>
|
<div>
|
||||||
<small>{entry?.number || '企业四域公共入口'}</small>
|
<small>{entry?.number || publicVestibule.hldpNumber}</small>
|
||||||
<h1>{educationSelected ? '教育行业' : entry?.name || fallbackName}</h1>
|
<h1>{educationSelected ? '教育行业' : entry?.name || publicVestibule.displayName}</h1>
|
||||||
<p>{educationSelected ? '光湖分域中的首个行业原型。模块进入个人频道后仍由个人服务器承载数据。' : entry?.responsibility || '读取企业灯塔后显示当前域职责与入口状态。'}</p>
|
<p>{educationSelected ? '光湖分域中的首个行业原型。模块进入个人频道后仍由个人服务器承载数据。' : entry?.responsibility || publicVestibule.responsibility}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className={`domain-connection ${registry?.verified ? 'online' : ''}`}>
|
<div className={`domain-connection ${registry?.verified ? 'online' : ''}`}>
|
||||||
<span aria-hidden="true" />
|
<span aria-hidden="true" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
|
import { Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
|
||||||
|
import { getPublicDomainVestibule, publicDomainDirectory, type DomainRouteId } from '../public-domain-directory';
|
||||||
type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
access: { blockers: string[]; runtimeReady: boolean; stage: 'checking' | 'login-required' | 'identity-verified' | 'runtime-ready' };
|
access: { blockers: string[]; runtimeReady: boolean; stage: 'checking' | 'login-required' | 'identity-verified' | 'runtime-ready' };
|
||||||
|
|
@ -10,19 +9,12 @@ interface Props {
|
||||||
onOpenConnection: () => void;
|
onOpenConnection: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domains: Array<{ id: DomainId; number: string; name: string; summary: string }> = [
|
|
||||||
{ id: 'main', number: '01', name: '光湖主域', summary: '公共产品事实、发布与公告入口' },
|
|
||||||
{ id: 'sub', number: '02', name: '光湖分域', summary: '行业入口与初始化频道目录' },
|
|
||||||
{ id: 'zero', number: '03', name: '光湖零域', summary: '实验、模块试装与质量验证入口' },
|
|
||||||
{ id: 'zero-sense', number: '04', name: '光湖零感域', summary: '治理、审批、回滚与审计入口' },
|
|
||||||
{ id: 'fifth', number: '05', name: '第五域 · 光湖本源域', summary: '零点原核的工程本体 · 语言架构层' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace, onOpenConnection }: Props) {
|
export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace, onOpenConnection }: Props) {
|
||||||
const [stage, setStage] = useState<'lighthouse' | 'directory' | 'fifth'>('lighthouse');
|
const [stage, setStage] = useState<'lighthouse' | 'directory' | 'fifth'>('lighthouse');
|
||||||
const [selectedDomain, setSelectedDomain] = useState<DomainId | null>(null);
|
const [selectedDomain, setSelectedDomain] = useState<DomainRouteId | null>(null);
|
||||||
|
const selectedVestibule = selectedDomain ? getPublicDomainVestibule(selectedDomain) : null;
|
||||||
|
|
||||||
const selectDomain = (id: DomainId) => {
|
const selectDomain = (id: DomainRouteId) => {
|
||||||
setSelectedDomain(id);
|
setSelectedDomain(id);
|
||||||
if (id === 'fifth') setStage('fifth');
|
if (id === 'fifth') setStage('fifth');
|
||||||
};
|
};
|
||||||
|
|
@ -40,15 +32,15 @@ export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace,
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<nav className="fifth-domain-rail" aria-label="五域公开导航">
|
<nav className="fifth-domain-rail" aria-label="五域公开导航">
|
||||||
{domains.map(domain => (
|
{publicDomainDirectory.map(domain => (
|
||||||
<button
|
<button
|
||||||
key={domain.id}
|
key={domain.routeId}
|
||||||
type="button"
|
type="button"
|
||||||
className={domain.id === 'fifth' ? 'selected' : ''}
|
className={domain.routeId === 'fifth' ? 'selected' : ''}
|
||||||
onClick={() => domain.id === 'fifth' ? undefined : (setStage('directory'), setSelectedDomain(domain.id))}
|
onClick={() => domain.routeId === 'fifth' ? undefined : (setStage('directory'), setSelectedDomain(domain.routeId))}
|
||||||
>
|
>
|
||||||
<span>{domain.number}</span>
|
<span>{domain.directoryNumber}</span>
|
||||||
<strong>{domain.name}</strong>
|
<strong>{domain.displayName}</strong>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -95,20 +87,28 @@ export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace,
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<nav className="public-domain-strip" aria-label="五域公开门厅">
|
<nav className="public-domain-strip" aria-label="五域公开门厅">
|
||||||
{domains.map(domain => (
|
{publicDomainDirectory.map(domain => (
|
||||||
<button
|
<button
|
||||||
key={domain.id}
|
key={domain.routeId}
|
||||||
type="button"
|
type="button"
|
||||||
className={selectedDomain === domain.id ? 'selected' : ''}
|
className={selectedDomain === domain.routeId ? 'selected' : ''}
|
||||||
onClick={() => selectDomain(domain.id)}
|
onClick={() => selectDomain(domain.routeId)}
|
||||||
>
|
>
|
||||||
<span>{domain.number}</span>
|
<span>{domain.directoryNumber}</span>
|
||||||
<strong>{domain.name}</strong>
|
<strong>{domain.displayName}</strong>
|
||||||
{stage === 'directory' && <small>{domain.summary}</small>}
|
{stage === 'directory' && <small>{domain.publicSummary}</small>}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
{stage === 'directory' && selectedVestibule && (
|
||||||
|
<aside className="public-vestibule-fact" aria-live="polite">
|
||||||
|
<strong>{selectedVestibule.stableDomainId}</strong>
|
||||||
|
<span>{selectedVestibule.themeLabel}</span>
|
||||||
|
<small>仅公开门厅资料 · 私有资源未装载</small>
|
||||||
|
</aside>
|
||||||
|
)}
|
||||||
|
|
||||||
<footer className="public-entry-footer">支持本地终端节点与云端常驻节点</footer>
|
<footer className="public-entry-footer">支持本地终端节点与云端常驻节点</footer>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { assertPublicDomainDirectory, getPublicDomainVestibule, publicDomainDirectory, type PublicDomainVestibule } from './public-domain-directory.js';
|
||||||
|
|
||||||
|
test('the public vestibule directory fixes the five official names and order', () => {
|
||||||
|
assert.deepEqual(publicDomainDirectory.map(item => [item.directoryNumber, item.displayName]), [
|
||||||
|
['01', '光湖主域'],
|
||||||
|
['02', '光湖分域'],
|
||||||
|
['03', '光湖零域'],
|
||||||
|
['04', '光湖零感域'],
|
||||||
|
['05', '第五域 · 光湖本源域'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the first four domain themes remain owned and unpublished by their own teams', () => {
|
||||||
|
for (const item of publicDomainDirectory.slice(0, 4)) {
|
||||||
|
assert.equal(item.themeOwner, 'domain-team');
|
||||||
|
assert.equal(item.themeStatus, 'UNPUBLISHED');
|
||||||
|
assert.match(item.themeLabel, /对应|光湖/);
|
||||||
|
}
|
||||||
|
const fifth = getPublicDomainVestibule('fifth');
|
||||||
|
assert.equal(fifth.themeOwner, 'fifth-domain');
|
||||||
|
assert.equal(fifth.themeStatus, 'SELECTED_REFERENCE');
|
||||||
|
assert.match(fifth.themeLabel, /湖面映星/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no public vestibule exposes private domain resources', () => {
|
||||||
|
assert.ok(publicDomainDirectory.every(item => item.privateResourcesExposed === false));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validation rejects a misspelled zero domain and shared enterprise theme override', () => {
|
||||||
|
const clone = publicDomainDirectory.map(item => ({ ...item })) as PublicDomainVestibule[];
|
||||||
|
clone[2] = { ...clone[2], displayName: '光湖灵域' };
|
||||||
|
assert.throws(() => assertPublicDomainDirectory(clone), /PUBLIC_DOMAIN_ZERO_NAME_INVALID/);
|
||||||
|
|
||||||
|
const themed = publicDomainDirectory.map(item => ({ ...item })) as PublicDomainVestibule[];
|
||||||
|
themed[0] = { ...themed[0], themeOwner: 'fifth-domain', themeStatus: 'SELECTED_REFERENCE' };
|
||||||
|
assert.throws(() => assertPublicDomainDirectory(themed), /ENTERPRISE_DOMAIN_THEME_OWNERSHIP_INVALID/);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
export type DomainRouteId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
|
||||||
|
|
||||||
|
export type PublicDomainVestibule = Readonly<{
|
||||||
|
routeId: DomainRouteId;
|
||||||
|
directoryNumber: '01' | '02' | '03' | '04' | '05';
|
||||||
|
stableDomainId: string;
|
||||||
|
hldpNumber: string;
|
||||||
|
displayName: string;
|
||||||
|
publicSummary: string;
|
||||||
|
responsibility: string;
|
||||||
|
repositoryId: string;
|
||||||
|
themeOwner: 'domain-team' | 'fifth-domain';
|
||||||
|
themeStatus: 'UNPUBLISHED' | 'SELECTED_REFERENCE';
|
||||||
|
themeLabel: string;
|
||||||
|
supportedNodeTypes: readonly ('local-terminal' | 'cloud-resident')[];
|
||||||
|
privateResourcesExposed: false;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const rawDirectory: PublicDomainVestibule[] = [
|
||||||
|
{
|
||||||
|
routeId: 'main',
|
||||||
|
directoryNumber: '01',
|
||||||
|
stableDomainId: 'DOMAIN-MAIN',
|
||||||
|
hldpNumber: 'HLDP-DOMAIN-MAIN-001',
|
||||||
|
displayName: '光湖主域',
|
||||||
|
publicSummary: '公共产品事实、发布与公告入口',
|
||||||
|
responsibility: '公共产品事实、正式发布、公告与公共回执。',
|
||||||
|
repositoryId: 'domain-main',
|
||||||
|
themeOwner: 'domain-team',
|
||||||
|
themeStatus: 'UNPUBLISHED',
|
||||||
|
themeLabel: '主题由光湖主域主控团队发布 · 当前未装载',
|
||||||
|
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
|
||||||
|
privateResourcesExposed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routeId: 'sub',
|
||||||
|
directoryNumber: '02',
|
||||||
|
stableDomainId: 'DOMAIN-SUB',
|
||||||
|
hldpNumber: 'HLDP-DOMAIN-SUB-001',
|
||||||
|
displayName: '光湖分域',
|
||||||
|
publicSummary: '行业入口与初始化频道目录',
|
||||||
|
responsibility: '行业入口、初始化频道与对应行业模块目录。',
|
||||||
|
repositoryId: 'domain-sub',
|
||||||
|
themeOwner: 'domain-team',
|
||||||
|
themeStatus: 'UNPUBLISHED',
|
||||||
|
themeLabel: '主题由光湖分域主控团队发布 · 当前未装载',
|
||||||
|
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
|
||||||
|
privateResourcesExposed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routeId: 'zero',
|
||||||
|
directoryNumber: '03',
|
||||||
|
stableDomainId: 'DOMAIN-ZERO',
|
||||||
|
hldpNumber: 'HLDP-DOMAIN-ZERO-001',
|
||||||
|
displayName: '光湖零域',
|
||||||
|
publicSummary: '实验、模块试装与质量验证入口',
|
||||||
|
responsibility: '实验、模块试装、真实预览与质量验证。',
|
||||||
|
repositoryId: 'domain-zero',
|
||||||
|
themeOwner: 'domain-team',
|
||||||
|
themeStatus: 'UNPUBLISHED',
|
||||||
|
themeLabel: '主题由光湖零域主控团队发布 · 当前未装载',
|
||||||
|
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
|
||||||
|
privateResourcesExposed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routeId: 'zero-sense',
|
||||||
|
directoryNumber: '04',
|
||||||
|
stableDomainId: 'DOMAIN-ZS',
|
||||||
|
hldpNumber: 'HLDP-DOMAIN-ZEROSENSE-001',
|
||||||
|
displayName: '光湖零感域',
|
||||||
|
publicSummary: '治理、审批、回滚与审计入口',
|
||||||
|
responsibility: '治理、部署审批、事故处置、回滚与审计。',
|
||||||
|
repositoryId: 'domain-zero-sense',
|
||||||
|
themeOwner: 'domain-team',
|
||||||
|
themeStatus: 'UNPUBLISHED',
|
||||||
|
themeLabel: '主题由光湖零感域主控团队发布 · 当前未装载',
|
||||||
|
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
|
||||||
|
privateResourcesExposed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
routeId: 'fifth',
|
||||||
|
directoryNumber: '05',
|
||||||
|
stableDomainId: 'DOM-FIFTH-0001',
|
||||||
|
hldpNumber: 'DOM-FIFTH-0001',
|
||||||
|
displayName: '第五域 · 光湖本源域',
|
||||||
|
publicSummary: '零点原核的工程本体 · 语言架构层',
|
||||||
|
responsibility: '光湖语言世界的语言架构层与语言协议更新源点。',
|
||||||
|
repositoryId: 'REPO-012',
|
||||||
|
themeOwner: 'fifth-domain',
|
||||||
|
themeStatus: 'SELECTED_REFERENCE',
|
||||||
|
themeLabel: '湖面映星 · 已选定参考 · 进入后由独立运行体装载',
|
||||||
|
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
|
||||||
|
privateResourcesExposed: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function assertPublicDomainDirectory(directory: readonly PublicDomainVestibule[]): void {
|
||||||
|
if (directory.length !== 5) throw new Error('PUBLIC_DOMAIN_DIRECTORY_MUST_CONTAIN_FIVE_DOMAINS');
|
||||||
|
const unique = (values: string[]) => new Set(values).size === values.length;
|
||||||
|
if (!unique(directory.map(item => item.routeId))) throw new Error('PUBLIC_DOMAIN_ROUTE_ID_DUPLICATED');
|
||||||
|
if (!unique(directory.map(item => item.directoryNumber))) throw new Error('PUBLIC_DOMAIN_NUMBER_DUPLICATED');
|
||||||
|
if (!unique(directory.map(item => item.stableDomainId))) throw new Error('PUBLIC_DOMAIN_STABLE_ID_DUPLICATED');
|
||||||
|
if (directory.map(item => item.directoryNumber).join(',') !== '01,02,03,04,05') {
|
||||||
|
throw new Error('PUBLIC_DOMAIN_DIRECTORY_ORDER_INVALID');
|
||||||
|
}
|
||||||
|
const zero = directory.find(item => item.routeId === 'zero');
|
||||||
|
if (zero?.displayName !== '光湖零域') throw new Error('PUBLIC_DOMAIN_ZERO_NAME_INVALID');
|
||||||
|
const fifth = directory.find(item => item.routeId === 'fifth');
|
||||||
|
if (fifth?.themeOwner !== 'fifth-domain' || fifth.themeStatus !== 'SELECTED_REFERENCE') {
|
||||||
|
throw new Error('FIFTH_DOMAIN_THEME_REFERENCE_INVALID');
|
||||||
|
}
|
||||||
|
if (directory.some(item => item.routeId !== 'fifth' && (item.themeOwner !== 'domain-team' || item.themeStatus !== 'UNPUBLISHED'))) {
|
||||||
|
throw new Error('ENTERPRISE_DOMAIN_THEME_OWNERSHIP_INVALID');
|
||||||
|
}
|
||||||
|
if (directory.some(item => item.privateResourcesExposed !== false)) {
|
||||||
|
throw new Error('PUBLIC_VESTIBULE_MUST_NOT_EXPOSE_PRIVATE_RESOURCES');
|
||||||
|
}
|
||||||
|
if (directory.some(item => item.supportedNodeTypes.join(',') !== 'local-terminal,cloud-resident')) {
|
||||||
|
throw new Error('PUBLIC_VESTIBULE_NODE_TYPES_INVALID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertPublicDomainDirectory(rawDirectory);
|
||||||
|
|
||||||
|
export const publicDomainDirectory: readonly PublicDomainVestibule[] = Object.freeze(
|
||||||
|
rawDirectory.map(item => Object.freeze({ ...item, supportedNodeTypes: Object.freeze([...item.supportedNodeTypes]) }))
|
||||||
|
);
|
||||||
|
|
||||||
|
export function getPublicDomainVestibule(routeId: DomainRouteId): PublicDomainVestibule {
|
||||||
|
const entry = publicDomainDirectory.find(item => item.routeId === routeId);
|
||||||
|
if (!entry) throw new Error(`PUBLIC_DOMAIN_NOT_FOUND: ${routeId}`);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
@ -233,6 +233,53 @@
|
||||||
min-height: 118px;
|
min-height: 118px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.public-lighthouse.is-directory .public-entry-footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-vestibule-fact {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 54px;
|
||||||
|
z-index: 2;
|
||||||
|
width: min(760px, calc(100vw - 40px));
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
border-top: 1px solid rgba(191, 216, 255, 0.28);
|
||||||
|
color: rgba(232, 241, 255, 0.9);
|
||||||
|
text-align: left;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-vestibule-fact strong,
|
||||||
|
.public-vestibule-fact span,
|
||||||
|
.public-vestibule-fact small {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-vestibule-fact strong {
|
||||||
|
color: rgba(198, 220, 255, 0.78);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-vestibule-fact span {
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-vestibule-fact small {
|
||||||
|
color: rgba(198, 215, 241, 0.62);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.fifth-brand {
|
.fifth-brand {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue