Commit Graph
35 Commits
Author SHA1 Message Date
XuzandClaude 1e81f85343 SFTP:断线时操作远程侧弹出重连对话框,工具栏不换行,分栏对半并按标签记住
- 断线后对远程侧的操作(导航、刷新、删改、属性、所有传输)不再没有
  反应:弹出「与 会话名 的 SFTP 连接已断开」,说明原因,可直接「重新
  连接」;队列里有停止的批次或正在自动重连时只说明该怎么办。远程工具栏
  和菜单在未连接时保持可点,只有连接中才禁用
- 列表还没读到目录时显示「正在连接 SFTP…」「正在读取目录…」,不再
  先显示「空目录」
- 工具栏固定一行高,放不下的按钮从右往左整个隐藏,不换行,两侧列表
  始终对齐
- 新开的 SFTP 标签本地、远程对半分;分隔线位置(含队列)由标签自己
  持有,切换标签后不再被重置(不持久化)
- 去掉当前面板路径标签的蓝色下划线
- 双击任一中间区标签的标题,显示 / 隐藏左侧会话栏(同 ⌘B)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-30 18:06:07 +08:00
XuzandClaude bf72546c64 SFTP:断网和读目录错误显示在窗口左下角,标签增加终端的命令,空闲时也能发现断网
- 读不了目录、连接断开的原因不再插在列表上方,改为用红字显示在窗口
  状态栏最左边(平时写「已连接 会话名」处);SFTP 标签在前时那里显示
  这个标签自己的连接状态。终端标签的会话断开时也是红字
- SFTP 标签菜单增加打开 SFTP、复制 IP 地址 / 主机名、重新连接,标签栏
  和远程终端一样有「SFTP」「重新连接」按钮;重新连接在已连接时也可用
  (先断开再连),有批次在跑或停在队首时不可用
- russh-sftp 的「session closed」算作断网,断网后刷新不再停在
  「已连接」、无法重连
- SFTP 通道流包一层 WatchedStream,读到结尾即发出关闭信号;工作线程
  空闲时等它,和终端一样在 keepalive 关掉连接时立刻报告断开,不必等
  下一次请求。等待时不持有客户端,主动断开仍会立即关掉连接

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-26 09:19:07 +08:00
XuzandClaude 59a3ffd1a6 SFTP:WinSCP 式传输队列,文件列表支持框选、按名称单元格选择
传输队列(每个 SFTP 标签一个,替代原来的进度条)
- 传输进行中也能再发起上传 / 下载,新批次排队依次执行;引擎每个命令后
  发 Idle,标签据此推进。已停止的批次挡在队首,等继续、丢弃续传进度
  或移出队列
- 队列面板在双栏下方,有项目时出现、可拖动高度:每批一行(来源、目标、
  已传输、剩余时间、速度、进度),进行中的批次带当前文件子行,批次行
  可展开看逐项结果;工具栏有继续、停止、移出、丢弃、清除已完成
- TransferProgress 补当前文件、只按传输阶段累计的用时、最近 3 秒的速度
  (TransferClock)、跳过 / 失败的字节和重连说明
- 确认框在有传输时提示这一批会排队

文件列表的选择(同 WinSCP 关闭「整行选择」)
- 名称单元格才是项目:悬停只有名称格变灰,选中底色和光标框铺满名称格,
  单击 / 双击 / 右键作用在整个名称格上;行上不再画任何状态,DataTable
  自带的整行 hover 用表格底色盖住,右键的整行边框立即清掉
- 光标框只画在未选中的光标项上(..、刚用 ⌘ 取消的项)
- 从名称格右边的空白、其他列或列表下方拖动是框选,按住 ⌘ / Shift 开始
  则加到原有选择上,拖到边缘自动滚动;框选只作用于开始它的面板
- 单击空白处(其他列、列表下方)取消选择,⌘ / Shift 单击不取消
- 只有拖图标和文件名才是拖文件:按下未选中的项时只选中它,拖已选中的
  项带走全部已选;只有到了另一侧列表上才显示「上传 / 下载 N 个项目」
  并用复制光标,其他地方(包括自己的列表)显示禁止光标

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-25 21:35:05 +08:00
XuzandClaude e8f13c29fb SFTP:大小列可切换格式,确认框清单与传输详情更清楚
- 大小列默认按整 KB 显示(同 WinSCP),右键「大小」列标题可切换为
  字节 / 千字节 / 简短格式,全局生效并保存在 settings.json;列标题的
  右键命中经 menu_hit 交给列表容器的同一个菜单,不另挂菜单
