Files
BakNRet/.scratch/tui/spec.md
T
Shuery 60f2ae3e0f docs: TUI 与入口重组的设计记录(4 条 ADR + 验收锚点)
grilling 三轮把设计树走完了,这里把它落成文件,避免决策只活在对话里。

ADR-0010 零依赖自研 TUI:三个候选库全部实测排除 —— ConsoleGuiTools 的 PSGallery 元数据声明最低 7.2(5.1 装不上);Spectre.Console 0.49.1 能在 5.1 加载但要塞 4 个第三方 DLL 且不支持鼠标;Terminal.Gui 1.15.0 能反射接线到真的渲染出窗口,但 Application.Shutdown() 在两端都抛 NRE。同时记下两条实测坑:无控制台时 $Host.UI.SupportsVirtualTerminal 会撒谎返回 True(第一道闸门必须是 [Console]::IsOutputRedirected),以及 5.1 的 RawUI.LengthInBufferCells 对中文返回 2、对边框字符返回 6(都不能用来排版)。

ADR-0011 进度回调这个例外:它是注入点不是状态,无头路径为 $null 时行为与今天完全一致。

ADR-0012 入口改名与垫片:这次留垫片而 Common.psm1 直接删,是因为前者是外部接口(断了是静默没用)后者是内部实现(断了当场报错)。并纠正了自己的一个伪前提 —— 本机实测没有注册任何 BakNRet 计划任务。

ADR-0013 TUI 写配置:外科式改写 + 校验通过才原子替换。硬事实是 SoftwareCatalog.psd1 根本不能被 Import-PowerShellDataFile 读入(动态表达式),任何"解析成对象再序列化"的方案都会毁掉表达式与注释。

.scratch/tui/spec.md 是这次改造的验收锚点:9 条已定决策 + 三轮的判据 + 风险表。

验收:Parse 两版仍绿。
2026-09-27 15:27:01 +08:00

69 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TUI 与入口重组 —— 验收锚点
本文件是这次改造的 spec:目标、已定决策、每轮的验收判据。**改动这个范围之外的代码要重新走一轮
确认**;改这个文件里的判据要说明为什么。
## 目标
把四个入口(主入口 + 备份 / 恢复 / 配置三个动作)与一个**零依赖 TUI** 落地,同时**不破坏**已经
建立的四条底线:
1. 两个 PowerShell 版本(5.1 与 7)都能跑;
2. 全仓解析零错、`test.ps1` 全绿、真实清单只读冒烟通过;
3. 无头/无人值守路径**行为与今天一致**(`exit` 退出码语义不变);
4. 零依赖承诺不被破坏(运行备份/恢复不需要任何模块)。
## 已定决策(细节见对应 ADR)
| # | 决策 | 出处 |
| --- | --- | --- |
| D1 | 零依赖自研 TUI;不用 ConsoleGuiTools / Spectre / Terminal.Gui | ADR-0010 |
| D2 | 界面**行内**(不平屏),只有状态行原地重画 | 本轮决策 |
| D3 | 第一道闸门是 `[Console]::IsOutputRedirected`;TUI 异常**不改退出码** | ADR-0010 |
| D4 | 日志:文件全量不变,TUI 下控制台输出降级为只写文件 | 本轮决策 |
| D5 | 进度用可注入回调(无头为 `$null`) | ADR-0011 |
| D6 | 入口改名 + 只留一轮的薄垫片;`Handle-Config` → `Edit-Config` | ADR-0012 |
| D7 | 三个配置界面:清单=行编辑器、配置=表单、名录=树+只读字段 | ADR-0013 |
| D8 | 写回=外科式改写,校验通过才原子替换;保存前留时间戳备份 | ADR-0013 |
| D9 | `-InputScript @('Down','Enter')` 作为输入缝;序列用尽仍在交互界面 → 直接报错退出 | 本轮决策 |
## 分三轮(每轮独立提交、独立断言、门禁全绿)
### 第一轮:TuiKit + 菜单骨架 + 清单编辑器
- `BakNRet/Public/` 里新增 TUI 积木:`Get-BakNRetCellWidth`(East-Asian-Wide 表 + 代理对)、
read-key 包装、`Write-BakNRetAt`(带"无控制台"退化)、菜单渲染、多选、确认。
- 入口重组:`Manage-Backup.ps1`(TUI + `-Action`/`-Quiet` 无头)、`Backup-Data.ps1`、
`Restore-Data.ps1`、`Edit-Config.ps1`;`Backup.ps1`/`Restore.ps1` 变垫片。
- `Edit-Config` 只做**清单行编辑器**。
**判据**:
- `Get-BakNRetCellWidth`:ASCII=列数、中文 2 列、字体边框 1 列、全角拉丁 2 列、半角片假名 1 列、
emoji(代理对)2 列、控制字符 0 列 —— 全部有断言。
- 清单编辑器**往返断言**:解析 → 改一个字段 → 写回 → 重新解析,除目标字段外逐字段相等,
注释行数不变。
- 无头闸门:把 `test.ps1` 的输出重定向时跑 `Manage-Backup -Action Backup -DryRun`,
断言不出现任何 TUI 输出、且退出码语义与今天一致。
- `-InputScript` 驱动:`@('Down','Enter')` 能走完"打开菜单 → 选备份 → 干跑 → 退出",序列用尽
却仍在交互界面时报错退出(不是挂起)。
- 垫片:`Backup.ps1` 转发后退出码与 `Backup-Data.ps1` 一致。
### 第二轮:配置编辑器(`BackupConfig.psd1` 扁平表单)
**判据**:往返断言(同上);非法取值被拒绝且**不落盘**;`logs/config-backups/` 里出现时间戳副本。
### 第三轮:名录编辑器(树 + 只读字段)
**判据**:往返断言;`Encrypt`/`Description` 可改,`Path`/`Exclude`/`Include` 只读;改完写回后
`Import-BaknretDataFile` 仍能读出**与改前逐字段相等**的内容(动态表达式与拼接串原样保留)。
## 风险
| 风险 | 表现 | 对策 |
| --- | --- | --- |
| 无控制台时 API 静默返回假值 | `RawUI.WindowSize` 给 `160x40`、`KeyAvailable` 恒 True | D3 的闸门;TUI 段全包 try/finally |
| 中文宽度算错 | 菜单右边框歪、光标定位偏 | 第一轮的宽度断言(含半角片假名/emoji) |
| TUI 吃掉了退出码 | 计划任务显示成功但备份失败 | TUI 异常绝不 `exit`;断言退出码语义 |
| 外科式改写改坏了别的行 | 配置注释丢失、表达式被毁 | 往返断言 + 校验通过才替换 + 时间戳备份 |
| 一次性做三个编辑器把质量摊薄 | 三个都半成品 | 分三轮,每轮独立验收 |