feat: ship dual-signed online module marketplace

This commit is contained in:
冰朔 2026-08-19 18:24:50 +08:00
commit b0eeade03d
40 changed files with 5774 additions and 111 deletions

View file

@ -8,6 +8,7 @@ import { StarlakeSurface, type DomainId } from './modules/qoder-surface/Starlake
import { FINISHES, TraditionalSurface, type FinishId, type TraditionalBroadcast, type TraditionalChannel, type TraditionalSystem } from './modules/qoder-surface/TraditionalSurface'
import { resolveVisualBalance } from './modules/qoder-surface/visual-balance'
import { PrivateChannelSurface, type InstalledChannelModule, type PrivateChannelAction } from './modules/private-channel/PrivateChannelSurface'
import { HumanAuthorizationCenter, type DirectSessionProjection } from './modules/human-authorization-center'
const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio })))
const PersonaChannelBody = lazy(() => import('./modules/persona-channel-body').then((module) => ({ default: module.PersonaChannelBody })))
@ -60,7 +61,7 @@ import './design-tokens.css'
import './styles.css'
type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear'
type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'education' | 'webNovel' | 'mobileSync' | 'persona' | 'code' | 'receipts' | 'system'
type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'education' | 'webNovel' | 'mobileSync' | 'persona' | 'code' | 'marketplace' | 'receipts' | 'system'
type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool'
type KnowledgeSource = 'native' | 'legacy'
@ -74,6 +75,43 @@ interface BundledModuleDescriptor {
installedState: string
signatureVerified: boolean
}
type MarketplaceKind = 'PHYSICAL_MODULE' | 'COGNITIVE_SKILL'
interface MarketplaceItemView {
itemNumber: string
artifactKind: MarketplaceKind
displayName: string
summary: string
version: string
sourceRepository: string
sourceRevision: string
artifactSha256: string
adapter?: string | null
permissions: string[]
executionAuthority: boolean
installedState: string
updateAvailable: boolean
}
interface MarketplaceSnapshot {
schema: 'hololake.online-marketplace-snapshot/v1'
state: string
trustState: string
catalogEpoch: number
catalogSha256: string
itemCount: number
catalogReceiptCount: number
latestCatalogReceiptHash: string
lastSyncNote: string
items: MarketplaceItemView[]
}
interface MarketplaceMutationOutcome {
state: string
itemNumber: string
artifactKind: MarketplaceKind
artifactSha256: string
packageCodeExecuted: false
realityAuthorityGranted: false
snapshot: MarketplaceSnapshot
}
interface WorldClimateSnapshot {
schema: 'hololake.world-climate/v1'
state: 'VERIFIED_LIVE' | 'TIME_ONLY_WEATHER_UNAVAILABLE'
@ -93,6 +131,7 @@ interface HomeStatus {
directLocalBrokerState: string
directConnectionCount: number
resumableSessionCount: number
directSessions: DirectSessionProjection[]
codeRepositoryMountCount: number
pnccReceiptCount: number
updateState: string
@ -284,8 +323,18 @@ interface ZeroPointSnapshot {
resolvedDomain: string
lastValidCheck: number
graceDeadline: number
protocol: { gracePeriodDays: number; lighthouseAnchorUrl: string; lighthouseResolveUrl: string; coreChannelSource: string; origin: string }
protocol: { gracePeriodDays: number; lighthouseAnchorUrl: string; lighthouseResolveUrl: string; enterpriseResolveUrl: string; coreChannelSource: string; origin: string }
syncNote: string
publicDistribution: {
state: string
epoch: number
version: string
contentRootSha256: string
rollbackAvailable: boolean
receiptCount: number
latestReceiptHash: string
trustState: string
}
}
function domainDisplayName(domain: string): string {
@ -385,14 +434,14 @@ interface EnterpriseReceiptEnvelope {
repository_projection?: EnterpriseReceiptProjection
}
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY', terminalLinkProtocol: 'HOLOLAKE_TERMINAL_LINK/3', terminalLinkTransport: 'UNVERIFIED', environmentFramePolicy: 'REQUIRED_AFTER_CONNECT_RESUME_AND_BEFORE_MUTATION' }
const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, directSessions: [], codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY', terminalLinkProtocol: 'HOLOLAKE_TERMINAL_LINK/3', terminalLinkTransport: 'UNVERIFIED', environmentFramePolicy: 'REQUIRED_AFTER_CONNECT_RESUME_AND_BEFORE_MUTATION' }
const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], modules: [], 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<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', workbench: '频道资料工作台', education: '教育工作台', webNovel: '网文作者工作台', mobileSync: '移动同步桥', persona: '人格频道本体', code: '人格代码频道', receipts: '运行回执', system: '系统详情' }
const viewLabels: Record<ViewId, string> = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', workbench: '频道资料工作台', education: '教育工作台', webNovel: '网文作者工作台', mobileSync: '移动同步桥', persona: '人格频道本体', code: '人格代码频道', marketplace: '分域模块商城', 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'],
@ -498,6 +547,11 @@ function humanError(error: unknown, context: 'knowledge' | 'code' | 'identity' |
if (value.includes('LOGIN_ACCOUNT_BINDING_UNREGISTERED')) return '这个编号尚未登记对应的仓库账号,系统已停止进入。'
if (value.includes('DOMAIN_LOGIN_NOT_PROVISIONED')) return '该域的企业服务器尚未接入,当前不能继续登录。'
if (value.includes('PERSISTENT_CREDENTIAL_UNAVAILABLE')) return '当前系统尚未接通安全凭证存储,不能完成签署。'
if (value.includes('MARKETPLACE_PERMISSION_CONFIRMATION_REQUIRED')) return '新增现实权限尚未得到本人确认,安装已停止。'
if (value.includes('MARKETPLACE_CATALOG_NOT_SYNCED')) return '模块商城目录尚未完成双签同步。'
if (value.includes('MARKETPLACE_CATALOG_CHANGED')) return '商城目录已更新,请重新确认后安装。'
if (value.includes('MARKETPLACE') && value.includes('SIGNATURE')) return '商城签名验证未通过,目录或安装包没有被采用。'
if (value.includes('MARKETPLACE') || value.includes('SKILL_')) return '模块商城操作未完成;本机现有安装保持不变。'
if (value.includes('ENTERPRISE_RECEIPT_FAILED')) return '企业回执没有写入,请稍后重新提交。'
if (value.includes('ENTERPRISE_RECEIPT_ROUTE_REQUIRED')) return '此签署入口仅适用于企业四域。'
if (value.includes('DOMAIN_ROUTE_REQUIRED') || value.includes('ZP_DOMAIN_ROUTE_REQUIRED')) return '编号尚未解析到已登记域,不能开始登录。'
@ -648,6 +702,11 @@ function HoloLakeApp() {
const [worldClimate, setWorldClimate] = useState<WorldClimateSnapshot | null>(null)
const [dynamicSurfaceBusy, setDynamicSurfaceBusy] = useState(false)
const [dynamicSurfaceMessage, setDynamicSurfaceMessage] = useState('')
const [marketplace, setMarketplace] = useState<MarketplaceSnapshot | null>(null)
const [marketplaceKind, setMarketplaceKind] = useState<MarketplaceKind>('PHYSICAL_MODULE')
const [marketplaceBusyItem, setMarketplaceBusyItem] = useState('')
const [marketplaceSyncing, setMarketplaceSyncing] = useState(false)
const [marketplaceMessage, setMarketplaceMessage] = useState('')
const [expanded, setExpanded] = useState<Set<string>>(() => new Set(['导入']))
const [editing, setEditing] = useState(false)
const [draft, setDraft] = useState('')
@ -1295,6 +1354,86 @@ function HoloLakeApp() {
else { setDynamicSurfaceModule(null); setWorldClimate(null); setDynamicSurfaceMessage('') }
}, [repoLogin?.username, repoLogin?.domain])
const refreshMarketplace = async () => {
try {
const snapshot = await invoke<MarketplaceSnapshot>('get_marketplace_snapshot')
setMarketplace(snapshot)
setMarketplaceMessage(snapshot.lastSyncNote)
return snapshot
} catch (error) {
setMarketplaceMessage(humanError(error, 'system'))
return null
}
}
const syncMarketplace = async () => {
setMarketplaceSyncing(true)
setMarketplaceMessage('正在读取线上目录并核验零点原核与企业发布双签……')
try {
const snapshot = await invoke<MarketplaceSnapshot>('sync_marketplace_catalog')
setMarketplace(snapshot)
setMarketplaceMessage(snapshot.lastSyncNote)
} catch (error) {
setMarketplaceMessage(humanError(error, 'system'))
} finally {
setMarketplaceSyncing(false)
}
}
const openMarketplace = () => {
openWorldTool('marketplace')
void refreshMarketplace().then((snapshot) => {
if (!snapshot || snapshot.state !== 'ACTIVE_VERIFIED_CATALOG') void syncMarketplace()
})
}
const installMarketplaceItem = async (item: MarketplaceItemView) => {
if (!marketplace) return
const permissionText = item.permissions.length
? `该模块请求以下现实能力:\n\n${item.permissions.map((permission) => `${permission}`).join('\n')}\n\n是否确认安装`
: `确认安装“${item.displayName}”?`
if (!window.confirm(permissionText)) return
setMarketplaceBusyItem(item.itemNumber)
setMarketplaceMessage(`正在下载、验签并安装 ${item.displayName}……`)
try {
const result = await invoke<MarketplaceMutationOutcome>('install_marketplace_item', { input: {
itemNumber: item.itemNumber,
expectedCatalogEpoch: marketplace.catalogEpoch,
expectedArtifactSha256: item.artifactSha256,
humanConfirmedPermissionExpansion: item.artifactKind === 'PHYSICAL_MODULE' && item.permissions.length > 0,
} })
setMarketplace(result.snapshot)
setMarketplaceMessage(`${item.displayName} 已完成下载、验签、自检和安装。`)
} catch (error) {
setMarketplaceMessage(humanError(error, 'system'))
} finally {
setMarketplaceBusyItem('')
}
}
const uninstallMarketplaceItem = async (item: MarketplaceItemView) => {
if (!window.confirm(`确认停用“${item.displayName}”?安装证据与用户数据会保留。`)) return
setMarketplaceBusyItem(item.itemNumber)
try {
const result = await invoke<MarketplaceMutationOutcome>('uninstall_marketplace_item', { input: { itemNumber: item.itemNumber } })
setMarketplace(result.snapshot)
setMarketplaceMessage(`${item.displayName} 已停用;数据与回执保留。`)
} catch (error) {
setMarketplaceMessage(humanError(error, 'system'))
} finally {
setMarketplaceBusyItem('')
}
}
const rollbackMarketplaceItem = async (item: MarketplaceItemView) => {
if (!window.confirm(`确认把“${item.displayName}”回退到上一份已验签版本?`)) return
setMarketplaceBusyItem(item.itemNumber)
try {
const result = await invoke<MarketplaceMutationOutcome>('rollback_marketplace_item', { input: { itemNumber: item.itemNumber } })
setMarketplace(result.snapshot)
setMarketplaceMessage(`${item.displayName} 已回退到上一份已验签版本。`)
} catch (error) {
setMarketplaceMessage(humanError(error, 'system'))
} finally {
setMarketplaceBusyItem('')
}
}
const cloneCodeChannel = async (event: React.FormEvent) => {
event.preventDefault()
if (!cloneUrl.trim()) return
@ -1558,7 +1697,11 @@ function HoloLakeApp() {
}
const syncZeroPoint = async () => {
setZpBusy(true); setZpMessage('')
try { setZeroPoint(await invoke<ZeroPointSnapshot>('zero_point_sync')) } catch (error) { setZpMessage(String(error)) } finally { setZpBusy(false) }
try {
const snapshot = await invoke<ZeroPointSnapshot>('zero_point_sync')
setZeroPoint(snapshot)
setZpMessage(snapshot.syncNote)
} catch (error) { setZpMessage(String(error)) } finally { setZpBusy(false) }
}
const renderOverview = () => (
<section className="content-page overview-page">
@ -1779,9 +1922,54 @@ function HoloLakeApp() {
</section>
)
const renderMarketplace = () => {
const items = marketplace?.items.filter((item) => item.artifactKind === marketplaceKind) || []
const active = marketplace?.state === 'ACTIVE_VERIFIED_CATALOG'
return <section className="content-page marketplace-page">
<header className="page-title marketplace-title"><div><span className="kicker">BRANCH DOMAIN · VERIFIED DISTRIBUTION</span><h1></h1><p></p></div><button className="primary-button" type="button" disabled={marketplaceSyncing} onClick={() => void syncMarketplace()}>{marketplaceSyncing ? '正在双签同步…' : active ? '检查线上更新' : '同步线上目录'}</button></header>
<section className="marketplace-proof" aria-label="商城信任状态">
<div><span></span><b>{active ? '线上双签已核验' : '等待首次同步'}</b></div>
<div><span></span><b>{marketplace?.catalogEpoch || '—'}</b></div>
<div><span></span><b>{marketplace?.itemCount ?? '—'}</b></div>
<div><span></span><b>{marketplace?.catalogReceiptCount || 0}</b></div>
</section>
<div className="marketplace-tabs" role="tablist" aria-label="商城资源类型">
<button className={marketplaceKind === 'PHYSICAL_MODULE' ? 'active' : ''} type="button" role="tab" onClick={() => setMarketplaceKind('PHYSICAL_MODULE')}><b></b><span></span></button>
<button className={marketplaceKind === 'COGNITIVE_SKILL' ? 'active' : ''} type="button" role="tab" onClick={() => setMarketplaceKind('COGNITIVE_SKILL')}><b></b><span></span></button>
</div>
<p className="marketplace-boundary">{marketplaceKind === 'PHYSICAL_MODULE' ? '成品模块可以声明现实权限;每次新增权限都必须由当前人类看见并确认。代码仓库只作为来源证据,客户端不会克隆并执行仓库。' : '思维技能执行权固定为 false、权限列表固定为空系统只在需要时读取不会自动塞进每轮提示词。'}</p>
<div className="marketplace-list">
{items.length ? items.map((item) => {
const busy = marketplaceBusyItem === item.itemNumber
const installed = item.installedState === 'ACTIVE' || item.installedState === 'ACTIVE_READONLY'
return <article className="marketplace-item" key={item.itemNumber}>
<header><div><span>{item.artifactKind === 'PHYSICAL_MODULE' ? '成品模块' : '只读思维技能'}</span><h2>{item.displayName}</h2><p>{item.summary}</p></div><em className={installed ? 'installed' : ''}>{item.updateAvailable ? '有已验签更新' : installed ? '已安装' : '可安装'}</em></header>
<dl>
<div><dt> / </dt><dd>{item.version} · {item.itemNumber}</dd></div>
<div><dt></dt><dd>{item.sourceRepository} · {item.sourceRevision.slice(0, 12)}</dd></div>
<div><dt></dt><dd>{item.artifactKind === 'PHYSICAL_MODULE' ? '仅经已登记适配器与所列权限' : '无现实执行权'}</dd></div>
<div><dt></dt><dd>{item.permissions.length ? item.permissions.join(' · ') : '无现实权限'}</dd></div>
</dl>
<div className="marketplace-actions">
{installed && !item.updateAvailable
? <button type="button" disabled={busy} onClick={() => void uninstallMarketplaceItem(item)}>{busy ? '处理中…' : '停用并保留数据'}</button>
: <button className="primary-button" type="button" disabled={busy || !active} onClick={() => void installMarketplaceItem(item)}>{busy ? '正在验签安装…' : item.updateAvailable ? '安装已验签更新' : '安装'}</button>}
{installed && <button type="button" disabled={busy} onClick={() => void rollbackMarketplaceItem(item)}>退</button>}
</div>
</article>
}) : <div className="marketplace-empty"><b>{active ? '当前分类尚无已登记资源' : '尚未取得可验证目录'}</b><p>{active ? '资源发布后会在下一次目录同步中出现。' : '点击“同步线上目录”;只有零点原核与企业发布双签同时通过,目录才会显示。'}</p></div>}
</div>
{marketplaceMessage && <p className="global-message marketplace-message">{marketplaceMessage}</p>}
{marketplace?.latestCatalogReceiptHash && <footer className="marketplace-receipt"> · {marketplace.latestCatalogReceiptHash.slice(0, 20)} · {marketplace.catalogSha256.slice(0, 20)}</footer>}
</section>
}
const renderSystem = () => (
<section className="content-page">
<header className="page-title"><div><span className="kicker">SYSTEM EVIDENCE</span><h1></h1><p></p></div></header>
<header className="page-title"><div><span className="kicker">HUMAN CONTROL</span><h1></h1><p></p></div></header>
<HumanAuthorizationCenter brokerState={status.directLocalBrokerState} activeConnectionCount={status.directConnectionCount} sessions={status.directSessions || []}/>
<details className="system-diagnostics">
<summary></summary>
<div className="system-grid">
<section className="plain-panel connection-panel">
<header><div><h2></h2><p> AI HoloLakeMCP </p></div><span className={nearbyDiscovery?.automaticSameDeviceDiscovery ? 'status-chip online' : 'status-chip'}>{nearbyDiscovery?.automaticSameDeviceDiscovery ? '本机自动发现已开启' : '自动发现不可用'}</span></header>
@ -1812,6 +2000,10 @@ function HoloLakeApp() {
<dl className="evidence-list">
<div><dt></dt><dd>{zeroPoint ? (zeroPoint.binding === 'bound' ? `已绑定(${zeroPoint.userNumber}` : '等待绑定(空白态)') : '—'}</dd></div>
<div><dt></dt><dd>{zeroPoint ? protocolOriginDisplayName(zeroPoint.protocol.origin) : '—'}</dd></div>
<div><dt></dt><dd> · </dd></div>
<div><dt></dt><dd>{zeroPoint?.publicDistribution.trustState === 'PROVISIONED' ? '零点原点与企业分发公钥已配置' : '等待配置两把独立发布公钥'}</dd></div>
<div><dt></dt><dd>{zeroPoint?.publicDistribution.version ? `${zeroPoint.publicDistribution.version} · epoch ${zeroPoint.publicDistribution.epoch}` : '尚无线上双签版本'}</dd></div>
<div><dt></dt><dd>{zeroPoint ? `${zeroPoint.publicDistribution.receiptCount}${zeroPoint.publicDistribution.rollbackAvailable ? ' · 保留上一可信版本' : ''}` : '—'}</dd></div>
<div><dt>线</dt><dd>{zeroPoint ? `${zeroPoint.protocol.gracePeriodDays}` : '—'}</dd></div>
<div><dt></dt><dd>{zeroPoint?.syncNote ?? '—'}</dd></div>
</dl>
@ -1819,7 +2011,7 @@ function HoloLakeApp() {
<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>
<button className="secondary-button" type="button" disabled={zpBusy} onClick={() => void syncZeroPoint()}></button>
<button className="secondary-button" type="button" disabled={zpBusy} onClick={() => void syncZeroPoint()}></button>
</div>
{zpMessage && <p className="global-message">{zpMessage}</p>}
</section>
@ -1831,6 +2023,7 @@ function HoloLakeApp() {
</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>
</div>
</details>
{systemMessage && <p className="global-message">{systemMessage}</p>}
</section>
)
@ -2032,6 +2225,7 @@ function HoloLakeApp() {
<div className="world-location"><h1>{repoLogin.domain === 'FIFTH_DOMAIN' ? domainDisplayName(repoLogin.domain) : '光湖零感域'}</h1><p>{repoLogin.domain === 'FIFTH_DOMAIN' ? '世界正在发生什么' : '公共工作入口 · 世界正在发生什么'}</p></div>
<div className="broadcast-stream"><p><i/> · 线</p><p><i/> · {enterpriseEntry?.registry_version || 'HLDP v1.0'}</p>{repoLogin.domain !== 'FIFTH_DOMAIN' && <p><i/> · {domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)}</p>}<p><i/>HoloLake · V0.5.0</p></div>
<LakePool className="home-primary" title={repoLogin.domain === 'FIFTH_DOMAIN' ? '永恒湖心系统' : '光湖频道'} meta={repoLogin.domain === 'FIFTH_DOMAIN' ? '进入私人系统' : `${domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)} · 责任工作入口`} open onClick={() => setWorldStage(repoLogin.domain === 'FIFTH_DOMAIN' ? (isZhizhi ? 'heart' : 'channel') : 'channel')}/>
<LakePool className="channel-knowledge" title="分域模块商城" meta="成品模块 · 思维大脑技能" onClick={openMarketplace}/>
<LakePool className="home-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
<EraHomeEntry timeline={eraTimeline} coordinate={beijingCoordinate} onOpen={openEraTimeline}/>
</section>}
@ -2071,12 +2265,14 @@ function HoloLakeApp() {
<LakePool className="channel-knowledge system-branch-heartbeat" title="心跳核心频道" meta="冰朔 · 私人频道" open onClick={() => setWorldStage('heartbeat')}/>
<LakePool className="channel-code system-branch-light-lake" title="光之湖" meta="人格体居所" onClick={() => setWorldStage('lightLake')}/>
<LakePool className="channel-light system-branch-love" title="爱之核心子系统" meta="责任主体 · 之之" onClick={() => setWorldStage('love')}/>
<LakePool className="channel-main" title="分域模块商城" meta="线上成品模块 · 只读思维技能" onClick={openMarketplace}/>
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
</> : <>
<LakePool className="channel-main" title={domainDisplayName(enterpriseEntry?.subject.domain || repoLogin.domain)} meta="本人责任工作域" open={enterpriseWork?.state === 'READY_READ_ONLY_WORK_PROJECTION'} onClick={() => setWorldStage('enterpriseWork')}/>
<LakePool className="channel-code" title="私有责任工作仓库" meta={enterpriseWork ? `${enterpriseWork.repository} · 已认证` : userPnccBusy ? '正在接入' : '暂不可用'} onClick={() => void openEnterpriseRepository()}/>
<LakePool className="channel-light" title="责任签署状态" meta={enterpriseEntry?.responsibility_receipt?.decision === 'ACCEPT' ? '已接受 · 已留存' : '等待本人确认'} onClick={() => openWorldTool('receipts')}/>
<LakePool className="channel-knowledge" title="前往我的频道" meta="接入说明 · 由本人或人格体完成" onClick={() => setWorldStage('personalNodeGuide')}/>
<LakePool className="channel-main" title="分域模块商城" meta="线上成品模块 · 只读思维技能" onClick={openMarketplace}/>
<LakePool className="channel-status" title="湖面天气" meta={connectionLabel} onClick={() => openWorldTool('system')}/>
</>}
</section>}
@ -2102,7 +2298,7 @@ function HoloLakeApp() {
<footer><b> · </b><span></span></footer>
</div>
</section>}
{worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' && <PrivateChannelSurface ownerName="冰朔" ownerNumber="ICE-GL∞" knowledgeCount={knowledge.uniqueDocumentCount} onBack={() => setWorldStage('channel')} onMarketplace={() => { setWorldStage('domain'); setActiveDomainInfo('BRANCH_DOMAIN') }} nativeActions={privateNativeActions} installedModules={privateInstalledModules}/>}
{worldStage === 'heartbeat' && repoLogin.domain === 'FIFTH_DOMAIN' && <PrivateChannelSurface ownerName="冰朔" ownerNumber="ICE-GL∞" knowledgeCount={knowledge.uniqueDocumentCount} onBack={() => setWorldStage('channel')} onMarketplace={openMarketplace} nativeActions={privateNativeActions} installedModules={privateInstalledModules}/>}
{worldStage === 'lightLake' && repoLogin.domain === 'FIFTH_DOMAIN' && <section className="channel-world light-lake-world">
<button className="world-back" type="button" onClick={() => setWorldStage('channel')}> 退</button>
<div className="world-location"><h1></h1><p> · AGE </p></div>
@ -2113,7 +2309,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 === 'workbench' ? renderWorkbench() : view === 'education' ? renderEducation() : view === 'webNovel' ? renderWebNovel() : view === 'mobileSync' ? renderMobileSync() : view === 'persona' ? renderPersonaBody() : 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 === 'education' ? renderEducation() : view === 'webNovel' ? renderWebNovel() : view === 'mobileSync' ? renderMobileSync() : view === 'persona' ? renderPersonaBody() : view === 'code' ? renderCode() : view === 'marketplace' ? renderMarketplace() : view === 'receipts' ? renderReceipts() : renderSystem()}</div>
</section>}
</main>
{worldStage === 'domain' && surface === 'world' && signedActiveGate && <><button className="domain-info-scrim" type="button" aria-label="关闭域信息" onClick={() => setActiveDomainInfo('')}/><section className={`domain-info-card info-${signedActiveGate.className.slice(2)}`} role="dialog" aria-modal="true" aria-label={`${signedActiveGate.title}系统信息`}><button className="gate-close" type="button" aria-label="关闭域信息" onClick={() => setActiveDomainInfo('')}>×</button><b>{signedActiveGate.title}</b><small>{signedActiveGate.gate}</small><span className="pool-facts">{signedActiveGate.facts.map(([label, value]) => <span className="pool-fact" key={label}><em>{label}</em><strong>{value}</strong></span>)}</span></section></>}

View file

@ -0,0 +1,125 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { numberedInvoke } from './numbered-ipc'
export interface DirectSessionProjection {
sessionId: string
laneId: string
clientInstanceId: string
state: 'LIVE' | 'RESUMABLE'
openedAtUnixMs: number
observedAtUnixMs: number
lastEventSequence: number
}
interface AuthorizationRequest {
requestId: string
action: 'OPEN_MAINTENANCE' | 'UNMOUNT' | 'PROMOTE_VERSION' | 'RETIRE'
targetNumber: string
targetKind: string
targetLabel: string
reason: string
impact: string
rollbackPlan: string
requesterLabel: string
state: 'PENDING_HUMAN' | 'APPROVED' | 'DENIED' | 'EXPIRED' | 'CONSUMED'
createdAtUnixMs: number
expiresAtUnixMs: number
humanNumber?: string
decidedAtUnixMs?: number
ticketId?: string
ticketExpiresAtUnixMs?: number
consumedAtUnixMs?: number
receiptHash: string
}
interface AuthorizationCenterSnapshot {
state: string
pendingCount: number
requests: AuthorizationRequest[]
purgeEnabled: boolean
}
interface Props {
brokerState: string
activeConnectionCount: number
sessions: DirectSessionProjection[]
}
const actionLabels: Record<AuthorizationRequest['action'], string> = {
OPEN_MAINTENANCE: '打开受控维护区',
UNMOUNT: '从当前频道卸载',
PROMOTE_VERSION: '登记并启用新版本',
RETIRE: '停用并保留历史坐标',
}
const stateLabels: Record<AuthorizationRequest['state'], string> = {
PENDING_HUMAN: '等待你决定', APPROVED: '已同意 · 等待执行体领取', DENIED: '已拒绝',
EXPIRED: '已过期', CONSUMED: '票据已领取一次',
}
function time(value?: number) {
return value ? new Date(value).toLocaleString('zh-CN', { hour12: false }) : '—'
}
export function HumanAuthorizationCenter({ brokerState, activeConnectionCount, sessions }: Props) {
const [snapshot, setSnapshot] = useState<AuthorizationCenterSnapshot | null>(null)
const [busy, setBusy] = useState('')
const [message, setMessage] = useState('')
const refresh = useCallback(async () => {
try {
setSnapshot(await numberedInvoke<AuthorizationCenterSnapshot>('get_authorization_center'))
setMessage('')
} catch (error) {
setSnapshot(null)
setMessage(String(error).includes('AUTHENTICATED_ACCOUNT_REQUIRED') ? '登录并完成编号验证后,授权中心才会打开。' : '授权中心当前不可读取;系统不会把未知状态当成已授权。')
}
}, [])
useEffect(() => {
void refresh()
const timer = window.setInterval(() => { if (!document.hidden) void refresh() }, 4_000)
return () => window.clearInterval(timer)
}, [refresh])
const decide = async (requestId: string, decision: 'APPROVE' | 'DENY') => {
setBusy(requestId)
setMessage('')
try {
await numberedInvoke('decide_authorization_request', { input: { requestId, decision } })
setMessage(decision === 'APPROVE' ? '已签发一次性票据;它只对原执行会话和本次目标有效。' : '已拒绝;执行体不会获得继续通道。')
await refresh()
} catch {
setMessage('这次决定没有写入。授权状态保持不变,请刷新后再看。')
} finally {
setBusy('')
}
}
const pending = snapshot?.requests.filter((item) => item.state === 'PENDING_HUMAN') || []
const history = snapshot?.requests.filter((item) => item.state !== 'PENDING_HUMAN').slice(0, 6) || []
const connectionState = activeConnectionCount > 0 ? '正在直连' : sessions.some((item) => item.state === 'LIVE') ? '最近仍有心跳' : '当前没有在线执行体'
const sessionList = useMemo(() => sessions.slice(0, 6), [sessions])
return <div className="human-authorization-layout">
<section className="plain-panel authorization-primary">
<header><div><span className="kicker">HUMAN DECISION</span><h2></h2><p>退</p></div><span className={pending.length ? 'status-chip attention' : 'status-chip online'}>{pending.length ? `${pending.length} 项待确认` : '没有待确认事项'}</span></header>
{pending.length === 0 ? <div className="authorization-empty"><b></b><span></span></div> : <div className="authorization-list">
{pending.map((request) => <article className="authorization-card" key={request.requestId}>
<div className="authorization-card-title"><div><small>{actionLabels[request.action]}</small><h3>{request.targetLabel}</h3><code>{request.targetNumber}</code></div><span>{stateLabels[request.state]}</span></div>
<dl><div><dt></dt><dd>{request.reason}</dd></div><div><dt></dt><dd>{request.impact}</dd></div><div><dt>退</dt><dd>{request.rollbackPlan}</dd></div><div><dt></dt><dd>{request.requesterLabel}</dd></div><div><dt></dt><dd>{time(request.expiresAtUnixMs)}</dd></div></dl>
<div className="authorization-actions"><button className="secondary-button" disabled={busy === request.requestId} onClick={() => void decide(request.requestId, 'DENY')}></button><button className="primary-button" disabled={busy === request.requestId} onClick={() => void decide(request.requestId, 'APPROVE')}>{busy === request.requestId ? '正在写入决定…' : '同意并签发一次性票据'}</button></div>
</article>)}
</div>}
{message && <p className="global-message">{message}</p>}
</section>
<section className="plain-panel collaboration-live">
<header><div><span className="kicker">LIVE COLLABORATION</span><h2> HoloLake</h2><p> Agent</p></div><span className={activeConnectionCount > 0 ? 'status-chip online' : 'status-chip'}>{connectionState}</span></header>
<div className="collaboration-summary"><div><b>{activeConnectionCount}</b><span></span></div><div><b>{sessions.length}</b><span></span></div><div><b>{brokerState === 'READY' ? '就绪' : '等待'}</b><span></span></div></div>
{sessionList.length ? <div className="session-projection-list">{sessionList.map((session) => <article key={session.sessionId}><i className={session.state === 'LIVE' ? 'live' : ''}/><div><b>{session.clientInstanceId}</b><span>{session.laneId} · {time(session.observedAtUnixMs)}</span></div><em>{session.state === 'LIVE' ? '在线' : '可续接'}</em></article>)}</div> : <div className="authorization-empty compact"><b></b><span> AI HoloLake </span></div>}
</section>
{history.length > 0 && <details className="authorization-history"><summary> · {history.length}</summary><div>{history.map((request) => <article key={request.requestId}><div><b>{request.targetLabel}</b><span>{actionLabels[request.action]}</span></div><em>{stateLabels[request.state]}</em><small>{request.humanNumber ? `${request.humanNumber} 决定 · ` : ''}{time(request.decidedAtUnixMs || request.createdAtUnixMs)}</small></article>)}</div></details>}
</div>
}

View file

@ -1176,6 +1176,70 @@ const ROUTES = {
"moduleNumber": "HLP-NIPC-MOD-0031",
"operationNumber": "HLP-NIPC-OP-0147",
"targetNumber": "HLP-NIPC-TGT-0031"
},
"get_authorization_center": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0032",
"operationNumber": "HLP-NIPC-OP-0148",
"targetNumber": "HLP-NIPC-TGT-0032"
},
"decide_authorization_request": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0032",
"operationNumber": "HLP-NIPC-OP-0149",
"targetNumber": "HLP-NIPC-TGT-0032"
},
"get_marketplace_snapshot": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0150",
"targetNumber": "HLP-NIPC-TGT-0033"
},
"sync_marketplace_catalog": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0151",
"targetNumber": "HLP-NIPC-TGT-0033"
},
"install_marketplace_item": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0152",
"targetNumber": "HLP-NIPC-TGT-0033"
},
"uninstall_marketplace_item": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0153",
"targetNumber": "HLP-NIPC-TGT-0033"
},
"rollback_marketplace_item": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0154",
"targetNumber": "HLP-NIPC-TGT-0033"
},
"get_active_cognitive_skills": {
"protocolVersion": "HLP-NIPC-v1",
"callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001",
"channelNumber": "HLP-NIPC-CH-0002",
"moduleNumber": "HLP-NIPC-MOD-0033",
"operationNumber": "HLP-NIPC-OP-0155",
"targetNumber": "HLP-NIPC-TGT-0033"
}
} as const