- format_size 在四舍五入跨过 1024 或 100 时换单位 / 取整,
  不再出现「1024 KB」「100.0 KB」
- 上传 / 下载确认框:首行写明数量与种类,共同目录只写一次,每项一行
  带图标、悬停显示完整路径,长路径不再折成两行看似两项
- 传输「详情」:结果改为结构化的 TransferDetail(完成 / 跳过 / 失败
  及原因),带状态图标、最新在上;尚无结束项目时显示说明,不再展开
  成空框;按钮显示展开状态

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-25 16:08:08 +08:00
XuzandClaude a61b915808 只在 Unix 上编译仅供 Unix 测试使用的 SFTP 测试辅助函数
local_test_server 和 batch_to 只被 cfg(unix) 的测试调用,在 Windows 上
成了死代码,让 clippy -D warnings 失败。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-25 14:19:32 +08:00
XuzandClaude e1c11df792 Windows CI:给以冒号结尾的测试命令加引号,修复 YAML 语法错误
Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-25 14:04:36 +08:00
XuzandClaude d6e1ed69ec 外部 CLI 支持 Windows:命名管道、独立控制台程序 shellrs-cli 与用户 PATH 安装
- 新增 shellrs-cli 二进制(各平台都构建);Windows 发布版的 GUI 改为
  windows_subsystem = "windows",命令由 shellrs-cli.exe 承担
- Windows 上经命名管道 \\.\pipe\shellrs-cli-<数据目录散列> 与应用通信:
  DACL 只允许当前用户、拒绝远程客户端、首个实例独占名字;客户端以
  SECURITY_IDENTIFICATION 打开并校验管道属主,防止抢注冒充
- 设置 → 外部 CLI 在 Windows 上把 shellrs-cli.exe 复制成
  %LOCALAPPDATA%\ShellRS\bin\shellrs.exe 并加入用户 PATH;旧副本启动时
  自动更新,正在使用的副本也能替换;新增「已安装旧版本」「不在 PATH」
  「找不到 shellrs-cli.exe」状态
- 客户端拆成按平台的 connect 与共用的读帧循环;进度行改用空格覆盖;
  Windows 控制台上非法 UTF-8 输出替换为 U+FFFD;命令文本去 BOM、CRLF 转 LF
