Files
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

4.1 KiB
Raw Permalink Blame History

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;断言退出码语义
外科式改写改坏了别的行 配置注释丢失、表达式被毁 往返断言 + 校验通过才替换 + 时间戳备份
一次性做三个编辑器把质量摊薄 三个都半成品 分三轮,每轮独立验收