feat: admit signed channel workbench module

This commit is contained in:
冰朔 2026-08-19 02:22:32 +08:00
commit bafaf464c2
25 changed files with 1886 additions and 44 deletions

View file

@ -1,8 +1,11 @@
import { StrictMode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { lazy, StrictMode, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createRoot } from 'react-dom/client'
import { numberedInvoke as invoke } from './modules/numbered-ipc'
import { splitFrontmatter, documentOutline, jumpToHeading, MarkdownDocument, markdownHtml } from './modules/knowledge-render'
import { NativeCompositionStudio, type CompositionDimension, type CompositionMeasure, type NativeCompositionProjection, type ProjectionView } from './modules/native-composition'
import type { ChannelWorkbenchSnapshot } from './modules/channel-workbench'
const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio })))
const TAG_TINTS = ['tag-lavender', 'tag-sky', 'tag-mint', 'tag-amber', 'tag-rose', 'tag-slate']
@ -49,7 +52,7 @@ import './design-tokens.css'
import './styles.css'
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
type ViewId = 'overview' | 'knowledge' | 'composition' | 'code' | 'receipts' | 'system'
type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'code' | 'receipts' | 'system'
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool'
type KnowledgeSource = 'native' | 'legacy'
@ -363,7 +366,7 @@ const previewCode: CodeChannelSnapshot = { state: 'UNAVAILABLE', channels: [], a
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<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
const viewLabels: Record<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', workbench: '频道资料工作台', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
const domainGates = [
{ 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'],
@ -596,6 +599,10 @@ function HoloLakeApp() {
const [compositionViews, setCompositionViews] = useState<ProjectionView[]>(['DASHBOARD', 'VERTICAL_BAR', 'TABLE'])
const [compositionBusy, setCompositionBusy] = useState(false)
const [compositionMessage, setCompositionMessage] = useState('')
const [workbenchModule, setWorkbenchModule] = useState<BundledModuleDescriptor | null>(null)
const [workbenchSnapshot, setWorkbenchSnapshot] = useState<ChannelWorkbenchSnapshot | null>(null)
const [workbenchBusy, setWorkbenchBusy] = useState(false)
const [workbenchMessage, setWorkbenchMessage] = useState('')
const [expanded, setExpanded] = useState<Set<string>>(() => new Set(['导入']))
const [editing, setEditing] = useState(false)
const [draft, setDraft] = useState('')
@ -1079,6 +1086,53 @@ function HoloLakeApp() {
} finally { setCompositionBusy(false) }
}
const refreshWorkbenchModule = async () => {
try {
const catalog = await invoke<BundledModuleDescriptor[]>('get_bundled_module_catalog')
const module = catalog.find((item) => item.moduleNumber === 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001') || null
setWorkbenchModule(module)
if (module?.installedState === 'ACTIVE') setWorkbenchSnapshot(await invoke<ChannelWorkbenchSnapshot>('get_channel_workbench_snapshot'))
return module
} catch (error) {
setWorkbenchMessage(humanError(error, 'system'))
return null
}
}
const openWorkbench = () => {
openWorldTool('workbench')
void refreshWorkbenchModule()
}
const activateWorkbenchModule = async () => {
setWorkbenchBusy(true)
setWorkbenchMessage('正在验证签名、登记编号并执行模块自检……')
try {
await invoke('activate_bundled_module', { input: { moduleNumber: 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001', humanConfirmedPermissionExpansion: true } })
const module = await refreshWorkbenchModule()
if (!module || module.installedState !== 'ACTIVE') throw new Error('HOLOLAKE_MODULE_NOT_ACTIVE')
setWorkbenchSnapshot(await invoke<ChannelWorkbenchSnapshot>('get_channel_workbench_snapshot'))
setWorkbenchMessage('模块已通过签名、编号、四项读写权限与自检验收。')
} catch (error) { setWorkbenchMessage(humanError(error, 'system')) }
finally { setWorkbenchBusy(false) }
}
const saveWorkbenchDocument = async (input: { documentId: string; title: string; body: string; expectedRevision: number }) => {
setWorkbenchBusy(true); setWorkbenchMessage('')
try {
const result = await invoke<{ receiptSha256: string }>('save_channel_document', { input })
setWorkbenchSnapshot(await invoke<ChannelWorkbenchSnapshot>('get_channel_workbench_snapshot'))
setWorkbenchMessage(`文档已写入本机频道;回执 ${result.receiptSha256.slice(0, 12)}`)
} catch (error) { setWorkbenchMessage(humanError(error, 'system')) }
finally { setWorkbenchBusy(false) }
}
const saveWorkbenchSpreadsheet = async (input: { tableId: string; title: string; columns: { columnId: string; title: string }[]; rows: { rowId: string; cells: string[] }[]; expectedRevision: number }) => {
setWorkbenchBusy(true); setWorkbenchMessage('')
try {
const result = await invoke<{ receiptSha256: string }>('save_channel_spreadsheet', { input })
setWorkbenchSnapshot(await invoke<ChannelWorkbenchSnapshot>('get_channel_workbench_snapshot'))
setWorkbenchMessage(`工作表已写入本机频道;回执 ${result.receiptSha256.slice(0, 12)}`)
} catch (error) { setWorkbenchMessage(humanError(error, 'system')) }
finally { setWorkbenchBusy(false) }
}
const cloneCodeChannel = async (event: React.FormEvent) => {
event.preventDefault()
if (!cloneUrl.trim()) return
@ -1451,6 +1505,20 @@ function HoloLakeApp() {
</section>
)
const renderWorkbench = () => (
<section className="full-workbench channel-workbench-world">
{workbenchModule?.installedState === 'ACTIVE' && workbenchSnapshot
? <Suspense fallback={<div className="workbench-empty"><p></p></div>}><ChannelWorkbenchStudio snapshot={workbenchSnapshot} busy={workbenchBusy} message={workbenchMessage} onSaveDocument={saveWorkbenchDocument} onSaveSpreadsheet={saveWorkbenchSpreadsheet}/></Suspense>
: <div className="workbench-empty">
<span></span><h2></h2>
<p></p>
<code>HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001 · DOCUMENT/SPREADSHEET READ+WRITE</code>
<button className="primary-button" type="button" disabled={workbenchBusy} onClick={() => void activateWorkbenchModule()}>{workbenchBusy ? '正在验收模块…' : '确认四项权限并启用'}</button>
{workbenchMessage && <p>{workbenchMessage}</p>}
</div>}
</section>
)
const renderCode = () => (
<section className="full-workbench code-workbench">
<aside className="code-channels">
@ -1719,6 +1787,7 @@ function HoloLakeApp() {
<LakePool className="channel-main" title="频道全景" meta="心跳核心频道" open onClick={() => openWorldTool('overview')}/>
<LakePool className="channel-knowledge" title="知识空间" meta={`${knowledge.uniqueDocumentCount} 唯一页`} onClick={() => openWorldTool('knowledge')}/>
<LakePool className="channel-light" title="结构组合" meta="签名模块 · 只读知识投影" onClick={() => { openWorldTool('composition'); void refreshCompositionModule() }}/>
<LakePool className="channel-code" title="资料工作台" meta="签名模块 · 文档与智能表格" onClick={openWorkbench}/>
{timeAuthorityModule && <LakePool className="channel-time" title="时间主控" meta={beijingCoordinate ? `光湖历第 ${beijingCoordinate.guanghuEraDay}` : '北京时间正在流动'} onClick={openEraTimeline}/>}
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
</section>}
@ -1731,7 +1800,7 @@ function HoloLakeApp() {
</section>}
{worldStage === 'tool' && <section className={`world-tool world-tool-${view}`}>
<header className="tool-worldbar"><button type="button" onClick={() => setWorldStage(toolReturnStage)}> 退</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 === 'composition' ? renderComposition() : view === 'code' ? renderCode() : view === 'receipts' ? renderReceipts() : renderSystem()}</div>
<div className={`tool-projection${inspectorOpen ? '' : ' inspector-closed'}`}>{view === 'overview' ? renderOverview() : view === 'knowledge' ? renderKnowledge() : view === 'composition' ? renderComposition() : view === 'workbench' ? renderWorkbench() : view === 'code' ? renderCode() : view === 'receipts' ? renderReceipts() : renderSystem()}</div>
</section>}
</main>
<footer className="world-footer"><b> · </b><span>GH-AIOS</span></footer>