P1.A21.F1-dock-host-generalize · XiLink 右 Dock Host 通用化(节点切换 + 通道自适应 + 刻度缩放)
Worker:ClaudeA · 部门:前端 P1-xilink · 预计:2.0d · 优先级:P0 关键路径起点 · 状态:dispatched · isolation:🧵 file(同 worktree 同 branch · 与 F3 dsp_algo / F5 dsp_algo 文件正交并行)
🔍 触发与解锁链
触发:用户 2026-06-13 09:55 提 5 点新需求 #1(右 dock meterdrawer 通用化)· 12:51 拍板 accept ADR-21 · 14:15 拍板 start F1+F3+F5 三连。
用户原话(verbatim · ADR-21 §1.1):
"1. 右侧 dock 接入的 meterdrawer · 在可以显示信号的基础之上需要能够识别当前链路中的节点,并且进行切换,比如所有的 source · sink · 还有就是 log_module_v1;选择链路固定的节点后,可以正确显示该节点的通道数量,做到可以切换;关于界面设计横纵坐标的需要有可以放大缩小的刻度操作;"
架构契约(ADR-21 §3.1 ① 输入/输出契约 · 业务契约 5 必填段全填): - DockHost 接收 ChainNodeRef metadata(nodeId / nodeKind / channelCount / sampleRate / position) - 节点切换 selector 复用 ADR-17 F6 widget endpoint 4 类(physical-input / sink-pre / log-module / xitune-module)+ 加 xilink-module 第 5 类(本 ADR 新增) - channelMask boolean[] 长度 === selectedNode.channelCount(动态) - scaleX/scaleY 单位由 module 决定(fft = Hz/dBFS · scope = ms/V · phase = Hz/° · transfer = Hz/dBFS)
解锁链(本任务 zombie 后): - F2 fft/scope 控件增强 ready(blocked-by-F1) - F4 phase-module 前端 ready(blocked-by-F1+F3) - F6 transfer-module 前端 ready(blocked-by-F1+F5)
任务定义(基于 ADR-21 §3.1)
子任务 ① · DockHost.vue 通用化(0.6d)
Step 1.1:NEW(或重构现有)frontend_vue3/src/stages/xilink/dock/DockHost.vue:
- props:{ moduleId: string; supportedNodeKinds: NodeKind[]; defaultNodeId?: string }
- 顶部插槽:DockNodeSelector(节点切换下拉)+ DockChannelToggle(通道开关)+ DockScaleControl(刻度滚轮 hint)
- 中部插槽:<slot name="canvas" :input="dockModuleInput" />(module 自渲染 canvas)
- 底部插槽:<slot name="controls" />(module 自定义控件如 fft displayMode)
- 严守 ADR-21 §3.1 ② 性能基线:节点切换 < 150ms · FPS ≥ 30(scope/fft)/ ≥ 60(meter)· 多 module 并存 ≥ 3 不降帧
Step 1.2:DockHost 内部 reactive state:
- selectedNodeId: Ref<string> · 默认 defaultNodeId || 'sink-pre'
- channelMask: Ref<boolean[]>(长度 = selectedNode.channelCount · 默认全 true)
- scaleX/scaleY: Reactive<{min, max}>(单位由 module 决定 · 通过 props.unit 注入)
- 5 类失败回退(ADR-21 §3.1 ③):节点不存在 fallback 到 sink-pre / channelMask 全关显示提示 / 刻度越界 swap / 渲染异常红 X / channelCount 突变重置 mask
子任务 ② · DockNodeSelector.vue 节点切换(0.4d)
Step 2.1:NEW frontend_vue3/src/stages/xilink/dock/DockNodeSelector.vue:
- 复用 ADR-17 F6 widget endpoint selector(78dc17c · 4 类)+ 加 xilink-module 第 5 类
- props:{ supportedNodeKinds: NodeKind[]; modelValue: string }(v-model)
- emit:update:modelValue
- 内部:el-select 下拉 · option 按 nodeKind 分组(source / sink / log_module_v1 / xitune-module / xilink-module)
- 数据源:从 chainStore 查所有 ChainNodeRef · 过滤 supportedNodeKinds
- ADR-21 §3.1 ④ 用户操作流 Step 2-3:default 'sink-pre' · 切到 log_module_v1 自动显示 channelCount
Step 2.2:useChainNodeMetadata.ts composable(NEW):
- 接口:useChainNodeMetadata(nodeId: Ref<string>) → 返回 reactive ChainNodeRef
- 从 chainStore.nodes 查 + watch nodeId 变化重查
- 暴露 channelCount / sampleRate / position 字段供 DockHost 用
子任务 ③ · useDockScale.ts 刻度缩放(0.4d)
Step 3.1:NEW frontend_vue3/src/stages/xilink/dock/useDockScale.ts:
- 接口:useDockScale(canvasRef, { initialScaleX, initialScaleY, axisUnit: 'Hz'|'ms'|'sample' })
- 滚轮事件:1 刻度 = 10% zoom(中心点保持)· clamp 上下限
- ResizeObserver 监听 canvas 容器 · debounce 100ms 触发 redraw 信号
- 返回 reactive { scaleX, scaleY, onWheel, onResize, redrawKey }
Step 3.2:scaleY swap fallback(ADR-21 §3.1 ③):若 scaleY.min > scaleY.max 自动 swap(无感恢复)
子任务 ④ · useDockChannelMask.ts 通道开关(0.3d)
Step 4.1:NEW frontend_vue3/src/stages/xilink/dock/useDockChannelMask.ts:
- 接口:useDockChannelMask(channelCount: Ref<number>) → reactive { mask: boolean[], toggle(idx), allOn(), allOff() }
- watch channelCount 变化:若新长度 ≠ 旧长度 → 重置全 true(ADR-21 §3.1 ③ "channelCount 突变重置 channelMask")
- channelMask all false 时 emit 'no-channel-selected' 信号给 DockHost(显示 "No channel selected" 提示)
子任务 ⑤ · 现有 fft/scope/rms module 接入回归(0.2d)
Step 5.1:read 现有 ADR-18 F5 落地的 4 popup(RMSMeterModulePopup / ScopeModulePopup / FftModulePopup / LogModulePollingPanel · 769405a)+ ADR-18 F7 DSP 算法层(c099772 · 输出 ChainNodeRef + frame)· 改造接入 DockHost 插槽(<slot name="canvas"> + <slot name="controls">)· 不破坏现有渲染逻辑
Step 5.2:vitest 回归 ADR-18 F5 + F7 落地基线(152 case)零回归
子任务 ⑥ · 测试(0.1d)
Step 6.1:vitest 加 ≥ 6 case(ADR-21 §3.1 ⑤ playwright e2e 模板对齐): - DockHost props.supportedNodeKinds 渲染 selector 正确 5 类 - DockNodeSelector 切换 source → log_module_v1 · channelCount 从 2 切到 4 · channelMask 重置全 true - useDockScale 滚轮 zoom 中心点保持 + clamp 上下限 - useDockChannelMask 通道开关 toggle / allOn / allOff - channelCount 突变 → channelMask 重置(ADR-21 §3.1 ③) - 节点不存在 fallback 到 sink-pre + 提示文案
Step 6.2:F8 ADR-15(ClaudeA 排队中 · 0.5d 估近 zombie)+ ADR-18 F5 + ADR-17 F6 基线零回归
完整 prompt(直接复制粘贴 ClaudeA 终端)
[U-thread] P1.A21.F1-dock-host-generalize · ADR-21 §3.1 DockHost 通用化(关键路径起点)
[部门] 前端 P1-xilink
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P1.K-shared-xilink-dock + P0.K-shared-types
[优先级] P0(2.0d · 关键路径起点 · 解锁 F2/F4/F6 全部前端 fork)
[ADR] docs/08-implementation/40-aios/ADR/ADR-AIOS-21-xilink-dock-and-analysis-modules.md(必读 §3.1 + §1.4 边界铁律)
[isolation] file(同 worktree 同 branch · 与 F3 dsp_algo / F5 dsp_algo 文件正交并行)
[参考文档绝对路径]
- 业务契约:ADR-21 §3.1 完整 5 必填段(① ChainNodeRef + DockModuleInput 接口 / ② 性能阈值 / ③ 5 类失败回退 / ④ 5 步操作流 / ⑤ playwright e2e 模板)
- 用户 2026-06-13 09:55 拍板原话(ADR-21 §1.1):节点切换 source/sink/log_module_v1 + 通道数自适应 + 横纵刻度缩放
- 范式 commits(worker 必读):
* 78dc17c P0.A17.F6 widget endpoint selector 4 类(本任务复用 + 加 xilink-module 第 5 类)
* 769405a P0.A18.F5 IP 库 + 4 popup(本任务 host 接入这些 popup)
* c099772 P_dsp.A18.F7 DSP 算法层 dspalgo_dll(本任务 host 渲染其输出 frame)
- 现状参考:
* frontend_vue3/src/stages/xilink/dock/(现有 dock 基础 host · 本任务通用化升级)
* frontend_vue3/src/components/popups/{RMSMeter,Scope,Fft}ModulePopup.vue(F5 现有 popup · 本任务接入插槽)
* frontend_vue3/src/stores/chainStore.ts(节点 metadata 真值源 · ChainNodeRef 字段查询)
* frontend_vue3/src/composables/useWidgetEndpointSelector.ts(F6 78dc17c · 复用 selector 模型)
- 三层分工:ADR-07 §1.3.4(L3 前端零数学 · 本任务纯 UI 渲染 + 滚轮事件 · 不做 DSP 计算)
[文件正交策略](.clinerules §任务隔离类型分配准则 v1.4):
isolation: file · 同 worktree 同 branch · 与 F3 / F5 并行 push 拉合并
本任务文件:frontend_vue3/src/stages/xilink/dock/{DockHost.vue + DockNodeSelector.vue + useDockScale.ts + useDockChannelMask.ts} + composables/useChainNodeMetadata.ts
F3 文件:dsp_algo/modules/phase_module/* + dspalgo_dll.c 加 phase export(完全正交)
F5 文件:dsp_algo/modules/transfer_module/* + dspalgo_dll.c 加 transfer export(完全正交)
完全正交 · 不冲突
【背景】
用户 2026-06-13 09:55 提 5 点新需求中 #1:右 dock meterdrawer 通用化 + 节点切换 + 通道数自适应 + 横纵刻度缩放。
ADR-21 accepted 2026-06-13 12:51 · 7 fork 13d · 本任务 F1 是关键路径起点(解锁 F2/F4/F6 全部前端 fork)。
ADR-18 已落地 rms/scope/fft 三个 module + log_module(F5 769405a)+ DSP 算法层(F7 c099772)· 但 dock host 仅基础接入 · 没做"节点 selector + 通道适配 + 刻度缩放"通用化。
本任务升级 host · **不破坏现有 module 渲染逻辑** · 通过插槽机制接入 fft/scope/rms/log/phase/transfer 6 类 module。
【架构关键约束】
⚡ 节点切换 5 类:source / sink / log_module_v1 / xitune-module / xilink-module(第 5 类是本 ADR 新增 · 复用 ADR-17 F6 4 类 + 扩 1)
🎨 通道数自适应:从 ChainNodeRef.channelCount 动态读 · 切节点时 channelMask 重置全 true · UI 通道开关数量动态
📋 横纵刻度缩放:滚轮 1 刻度 = 10% zoom(中心点保持)+ ResizeObserver 触发 redraw + 单位由 module 决定(unit prop 注入)
📋 三层分工:L3 前端零数学(本任务纯 UI 渲染 + 滚轮事件 · 不做 FFT/RMS/STFT 计算)
📋 F1 范围与下游 fork 边界:本 F1 仅做 host 通用化 + 现有 module 接入回归 · **不实装 phase/transfer module 本体**(那是 F4/F6 范围 · 等 F3/F5 算法 zombie)
⚡ ADR-21 §1.4 铁律:phase/transfer 计算在 dsp_algo(L1 算法库)· 本任务前端仅消费 frame schema 渲染
【执行步骤】
Step 0 · 文件注入真值核查(强制门槛 · F2 教训承接)
- grep -r "DockHost" frontend_vue3/src/stages/xilink/dock/ · 确认现有 host 真签名
- read frontend_vue3/src/composables/useWidgetEndpointSelector.ts(F6 78dc17c · 复用基础)
- read frontend_vue3/src/components/popups/RMSMeterModulePopup.vue + ScopeModulePopup.vue + FftModulePopup.vue(F5 769405a · 接入目标)
- grep "channelCount" frontend_vue3/src/stores/chainStore.ts · 确认节点 metadata 字段
- 留 commit log:Step 0 三层核查记录
Step 1 · DockHost.vue 通用化 0.6d(子任务 ①)
- 重构现有 host(若存在)· 否则 NEW · 加 supportedNodeKinds props + 3 插槽(canvas / controls / selector)
- 5 类失败回退(ADR-21 §3.1 ③ 全覆盖)
- 性能基线:节点切换 < 150ms · FPS ≥ 30 / ≥ 60
Step 2 · DockNodeSelector.vue + useChainNodeMetadata.ts 0.4d(子任务 ②)
- selector 5 类 + chainStore 节点查询 + 切换响应
- useChainNodeMetadata watch nodeId 变化重查
Step 3 · useDockScale.ts 刻度缩放 0.4d(子任务 ③)
- 滚轮 zoom 中心点保持 + clamp + ResizeObserver
- axisUnit 注入(Hz/ms/sample · module 自定义)
Step 4 · useDockChannelMask.ts 通道开关 0.3d(子任务 ④)
- watch channelCount 变化重置 mask
- allOn / allOff / toggle API
Step 5 · 现有 module 接入回归 0.2d(子任务 ⑤)
- 改造 RMSMeterModulePopup / ScopeModulePopup / FftModulePopup 接入 DockHost 插槽
- **不破坏现有渲染逻辑** · 仅 wrap 一层 host
- vitest 回归 ADR-18 F5 + F7 + ADR-17 F6 基线 152 case 零回归
Step 6 · 测试 + 类型/构建/测试全绿 0.1d(子任务 ⑥)
- vitest 加 ≥ 6 case
- vue-tsc --noEmit 0 errors
- npm run build 0 errors
- npm run test 全过(基线 + 6 新增)
Step 7 · 浏览器实测 + commit
- 启动 backend(dotnet run)+ frontend(npm run dev)
- 验收点(ADR-21 §3.1 ④ 用户操作流):
☐ 进入 xilink stage · 右 dock 点 "+ fft-module"
☐ Module 顶部 selector 默认 sink-pre · 显示 2 通道开关
☐ 切换到 log_module_v1 · 自动显示该 module channelCount(若 4)+ 4 通道开关
☐ 滚轮缩放横轴 0-22kHz → 100Hz-10kHz · canvas 即时重绘
☐ 拖拽 dock 高度 · ResizeObserver 触发重绘 · 30fps 保持
☐ 切换到不存在节点 · fallback 到 sink-pre + 提示文案
- git add . && git commit -m "feat(xilink/dock): P1.A21.F1 DockHost 通用化(节点切换 5 类 + 通道自适应 + 刻度缩放)
用户 2026-06-13 09:55 提 5 点新需求 #1 · 12:51 accept ADR-21 · 14:15 拍板 start F1+F3+F5 三连。
F1 本任务(ADR-21 §3.1):
① DockHost.vue 通用化(supportedNodeKinds props + 3 插槽 + 5 类失败回退)
② DockNodeSelector.vue 节点切换 5 类(复用 ADR-17 F6 + 加 xilink-module)+ useChainNodeMetadata composable
③ useDockScale.ts 滚轮缩放 + ResizeObserver(中心点保持 + clamp + axisUnit 注入)
④ useDockChannelMask.ts 通道开关(watch channelCount 重置 + toggle/allOn/allOff)
⑤ 现有 RMS/Scope/Fft popup 接入 DockHost 插槽(零回归 152 case)
⑥ vitest +6 case · vue-tsc + build 全绿
解锁 F2 控件增强 + F4 phase-module 前端 + F6 transfer-module 前端
[step=7/7] [pid=P1] [uid=P1.A21.F1-dock-host-generalize] [type=fork] [isolation=file]
[occupies=P1.K-shared-xilink-dock+P0.K-shared-types] [files=5] [ipc=rest+ws-ready]
[adr=ADR-AIOS-21 §3.1 DockHost 通用化(#1 节点切换+通道自适应+刻度缩放)]"
【验收】
☐ Step 0 文件注入真值核查通过(grep + read 4 个标本 + chainStore channelCount 字段确认)
☐ Step 1 DockHost.vue 通用化 · 5 类失败回退全覆盖 · 性能基线达标(< 150ms / ≥ 30fps)
☐ Step 2 DockNodeSelector 5 类切换 · useChainNodeMetadata watch 重查正确
☐ Step 3 useDockScale 滚轮 zoom 中心点保持 + clamp + ResizeObserver redraw
☐ Step 4 useDockChannelMask channelCount 突变重置 + 全关提示信号
☐ Step 5 现有 RMS/Scope/Fft popup 接入插槽 · ADR-18 F5+F7 + ADR-17 F6 基线 152 case 零回归
☐ Step 6 vitest +6 case 全过 · vue-tsc + build 0 errors
☐ Step 7 浏览器实测 6 验收点全过(ADR-21 §3.1 ④ 操作流端到端)
☐ commit message 含 7 元组 trailer + ADR §3.1 引用
【禁止】
❌ 禁止跳过 Step 0 文件注入核查(F2 教训:派发前必须 grep + read 4 标本 + chainStore 字段确认)
❌ 禁止在前端做 DSP 数学(三层分工 · phase/transfer 计算在 F3/F5 dsp_algo · 本任务仅 UI 渲染)
❌ 禁止把 DockHost 设计成"破坏式重构"(必须保留现有 RMS/Scope/Fft popup 渲染逻辑 · 仅 wrap 一层插槽)
❌ 禁止超前实施 phase-module / transfer-module 本体(那是 F4/F6 范围 · 本 F1 仅做 host)
❌ 禁止破坏 ADR-18 F5 + F7 + ADR-17 F6 已锁 152 case 基线(零回归是硬门槛)
❌ 禁止跳过 vitest +6 case(验收硬门槛)
❌ 禁止 commit 缺三元组 trailer(.clinerules v1.6 铁律)
❌ 禁止嵌入完整 SFC > 60 行 / TS interface > 5 行(.clinerules v1.6)· 拆 child component 或 v-if 渲染分支
解锁链(本任务 zombie 后)
- ✅ DockHost 通用化完成 · 5 类节点切换 + 通道自适应 + 刻度缩放 UX 上线
- ✅ ADR-18 现有 4 popup 接入新 host(零回归)
- ✅ F2 fft/scope 控件增强 ready(ClaudeA 1.5d · 接 displayMode/peakHold/peakTrack)
- ✅ F4 phase-module 前端 ready(blocked-by-F1+F3 · 等 F3 zombie 后 ClaudeA 1.5d)
- ✅ F6 transfer-module 前端 ready(blocked-by-F1+F5 · 等 F5 zombie 后 ClaudeA 2.0d)
风险评估
| 风险 | 缓解 |
|---|---|
| 现有 dock host 重构破坏 ADR-18 F5 已落地 popup 渲染 | Step 0 强制 grep + read 4 popup 真签名 + Step 5 vitest 回归 152 case 零回归 + 插槽机制保持渲染逻辑不变 |
| ChainNodeRef metadata 字段在 chainStore 不齐(channelCount 缺失) | Step 0 grep "channelCount" 确认 · 若缺则 fork 内补丁(append-only · 不破坏现有字段)+ ADR-21 §3.1 ① 锁定 schema |
| 滚轮 zoom 中心点保持算法易错 | Step 3 用 canvas pointer event clientX/Y → 数据坐标变换 · 仿现有 SpectrumChart 做法 · 单测 zoom 1.5x 后 hover 点不变 |
| ResizeObserver debounce 100ms 与 30fps 冲突 | RAF 节流 + ResizeObserver debounce 串联 · 测试 5 instance 同 stage 不掉帧 |
| 5 类节点切换 selector 与 ADR-17 F6 4 类冲突 | 复用 useWidgetEndpointSelector composable · 加第 5 类 xilink-module 用 prop 注入(向后兼容) |
| ClaudeA 排队膨胀(F8 0.5d ADR-15 + 本 F1 2.0d + 后续 F2/F4/F6 共 5.5d 串行) | F8 估近 zombie · 本 F1 是关键路径起点不可让 · 后续 F2 与 F3 dsp_algo 文件正交可并行 |
| F3/F5 dsp_algo 输出 frame schema 与本任务 host 数据流契约不一致 | ADR-21 §3.3/§3.4 ① 已锁定 frame schema(MeterFrame_Phase / MeterFrame_Transfer · 复用 ADR-12 §3.4/§3.3)· 跨栈一致 |
历史
| 时间 | 事件 | hash |
|---|---|---|
| 2026-06-13 14:16 | dispatched(用户 14:15 拍板 start F1+F3+F5 三连 · ClaudeA 关键路径起点 2.0d) | — |