- 新增 tests/cli_bin.rs 进程级端到端测试与 Windows CI 工作流
- SKILL.md 增加 Windows / PowerShell 说明,README、CLAUDE.md 同步

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-25 14:00:21 +08:00
Xuz 9704a8cc20 新增外部 CLI:给 AI Agent 用的 shellrs 命令与设置页「外部 CLI」
- shellrs list / exec / upload / download:经本机套接字交给正在运行的 ShellRS 执行,命令本身不接触密码、钥匙串和数据库
- 设置 → 外部 CLI:启用开关(默认关)、把 shellrs 链接进 PATH、为通用 Agent / Codex / Claude Code / OpenCode 安装 skill
- exec 非交互登录,陌生主机或缺少密码直接失败;读到通道关闭为止,退出码透传远程命令
- 传输复用 SFTP 引擎,无人值守地回答提问;按 scp 规则解析目标并保留可执行位
- 套接字 0600 并校验对端 uid;开关关闭时仍监听但拒绝请求
- 修复 SFTP 批次准备失败时不发送 Idle 的问题
2026-09-25 13:11:23 +08:00
Xuz b2143edd01 会话列表悬停时在行右侧显示 用户@主机:端口 的 tooltip
- 会话行悬停 500 ms 后在行右侧弹出 tooltip,只显示 user@host:port,配色为主题反色
- 面板自持 TooltipOverlay 并指定 Placement::Right,不贴着鼠标、不遮挡下方行
- 修复鼠标向下移动到下一行时 tooltip 被上一行的收起请求提前收掉的问题
- 会话树里的系统徽章不再单独弹 tooltip,避免两个 tooltip 叠在一起
2026-09-25 12:01:39 +08:00
Xuz ead3712045 会话增加对外标识 public_id,右键菜单支持「复制 ID」
- sessions 表加 public_id 列(schema v7,唯一索引),16 位字母数字随机串,由 rand 生成
- 新建、复制会话时生成且不与已有的重复;编辑不变,删除后不复用;老库和手工插入的行在打开时补上
- 会话树和最近连接的右键菜单新增「复制 ID」,复制到剪贴板并通知
2026-09-25 11:30:53 +08:00
Xuz e3b5e20efe 程序改名为 ShellRS,命名空间改为 shellrs
- 界面显示名(窗口标题、标题栏、文案)改为 ShellRS
- crate、二进制、动作命名空间、线程名改为 shellrs
- 数据目录、数据库文件、SHELLRS_DATA_DIR、钥匙串服务名改为 shellrs;程序尚未发布,不保留旧名兼容代码
2026-09-25 10:49:37 +08:00
Xuz 5b6eee0b71 新增设置:外观(界面语言、应用外观)与终端字体配置,最近连接支持右键菜单
- 会话列表底部新增「设置」按钮(⌘, / Ctrl+,),在中间区打开单例的「设置」标签页
- 设置页用 gpui-kit 的 Settings 组件分两栏:左边分类(外观、终端,可搜索),右边设置分组
- 设置写穿到数据目录的 settings.json,写失败或读不出时弹通知;由工作区统一应用到窗口
- 外观 → 常规:界面语言(跟随系统 / 简体中文 / English,目前只切换 gpui-kit 自带文案)、应用外观(深色 / 浅色 / 跟随系统,随系统切换);标题栏主题按钮改为写这一项
- 终端 → 字体配置:字体(仅列出已安装的等宽字体,后台扫描)、字号、行高,默认值与原来一致,立即作用于已打开的终端;下方按终端排法预览数字、符号和中文
- 最近连接列表的主机支持右键菜单,与会话树共用同一个会话菜单
2026-09-25 10:00:20 +08:00
Xuz 348fe48e34 SFTP 路径标签与打开目录对话框、多 SFTP 标签、标签重命名,最近连接支持右键菜单
- 去掉面板顶部「用户@主机:端口」一行;路径栏改为 WinSCP 式路径标签:逐级可点、悬停高亮、过长时中段折叠
- 新增「打开目录/书签」对话框:目录输入加本侧书签列表,可添加、删除、上移、下移;书签按钮、⌘O 与路径标签均可打开
- 路径标签常驻浅蓝底色,当前面板加深蓝下边线;点列表空白处一次即切换当前面板
- 「正在连接 SFTP…」「正在读取目录…」移到列表下方状态栏,读取超过 300 ms 才显示,列表不再抖动
- 每次打开 SFTP 都新开标签(按 ExplorerId 寻址),各自独立连接与传输批次
- SFTP 标签使用主机系统徽章,右键菜单改为「重命名标签…」加关闭命令;重命名对话框与远程终端共用
- 最近连接列表的主机支持右键菜单,与会话树共用同一个会话菜单
2026-09-25 08:47:28 +08:00
Xuz 4badde667f SFTP 双栏对标 WinSCP:两行工具栏、WinSCP 列、多选、右键菜单、文件操作与下载 2026-09-25 01:54:32 +08:00
Xuz adbd1de688 终端右键菜单增加查找、清屏、SFTP、重新连接与断开连接 2026-09-24 23:13:14 +08:00
Xuz 48ac2b95aa 测试连接改为真实登录,结果以通知呈现 2026-09-24 22:38:22 +08:00
Xuz 6b1cd97317 通知层绘制在对话框之上 2026-09-24 22:37:54 +08:00
Xuz fa87917d9c 标签栏显示 SSH 连接延迟 2026-09-24 22:37:18 +08:00
Xuz 4428237b25 标签页增加右键菜单与主机徽章,统一「…」菜单 2026-09-24 22:36:24 +08:00
Xuz 1af8d0df97 新建、编辑会话增加测试连接功能 2026-09-24 20:55:38 +08:00
Xuz 4b9dac8fb8 修复会话树右键后选中高亮残留 2026-09-24 19:05:15 +08:00
Xuz 47b2efdcc7 调整会话树展示并持久化分组展开状态 2026-09-24 18:55:37 +08:00
Xuz b78aa7f937 实现会话树拖动排序并调整选中与连接交互 2026-09-24 18:27:47 +08:00
Xuz 23da7a6724 实现真实 SFTP 上传与 WinSCP 式断点续传 2026-09-16 10:32:50 +08:00
XuzandClaude Opus 5 698b9ed925 主机徽章去掉描边,开始页也用它
- 去掉徽章的边框。加描边本来是防纯黑或纯白的品牌在同色主题下糊掉,
  但那两个(Apple、AlmaLinux)用的是 foreground / background,跟着
  主题翻转,天然高对比,描边是多余的。
