chore: snapshot audited HoloLake convergence baseline
This commit is contained in:
parent
7a14c06e41
commit
1b6b17b5ab
47 changed files with 10722 additions and 87 deletions
|
|
@ -0,0 +1,137 @@
|
|||
import type { CSSProperties } from 'react'
|
||||
export { nativeCompositionManifest } from './manifest'
|
||||
|
||||
export type CompositionDimension = 'SOURCE' | 'TOP_LEVEL_FOLDER'
|
||||
export type CompositionMeasure = 'DOCUMENT_COUNT' | 'TOTAL_BYTES' | 'DUPLICATE_COUNT'
|
||||
export type ProjectionView = 'DASHBOARD' | 'COMPARISON' | 'VERTICAL_BAR' | 'CLASSIFICATION' | 'TABLE'
|
||||
|
||||
export interface NativeCompositionRow {
|
||||
rowId: string
|
||||
source: string
|
||||
path: string
|
||||
title: string
|
||||
topLevelFolder: string
|
||||
sizeBytes: number
|
||||
duplicateCount: number
|
||||
updatedAtUnixMs: number
|
||||
}
|
||||
|
||||
export interface NativeCompositionGroup {
|
||||
key: string
|
||||
label: string
|
||||
documentCount: number
|
||||
totalBytes: number
|
||||
duplicateCount: number
|
||||
measureValue: number
|
||||
share: number
|
||||
}
|
||||
|
||||
export interface NativeCompositionProjection {
|
||||
schema: string
|
||||
state: string
|
||||
executionId: string
|
||||
executedAtUnixMs: number
|
||||
sourceIsRealAccountData: boolean
|
||||
readOnly: boolean
|
||||
dimension: CompositionDimension
|
||||
measure: CompositionMeasure
|
||||
views: ProjectionView[]
|
||||
nativeObject: {
|
||||
schema: string
|
||||
objectId: string
|
||||
title: string
|
||||
rowCount: number
|
||||
truncated: boolean
|
||||
sourceReceipt: string
|
||||
rows: NativeCompositionRow[]
|
||||
}
|
||||
groups: NativeCompositionGroup[]
|
||||
metrics: {
|
||||
rawDocumentCount: number
|
||||
uniqueDocumentCount: number
|
||||
duplicateDocumentCount: number
|
||||
totalBytes: number
|
||||
groupCount: number
|
||||
}
|
||||
recipe: {
|
||||
schema: string
|
||||
recipeId: string
|
||||
title: string
|
||||
nodes: { nodeId: string; moduleId: string }[]
|
||||
edges: { from: string; to: string }[]
|
||||
}
|
||||
dataSha256: string
|
||||
receiptSha256: string
|
||||
}
|
||||
|
||||
const viewNames: Record<ProjectionView, string> = {
|
||||
DASHBOARD: '仪表盘',
|
||||
COMPARISON: '对比',
|
||||
VERTICAL_BAR: '柱状图',
|
||||
CLASSIFICATION: '分类',
|
||||
TABLE: '明细表',
|
||||
}
|
||||
|
||||
const measureNames: Record<CompositionMeasure, string> = {
|
||||
DOCUMENT_COUNT: '文档数量',
|
||||
TOTAL_BYTES: '数据量',
|
||||
DUPLICATE_COUNT: '重复数量',
|
||||
}
|
||||
|
||||
export function formatCompositionValue(value: number, measure: CompositionMeasure): string {
|
||||
if (measure !== 'TOTAL_BYTES') return `${value.toLocaleString('zh-CN')} 篇`
|
||||
if (value < 1024) return `${value.toLocaleString('zh-CN')} B`
|
||||
if (value < 1024 * 1024) return `${(value / 1024).toFixed(value < 10 * 1024 ? 1 : 0)} KB`
|
||||
return `${(value / 1024 / 1024).toFixed(1)} MB`
|
||||
}
|
||||
|
||||
export function toggleProjectionView(current: ProjectionView[], view: ProjectionView): ProjectionView[] {
|
||||
if (current.includes(view)) return current.length === 1 ? current : current.filter((item) => item !== view)
|
||||
return [...current, view]
|
||||
}
|
||||
|
||||
interface NativeCompositionStudioProps {
|
||||
projection: NativeCompositionProjection | null
|
||||
dimension: CompositionDimension
|
||||
measure: CompositionMeasure
|
||||
selectedViews: ProjectionView[]
|
||||
busy: boolean
|
||||
message: string
|
||||
onDimensionChange: (value: CompositionDimension) => void
|
||||
onMeasureChange: (value: CompositionMeasure) => void
|
||||
onViewsChange: (value: ProjectionView[]) => void
|
||||
onExecute: () => void
|
||||
}
|
||||
|
||||
export function NativeCompositionStudio(props: NativeCompositionStudioProps) {
|
||||
const { projection, dimension, measure, selectedViews, busy, message } = props
|
||||
const groups = projection?.groups || []
|
||||
const activeDimension = projection?.dimension || dimension
|
||||
const activeMeasure = projection?.measure || measure
|
||||
const activeViews = projection?.views || selectedViews
|
||||
const maximum = Math.max(1, ...groups.map((group) => group.measureValue))
|
||||
return <div className="native-composition-layout">
|
||||
<aside className="composition-recipe-panel">
|
||||
<header><span>原生组合配方</span><h2>知识结构观察</h2><p>同一份数据 · 多种动态投影</p></header>
|
||||
<section className="composition-source"><i/><div><span>真实数据源</span><b>当前账号知识目录</b><small>{projection ? `${projection.nativeObject.rowCount} 条原生对象` : '等待系统读取'}</small></div></section>
|
||||
<div className="composition-connector" aria-hidden="true"/>
|
||||
<label><span>分类维度</span><select aria-label="组合分类维度" value={dimension} onChange={(event) => props.onDimensionChange(event.target.value as CompositionDimension)}><option value="TOP_LEVEL_FOLDER">一级目录</option><option value="SOURCE">知识来源</option></select></label>
|
||||
<label><span>统计指标</span><select aria-label="组合统计指标" value={measure} onChange={(event) => props.onMeasureChange(event.target.value as CompositionMeasure)}><option value="DOCUMENT_COUNT">文档数量</option><option value="TOTAL_BYTES">数据量</option><option value="DUPLICATE_COUNT">重复数量</option></select></label>
|
||||
<fieldset><legend>人类投影</legend>{(Object.keys(viewNames) as ProjectionView[]).map((view) => <label key={view}><input type="checkbox" checked={selectedViews.includes(view)} onChange={() => props.onViewsChange(toggleProjectionView(selectedViews, view))}/><span>{viewNames[view]}</span></label>)}</fieldset>
|
||||
<button className="composition-execute" type="button" disabled={busy} onClick={props.onExecute}>{busy ? '系统正在组合…' : '按当前配方重新组合'}</button>
|
||||
<footer>{projection ? <><b>已由原生内核执行</b><span>回执 {projection.receiptSha256.slice(0, 12)}</span><small>{new Date(projection.executedAtUnixMs).toLocaleString('zh-CN')}</small></> : <span>{message || '组合执行只读,不修改知识原文。'}</span>}</footer>
|
||||
</aside>
|
||||
<main className="composition-projection-stage" aria-busy={busy}>
|
||||
<header><div><span>HUMAN PROJECTION</span><h1>知识空间结构组合</h1><p>{projection ? `按${activeDimension === 'SOURCE' ? '知识来源' : '一级目录'}观察${measureNames[activeMeasure]} · 数据来自当前账号真实知识目录` : '正在准备当前账号的原生知识对象'}</p></div><div className="composition-state"><i/><span>{projection?.sourceIsRealAccountData ? '真实数据已接入' : '等待数据'}</span><small>{projection?.readOnly ? '只读组合' : '未执行'}</small></div></header>
|
||||
{message && <p className="composition-message">{message}</p>}
|
||||
{projection && projection.nativeObject.rowCount === 0 && <section className="composition-empty"><span>空</span><h2>当前知识目录还没有内容</h2><p>这里不会用样例冒充真实数据。先在知识空间导入或建立页面,再重新组合。</p></section>}
|
||||
{projection && projection.nativeObject.rowCount > 0 && <div className="composition-view-grid">
|
||||
{activeViews.includes('DASHBOARD') && <section className="composition-view composition-dashboard"><header><span>仪表盘</span><small>同一执行结果的总览</small></header><div><article><span>唯一知识页</span><strong>{projection.metrics.uniqueDocumentCount.toLocaleString('zh-CN')}</strong><small>篇</small></article><article><span>原始页面</span><strong>{projection.metrics.rawDocumentCount.toLocaleString('zh-CN')}</strong><small>篇</small></article><article><span>知识数据量</span><strong>{formatCompositionValue(projection.metrics.totalBytes, 'TOTAL_BYTES')}</strong><small>原生目录</small></article><article><span>动态分类</span><strong>{projection.metrics.groupCount}</strong><small>组</small></article></div></section>}
|
||||
{activeViews.includes('VERTICAL_BAR') && <section className="composition-view composition-bars"><header><span>柱状图</span><small>{measureNames[activeMeasure]} · 动态比例</small></header><div className="composition-bar-plot">{groups.slice(0, 12).map((group) => <article key={group.key}><div className="composition-bar-track"><i style={{ '--bar-height': `${Math.max(4, group.measureValue / maximum * 100)}%` } as CSSProperties}/><em>{formatCompositionValue(group.measureValue, activeMeasure)}</em></div><b title={group.label}>{group.label}</b></article>)}</div></section>}
|
||||
{activeViews.includes('COMPARISON') && <section className="composition-view composition-comparison"><header><span>对比</span><small>各分类占比</small></header><div>{groups.slice(0, 10).map((group) => <article key={group.key}><div><b>{group.label}</b><span>{formatCompositionValue(group.measureValue, activeMeasure)}</span></div><i><em style={{ '--share': `${group.share * 100}%` } as CSSProperties}/></i><small>{(group.share * 100).toFixed(1)}%</small></article>)}</div></section>}
|
||||
{activeViews.includes('CLASSIFICATION') && <section className="composition-view composition-classification"><header><span>分类</span><small>从原生路径实时生成</small></header><div>{groups.map((group, index) => <article key={group.key}><i>{String(index + 1).padStart(2, '0')}</i><div><b>{group.label}</b><span>{group.documentCount} 篇文档 · {formatCompositionValue(group.totalBytes, 'TOTAL_BYTES')}</span></div><strong>{formatCompositionValue(group.measureValue, activeMeasure)}</strong></article>)}</div></section>}
|
||||
{activeViews.includes('TABLE') && <section className="composition-view composition-table"><header><span>原生明细表</span><small>{projection.nativeObject.schema} · 只读</small></header><div><table><thead><tr><th>标题</th><th>一级分类</th><th>来源</th><th>数据量</th><th>重复</th><th>更新时间</th></tr></thead><tbody>{projection.nativeObject.rows.slice(0, 100).map((row) => <tr key={row.rowId}><td title={row.path}>{row.title}</td><td>{row.topLevelFolder}</td><td>{row.source === 'native' ? '光湖原生' : row.source}</td><td>{formatCompositionValue(row.sizeBytes, 'TOTAL_BYTES')}</td><td>{row.duplicateCount}</td><td>{new Date(row.updatedAtUnixMs).toLocaleDateString('zh-CN')}</td></tr>)}</tbody></table></div></section>}
|
||||
</div>}
|
||||
</main>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
export const nativeCompositionManifest = {
|
||||
schema: 'hololake.composition-module/v1',
|
||||
moduleId: 'hololake.native-composition-projection',
|
||||
name: '原生组合视图',
|
||||
version: '0.1.0',
|
||||
slot: 'education-composition',
|
||||
origin: 'resident',
|
||||
input: 'hololake.human-projection/v1',
|
||||
exports: ['NativeCompositionStudio', 'formatCompositionValue', 'toggleProjectionView'],
|
||||
authority: 'READ_ONLY_HUMAN_PROJECTION',
|
||||
} as const
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { formatCompositionValue, toggleProjectionView, type ProjectionView } from './index'
|
||||
|
||||
describe('native composition human projection', () => {
|
||||
it('formats one native measure consistently across projections', () => {
|
||||
expect(formatCompositionValue(12, 'DOCUMENT_COUNT')).toBe('12 篇')
|
||||
expect(formatCompositionValue(1536, 'TOTAL_BYTES')).toBe('1.5 KB')
|
||||
expect(formatCompositionValue(2, 'DUPLICATE_COUNT')).toBe('2 篇')
|
||||
})
|
||||
|
||||
it('allows free projection combinations but never an empty projection', () => {
|
||||
const initial: ProjectionView[] = ['DASHBOARD', 'TABLE']
|
||||
expect(toggleProjectionView(initial, 'VERTICAL_BAR')).toEqual(['DASHBOARD', 'TABLE', 'VERTICAL_BAR'])
|
||||
expect(toggleProjectionView(initial, 'TABLE')).toEqual(['DASHBOARD'])
|
||||
expect(toggleProjectionView(['TABLE'], 'TABLE')).toEqual(['TABLE'])
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue