- migrated legacy_doc_id: D3-FE-ARCH-003 level: l3
Xisound 前端共享基础设施架构 v1.1
v1.1 升级要点(2026-05-09)
- 新增 §2.3 三大模块组件 API(ModuleNode / ModulePalette / ModuleTuningDialog)· 仅声明 Props 接口 · 详细 UI 实施规范见 D3-FE-ARCH-007
- 新增 §2.6 通用 atoms 强制契约(XiFileDropzone / XiProgressBar / XiAlert / XiInfoTable)· 对接 D3-FE-ARCH-010 source/sink UI 需求
- 修复 H4 标题尾部 emoji(A5 违规修正)
- 严格遵守 md-writing-spec v1.1
文档定位
- 上游:D3-FE-ARCH-001 顶层架构说明书 第 4 章
- 本文范围:
packages/下 5 个共享库的详细架构;是所有 apps 的地基 - 约束强度:本文定义的 API 边界为强制契约;apps 不得绕过直接实现同类能力
- 目标读者:前端基建工程师 / 各产品 app 开发者 / 消费实现 plan 的智能体
1. 共享 packages 总览
1.1 5 个 packages 的依赖关系
graph TB
subgraph Apps["apps/"]
Studio["xi-studio"]
Forge["xi-forge"]
VST["xi-vst"]
Tune["xi-tune"]
Test["xi-test"]
Probe["xi-probe"]
end
subgraph Packages["packages/"]
UIKit["@xi/ui-kit"]
Protocol["@xi/protocol"]
StoreCore["@xi/store-core"]
DSPUtils["@xi/dsp-utils"]
AISDK["@xi/ai-sdk"]
end
subgraph External["外部依赖"]
Vue["Vue 3"]
Pinia["Pinia"]
Tailwind["Tailwind CSS"]
ECharts["ECharts"]
Zod["zod"]
end
Apps --> UIKit
Apps --> Protocol
Apps --> StoreCore
Apps --> AISDK
Studio --> DSPUtils
Forge --> DSPUtils
Tune --> DSPUtils
Probe --> DSPUtils
UIKit --> Vue
UIKit --> Tailwind
UIKit --> ECharts
StoreCore --> Pinia
StoreCore --> Protocol
Protocol --> Zod
AISDK --> Protocol
DSPUtils --> Vue
class Studio,Forge,VST,Tune,Test,Probe xyL2
class UIKit,Protocol,StoreCore,DSPUtils,AISDK xyL3
class Vue,Pinia,Tailwind,ECharts,Zod xyL0
class Apps xySgL4; class Packages xySgL4;
1.2 依赖原则
| 原则 | 说明 |
|---|---|
| Packages 之间单向依赖 | @xi/store-core 可依赖 @xi/protocol;@xi/ai-sdk 可依赖 @xi/protocol;其他组合一律禁止 |
@xi/ui-kit 不依赖其他 xi package |
保持纯 UI 层,避免循环依赖 |
Apps 依赖 packages 必须用 workspace:* |
由 pnpm + changesets 统一版本 |
| Packages 发布到私有 npm registry(Y2+) | Y1 内部使用 workspace 即可 |
2. @xi/ui-kit · UI 组件库
2.1 定位与目标
- 单一真相:所有 apps 的 UI 原子/分子/复合组件统一入口
- 设计一致性:通过 Tailwind preset + 设计令牌保证跨产品视觉统一
- 按需引入:支持 tree-shaking,apps 不会因为引入 ui-kit 而膨胀
2.2 目录结构
packages/ui-kit/
├── src/
│ ├── atoms/ # 原子组件
│ │ ├── Button.vue
│ │ ├── Input.vue
│ │ ├── Select.vue
│ │ ├── Checkbox.vue
│ │ ├── Radio.vue
│ │ ├── Switch.vue
│ │ ├── Slider.vue
│ │ ├── Tag.vue
│ │ ├── Badge.vue
│ │ ├── Icon.vue
│ │ └── Tooltip.vue
│ ├── molecules/ # 分子组件
│ │ ├── Tabs.vue
│ │ ├── Modal.vue
│ │ ├── Drawer.vue
│ │ ├── Toast.vue (+ service)
│ │ ├── PropertyEditor.vue
│ │ ├── Toolbar.vue
│ │ ├── StatusBar.vue
│ │ ├── SplitPane.vue
│ │ ├── TreeView.vue
│ │ ├── DropZone.vue
│ │ └── ContextMenu.vue
│ ├── organisms/ # 产品专用复合组件
│ │ ├── LinkCanvas/ # 链路画布(XiStudio + XiForge 共用 · 算法画布)
│ │ │ ├── LinkCanvas.vue
│ │ │ ├── ModuleNode.vue
│ │ │ ├── Connection.vue
│ │ │ └── types.ts
│ │ ├── SignalCanvas/ # ⭐ 新增 · 信号流画布(XiStudio 专用 · 对标 LiveAmp)
│ │ │ ├── SignalCanvas.vue
│ │ │ ├── SignalSourceNode.vue # signal_common_v1 渲染
│ │ │ ├── SignalOperatorNode.vue # mapping/smooth/add/... 通用节点
│ │ │ ├── SignalOutputNode.vue # signal_output_v1 渲染(含实时值显示)
│ │ │ ├── ControlWire.vue # control-rate 连线(动画粒子流)
│ │ │ └── types.ts
│ │ ├── ModulePalette.vue # 左侧模块面板
│ │ ├── SpectrumChart.vue # 频谱图(基于 ECharts)
│ │ ├── WaveformChart.vue # 波形图
│ │ ├── EQCurveEditor.vue # EQ 曲线编辑器
│ │ ├── LevelMeter.vue # 电平表
│ │ └── CopilotDrawer.vue # AI Copilot 抽屉(引用 ai-sdk 组件)
│ ├── composables/ # 共享 composition API
│ │ ├── useBreakpoint.ts
│ │ ├── useTheme.ts
│ │ ├── useHotkey.ts
│ │ └── useDraggable.ts
│ ├── tokens/ # 设计令牌
│ │ ├── colors.ts
│ │ ├── typography.ts
│ │ ├── spacing.ts
│ │ ├── shadow.ts
│ │ └── motion.ts
│ ├── tailwind-preset.ts # Tailwind preset(供 apps 引用)
│ ├── i18n/
│ │ ├── zh-CN.json
│ │ └── en-US.json
│ └── index.ts # 总导出
├── package.json
└── tsconfig.json
2.3 核心组件 API 契约
<LinkCanvas> · 链路画布
用途:XiStudio 的 Link Editor 和 XiForge 的 Module Designer 共用。
interface LinkCanvasProps {
modules: ModuleInstance[] // 模块列表(来自 store-core 的 LinkModel)
connections: Connection[] // 连接列表
readonly?: boolean // 只读模式
snap?: number // 网格对齐像素(默认 8)
onModuleMove?: (id: string, pos: Position) => void
onConnectionAdd?: (from: PortRef, to: PortRef) => void
onConnectionRemove?: (id: string) => void
onSelectionChange?: (ids: string[]) => void
}
插槽:
#module-content(module):自定义模块内部渲染(XiForge 可能自定义,XiStudio 用默认)#toolbar:画布顶部工具栏扩展位
<PropertyEditor> · 属性编辑器
用途:模块参数 / 调音参数 / 测试配置 统一编辑界面。
interface PropertySchema {
key: string
label: string
type: 'number' | 'string' | 'boolean' | 'enum' | 'slider' | 'color'
min?: number
max?: number
step?: number
options?: Array<{ label: string; value: any }>
unit?: string
tooltip?: string
readonly?: boolean
group?: string // 分组折叠
}
interface PropertyEditorProps {
schema: PropertySchema[]
modelValue: Record<string, any>
'onUpdate:modelValue': (v: Record<string, any>) => void
}
<SpectrumChart> · 频谱图
- 输入:
data: Float32Array(FFT 幅值)+sampleRate: number - 刻度:对数频率轴(20Hz-20kHz)+ dB 幅值轴(-80 ~ 0 dB)
- 性能要求:60 FPS · 支持 2048 点 FFT 实时渲染
- 扩展点:
slot#overlay供叠加额外曲线(如 EQ 响应)
<EQCurveEditor> · EQ 曲线编辑器
- 输入/输出:
EQBand[](每段 = type + freq + gain + q) - 实时预览:通过
@xi/dsp-utils的 biquad 计算合成曲线 - 交互:拖拽控制点改频率/增益;滚轮改 Q;右键菜单改类型
<ModuleNode> · 画布卡片 Shell(强制契约 · v1.1 新增)
用途:所有 DSP 算法模块在 <LinkCanvas> 中的统一卡片渲染。详细 UI 实施规范(7 强制元素 / 状态指示灯 / 错误徽标等)见 D3-FE-ARCH-007 §2。
interface ModuleNodeProps {
module: ModuleModel // 来自 @xi/store-core 的领域模型
selected?: boolean
readonly?: boolean
liveStatus?: 'idle' | 'running' | 'error' | 'loading' // 与 useSourceStateStore 对接
onToggleEnabled?: (id: string, enabled: boolean) => void
onOpenTuningDialog?: (id: string) => void
onContextMenu?: (id: string, event: MouseEvent) => void
}
强制约束:
- 所有 apps 必须使用本组件渲染画布上的模块卡片,不得自实现同类组件
- 卡片必须包含 D3-FE-ARCH-007 §2.1 规定的 7 强制元素(toggle / icon / name / 输入端口 / 输出端口 / status / settings)
- 用法:
<ModuleNode :module="m" :selected="isSel" @open-tuning-dialog="openDialog" />
<ModulePalette> · 左侧模块面板(强制契约 · v1.1 新增)
用途:所有 apps 的 ModuleLibrary 列表渲染。详细规范见 D3-FE-ARCH-007 §3。
interface ModulePaletteProps {
modules: ModuleDefinition[] // 模块库定义列表
groupBy?: 'category' | 'package' // 默认 category(20 类分组)
filter?: string // 搜索关键字
onModuleDragStart?: (def: ModuleDefinition, event: DragEvent) => void
onModuleClick?: (def: ModuleDefinition) => void
}
interface ModuleDefinition {
type: string // 如 'source_v1' / 'eq-parametric'
category: string // 20 类之一
displayName: string // i18n key
iconUrl?: string // 图标 SVG 路径
description?: string // 副标题/tooltip
}
强制约束:
- 必须显示 icon + name + category 三件套(D3-FE-ARCH-007 §3.1)
- 缺 icon 时使用三级 fallback(type → category → default)
- 用法:
<ModulePalette :modules="lib" group-by="category" @module-drag-start="onDrag" />
<ModuleTuningDialog> · 模块调音对话框(强制契约 · v1.1 新增)
用途:模块参数编辑统一容器,三段式布局(preset 区 / argument 区 / 操作区)。详细规范见 D3-FE-ARCH-007 §4。
interface ModuleTuningDialogProps {
moduleId: string
visible: boolean
'onUpdate:visible': (v: boolean) => void
}
强制插槽:
#preset-area:顶部 preset 管理区(必填 · 详见 D3-FE-ARCH-009 §4)#argument-area:中间参数区(必填 · 三块结构:输入参数 / 输出端口信息 / 性能监控)#actions-area:底部操作区(保存 / 取消 / 应用)
强制约束:
- 关闭对话框时如有未保存改动必须弹确认(D3-FE-ARCH-009 §4.3)
- 用法:
<ModuleTuningDialog :module-id="id" v-model:visible="show" />
<SignalCanvas> · 信号流画布(对标 LiveAmp · XiStudio 专用)
用途:XiStudio 的 Signal Flow Editor 独立画布。管理车载系统信号(CAN/速度/音量/GPIO/事件)的采集→运算→输出,供算法模块通过 externalBind 订阅。
与 <LinkCanvas> 的差异:
| 维度 | <LinkCanvas>(算法画布) |
<SignalCanvas>(信号画布) |
|---|---|---|
| 节点类型 | 20 类 DSP 算法模块 | signal_common / signal_output / 11 个运算模块 |
| 连线含义 | audio-rate(48kHz Float32) | control-rate(100~1000Hz Float32) |
| 连线视觉 | 静态粗线(音频流) | 动画粒子流(信号脉动) |
| 节点视觉 | 标准模块盒 | 节点上实时显示信号值(LiveAmp 标志性特征) |
| 画布背景 | 白/深灰网格 | 轻蓝色渐变(与算法画布视觉区分) |
| 产出物 | LinkModel(audioGraph) | SignalGraphModel(signalGraph + bindings) |
Props/Events:
interface SignalCanvasProps {
nodes: SignalNode[] // 信号节点列表(signal_common / signal_operator / signal_output)
connections: ControlWire[] // control-rate 连线
readonly?: boolean
liveValues?: Record<string, number> // 节点 id → 实时值映射(来自 signal-value-event 消息)
onNodeMove?: (id: string, pos: Position) => void
onConnectionAdd?: (from: PortRef, to: PortRef) => void
onConnectionRemove?: (id: string) => void
onSelectionChange?: (ids: string[]) => void
}
interface SignalNode {
id: string
type: 'signal_common' | 'signal_output' | 'mapping' | 'linear' | 'smooth' | 'gradient' | 'add' | 'sub' | 'mul' | 'div' | 'logic' | 'random'
position: Position
params: Record<string, any>
liveValue?: number // 当前实时值(节点内显示)
}
interface ControlWire {
id: string
from: { nodeId: string; portId: string }
to: { nodeId: string; portId: string }
}
插槽:
#node-content(node):自定义节点内部渲染(signal_common_v1有独特的源类型图标和范围 UI)#toolbar:画布顶部工具栏扩展(添加节点 / 自动布局 / 导出 SVG)
性能要求:
- 50 个信号节点同时存在时 60 FPS 拖拽
- liveValues 更新 100Hz(对应 control-rate),节点值显示使用 RAF 节流
<ControlWire>的粒子动画走 CSS @keyframes(不用 JS 驱动)
2.4 Tailwind 设计令牌
// tailwind-preset.ts 核心片段
export default {
theme: {
colors: {
xi: {
primary: { 50: '#FFF8EF', 500: '#D4A574', 900: '#6B4423' },
accent: { 500: '#9D4EDD' },
ink: { 900: '#1A2332' },
paper: { 50: '#F5F2EA' },
success: '#10B981',
warning: '#F59E0B',
danger: '#EF4444',
info: '#3B82F6',
}
},
fontFamily: {
sans: ['"Inter"', '"PingFang SC"', 'sans-serif'],
mono: ['"JetBrains Mono"', 'monospace'],
}
},
// 暗色模式通过 class 切换
darkMode: 'class',
}
2.5 i18n 约定
- 每个组件的文案必须走 i18n key,不得硬编码中英文
- Key 命名:
ui.{component}.{purpose}(如ui.modal.confirm-cancel) - Apps 通过
useI18nStore(来自@xi/store-core)切换语言,@xi/ui-kit自动响应
2.6 通用 atoms 强制契约(v1.1 新增 · 对接 D3-FE-ARCH-010)
D3-FE-ARCH-010 Source/Sink 前端实施规范 引用了以下 4 个通用组件,本节给出强制级 API 契约。所有 apps 涉及文件上传 / 进度展示 / 警告提示 / 元信息表的场景必须使用本节组件。
<XiFileDropzone> · 拖拽上传区
interface XiFileDropzoneProps {
accept: string // 必填 · MIME 或扩展名(如 '.wav,.flac')
maxSizeMb?: number // 默认 100 · 超出弹错误
multiple?: boolean // 默认 false
disabled?: boolean
'onSelect': (files: File[]) => void
}
必填项:accept / onSelect。
强制约束:
- 必须支持拖拽 + 点击双入口(点击触发隐藏的
<input type="file">) - 必须有键盘 focus 后按 Enter 触发文件选择对话框(a11y)
- 文件超出
maxSizeMb时调用useNotificationStore弹 error toast,不调用onSelect
用法:<XiFileDropzone accept=".wav" :max-size-mb="500" @select="handleWav" />
<XiProgressBar> · 进度条
interface XiProgressBarProps {
percent: number // 必填 · 0-100
label?: string // 可选文字(如 "加载中 30%")
variant?: 'default' | 'success' | 'warning' | 'error' // 默认 default(曦紫)
indeterminate?: boolean // 不确定进度(无 percent 时使用)
}
必填项:percent(除非 indeterminate=true)。
强制约束:
percent范围外(< 0 或 > 100)必须 clamp 到 [0, 100]- 颜色统一走 Tailwind preset 的
xi.primary/xi.success/xi.warning/xi.danger,禁止手写 hex
用法:<XiProgressBar :percent="progress" :label="\加载中 ${progress}%`" />`
<XiAlert> · 警告/错误/信息条
interface XiAlertProps {
type: 'info' | 'success' | 'warning' | 'error' // 必填
message: string // 必填
description?: string // 可选详细说明
closable?: boolean // 默认 false
actions?: Array<{ // 可选 action 按钮(如"重试"/"修改采样率")
label: string
onClick: () => void
}>
'onClose'?: () => void
}
必填项:type / message。
强制约束:
- 必须设置
role="alert"+aria-live="polite"(a11y) - 颜色映射:info → xi.info / success → xi.success / warning → xi.warning / error → xi.danger
actions数组超过 3 个必须使用下拉菜单收纳(避免一字排开过宽)
用法:<XiAlert type="error" message="wav 加载失败" :actions="[{label: '重试', onClick: retry}]" />
<XiInfoTable> · 元信息表
用法:
<XiInfoTable>
<tr><td>文件名</td><td>{{ filename }}</td></tr>
<tr><td>采样率</td><td>{{ sampleRate }} Hz</td></tr>
</XiInfoTable>
强制约束:
- 不允许直接用
<table>渲染元信息,必须使用本组件(统一字号 / 间距 / 边框样式) - 单元格内禁止嵌
<br>/<span style>(违反 md-writing-spec §7.1) - 长值(> 80 字符)必须 truncate + tooltip
3. @xi/protocol · 通信协议契约
3.1 定位与目标
- 单一真相源:前后端通信、app 间通信、插件协议所有 Schema 只在此定义一次
- 类型安全:TS 类型 + zod 运行时校验双重保障
- 契约驱动:backend (C#) 侧对应的 DTO/Schema 必须与本 package 同步(通过自动化工具或人工 review)
3.2 目录结构
packages/protocol/
├── src/
│ ├── websocket/ # WebSocket 消息 Schema
│ │ ├── audio-engine-event.ts
│ │ ├── control-command.ts
│ │ ├── signal-value-event.ts # ⭐ 新增 · 信号实时值推送
│ │ ├── signal-control-command.ts # ⭐ 新增 · 信号命令(激活/停用/写入)
│ │ └── index.ts
│ ├── http/ # HTTP API 契约
│ │ ├── projects.ts
│ │ ├── presets.ts
│ │ ├── algo-library.ts
│ │ ├── compile.ts # ⭐ 新增 · XiForge 编译(云端+本地双模式 · DQ-03)
│ │ └── index.ts
│ ├── deep-link/ # xi:// URL Scheme
│ │ ├── schema.ts
│ │ └── parser.ts
│ ├── bus/ # 跨 app 消息总线
│ │ ├── broadcast-channel.ts
│ │ └── events.ts
│ ├── signal-flow/ # ⭐ 新增 · 信号流画布 schema(对标 LiveAmp)
│ │ ├── signal-node.ts # SignalNode / SignalCommonParams / SignalOperatorParams
│ │ ├── signal-graph.ts # SignalGraphModel(节点 + 连线 + 绑定)
│ │ ├── bindings.ts # ParamBinding(算法模块订阅信号的契约)
│ │ └── index.ts
│ ├── xvst/ # XVST 插件协议(Y1Q3+)
│ │ ├── manifest.ts
│ │ ├── lifecycle.ts
│ │ └── runtime.ts
│ ├── common/
│ │ ├── types.ts # 共享类型(Position / Port / Node 等)
│ │ └── errors.ts # 统一错误码
│ └── index.ts
├── package.json
└── tsconfig.json
3.3 核心契约
WebSocket 消息
// audio-engine-event.ts
import { z } from 'zod'
export const AudioEngineEventSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('link-state'),
payload: z.object({
linkId: z.string(),
state: z.enum(['idle', 'running', 'error']),
timestamp: z.number(),
}),
}),
z.object({
type: z.literal('level-meter'),
payload: z.object({
channel: z.number(),
rms: z.number(),
peak: z.number(),
}),
}),
z.object({
type: z.literal('fft-snapshot'),
payload: z.object({
channel: z.number(),
data: z.array(z.number()), // Float32 序列化为数组
sampleRate: z.number(),
}),
}),
// ... 更多事件
])
export type AudioEngineEvent = z.infer<typeof AudioEngineEventSchema>
// control-command.ts
export const ControlCommandSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('start-link'),
payload: z.object({ linkId: z.string() }),
}),
z.object({
type: z.literal('update-param'),
payload: z.object({
moduleId: z.string(),
paramKey: z.string(),
value: z.union([z.number(), z.string(), z.boolean()]),
}),
}),
// ... 更多命令
])
export type ControlCommand = z.infer<typeof ControlCommandSchema>
Signal I/O WebSocket 消息(对标 LiveAmp · 详见 D3-FE-ARCH-001 §5.8)
// signal-value-event.ts · 后端 → 前端 · 实时信号值推送
import { z } from 'zod'
export const SignalValueEventSchema = z.object({
type: z.literal('signal-value-event'),
payload: z.object({
signalName: z.string(), // 如 "vehicle.speed" / "system.volume"
value: z.number(),
timestamp: z.number(), // 毫秒时间戳
quality: z.enum(['valid', 'stale', 'error']).optional(),
}),
})
export type SignalValueEvent = z.infer<typeof SignalValueEventSchema>
// signal-control-command.ts · 前端 → 后端 · 信号命令
export const SignalControlCommandSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('signal-activate'),
payload: z.object({
signalName: z.string(),
sourceConfig: z.any(), // SignalCommonParams(详细 schema 见 signal-flow/signal-node.ts)
}),
}),
z.object({
type: z.literal('signal-deactivate'),
payload: z.object({ signalName: z.string() }),
}),
z.object({
type: z.literal('signal-write'), // 手动写入信号值(测试场景 / XiTest Signal 注入)
payload: z.object({
signalName: z.string(),
value: z.number(),
}),
}),
z.object({
type: z.literal('signal-subscribe'), // 前端订阅某信号的实时推送
payload: z.object({ signalName: z.string() }),
}),
z.object({
type: z.literal('signal-unsubscribe'),
payload: z.object({ signalName: z.string() }),
}),
])
export type SignalControlCommand = z.infer<typeof SignalControlCommandSchema>
性能约定:
signal-value-event推送频率由SignalCommonParams.updateRateHz控制(默认 100Hz · 范围 10~1000Hz · DQ-08)- 前端仅对当前 Signal Flow 画布可见的信号订阅,其他信号靠后台缓存(节省带宽)
- 批量推送:后端每 10ms 把积攒的多个信号值打包成一条
signal-value-event-batch(避免 100 信号 × 100Hz = 10k msg/s)
Signal Flow 画布数据模型
// signal-flow/signal-node.ts
import { z } from 'zod'
export const SignalCommonParamsSchema = z.object({
signalName: z.string(),
sourceType: z.enum(['can', 'gpio', 'system', 'event', 'custom']),
minValue: z.number(),
maxValue: z.number(),
defaultValue: z.number(),
unit: z.string().optional(),
updateRateHz: z.number().int().min(10).max(1000).default(100),
canId: z.number().int().optional(),
canSignalOffset: z.number().int().optional(),
canSignalLength: z.number().int().optional(),
gpioPin: z.number().int().optional(),
gpioActiveHigh: z.boolean().optional(),
})
export type SignalCommonParams = z.infer<typeof SignalCommonParamsSchema>
// signal-flow/signal-graph.ts
export const SignalGraphModelSchema = z.object({
modules: z.array(z.object({ // 信号节点(signal_common / signal_output / mapping / smooth / ...)
id: z.string(),
type: z.string(),
position: z.object({ x: z.number(), y: z.number() }),
params: z.record(z.any()),
})),
connections: z.array(z.object({ // control-rate 连线
id: z.string(),
from: z.object({ nodeId: z.string(), portId: z.string() }),
to: z.object({ nodeId: z.string(), portId: z.string() }),
})),
})
export type SignalGraphModel = z.infer<typeof SignalGraphModelSchema>
// signal-flow/bindings.ts · 算法模块的外部绑定
export const ParamBindingSchema = z.object({
moduleId: z.string(), // LinkEditor 画布上的算法模块 id
paramKey: z.string(),
signalName: z.string(), // 订阅的 signal_output_v1 的 signalName
mode: z.enum(['absolute', 'offset', 'multiplier']),
fallbackToDefault: z.boolean().default(true),
})
export type ParamBinding = z.infer<typeof ParamBindingSchema>
// link.json 顶层 schema 演变(见 D3-FE-ARCH-001 §5.8.5)
export const LinkProjectSchema = z.object({
audioGraph: /* 原 LinkModel.modules + connections */,
signalGraph: SignalGraphModelSchema,
bindings: z.array(ParamBindingSchema),
})
Deep-link URL Scheme
// deep-link/schema.ts
export interface DeepLink {
app: 'xi-studio' | 'xi-forge' | 'xi-tune' | 'xi-test' | 'xi-probe' | 'xi-vst'
route: string // app 内部路由路径,如 /session/abc
params: Record<string, string> // session / project / token / intent 等
}
// 编码:xi://xi-tune/session/abc123?project=proj001&token=tmp_xyz
// 解码:parseDeepLink(url: string): DeepLink
// 构造:buildDeepLink(link: DeepLink): string
跨 app 消息总线
// bus/events.ts
export interface XiBusEvents {
'project.updated': { projectId: string; source: string }
'algo-library.refreshed': { }
'copilot.handoff': { fromApp: string; toApp: string; intent: string; context: any }
// ... 其他跨 app 事件
}
// bus/broadcast-channel.ts
export class XiBus {
on<K extends keyof XiBusEvents>(event: K, handler: (payload: XiBusEvents[K]) => void): void
emit<K extends keyof XiBusEvents>(event: K, payload: XiBusEvents[K]): void
off<K extends keyof XiBusEvents>(event: K, handler: Function): void
}
3.4 与后端契约同步
Docs-First 约束:
- 新增消息类型必须先在
@xi/protocol提 PR(前端契约变更) - 后端(C# ASP.NET Core)用
NJsonSchema或人工同步对应 DTO - CI 可选:通过
openapi-ts从 backend 的 OpenAPI 生成前端类型做交叉校验(Y1Q3 引入)
4. @xi/store-core · Pinia 基础 Store + 领域模型
4.1 定位与目标
- 基础 store:所有 apps 共用的全局状态(auth / theme / i18n / layout / notification)
- 领域模型:链路 / 模块 / 参数 / 预设的类型定义 + 工厂函数 + 通用接口,不含业务逻辑
- apps 的 store 通过
extends或compose复用 store-core 的基础能力
4.2 目录结构
packages/store-core/
├── src/
│ ├── stores/ # Pinia store 定义
│ │ ├── useAuthStore.ts
│ │ ├── useThemeStore.ts
│ │ ├── useI18nStore.ts
│ │ ├── useLayoutStore.ts
│ │ ├── useNotificationStore.ts
│ │ └── usePresetStore.ts # 接口层 · apps 提供 adapter 实现
│ ├── models/ # 领域模型(类型 + 工厂)
│ │ ├── LinkModel.ts
│ │ ├── ModuleModel.ts
│ │ ├── ParamModel.ts
│ │ ├── ProjectModel.ts
│ │ └── PresetModel.ts
│ ├── adapters/ # 扩展点接口
│ │ ├── PresetAdapter.ts
│ │ ├── ProjectAdapter.ts
│ │ └── AuthAdapter.ts
│ ├── event-bus/
│ │ └── mitt-bus.ts # 跨 store 事件广播
│ └── index.ts
├── package.json
└── tsconfig.json
4.3 核心 Store API
useAuthStore
export const useAuthStore = defineStore('xi-auth', () => {
const user = ref<User | null>(null)
const token = ref<string | null>(null)
const tenant = ref<Tenant | null>(null)
async function login(credentials: Credentials): Promise<void>
async function logout(): Promise<void>
async function refreshToken(): Promise<void>
function hasPermission(key: string): boolean
return { user, token, tenant, login, logout, refreshToken, hasPermission }
})
useThemeStore
export const useThemeStore = defineStore('xi-theme', () => {
const mode = ref<'light' | 'dark' | 'auto'>('auto')
const primaryColor = ref<string>('#D4A574')
function setMode(m: 'light' | 'dark' | 'auto'): void
function setPrimary(color: string): void
// 响应 prefers-color-scheme + 持久化到 localStorage
})
usePresetStore(接口层)
// 抽象接口,各 app 提供 adapter
export interface PresetAdapter {
list(): Promise<PresetSummary[]>
load(id: string): Promise<PresetData>
save(preset: PresetData): Promise<string>
delete(id: string): Promise<void>
}
// store-core 提供的工厂,app 注入自己的 adapter
export function createPresetStore(adapter: PresetAdapter) { ... }
4.4 领域模型
LinkModel
export interface LinkModel {
id: string
name: string
version: string
modules: ModuleModel[]
connections: Connection[]
metadata: Record<string, any>
createdAt: number
updatedAt: number
}
export function createLink(partial: Partial<LinkModel>): LinkModel
export function validateLink(link: LinkModel): ValidationResult
export function cloneLink(link: LinkModel): LinkModel
ModuleModel
export interface ModuleModel {
id: string
type: string // 模块类型代号(如 'eq-parametric')
position: Position
params: Record<string, any>
ports: {
inputs: Port[]
outputs: Port[]
}
metadata?: {
displayName?: string
iconUrl?: string
category?: string
}
}
export function createModule(type: string, position: Position): ModuleModel
export function moduleDefFromLibrary(type: string): ModuleDefinition | null
4.5 事件总线
// event-bus/mitt-bus.ts
export const storeBus = mitt<StoreBusEvents>()
export interface StoreBusEvents {
'auth.login': { user: User }
'auth.logout': {}
'theme.changed': { mode: string }
'link.changed': { linkId: string }
'module.param-updated': { moduleId: string; key: string; value: any }
}
5. @xi/dsp-utils · DSP 可视化/计算工具
5.1 定位与目标
- 纯函数:不依赖 Vue / Pinia / DOM,可在 worker 中运行
- 零 side-effect:可被任意 app 引入做 DSP 辅助计算
5.2 目录结构
packages/dsp-utils/
├── src/
│ ├── fft/
│ │ ├── window.ts # 窗函数(hann / hamming / blackman)
│ │ ├── fft.ts # FFT 包装(可选复用 fft.js)
│ │ └── spectrum.ts # 频谱计算
│ ├── eq/
│ │ ├── biquad.ts # biquad 系数计算
│ │ ├── frequency-response.ts # 频响曲线
│ │ └── eq-types.ts # 低通/高通/峰值/搁架 等类型
│ ├── meter/
│ │ ├── rms.ts
│ │ ├── peak.ts
│ │ └── lufs.ts # 响度计量
│ ├── format/
│ │ ├── pcm.ts # float32 ↔ int16
│ │ ├── resample.ts
│ │ └── mixer.ts # 简单通道混合
│ ├── color/
│ │ └── palette.ts # 声压/电平色标
│ └── index.ts
├── package.json
└── tsconfig.json
5.3 关键 API
// fft/spectrum.ts
export function computeSpectrum(
samples: Float32Array,
sampleRate: number,
options?: { window?: 'hann' | 'hamming'; fftSize?: number }
): { frequencies: Float32Array; magnitudes: Float32Array }
// eq/biquad.ts
export function computeBiquadCoeffs(type: EQType, freq: number, gain: number, q: number, sampleRate: number): BiquadCoeffs
// eq/frequency-response.ts
export function computeFrequencyResponse(
coeffs: BiquadCoeffs[],
freqRange: [number, number],
points: number
): { freqs: Float32Array; magnitudeDb: Float32Array; phaseDeg: Float32Array }
// format/pcm.ts
export function float32ToInt16(input: Float32Array): Int16Array
export function int16ToFloat32(input: Int16Array): Float32Array
5.4 性能约束
- 所有函数必须支持大数组原地操作(避免频繁 allocate)
- FFT 推荐使用
fft.js或dsp.js(单元测试验证正确性) - 可选:提供 WASM 版本(Y2+ 若性能瓶颈)
6. @xi/ai-sdk · XiMind 客户端 SDK
6.1 定位与目标
- XiMind 的前端窗口:所有 apps 调用 AI 能力的统一入口
- 会话管理:流式对话 / 上下文 / 中断 / 恢复
- 工具调用:AI 驱动 app 执行操作(function-calling)
- 多模态:文本 / 频谱图 / 音频片段 / 截图
- UI 集成:提供现成 Vue 组件(
<CopilotDrawer>/<InlineAssist>)
6.2 目录结构
packages/ai-sdk/
├── src/
│ ├── client/
│ │ ├── XiMindClient.ts # 核心 client 类
│ │ ├── transport.ts # SSE / WebSocket 传输层
│ │ └── auth.ts
│ ├── session/
│ │ ├── Session.ts # 单个对话会话
│ │ ├── context.ts # 上下文管理
│ │ └── streaming.ts # 流式响应处理
│ ├── tools/
│ │ ├── ToolRegistry.ts # function-calling 工具注册表
│ │ ├── builtin-tools.ts # 内置工具(open-app / update-link 等)
│ │ └── types.ts
│ ├── rag/
│ │ ├── RAGClient.ts # 检索 XiAlgo / 项目知识
│ │ └── types.ts
│ ├── multimodal/
│ │ ├── spectrum-input.ts
│ │ ├── audio-input.ts
│ │ └── image-input.ts
│ ├── composables/ # Vue Composables(供 apps 使用)
│ │ ├── useCopilot.ts
│ │ ├── useCopilotSession.ts
│ │ └── useAIGenerate.ts
│ ├── components/ # Vue 组件(供 @xi/ui-kit 引用)
│ │ ├── CopilotDrawer.vue
│ │ ├── InlineAssist.vue
│ │ └── VoiceInput.vue
│ └── index.ts
├── package.json
└── tsconfig.json
6.3 核心 API
// client/XiMindClient.ts
export interface XiMindClientOptions {
endpoint: string // XiMind 云端地址
tenantId: string
getToken: () => Promise<string>
defaultModel?: string
}
export class XiMindClient {
constructor(options: XiMindClientOptions)
createSession(opts?: SessionOptions): Session
listModels(): Promise<Model[]>
getUsage(): Promise<UsageStats>
}
// session/Session.ts
export class Session {
async send(message: UserMessage): Promise<AssistantMessage>
async sendStreaming(message: UserMessage): AsyncIterable<AssistantDelta>
registerTool(tool: Tool): void
abort(): void
getHistory(): Message[]
}
// tools/types.ts
export interface Tool<TParams = any, TResult = any> {
name: string
description: string
parameters: z.ZodSchema<TParams>
execute: (params: TParams) => Promise<TResult>
}
// composables/useCopilot.ts
export function useCopilot(options?: UseCopilotOptions): {
session: Ref<Session | null>
messages: Ref<Message[]>
sending: Ref<boolean>
send: (text: string) => Promise<void>
abort: () => void
}
6.4 function-calling 标准工具集
| 工具名 | 用途 | 调用方 |
|---|---|---|
open-app |
在指定 app 打开(deep-link 发起) | 所有 apps |
update-link |
修改当前链路(增删模块/改连接) | XiStudio / XiForge |
update-param |
修改模块参数 | XiStudio / XiTune / XiForge |
run-measurement |
触发 XiTune 测量 | XiTune |
run-test-suite |
运行 XiTest 用例 | XiTest |
search-algo-library |
检索 XiAlgo 算法库 | 所有 apps |
generate-code |
生成算法代码(XiForge 专用) | XiForge |
6.5 后端契约与版本
- v0.1(Y1Q3):基础会话 + SSE 流式 + 5 个内置工具
- v0.5(Y2H2):RAG 检索 + 多模态输入 + 完整工具集
- v1.0(Y3H1):XiMind 商用发布同步 · 契约冻结
7. 版本管理与发布
7.1 changesets 流程
- 开发者修改 package 时执行
pnpm changeset - 选择受影响 packages + 语义版本(major/minor/patch)
- 填写变更描述
- CI 合并后自动生成
CHANGELOG.md+ 打 tag
7.2 版本发布策略
| 阶段 | 策略 |
|---|---|
| Y1(Monorepo 内部) | workspace:* 同仓库滚动,不发 npm |
| Y2+ | 发布到 Xisound 私有 npm registry,外部合作伙伴可引用 |
| 重大架构变更 | 跨 major 版本,提供迁移指南 |
7.3 SemVer 约束
| 变更类型 | 版本位 |
|---|---|
| 新增 API(向后兼容) | minor |
| 修复 bug(不改 API) | patch |
| 破坏性 API 变更 | major |
| package 之间破坏性变更需同步 major 升级整组 | 约定 |
8. 测试策略
8.1 单元测试
- 每个 package 必须有 Vitest 单测覆盖
- 覆盖率要求:
@xi/dsp-utils/@xi/protocol≥ 90%;其他 ≥ 70%
8.2 组件测试
@xi/ui-kit用@vue/test-utils+happy-dom测组件行为- 关键组件(LinkCanvas / EQCurveEditor / SpectrumChart)必须有交互测试
8.3 视觉回归测试(可选)
- 用 Storybook + Chromatic 或 playwright screenshot 对 ui-kit 做视觉回归
- 引入时机:Y1Q3 ui-kit v0.5 后
8.4 契约测试
@xi/protocol的 Schema 必须有 fixture 数据做往返 parse/stringify 测试- 后端同步后做跨 repo 的契约冒烟测试(Y1Q4 CI 引入)
9. 待决策清单(DQ)
| DQ 编号 | 问题 | 建议 |
|---|---|---|
| DQ-UI-01 | ui-kit 是否提供 Ant Design Vue / Element Plus 的适配层(若已有产品用) | 建议不提供;全部自研,避免多 UI 库风格冲突 |
| DQ-UI-02 | LinkCanvas 底层是用 Canvas / SVG / Vue 组件 | 建议 SVG + Vue 组件(可交互、易 debug、性能足够 ≤1000 节点) |
| DQ-PROTO-01 | WebSocket 单通道 vs 多通道(按事件类型分端口) | 建议单通道 + 消息 type 字段区分 |
| DQ-STORE-01 | 是否使用 pinia-plugin-persistedstate 持久化 |
建议:useAuthStore / useThemeStore 持久化;其他不持久化 |
| DQ-AI-01 | ai-sdk 传输层选 SSE 还是 WebSocket | 建议 SSE(对话场景够用,调试简单);WebSocket 留给未来语音流式 |
10. 版本与变更记录
| 版本 | 日期 | 作者 | 说明 |
|---|---|---|---|
| v1.0 | 2026-05-08 | work-cline | 初稿;5 个 packages 的完整架构契约 |
| v1.1 | 2026-05-09 | work-cline | 新增 §2.3 三大模块组件 API(ModuleNode / ModulePalette / ModuleTuningDialog · B 方案仅 Props 接口 + 链 007);新增 §2.6 通用 atoms 强制契约(XiFileDropzone / XiProgressBar / XiAlert / XiInfoTable · 对接 D3-FE-ARCH-010);修复 2 处 H4 标题尾部 emoji 违规(A5 / md-writing-spec §4) |
附录 A · 引用
- D3-FE-ARCH-001 顶层架构说明书(§4 共享基础设施层 · 本文为其详细展开)
- D3-FE-ARCH-002 文档规划 Roadmap
- D3-FE-ARCH-004 实现 plan 总纲(含 PLAN-002/003 覆盖本文 packages 的落地计划)