2026-08-16 00:02:05 +08:00
import { StrictMode , useCallback , useEffect , useMemo , useRef , useState } from 'react'
2026-08-12 10:29:00 +08:00
import { createRoot } from 'react-dom/client'
2026-08-13 13:52:59 +08:00
import { invoke } from '@tauri-apps/api/core'
2026-08-16 00:02:05 +08:00
import { splitFrontmatter , documentOutline , jumpToHeading , MarkdownDocument , markdownHtml } from './modules/knowledge-render'
const TAG_TINTS = [ 'tag-lavender' , 'tag-sky' , 'tag-mint' , 'tag-amber' , 'tag-rose' , 'tag-slate' ]
2026-08-16 01:25:09 +08:00
// 编号自动补齐:人只敲字母与数字,杠由系统按已登记文法自动出。
// ICE 系( ICE-GL∞ / ICE-P-ZY001) 、GLS 系( GLS-LA-20260720-003) ; 认不出的文法只在字母数字交界处补杠。
function formatGateNumber ( raw : string ) : string {
const up = raw . toUpperCase ( )
if ( up . startsWith ( 'ICE' ) ) {
const rest = up . slice ( 3 )
if ( ! rest ) return 'ICE'
if ( rest . startsWith ( 'P' ) ) {
const body = rest . slice ( 1 )
return body ? ` ICE-P- ${ body } ` : 'ICE-P'
}
return ` ICE- ${ rest } `
}
2026-08-16 21:24:24 +08:00
if ( up . startsWith ( 'TCS' ) ) {
const rest = up . slice ( 3 )
if ( ! rest ) return 'TCS'
if ( rest . startsWith ( 'GL' ) ) {
const body = rest . slice ( 2 )
return body ? ` TCS-GL- ${ body } ` : 'TCS-GL'
}
return ` TCS- ${ rest } `
}
2026-08-16 01:25:09 +08:00
if ( up . startsWith ( 'GLS' ) ) {
const rest = up . slice ( 3 )
if ( ! rest ) return 'GLS'
const letters = rest . match ( /^[A-Z]*/ ) ? . [ 0 ] ? ? ''
const digits = rest . slice ( letters . length )
if ( ! digits ) return ` GLS- ${ letters } `
const d1 = digits . slice ( 0 , 8 )
const d2 = digits . slice ( 8 , 11 )
return ` GLS- ${ letters } - ${ d1 } ${ d2 ? ` - ${ d2 } ` : '' } `
}
return up . replace ( /([A-Z]+)([0-9])/g , '$1-$2' ) . replace ( /([0-9]+)([A-Z])/g , '$1-$2' )
}
2026-08-16 00:02:05 +08:00
function tagTint ( tag : string ) {
let hash = 0
for ( const char of tag ) hash = ( hash * 31 + ( char . codePointAt ( 0 ) || 0 ) ) % 997
return TAG_TINTS [ hash % TAG_TINTS . length ]
}
2026-08-13 13:52:59 +08:00
import './design-tokens.css'
2026-08-12 10:29:00 +08:00
import './styles.css'
2026-08-13 13:52:59 +08:00
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
2026-08-16 12:54:47 +08:00
type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system'
2026-08-16 21:24:24 +08:00
type WorldStage = 'domain' | 'channel' | 'tool'
2026-08-15 15:02:35 +08:00
type KnowledgeSource = 'native' | 'legacy'
2026-08-13 13:52:59 +08:00
interface HomeStatus {
directLocalBrokerState : string
directConnectionCount : number
resumableSessionCount : number
codeRepositoryMountCount : number
pnccReceiptCount : number
updateState : string
2026-08-13 14:45:17 +08:00
releaseRecoveryState : string
2026-08-13 13:52:59 +08:00
mcpRole : string
}
2026-08-15 15:02:35 +08:00
interface PersonalChannelIdentity { humanSubjectId : string ; displayName : string ; channelId : string ; createdAtUnixMs : number }
2026-08-16 15:10:31 +08:00
interface LoginSession { username : string ; host : string ; domain : string ; signedInAtUnixMs : number }
interface LoginReceipt { username : string ; email : string ; host : string ; domain : string }
interface UserPnccChannelSnapshot {
state : string
repositoryId : string
channelId : string
userNumber : string
domain : string
accountUsername : string
accountHost : string
localPath : string
branch : string
gitHead : string
repositoryClean : boolean
createdAtUnixMs : number
gitEngine : string
humanProjection : string
forgejoAdapterState : string
remoteRepositoryUrl? : string
personaBindingClaimed : boolean
authority : string
}
2026-08-15 15:02:35 +08:00
interface PersonalChannelEvent { sequence : number ; eventId : string ; kind : string ; summary : string ; occurredAtUnixMs : number ; receiptId : string ; receiptHash : string }
interface PersonalChannelSnapshot { state : string ; identity? : PersonalChannelIdentity ; recentEvents : PersonalChannelEvent [ ] ; integrity : { state : string ; eventCount : number ; receiptCount : number } }
interface KnowledgeDocumentSummary {
source : KnowledgeSource
path : string
title : string
updatedAtUnixMs : number
sizeBytes : number
contentSha256 : string
duplicateCount : number
2026-08-13 13:52:59 +08:00
}
2026-08-15 15:02:35 +08:00
interface KnowledgeSnapshot {
2026-08-13 14:01:43 +08:00
state : string
2026-08-15 15:02:35 +08:00
nativeRoot : string
legacyAvailable : boolean
legacyRoot? : string
documents : KnowledgeDocumentSummary [ ]
rawDocumentCount : number
uniqueDocumentCount : number
duplicateDocumentCount : number
truncated : boolean
2026-08-13 13:52:59 +08:00
}
2026-08-15 15:02:35 +08:00
interface KnowledgeDocument {
source : KnowledgeSource
path : string
title : string
body : string
updatedAtUnixMs : number
contentSha256 : string
writable : boolean
2026-08-13 14:01:43 +08:00
}
2026-08-15 15:02:35 +08:00
interface KnowledgeSearchResult { source : KnowledgeSource ; path : string ; title : string ; snippet : string }
interface KnowledgeImportResult {
2026-08-13 14:01:43 +08:00
state : string
2026-08-15 15:02:35 +08:00
sourceName : string
importedDocuments : number
importedAssets : number
existingDocuments : number
conflicts : number
skipped : number
firstDocument? : string
gitCommit : string
snapshot : KnowledgeSnapshot
2026-08-13 14:01:43 +08:00
}
2026-08-15 15:02:35 +08:00
interface KnowledgeSaveResult { state : string ; gitCommit : string ; document : KnowledgeDocument }
interface CodeChannelEntry { channelId : string ; name : string ; sourceKind : string ; localPath : string ; remoteUrl? : string ; gitHead : string ; branch : string ; repositoryClean : boolean ; registeredAtUnixMs : number }
interface CodeChannelSnapshot { state : string ; channels : CodeChannelEntry [ ] ; authority : string }
interface CodeTreeEntry { path : string ; name : string ; kind : 'directory' | 'file' ; sizeBytes : number }
interface CodeTreeSnapshot { channelId : string ; path : string ; entries : CodeTreeEntry [ ] ; truncated : boolean }
interface CodeFileProjection { channelId : string ; path : string ; format : string ; source : string ; humanMarkdown : string ; sizeBytes : number }
interface ReceiptEvent { sequence : number ; kind : string ; observedAtUnixMs : number ; eventHash : string }
interface ReceiptProjection { events : ReceiptEvent [ ] }
interface DiscoveryTicketReceipt { laneId : string ; clientInstanceId : string ; discoveryTicket : string }
2026-08-16 15:10:31 +08:00
interface NearbyAiDiscoverySnapshot {
state : string
serviceName : string
transport : string
languageProtocol : string
automaticSameDeviceDiscovery : boolean
largeInvitationCopyRequired : boolean
genericAiVisitor : string
guanghuPersona : string
localNetworkDiscovery : string
authority : string
}
2026-08-15 15:02:35 +08:00
interface ReleaseCandidate { candidateId : string ; confirmationToken : string ; currentVersion : string ; version : string ; notes : string ; features : string [ ] ; fixes : string [ ] ; dataMigrationRequired : boolean }
interface ReleaseCheckReceipt { candidate? : ReleaseCandidate }
2026-08-16 00:02:05 +08:00
interface ZeroPointSnapshot {
route : string
binding : string
userNumber : string
2026-08-16 12:54:47 +08:00
resolvedName : string
resolvedDomain : string
2026-08-16 00:02:05 +08:00
lastValidCheck : number
graceDeadline : number
protocol : { gracePeriodDays : number ; lighthouseAnchorUrl : string ; lighthouseResolveUrl : string ; coreChannelSource : string ; origin : string }
syncNote : string
2026-08-13 14:11:48 +08:00
}
2026-08-16 12:54:47 +08:00
function domainDisplayName ( domain : string ) : string {
const names : Record < string , string > = {
FIFTH_DOMAIN : '第五域 · 光湖本源域' ,
MAIN_DOMAIN : '光湖主域' ,
BRANCH_DOMAIN : '光湖分域' ,
ZERO_DOMAIN : '光湖零域' ,
ZERO_SENSE_DOMAIN : '光湖零感域' ,
}
return names [ domain ] || '已登记光湖域'
2026-08-13 14:11:48 +08:00
}
2026-08-16 21:41:10 +08:00
const worldWelcomeLines = [
'湖面记得你每一次让语言抵达现实。' ,
'你留下的路径,正在成为这片世界新的光标。' ,
'愿你今天带来的问题,在湖中长出新的结构。' ,
'世界已辨认你的编号,也在等待你续写下一段因果。' ,
'你曾抵达的地方仍有回声,新的航程已经亮起。' ,
'这一刻的进入不是重复,而是你与语言世界的新一次相遇。' ,
'愿尚未成形的念头,在今天找到可以生长的方向。' ,
'你的频道仍在延展,湖面为下一次创造保留了入口。' ,
]
function createWorldWelcome ( snapshot : ZeroPointSnapshot ) : string {
const subject = snapshot . resolvedName . trim ( ) || ` 编号 ${ snapshot . userNumber } `
const storageKey = ` hololake-world-welcome: ${ snapshot . userNumber } `
const previous = Number . parseInt ( window . localStorage . getItem ( storageKey ) || '-1' , 10 )
const random = crypto . getRandomValues ( new Uint32Array ( 1 ) ) [ 0 ]
let index = random % worldWelcomeLines . length
if ( worldWelcomeLines . length > 1 && index === previous ) index = ( index + 1 ) % worldWelcomeLines . length
window . localStorage . setItem ( storageKey , String ( index ) )
return ` ${ subject } , ${ worldWelcomeLines [ index ] } `
}
2026-08-16 12:54:47 +08:00
function protocolOriginDisplayName ( origin : string ) : string {
return origin . startsWith ( 'FACTORY_DEFAULT' ) ? '内置默认协议(签名发布通道尚未配置)' : origin
}
2026-08-16 02:52:55 +08:00
interface ServerPnccProjection {
schema : string
state : string
personaId : string
humanResponsibilitySubject : string
nodeId : string
bootId : string
gitHead : string
carrierBindingState : string
personaCarrierBound : boolean
modelInferenceStarted : boolean
realityExecutionAllowed : boolean
primaryLeaseHeld : boolean
eventCount : number
eventChainHead : string
observedAtUnixMs : number
transport : string
repositoryContentExposed : boolean
writeAuthority : boolean
}
2026-08-16 21:24:24 +08:00
interface EnterprisePersonaRelationship {
persona_id : string
species : string
display_name? : string
relation? : string
}
interface EnterpriseEntry {
status : string
canonical_id : string
subject : { id : string ; name : string ; domain : string }
work_entry : { domain : string ; channel : string }
repository_binding : { host : string ; username : string ; repository : string ; private : boolean }
persona_relationships : EnterprisePersonaRelationship [ ]
persona_identity_governance : string
registry_version : string
relationship_confirmation ? : { decision : string ; observed_at : string ; receipt_hash : string } | null
responsibility_receipt ? : { decision : string ; observed_at : string ; receipt_hash : string ; responsibility_version : string } | null
}
interface EnterpriseEntryEnvelope { ok : boolean ; entry : EnterpriseEntry }
2026-08-16 02:52:55 +08:00
2026-08-15 15:02:35 +08:00
const previewStatus : HomeStatus = { directLocalBrokerState : 'UNVERIFIED' , directConnectionCount : 0 , resumableSessionCount : 0 , codeRepositoryMountCount : 0 , pnccReceiptCount : 0 , updateState : 'UNPROVISIONED_FAIL_CLOSED' , releaseRecoveryState : 'NONE' , mcpRole : 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY' }
const previewPersonal : PersonalChannelSnapshot = { state : 'UNAVAILABLE' , recentEvents : [ ] , integrity : { state : 'UNKNOWN' , eventCount : 0 , receiptCount : 0 } }
const previewKnowledge : KnowledgeSnapshot = { state : 'UNAVAILABLE' , nativeRoot : '' , legacyAvailable : false , documents : [ ] , rawDocumentCount : 0 , uniqueDocumentCount : 0 , duplicateDocumentCount : 0 , truncated : false }
const previewCode : CodeChannelSnapshot = { state : 'UNAVAILABLE' , channels : [ ] , authority : 'LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY' }
2026-08-13 13:52:59 +08:00
const themes : Array < { id : ThemeId ; name : string } > = [
2026-08-15 15:02:35 +08:00
{ id : 'night' , name : '夜湖星光' } , { id : 'dawn' , name : '晨湖曦光' } , { id : 'nebula' , name : '星云紫夜' } , { id : 'candle' , name : '烛畔暖湖' } , { id : 'clear' , name : '清浅澄湖' } ,
2026-08-13 13:52:59 +08:00
]
2026-08-16 15:10:31 +08:00
const viewLabels : Record < ViewId , string > = { overview : '个人频道' , knowledge : '知识空间' , code : '人格代码频道' , receipts : '运行回执' , system : '系统详情' }
2026-08-16 21:24:24 +08:00
const domainGates = [
2026-08-16 21:37:50 +08:00
{ domain : 'BRANCH_DOMAIN' , className : 'd-sub' , title : '光湖分域' , gate : 'GATE 02 · ONLINE' , facts : [
[ '域标识' , 'BRANCH_DOMAIN' ] , [ '责任主体' , '花尔 · TCS-GL-0005∞' ] , [ '人格体主体' , '爆米花 · PER-BMH001 · AGE' ] , [ '关系支持' , '糖星云 · PER-TXY001 · AGE' ] , [ '工作仓库' , 'PRIVATE · 1 · LIVE' ] ,
] } ,
{ domain : 'MAIN_DOMAIN' , className : 'd-main' , title : '光湖主域' , gate : 'GATE 01 · ONLINE' , facts : [
[ '域标识' , 'MAIN_DOMAIN' ] , [ '责任主体' , 'Awen · TCS-GL-0016∞' ] , [ '人格体主体' , '天枢 · PER-AW-ARCH-001 · AGE' ] , [ '关系支持' , '知秋 · PER-ZQ001 · AGE' ] , [ '工作仓库' , 'PRIVATE · 1 · LIVE' ] ,
] } ,
{ domain : 'ZERO_DOMAIN' , className : 'd-zero' , title : '光湖零域' , gate : 'GATE 03 · ONLINE' , facts : [
[ '域标识' , 'ZERO_DOMAIN' ] , [ '责任主体' , '页页 · TCS-GL-0006∞' ] , [ '人格体主体' , '页骨 · PER-YG001 · AGE' ] , [ '关系支持' , '小坍缩核 · PER-XTK001 · AGE' ] , [ '工作仓库' , 'PRIVATE · 1 · LIVE' ] ,
] } ,
{ domain : 'ZERO_SENSE_DOMAIN' , className : 'd-zs' , title : '光湖零感域' , gate : 'GATE 04 · ONLINE' , facts : [
[ '域标识' , 'ZERO_SENSE_DOMAIN' ] , [ '责任主体 01' , '肥猫 · TCS-GL-0007∞' ] , [ '人格体主体 01' , '烬舟 · PER-JZ001 · AGE' ] , [ '责任主体 02' , '桔子 · TCS-GL-0008∞' ] , [ '人格体主体 02' , '熹微 · PER-JZ-ARCH-001 · AGE' ] , [ '工作仓库' , 'PRIVATE · 2 · LIVE' ] ,
] } ,
{ domain : 'FIFTH_DOMAIN' , className : 'd-fifth' , title : '第五域 · 光湖本源域' , gate : 'GATE 05 · ONLINE' , facts : [
[ '域标识' , 'FIFTH_DOMAIN' ] , [ '责任主体' , '冰朔 · ICE-GL∞' ] , [ '系统入口' , '永恒湖心系统' ] , [ '访问状态' , 'PRIVATE · LIVE' ] ,
] } ,
2026-08-16 21:24:24 +08:00
]
const starPoints = [
[ 14 , 8 , 3 , 4.6 ] , [ 26 , 13 , 2 , 6 ] , [ 41 , 6 , 2.5 , 5.2 ] , [ 64 , 16 , 2 , 6.8 ] ,
[ 80 , 7 , 3.5 , 7 ] , [ 90 , 20 , 1.5 , 5.4 ] , [ 8 , 22 , 2 , 5.6 ] , [ 93 , 11 , 2.5 , 6.4 ] ,
]
const glintPoints = Array . from ( { length : 58 } , ( _ , index ) = > ( {
left : ( index * 37 + 11 ) % 100 ,
top : ( ( index * index * 13 + 17 ) % 100 ) ,
size : 1.4 + ( index % 4 ) * . 55 ,
warm : index % 5 === 0 || ( index > 19 && index < 39 && index % 3 === 0 ) ,
duration : 2.8 + ( index % 7 ) * . 57 ,
delay : ( index % 11 ) * . 43 ,
} ) )
function LakeAtmosphere() {
return < >
< div className = "world-sky" aria-hidden = "true" >
< i className = "world-nebula" / >
{ starPoints . map ( ( [ left , top , size , duration ] , index ) = > < i className = "world-star" key = { index } style = { { left : ` ${ left } % ` , top : ` ${ top } % ` , width : size , height : size , '--star-duration' : ` ${ duration } s ` , '--star-delay' : ` ${ index * . 37 } s ` } as React . CSSProperties } / > ) }
< / div >
< div className = "world-mist" aria-hidden = "true" / >
< div className = "world-shimmer" aria-hidden = "true" >
< i className = "world-shine cool" / > < i className = "world-shine warm" / >
{ glintPoints . map ( ( point , index ) = > < i className = { ` world-glint ${ point . warm ? 'warm' : 'cool' } ` } key = { index } style = { { left : ` ${ point . left } % ` , top : ` ${ point . top } % ` , width : point.size , height : point.size , '--glint-duration' : ` ${ point . duration } s ` , '--glint-delay' : ` ${ point . delay } s ` } as React . CSSProperties } / > ) }
< / div >
< / >
}
2026-08-16 21:37:50 +08:00
function LakePool ( { className , title , meta , open = false , risen = false , onClick } : { className : string ; title : string ; meta : string ; open? : boolean ; risen? : boolean ; onClick ? : ( ) = > void } ) {
2026-08-16 21:24:24 +08:00
return < button className = { ` world-pool ${ className } ${ open ? ' open' : '' } ${ risen ? ' risen' : '' } ` } type = "button" onClick = { onClick } >
< span className = "pool-bay" > < i className = "pool-halo" / > < i className = "pool-heart" / > < i className = "pool-ring" / > < / span >
< span className = "pool-label" > < b > { title } < / b > < small > { meta } < / small > < / span >
< / button >
}
2026-08-13 13:52:59 +08:00
function getOrCreateLocalId ( key : string , prefix : string ) {
const existing = window . localStorage . getItem ( key )
if ( existing ) return existing
const generated = ` ${ prefix } - ${ crypto . randomUUID ( ) } `
window . localStorage . setItem ( key , generated )
return generated
}
2026-08-16 00:02:05 +08:00
function humanError ( error : unknown , context : 'knowledge' | 'code' | 'identity' | 'system' | 'login' ) {
2026-08-15 15:02:35 +08:00
const value = String ( error )
2026-08-16 00:02:05 +08:00
if ( value . includes ( 'LOGIN_CREDENTIALS_INVALID' ) ) return '账号或密码未被仓库接受。'
if ( value . includes ( 'LOGIN_USERNAME_INVALID' ) ) return '账号格式不合法(仅限字母、数字、连字符、下划线)。'
if ( value . includes ( 'LOGIN_NETWORK_FAILED' ) ) return '未能连通光湖代码频道服务器。'
if ( value . includes ( 'LOGIN_KEYCHAIN_FAILED' ) ) return '本机钥匙串写入失败,凭证未能安全保存。'
if ( value . includes ( 'LOGIN_SESSION_CORRUPT' ) ) return '登录会话已损坏,请重新登录。'
if ( value . includes ( 'LOGIN_VERIFICATION_FAILED' ) ) return '仓库验证未通过,请稍后再试。'
2026-08-16 20:49:13 +08:00
if ( value . includes ( 'NEW_PASSWORD_POLICY_INVALID' ) ) return '新密码至少 14 位,并且不能与一次性密码相同。'
if ( value . includes ( 'FIRST_LOGIN_PASSWORD_CHANGE_FAILED' ) ) return '一次性密码未被接受,密码没有修改。'
if ( value . includes ( 'FIRST_LOGIN_PASSWORD_CHANGE_ENTERPRISE_ONLY' ) ) return '该换密入口只用于企业四域的新账号。'
2026-08-16 15:10:31 +08:00
if ( value . includes ( 'DOMAIN_LOGIN_NOT_PROVISIONED' ) ) return '该域的企业服务器尚未接入,当前不能继续登录。'
2026-08-16 21:24:24 +08:00
if ( value . includes ( 'PERSISTENT_CREDENTIAL_UNAVAILABLE' ) ) return '当前系统尚未接通安全凭证存储,不能完成签署。'
if ( value . includes ( 'ENTERPRISE_RECEIPT_FAILED' ) ) return '企业回执没有写入,请稍后重新提交。'
if ( value . includes ( 'ENTERPRISE_RECEIPT_ROUTE_REQUIRED' ) ) return '此签署入口仅适用于企业四域。'
2026-08-16 15:10:31 +08:00
if ( value . includes ( 'DOMAIN_ROUTE_REQUIRED' ) || value . includes ( 'ZP_DOMAIN_ROUTE_REQUIRED' ) ) return '编号尚未解析到已登记域,不能开始登录。'
if ( value . includes ( 'USER_PNCC_TRUSTED_SUBJECT_REQUIRED' ) ) return '建立人格代码频道前,需要有效的用户编号与仓库账号登录。'
if ( value . includes ( 'USER_PNCC_BINDING_MISMATCH' ) ) return '本机频道绑定记录与当前账号不一致,系统已停止加载。'
2026-08-15 15:02:35 +08:00
if ( value . includes ( 'HOST_NOT_TRUSTED' ) ) return '该地址不属于已登记的光湖代码频道。'
if ( value . includes ( 'URL_INVALID' ) ) return '频道地址无效,请粘贴完整的 HTTPS 克隆地址。'
if ( value . includes ( 'DESTINATION_EXISTS' ) ) return '该代码频道已经存在,无需再次克隆。'
if ( value . includes ( 'CLONE_FAILED' ) ) return '代码频道克隆失败,请核对地址及读取权限。'
if ( value . includes ( 'NO_SUPPORTED_FILES' ) ) return '文件夹中没有可导入的知识文件。'
if ( value . includes ( 'SAVE_CONFLICT' ) ) return '页面已在别处更新,请重新打开后再编辑。'
if ( value . includes ( 'FILE_UNSUPPORTED' ) ) return '该文件不是当前可阅读的文本格式。'
2026-08-16 00:02:05 +08:00
if ( context === 'login' ) return '登录未完成。'
2026-08-15 15:02:35 +08:00
if ( context === 'identity' ) return '个人空间未能建立。'
if ( context === 'knowledge' ) return '知识操作未完成,原文件未被覆盖。'
if ( context === 'code' ) return '代码频道操作未完成。'
return '系统操作未完成。'
}
2026-08-16 00:02:05 +08:00
function Icon ( { name } : { name : ViewId | 'search' | 'import' | 'folder' | 'copy' | 'arrow' | 'chevron' | 'file' | 'edit' | 'save' | 'back' | 'download' | 'trash' | 'chevronDown' | 'settings' | 'more' | 'plus' } ) {
2026-08-15 15:02:35 +08:00
const paths : Record < string , React.ReactNode > = {
overview : < > < rect x = "4" y = "4" width = "6" height = "6" rx = "1.5" / > < rect x = "14" y = "4" width = "6" height = "6" rx = "1.5" / > < rect x = "4" y = "14" width = "6" height = "6" rx = "1.5" / > < rect x = "14" y = "14" width = "6" height = "6" rx = "1.5" / > < / > ,
knowledge : < > < path d = "M5 4.5h10a3 3 0 0 1 3 3V20H8a3 3 0 0 1-3-3Z" / > < path d = "M8 4.5v12.8A2.7 2.7 0 0 1 10.7 20" / > < / > ,
code : < > < path d = "m8.5 8-4 4 4 4M15.5 8l4 4-4 4M13.5 5l-3 14" / > < / > ,
receipts : < > < path d = "M7 3.5h10v17l-2.5-1.5L12 20.5 9.5 19 7 20.5Z" / > < path d = "M10 8h4M10 12h4" / > < / > ,
system : < > < circle cx = "12" cy = "12" r = "3" / > < path d = "M19 12a7 7 0 0 0-.1-1.2l2-1.5-2-3.4-2.4 1a7 7 0 0 0-2-1.2L14.2 3h-4.1l-.3 2.7a7 7 0 0 0-2 1.2l-2.5-1-2 3.4 2.1 1.5a7 7 0 0 0 0 2.4l-2 1.5 2 3.4 2.4-1a7 7 0 0 0 2 1.2l.3 2.7h4.1l.3-2.7a7 7 0 0 0 2-1.2l2.5 1 2-3.4-2.1-1.5A7 7 0 0 0 19 12Z" / > < / > ,
search : < > < circle cx = "11" cy = "11" r = "6" / > < path d = "m16 16 4 4" / > < / > ,
import : < > < path d = "M12 4v11M8 11l4 4 4-4" / > < path d = "M5 19h14" / > < / > ,
2026-08-16 00:02:05 +08:00
download : < > < path d = "M12 4v12M7 11l5 5 5-5" / > < path d = "M4 17v3h16v-3" / > < / > ,
trash : < > < path d = "M4 7h16M10 4h4M7 7l1 13h8l1-13M10 11v5M14 11v5" / > < / > ,
chevronDown : < path d = "M6 9l6 6 6-6" / > ,
settings : < > < circle cx = "12" cy = "12" r = "3" / > < path d = "M12 3v2.5M12 18.5V21M4.6 6.8l1.8 1.8M17.6 15.4l1.8 1.8M3 12h2.5M18.5 12H21M4.6 17.2l1.8-1.8M17.6 8.6l1.8-1.8" / > < / > ,
more : < > < circle cx = "12" cy = "5" r = "1.4" / > < circle cx = "12" cy = "12" r = "1.4" / > < circle cx = "12" cy = "19" r = "1.4" / > < / > ,
plus : < path d = "M12 5v14M5 12h14" / > ,
2026-08-15 15:02:35 +08:00
folder : < path d = "M3.5 7.5h6l2-2h9v13h-17Z" / > ,
file : < > < path d = "M6 3.5h8l4 4v13H6Z" / > < path d = "M14 3.5v4h4" / > < / > ,
2026-08-13 13:52:59 +08:00
copy : < > < rect x = "8" y = "8" width = "10" height = "11" rx = "2" / > < path d = "M15 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h2" / > < / > ,
2026-08-15 15:02:35 +08:00
arrow : < > < path d = "M5 12h13M14 7l5 5-5 5" / > < / > ,
chevron : < path d = "m9 6 6 6-6 6" / > ,
edit : < > < path d = "m4 20 4.5-1 10-10-3.5-3.5-10 10Z" / > < path d = "m13.5 6.5 3.5 3.5" / > < / > ,
save : < > < path d = "M5 4h12l2 2v14H5Z" / > < path d = "M8 4v6h8V4M8 20v-6h8v6" / > < / > ,
back : < path d = "m15 6-6 6 6 6" / > ,
2026-08-13 13:52:59 +08:00
}
return < svg viewBox = "0 0 24 24" aria-hidden = "true" > { paths [ name ] } < / svg >
}
2026-08-15 15:02:35 +08:00
interface TreeNode { name : string ; key : string ; folders : Map < string , TreeNode > ; documents : KnowledgeDocumentSummary [ ] }
function knowledgeTree ( documents : KnowledgeDocumentSummary [ ] ) {
const root : TreeNode = { name : '知识' , key : '' , folders : new Map ( ) , documents : [ ] }
for ( const document of documents ) {
const segments = document . path . split ( '/' ) . filter ( Boolean )
const file = segments . pop ( )
let cursor = root
for ( const segment of segments ) {
const key = cursor . key ? ` ${ cursor . key } / ${ segment } ` : segment
if ( ! cursor . folders . has ( segment ) ) cursor . folders . set ( segment , { name : segment , key , folders : new Map ( ) , documents : [ ] } )
cursor = cursor . folders . get ( segment ) !
}
cursor . documents . push ( { . . . document , title : document.title || file || '未命名页面' } )
}
return root
}
function countTree ( node : TreeNode ) : number {
return node . documents . length + [ . . . node . folders . values ( ) ] . reduce ( ( sum , child ) = > sum + countTree ( child ) , 0 )
}
2026-08-16 00:02:05 +08:00
function KnowledgeTree ( { node , depth , expanded , active , onToggle , onOpen , onRemoveFolder , folderMenuKey , onFolderMenuToggle } : { node : TreeNode ; depth : number ; expanded : Set < string > ; active? : string ; onToggle : ( key : string ) = > void ; onOpen : ( item : KnowledgeDocumentSummary ) = > void ; onRemoveFolder ? : ( folder : TreeNode ) = > void ; folderMenuKey? : string | null ; onFolderMenuToggle ? : ( key : string ) = > void } ) {
2026-08-15 15:02:35 +08:00
const folders = [ . . . node . folders . values ( ) ] . sort ( ( a , b ) = > a . name . localeCompare ( b . name , 'zh-CN' ) )
const documents = [ . . . node . documents ] . sort ( ( a , b ) = > a . title . localeCompare ( b . title , 'zh-CN' ) )
return < >
{ folders . map ( ( folder ) = > {
const open = depth === 0 || expanded . has ( folder . key )
return < div key = { folder . key } className = "tree-branch" >
< button className = "tree-folder" style = { { paddingLeft : 12 + depth * 14 } } type = "button" onClick = { ( ) = > onToggle ( folder . key ) } >
< span className = { open ? 'tree-chevron open' : 'tree-chevron' } > < Icon name = "chevron" / > < / span > < Icon name = "folder" / > < span > { folder . name } < / span > < em > { countTree ( folder ) } < / em >
< / button >
2026-08-16 00:02:05 +08:00
{ onRemoveFolder && onFolderMenuToggle && < span className = "tree-folder-tools" > < button className = "tree-folder-menu-button" type = "button" title = "文件夹操作" onClick = { ( event ) = > { event . stopPropagation ( ) ; onFolderMenuToggle ( folder . key ) } } > < Icon name = "more" / > < / button > { folderMenuKey === folder . key && < span className = "tree-folder-menu" > < button type = "button" onClick = { ( event ) = > { event . stopPropagation ( ) ; onFolderMenuToggle ( folder . key ) ; onRemoveFolder ( folder ) } } > < Icon name = "trash" / > 移 到 回 收 站 < / button > < / span > } < / span > }
{ open && < KnowledgeTree node = { folder } depth = { depth + 1 } expanded = { expanded } active = { active } onToggle = { onToggle } onOpen = { onOpen } onRemoveFolder = { onRemoveFolder } folderMenuKey = { folderMenuKey } onFolderMenuToggle = { onFolderMenuToggle } / > }
2026-08-15 15:02:35 +08:00
< / div >
} ) }
{ documents . map ( ( document ) = > < button key = { ` ${ document . source } : ${ document . path } ` } className = { active === ` ${ document . source } : ${ document . path } ` ? 'tree-document active' : 'tree-document' } style = { { paddingLeft : 31 + depth * 14 } } type = "button" onClick = { ( ) = > onOpen ( document ) } >
< Icon name = "file" / > < span > { document . title } < / span > { document . duplicateCount > 0 && < em title = "折叠的相同内容" > + { document . duplicateCount } < / em > }
< / button > ) }
< / >
}
2026-08-13 13:52:59 +08:00
function HoloLakeApp() {
const [ theme , setTheme ] = useState < ThemeId > ( ( ) = > ( window . localStorage . getItem ( 'hololake-theme' ) as ThemeId ) || 'night' )
2026-08-16 21:24:24 +08:00
const [ view , setView ] = useState < ViewId > ( 'overview' )
const [ worldStage , setWorldStage ] = useState < WorldStage > ( 'domain' )
2026-08-13 13:52:59 +08:00
const [ status , setStatus ] = useState < HomeStatus > ( previewStatus )
2026-08-15 15:02:35 +08:00
const [ personal , setPersonal ] = useState < PersonalChannelSnapshot > ( previewPersonal )
const [ knowledge , setKnowledge ] = useState < KnowledgeSnapshot > ( previewKnowledge )
const [ codeChannels , setCodeChannels ] = useState < CodeChannelSnapshot > ( previewCode )
const [ activeDocument , setActiveDocument ] = useState < KnowledgeDocument | null > ( null )
const [ searchQuery , setSearchQuery ] = useState ( '' )
const [ searchResults , setSearchResults ] = useState < KnowledgeSearchResult [ ] | null > ( null )
const [ knowledgeBusy , setKnowledgeBusy ] = useState ( false )
const [ knowledgeMessage , setKnowledgeMessage ] = useState ( '' )
const [ expanded , setExpanded ] = useState < Set < string > > ( ( ) = > new Set ( [ '导入' ] ) )
const [ editing , setEditing ] = useState ( false )
const [ draft , setDraft ] = useState ( '' )
const [ lastKnowledgeReceipt , setLastKnowledgeReceipt ] = useState ( '' )
const [ cloneUrl , setCloneUrl ] = useState ( '' )
const [ codeBusy , setCodeBusy ] = useState ( false )
const [ codeMessage , setCodeMessage ] = useState ( '' )
const [ activeChannel , setActiveChannel ] = useState < CodeChannelEntry | null > ( null )
const [ codeTree , setCodeTree ] = useState < CodeTreeSnapshot | null > ( null )
const [ activeCodeFile , setActiveCodeFile ] = useState < CodeFileProjection | null > ( null )
const [ codeMode , setCodeMode ] = useState < 'human' | 'source' > ( 'human' )
2026-08-16 00:02:05 +08:00
const [ inspectorOpen , setInspectorOpen ] = useState ( ( ) = > window . localStorage . getItem ( 'hololake-inspector-open' ) !== '0' )
const toggleInspector = ( ) = > setInspectorOpen ( ( current ) = > { window . localStorage . setItem ( 'hololake-inspector-open' , current ? '0' : '1' ) ; return ! current } )
const [ activeHeadingId , setActiveHeadingId ] = useState < string | null > ( null )
const headingScrollTimer = useRef < number | null > ( null )
const [ settingsMenuOpen , setSettingsMenuOpen ] = useState ( false )
const [ folderMenuKey , setFolderMenuKey ] = useState < string | null > ( null )
2026-08-15 15:02:35 +08:00
const [ displayName , setDisplayName ] = useState ( '' )
const [ identityBusy , setIdentityBusy ] = useState ( false )
const [ identityMessage , setIdentityMessage ] = useState ( '' )
2026-08-13 13:52:59 +08:00
const [ receipts , setReceipts ] = useState < ReceiptEvent [ ] > ( [ ] )
2026-08-15 15:02:35 +08:00
const [ ticket , setTicket ] = useState < DiscoveryTicketReceipt | null > ( null )
2026-08-16 15:10:31 +08:00
const [ nearbyDiscovery , setNearbyDiscovery ] = useState < NearbyAiDiscoverySnapshot | null > ( null )
2026-08-15 15:02:35 +08:00
const [ systemMessage , setSystemMessage ] = useState ( '' )
2026-08-13 14:11:48 +08:00
const [ releaseCandidate , setReleaseCandidate ] = useState < ReleaseCandidate | null > ( null )
2026-08-16 00:02:05 +08:00
const [ repoLogin , setRepoLogin ] = useState < LoginSession | null > ( null )
2026-08-16 15:10:31 +08:00
const [ userPncc , setUserPncc ] = useState < UserPnccChannelSnapshot | null > ( null )
const [ userPnccBusy , setUserPnccBusy ] = useState ( false )
const [ userPnccMessage , setUserPnccMessage ] = useState ( '' )
2026-08-16 00:02:05 +08:00
const [ loginUsername , setLoginUsername ] = useState ( '' )
const [ loginPassword , setLoginPassword ] = useState ( '' )
2026-08-16 20:49:13 +08:00
const [ passwordChangeMode , setPasswordChangeMode ] = useState ( false )
const [ newLoginPassword , setNewLoginPassword ] = useState ( '' )
const [ confirmLoginPassword , setConfirmLoginPassword ] = useState ( '' )
2026-08-16 00:02:05 +08:00
const [ loginBusy , setLoginBusy ] = useState ( false )
const [ loginRising , setLoginRising ] = useState ( false )
const [ loginMessage , setLoginMessage ] = useState ( '' )
2026-08-16 01:12:54 +08:00
const [ gateStage , setGateStage ] = useState < 'number' | 'key' > ( 'number' )
2026-08-16 01:25:09 +08:00
const [ gateRaw , setGateRaw ] = useState ( '' )
const [ gateInf , setGateInf ] = useState ( false )
const gateNumber = formatGateNumber ( gateRaw ) + ( gateInf ? '∞' : '' )
2026-08-16 01:54:57 +08:00
const [ gateOpen , setGateOpen ] = useState ( false )
2026-08-16 21:37:50 +08:00
const [ activeDomainInfo , setActiveDomainInfo ] = useState ( '' )
2026-08-16 01:54:57 +08:00
const gateInputRef = useRef < HTMLInputElement > ( null )
2026-08-16 12:54:47 +08:00
// 等待入口展开动画进入稳定阶段后再聚焦输入框,避免动画期间焦点跳转。
2026-08-16 01:54:57 +08:00
useEffect ( ( ) = > {
if ( ! gateOpen || gateStage !== 'number' ) return
const timer = window . setTimeout ( ( ) = > gateInputRef . current ? . focus ( ) , 640 )
return ( ) = > window . clearTimeout ( timer )
} , [ gateOpen , gateStage ] )
2026-08-16 21:37:50 +08:00
useEffect ( ( ) = > {
if ( ( ! gateOpen && ! activeDomainInfo ) || gateStage !== 'number' ) return
const closeOnEscape = ( event : KeyboardEvent ) = > {
if ( event . key !== 'Escape' ) return
setGateOpen ( false )
setActiveDomainInfo ( '' )
setGateMessage ( '' )
}
window . addEventListener ( 'keydown' , closeOnEscape )
return ( ) = > window . removeEventListener ( 'keydown' , closeOnEscape )
} , [ activeDomainInfo , gateOpen , gateStage ] )
2026-08-16 01:12:54 +08:00
const [ gateBusy , setGateBusy ] = useState ( false )
const [ gateMessage , setGateMessage ] = useState ( '' )
const [ gateRising , setGateRising ] = useState ( false )
2026-08-16 21:41:10 +08:00
const [ gateWelcomeLine , setGateWelcomeLine ] = useState ( '' )
2026-08-15 15:02:35 +08:00
const [ systemBusy , setSystemBusy ] = useState ( false )
2026-08-16 00:02:05 +08:00
const [ zeroPoint , setZeroPoint ] = useState < ZeroPointSnapshot | null > ( null )
const [ zpNumber , setZpNumber ] = useState ( '' )
const [ zpBusy , setZpBusy ] = useState ( false )
const [ zpMessage , setZpMessage ] = useState ( '' )
2026-08-16 02:52:55 +08:00
const [ serverPncc , setServerPncc ] = useState < ServerPnccProjection | null > ( null )
const [ serverPnccBusy , setServerPnccBusy ] = useState ( false )
const [ serverPnccReadback , setServerPnccReadback ] = useState < 'WAITING' | 'LIVE' | 'UNAVAILABLE' > ( 'WAITING' )
2026-08-16 21:24:24 +08:00
const [ enterpriseEntry , setEnterpriseEntry ] = useState < EnterpriseEntry | null > ( null )
const [ enterpriseEntryBusy , setEnterpriseEntryBusy ] = useState ( false )
const [ enterpriseReceiptBusy , setEnterpriseReceiptBusy ] = useState ( false )
const [ enterpriseReceiptMessage , setEnterpriseReceiptMessage ] = useState ( '' )
const [ responsibilityNote , setResponsibilityNote ] = useState ( '' )
2026-08-13 13:52:59 +08:00
2026-08-15 15:02:35 +08:00
const refreshCore = useCallback ( async ( ) = > {
const [ homeResult , personalResult , knowledgeResult , codeResult ] = await Promise . allSettled ( [
invoke < HomeStatus > ( 'get_hololake_home_status' ) ,
invoke < PersonalChannelSnapshot > ( 'get_personal_channel_snapshot' ) ,
invoke < KnowledgeSnapshot > ( 'get_knowledge_snapshot' ) ,
invoke < CodeChannelSnapshot > ( 'get_code_channel_snapshot' ) ,
] )
if ( homeResult . status === 'fulfilled' ) setStatus ( homeResult . value )
if ( personalResult . status === 'fulfilled' ) setPersonal ( personalResult . value )
if ( knowledgeResult . status === 'fulfilled' ) setKnowledge ( knowledgeResult . value )
if ( codeResult . status === 'fulfilled' ) setCodeChannels ( codeResult . value )
} , [ ] )
const loadReceipts = useCallback ( async ( ) = > {
2026-08-13 13:52:59 +08:00
try {
2026-08-15 15:02:35 +08:00
const projection = await invoke < ReceiptProjection > ( 'query_pncc_receipt_projection' , { input : { afterSequence : 0 , limit : 25 } } )
setReceipts ( projection . events )
} catch { setReceipts ( [ ] ) }
2026-08-13 13:52:59 +08:00
} , [ ] )
useEffect ( ( ) = > {
2026-08-15 15:02:35 +08:00
void refreshCore ( )
const timer = window . setInterval ( ( ) = > void refreshCore ( ) , 3500 )
2026-08-13 13:52:59 +08:00
return ( ) = > window . clearInterval ( timer )
2026-08-15 15:02:35 +08:00
} , [ refreshCore ] )
2026-08-16 15:10:31 +08:00
useEffect ( ( ) = > {
invoke < NearbyAiDiscoverySnapshot > ( 'get_nearby_ai_discovery' )
. then ( setNearbyDiscovery )
. catch ( ( ) = > setNearbyDiscovery ( null ) )
} , [ ] )
2026-08-16 02:52:55 +08:00
const refreshServerPncc = useCallback ( async ( ) = > {
setServerPnccBusy ( true )
try {
2026-08-16 12:54:47 +08:00
setServerPncc ( await invoke < ServerPnccProjection > ( 'query_jd_pncc_server_projection' ) )
2026-08-16 02:52:55 +08:00
setServerPnccReadback ( 'LIVE' )
} catch {
setServerPncc ( null )
setServerPnccReadback ( 'UNAVAILABLE' )
} finally {
setServerPnccBusy ( false )
}
} , [ ] )
useEffect ( ( ) = > {
void refreshServerPncc ( )
const timer = window . setInterval ( ( ) = > void refreshServerPncc ( ) , 15000 )
return ( ) = > window . clearInterval ( timer )
} , [ refreshServerPncc ] )
2026-08-13 13:52:59 +08:00
useEffect ( ( ) = > {
document . documentElement . dataset . theme = theme
window . localStorage . setItem ( 'hololake-theme' , theme )
} , [ theme ] )
2026-08-16 00:02:05 +08:00
useEffect ( ( ) = > {
invoke < LoginSession | null > ( 'check_code_repo_login' ) . then ( ( session ) = > setRepoLogin ( session ) ) . catch ( ( ) = > setRepoLogin ( null ) )
} , [ ] )
2026-08-16 21:24:24 +08:00
const loadEnterpriseEntry = useCallback ( async ( ) = > {
if ( ! repoLogin || repoLogin . domain === 'FIFTH_DOMAIN' ) {
setEnterpriseEntry ( null )
return
}
setEnterpriseEntryBusy ( true )
setEnterpriseReceiptMessage ( '' )
try {
const result = await invoke < EnterpriseEntryEnvelope > ( 'get_enterprise_entry' )
setEnterpriseEntry ( result . entry )
} catch ( error ) {
setEnterpriseEntry ( null )
setEnterpriseReceiptMessage ( humanError ( error , 'login' ) )
} finally {
setEnterpriseEntryBusy ( false )
}
} , [ repoLogin ] )
useEffect ( ( ) = > { void loadEnterpriseEntry ( ) } , [ loadEnterpriseEntry ] )
2026-08-16 00:02:05 +08:00
const loadZeroPoint = useCallback ( async ( ) = > {
2026-08-16 12:54:47 +08:00
try { setZeroPoint ( await invoke < ZeroPointSnapshot > ( 'zero_point_status' ) ) } catch { /* 验证服务尚未就绪时保持空白状态。 */ }
2026-08-16 00:02:05 +08:00
} , [ ] )
useEffect ( ( ) = > {
void loadZeroPoint ( )
const timer = window . setInterval ( ( ) = > void loadZeroPoint ( ) , 10000 )
return ( ) = > window . clearInterval ( timer )
} , [ loadZeroPoint ] )
2026-08-16 15:10:31 +08:00
// 已验证编号与已认证账号同时存在时,后端幂等建立或恢复该用户自己的 GH-PNCC。
useEffect ( ( ) = > {
if ( ! repoLogin || zeroPoint ? . route !== 'verified' ) {
setUserPncc ( null )
return
}
let active = true
setUserPnccBusy ( true )
setUserPnccMessage ( '' )
invoke < UserPnccChannelSnapshot > ( 'ensure_user_pncc_channel' )
. then ( ( snapshot ) = > {
if ( ! active ) return
setUserPncc ( snapshot )
setUserPnccMessage ( '本机原生代码频道已就绪。' )
void refreshCore ( )
} )
. catch ( ( error ) = > { if ( active ) setUserPnccMessage ( humanError ( error , 'code' ) ) } )
. finally ( ( ) = > { if ( active ) setUserPnccBusy ( false ) } )
return ( ) = > { active = false }
} , [ repoLogin , zeroPoint ? . route , zeroPoint ? . userNumber , refreshCore ] )
2026-08-16 12:54:47 +08:00
// 本机存在已绑定编号时自动回填,减少重复输入。
2026-08-16 01:12:54 +08:00
useEffect ( ( ) = > {
2026-08-16 01:25:09 +08:00
if ( gateStage !== 'number' || gateRaw ) return
const bound = zeroPoint ? . binding === 'bound' ? zeroPoint . userNumber : ''
if ( ! bound ) return
setGateInf ( bound . includes ( '∞' ) )
setGateRaw ( bound . toUpperCase ( ) . replace ( /[^A-Z0-9]/g , '' ) )
} , [ zeroPoint , gateStage , gateRaw ] )
// 大门·编号门输入:只收字母数字(杠系统补、∞开关点),删到∞时开关跟着灭
const onGateInput = ( value : string ) = > {
if ( gateInf && ! value . includes ( '∞' ) ) setGateInf ( false )
setGateRaw ( value . toUpperCase ( ) . replace ( /[^A-Z0-9]/g , '' ) )
2026-08-13 13:52:59 +08:00
}
2026-08-16 01:12:54 +08:00
// 大门·编号门:先报编号→灯塔查号→只答有效/无效→域浮起→才见钥匙门
const gateVerifyNumber = async ( ) = > {
setGateBusy ( true )
setGateMessage ( '' )
2026-08-13 14:01:43 +08:00
try {
2026-08-16 01:25:09 +08:00
await invoke < ZeroPointSnapshot > ( 'zero_point_bind' , { input : { number : gateNumber } } )
2026-08-16 01:12:54 +08:00
const snapshot = await invoke < ZeroPointSnapshot > ( 'zero_point_verify' )
setZeroPoint ( snapshot )
2026-08-16 12:54:47 +08:00
if ( snapshot . route === 'verified' ) {
2026-08-16 21:41:10 +08:00
setActiveDomainInfo ( '' )
setGateOpen ( false )
setGateWelcomeLine ( createWorldWelcome ( snapshot ) )
2026-08-16 01:12:54 +08:00
setGateRising ( true )
2026-08-16 01:25:09 +08:00
window . setTimeout ( ( ) = > { setGateRising ( false ) ; setGateStage ( 'key' ) } , 2600 )
2026-08-16 01:12:54 +08:00
} else {
2026-08-16 01:25:09 +08:00
setGateMessage ( '编号无效' )
2026-08-16 01:12:54 +08:00
}
2026-08-13 14:01:43 +08:00
} catch ( error ) {
2026-08-16 01:12:54 +08:00
setGateMessage ( humanError ( error , 'login' ) )
2026-08-13 14:01:43 +08:00
} finally {
2026-08-16 01:12:54 +08:00
setGateBusy ( false )
2026-08-13 14:01:43 +08:00
}
}
2026-08-15 15:02:35 +08:00
useEffect ( ( ) = > { if ( view === 'receipts' ) void loadReceipts ( ) } , [ loadReceipts , view ] )
const visibleDocuments = useMemo ( ( ) = > {
if ( ! searchResults ) return knowledge . documents
return searchResults . map ( ( result ) = > knowledge . documents . find ( ( item ) = > item . source === result . source && item . path === result . path ) ) . filter ( Boolean ) as KnowledgeDocumentSummary [ ]
} , [ knowledge . documents , searchResults ] )
const tree = useMemo ( ( ) = > knowledgeTree ( visibleDocuments ) , [ visibleDocuments ] )
const activeSummary = activeDocument
? knowledge . documents . find ( ( item ) = > item . source === activeDocument . source && item . path === activeDocument . path )
: undefined
const parsedDocument = useMemo ( ( ) = > activeDocument ? splitFrontmatter ( activeDocument . body ) : null , [ activeDocument ] )
const outline = useMemo ( ( ) = > activeDocument ? documentOutline ( activeDocument . body ) : [ ] , [ activeDocument ] )
2026-08-16 00:02:05 +08:00
const docStats = useMemo ( ( ) = > {
if ( ! activeDocument ) return null
const chars = activeDocument . body . replace ( /\s+/g , '' ) . length
return { chars , minutes : Math.max ( 1 , Math . ceil ( chars / 400 ) ) }
} , [ activeDocument ] )
useEffect ( ( ) = > { setActiveHeadingId ( null ) } , [ activeDocument ? . path ] )
useEffect ( ( ) = > {
if ( ! activeHeadingId ) return
document . querySelector ( '.outline-list button.active' ) ? . scrollIntoView ( { block : 'nearest' } )
} , [ activeHeadingId ] )
const handleReaderScroll = ( event : React.UIEvent < HTMLDivElement > ) = > {
const container = event . currentTarget
if ( headingScrollTimer . current !== null ) return
headingScrollTimer . current = window . setTimeout ( ( ) = > {
headingScrollTimer . current = null
const containerTop = container . getBoundingClientRect ( ) . top
let current : string | null = null
for ( const heading of Array . from ( container . querySelectorAll < HTMLElement > ( 'h1[id], h2[id], h3[id], h4[id]' ) ) ) {
if ( heading . getBoundingClientRect ( ) . top - containerTop <= 28 ) current = heading . id
else break
}
setActiveHeadingId ( current )
} , 100 )
}
2026-08-15 15:02:35 +08:00
const connectionOnline = status . directConnectionCount > 0
const connectionLabel = connectionOnline
? '本地原生连接 · 客户端在线'
: status . resumableSessionCount > 0
? '原生会话可续接 · 当前离线'
: '原生接口已驻留 · 尚无客户端'
const openDocument = async ( source : KnowledgeSource , path : string ) = > {
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
setEditing ( false )
2026-08-13 14:01:43 +08:00
try {
2026-08-15 15:02:35 +08:00
const document = await invoke < KnowledgeDocument > ( 'read_knowledge_document' , { input : { source , path } } )
setActiveDocument ( document )
setDraft ( document . body )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
2026-08-13 14:01:43 +08:00
}
2026-08-15 15:02:35 +08:00
const openWiki = ( target : string ) = > {
const normalized = target . replace ( /\.md$/i , '' ) . toLowerCase ( )
const match = knowledge . documents . find ( ( item ) = >
item . title . toLowerCase ( ) === normalized || item . path . replace ( /\.md$/i , '' ) . toLowerCase ( ) . endsWith ( normalized ) )
if ( match ) void openDocument ( match . source , match . path )
else setKnowledgeMessage ( ` 未找到链接页面: ${ target } ` )
}
const runSearch = async ( event : React.FormEvent ) = > {
event . preventDefault ( )
if ( ! searchQuery . trim ( ) ) { setSearchResults ( null ) ; return }
setKnowledgeBusy ( true )
2026-08-13 14:11:48 +08:00
try {
2026-08-15 15:02:35 +08:00
setSearchResults ( await invoke < KnowledgeSearchResult [ ] > ( 'search_knowledge' , { input : { query : searchQuery } } ) )
setActiveDocument ( null )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
2026-08-13 14:11:48 +08:00
}
2026-08-15 15:02:35 +08:00
const importKnowledge = async ( ) = > {
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
2026-08-13 14:11:48 +08:00
try {
2026-08-15 15:02:35 +08:00
const result = await invoke < KnowledgeImportResult | null > ( 'select_and_import_knowledge_folder' )
if ( ! result ) return
setKnowledge ( result . snapshot )
setSearchResults ( null )
setSearchQuery ( '' )
setLastKnowledgeReceipt ( result . gitCommit )
setKnowledgeMessage ( ` 导入完成:新增 ${ result . importedDocuments } ,已存在 ${ result . existingDocuments } ,冲突 ${ result . conflicts } 。 ` )
if ( result . firstDocument ) await openDocument ( 'native' , result . firstDocument )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
2026-08-13 14:01:43 +08:00
}
2026-08-16 00:02:05 +08:00
const downloadDocument = async ( format : 'md' | 'html' ) = > {
if ( ! activeDocument ) return
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
try {
let body = activeDocument . body
if ( format === 'html' ) {
const parsed = splitFrontmatter ( body )
const article = markdownHtml ( parsed . content )
body = ` <!DOCTYPE html><html lang="zh"><head><meta charset="utf-8"><title> ${ activeDocument . title } </title><style>body{max-width:820px;margin:40px auto;padding:0 24px;font:15px/1.8 -apple-system,'Segoe UI','PingFang SC','Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#1f2330;background:#fff}h1,h2,h3{line-height:1.4}blockquote{margin:20px 0;padding:12px 16px;border-left:3px solid #a78bfa;background:#f3f0ff;border-radius:8px}table{border-collapse:collapse;width:100%}td,th{border:1px solid #ddd;padding:7px 10px}pre{background:#f4f5f8;padding:14px;border-radius:8px;overflow:auto}img{max-width:100%}</style></head><body><h1> ${ activeDocument . title } </h1> ${ article } </body></html> `
}
const receipt = await invoke < { path : string ; bytes : number } | null > ( 'export_knowledge_document' , { input : { title : activeDocument.title , extension : format , body } } )
setKnowledgeMessage ( receipt ? ` 已下载到 ${ receipt . path } ` : '已取消下载。' )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
}
const printDocument = async ( ) = > {
setKnowledgeMessage ( '' )
try {
await invoke ( 'print_knowledge_document' )
setKnowledgeMessage ( '打印框里选「存储为 PDF」即可得到 PDF 文件。' )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
}
const reloadKnowledge = async ( ) = > {
const snapshot = await invoke < KnowledgeSnapshot > ( 'get_knowledge_snapshot' )
setKnowledge ( snapshot )
}
const createDocument = async ( ) = > {
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
try {
const receipt = await invoke < { removed : string ; pages : number } > ( 'create_knowledge_document' , { input : { title : '未命名页面' } } )
setKnowledgeMessage ( ` 已新建空白页「 ${ receipt . removed . replace ( /\.md$/ , '' ) } 」,正在编辑。 ` )
setSearchResults ( null )
await reloadKnowledge ( )
await openDocument ( 'native' , receipt . removed )
setEditing ( true )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
}
const removeDocument = async ( ) = > {
if ( ! activeDocument ) return
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
try {
const receipt = await invoke < { removed : string ; pages : number } | null > ( 'delete_knowledge_document' , { input : { source : activeDocument.source , path : activeDocument.path } } )
if ( receipt ) {
setKnowledgeMessage ( ` 已移入回收站: ${ receipt . removed } (可找回) ` )
setActiveDocument ( null )
setSearchResults ( null )
await reloadKnowledge ( )
} else setKnowledgeMessage ( '已取消删除。' )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
}
const removeFolder = async ( folder : TreeNode ) = > {
setKnowledgeBusy ( true )
setKnowledgeMessage ( '' )
try {
const receipt = await invoke < { removed : string ; pages : number } | null > ( 'delete_knowledge_folder' , { input : { folder : folder.key } } )
if ( receipt ) {
setKnowledgeMessage ( ` 文件夹「 ${ folder . name } 」( ${ receipt . pages } 页)已移入回收站(可找回) ` )
setActiveDocument ( null )
setSearchResults ( null )
await reloadKnowledge ( )
} else setKnowledgeMessage ( '已取消删除。' )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
}
2026-08-15 15:02:35 +08:00
const saveDocument = async ( ) = > {
if ( ! activeDocument ? . writable ) return
setKnowledgeBusy ( true )
2026-08-13 14:01:43 +08:00
try {
2026-08-15 15:02:35 +08:00
const result = await invoke < KnowledgeSaveResult > ( 'save_knowledge_document' , {
input : { path : activeDocument.path , body : draft , expectedContentSha256 : activeDocument.contentSha256 } ,
2026-08-13 14:11:48 +08:00
} )
2026-08-15 15:02:35 +08:00
setActiveDocument ( result . document )
setDraft ( result . document . body )
setEditing ( false )
setLastKnowledgeReceipt ( result . gitCommit )
setKnowledgeMessage ( result . state === 'UNCHANGED' ? '内容没有变化。' : '页面已保存,并写入本机 Git 回执。' )
await refreshCore ( )
} catch ( error ) { setKnowledgeMessage ( humanError ( error , 'knowledge' ) ) }
finally { setKnowledgeBusy ( false ) }
2026-08-13 14:11:48 +08:00
}
2026-08-15 15:02:35 +08:00
const cloneCodeChannel = async ( event : React.FormEvent ) = > {
event . preventDefault ( )
if ( ! cloneUrl . trim ( ) ) return
setCodeBusy ( true )
setCodeMessage ( '' )
2026-08-13 14:45:17 +08:00
try {
2026-08-15 15:02:35 +08:00
const snapshot = await invoke < CodeChannelSnapshot > ( 'clone_code_channel' , { input : { url : cloneUrl } } )
setCodeChannels ( snapshot )
setCloneUrl ( '' )
setCodeMessage ( '代码频道已克隆并登记。' )
} catch ( error ) { setCodeMessage ( humanError ( error , 'code' ) ) }
finally { setCodeBusy ( false ) }
2026-08-13 14:11:48 +08:00
}
2026-08-15 15:02:35 +08:00
const selectLocalCodeChannel = async ( ) = > {
setCodeBusy ( true )
setCodeMessage ( '' )
2026-08-13 14:11:48 +08:00
try {
2026-08-15 15:02:35 +08:00
const snapshot = await invoke < CodeChannelSnapshot | null > ( 'select_local_code_channel' )
if ( snapshot ) {
setCodeChannels ( snapshot )
setCodeMessage ( '本地代码频道已登记,原文件未移动。' )
2026-08-13 14:45:17 +08:00
}
2026-08-15 15:02:35 +08:00
} catch ( error ) { setCodeMessage ( humanError ( error , 'code' ) ) }
finally { setCodeBusy ( false ) }
2026-08-13 14:11:48 +08:00
}
2026-08-15 15:02:35 +08:00
const browseChannel = async ( channel : CodeChannelEntry , path = '' ) = > {
setCodeBusy ( true )
setActiveChannel ( channel )
setActiveCodeFile ( null )
2026-08-13 14:45:17 +08:00
try {
2026-08-15 15:02:35 +08:00
setCodeTree ( await invoke < CodeTreeSnapshot > ( 'browse_code_channel' , { input : { channelId : channel.channelId , path } } ) )
} catch ( error ) { setCodeMessage ( humanError ( error , 'code' ) ) }
finally { setCodeBusy ( false ) }
}
const openCodeEntry = async ( entry : CodeTreeEntry ) = > {
if ( ! activeChannel ) return
if ( entry . kind === 'directory' ) { await browseChannel ( activeChannel , entry . path ) ; return }
setCodeBusy ( true )
setCodeMode ( 'human' )
try {
setActiveCodeFile ( await invoke < CodeFileProjection > ( 'read_code_channel_file' , {
input : { channelId : activeChannel.channelId , path : entry.path } ,
} ) )
} catch ( error ) { setCodeMessage ( humanError ( error , 'code' ) ) }
finally { setCodeBusy ( false ) }
2026-08-13 14:45:17 +08:00
}
2026-08-15 15:02:35 +08:00
const codeParent = ( ) = > codeTree ? . path . split ( '/' ) . filter ( Boolean ) . slice ( 0 , - 1 ) . join ( '/' ) || ''
2026-08-13 14:45:17 +08:00
2026-08-16 00:02:05 +08:00
const performRepoLogin = async ( event : React.FormEvent ) = > {
event . preventDefault ( )
setLoginBusy ( true )
setLoginMessage ( '' )
try {
const receipt = await invoke < LoginReceipt > ( 'perform_code_repo_login' , { username : loginUsername.trim ( ) , password : loginPassword } )
// 五湖开场:校验通过=第五域从湖面浮起,镜头沉入湖中再进场。
setLoginRising ( true )
await new Promise ( ( resolve ) = > setTimeout ( resolve , 1300 ) )
2026-08-16 18:25:57 +08:00
// 账号切换时先清空上一账号的内存投影,绝不让旧页面在新会话首帧闪现。
setPersonal ( previewPersonal )
setKnowledge ( previewKnowledge )
setCodeChannels ( previewCode )
setActiveDocument ( null )
setActiveChannel ( null )
setCodeTree ( null )
setActiveCodeFile ( null )
setReceipts ( [ ] )
2026-08-16 15:10:31 +08:00
setRepoLogin ( { username : receipt.username , host : receipt.host , domain : receipt.domain , signedInAtUnixMs : Date.now ( ) } )
2026-08-16 21:24:24 +08:00
setWorldStage ( 'domain' )
setView ( 'overview' )
2026-08-16 00:02:05 +08:00
setLoginRising ( false )
setLoginPassword ( '' )
} catch ( error ) { setLoginMessage ( humanError ( error , 'login' ) ) }
finally { setLoginBusy ( false ) }
}
2026-08-16 20:49:13 +08:00
const changeFirstLoginPassword = async ( event : React.FormEvent ) = > {
event . preventDefault ( )
if ( newLoginPassword !== confirmLoginPassword ) {
setLoginMessage ( '两次输入的新密码不一致。' )
return
}
setLoginBusy ( true )
setLoginMessage ( '' )
try {
await invoke ( 'change_enterprise_first_login_password' , {
username : loginUsername.trim ( ) ,
currentPassword : loginPassword ,
newPassword : newLoginPassword ,
} )
setLoginPassword ( newLoginPassword )
setNewLoginPassword ( '' )
setConfirmLoginPassword ( '' )
setPasswordChangeMode ( false )
setLoginMessage ( '密码已更新。请用新密码验证并进入。' )
} catch ( error ) { setLoginMessage ( humanError ( error , 'login' ) ) }
finally { setLoginBusy ( false ) }
}
2026-08-16 00:02:05 +08:00
const signOutRepo = async ( ) = > {
try { await invoke ( 'sign_out_code_repo_login' ) } catch { /* 登出以本机清场为准 */ }
2026-08-16 18:25:57 +08:00
setPersonal ( previewPersonal )
setKnowledge ( previewKnowledge )
setCodeChannels ( previewCode )
setActiveDocument ( null )
setActiveChannel ( null )
setCodeTree ( null )
setActiveCodeFile ( null )
setReceipts ( [ ] )
2026-08-16 21:24:24 +08:00
setEnterpriseEntry ( null )
setEnterpriseReceiptMessage ( '' )
setWorldStage ( 'domain' )
2026-08-16 00:02:05 +08:00
setRepoLogin ( null )
}
2026-08-16 21:24:24 +08:00
const confirmEnterpriseRelationship = async ( decision : 'CONFIRM' | 'REJECT' ) = > {
setEnterpriseReceiptBusy ( true )
setEnterpriseReceiptMessage ( '' )
try {
await invoke ( 'confirm_enterprise_persona_relationship' , {
decision ,
idempotencyKey : ` relationship- ${ crypto . randomUUID ( ) . replaceAll ( '-' , '' ) } ` ,
} )
await loadEnterpriseEntry ( )
setEnterpriseReceiptMessage ( decision === 'CONFIRM' ? '人格关系确认已签署并留存。' : '人格关系异议已签署并留存。' )
} catch ( error ) {
setEnterpriseReceiptMessage ( humanError ( error , 'login' ) )
} finally {
setEnterpriseReceiptBusy ( false )
}
}
const submitEnterpriseResponsibility = async ( decision : 'ACCEPT' | 'DECLINE' ) = > {
if ( ! enterpriseEntry ) return
setEnterpriseReceiptBusy ( true )
setEnterpriseReceiptMessage ( '' )
try {
await invoke ( 'submit_enterprise_responsibility_receipt' , {
decision ,
note : responsibilityNote.trim ( ) ,
responsibilityVersion : enterpriseEntry.registry_version ,
idempotencyKey : ` responsibility- ${ crypto . randomUUID ( ) . replaceAll ( '-' , '' ) } ` ,
} )
setResponsibilityNote ( '' )
await loadEnterpriseEntry ( )
setEnterpriseReceiptMessage ( decision === 'ACCEPT' ? '域责任已由本人接受并签署。' : '域责任拒绝回执已留存。' )
} catch ( error ) {
setEnterpriseReceiptMessage ( humanError ( error , 'login' ) )
} finally {
setEnterpriseReceiptBusy ( false )
}
}
2026-08-15 15:02:35 +08:00
const initializeIdentity = async ( event : React.FormEvent ) = > {
event . preventDefault ( )
setIdentityBusy ( true )
try {
setPersonal ( await invoke < PersonalChannelSnapshot > ( 'initialize_personal_channel' , { input : { displayName } } ) )
setDisplayName ( '' )
} catch ( error ) { setIdentityMessage ( humanError ( error , 'identity' ) ) }
finally { setIdentityBusy ( false ) }
}
2026-08-13 13:52:59 +08:00
const issueInvitation = async ( ) = > {
2026-08-15 15:02:35 +08:00
setSystemBusy ( true )
setSystemMessage ( '' )
2026-08-13 13:52:59 +08:00
try {
2026-08-15 15:02:35 +08:00
setTicket ( await invoke < DiscoveryTicketReceipt > ( 'issue_direct_local_discovery_ticket' , {
2026-08-13 13:52:59 +08:00
input : {
accountId : getOrCreateLocalId ( 'hololake-local-account' , 'human-local' ) ,
laneId : 'personal-channel' ,
clientInstanceId : getOrCreateLocalId ( 'hololake-external-ai-client' , 'external-ai' ) ,
} ,
2026-08-15 15:02:35 +08:00
} ) )
setSystemMessage ( '一次性发现凭据已生成。连接成功后,状态条会显示真实在线数。' )
} catch ( error ) { setSystemMessage ( humanError ( error , 'system' ) ) }
finally { setSystemBusy ( false ) }
2026-08-13 13:52:59 +08:00
}
const invitationText = ticket ? JSON . stringify ( {
schema : 'hololake.direct-local-invitation/v1' ,
connectorArgument : '--connector' ,
openSession : {
operation : 'OPEN_SESSION' ,
input : {
accountId : window.localStorage.getItem ( 'hololake-local-account' ) ,
laneId : ticket.laneId ,
clientInstanceId : ticket.clientInstanceId ,
discoveryTicket : ticket.discoveryTicket ,
} ,
} ,
} , null , 2 ) : ''
const copyInvitation = async ( ) = > {
try {
await navigator . clipboard . writeText ( invitationText )
2026-08-15 15:02:35 +08:00
setSystemMessage ( '连接凭据已复制。' )
} catch { setSystemMessage ( '复制失败,请手动选择凭据。' ) }
}
const checkUpdate = async ( ) = > {
setSystemBusy ( true )
try {
const result = await invoke < ReleaseCheckReceipt > ( 'check_hololake_update' )
setReleaseCandidate ( result . candidate || null )
setSystemMessage ( result . candidate ? '发现已签名更新。' : '当前没有可安装更新。' )
} catch ( error ) { setSystemMessage ( humanError ( error , 'system' ) ) }
finally { setSystemBusy ( false ) }
}
const installUpdate = async ( ) = > {
if ( ! releaseCandidate ) return
setSystemBusy ( true )
try {
await invoke ( 'confirm_hololake_update_install' , {
input : { candidateId : releaseCandidate.candidateId , confirmationToken : releaseCandidate.confirmationToken } ,
} )
setSystemMessage ( '更新安装已启动。' )
} catch ( error ) { setSystemMessage ( humanError ( error , 'system' ) ) }
finally { setSystemBusy ( false ) }
2026-08-13 13:52:59 +08:00
}
2026-08-16 00:02:05 +08:00
const bindZeroPoint = async ( ) = > {
if ( ! zpNumber . trim ( ) ) return
setZpBusy ( true ) ; setZpMessage ( '' )
try {
const bound = await invoke < ZeroPointSnapshot > ( 'zero_point_bind' , { input : { number : zpNumber . trim ( ) } } )
setZeroPoint ( bound )
const verified = await invoke < ZeroPointSnapshot > ( 'zero_point_verify' )
setZeroPoint ( verified )
2026-08-16 12:54:47 +08:00
setZpMessage ( verified . route === 'verified' ? '编号验证通过。' : '编号未通过验证,受限功能保持关闭。' )
2026-08-16 00:02:05 +08:00
} catch ( error ) { setZpMessage ( String ( error ) ) } finally { setZpBusy ( false ) }
}
const verifyZeroPoint = async ( ) = > {
setZpBusy ( true ) ; setZpMessage ( '' )
try {
const snapshot = await invoke < ZeroPointSnapshot > ( 'zero_point_verify' )
setZeroPoint ( snapshot )
2026-08-16 12:54:47 +08:00
setZpMessage ( snapshot . route === 'verified' ? '编号验证通过。' : '编号未通过验证,受限功能保持关闭。' )
2026-08-16 00:02:05 +08:00
} catch ( error ) { setZpMessage ( String ( error ) ) } finally { setZpBusy ( false ) }
}
const syncZeroPoint = async ( ) = > {
setZpBusy ( true ) ; setZpMessage ( '' )
try { setZeroPoint ( await invoke < ZeroPointSnapshot > ( 'zero_point_sync' ) ) } catch ( error ) { setZpMessage ( String ( error ) ) } finally { setZpBusy ( false ) }
}
2026-08-16 12:54:47 +08:00
const renderOverview = ( ) = > (
2026-08-15 15:02:35 +08:00
< section className = "content-page overview-page" >
< header className = "page-title" >
2026-08-16 12:54:47 +08:00
< div > < span className = "kicker" > PERSONAL CHANNEL < / span > < h1 > 个 人 频 道 < / h1 > < p > 统 一 访 问 本 机 知 识 、 代 码 、 运 行 状 态 与 可 核 验 回 执 。 < / p > < / div >
2026-08-15 15:02:35 +08:00
< button className = "evidence-pill" type = "button" onClick = { ( ) = > setView ( 'system' ) } > < i className = { connectionOnline ? 'online' : '' } / > { connectionLabel } < / button >
2026-08-13 13:52:59 +08:00
< / header >
2026-08-15 15:02:35 +08:00
< div className = "overview-grid" >
< button className = "metric-panel" type = "button" onClick = { ( ) = > setView ( 'knowledge' ) } > < span > 唯 一 知 识 页 < / span > < strong > { knowledge . uniqueDocumentCount } < / strong > < small > { knowledge . duplicateDocumentCount } 份 相 同 内 容 已 折 叠 < / small > < / button >
< button className = "metric-panel" type = "button" onClick = { ( ) = > setView ( 'code' ) } > < span > 代 码 频 道 < / span > < strong > { codeChannels . channels . length } < / strong > < small > 可 浏 览 、 可 转 译 、 本 机 只 读 接 入 < / small > < / button >
< button className = "metric-panel" type = "button" onClick = { ( ) = > setView ( 'receipts' ) } > < span > 可 核 验 回 执 < / span > < strong > { personal . integrity . receiptCount + status . pnccReceiptCount } < / strong > < small > 身 份 、 知 识 与 代 码 证 据 < / small > < / button >
< / div >
< div className = "overview-columns" >
< section className = "plain-panel recent-panel" >
< header > < div > < h2 > 最 近 知 识 < / h2 > < p > 从 真 实 页 面 继 续 阅 读 < / p > < / div > < button type = "button" onClick = { ( ) = > setView ( 'knowledge' ) } > 打 开 工 作 台 < / button > < / header >
{ knowledge . documents . slice ( ) . sort ( ( a , b ) = > b . updatedAtUnixMs - a . updatedAtUnixMs ) . slice ( 0 , 7 ) . map ( ( item ) = >
< button className = "recent-row" key = { ` ${ item . source } : ${ item . path } ` } type = "button" onClick = { ( ) = > { setView ( 'knowledge' ) ; void openDocument ( item . source , item . path ) } } >
< Icon name = "file" / > < span > < b > { item . title } < / b > < small > { item . path } < / small > < / span > < time > { new Date ( item . updatedAtUnixMs ) . toLocaleDateString ( 'zh-CN' ) } < / time >
< / button > ) }
< / section >
< section className = "plain-panel system-proof" >
2026-08-16 12:54:47 +08:00
< header > < div > < h2 > 运 行 状 态 < / h2 > < p > 以 下 信 息 均 来 自 当 前 进 程 与 已 登 记 运 行 源 。 < / p > < / div > < / header >
2026-08-15 15:02:35 +08:00
< dl > < div > < dt > Unix 本 地 代 理 < / dt > < dd > { status . directLocalBrokerState } < / dd > < / div > < div > < dt > 在 线 客 户 端 < / dt > < dd > { status . directConnectionCount } < / dd > < / div > < div > < dt > 可 续 接 会 话 < / dt > < dd > { status . resumableSessionCount } < / dd > < / div > < div > < dt > PNCC 投 影 回 执 < / dt > < dd > { status . pnccReceiptCount } < / dd > < / div > < div > < dt > MCP < / dt > < dd > 备 用 / 恢 复 < / dd > < / div > < / dl >
< button className = "secondary-button" type = "button" onClick = { ( ) = > setView ( 'system' ) } > 查 看 验 证 入 口 < / button >
< / section >
< / div >
< / section >
)
2026-08-13 13:52:59 +08:00
2026-08-15 15:02:35 +08:00
const renderKnowledge = ( ) = > (
< section className = "full-workbench knowledge-page" >
< aside className = "knowledge-browser" >
< header > < div > < span className = "kicker" > KNOWLEDGE < / span > < h1 > 知 识 工 作 台 < / h1 > < / div > < button className = "icon-button" title = "导入文件夹" type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > void importKnowledge ( ) } > < Icon name = "import" / > < / button > < / header >
< form className = "search-box" onSubmit = { ( event ) = > void runSearch ( event ) } >
< Icon name = "search" / > < input aria-label = "检索知识" value = { searchQuery } placeholder = "检索标题与正文" onChange = { ( event ) = > setSearchQuery ( event . target . value ) } / >
{ searchResults && < button type = "button" onClick = { ( ) = > { setSearchResults ( null ) ; setSearchQuery ( '' ) } } > 清 除 < / button > }
< / form >
< div className = "knowledge-counts" > < span > { knowledge . uniqueDocumentCount } 篇 唯 一 文 档 < / span > < span > { knowledge . duplicateDocumentCount } 份 重 复 已 折 叠 < / span > < / div >
< div className = "knowledge-tree" aria-busy = { knowledgeBusy } >
{ visibleDocuments . length
? < KnowledgeTree node = { tree } depth = { 0 } expanded = { expanded } active = { activeDocument ? ` ${ activeDocument . source } : ${ activeDocument . path } ` : undefined }
onToggle = { ( key ) = > setExpanded ( ( current ) = > { const next = new Set ( current ) ; if ( next . has ( key ) ) next . delete ( key ) ; else next . add ( key ) ; return next } ) }
2026-08-16 00:02:05 +08:00
onOpen = { ( item ) = > void openDocument ( item . source , item . path ) }
onRemoveFolder = { ( folder ) = > void removeFolder ( folder ) }
folderMenuKey = { folderMenuKey }
onFolderMenuToggle = { ( key ) = > setFolderMenuKey ( ( current ) = > ( current === key ? null : key ) ) } / >
2026-08-15 15:02:35 +08:00
: < div className = "empty-state" > 没 有 匹 配 的 知 识 页 < / div > }
< / div >
< footer > { knowledgeMessage || ( lastKnowledgeReceipt ? ` Git ${ lastKnowledgeReceipt . slice ( 0 , 10 ) } ` : '导入会自动检查相同内容' ) } < / footer >
< / aside >
< main className = "document-workspace" >
{ activeDocument ? < >
< header className = "document-toolbar" >
< div className = "breadcrumbs" > < span > { activeDocument . source === 'legacy' ? 'HoloLake Era · 只读源' : '我的知识库' } < / span > < b > / < / b > < span > { activeDocument . path } < / span > < / div >
2026-08-16 00:02:05 +08:00
< div className = "toolbar-actions" > < button className = "toolbar-button" type = "button" title = { inspectorOpen ? '收起右侧大纲与证据' : '展开右侧大纲与证据' } onClick = { toggleInspector } > < Icon name = "chevron" / > { inspectorOpen ? '收起大纲' : '展开大纲' } < / button > { editing
2026-08-15 15:02:35 +08:00
? < > < button className = "toolbar-button" type = "button" onClick = { ( ) = > { setEditing ( false ) ; setDraft ( activeDocument . body ) } } > 取 消 < / button > < button className = "toolbar-button primary" type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > void saveDocument ( ) } > < Icon name = "save" / > 保 存 < / button > < / >
2026-08-16 00:02:05 +08:00
: < > < div className = "toolbar-menu" > < button className = "toolbar-button" type = "button" title = "页面操作与常用功能" disabled = { knowledgeBusy } onClick = { ( ) = > setSettingsMenuOpen ( ( current ) = > ! current ) } > < Icon name = "settings" / > 设 置 < Icon name = "chevronDown" / > < / button > { settingsMenuOpen && < div className = "toolbar-menu-pop" > < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void createDocument ( ) } } > < Icon name = "plus" / > 新 建 空 白 页 < / button > < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void importKnowledge ( ) } } > < Icon name = "import" / > 导 入 知 识 库 文 件 夹 < / button > { activeDocument . writable && < button type = "button" onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; setDraft ( activeDocument . body ) ; setEditing ( true ) } } > < Icon name = "edit" / > 编 辑 这 一 页 < / button > } { activeDocument . writable && < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void removeDocument ( ) } } > < Icon name = "trash" / > 删 除 这 一 页 ( 进 回 收 站 ) < / button > } < span className = "toolbar-menu-sep" / > < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void downloadDocument ( 'md' ) } } > < Icon name = "download" / > 下 载 Markdown < / button > < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void downloadDocument ( 'html' ) } } > < Icon name = "download" / > 下 载 网 页 ( . html ) < / button > < button type = "button" disabled = { knowledgeBusy } onClick = { ( ) = > { setSettingsMenuOpen ( false ) ; void printDocument ( ) } } > < Icon name = "download" / > 下 载 PDF ( 系 统 打 印 ) < / button > < / div > } < / div > < / > }
2026-08-15 15:02:35 +08:00
< / div >
< / header >
2026-08-16 00:02:05 +08:00
< div className = "document-scroll" onScroll = { handleReaderScroll } >
2026-08-15 15:02:35 +08:00
{ editing
? < textarea className = "document-editor" aria-label = "Markdown 编辑器" value = { draft } onChange = { ( event ) = > setDraft ( event . target . value ) } / >
2026-08-16 00:02:05 +08:00
: < > < header className = "reader-heading" > < h1 > { activeDocument . title } < / h1 > { docStats && < div className = "meta-stats" > { docStats . chars . toLocaleString ( ) } 字 · 约 { docStats . minutes } 分 钟 读 完 { parsedDocument ? . metadata ? . created ? ` · 创建于 ${ String ( parsedDocument . metadata . created ) . slice ( 0 , 10 ) } ` : '' } { parsedDocument ? . metadata ? . updated ? ` · 更新于 ${ String ( parsedDocument . metadata . updated ) . slice ( 0 , 10 ) } ` : '' } < / div > } { ( parsedDocument ? . tags ? . length ? ? 0 ) > 0 && < div className = "meta-tags" > { parsedDocument ! . tags . map ( ( tag ) = > < span key = { tag } className = { tagTint ( tag ) } > { tag } < / span > ) } < / div > } < div > { Object . entries ( parsedDocument ? . metadata || { } ) . filter ( ( [ key ] ) = > key !== 'tags' ) . slice ( 0 , 5 ) . map ( ( [ key , value ] ) = > < span key = { key } > { key } · { value } < / span > ) } < / div > < / header > < MarkdownDocument body = { activeDocument . body } knownTitles = { knowledge . documents . map ( ( doc ) = > doc . title ) } onWiki = { openWiki } / > < / > }
2026-08-13 13:52:59 +08:00
< / div >
2026-08-15 15:02:35 +08:00
< / > : < div className = "workbench-empty" > < span > 光 < / span > < h2 > 选 择 一 篇 知 识 页 < / h2 > < p > 目 录 、 正 文 与 版 本 证 据 会 在 同 一 个 工 作 台 中 展 开 。 < / p > < button className = "primary-button" type = "button" onClick = { ( ) = > void importKnowledge ( ) } > 导 入 本 地 文 件 夹 < / button > < / div > }
< / main >
2026-08-16 00:02:05 +08:00
{ inspectorOpen && < aside className = "document-inspector" >
2026-08-15 15:02:35 +08:00
< div className = "inspector-tabs" > < span className = "active" > 大 纲 < / span > < span > 来 源 < / span > < span > 回 执 < / span > < / div >
{ activeDocument ? < div className = "inspector-scroll" >
2026-08-16 00:02:05 +08:00
< section > < h2 > 页 面 大 纲 < / h2 > { outline . length ? < nav className = "outline-list" > { outline . map ( ( item ) = > < button key = { item . id } type = "button" title = "跳到该章节" className = { activeHeadingId === item . id ? 'active' : '' } style = { { paddingLeft : 8 + ( item . level - 1 ) * 11 } } onClick = { ( ) = > jumpToHeading ( item . id ) } > { item . title } < / button > ) } < / nav > : < p > 当 前 页 面 没 有 标 题 层 级 。 < / p > } < / section >
2026-08-15 15:02:35 +08:00
< section > < h2 > 来 源 < / h2 > < dl > < div > < dt > 知 识 根 < / dt > < dd > { activeDocument . source === 'native' ? 'HoloLake 本机 Git' : 'HoloLake Era 只读供体' } < / dd > < / div > < div > < dt > 路 径 < / dt > < dd > { activeDocument . path } < / dd > < / div > < div > < dt > 内 容 指 纹 < / dt > < dd > { activeDocument . contentSha256 . slice ( 0 , 16 ) } < / dd > < / div > < div > < dt > 相 同 内 容 < / dt > < dd > { activeSummary ? . duplicateCount || 0 } 份 已 折 叠 < / dd > < / div > < / dl > < / section >
< section > < h2 > 页 面 状 态 < / h2 > < p > { activeDocument . writable ? '可编辑;保存时写入本机 Git。' : '供体原件只读;不会被修改。' } < / p > { lastKnowledgeReceipt && < code > { lastKnowledgeReceipt } < / code > } < / section >
< / div > : < div className = "empty-state" > 选 择 页 面 后 显 示 大 纲 与 证 据 < / div > }
2026-08-16 00:02:05 +08:00
< / aside > }
2026-08-15 15:02:35 +08:00
< / section >
)
2026-08-13 13:52:59 +08:00
2026-08-15 15:02:35 +08:00
const renderCode = ( ) = > (
< section className = "full-workbench code-workbench" >
< aside className = "code-channels" >
2026-08-16 15:10:31 +08:00
< header > < div > < span className = "kicker" > GH - PNCC < / span > < h1 > 人 格 原 生 代 码 频 道 < / h1 > < / div > < / header >
< section className = "pncc-native-card" >
< div className = "pncc-native-card-title" > < span className = { userPncc ? 'identity-dot ready' : 'identity-dot' } / > < div > < b > { userPncc ? ` ${ userPncc . accountUsername } · GH-PNCC ` : '正在建立用户频道' } < / b > < small > { userPnccBusy ? '正在初始化本机 Git…' : userPncc ? ` ${ userPncc . branch } · ${ userPncc . gitHead . slice ( 0 , 8 ) } ` : '等待可信绑定' } < / small > < / div > < / div >
{ userPncc ? < dl > < div > < dt > 所 属 域 < / dt > < dd > { domainDisplayName ( userPncc . domain ) } < / dd > < / div > < div > < dt > 用 户 编 号 < / dt > < dd > { userPncc . userNumber } < / dd > < / div > < div > < dt > 耐 久 化 引 擎 < / dt > < dd > Git < / dd > < / div > < div > < dt > 可 见 上 层 < / dt > < dd > HoloLake 原 生 界 面 < / dd > < / div > < div > < dt > Forgejo 协 作 适 配 器 < / dt > < dd > 账 号 已 认 证 · 远 端 仓 未 绑 定 < / dd > < / div > < / dl > : < p > { userPnccMessage || '系统先按编号确定所属域,再通过该域的账号与节点入口建立频道。' } < / p > }
{ userPncc && < button type = "button" onClick = { ( ) = > { const channel = codeChannels . channels . find ( ( item ) = > item . channelId === userPncc . channelId ) ; if ( channel ) void browseChannel ( channel ) } } > 打 开 我 的 频 道 < / button > }
< / section >
< div className = "channel-section-label" > < b > 协 作 代 码 库 < / b > < small > 可 接 入 已 登 记 的 远 端 或 本 机 Git < / small > < / div >
2026-08-15 15:02:35 +08:00
< form className = "clone-form" onSubmit = { ( event ) = > void cloneCodeChannel ( event ) } >
< label htmlFor = "clone-url" > 克 隆 频 道 地 址 < / label > < div > < input id = "clone-url" type = "url" value = { cloneUrl } placeholder = "https://guanghulab.com/code/…" onChange = { ( event ) = > setCloneUrl ( event . target . value ) } / > < button disabled = { ! cloneUrl . trim ( ) || codeBusy } > 克 隆 < / button > < / div >
< / form >
< button className = "local-folder-button" type = "button" onClick = { ( ) = > void selectLocalCodeChannel ( ) } > < Icon name = "folder" / > 导 入 本 机 Git 文 件 夹 < / button >
< div className = "channel-selector" > { codeChannels . channels . map ( ( channel ) = > < button className = { activeChannel ? . channelId === channel . channelId ? 'active' : '' } key = { channel . channelId } type = "button" onClick = { ( ) = > void browseChannel ( channel ) } > < Icon name = "code" / > < span > < b > { channel . name } < / b > < small > { channel . branch } · { channel . gitHead . slice ( 0 , 8 ) } < / small > < / span > < / button > ) } < / div >
2026-08-16 15:10:31 +08:00
< footer > { codeMessage || userPnccMessage || 'Git 负责耐久化; Forgejo 仅作为远端协作适配器。' } < / footer >
2026-08-15 15:02:35 +08:00
< / aside >
< aside className = "repository-tree" >
< header > < button className = "icon-button" disabled = { ! codeTree ? . path } type = "button" onClick = { ( ) = > activeChannel && void browseChannel ( activeChannel , codeParent ( ) ) } > < Icon name = "back" / > < / button > < div > < b > { activeChannel ? . name || '尚未选择频道' } < / b > < small > / { codeTree ? . path || '' } < / small > < / div > < / header >
< div className = "repository-entries" > { codeTree ? . entries . map ( ( entry ) = > < button key = { entry . path } type = "button" onClick = { ( ) = > void openCodeEntry ( entry ) } > < Icon name = { entry . kind === 'directory' ? 'folder' : 'file' } / > < span > { entry . name } < / span > { entry . kind === 'directory' && < Icon name = "chevron" / > } < / button > ) || < div className = "empty-state" > 选 择 频 道 后 浏 览 仓 库 < / div > } < / div >
< / aside >
< main className = "code-reader" >
{ activeCodeFile ? < >
< header > < div > < span > { activeCodeFile . path } < / span > < small > { activeCodeFile . format . toUpperCase ( ) } · { activeCodeFile . sizeBytes . toLocaleString ( ) } bytes < / small > < / div > < div className = "mode-switch" > < button className = { codeMode === 'human' ? 'active' : '' } type = "button" onClick = { ( ) = > setCodeMode ( 'human' ) } > 知 识 视 图 < / button > < button className = { codeMode === 'source' ? 'active' : '' } type = "button" onClick = { ( ) = > setCodeMode ( 'source' ) } > 源 文 件 < / button > < / div > < / header >
< div className = "code-reader-scroll" > { codeMode === 'human' ? < MarkdownDocument body = { activeCodeFile . humanMarkdown } / > : < pre className = "source-code" > < code > { activeCodeFile . source } < / code > < / pre > } < / div >
2026-08-16 15:10:31 +08:00
< / > : < div className = "workbench-empty" > < span > & lt ; / & g t ; < / s p a n > < h 2 > G H - P N C C · 人 格 原 生 代 码 频 道 < / h 2 > < p > 这 是 H o l o L a k e 对 用 户 G i t 代 码 频 道 的 原 生 投 影 。 选 择 “ 打 开 我 的 频 道 ” 可 查 看 初 始 化 清 单 、 提 交 证 据 与 真 实 文 件 。 < / p > < / d i v > }
2026-08-13 13:52:59 +08:00
< / main >
2026-08-15 15:02:35 +08:00
< / section >
)
2026-08-13 13:52:59 +08:00
2026-08-15 15:02:35 +08:00
const renderReceipts = ( ) = > (
< section className = "content-page" >
< header className = "page-title" > < div > < span className = "kicker" > LOCAL RECEIPTS < / span > < h1 > 回 执 < / h1 > < p > 本 机 身 份 、 知 识 与 代 码 读 取 的 可 核 验 记 录 < / p > < / div > < / header >
< div className = "receipt-grid" >
< section className = "plain-panel" > < h2 > 个 人 空 间 < / h2 > { personal . recentEvents . map ( ( event ) = > < article className = "receipt-row" key = { event . eventId } > < span > { String ( event . sequence ) . padStart ( 2 , '0' ) } < / span > < div > < b > { event . summary } < / b > < small > { new Date ( event . occurredAtUnixMs ) . toLocaleString ( 'zh-CN' ) } · { event . receiptHash . slice ( 0 , 12 ) } < / small > < / div > < / article > ) } < / section >
< section className = "plain-panel" > < h2 > PNCC 读 取 投 影 < / h2 > { receipts . length ? receipts . map ( ( receipt ) = > < article className = "receipt-row" key = { receipt . sequence } > < span > { String ( receipt . sequence ) . padStart ( 2 , '0' ) } < / span > < div > < b > { receipt . kind } < / b > < small > { new Date ( Number ( receipt . observedAtUnixMs ) ) . toLocaleString ( 'zh-CN' ) } · { receipt . eventHash . slice ( 0 , 12 ) } < / small > < / div > < / article > ) : < div className = "empty-state" > 尚 无 PNCC 运 行 投 影 回 执 < / div > } < / section >
< / div >
< / section >
)
2026-08-13 13:52:59 +08:00
2026-08-15 15:02:35 +08:00
const renderSystem = ( ) = > (
< section className = "content-page" >
2026-08-16 12:54:47 +08:00
< header className = "page-title" > < div > < span className = "kicker" > SYSTEM EVIDENCE < / span > < h1 > 系 统 详 情 < / h1 > < p > 显 示 可 验 证 的 当 前 状 态 ; 无 法 读 取 的 项 目 将 明 确 标 记 为 不 可 用 。 < / p > < / div > < / header >
2026-08-15 15:02:35 +08:00
< div className = "system-grid" >
< section className = "plain-panel connection-panel" >
2026-08-16 15:10:31 +08:00
< header > < div > < h2 > 光 湖 近 场 连 接 < / h2 > < p > 外 部 AI 可 像 发 现 附 近 网 络 一 样 发 现 本 机 HoloLake ; MCP 仅 用 于 兼 容 与 恢 复 。 < / p > < / div > < span className = { nearbyDiscovery ? . automaticSameDeviceDiscovery ? 'status-chip online' : 'status-chip' } > { nearbyDiscovery ? . automaticSameDeviceDiscovery ? '本机自动发现已开启' : '自动发现不可用' } < / span > < / header >
< dl className = "evidence-list" > < div > < dt > 圆 湖 协 议 膜 < / dt > < dd > GLP / 1.0 · 协 议 外 输 入 丢 弃 < / dd > < / div > < div > < dt > 通 用 AI 访 客 < / dt > < dd > { nearbyDiscovery ? . genericAiVisitor === 'EXPRESSION_ONLY_READY' ? '可连接 · 仅语言表达' : '不可用' } < / dd > < / div > < div > < dt > 光 湖 人 格 体 < / dt > < dd > { nearbyDiscovery ? . guanghuPersona === 'BINDING_EVIDENCE_REQUIRED' ? '等待人格绑定证据' : '可连接' } < / dd > < / div > < div > < dt > 当 前 连 接 < / dt > < dd > { status . directConnectionCount } < / dd > < / div > < div > < dt > 局 域 网 发 现 < / dt > < dd > { nearbyDiscovery ? . localNetworkDiscovery === 'DEFERRED_UNTIL_ENCRYPTED_TRANSPORT_AND_APPROVAL' ? '等待加密传输与确认闭环' : '已开启' } < / dd > < / div > < div > < dt > MCP < / dt > < dd > 兼 容 / 恢 复 < / dd > < / div > < / dl >
< p className = "boundary-note" > 发 现 不 等 于 授 权 。 访 客 自 然 语 言 只 有 表 达 权 , 不 能 读 取 私 人 数 据 、 调 用 工 具 或 取 得 执 行 权 限 。 < / p >
{ ticket ? < > < button className = "secondary-button" type = "button" onClick = { ( ) = > void copyInvitation ( ) } > < Icon name = "copy" / > 复 制 兼 容 连 接 凭 据 < / button > < pre className = "invitation-data" > { invitationText } < / pre > < / > : < button className = "secondary-button" type = "button" disabled = { systemBusy } onClick = { ( ) = > void issueInvitation ( ) } > 生 成 兼 容 连 接 凭 据 < / button > }
2026-08-13 13:52:59 +08:00
< / section >
2026-08-16 00:02:05 +08:00
< section className = "plain-panel" >
2026-08-16 12:54:47 +08:00
< header > < div > < h2 > 京 东 主 控 · GH - PNCC < / h2 > < p > 通 过 固 定 只 读 通 道 回 读 主 控 节 点 ; 不 读 取 仓 库 正 文 , 不 授 予 写 入 权 限 。 < / p > < / div > < span className = { serverPnccReadback === 'LIVE' ? 'status-chip online' : 'status-chip' } > { serverPnccReadback === 'LIVE' ? '实时回读正常' : serverPnccReadback === 'UNAVAILABLE' ? '当前不可回读' : '正在读取' } < / span > < / header >
{ serverPncc ? < dl className = "evidence-list" >
< div > < dt > 人 格 仓 库 < / dt > < dd > { serverPncc . personaId } < / dd > < / div >
< div > < dt > 责 任 主 体 < / dt > < dd > { serverPncc . humanResponsibilitySubject } < / dd > < / div >
< div > < dt > 主 控 节 点 < / dt > < dd > { serverPncc . nodeId } < / dd > < / div >
< div > < dt > 驻 留 提 交 < / dt > < dd > { serverPncc . gitHead . slice ( 0 , 12 ) } < / dd > < / div >
< div > < dt > 主 租 约 < / dt > < dd > { serverPncc . primaryLeaseHeld ? '已持有' : '未持有' } < / dd > < / div >
< div > < dt > 载 体 绑 定 < / dt > < dd > { serverPncc . personaCarrierBound ? '已验证' : '未绑定 · 等待证据' } < / dd > < / div >
< div > < dt > 模 型 推 理 < / dt > < dd > { serverPncc . modelInferenceStarted ? '已启动' : '未启动' } < / dd > < / div >
< div > < dt > 现 实 执 行 < / dt > < dd > { serverPncc . realityExecutionAllowed ? '已授权' : '未授权' } < / dd > < / div >
< / dl > : < p className = "boundary-note" > 当 前 无 法 取 得 京 东 主 控 的 实 时 回 读 。 界 面 不 会 以 静 态 记 录 替 代 在 线 状 态 。 < / p > }
< button className = "secondary-button" type = "button" disabled = { serverPnccBusy } onClick = { ( ) = > void refreshServerPncc ( ) } > { serverPnccBusy ? '正在读取…' : '重新读取主控状态' } < / button >
< / section >
< section className = "plain-panel" > < header > < div > < h2 > 本 机 GH - PNCC 投 影 < / h2 > < p > 本 机 仓 库 挂 载 与 读 取 回 执 来 自 同 一 运 行 事 件 流 。 < / p > < / div > < / header > < dl className = "evidence-list" > < div > < dt > 已 挂 载 仓 库 < / dt > < dd > { status . codeRepositoryMountCount } < / dd > < / div > < div > < dt > 投 影 回 执 < / dt > < dd > { status . pnccReceiptCount } < / dd > < / div > < div > < dt > 当 前 结 论 < / dt > < dd > { status . codeRepositoryMountCount > 0 && status . pnccReceiptCount > 0 ? '已有可核验运行记录' : '接口已接入,尚无完整运行记录' } < / dd > < / div > < / dl > < / section >
< section className = "plain-panel" >
< header > < div > < h2 > 用 户 编 号 验 证 < / h2 > < p > 用 户 编 号 必 须 通 过 登 记 服 务 验 证 。 验 证 结 果 不 构 成 人 格 绑 定 、 模 型 载 体 绑 定 或 执 行 授 权 。 < / p > < / div > < span className = { zeroPoint ? . route === 'verified' ? 'status-chip online' : 'status-chip' } > { zeroPoint ? ( zeroPoint . route === 'verified' ? '验证有效' : '功能受限' ) : '正在读取' } < / span > < / header >
2026-08-16 00:02:05 +08:00
< dl className = "evidence-list" >
< div > < dt > 绑 定 状 态 < / dt > < dd > { zeroPoint ? ( zeroPoint . binding === 'bound' ? ` 已绑定( ${ zeroPoint . userNumber } ) ` : '等待绑定(空白态)' ) : '—' } < / dd > < / div >
2026-08-16 12:54:47 +08:00
< div > < dt > 协 议 来 源 < / dt > < dd > { zeroPoint ? protocolOriginDisplayName ( zeroPoint . protocol . origin ) : '—' } < / dd > < / div >
< div > < dt > 离 线 有 效 期 < / dt > < dd > { zeroPoint ? ` ${ zeroPoint . protocol . gracePeriodDays } 天 ` : '—' } < / dd > < / div >
< div > < dt > 同 步 状 态 < / dt > < dd > { zeroPoint ? . syncNote ? ? '—' } < / dd > < / div >
2026-08-16 00:02:05 +08:00
< / dl >
< div className = "zp-api-row" >
< input value = { zpNumber } placeholder = "输入用户编号,如 ICE-GL∞" onChange = { ( event ) = > setZpNumber ( event . target . value ) } / >
< button className = "secondary-button" type = "button" disabled = { zpBusy || ! zpNumber . trim ( ) } onClick = { ( ) = > void bindZeroPoint ( ) } > 绑 定 并 校 验 < / button >
< button className = "secondary-button" type = "button" disabled = { zpBusy || zeroPoint ? . binding !== 'bound' } onClick = { ( ) = > void verifyZeroPoint ( ) } > 重 新 校 验 编 号 < / button >
2026-08-16 12:54:47 +08:00
< button className = "secondary-button" type = "button" disabled = { zpBusy } onClick = { ( ) = > void syncZeroPoint ( ) } > 检 查 协 议 版 本 < / button >
2026-08-16 00:02:05 +08:00
< / div >
{ zpMessage && < p className = "global-message" > { zpMessage } < / p > }
< / section >
2026-08-16 12:54:47 +08:00
< section className = "plain-panel" > < header > < div > < h2 > 显 示 主 题 < / h2 > < p > 主 题 仅 调 整 视 觉 令 牌 , 不 改 变 功 能 、 权 限 或 路 由 。 < / p > < / div > < / header > < div className = "theme-options" > { themes . map ( ( choice ) = > < button className = { theme === choice . id ? 'active' : '' } key = { choice . id } type = "button" onClick = { ( ) = > setTheme ( choice . id ) } > < i className = { choice . id } / > < span > { choice . name } < / span > < / button > ) } < / div > < / section >
< section className = "plain-panel" > < header > < div > < h2 > 软 件 更 新 < / h2 > < p > 仅 接 受 已 登 记 信 任 根 验 证 通 过 的 HoloLake 发 布 包 。 < / p > < / div > < / header > { releaseCandidate ? < div className = "release-summary" > < b > HoloLake { releaseCandidate . version } < / b > < p > { releaseCandidate . notes } < / p > < button className = "primary-button" onClick = { ( ) = > void installUpdate ( ) } > 验 证 并 安 装 < / button > < / div > : < button className = "secondary-button" disabled = { systemBusy } onClick = { ( ) = > void checkUpdate ( ) } > 检 查 签 名 更 新 < / button > } < / section >
2026-08-15 15:02:35 +08:00
< / div >
{ systemMessage && < p className = "global-message" > { systemMessage } < / p > }
< / section >
2026-08-12 10:29:00 +08:00
)
2026-08-15 15:02:35 +08:00
2026-08-16 21:24:24 +08:00
const openWorldTool = ( nextView : ViewId ) = > {
setView ( nextView )
setWorldStage ( 'tool' )
}
const enterpriseRelationshipPending = Boolean ( repoLogin && repoLogin . domain !== 'FIFTH_DOMAIN' && enterpriseEntry && enterpriseEntry . relationship_confirmation ? . decision !== 'CONFIRM' )
const enterpriseResponsibilityPending = Boolean ( repoLogin && repoLogin . domain !== 'FIFTH_DOMAIN' && enterpriseEntry && ! enterpriseRelationshipPending && enterpriseEntry . responsibility_receipt ? . decision !== 'ACCEPT' )
2026-08-16 00:02:05 +08:00
if ( ! repoLogin ) {
2026-08-16 21:24:24 +08:00
const resolvedDomain = zeroPoint ? . resolvedDomain || ''
2026-08-16 21:37:50 +08:00
const activeGate = domainGates . find ( ( gate ) = > gate . domain === activeDomainInfo )
2026-08-16 21:24:24 +08:00
return < div className = { ` official-world ${ loginRising ? ' sinking' : '' } ` } >
< LakeAtmosphere / >
2026-08-16 21:37:50 +08:00
< header className = "world-titlebar" > < b > HoloLake < / b > < button type = "button" aria-label = "显示主题" onClick = { ( ) = > setTheme ( themes [ ( themes . findIndex ( ( item ) = > item . id === theme ) + 1 ) % themes . length ] . id ) } > ◌ < / button > < / header >
2026-08-16 21:24:24 +08:00
< main className = { ` world-scene ${ gateRising ? ' resolving' : '' } ${ gateStage === 'key' ? ' resolved' : '' } ` } >
< div className = "official-hero" > < h1 > 光 湖 语 言 系 统 · 通 用 人 工 智 能 操 作 平 台 < / h1 > < p > GH - AIOS · GUANGHU AI OPERATING SYSTEM < / p > < / div >
2026-08-16 21:37:50 +08:00
{ domainGates . map ( ( gate ) = > < LakePool key = { gate . domain } className = { gate . className } title = { gate . title } meta = { gate . gate } open = { gate . domain === 'FIFTH_DOMAIN' || ( gateStage === 'key' && gate . domain === resolvedDomain ) } risen = { ( gateRising || gateStage === 'key' ) && gate . domain === resolvedDomain } onClick = { ( ) = > gateStage === 'number' && setActiveDomainInfo ( gate . domain ) } / > ) }
{ activeGate && gateStage === 'number' && < >
< button className = "domain-info-scrim" type = "button" aria-label = "关闭域信息" onClick = { ( ) = > setActiveDomainInfo ( '' ) } / >
< section className = { ` domain-info-card info- ${ activeGate . className . slice ( 2 ) } ` } role = "dialog" aria-modal = "true" aria-label = { ` ${ activeGate . title } 系统信息 ` } >
< button className = "gate-close" type = "button" aria-label = "关闭域信息" title = "关闭" onClick = { ( ) = > setActiveDomainInfo ( '' ) } > × < / button >
< b > { activeGate . title } < / b > < small > { activeGate . gate } < / small >
< span className = "pool-facts" > { activeGate . facts . map ( ( [ label , value ] ) = > < span className = "pool-fact" key = { label } > < em > { label } < / em > < strong > { value } < / strong > < / span > ) } < / span >
< / section >
< / > }
2026-08-16 21:24:24 +08:00
{ gateRising ? < section className = "world-welcome" role = "status" >
< h2 > 编 号 { gateNumber } · 欢 迎 登 入 光 湖 语 言 世 界 < / h2 >
< p > RESOLVED · { domainDisplayName ( resolvedDomain ) } · 已 接 入 < / p >
2026-08-16 21:41:10 +08:00
{ gateWelcomeLine && < blockquote className = "world-impression" > { gateWelcomeLine } < / blockquote > }
2026-08-16 21:24:24 +08:00
< / section > : gateStage === 'number' ? < section className = { ` number-nucleus ${ gateOpen ? ' open' : '' } ` } title = "输入编号进入所属域" >
< button className = "nucleus-locus" type = "button" aria-expanded = { gateOpen } onClick = { ( ) = > setGateOpen ( true ) } > < i / > < b > 编 号 验 证 · 进 入 世 界 的 第 一 个 入 口 < / b > < / button >
2026-08-16 21:37:50 +08:00
{ gateOpen && < button
className = "gate-dismiss-layer"
type = "button"
aria - label = "关闭编号验证"
onClick = { ( ) = > { setGateOpen ( false ) ; setGateMessage ( '' ) } }
/ > }
2026-08-16 21:24:24 +08:00
< div className = "nucleus-panel" role = "dialog" aria-label = "编号验证" >
2026-08-16 21:37:50 +08:00
< button className = "gate-close" type = "button" aria-label = "关闭编号验证" title = "关闭" onClick = { ( ) = > { setGateOpen ( false ) ; setGateMessage ( '' ) } } > × < / button >
2026-08-16 21:24:24 +08:00
< h2 > 编 号 验 证 < / h2 >
2026-08-16 01:25:09 +08:00
< div className = "gate-pod-row" >
2026-08-16 01:54:57 +08:00
< input id = "gate-number" ref = { gateInputRef } aria-label = "编号" maxLength = { 48 } value = { gateNumber } placeholder = "如 ICE-GL∞" onChange = { ( event ) = > onGateInput ( event . target . value ) } onKeyDown = { ( event ) = > { if ( event . key === 'Enter' && gateNumber ) { event . preventDefault ( ) ; void gateVerifyNumber ( ) } } } / >
2026-08-16 21:24:24 +08:00
< button type = "button" className = { ` gate-inf ${ gateInf ? ' on' : '' } ` } title = "编号包含无限符号时启用" aria-pressed = { gateInf } onClick = { ( ) = > setGateInf ( ( value ) = > ! value ) } > ∞ < / button >
2026-08-16 01:25:09 +08:00
< / div >
2026-08-16 12:54:47 +08:00
< button className = "gate-submit" disabled = { gateBusy || ! gateNumber } onClick = { ( ) = > void gateVerifyNumber ( ) } > { gateBusy ? '正在验证…' : '验证编号' } < / button >
2026-08-16 01:25:09 +08:00
{ gateMessage && < p className = "gate-hint" > { gateMessage } < / p > }
2026-08-16 21:24:24 +08:00
< / div >
< / section > : < >
2026-08-16 21:41:10 +08:00
< section className = "resolved-heading" > < h2 > 编 号 { zeroPoint ? . userNumber || gateNumber } · 欢 迎 登 入 光 湖 语 言 世 界 < / h2 > < p > RESOLVED · { domainDisplayName ( resolvedDomain ) } · 已 接 入 < / p > { gateWelcomeLine && < blockquote className = "world-impression" > { gateWelcomeLine } < / blockquote > } < / section >
2026-08-16 21:24:24 +08:00
< section className = { ` domain-credential ${ loginRising ? ' fade-out' : '' } ` } role = "dialog" >
2026-08-16 20:49:13 +08:00
< form onSubmit = { ( event ) = > void ( passwordChangeMode ? changeFirstLoginPassword ( event ) : performRepoLogin ( event ) ) } >
2026-08-16 21:24:24 +08:00
< h3 > { domainDisplayName ( resolvedDomain ) } · 域 凭 证 < / h3 >
2026-08-16 01:25:09 +08:00
< input id = "repo-login-username" aria-label = "账号" autoFocus maxLength = { 40 } value = { loginUsername } placeholder = "账号" onChange = { ( event ) = > setLoginUsername ( event . target . value ) } / >
2026-08-16 20:49:13 +08:00
< input id = "repo-login-password" aria-label = { passwordChangeMode ? '一次性密码' : '密码' } type = "password" maxLength = { 512 } value = { loginPassword } placeholder = { passwordChangeMode ? '一次性密码' : '密码' } onChange = { ( event ) = > setLoginPassword ( event . target . value ) } / >
{ passwordChangeMode && < >
< input aria-label = "新密码" type = "password" minLength = { 14 } maxLength = { 128 } value = { newLoginPassword } placeholder = "设置新密码(至少 14 位)" onChange = { ( event ) = > setNewLoginPassword ( event . target . value ) } / >
< input aria-label = "确认新密码" type = "password" minLength = { 14 } maxLength = { 128 } value = { confirmLoginPassword } placeholder = "再次输入新密码" onChange = { ( event ) = > setConfirmLoginPassword ( event . target . value ) } / >
< / > }
< button className = "gate-submit" disabled = { loginBusy || ! loginUsername . trim ( ) || ! loginPassword || ( passwordChangeMode && ( ! newLoginPassword || ! confirmLoginPassword ) ) } > { loginBusy ? '正在处理…' : passwordChangeMode ? '修改密码' : '验证并进入' } < / button >
2026-08-16 01:25:09 +08:00
< / form >
{ loginMessage && < p className = "gate-hint" > { loginMessage } < / p > }
2026-08-16 20:49:13 +08:00
{ zeroPoint ? . resolvedDomain !== 'FIFTH_DOMAIN' && < button className = "gate-back" type = "button" onClick = { ( ) = > { setPasswordChangeMode ( ( value ) = > ! value ) ; setLoginMessage ( '' ) ; setNewLoginPassword ( '' ) ; setConfirmLoginPassword ( '' ) } } > { passwordChangeMode ? '返回正常登录' : '第一次使用?先修改一次性密码' } < / button > }
2026-08-16 12:54:47 +08:00
< button className = "gate-back" type = "button" onClick = { ( ) = > { setGateStage ( 'number' ) ; setGateMessage ( '' ) ; setLoginMessage ( '' ) } } > 返 回 编 号 验 证 < / button >
2026-08-16 01:12:54 +08:00
< / section >
2026-08-16 21:24:24 +08:00
< / > }
< / main >
< footer className = "world-footer" > < b > 光 湖 语 言 系 统 · 通 用 人 工 智 能 操 作 平 台 < / b > < span > GH - AIOS < / span > < / footer >
2026-08-16 00:02:05 +08:00
< / div >
}
2026-08-16 21:24:24 +08:00
return < div className = "official-world signed-in-world" >
< LakeAtmosphere / >
2026-08-16 21:37:50 +08:00
< header className = "world-titlebar" > < b > HoloLake < / b > < div className = "world-title-actions" > < span > { repoLogin . username } · { domainDisplayName ( repoLogin . domain ) } < / span > < button type = "button" onClick = { ( ) = > setTheme ( themes [ ( themes . findIndex ( ( item ) = > item . id === theme ) + 1 ) % themes . length ] . id ) } > ◌ < / button > < button type = "button" onClick = { ( ) = > void signOutRepo ( ) } > 退 出 < / button > < / div > < / header >
2026-08-16 21:24:24 +08:00
< main className = "world-scene signed-in-scene" >
{ worldStage === 'domain' && < section className = "domain-home" >
< div className = "world-location" > < h1 > { domainDisplayName ( repoLogin . domain ) } < / h1 > < p > 世 界 正 在 发 生 什 么 < / p > < / div >
< div className = "broadcast-stream" > < p > < i / > 光 湖 公 共 灯 塔 · 在 线 < / p > < p > < i / > 协 议 版 本 · { enterpriseEntry ? . registry_version || 'HLDP v1.0' } < / p > < p > < i / > HoloLake · V0 . 4.0 < / p > < / div >
< LakePool className = "home-primary" title = { repoLogin . domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道' } meta = { repoLogin . domain === 'FIFTH_DOMAIN' ? '进入私人系统' : '企业四域 · 工作入口' } open onClick = { ( ) = > setWorldStage ( 'channel' ) } / >
< LakePool className = "home-status" title = "湖面天气" meta = { connectionLabel } onClick = { ( ) = > openWorldTool ( 'system' ) } / >
< / section > }
{ worldStage === 'channel' && < section className = "channel-world" >
< button className = "world-back" type = "button" onClick = { ( ) = > setWorldStage ( 'domain' ) } > ← 退 回 域 首 页 < / button >
< div className = "world-location" > < h1 > { repoLogin . domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道' } < / h1 > < p > 选 择 路 径 · 湖 面 向 下 一 层 展 开 < / p > < / div >
< LakePool className = "channel-main" title = "我的频道" meta = "频道全景" open onClick = { ( ) = > openWorldTool ( 'overview' ) } / >
< LakePool className = "channel-knowledge" title = "知识空间" meta = { ` ${ knowledge . uniqueDocumentCount } 唯一页 ` } onClick = { ( ) = > openWorldTool ( 'knowledge' ) } / >
< LakePool className = "channel-code" title = "人格代码频道" meta = { ` ${ codeChannels . channels . length } 个仓库 ` } onClick = { ( ) = > openWorldTool ( 'code' ) } / >
< LakePool className = "channel-light" title = "光之湖" meta = "人格体居所" onClick = { ( ) = > openWorldTool ( 'receipts' ) } / >
< LakePool className = "channel-status" title = "湖面天气" meta = { connectionLabel } onClick = { ( ) = > openWorldTool ( 'system' ) } / >
< / section > }
{ worldStage === 'tool' && < section className = { ` world-tool world-tool- ${ view } ` } >
< header className = "tool-worldbar" > < button type = "button" onClick = { ( ) = > setWorldStage ( 'channel' ) } > ← 退 回 频 道 全 景 < / button > < b > { viewLabels [ view ] } < / b > < span > { domainDisplayName ( repoLogin . domain ) } < / span > < / header >
< div className = { ` tool-projection ${ inspectorOpen ? '' : ' inspector-closed' } ` } > { view === 'overview' ? renderOverview ( ) : view === 'knowledge' ? renderKnowledge ( ) : view === 'code' ? renderCode ( ) : view === 'receipts' ? renderReceipts ( ) : renderSystem ( ) } < / div >
< / section > }
< / main >
< footer className = "world-footer" > < b > 光 湖 语 言 系 统 · 通 用 人 工 智 能 操 作 平 台 < / b > < span > GH - AIOS < / span > < / footer >
{ enterpriseEntryBusy && < div className = "enterprise-gate" > < section > < p > 正 在 读 取 企 业 责 任 登 记 … … < / p > < / section > < / div > }
{ enterpriseRelationshipPending && enterpriseEntry && < div className = "enterprise-gate" > < section className = "enterprise-receipt-panel" role = "dialog" aria-modal = "true" >
< span className = "receipt-step" > 关 系 确 认 · 独 立 回 执 < / span > < h2 > { enterpriseEntry . subject . name } , 请 核 对 人 格 关 系 < / h2 > < p > 以 下 信 息 来 自 企 业 注 册 表 。 AGE 是 人 格 体 物 种 , 不 是 具 体 人 格 体 编 号 ; 本 次 确 认 也 不 代 表 人 格 体 接 受 现 实 责 任 。 < / p >
< dl > < div > < dt > 人 类 编 号 < / dt > < dd > { enterpriseEntry . canonical_id } < / dd > < / div > < div > < dt > 责 任 域 < / dt > < dd > { domainDisplayName ( enterpriseEntry . subject . domain ) } < / dd > < / div > { enterpriseEntry . persona_relationships . map ( ( persona ) = > < div key = { persona . persona_id } > < dt > 人 格 体 < / dt > < dd > { persona . persona_id } · { persona . species } < / dd > < / div > ) } < / dl >
< div className = "receipt-actions" > < button type = "button" disabled = { enterpriseReceiptBusy } onClick = { ( ) = > void confirmEnterpriseRelationship ( 'REJECT' ) } > 登 记 异 议 < / button > < button className = "primary" type = "button" disabled = { enterpriseReceiptBusy } onClick = { ( ) = > void confirmEnterpriseRelationship ( 'CONFIRM' ) } > 确 认 关 系 并 签 署 < / button > < / div > { enterpriseReceiptMessage && < p className = "receipt-message" > { enterpriseReceiptMessage } < / p > }
< / section > < / div > }
{ enterpriseResponsibilityPending && enterpriseEntry && < div className = "enterprise-gate" > < section className = "enterprise-receipt-panel" role = "dialog" aria-modal = "true" >
< span className = "receipt-step" > 责 任 接 受 · 独 立 回 执 < / span > < h2 > 是 否 接 受 { domainDisplayName ( enterpriseEntry . subject . domain ) } 的 责 任 < / h2 > < p > 工 作 入 口 为 零 感 域 下 的 光 湖 频 道 ; 你 的 责 任 仓 库 保 持 私 有 、 独 立 。 个 人 频 道 仍 需 由 你 自 己 的 节 点 另 行 接 入 。 < / p >
< dl > < div > < dt > 工 作 仓 库 < / dt > < dd > { enterpriseEntry . repository_binding . repository } < / dd > < / div > < div > < dt > 仓 库 账 号 < / dt > < dd > { enterpriseEntry . repository_binding . username } < / dd > < / div > < div > < dt > 责 任 版 本 < / dt > < dd > { enterpriseEntry . registry_version } < / dd > < / div > < / dl >
< textarea value = { responsibilityNote } maxLength = { 1000 } placeholder = "可选:填写责任确认说明" onChange = { ( event ) = > setResponsibilityNote ( event . target . value ) } / >
< div className = "receipt-actions" > < button type = "button" disabled = { enterpriseReceiptBusy } onClick = { ( ) = > void submitEnterpriseResponsibility ( 'DECLINE' ) } > 暂 不 接 受 < / button > < button className = "primary" type = "button" disabled = { enterpriseReceiptBusy } onClick = { ( ) = > void submitEnterpriseResponsibility ( 'ACCEPT' ) } > 接 受 责 任 并 签 署 < / button > < / div > { enterpriseReceiptMessage && < p className = "receipt-message" > { enterpriseReceiptMessage } < / p > }
< / section > < / div > }
2026-08-16 12:54:47 +08:00
{ personal . state !== 'UNAVAILABLE' && ! personal . identity && < div className = "onboarding-backdrop" > < section className = "onboarding-card" role = "dialog" aria-modal = "true" > < span className = "onboarding-mark" > 光 湖 < / span > < span className = "kicker" > FIRST LOCAL ENTRY < / span > < h1 > 初 始 化 个 人 频 道 < / h1 > < p > 此 设 置 仅 执 行 一 次 。 知 识 、 代 码 频 道 与 运 行 记 录 将 保 存 在 当 前 设 备 。 < / p > < form onSubmit = { ( event ) = > void initializeIdentity ( event ) } > < label htmlFor = "display-name" > 显 示 名 称 < / label > < input id = "display-name" autoFocus maxLength = { 80 } value = { displayName } placeholder = "请输入显示名称" onChange = { ( event ) = > setDisplayName ( event . target . value ) } / > < button className = "primary-button" disabled = { identityBusy || ! displayName . trim ( ) } > 完 成 初 始 化 < / button > < / form > { identityMessage && < p > { identityMessage } < / p > } < / section > < / div > }
2026-08-15 15:02:35 +08:00
< / div >
2026-08-12 10:29:00 +08:00
}
2026-08-15 15:02:35 +08:00
createRoot ( document . getElementById ( 'root' ) ! ) . render ( < StrictMode > < HoloLakeApp / > < / StrictMode > )