Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ docsite/
.superpowers
docs/superpowers
.claude
.idea/
/make/
63 changes: 63 additions & 0 deletions .harness/decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Decisions

## 2026-04-16

### 决策 1:飞书入口采用“专用 view + 复用 WebViewModel”

- 原因:与现有 `web/help/tsunami` 模式一致
- 收益:可以独立承载飞书默认 URL、分区、按钮和启动逻辑
- 代价:比纯复用 `web` 多一个轻量 view 文件,但维护性更好

### 决策 2:本地飞书 App 启动放在主进程

- 原因:协议调用、注册表探测、路径探测都属于平台能力
- 收益:前端只保留一个简单 API,不需要承载 Windows 细节
- 回退链路:协议 -> `feishu:apppath` -> 注册表 -> 常见路径 -> 应用内网页

### 决策 3:飞书新窗口不再走普通 openLink,而是继承 `persist:feishu`

- 原因:登录/授权/聊天子页面需要共享 cookie 与 storage
- 实现:在 `FeishuViewModel.handleNewWindow()` 中创建带 `web:partition` 的新 web block

### 决策 4:入口改为 `Feishu App` + `Feishu Web` 双入口

- 原因:本地 App 与应用内网页的能力边界不同,强行合并会让用户误以为本地窗口是内嵌网页
- 收益:入口语义更清晰,既能一键启动本地飞书,也能明确打开应用内网页聊天页
- 代价:侧边栏多一个轻量入口,但整体可维护性更好

### 决策 5:`Feishu Web` 最终只保留图标隐藏入口

- 原因:用户确认“小眼睛”图标已经满足关闭需求,不再需要额外的文字隐藏按钮
- 收益:界面更干净,同时保留现有 block header 的统一交互
- 范围:移除额外文字按钮,不影响 `Feishu Web` 的网页容器能力

## 2026-04-21

# ADR-20260421-001: 终端问题改为“两阶段闭环”推进

## Context
- 当前终端已停用历史恢复链路,并已有单 terminal smoke
- 用户最新截图显示:真实多 terminal split-pane 场景下,输入框错位和滚轮回归仍会发生
- 现有 smoke 通过 `window.term` 与内部 `.xterm-scrollable-element` 直派发事件,不能代表真实用户路径

## Options
- option A:继续在现有 `termwrap.ts` 上直接 patch
- option B:先补多 terminal / 真实焦点 / 真实 wheel 路径 smoke,再改业务逻辑
- option C:先清理后端历史缓存死代码

## Decision
- chosen option:B
- why it was chosen:当前最大不确定性不是“补丁怎么写”,而是“真实失败路径是否已被自动化覆盖”;先补复现场景,再把业务逻辑收口到 xterm 官方扩展点,风险最低

## Consequences
- positive effects
- 避免再次出现“单测和单 terminal smoke 通过,但用户真实场景仍失败”
- 后续 wheel/IME 重构有更稳定的回归闭环
- negative effects
- 比直接 patch 多一个前置任务包,短期交付稍慢
- follow-up work
- `TASK-TERM-003`
- `TASK-TERM-004`

