feat(ui): 重设计文件管理器书签与传输面板

新增书签作用域与连接关联,后端为 favorite_paths
补充 scope 和 connection_id 字段及查询写入支持

前端重构书签弹窗与编辑表单,支持本地/云端筛选、
作用域选择与多语言文案更新

文件管理器工具栏改为紧凑图标样式,上传入口合并为
下拉菜单,并新增底部传输面板统一展示上传任务

同时优化 SSH 终端运行态为显式状态机,并为短命令
补充最短可见时间,避免运行中标记闪烁难以感知
This commit is contained in:
yinjianm
2026-05-01 22:54:29 +08:00
parent 96d9950c6b
commit 2233e3fa4f
33 changed files with 1868 additions and 1541 deletions
@@ -0,0 +1,12 @@
{
"status": "completed",
"completed": 5,
"failed": 0,
"skipped": 0,
"pending": 0,
"total": 5,
"done": 5,
"percent": 100,
"current": "-",
"updated_at": "2026-04-21 05:43:00"
}
@@ -0,0 +1,150 @@
# 变更提案: ssh-terminal-runtime-state-machine
## 元信息
```yaml
类型: 修复/优化
方案类型: implementation
优先级: P1
状态: 实施中
创建: 2026-04-21
```
---
## 1. 需求
### 背景
仓库已经在 2026-04-19 做过一轮 `%` 运行中提示增强,当前 [TerminalTabBar.vue](/E:/code/vue/nexus-terminal/packages/frontend/src/components/TerminalTabBar.vue) 和 [LayoutRenderer.vue](/E:/code/vue/nexus-terminal/packages/frontend/src/components/LayoutRenderer.vue) 也已经接上了 `%` UI。但现有实现仍把运行态建模成单个 `isCommandRunning` 布尔值,再叠加 `terminalInputBuffer` 和 prompt 正则做启停判断。这个模型无法区分“刚提交命令但还没输出”“命令正在持续输出”“连接异常断开”“错误导致提前结束”等阶段,导致一些真实场景下 `%` 要么瞬时闪烁,要么被过早清掉,用户体感上接近“没有实际作用”。
### 目标
- 将 SSH 命令运行态从单个布尔值升级成显式状态机,至少能区分 `idle / typing / pending / running / disconnected / error`
- 继续基于发送命令、shell prompt、断连与错误链路派生运行态,但收口到统一的状态转移逻辑,而不是分散地手改布尔值。
- 让顶部服务器标签和服务器内部终端标签继续显示 `%`,但由“运行态处于活动阶段”统一派生,确保两层显示一致。
- 解决“快速命令根本看不到 `%`”的问题,让极短命令也能有足够短但可感知的可视反馈。
### 约束条件
```yaml
时间约束: 本轮限定在 packages/frontend 内完成,不扩展 backend WebSocket 协议
性能约束: 继续沿用轻量字符串缓冲与尾部 prompt 检测,不引入全终端内容扫描
兼容性约束: RDP/VNC 标签行为不变,现有 WorkspaceView / Terminal / session getter 数据流尽量少破坏
业务约束: `%` 仍是前端派生提示,不承诺成为服务端权威任务状态
```
### 验收标准
- [ ] SSH 会话在底部命令输入框、快捷指令、文件管理器和终端内回车等现有发送入口触发后,会进入显式运行态活动阶段并显示 `%`
- [ ] shell prompt 返回、连接断开、SSH 错误和 `Ctrl+C` 中断后,运行态会按统一状态机退出活动阶段,顶部服务器标签和内部终端标签同步消失 `%`
- [ ] 极短命令不会因为“提交后立刻命中 prompt”而完全看不到 `%`,标签上至少存在一个可感知的短暂运行中提示
- [ ] `npm --workspace @nexus-terminal/frontend run build` 通过,且没有新增类型或模板错误
---
## 2. 方案
### 技术方案
本轮不再继续扩展旧的 `isCommandRunning` 布尔值,而是引入一层显式 SSH 运行态模型,并把发送输入、输出探测、断连和错误全部收口到同一套 reducer 风格的状态转移函数中。
实现分成三层:
1.`session` 模块中新增 SSH 运行态类型与状态字段,至少包含 `phase``lastTransitionAt``lastCompletedAt` 和输入缓冲;`isCommandRunning` 改为从 `phase` 派生,而不是作为主状态独立维护。
2.`useSshTerminal.ts` 中把“发送非空命令”“收到输出”“命中 prompt”“中断”“断连”“错误”统一转换成状态机事件,避免旧实现那种一边写布尔、一边清空输入缓存的分散逻辑。
3. 在标签 UI 层继续复用 `%`,但由 `isRuntimeActive(phase)` 统一判定。对于极快结束的命令,增加一个很短的最小可见窗口,让 `%` 不至于只闪过一个渲染帧。
### 影响范围
```yaml
涉及模块:
- frontend/session: 调整 SSH 会话运行态模型与 getter 派生字段
- frontend/composables: 重写 useSshTerminal.ts 内的运行态转移逻辑
- frontend/ui: 顶部服务器标签与服务器内终端标签改为消费新的派生活动态
- frontend/knowledge: 同步 frontend.md 与 CHANGELOG.md 中的运行态描述
预计变更文件: 6-8
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| prompt 检测仍可能覆盖不到个别定制 shell | 中 | 保持 prompt 识别只负责“退出活动态”,同时由断连、错误和中断链路兜底清理 |
| 新状态机与旧 getter 混用导致 UI 不更新或语义冲突 | 中 | 收敛 getter,只保留一个派生活动态出口,模板层继续吃布尔结果以减小改动面 |
| 为了让短命令可见而增加最短展示时间后,可能让极快命令多亮几百毫秒 | 低 | 将最短窗口控制在短阈值,只解决“完全看不到”的问题,不把标签做成长时延迟态 |
---
## 3. 技术设计
### 架构设计
```mermaid
flowchart TD
A[CommandInputBar / QuickCommands / FileManager / Terminal] --> B[terminalManager.sendData or handleTerminalData]
B --> C[SSH Runtime Reducer]
C --> D[session.commandRuntimePhase]
E[ssh:output] --> C
F[ssh:disconnected / ssh:error / Ctrl+C] --> C
D --> G[session getter 派生 isCommandRunning]
G --> H[TerminalTabBar 服务器标签 %]
G --> I[LayoutRenderer 内部终端标签 %]
```
### 数据模型
| 字段 | 类型 | 说明 |
|------|------|------|
| `commandRuntimePhase` | `Ref<'idle' \| 'typing' \| 'pending' \| 'running' \| 'disconnected' \| 'error'>` | SSH 终端当前所处的显式运行阶段 |
| `commandRuntimeReason` | `Ref<'init' \| 'input' \| 'submit' \| 'output' \| 'prompt' \| 'interrupt' \| 'disconnect' \| 'error' \| 'connected'>` | 最近一次状态迁移的原因,便于调试与后续扩展 |
| `commandRuntimeVisibleUntil` | `Ref<number>` | 运行中提示至少显示到的时间点,用于避免极短命令完全不可见 |
| `terminalInputBuffer` | `Ref<string>` | 当前一行尚未提交的终端输入缓冲,继续用于判断回车是否提交了非空命令 |
---
## 4. 核心场景
### 场景: 快捷命令或底部命令输入框发送非空命令
**模块**: frontend
**条件**: 用户在某个 SSH 会话上通过底部命令输入框、快捷指令、命令历史或文件管理器触发 `terminalManager.sendData(...)`
**行为**: 状态机收到“提交命令”事件,切换到 `pending`,并立即打开 `%` 运行中提示
**结果**: 顶部服务器标签和当前服务器内部终端标签都能同步看到 `%`
### 场景: 命令快速结束但提示仍可见
**模块**: frontend
**条件**: 用户发送一个几乎立即返回 prompt 的短命令
**行为**: 状态机在提交后进入 `pending`,即使命中 prompt 也会遵守最短可见窗口再退出活动阶段
**结果**: `%` 不会只闪过一个渲染帧,用户能明确感知“刚刚执行过”
### 场景: shell prompt、断连和错误统一退出活动态
**模块**: frontend
**条件**: SSH 会话收到 prompt 尾部、`ssh:disconnected``ssh:error``Ctrl+C`
**行为**: 状态机依据事件原因切换到 `idle / disconnected / error` 等非活动阶段
**结果**: 顶部服务器标签与内部终端标签的 `%` 同步消失,不再残留旧状态
---
## 5. 技术决策
### ssh-terminal-runtime-state-machine#D001: 用显式运行态阶段替代单一 `isCommandRunning` 布尔值
**日期**: 2026-04-21
**状态**: ✅采纳
**背景**: 当前 `%` 标签虽然已经渲染到 UI,但旧实现只能用 `true/false` 表达“正在运行”,导致“刚提交命令但还没输出”“命令执行中”“连接已断开”“错误终止”等语义全部混在一个布尔值里,既难维护,也难稳定派生 UI。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 继续沿用布尔值并追加更多 if/else | 改动最少 | 状态语义继续混乱,问题很容易回归 |
| B: 引入显式运行态阶段和统一转移函数 | 状态来源清晰,便于覆盖 prompt/断连/错误等链路 | 需要调整 session 类型和 useSshTerminal 逻辑 |
**决策**: 选择方案 B
**理由**: 用户当前反馈的根因不是“少一个 if 判断”,而是模型过弱。只有把 SSH 运行态从布尔提升为阶段状态,才能真正稳定地驱动 `%`
**影响**: 影响 `session` 类型定义、getter 派生逻辑、`useSshTerminal.ts` 的输入/输出处理以及两个标签组件的消费方式
### ssh-terminal-runtime-state-machine#D002: 为极短命令增加最短可见窗口,而不是 prompt 一到就立刻灭掉 `%`
**日期**: 2026-04-21
**状态**: ✅采纳
**背景**: 用户明确反馈“整个没看到实际作用”,说明仅靠“提交置位、prompt 清除”的旧策略在短命令场景下可见性太差,即便逻辑成立也没有体感价值。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: prompt 命中后立即清除 `%` | 语义最直接 | 短命令常常一闪而过,用户几乎感知不到 |
| B: 运行态活动阶段增加一个很短的最小可见窗口 | 保持真实链路派生,同时确保用户能看到反馈 | 极快命令会多保留极短时间 |
**决策**: 选择方案 B
**理由**: 本轮目标不是做“理论上存在过”的状态,而是让用户真的看到 `%` 起作用。短暂的最小展示时间能显著提升感知质量,而且不需要改变后端协议。
**影响**: 需要在前端状态机里记录时间戳,并在 prompt/错误/断连清理时考虑延迟退出
---
## 6. 成果设计
N/A。本轮不新增视觉体系,只保持现有深色终端工作台内的 `%` 提示语义,并增强其可见性与状态来源准确度。
@@ -0,0 +1,53 @@
# 任务清单: ssh-terminal-runtime-state-machine
> **@status:** completed | 2026-04-21 05:47
```yaml
@feature: ssh-terminal-runtime-state-machine
@created: 2026-04-21
@status: completed
@mode: R2
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 5 | 0 | 0 | 5 |
---
## 任务列表
### 1. 运行态模型重构
- [√] 1.1 在 `packages/frontend/src/stores/session/types.ts``packages/frontend/src/stores/session/actions/sessionActions.ts` 中引入 SSH 显式运行态字段,并移除旧的主布尔状态入口 | depends_on: []
- [√] 1.2 在 `packages/frontend/src/composables/useSshTerminal.ts` 中收口发送、prompt、断连、错误与中断链路,改为统一状态机转移逻辑 | depends_on: [1.1]
### 2. 标签派生与可见性修复
- [√] 2.1 在 `packages/frontend/src/stores/session/getters.ts` 中改为从 `commandRuntimePhase` 派生活动态,并保持 `TerminalTabBar.vue``LayoutRenderer.vue` 继续复用现有 `%` 模板显示一致 | depends_on: [1.2]
- [√] 2.2 为极短命令加入最短可见窗口,确保 `%` 不会只闪烁一个渲染帧 | depends_on: [1.2]
### 3. 验证与知识库同步
- [√] 3.1 运行 `npm --workspace @nexus-terminal/frontend run build` 验证前端编译通过,并同步 `.helloagents/modules/frontend.md``.helloagents/CHANGELOG.md` | depends_on: [2.1, 2.2]
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-21 05:31 | 方案包创建 | 完成 | 已创建 `202604210531_ssh-terminal-runtime-state-machine`,准备进入开发实施 |
| 2026-04-21 05:40 | 1.1 / 1.2 | 完成 | 已引入 `commandRuntimePhase` 显式状态,并将发送、prompt、断连、错误与中断链路统一收口到状态转移逻辑 |
| 2026-04-21 05:41 | 2.1 / 2.2 | 完成 | getter 现已从 `commandRuntimePhase` 派生活动态,并为极短命令补上最短可见窗口,保持两层 `%` 继续复用现有模板 |
| 2026-04-21 05:43 | 3.1 | 完成 | `npm --workspace @nexus-terminal/frontend run build` 通过,并同步 `frontend.md``CHANGELOG.md` |
---
## 执行备注
- 本轮为前端单包修复,不修改 backend WebSocket 协议。
- 历史方案 `202604192106_terminal-running-indicator` 已完成,但现场反馈显示旧布尔模型体感无效,本轮在其基础上做显式状态机收敛。
- 顶部服务器标签与内部终端标签继续保留 `%` 设计,不新增新的运行态 UI 组件。