- 徽章抽成 shared::HostMark,会话树和「最近连接」开始页共用一份。
  尺寸走 Sizable:树里 small(20px),开始页默认 medium(32px),
  照着那行两行文字的高度定的。尺寸和字号都用 rem 助手,跟着缩放走。
- 开始页原来那个通用服务器图标换掉了;空状态里的大图标保留,那是
  插图不是主机标记。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ub6VpmWWAJB15MnYLT9G5
2026-09-12 17:13:33 +08:00
XuzandClaude Opus 5 d8e41646ec 系统图标改成品牌色徽章
图标从 14px 提到 20px,加上该系统品牌色的圆形底。

- 品牌色用各项目公布的官方值,写在 host_os! 表里而不是渲染处;设计
  规范禁止界面写死颜色,但留了「颜色本身即数据」这一档,品牌色属于
  这一类。
- logo 一律白色,只有 Tux 和 OpenBSD 这两个黄色品牌换成黑色,判据是
  WCAG 相对亮度,阈值取得比正文对比度要求高,因为几乎所有品牌都用白
  标画自己的 logo。
- AlmaLinux 和 macOS 没有单一官方色,改用 foreground / background 跟
  随主题,这也是 Apple 官方的画法。
- 徽章沿用 Avatar 的画法:主题圆角加一圈 border 描边。描边是纯黑或
  纯白品牌在明暗两个主题下都还看得见的原因。
- 分组行和其他行用 plain_mark 占同宽槽位,否则标签对不齐。
- 未探测到的会话退回 muted 底色加名称首字,aria_label 也从那个字改成
  「未探测到系统」,屏幕阅读器读一个孤零零的字没有意义。

顺带在文档里记下:GPUI 的 svg() 是单色遮罩,要彩色只能走 img()。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ub6VpmWWAJB15MnYLT9G5
2026-09-12 13:52:04 +08:00
XuzandClaude Opus 5 321a65d842 探测主机操作系统并用它标记会话列表
每次连接成功后探测一次远端系统,会话树前面的图标换成对应的发行版
logo;没探测过或探测失败就画会话名的第一个字。

- 探测走一条独立的 exec 通道,跑 `uname -s; cat /etc/os-release`,
  在主 IO 循环里和 shell 输出一起轮询,终端不会为此多等一个往返。
  POSIX 那条毫无输出时(Windows 的 shell 就是这样)再问一次
  `cmd /c ver`,仍认不出就放弃,会话保留原来的标记。
- 认 14 个发行版加通用 Linux、macOS、Windows 和三个 BSD。派生发行版
  靠 ID_LIKE 兜底,Linux Mint 因此显示 Ubuntu。
- 结果沿 TerminalTransportEvent::HostOsDetected → 引擎 → TerminalView
  → TerminalPanel → 工作区 → SessionStore::set_host_os 这条链走,和
  prompt 是同一套管线。os 列由 set_host_os 单独写,update_session 不
  碰它,和 last_connected_at 一样。
- 图标是 assets/icons/os/ 下 20 个单色 SVG,取自 Simple Icons(CC0);
  Windows 那个是自己画的四格,Simple Icons 已把微软图标下架。
- 加一步 V3 迁移补 os 列,这样已有的本地库不用删。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ub6VpmWWAJB15MnYLT9G5
2026-09-12 12:36:20 +08:00
XuzandClaude Opus 5 f0d66c4f8b 用系统钥匙串保存 SSH 密码与私钥口令
会话对话框新增密码字段和私钥口令字段,填一次之后连接就不再弹认证框。
秘密存进系统钥匙串(keyring 4:macOS Keychain / Windows 凭据管理器 /
Secret Service),数据库里依然不出现任何秘密。

- 新增 secrets 模块:SecretRef 按 user@host:port 归属密码、按文件路径
  归属私钥口令,所以会话改名、复制都不丢,同一把钥匙只问一次。
  SecretStore 的方法全是阻塞的,只能在后台执行器或传输层工作线程上调用。
- SSH 认证在弹框前先查钥匙串;被服务器拒绝时退回弹框并说明原因,条目
  保留不动,因为那是用户在对话框里填的。
- 写入的唯一入口是 SessionStore::save_secret,失败沿用 PersistFailed。
  会话换端点或被删除时,用 password_in_use 判断旧条目是否还有人用。
- 对话框的预填是异步的,*_loaded 置上之前空字段不算「用户清空了」,
  预填还没回来就点保存不会误删。
- 修正 CLAUDE.md / AGENTS.md 里「SSH 仍是 mock」的过期描述。