## Review Date
- 2026-04-22
111 changes: 111 additions & 0 deletions .harness/feature-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
[
{
"id": "TASK-001",
"title": "飞书入口增强与 Harness 初始化",
"status": "passing",
"priority": "P1",
"scope": [
"emain/emain-feishu.ts",
"emain/emain-ipc.ts",
"emain/preload.ts",
"frontend/app/view/feishuview/feishuview.tsx",
"frontend/app/view/webview/webview.tsx",
"frontend/app/view/webview/webviewenv.ts",
"frontend/app/block/blockregistry.ts",
"frontend/app/block/blockutil.tsx",
"pkg/wconfig/defaultconfig/widgets.json",
"pkg/wconfig/defaultconfig/settings.json",
"pkg/wconfig/settingsconfig.go",
"frontend/types/custom.d.ts",
"frontend/types/gotypes.d.ts",
"schema/settings.json",
".harness/*",
"scripts/verify.ps1",
"AGENTS.md",
"CLAUDE.md"
],
"acceptance": [
"飞书入口支持本地 App 自动发现、配置路径覆盖与网页兜底",
"飞书视图弹出的新窗口继承 persist:feishu 分区",
"用户可见地提供 `Feishu App` / `Feishu Web` 双入口,且 `Feishu Web` 可直接隐藏当前卡片",
"最小验证命令 scripts/verify.ps1 通过",
"仓库具备最小可续跑的 Harness 工件"
],
"notes": "代码实现与 verify 已完成;剩余阻塞是运行态 smoke 依赖账号态,且本地启动环境还缺少可用的 WCLOUD_ENDPOINT。"
},
{
"id": "TASK-TERM-001",
"title": "终端滚轮与输入法位置专项修复",
"status": "passing",
"priority": "P1",
"scope": [
"frontend/app/view/term/termwrap.ts",
"frontend/app/view/term/termutil.ts",
"frontend/app/view/term/fitaddon.ts",
"frontend/app/view/term/osc-handlers.ts",
".harness/*"
],
"acceptance": [
"普通终端历史可以用鼠标滚轮上下滚动",
"Codex/Agent 会话中 normal buffer 与 alternate buffer 的滚轮行为符合预期",
"中文输入法候选框/组合文本不再出现在左上角或历史 viewport 位置",
"调整窗口大小或从历史恢复后,当前输入位置和可视 viewport 不错位",
"vitest、scripts/verify.ps1 与 electron-builder 验证完成或明确记录阻塞"
],
"notes": "已回到 upstream/main termwrap 官方主线,只保留 termsize 强制同步、Codex/Agent IME 锚点和 normal buffer 滚轮兜底。最新修正后,wheel 兜底改为 bubble 阶段,避免抢占 xterm 内部 xterm-scrollable-element;IME 改为跟随当前 cursor 行列,而不是固定中线。按用户最新要求,前端已彻底停用 terminal 历史缓存/恢复链路:不再读取 cache:term:full、不再调用 SaveTerminalState,只保留当前会话 term blockfile 的实时 append;为避免初始化阶段丢数据,新增 heldData 顺序回放。"
},
{
"id": "TASK-TERM-002",
"title": "终端回归 Smoke 自动化闭环",
"status": "passing",
"priority": "P1",
"scope": [
"scripts/smoke-terminal.ps1",
".harness/*"
],
"acceptance": [
"脚本可启动最新 make\\win-unpacked\\Wave.exe 并连接 Electron CDP",
"脚本可静态确认 termwrap.ts 不再包含历史缓存/恢复入口",
"脚本可运行态确认 window.term 可达、历史方法为空、serializeAddon 不存在",
"脚本可验证 wheel 改变 viewportY,IME textarea 与 cursor 对齐",
"脚本输出 JSON 和截图,失败时给出明确原因"
],
"notes": "已新增 scripts/smoke-terminal.ps1。首次 smoke 抓到旧 win-unpacked bundle 仍暴露历史方法;重跑 scripts/verify.ps1 与 electron-builder --win dir 后通过。最新结果 JSON 为 D:\\files\\AI_output\\waveterm-terminal-smoke\\terminal-smoke-20260421-162451.json,截图为 D:\\files\\AI_output\\waveterm-terminal-smoke\\terminal-smoke-20260421-162451.png;wheel viewportY 127->87,IME topDelta/leftDelta 均为 0。"
},
{
"id": "TASK-TERM-003",
"title": "多终端焦点与真实事件路径 Smoke 补强",
"status": "in_progress",
"priority": "P1",
"scope": [
"scripts/smoke-terminal.ps1",
".harness/*"
],
"acceptance": [
"脚本可识别多 terminal block,而不是只验证 window.term",
"脚本可断言 active/focused terminal 与 IME helper 所属 terminal 一致",
"脚本可区分真实外层 wheel 路径与内部 scrollableElement 路径结果",
"split-pane 场景失败时可明确标出焦点归属问题或 wheel 路由问题"
],
"notes": "来自批准的方向 A。该任务只扩展 smoke 覆盖范围,不改业务逻辑,目标是稳定复现用户最新截图中的多终端输入框错位与滚轮回归。"
},
{
"id": "TASK-TERM-004",
"title": "将 Wheel / IME 修复收口到 xterm 官方扩展点与焦点归属",
"status": "in_progress",
"priority": "P1",
"scope": [
"frontend/app/view/term/termwrap.ts",
"frontend/app/view/term/termutil.ts",
"frontend/app/view/term/termutil.test.ts",
".harness/*"
],
"acceptance": [
"使用 xterm 官方 wheel hook 处理 normal buffer 滚轮兜底",
"只有 active terminal 可重定位 IME helper",
"多 terminal split-pane 场景中输入框不串位,滚轮不串 terminal",
"vitest、verify、smoke、electron-builder 验证通过"
],
"notes": "这是批准方向 A 的第二个闭环任务;需在 TASK-TERM-003 给出更真实复现证据后再改业务逻辑。"
}
]
167 changes: 167 additions & 0 deletions .harness/opportunities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
{
"opportunities": [
{
"id": "OPP-TERM-002",
"title": "把滚轮与 IME 兜底收敛到 xterm 官方扩展点",
"problem": "当前终端滚轮与 IME 修复仍在 `termwrap.ts` 里通过外层 DOM wheel listener、正则识别 Agent TUI、直接改 textarea/composition-view style 实现,容易与 xterm 内部 viewport、mouse tracking、composition helper 生命周期冲突。",
"evidence": [
{
"source": "xterm.js Terminal API",
"url": "https://xtermjs.org/docs/api/terminal/classes/terminal/#attachcustomwheeleventhandler",
"strength": "strong",
"note": "xterm.js 已提供 `attachCustomWheelEventHandler`,用于让 embedder 决定是否继续处理 terminal wheel event。"
},
{
"source": "xterm.js 6.0.0 release",
"url": "https://github.com/xtermjs/xterm.js/releases/tag/6.0.0",
"strength": "strong",
"note": "xterm.js 6.0.0 集成 VS Code scrollbar,明确说明 viewport/scrollbar 行为有重大变化。"
},
{
"source": "xterm.js issue #5734",
"url": "https://github.com/xtermjs/xterm.js/issues/5734",
"strength": "strong",
"note": "xterm.js 6.0.0 + Electron + Claude/AI CLI 下中文 IME 候选窗定位错误,与当前问题高度相似。"
},
{
"source": "xterm.js PR #5759",
"url": "https://github.com/xtermjs/xterm.js/pull/5759",
"strength": "strong",
"note": "上游已合并 IME 方向修复:compositionstart 前同步 textarea,compositionstart 后立即更新 composition element。"
},
{
"source": "本地代码审查",
"url": "frontend/app/view/term/termwrap.ts",
"strength": "strong",
"note": "`installNormalBufferWheelScrollback()` 当前在 bubble 阶段先判断 `event.defaultPrevented`;如果 xterm 内部已先消费 wheel,Wave 兜底不会运行。"
},
{
"source": "用户 2026-04-21 截图反馈",
"url": ".harness/progress.md",
"strength": "strong",
"note": "最新多终端截图显示真实 split-pane 场景里输入框仍错位、滚轮又失效,说明当前修复在真实焦点切换场景下仍不稳定。"
}
],
"candidate_solutions": [
"用 `terminal.attachCustomWheelEventHandler` 替代外层 DOM wheel listener,在 xterm 默认处理前决定 normal-buffer wheel 是否转为 scrollback",
"给 IME 兜底增加 focused terminal ownership,只允许当前真实焦点 terminal 改 helper textarea/composition-view 位置",
"在 xterm compositionstart / focus 生命周期附近做最小 IME 同步,参考 xterm PR #5759,而不是持续 onRender 改 style",
"把 Agent/Codex 检测从可见文本正则降级为 fallback,只在 xterm 原生同步失败时启用"
],
"reach": 5,
"impact": 5,
"confidence": "high",
"effort": 3,
"architecture_fit": "high",
"strategic_fit": "high",
"risk_penalty": "medium",
"maintenance_penalty": "low",
"status": "approved"
},
{
"id": "OPP-TERM-003",
"title": "建立终端回归 smoke 自动化闭环",
"problem": "多轮修复反复出现“代码已改但用户测到旧包/旧实例/无法确认真实滚轮和 IME”的问题,当前验证主要靠人工和临时 CDP 命令,无法稳定防止回归。",
"evidence": [
{
"source": "本地 .harness/progress.md",
"url": ".harness/progress.md",
"strength": "strong",
"note": "已记录多次产物未刷新、CDP 截图不稳定、真实系统 IME 难自动化的问题。"
},
{
"source": "Microsoft IME guidance",
"url": "https://learn.microsoft.com/en-us/windows/apps/develop/input/input-method-editors",
"strength": "medium",
"note": "Microsoft 建议有文本输入的应用对 IME 端到端体验做测试,并修复候选窗遮挡等问题。"
},
{
"source": "TextBox DesiredCandidateWindowAlignment",
"url": "https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.controls.textbox.desiredcandidatewindowalignment?view=windows-app-sdk-1.8",
"strength": "medium",
"note": "Windows 输入体验默认硬件键盘下 IME 跟随 cursor;这可作为 Wave 的 smoke 断言目标。"
}
],
"candidate_solutions": [
"新增 `scripts/smoke-terminal.ps1`:关闭旧进程、启动最新 win-unpacked、连接 CDP、断言运行路径/版本/行列/无历史恢复",
"用 DOM/JS 断言 xterm `viewportY` 变化、helper textarea 与 cursor 对齐,而不是依赖截图",
"把完整分发包时间戳、SHA256、运行态 `location.href` 写入 smoke 输出,避免误测旧包"
],
"reach": 5,
"impact": 4,
"confidence": "high",
"effort": 2,
"architecture_fit": "high",
"strategic_fit": "high",
"risk_penalty": "low",
"maintenance_penalty": "low",
"status": "implemented"
},
{
"id": "OPP-TERM-005",
"title": "补强多终端焦点与真实事件路径 smoke",
"problem": "当前 smoke 已能证明最新包、历史链路移除、单终端 DOM 级 wheel/IME 断言可通过,但它仍通过 `window.term` 单实例、强制 `shouldAnchorImeForAgentTui=()=>true` 和直接向 `.xterm-scrollable-element` 派发 `WheelEvent` 的方式验证,无法覆盖真实多终端 split-pane、焦点切换和 OS 事件路径。",
"evidence": [
{
"source": "本地 smoke 脚本",
"url": "scripts/smoke-terminal.ps1",
"strength": "strong",
"note": "当前 smoke 只验证单个 `window.term`,并强制调用 `syncImePositionForAgentTui()`,没有验证真实 focus owner。"
},
{
"source": "用户 2026-04-21 截图反馈",
"url": ".harness/progress.md",
"strength": "strong",
"note": "用户最新截图显示脚本通过后,真实 UI 中滚轮和输入框问题仍会复现,说明 smoke 覆盖范围不足。"
}
],
"candidate_solutions": [
"让 smoke 先枚举页面上的多个 terminal block,选择当前可见且聚焦的 terminal,而不是默认 `window.term`",
"增加 split-pane 场景断言:上方 Codex 终端与下方 PowerShell 终端同时存在时,只有 active terminal 的 textarea/composition-view 允许被改位置",
"把 wheel 断言从内部 scrollableElement 直派发升级为对 terminal connectElem/外层容器派发,尽量贴近真实用户路径"
],
"reach": 4,
"impact": 4,
"confidence": "high",
"effort": 2,
"architecture_fit": "high",
"strategic_fit": "high",
"risk_penalty": "low",
"maintenance_penalty": "low",
"status": "approved"
},
{
"id": "OPP-TERM-004",
"title": "清理后端未使用的终端历史缓存入口",
"problem": "前端已经停用 terminal 历史缓存/恢复,但后端仍保留 `SaveTerminalState`、`BlockFile_Cache` 等入口;未来维护者可能误以为历史缓存仍是受支持能力并重新接回。",
"evidence": [
{
"source": "Wave upstream termwrap",
"url": "https://raw.githubusercontent.com/wavetermdev/waveterm/main/frontend/app/view/term/termwrap.ts",
"strength": "medium",
"note": "上游当前仍包含 `cache:term:full` 与 `SaveTerminalState` 链路;本 fork 已按用户要求偏离上游。"
},
{
"source": "本地代码检索",
"url": "frontend/app/view/term/termwrap.ts",
"strength": "strong",
"note": "本地前端已不存在 `cache:term:full`、`SaveTerminalState`、`SerializeAddon` 引用。"
}
],
"candidate_solutions": [
"删除或标记废弃后端 `SaveTerminalState` 与 `BlockFile_Cache`,并更新生成类型",
"保留 API 但改名/注释为 deprecated,避免误接回前端",
"把历史缓存作为显式 feature flag,默认关闭"
],
"reach": 3,
"impact": 3,
"confidence": "medium",
"effort": 3,
"architecture_fit": "medium",
"strategic_fit": "medium",
"risk_penalty": "medium",
"maintenance_penalty": "medium",
"status": "candidate"
}
]
}
Loading