import { StrictMode, useCallback, useEffect, useMemo, useState } from 'react' import { createRoot } from 'react-dom/client' import { invoke } from '@tauri-apps/api/core' import DOMPurify from 'dompurify' import { marked } from 'marked' import './design-tokens.css' import './styles.css' type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear' type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system' type KnowledgeSource = 'native' | 'legacy' interface HomeStatus { directLocalBrokerState: string directConnectionCount: number resumableSessionCount: number codeRepositoryMountCount: number pnccReceiptCount: number updateState: string releaseRecoveryState: string mcpRole: string } interface PersonalChannelIdentity { humanSubjectId: string; displayName: string; channelId: string; createdAtUnixMs: number } 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 } interface KnowledgeSnapshot { state: string nativeRoot: string legacyAvailable: boolean legacyRoot?: string documents: KnowledgeDocumentSummary[] rawDocumentCount: number uniqueDocumentCount: number duplicateDocumentCount: number truncated: boolean } interface KnowledgeDocument { source: KnowledgeSource path: string title: string body: string updatedAtUnixMs: number contentSha256: string writable: boolean } interface KnowledgeSearchResult { source: KnowledgeSource; path: string; title: string; snippet: string } interface KnowledgeImportResult { state: string sourceName: string importedDocuments: number importedAssets: number existingDocuments: number conflicts: number skipped: number firstDocument?: string gitCommit: string snapshot: KnowledgeSnapshot } 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 } interface ReleaseCandidate { candidateId: string; confirmationToken: string; currentVersion: string; version: string; notes: string; features: string[]; fixes: string[]; dataMigrationRequired: boolean } interface ReleaseCheckReceipt { candidate?: ReleaseCandidate } 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' } const themes: Array<{ id: ThemeId; name: string }> = [ { id: 'night', name: '夜湖星光' }, { id: 'dawn', name: '晨湖曦光' }, { id: 'nebula', name: '星云紫夜' }, { id: 'candle', name: '烛畔暖湖' }, { id: 'clear', name: '清浅澄湖' }, ] const viewLabels: Record = { overview: '总览', knowledge: '知识工作台', code: '代码频道', receipts: '回执', system: '系统详情' } 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 } function humanError(error: unknown, context: 'knowledge' | 'code' | 'identity' | 'system') { const value = String(error) 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 '该文件不是当前可阅读的文本格式。' if (context === 'identity') return '个人空间未能建立。' if (context === 'knowledge') return '知识操作未完成,原文件未被覆盖。' if (context === 'code') return '代码频道操作未完成。' return '系统操作未完成。' } function Icon({ name }: { name: ViewId | 'search' | 'import' | 'folder' | 'copy' | 'arrow' | 'chevron' | 'file' | 'edit' | 'save' | 'back' }) { const paths: Record = { overview: <>, knowledge: <>, code: <>, receipts: <>, system: <>, search: <>, import: <>, folder: , file: <>, copy: <>, arrow: <>, chevron: , edit: <>, save: <>, back: , } return } function escapeHtml(value: string) { return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"') } function splitFrontmatter(body: string) { const normalized = body.replace(/\r\n/g, '\n') if (!normalized.startsWith('---\n')) return { content: normalized, metadata: {} as Record } const closing = normalized.indexOf('\n---\n', 4) if (closing < 0) return { content: normalized, metadata: {} as Record } const metadata: Record = {} for (const line of normalized.slice(4, closing).split('\n')) { const match = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line) if (match) metadata[match[1]] = match[2].replace(/^['"]|['"]$/g, '') } return { content: normalized.slice(closing + 5), metadata } } function markdownHtml(body: string) { const withWikiLinks = body.replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, (_whole, target: string, label?: string) => ``) const html = marked.parse(withWikiLinks, { async: false, gfm: true, breaks: false }) as string return DOMPurify.sanitize(html, { ADD_ATTR: ['data-wiki'], ADD_TAGS: ['button'] }) } function documentOutline(body: string) { return splitFrontmatter(body).content.split('\n').flatMap((line, index) => { const match = /^(#{1,4})\s+(.+)$/.exec(line.trim()) return match ? [{ id: `outline-${index}`, level: match[1].length, title: match[2].replace(/[*_`]/g, '') }] : [] }).slice(0, 24) } function MarkdownDocument({ body, onWiki }: { body: string; onWiki?: (target: string) => void }) { const parsed = useMemo(() => splitFrontmatter(body), [body]) const html = useMemo(() => markdownHtml(parsed.content), [parsed.content]) return
{ const element = (event.target as HTMLElement).closest('[data-wiki]') if (element?.dataset.wiki && onWiki) onWiki(element.dataset.wiki) }} dangerouslySetInnerHTML={{ __html: html }} /> } interface TreeNode { name: string; key: string; folders: Map; 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) } function KnowledgeTree({ node, depth, expanded, active, onToggle, onOpen }: { node: TreeNode; depth: number; expanded: Set; active?: string; onToggle: (key: string) => void; onOpen: (item: KnowledgeDocumentSummary) => void }) { 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
{open && }
})} {documents.map((document) => )} } function HoloLakeApp() { const [theme, setTheme] = useState(() => (window.localStorage.getItem('hololake-theme') as ThemeId) || 'night') const [view, setView] = useState('knowledge') const [status, setStatus] = useState(previewStatus) const [personal, setPersonal] = useState(previewPersonal) const [knowledge, setKnowledge] = useState(previewKnowledge) const [codeChannels, setCodeChannels] = useState(previewCode) const [activeDocument, setActiveDocument] = useState(null) const [searchQuery, setSearchQuery] = useState('') const [searchResults, setSearchResults] = useState(null) const [knowledgeBusy, setKnowledgeBusy] = useState(false) const [knowledgeMessage, setKnowledgeMessage] = useState('') const [expanded, setExpanded] = useState>(() => 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(null) const [codeTree, setCodeTree] = useState(null) const [activeCodeFile, setActiveCodeFile] = useState(null) const [codeMode, setCodeMode] = useState<'human' | 'source'>('human') const [displayName, setDisplayName] = useState('') const [identityBusy, setIdentityBusy] = useState(false) const [identityMessage, setIdentityMessage] = useState('') const [receipts, setReceipts] = useState([]) const [ticket, setTicket] = useState(null) const [systemMessage, setSystemMessage] = useState('') const [releaseCandidate, setReleaseCandidate] = useState(null) const [systemBusy, setSystemBusy] = useState(false) const refreshCore = useCallback(async () => { const [homeResult, personalResult, knowledgeResult, codeResult] = await Promise.allSettled([ invoke('get_hololake_home_status'), invoke('get_personal_channel_snapshot'), invoke('get_knowledge_snapshot'), invoke('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 () => { try { const projection = await invoke('query_pncc_receipt_projection', { input: { afterSequence: 0, limit: 25 } }) setReceipts(projection.events) } catch { setReceipts([]) } }, []) useEffect(() => { void refreshCore() const timer = window.setInterval(() => void refreshCore(), 3500) return () => window.clearInterval(timer) }, [refreshCore]) useEffect(() => { document.documentElement.dataset.theme = theme window.localStorage.setItem('hololake-theme', theme) }, [theme]) 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]) const connectionOnline = status.directConnectionCount > 0 const connectionLabel = connectionOnline ? '本地原生连接 · 客户端在线' : status.resumableSessionCount > 0 ? '原生会话可续接 · 当前离线' : '原生接口已驻留 · 尚无客户端' const openDocument = async (source: KnowledgeSource, path: string) => { setKnowledgeBusy(true) setKnowledgeMessage('') setEditing(false) try { const document = await invoke('read_knowledge_document', { input: { source, path } }) setActiveDocument(document) setDraft(document.body) } catch (error) { setKnowledgeMessage(humanError(error, 'knowledge')) } finally { setKnowledgeBusy(false) } } 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) try { setSearchResults(await invoke('search_knowledge', { input: { query: searchQuery } })) setActiveDocument(null) } catch (error) { setKnowledgeMessage(humanError(error, 'knowledge')) } finally { setKnowledgeBusy(false) } } const importKnowledge = async () => { setKnowledgeBusy(true) setKnowledgeMessage('') try { const result = await invoke('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) } } const saveDocument = async () => { if (!activeDocument?.writable) return setKnowledgeBusy(true) try { const result = await invoke('save_knowledge_document', { input: { path: activeDocument.path, body: draft, expectedContentSha256: activeDocument.contentSha256 }, }) 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) } } const cloneCodeChannel = async (event: React.FormEvent) => { event.preventDefault() if (!cloneUrl.trim()) return setCodeBusy(true) setCodeMessage('') try { const snapshot = await invoke('clone_code_channel', { input: { url: cloneUrl } }) setCodeChannels(snapshot) setCloneUrl('') setCodeMessage('代码频道已克隆并登记。') } catch (error) { setCodeMessage(humanError(error, 'code')) } finally { setCodeBusy(false) } } const selectLocalCodeChannel = async () => { setCodeBusy(true) setCodeMessage('') try { const snapshot = await invoke('select_local_code_channel') if (snapshot) { setCodeChannels(snapshot) setCodeMessage('本地代码频道已登记,原文件未移动。') } } catch (error) { setCodeMessage(humanError(error, 'code')) } finally { setCodeBusy(false) } } const browseChannel = async (channel: CodeChannelEntry, path = '') => { setCodeBusy(true) setActiveChannel(channel) setActiveCodeFile(null) try { setCodeTree(await invoke('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('read_code_channel_file', { input: { channelId: activeChannel.channelId, path: entry.path }, })) } catch (error) { setCodeMessage(humanError(error, 'code')) } finally { setCodeBusy(false) } } const codeParent = () => codeTree?.path.split('/').filter(Boolean).slice(0, -1).join('/') || '' const initializeIdentity = async (event: React.FormEvent) => { event.preventDefault() setIdentityBusy(true) try { setPersonal(await invoke('initialize_personal_channel', { input: { displayName } })) setDisplayName('') } catch (error) { setIdentityMessage(humanError(error, 'identity')) } finally { setIdentityBusy(false) } } const issueInvitation = async () => { setSystemBusy(true) setSystemMessage('') try { setTicket(await invoke('issue_direct_local_discovery_ticket', { input: { accountId: getOrCreateLocalId('hololake-local-account', 'human-local'), laneId: 'personal-channel', clientInstanceId: getOrCreateLocalId('hololake-external-ai-client', 'external-ai'), }, })) setSystemMessage('一次性发现凭据已生成。连接成功后,状态条会显示真实在线数。') } catch (error) { setSystemMessage(humanError(error, 'system')) } finally { setSystemBusy(false) } } 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) setSystemMessage('连接凭据已复制。') } catch { setSystemMessage('复制失败,请手动选择凭据。') } } const checkUpdate = async () => { setSystemBusy(true) try { const result = await invoke('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) } } const renderOverview = () => (
PERSONAL HOLOLAKE

个人工作空间

知识、代码与本机协作的统一入口

最近知识

从真实页面继续阅读

{knowledge.documents.slice().sort((a, b) => b.updatedAtUnixMs - a.updatedAtUnixMs).slice(0, 7).map((item) => )}

运行证据

状态来自当前进程,不使用演示值

Unix 本地代理
{status.directLocalBrokerState}
在线客户端
{status.directConnectionCount}
可续接会话
{status.resumableSessionCount}
PNCC 投影回执
{status.pnccReceiptCount}
MCP
备用 / 恢复
) const renderKnowledge = () => (
{activeDocument ? <>
{activeDocument.source === 'legacy' ? 'HoloLake Era · 只读源' : '我的知识库'}/{activeDocument.path}
{editing ? <> : activeDocument.writable && }
{editing ?