跳转至
  • 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> · 元信息表

interface XiInfoTableProps {
  // 无 Props · 内容通过默认 slot 提供 <tr><td>...</td></tr>
}

用法

<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/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 通过 extendscompose 复用 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.jsdsp.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 流程

  1. 开发者修改 package 时执行 pnpm changeset
  2. 选择受影响 packages + 语义版本(major/minor/patch)
  3. 填写变更描述
  4. 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 · 引用