Skip to content

Commit 311e2dc

Browse files
committed
✨ feat: 命令输出界面支持失败命令重试与管理员提权
- OpLogEntry 新增 executable/args 结构化字段,runCommand 在 log:true 时注入, 供重试直接读取而不必反解析 title - OutputScreen 对 failed 条目提供 r 普通重试 / a 管理员重试(win32 UAC); 底栏改为上下文提示,无提示时不渲染 - 新增 runCommandElevated:经 PowerShell Start-Process -Verb RunAs 提权运行 cmd /c,输出重定向到临时文件后读回写入同一 opLog;UAC 被拒绝/spawn 失败 记一行失败说明(不二次弹窗);非 win32 回退普通 runCommand - 重试经 onRetry 回调交 App 执行并 reloadManagers 刷新对应管理器 - 新增 5 个 output.* i18n 键(中/英),补充 ops/output-screen 测试
1 parent ba5688a commit 311e2dc

15 files changed

Lines changed: 1658 additions & 226 deletions

CLAUDE.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,16 @@ src/
116116
│ ├── ManagerStrip.tsx # 顶栏:设置/搜索按钮 + 过滤输入 + "全部"+各管理器按钮
117117
│ └── TaskStatus.tsx # 底栏右侧任务状态(订阅 opLog:运行中转圈+计数、✓ 3 秒隐藏、✗ 常驻)
118118
└── screens/
119-
├── ConfirmDialog.tsx # 确认框(命令预览 + 确定/取消)
120-
├── SearchScreen.tsx # 搜索(registry 分组并发,i 安装 / v 详情;失败来源在状态栏标注)
121-
├── DetailScreen.tsx # 包详情(后台 view,加载态,更新/删除/关闭)
119+
├── ConfirmDialog.tsx # 确认框(命令预览 + 确定/取消;options 多按钮模式:合并
120+
│ # registry 安装时每个可用管理器一个按钮,预览随聚焦切换)
121+
├── SearchScreen.tsx # 搜索(registry 分组并发,i 安装 / v 详情;失败来源在状态栏标注;
122+
│ # 范围条按 registry 分组显示(pnpm/bun 并入 npm);active=false
123+
│ # 时被上层 overlay 压住,不响应按键、输入框不聚焦)
124+
├── DetailScreen.tsx # 包详情(后台 view,加载态;已安装视图:更新(默认聚焦)/删除/安装版本,
125+
│ # 搜索视图:安装(默认聚焦,装最新版)/安装版本/关闭;active 门控同 SearchScreen)
122126
├── OutputScreen.tsx # 命令输出(安装/更新/卸载执行日志:左条目列表 + 右 sticky 输出区,
123-
│ # 运行中实时追加并跟随底部,↑↓ 切条目,PgUp/PgDn/Home/End 滚动)
127+
│ # 运行中实时追加并跟随底部,↑↓ 切条目,PgUp/PgDn/Home/End 滚动;
128+
│ # p 终止运行中条目,r 重试/a 管理员重试 failed 条目,底栏上下文提示)
124129
└── SettingsScreen.tsx # 设置(快捷键/图标/语言,保存回 config.json;自建列表交互
125130
# 同 PackageTable,见设置界面鼠标行为段落与 settings-screen-mouse 测试)
126131
```
@@ -141,7 +146,7 @@ src/
141146
| —(迁移时新增) | `focus.ts``components/ModalBackdrop.tsx`(OpenTUI 无 Textual 模态/焦点原语,迁移时补) |
142147
| Textual `DataTable` | 自建 `PackageTable` |
143148
| Textual `@work` worker| 普通 async + setState/rerender |
144-
| `push_screen/dismiss` | overlay 数组(一次一层) |
149+
| `push_screen/dismiss` | overlay 栈(多层叠放,仅顶层接收按键) |
145150

146151
### 运行时要点
147152

@@ -197,14 +202,14 @@ src/
197202
- **加载状态统一用 `LoadingIndicator`**:所有需要显示"加载中"的地方都应使用
198203
`src/components/LoadingIndicator.tsx`(单方向扫描 + 尾部色衰减动画),不要再写
199204
静态 `<text>加载中...</text>`。已接入:主页 `loadingHint` 空表占位、详情屏
200-
`state.status==="loading"`搜索屏状态栏(搜索中)与 `PackageTable.emptyHint`(加载态)。
205+
`state.status==="loading"`搜索屏底栏右侧状态(搜索中)与 `PackageTable.emptyHint`(加载态)。
201206
动画靠 `setInterval` 推帧 + React state 重绘,卸载清计时器;`PackageTable.emptyHint`
202207
已放宽为 `ReactNode` 以容纳它。新增加载场景一律复用此组件,保证全局风格一致。
203208
- **`scrollX` 模式的空白行修复**:OpenTUI ScrollBox 在内容未横向溢出时,横向滚动条
204209
本应隐藏,但首布局仍为它预留 1 行(visible 切到 false 后布局未刷新),导致表格
205210
底部多一行空白。`PackageTable``useEffect` 在内容 `onSizeChange` 时调
206211
`horizontalScrollBar.resetVisibilityControl()` 重算可见性并重排,回收该预留行。
207-
`scrollX` 表格的 `visibleRows` 按"无预留"算(搜索界面用 `height - 3`,主页
212+
`scrollX` 表格的 `visibleRows` 按"无预留"算(搜索界面用 `height - 2` 输入行1+底栏1,主页
208213
`scrollX``height - 4` 顶栏1+底栏1+表头1+paddingTop1)。
209214
- **不画竖直滚动条**:PackageTable 的纵向滚动是光标驱动的窗口(`windowStart`),
210215
不是 ScrollBox 平移,故不复用 ScrollBox 原生 `scrollY`,也没有自绘滚动条——
@@ -213,8 +218,19 @@ src/
213218
- **搜索"全部"并发 + 失败可见**`SearchScreen.doSearch``Promise.allSettled` 并发
214219
各 registry 代表(`buildSearchGroups`:同 registry 只搜一次,npm 系用 npm;不同
215220
registry 如 scoop/winget 各自独立)。任一来源 search 抛错**不能静默吞掉**——要在
216-
状态栏用 `search.status_partial_failed``(部分来源搜索失败:{names})`)标注,
221+
底栏右侧状态用 `search.status_partial_failed``(部分来源搜索失败:{names})`)标注,
217222
否则用户会误以为"全部"没搜某个管理器(曾误判 scoop 未被搜索)。
223+
- **overlay 是栈不是单层**:搜索页打开详情/确认框时压栈(下层保持挂载、搜索
224+
状态不丢),关闭上层回到下层——下载页装包后不再被踢回主页。只有顶层接收
225+
按键:`SearchScreen`/`DetailScreen``active` prop 为 false 时 useKeyboard
226+
直接返回,且搜索输入框 `focused` 连带 false(否则字符键会穿过上层 overlay
227+
落进搜索框)。确认框 onConfirm 用 `popOverlay(2)` 连详情一起关,onCancel
228+
`popOverlay()` 回到详情。
229+
- **合并 registry 安装在确认框选管理器**:同 registry(npm/pnpm/bun)搜索只搜
230+
代表(npm)、结果合并。安装入口(搜索 `i`、详情"安装"/"安装版本")统一走
231+
`confirmSearchInstall`:确认框 options 多按钮模式,每个可用管理器一个按钮
232+
(代表排最前),命令预览随聚焦按钮切换;已安装视图的更新/卸载/安装版本
233+
管理器固定,用经典确定/取消。确认后留在搜索页(后台执行)。
218234
- **overlay 背景跟随终端**`SearchScreen` 等 overlay 用 `terminal-colors.ts`
219235
`getTerminalBackground` 取终端默认背景色(主页根容器透明,overlay 必须实底盖住)。
220236
为避免"先渲染一帧 FALLBACK 深色再切到真实背景"的闪烁,`App` 启动时即调
@@ -226,6 +242,14 @@ src/
226242
输出实时追加;关闭即退订。**新增加入操作日志的命令必须传 `{ log: true }`**
227243
否则用户在"命令输出"界面看不到它;纯查询类命令不要传。opLog 是 mutable 单例,
228244
OutputScreen 每次渲染直接读 `opLog.entries`(同 ManagerRegistry 的刷新约定)。
245+
**失败条目可重试**`OutputScreen``failed` 条目提供 `r`(普通重试)/ `a`
246+
(win32 以管理员身份重试,UAC 提权)。重试目标取条目结构化的 `executable`/`args`
247+
`runCommand``log:true` 时注入 `opLog.begin(title, { executable, args })`),
248+
未注入时回退解析 `title`。提权由 `_cli.runCommandElevated` 实现:经 PowerShell
249+
`Start-Process -Verb RunAs` 提权运行 `cmd /c "<exe> <args> > out.tmp 2> err.tmp"`
250+
把提权子进程 stdout/stderr 重定向到临时文件后读回写入同一 opLog(用户仍在"命令输出"
251+
界面可见);UAC 被拒绝/spawn 失败则记一行失败说明(不二次弹窗)。重试经 `onRetry`
252+
回调交 App 执行并 `reloadManagers([executable])`(executable==管理器 name)。
229253
- **命令执行状态不走 toast**:安装/更新/卸载的结果不再弹右下角 toast(其他提示
230254
如"没有选中的包"仍用 toast)。主页底栏右侧 `components/TaskStatus.tsx` 订阅同一
231255
opLog:有运行中条目显示转圈(Braille 字符帧)+ `{n}个任务`;一批全部结束且全部
@@ -263,7 +287,7 @@ src/
263287
| `alt+s` | 打开设置 |
264288
| `Ctrl+C` | 退出 |
265289

266-
搜索界面:`i` 安装、`v` 详情、`Esc` 返回。详情/确认:`← →` 切按钮、`Esc` 关闭。
290+
搜索界面:`i` 安装(确认框内选管理器,确认后留在搜索页)`v` 详情、`Esc` 返回。详情/确认:`← →` 切按钮、`Esc` 关闭(逐层返回)
267291

268292
## 如何新增一个包管理器后端
269293

0 commit comments

Comments
 (0)