测试服务端原先对错误密码返回 UnsupportedMethod,russh 会因此摘掉密码
方式,真实服务器不是这个行为;改成 OpenSSH 那样拒绝但保留密码方式。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ub6VpmWWAJB15MnYLT9G5
2026-09-12 10:23:22 +08:00
since2006 e5f8790dba Merge branch 'sqlite-session-persistence' of https://github.com/since2006/shellr into sqlite-session-persistence 2026-09-12 09:34:02 +08:00
since2006 13afe7fc67 chore: gpui-kit skills 2026-09-12 09:33:59 +08:00
Xuz 05e05709f0 实现真实 SSH 客户端并支持多连接 2026-09-12 09:27:14 +08:00
since2006andClaude Opus 5 5459fe0378 SFTP 标签页激活时接管焦点
中间区一次只渲染激活的那个标签页。会话的终端标签失活后不再渲染,焦点却
还留在它身上,句柄于是不在这一帧的派发树里——「×」用 window.dispatch_action
派发 CloseTerminal / CloseExplorer,动作没有落点就被丢掉,两个标签的关闭
按钮一起失效,⌘W 和其它快捷键同理。

ExplorerPanel::set_active 漏了接管焦点,TerminalPanel 和 LocalTerminalPanel
都有。补上,三个面板一致。

回归测试 both_tabs_of_one_session_stay_closable:连上 db-01,开 SFTP,先关
SFTP 再关终端,两次都得真的关掉。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TChp1yPThAzy3UcdXZcZA9
2026-09-11 23:42:39 +08:00
since2006andClaude Opus 5 b0cb99f2bc 修复右键树节点时的无障碍焦点崩溃
gpui-component 的 ContextMenu 在 request_layout 里就把焦点抢给弹出菜单,
而树行是 uniform_list 在 prepaint 阶段才渲染布局的,于是焦点在一帧的中途
易主:树容器(被右键点击聚焦,且带无障碍角色)在这一帧更早的 prepaint 里
已经报告过自己是焦点节点,弹出菜单再报告一次就撞上 gpui 的断言
「set_focus called more than once in a single frame」。只在开了辅助功能的
调试构建里 panic,release 构建打警告。

把 .context_menu(..) 从每行挪到 #session-tree 容器上,容器走正常布局阶段,
焦点在 prepaint 开始前就定下来了。指针下的节点用 Rc<Cell<Option<SessionNode>>>
传递:容器在捕获阶段先清空,被命中的行在冒泡阶段写回自己——所有捕获处理器
都先于任何冒泡处理器,顺序有保证。gpui-kit 自带的 Table 也是这么挂的,所以
SFTP 文件列表一直没崩。

顺带:在行下方的空白处右键现在会出根菜单,以前没反应。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TChp1yPThAzy3UcdXZcZA9
2026-09-11 23:16:51 +08:00
since2006andClaude Opus 5 6bec390ecd 用 SQLite 持久化会话与分组
会话和分组不再只活在进程里。`SessionStore` 带 `Context` 的 mutator 现在
「改内存 → 写库 → notify」,落到 `~/Library/Application Support/shellr/
shellr.db`(`SHELLR_DATA_DIR` 可覆盖目录)。内存仍是唯一事实来源:写库
失败只丢持久化、不丢用户的编辑,store 发 `PersistFailed`,工作区据此弹
错误通知,绝不静默吞掉。

分组从只读的种子数据变成真正的配置项:任意层级嵌套,可新建 / 重命名 /
改上级 / 删除,删除按外键 `ON DELETE CASCADE` 连同子分组和里面的会话一起
带走,确认框写明数量。会话因此可以不属于任何分组,渲染在树的根层级——
首次启动是空库,第一个会话必须能在还没有任何分组时建出来。

- rusqlite 0.40(bundled):同步写、WAL、外键、`PRAGMA user_version` 迁移
- `app/paths.rs` 解析数据目录;`dirs` 6 已在依赖树里,不增加编译量
- `Workspace::new` 改为外部注入 store:`main.rs` 从库加载,测试注入 seed
- `ConnectionState` 不入库;`last_connected_at` 入库,最近连接跨启动保留
- 新增 5 个 UI 测试,其中一个走真实写路径后用第二个连接读回同一个文件

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcMDBhETbHS7rEucBMAVvv
2026-09-11 22:32:10 +08:00
Xuz 3bceb8879d 初始化 shellr 项目 2026-09-11 20:41:07 +08:00