View file

@ -331,6 +331,49 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
.receipt-row b { color: var(--content-secondary); font-size: 14px; font-weight: 590; }
.receipt-row small { margin-top: 6px; color: var(--content-muted); font-size: 12px; }
.system-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.human-authorization-layout { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(320px, .65fr); gap: 18px; margin-bottom: 18px; }
.authorization-primary { grid-row: span 2; }
.plain-panel header .kicker { display: block; margin-bottom: 8px; color: var(--accent-light); font-size: 10px; font-weight: 750; letter-spacing: .14em; }
.status-chip.attention { border-color: color-mix(in srgb, var(--warn) 48%, transparent); color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.authorization-empty { display: grid; gap: 7px; min-height: 128px; place-content: center; margin-top: 18px; padding: 24px; border: 1px dashed var(--panel-edge); border-radius: 14px; text-align: center; }
.authorization-empty b { color: var(--content-primary); font-size: 16px; }
.authorization-empty span { color: var(--content-muted); font-size: 13px; }
.authorization-empty.compact { min-height: 94px; }
.authorization-list { display: grid; gap: 14px; margin-top: 20px; }
.authorization-card { padding: 18px; border: 1px solid color-mix(in srgb, var(--accent-light) 24%, var(--panel-edge)); border-radius: 14px; background: color-mix(in srgb, var(--primitive-glass) 80%, transparent); }
.authorization-card-title { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.authorization-card-title small { color: var(--accent-light); font-weight: 700; }
.authorization-card-title h3 { margin: 5px 0 4px; color: var(--content-primary); font-size: 18px; }
.authorization-card-title code { color: var(--content-muted); font-size: 11px; overflow-wrap: anywhere; }
.authorization-card-title > span { flex: none; padding: 5px 9px; border-radius: 999px; color: var(--warn); background: color-mix(in srgb, var(--warn) 10%, transparent); font-size: 11px; }
.authorization-card dl { display: grid; gap: 10px; margin: 18px 0; }
.authorization-card dl div { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 12px; }
.authorization-card dt { color: var(--content-muted); font-size: 12px; }
.authorization-card dd { margin: 0; color: var(--content-secondary); font-size: 13px; line-height: 1.55; }
.authorization-actions { display: flex; justify-content: flex-end; gap: 10px; }
.authorization-actions button { min-height: 38px; padding: 0 15px; }
.collaboration-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 20px 0 14px; }
.collaboration-summary div { display: grid; gap: 3px; padding: 13px 10px; border-radius: 11px; background: var(--primitive-glass); text-align: center; }
.collaboration-summary b { color: var(--content-primary); font-size: 18px; }
.collaboration-summary span { color: var(--content-muted); font-size: 11px; }
.session-projection-list { display: grid; gap: 7px; }
.session-projection-list article { display: grid; grid-template-columns: 9px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 10px 2px; border-bottom: 1px solid var(--panel-edge); }
.session-projection-list i { width: 8px; height: 8px; border-radius: 50%; background: var(--content-muted); }
.session-projection-list i.live { background: var(--good); box-shadow: 0 0 10px color-mix(in srgb, var(--good) 70%, transparent); }
.session-projection-list div { display: grid; gap: 3px; min-width: 0; }
.session-projection-list b { overflow: hidden; color: var(--content-primary); font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.session-projection-list span, .session-projection-list em { color: var(--content-muted); font-size: 11px; font-style: normal; }
.authorization-history, .system-diagnostics { grid-column: 1 / -1; border: 1px solid var(--panel-edge); border-radius: 13px; background: var(--primitive-glass); }
.authorization-history > summary, .system-diagnostics > summary { padding: 14px 17px; color: var(--content-secondary); font-size: 13px; font-weight: 650; cursor: pointer; }
.authorization-history > div { display: grid; gap: 1px; padding: 0 14px 14px; }
.authorization-history article { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 14px; padding: 11px; border-radius: 9px; background: color-mix(in srgb, var(--primitive-glass-hover) 55%, transparent); }
.authorization-history article div { display: flex; gap: 9px; }
.authorization-history article b { color: var(--content-primary); font-size: 13px; }
.authorization-history article span, .authorization-history article small, .authorization-history article em { color: var(--content-muted); font-size: 11px; font-style: normal; }
.authorization-history article small { grid-column: 1 / -1; }
.system-diagnostics { margin-top: 4px; }
.system-diagnostics[open] > summary { border-bottom: 1px solid var(--panel-edge); }
.system-diagnostics .system-grid { padding: 18px; }
.connection-panel { grid-row: span 2; }
.status-chip { padding: 6px 9px; border: 1px solid var(--panel-edge); border-radius: 999px; color: var(--content-muted); background: var(--primitive-glass); font-size: 12px; }
.status-chip.online { color: var(--state-ready); }
@ -366,7 +409,7 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
.knowledge-page { grid-template-columns: 250px minmax(390px, 1fr); }
.document-inspector { display: none; }
.code-workbench { grid-template-columns: 220px 230px minmax(0, 1fr); }
.overview-grid, .system-grid { grid-template-columns: 1fr; }
.overview-grid, .system-grid, .human-authorization-layout { grid-template-columns: 1fr; }
.overview-columns { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
@ -783,6 +826,45 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
}
.receipt-message { color: var(--accent-light) !important; font-weight: 650; text-align: right; }
/* 分域商城是一张可核验清单,不伪装成促销卡片。 */
.marketplace-page { max-width: 1180px; }
.marketplace-title { align-items: flex-end; }
.marketplace-title > button { flex: 0 0 auto; margin-bottom: 7px; }
.marketplace-proof { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0 0 22px; border: 1px solid var(--panel-edge); border-radius: 14px; overflow: hidden; background: color-mix(in srgb, var(--panel-bg) 78%, transparent); }
.marketplace-proof > div { min-width: 0; padding: 14px 17px; border-right: 1px solid var(--panel-edge); }
.marketplace-proof > div:last-child { border-right: 0; }
.marketplace-proof span, .marketplace-proof b { display: block; }
.marketplace-proof span { margin-bottom: 6px; color: var(--content-faint); font-size: 10px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; }
.marketplace-proof b { overflow: hidden; color: var(--content-primary); font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.marketplace-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.marketplace-tabs button { display: grid; gap: 6px; padding: 16px 18px; border: 1px solid var(--panel-edge); border-radius: 13px; color: var(--content-secondary); background: var(--primitive-glass); text-align: left; cursor: pointer; }
.marketplace-tabs button.active { border-color: color-mix(in srgb, var(--primitive-cool-glow) 58%, var(--panel-edge)); background: color-mix(in srgb, var(--primitive-cool-glow) 11%, var(--panel-bg)); box-shadow: inset 0 0 22px color-mix(in srgb, var(--primitive-cool-glow) 5%, transparent); }
.marketplace-tabs b { color: var(--content-primary); font-size: 15px; }
.marketplace-tabs span { color: var(--content-muted); font-size: 11.5px; line-height: 1.55; }
.marketplace-boundary { margin: 12px 0 20px; padding: 11px 14px; border-left: 2px solid var(--accent-light); color: var(--content-muted); background: color-mix(in srgb, var(--primitive-glass) 58%, transparent); font-size: 11.5px; line-height: 1.7; }
.marketplace-list { display: grid; border-top: 1px solid var(--panel-edge); }
.marketplace-item { padding: 22px 2px; border-bottom: 1px solid var(--panel-edge); }
.marketplace-item > header { display: flex; justify-content: space-between; gap: 28px; }
.marketplace-item > header > div { min-width: 0; }
.marketplace-item > header span { color: var(--accent-light); font-size: 9.5px; font-weight: 750; letter-spacing: .15em; }
.marketplace-item h2 { margin: 6px 0; color: var(--content-primary); font-size: 19px; }
.marketplace-item header p { max-width: 760px; margin: 0; color: var(--content-muted); font-size: 12px; line-height: 1.65; }
.marketplace-item header em { align-self: start; flex: 0 0 auto; padding: 5px 9px; border: 1px solid var(--panel-edge); border-radius: 999px; color: var(--content-faint); font-size: 10px; font-style: normal; }
.marketplace-item header em.installed { border-color: color-mix(in srgb, var(--primitive-cool-glow) 45%, var(--panel-edge)); color: var(--accent-light); }
.marketplace-item dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px 26px; margin: 16px 0 0; }
.marketplace-item dl div { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 10px; padding: 8px 0; border-bottom: 1px solid color-mix(in srgb, var(--panel-edge) 62%, transparent); }
.marketplace-item dt { color: var(--content-faint); font-size: 10.5px; }
.marketplace-item dd { overflow-wrap: anywhere; margin: 0; color: var(--content-secondary); font-size: 10.5px; }
.marketplace-actions { display: flex; justify-content: flex-end; gap: 9px; margin-top: 16px; }
.marketplace-actions button { min-height: 36px; padding: 0 13px; border: 1px solid var(--panel-edge); border-radius: 9px; color: var(--content-secondary); background: var(--primitive-glass); font-size: 11px; font-weight: 680; cursor: pointer; }
.marketplace-actions button:disabled { opacity: .52; cursor: default; }
.marketplace-actions .primary-button { color: var(--button-primary-text); background: var(--button-primary-bg); }
.marketplace-empty { padding: 52px 18px; color: var(--content-muted); text-align: center; }
.marketplace-empty b { color: var(--content-primary); }
.marketplace-empty p { font-size: 12px; }
.marketplace-message { margin-top: 18px; }
.marketplace-receipt { padding: 15px 2px 0; color: var(--content-faint); font-size: 10px; overflow-wrap: anywhere; }
@media (max-width: 900px) {
.d-main { left: 2%; } .d-sub { left: 22%; } .d-zero { right: 22%; } .d-zs { right: 2%; }
.domain-info-card.info-main { left: 18px; } .domain-info-card.info-sub { left: calc(22% - 75px); }
@ -791,6 +873,10 @@ svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-lineca
.channel-knowledge { left: 4%; } .channel-code { left: 25%; } .channel-light { right: 25%; } .channel-status { right: 4%; }
.broadcast-stream { left: 5%; }
.personal-node-guide ol { grid-template-columns: 1fr; }
.marketplace-proof { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.marketplace-proof > div:nth-child(2) { border-right: 0; }
.marketplace-proof > div:nth-child(-n+2) { border-bottom: 1px solid var(--panel-edge); }
.marketplace-item dl { grid-template-columns: 1fr; }
}
@media (max-height: 720px) {
.official-hero { top: 14px; } .official-hero h1 { font-size: 24px; }