Skip to content

chore(deps): russh 升级 0.49 → 0.61 + 对应 API 迁移#151

Open
kodingsky wants to merge 182 commits into
jeff141:mainfrom
kodingsky:main
Open

chore(deps): russh 升级 0.49 → 0.61 + 对应 API 迁移#151
kodingsky wants to merge 182 commits into
jeff141:mainfrom
kodingsky:main

Conversation

@kodingsky

Copy link
Copy Markdown

…0.61 with API migration

  • Cargo.toml: russh 0.49 → 0.61(features=[ring]), 移除 russh-keys, ssh-key 0.6 → 0.7.0-rc.10
  • Handler 不再用 #[async_trait](trait 用 fn -> impl Future)
  • Auth 返回 AuthResult → 末尾加 .success()
  • PrivateKeyWithHashAlg::new() 返回 struct 而非 Result
  • Kb::Failure 改为 struct variant → Kb::Failure { .. }
  • Handler 新增方法(extended_data / exit_status / channel_eof / channel_close)有默认实现,无需覆盖
  • 移除已删除方法留下的 unused import(ChannelId)

jeff141 and others added 30 commits June 5, 2026 14:09
fix: 修复ssh密钥认证连接失败
…nux 桌面集成

Security fixes / 安全修复:
- jeff141#12 Windows 命令注入: open_with_os 改用 ShellExecuteW + sanitize_filename
- jeff141#10 编辑会话密码回显: 不再回填真实密码, 留空保留原密码
- jeff141#8  密码内存清零: 新增 Secret 类型(zeroize), Drop 清零 + Debug 脱敏

Features / 新增:
- jeff141#9 完整 i18n: 中/英双语, 运行时实时切换(Slint @tr + bundled .po + Rust t())
- 私钥文件选择器 + .pub 容错 + 路径统一 /
- Linux: meatshell.desktop + install-linux.sh + xdg_app_id(Wayland 图标)
- README 截图(docs/screenshots, 敏感信息已打码)

详见 CHANGELOG.md / see CHANGELOG.md for details.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The fullwidth plus (U+FF0B +) had no glyph in the Latin font Slint picks for
English text, so it rendered as a tofu box. Use an ASCII "+" instead (renders
in any font; Chinese button now shows "+ 新建会话").

全角加号在英文(拉丁字体)语境下没有字形 → 豆腐块。改用 ASCII "+",任何字体都能
渲染;中文按钮也相应改为 "+ 新建会话"。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an "Import ~/.ssh/config" button on the welcome page. Parses the standard
SSH config (Host / HostName / User / Port / IdentityFile; wildcard `Host *`
blocks skipped) and adds each host as a session, skipping duplicates by alias or
host+user. Hosts with an IdentityFile default to key auth. Includes a unit test.

欢迎页新增「导入 ~/.ssh/config」按钮:解析标准 SSH 配置(Host/HostName/User/
Port/IdentityFile,跳过 `Host *` 通配块),将主机加为会话并按别名或 host+user 去
重;带 IdentityFile 的默认密钥认证。含单元测试。

Also: bump to v0.2.3, update CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the SSH-config import action out of the welcome header (cleaner) into the
gear/settings menu as a third item next to language toggle and About. The import
result hint still shows in the Quick-connect header.

把导入操作从欢迎页头部(挤在新建会话旁不够美观)移到齿轮设置菜单,作为第三个菜单
项,与语言切换、关于并列;导入结果提示仍显示在快速连接栏。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add .github/workflows/release.yml: builds native binaries for Windows
(x86_64), Linux (x86_64), and macOS (arm64 + x86_64) via a matrix. Installs the
Slint/rfd/GTK system deps on the Linux runner. On a `v*` tag it attaches the
archives to a GitHub Release; on manual dispatch it uploads workflow artifacts.

新增三平台(Windows/Linux/macOS arm64+x86)构建发布工作流;Linux runner 自动安装
Slint/rfd/GTK 依赖;打 v* tag 自动建 Release 并上传产物。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
install-linux.sh now picks up the `meatshell` binary sitting next to it (the
release-package layout) when no argument is given, falling back to the
source-tree ./target/release path only when there's no sibling binary. Also
auto-chmod +x the binary. README (zh/en) gains a Download & install section
covering the GitHub Actions builds for Windows / Linux / macOS.

install-linux.sh 默认用同目录的 meatshell(发布包布局),无参数即可装;源码树下
才回退到 ./target/release。并自动给二进制加可执行权限。README 中英补充三平台下载
与 Linux 傻瓜式安装说明。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GitHub Actions only runs workflows under .github/workflows/. The release
workflow had ended up at .github/release.yml (which GitHub treats as a release-
notes config, not a workflow), so it never triggered. Move it back.

GitHub Actions 只运行 .github/workflows/ 下的工作流;文件被放到了 .github/
release.yml(被当成 release notes 配置,不是 workflow),不会触发。移回正确位置。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
text-muted (#6b6f7a) was only ~3.4:1 on the dark background — below WCAG AA —
so placeholders, secondary labels and disk details were hard to read. Brighten
text-muted to #9196a3 and text-secondary slightly to #b4b9c4.

text-muted 在深色背景上对比度仅 ~3.4:1(低于 WCAG AA),占位符/次要标签/磁盘明细
偏暗看不清。提亮 text-muted 与 text-secondary。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connections can now tunnel through a SOCKS5 (socks5://) or HTTP CONNECT
(http://) proxy with optional user:pass@ credentials, for network-restricted
environments. Resolved from the per-session "Proxy" field, falling back to the
$ALL_PROXY environment variable; empty = direct. src/proxy.rs establishes the
tunneled TcpStream (tokio-socks for SOCKS5, a hand-rolled HTTP CONNECT) and the
existing connection code hands it to russh's connect_stream. Verified
end-to-end: meatshell -> local SOCKS5 proxy -> server, both shell and SFTP.

新增 SSH/SFTP 经 SOCKS5 / HTTP CONNECT 代理(支持 user:pass@ 认证),用于网络受限
环境。会话「代理」字段优先,留空则用 $ALL_PROXY,再空直连。已端到端验证:
meatshell -> 本地 SOCKS5 代理 -> 服务器,shell 与 SFTP 均走代理。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jeff141#16 (perf): pipelined SFTP upload — keep ~32 WRITE requests in flight on a
  dedicated raw SFTP channel instead of write-and-wait, hiding RTT (was ~15x
  slower than scp). Verified: 3 MB upload, remote md5 matches local exactly.
jeff141#18 (bug): dragging the SFTP panel up cleared recent terminal output, because
  vt100's set_size truncates the grid from the bottom. Before shrinking, save the
  top rows to scrollback and scroll so the bottom (recent) rows stay visible.
jeff141#15 (security): debug logs recorded raw SSH keystroke bytes (could include
  passwords); log only the byte length now (ssh.rs + app.rs).

jeff141#16 上传流水线(并发 32 个 WRITE,藏住 RTT,原比 scp 慢约 15 倍);已验证 3MB 上传
远端 md5 与本地完全一致。jeff141#18 上拉面板清空输出(vt100 缩小从底部截断),改为缩小前
存顶部行入回滚并滚动保留底部。jeff141#15 debug 日志不再记录原始按键(可能含密码)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 新增 format_mem_mib():总内存 >=1GiB 时自动用 GiB 单位显示
- StatRow detail 文本固定宽度改为 min-width + stretch,不再被进度条挤压
- 大数值(>=100G)或整数G值省略小数点,进一步缩短字符串长度
- detail min-width 从 56px 提升至 64px
The earlier jeff141#15 fix only covered the byte-length log site. Two more leaks
remained in wire_key_input: send_key logged the raw key string at debug
level, and the [KEY_DIAG] IME diagnostic logged Shift-typed key code points
at info level (no RUST_LOG required). Both could expose password characters.

Add redact_key(): reveals only C0/C1 control code points (the IME markers
the diagnostics rely on) and masks every printable character.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add two non-SSH transports that reuse the full terminal pipeline:

- Serial (src/serial.rs): opens COM3 / /dev/ttyUSB0 via the serialport
  crate (blocking reader thread + spawn_blocking writes), configurable
  baud / data bits / stop bits / parity / flow control. Closes jeff141#14.
- Telnet (src/telnet.rs): TCP with RFC 854 IAC option negotiation
  (suppress-go-ahead, echo, NAWS window size), strips IAC from the data
  stream, escapes 0xFF in input, re-sends NAWS on resize, and tunnels
  through the existing SOCKS5/HTTP proxy. Closes jeff141#17.

Session gains a kind field (ssh/serial/telnet) plus serial settings;
old configs default to ssh. The session dialog gets a connection-type
selector and per-kind fields. Serial/Telnet have no SFTP or resource
monitor, so those panels are hidden/labelled accordingly.

CI installs libudev-dev for the serialport crate on Linux.

Verified: telnet end-to-end against a local echo server (IAC stripped,
bidirectional input/output), serial worker error path (open COM99 ->
localized error in terminal), and all three dialog modes render correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n shrink

The previous shrink fix scrolled the screen up by the full row delta and
saved that many top rows to scrollback. When the grid was not full (a
fresh shell with a few prompt lines and blank space below), this pushed
the real content off the top into history, leaving a blank screen with
the cursor stranded at the top; rapid up/down dragging repeated it until
the prompt was gone.

Now scroll up only by max(0, cursor_row+1 - new_rows): rows below the
cursor are unused blanks and truncate for free, so content and the prompt
stay put and rapid resizing is stable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Selection endpoints were stored as visible-window rows, so when the view
auto-scrolled during a drag (dragging past the bottom edge of a short
terminal to select more than one screen of scrollback) the anchor could
not stay pinned to content that scrolled off-window. Releasing then copied
only the final visible window, losing everything above it — so a
top-to-bottom selection of scrolled-back output never captured all of it.

Store the anchor and focus as absolute (combined_row, col) indices over the
virtual history+live buffer. vis_to_abs maps a visible row to its absolute
index for the current view; selection_rects_visible clips highlights to the
window; extract_selection_text reads the whole selection from the combined
buffer regardless of the current scroll position. Auto-scroll now just moves
the view and re-points the focus at the edge row, leaving the absolute anchor
fixed. Removes the old visible-coordinate helpers.

Unit tests cover the visible<->absolute mapping, history+live extraction,
view-independence of a copy, and highlight clipping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SFTP delete is irreversible (no trash), so a misclick on the context-menu
Delete item could silently destroy a file. Gate it behind an in-app
ConfirmDialog (ui/confirm_dialog.slint) styled to match the rest of
meatshell — a dark card over a dimmed backdrop, like SessionDialog, with a
red destructive button and the full path shown. The confirmation lives in
the UI layer: the context-menu delete now opens the modal and stashes the
target; the existing Rust delete only fires on confirm. Backdrop click or
Cancel dismisses without deleting.

Verified end-to-end: Delete opens the themed dialog; the red button removes
the file (status "已删除…", row disappears); Cancel/backdrop leaves it intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The download path was built directly from the server-supplied name via
base_name(), so a malicious SFTP server could craft a name with path
separators, shell-special characters, or a Windows reserved device name
(CON, NUL, COM1, ...) to write outside the chosen directory or target a
device. The open/edit flow already ran names through sanitize_filename();
the download path did not.

Run downloads through sanitize_filename, and harden that helper to also
neutralise Windows reserved device names (reserved case-insensitively and
even with an extension) and strip leading whitespace, while preserving Unix
dotfiles. Unit tests cover separators/traversal, special chars, reserved
names, whitespace trimming, and the empty fallback.

Closes jeff141#26. (jeff141#24, the resize-churn cursor bug, was already fixed by the
cursor-based shrink change.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The sidebar monitor runs a small loop over an SSH exec channel parsing
/proc and df output. Three hardenings against a malicious/compromised
server (which controls that output and the shell environment):

1. PATH hijacking: prepend "PATH=/usr/bin:/bin:/usr/sbin:/sbin; export PATH"
   to the monitor command so a server with a tampered PATH or BASH_ENV cannot
   shadow awk/cat/df/sleep. A fixed PATH is more portable than hardcoding one
   absolute path per tool; monitoring is best-effort either way.

2. Unbounded buffer: the reassembly buffer grew until the __MSTICK__ marker
   arrived. A server that streams data and never emits the marker could grow
   it without limit. Cap the leftover tail at 1 MiB and drop it past that.

3. Parser robustness: a server can put arbitrary values in /proc/stat,
   /proc/net/dev and df output. Sum CPU jiffies and the KiB->bytes multiply
   with saturating arithmetic (no debug overflow panic), and cap interfaces
   and filesystems at 64 per sample so a flood of fabricated rows cannot
   swamp the parser or sidebar.

Unit tests cover df saturation, CPU overflow values, and the interface cap.
Verified live that the PATH-reset monitor still reports CPU/mem/swap/net/disk.

Closes jeff141#27.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Uses linuxdeploy + linuxdeploy-plugin-appimage (APPIMAGE_EXTRACT_AND_RUN=1
so no FUSE needed in CI). Produces a single-file portable executable alongside
the existing .tar.gz; both are uploaded as workflow artifacts and attached to
GitHub Releases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on-error)

`--plugin appimage` conflicts with `--output appimage`; the output plugin is
invoked automatically by --output. Also added `continue-on-error: true` so a
future AppImage failure can't take the regular .tar.gz down with it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an image crate dependency (Linux-only, png feature only) and a
set_window_icon helper that decodes the bundled 512x512 PNG at startup
and hands it to slint::Window::set_window_icon. This makes the Wayland/
X11 dock show the correct icon when running the AppImage without
AppImageLauncher, or the bare binary without install-linux.sh.

Windows keeps its icon from the linked .ico; macOS from the app bundle --
neither is affected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
slint::Window has no set_window_icon method; replace with the winit
equivalent accessed via with_winit_window. Works on X11 sessions
(sets _NET_WM_ICON); is a no-op on Wayland by design.

修复 Linux 窗口图标:改用 winit API,slint::Window 并无
set_window_icon 方法,改为通过 with_winit_window 调用 winit 的同名
方法。X11 会话下生效(写入 _NET_WM_ICON);Wayland 下为空操作,符合预期。
Passwords in sessions.json are no longer stored in plaintext. On first
launch a random 256-bit key is written to secret.key in the same config
directory (mode 0600 on Unix). Each non-empty password is encrypted with
ChaCha20-Poly1305 (random 96-bit nonce per value) and stored as
"enc:v1:<base64url(nonce||ciphertext)>".

Legacy plaintext passwords from older installs are kept as-is in memory
and silently re-encrypted on the next save — no user action required.
Copying sessions.json alone is no longer sufficient to read credentials.

新增:密码静态加密,改用 ChaCha20-Poly1305 存储

sessions.json 中的密码不再明文保存。首次启动时在配置目录生成随机
256 位密钥文件 secret.key(Unix 下权限 0600)。每个非空密码使用
ChaCha20-Poly1305(每次随机 96 位 nonce)加密后以
"enc:v1:<base64url(nonce||密文)>" 格式写入 JSON。

旧版本的明文密码加载后保持原样,下次保存配置时自动加密,无需用户操作。
单独拿走 sessions.json 已无法还原密码。
- Add reactive light/dark theme system with sun/moon toggle button;
  defaults to system preference (dark-light crate), persisted in config
- Redesign ANSI 16-color palette: separate fg (ANSI16_LIGHT) and bg
  (ANSI16_LIGHT_BG) tables for light mode so slot-7 white stays readable
  as text while TUI background fills render as light pastels
- Add HSL-based transform for true-color (24-bit) backgrounds in light
  mode: dark RGB backgrounds (L < 0.45) are remapped to light pastels,
  making btop/htop/vim feel native to the light theme
- Add symmetric darken_light_fg() transform: light RGB foregrounds
  (L >= 0.55) are darkened so btop text stays readable on white background
- Store raw vt100::Color in HistSpan; convert at render time so theme
  switching retroactively recolors the entire scrollback history
- Bump version to 0.2.5
feat: 新增浅色/深色主题,TUI 程序自适应配色

- 添加响应式浅色/深色主题切换,顶栏太阳/月亮按钮一键切换;
  启动时跟随系统设置(dark-light crate),偏好持久化到配置文件
- 重设计 ANSI 16 色调色板:浅色模式下前景(ANSI16_LIGHT)与背景
  (ANSI16_LIGHT_BG)使用独立映射表,确保 slot-7 白色作为文字仍
  可读,TUI 填充块渲染为浅色粉彩风格
- 对真彩色(24-bit)背景添加 HSL 亮度重映射:浅色模式下深色 RGB
  背景(亮度 L < 0.45)自动映射为浅色,btop/htop/vim 在浅色主题下
  视觉风格统一
- 添加对称的 darken_light_fg():浅色 RGB 前景(L ≥ 0.55)压暗,
  确保 btop 文字在白色背景上可读
- HistSpan 改为存储原始 vt100::Color,渲染时再转换颜色,切换主题
  可即时重绘整个历史滚动缓冲区
- 版本升至 0.2.5
jeff141 and others added 25 commits June 19, 2026 20:49
SFTP 命令循环是串行的:上传/下载在循环里被 await,长传输会把后面的「列目录/
切目录」命令阻塞住,导致传输时无法切换目录。现在把 SftpSession 与连接句柄包进
Arc,上传/下载放到独立 tokio 任务里跑,命令循环空出来,切目录的 ListDir 能与
传输并发执行(SFTP 单通道支持请求流水线)。

The SFTP command loop is sequential — uploads/downloads were awaited inline, so
a long transfer blocked subsequent list/switch-directory commands. Wrap the
SftpSession and connection handle in Arc and run transfers on their own tokio
task, freeing the loop so a ListDir runs concurrently with the transfer (SFTP
pipelines requests over the one channel).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
端口转发可以多条规则,之前无法区分。给 PortForward 加了可选的名称字段:转发编辑器
新增「名称(可选)」输入,已添加的规则在列表里以高亮标签显示名称,便于区分不同服务。
旧配置无该字段时按空名兼容(serde default)。

Port forwards can have several rules that were hard to tell apart. Add an
optional name to PortForward: the editor gets a "Name (optional)" field and each
saved rule shows its name as a highlighted label in the list. Backward
compatible with old configs via serde default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eff141#118)

新增「界面缩放」设置(设置→界面,80%–200%),实时生效、持久化保存。Theme 里加了
ui-scale 系数,字号、间距、圆角都乘以它,因此整个界面(含图标字形)随之放大/缩小,
解决"图标/文字太小"的反馈。终端字体仍由其单独的字号设置控制,不受影响。
(注:此方案缩放的是 Theme 尺寸令牌;少量硬编码 px 的容器不随缩放,极端比例下可能略紧。)

Add a "UI scale" setting (Settings → Interface, 80%–200%) that applies live and
persists. A ui-scale factor in Theme multiplies all font sizes, spacing and
radii, so the whole interface (including icon glyphs) grows/shrinks — addressing
the "icons/text too small" feedback. The terminal font keeps its own size
setting. (Note: this scales Theme size tokens; a few hard-coded-px containers
don't follow, so they may feel tight at extreme scales.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jeff141#100)

SFTP 面板每行加了复选框,可勾选多个文件/文件夹。勾选后工具栏出现「✓N」计数和
两个按钮:批量下载(选一个目标文件夹,全部下载进去)、批量删除(走删除确认弹窗
的批量模式)。选择状态以模型为准(每个 entry 的 selected 标记),切换目录/刷新时
自动清空。

Each row in the SFTP panel gains a checkbox for selecting multiple files/folders.
Once anything is checked, the toolbar shows a "✓N" count plus two actions: batch
download (pick one destination folder, all go there) and batch delete (through
the delete-confirm dialog in batch mode). Selection lives in the model (a
per-entry `selected` flag) and clears on navigation/refresh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…otice (jeff141#100)

- 多选下载改为远端 tar 打包成单个压缩包再下载,下载后删除临时包,
  根治并发传输丢文件的问题;单选仍按普通文件下载、不压缩。
- 下载空文件夹时提示「空文件夹」而非创建空目录。
- 远端文件名经单引号转义后再拼入 tar/rm 命令,避免恶意服务端命令注入。

- Multi-select download now tars the selection into one archive on the
  remote, downloads it, then removes the temp — fixing the concurrent
  transfer that dropped files; a single selection still downloads as a
  plain, uncompressed file.
- Downloading an empty folder reports "Empty folder" instead of creating
  an empty local directory.
- Server-supplied names are single-quote escaped before tar/rm to avoid
  command injection from a malicious server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…i-checked, plain tar named after first file (jeff141#100)

- 之前打包只挂在工具栏「下载选中」按钮上,右键/单文件下载仍逐个下载。
  现改为:只要勾选了 ≥2 项,右键、行内按钮、工具栏的任何"下载"动作都
  打包整组(下载后自动清空勾选);勾 0/1 项时仍按点中的单文件下载。
- 压缩包改为纯 tar(不再 gzip),更快——用户不在意体积。
- 压缩包以第一个文件名命名,如 11.txt → 11等文件.tar(经文件名净化)。

- Archiving used to be wired only to the toolbar "download selected"
  button, so right-click / single-file download still pulled files one by
  one. Now ANY download (right-click, row button, toolbar) packs the whole
  checked set when 2+ are checked, then clears the selection; with 0/1
  checked it downloads the clicked file as-is.
- Archive is now a plain tar (no gzip) for speed — size is not a concern.
- Archive is named after the first item's stem, e.g. 11.txt -> 11等文件.tar
  (sanitized, since names come from the server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
)

- 多选打包下载时,远端 tar 还在跑、字节尚未开始传输的阶段,传输记录里
  立刻显示一行「文件准备中」(新增传输状态 3),避免大文件时的静默等待。
- 该行复用同一个传输 id,tar 完成开始下载后原地变为「下载中」进度条,
  再到「已完成」,无需新增一行。

- During multi-select archive download, a "Preparing..." row now appears
  in the transfer panel immediately while the remote tar runs and before
  any bytes flow (new transfer state 3), so a large selection is no longer
  a silent wait.
- The row reuses the same transfer id, so it turns into the live download
  progress bar in place once tar finishes, then "Done" — no extra row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…llable download with cleanup (jeff141#100)

慢的根因:下载是「读一块→等服务端→再读下一块」的串行流程,吞吐被往返
延迟卡死(上传早已流水线化,下载没有)。现把下载也改成专用 raw SFTP
通道 + 多请求并发在途(每个带绝对 offset,乱序返回也能正确落盘),延迟
被掩盖,大文件/打包包下载明显变快。

新增「取消下载」:
- 每个传输登记一个取消标志;传输记录每行在「下载中/文件准备中」时显示
  一个 × 按钮,点击即取消(新增状态 4「已取消」)。
- 取消/失败都会清理本地半成品文件;打包下载还会删除远端 /tmp 临时 tar,
  本地和线上都不留垃圾。
- 文件夹下载与「外部打开」用空标志,不受影响。

Root cause of the slowness: downloads read one chunk, waited for the
server, then read the next — throughput was capped by round-trip latency
(uploads were already pipelined, downloads were not). Downloads now use a
dedicated raw SFTP channel and keep many READ requests in flight at once
(each tagged with its absolute offset so out-of-order completion is fine),
hiding the latency. Large files and archive bundles download much faster.

New "cancel download":
- Each transfer registers a cancel flag; every transfer row shows an x
  button while active/preparing, which cancels it (new state 4 Cancelled).
- Cancel and error both remove the partial local file; archive downloads
  also delete the remote /tmp temp tar — no junk left locally or remote.
- Folder download and "open externally" pass a no-op flag, unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ll-text-invisible on macOS 26 (jeff141#108)

macOS 26 (Tahoe) 改了 CoreText 行为,Slint 默认的 femtovg 渲染器取字失败,
导致界面所有文本(含内嵌等宽字体)全部消失。macOS 15 不受影响,所以朋友的
M4 没复现。Skia 字体路径更稳,也是 Slint 在 macOS 上的推荐渲染器。

- 仅在 macOS 通过 target 条件依赖引入 renderer-skia 特性,Windows/Linux 完全
  不编译 Skia、继续用 femtovg。
- main.rs 在 macOS 启动时(未显式指定时)设置 SLINT_BACKEND=winit-skia。

macOS 26 (Tahoe) changed CoreText behaviour and Slint's default femtovg
renderer failed its font lookup, so all UI text (including the embedded
mono font) vanished. macOS 15 is unaffected, which is why it didn't repro
on the reporter's friend's M4. Skia has a more robust font path and is
Slint's recommended renderer on macOS.

- Pull in the renderer-skia feature only on macOS via a target-conditional
  dependency; Windows/Linux never compile Skia and keep femtovg.
- main.rs sets SLINT_BACKEND=winit-skia on macOS at startup (unless already set).

Note: needs a macOS CI build to confirm Skia compiles — watch the
x86_64-apple-darwin cross-compile job in particular. Reporter on macOS 26
must verify the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SFTP 多选打包下载、可取消传输、下载提速、端口转发命名、界面缩放,
修复 macOS 26 文本消失 (jeff141#108) 与欢迎页会话列表滚动 (jeff141#116)。

SFTP multi-select archive download, cancellable transfers, faster
downloads, named port-forward rules, global UI scale; fixes macOS 26
all-text-invisible (jeff141#108) and welcome session-list scrolling (jeff141#116).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Slint 的文本排版会用到 ICU4X,而 icu_provider 在未开启 logging feature 时,
其内部 log 宏直接退化为 std::eprintln!,于是 stderr 被
「ICU4X data error: No segmentation model for language: ja」刷屏(绕过 tracing,
RUST_LOG 也压不住),release 版同样如此。

- 打开 icu_provider 的 logging feature,使这些诊断改走真正的 log crate,
  经 tracing-log 桥接进我们的 tracing 订阅器。
- init_tracing 在 stderr 与文件层都静音 icu_provider/icu_segmenter/icu_normalizer
  目标,彻底消除噪音。

Slint's text layout pulls in ICU4X; with icu_provider's logging feature off,
its internal log macro degrades to a raw std::eprintln!, flooding stderr with
"ICU4X data error: No segmentation model for language: ja" (bypassing tracing, so
RUST_LOG could not suppress it) — in release builds too.

- Enable icu_provider's logging feature so those diagnostics go through the real
  log crate and into our tracing subscriber via the tracing-log bridge.
- init_tracing silences the icu_provider/icu_segmenter/icu_normalizer targets on
  both the stderr and file layers, removing the noise entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eff141#100)

之前只有下载能取消,上传不行。现在单文件上传也注册取消标志,传输记录里的
取消按钮对上传同样生效;取消后删除远端没传完的半成品文件,并刷新目录列表。
上传失败时同样清理远端残留。文件夹上传用空标志,不可单独取消。

Only downloads were cancellable before. Single-file uploads now register a
cancel flag too, so the transfer row's cancel button works for uploads as
well; cancelling removes the half-written remote file and refreshes the
listing. A failed upload likewise cleans up the remote partial. Folder
uploads pass a no-op flag (not individually cancellable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r, fix macOS text-invisible (jeff141#129)

0.4.10 为绕过 macOS 26 上 femtovg 取字失败 (jeff141#108),在 macOS 强制改用 Skia
渲染器。该改动未经真机验证就发布,反而弄坏了另一批 Mac (Apple Silicon /
macOS 26.5):Skia 无法解析「PingFang SC」界面字体,导致这些机器上文字全部
消失(图标因使用内嵌字体而正常)。

现默认改回 femtovg(对绝大多数机器正常);macOS 仍编译 renderer-skia,
femtovg 失效的机器可在启动时用 SLINT_BACKEND=winit-skia 手动启用。

顺带发布 0.4.11:含上传可取消并清理远端 (jeff141#100)、屏蔽 ICU4X 噪音日志、
README 赞助入口。

0.4.10 force-set the Skia renderer on macOS to work around femtovg failing
to render text on macOS 26 (jeff141#108). That fix shipped without on-device
verification and broke a different set of Macs (Apple Silicon / macOS 26.5):
Skia could not resolve the "PingFang SC" UI font, so all text vanished there
instead (icons survived because they use an embedded font).

The default now stays femtovg (known-good for the majority); renderer-skia is
still compiled in on macOS so users for whom femtovg fails can opt in at
launch with SLINT_BACKEND=winit-skia.

Also cuts release 0.4.11: cancellable uploads with remote cleanup (jeff141#100),
silenced ICU4X log noise, README sponsor link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…K UI font to Heiti SC, fix macOS 26 blank text; release 0.4.12 (jeff141#129)

根因最终定位:部分 macOS 26 机器上 femtovg 无法栅格化新版系统中文字体
(PingFang SC、Hiragino)——fontdb 能找到它们,但每个字形都画成空白;而老字体
Heiti SC / STHeiti / Songti SC 渲染完全正常(已在 M2 / macOS 26 上逐字体肉眼实测)。

既不是渲染器(0.4.11 单独退回 femtovg 没用),也不是字体加载(fontdb 加载了 906
个 face、按名也能查到 PingFang),纯粹是 PingFang/Hiragino 这类字体 femtovg 画不出。

改法:
- 启动时用 fontdb 探测,macOS 候选清单改为优先 Heiti SC(干净黑体、所有 macOS
  自带),STHeiti / Songti SC 作后备,PingFang/Hiragino 降为末位。
- 一个系统 CJK 字体都匹配不到时,回退内置 "Meatshell Mono",保证窗口永不全白。
- 新增 MEATSHELL_UI_FONT="<字体名>" 环境变量,免重编强制指定任意字体(诊断/逃生)。
- 打一行 WARN 摘要(加载到几个 face + 选中哪个字体)进 error.log,便于诊断。

Root cause finally pinned: on some macOS 26 machines femtovg cannot rasterize the
modern system CJK fonts (PingFang SC, Hiragino) — fontdb finds them but every
glyph comes out blank — while the older Heiti SC / STHeiti / Songti SC faces
render perfectly (verified per-font on an M2 / macOS 26). It was never the
renderer (0.4.11's femtovg revert alone didn't help) nor font loading (fontdb
loaded 906 faces and resolved PingFang by name).

Change:
- Probe fontdb at startup; the macOS candidate list now prefers "Heiti SC" (a
  clean sans-serif present on every macOS), with STHeiti / Songti SC as fallbacks
  and PingFang/Hiragino demoted to last.
- If no system CJK family matches, fall back to the embedded "Meatshell Mono" so
  the window is never blank.
- Add a MEATSHELL_UI_FONT="<family>" env override to force any family without a
  rebuild (diagnostics / escape hatch).
- Log a one-line WARN summary (face count + chosen font) to error.log.

Also bumps the release to 0.4.12.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…groups (jeff141#55)

快捷命令新增可选分组,体验对齐欢迎页的会话分组:

- 数据:QuickCommand 增加 group 字段(serde default,旧配置兼容);留空归入隐式
  「default」分组。
- 命令栏弹窗:按分组渲染,每组带标题,点击收起/展开(运行时状态,不持久化)。
- 管理对话框:新增「分组(可选)」输入框;列表按分组显示分组头。
- 分组会重排显示顺序,删除改用 orig-index 回指原始存储位置,避免删错条目。
- 新增 toggle-quick-group 回调 + on_toggle_quick_group 处理(用运行时 HashSet
  记录已收起的分组)。
- 补 zh 翻译:分组(可选)、留空 → 默认分组。

Quick commands gain an optional group, mirroring the welcome page's session
groups:

- Data: QuickCommand gets a `group` field (serde default → backward compatible);
  empty means the implicit "default" group.
- Command-bar popup: rendered grouped, each group has a clickable header to
  collapse/expand (runtime-only state, not persisted).
- Manage dialog: new "Group (optional)" field; list shows group headers.
- Grouping reorders the display, so delete now uses orig-index pointing back to
  the stored entry to avoid deleting the wrong one.
- New toggle-quick-group callback + on_toggle_quick_group handler (collapsed
  groups tracked in a runtime HashSet).
- zh translations added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uick commands with full group ops (jeff141#55)

把欢迎页快速连接会话的整套右键管理搬到快捷命令的管理对话框:

- 右键命令:编辑(进编辑模式,Add 变 Save)、复制、删除、移动到分组。
- 右键分组标题:重命名分组、删除空分组、新建分组;另加「+ 新建分组」按钮。
- 数据:config 增加 quick_groups(显式分组,支持空分组持久化),并新增
  add/remove/rename_quick_group、update_quick_command 方法。
- 模型:空分组渲染为 orig-index = -1 的占位头行,可被重命名/删除(对齐空会话文件夹)。
- 移动到「default」= 清空分组名;分组重排后删除用 orig-index 回指原始条目。
- 新增回调与处理:edit/save/duplicate/move-quick-command、submit/delete-quick-group。

Ports the welcome page's quick-connect right-click management onto the quick-
command manage dialog:

- Right-click a command: Edit (edit mode — Add becomes Save), Duplicate, Delete,
  Move to group.
- Right-click a group header: Rename group, Delete (empty) group, New group; plus
  a "+ New group" button.
- Data: config gains `quick_groups` (explicit groups so empty ones persist) and
  add/remove/rename_quick_group + update_quick_command helpers.
- Model: an empty group is a placeholder header row (orig-index -1) that can be
  renamed/deleted, mirroring empty session folders.
- Move to "default" clears the group; deletes use orig-index since grouping
  reorders the display.
- New callbacks + handlers: edit/save/duplicate/move-quick-command,
  submit/delete-quick-group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…groups collapsed by default (jeff141#55)

上一版把右键菜单只加在管理对话框,但用户是在命令栏弹窗上操作,那里没有右键
菜单(只有左键运行/收起),所以右键没反应。

- 把整套右键菜单搬进命令栏弹窗:右键命令 → 编辑/复制/删除/移动到分组;右键分组
  标题 → 重命名/删除空分组/新建分组。TouchArea 同时带 clicked + pointer-event,
  与会话列表一致(只有 pointer-event 时在 Flickable 内不抓取右键)。
- 管理对话框的右键 TouchArea 也补上 clicked,保证在 Flickable 内右键生效。
- 分组默认全收起:启动时把所有分组名放进收起集合(init 渲染与运行时集合一致)。
- 新增 TerminalView 回调并在 app.slint 转发到 root:edit/duplicate/delete/move-
  quick-command、new/rename/delete-quick-group。
- 补 zh 翻译「重命名分组」。

The previous commit only added the right-click menus to the manage dialog, but
the user operates in the command-bar popup, which had no right-click menu (only
left-click run / collapse) — so right-click did nothing.

- Port the full right-click menus into the popup: right-click a command for
  Edit/Duplicate/Delete/Move-to; right-click a group header for
  Rename/Delete-empty/New group. Each TouchArea now has both clicked and
  pointer-event, matching the session list (a pointer-event-only TouchArea
  doesn't grab right-clicks inside a Flickable).
- The manage-dialog right-click TouchAreas got a clicked grab too.
- Groups collapsed by default: seed the collapse set with all group names at
  startup (init render and runtime set agree).
- New TerminalView callbacks forwarded to root in app.slint.
- zh translation for "Rename group".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k log noise

- 过滤器新增 fontdb=error:屏蔽 fontdb 对无法解析、但会自动跳过的系统字体发出的
  「malformed font」WARN(如 Windows 的 mstmc.ttf);保留 error 级别以便真正的错误仍可见。
- 把 resolve_ui_font_family 里"命中系统字体 / MEATSHELL_UI_FONT 覆盖"两条日志从
  warn 降为 debug;只有"找不到任何系统 CJK 字体、回退内置"的失败路径仍保留 warn
  (继续进 error.log,便于诊断)。
- silence_icu 改名 quiet_noise(现在同时管 ICU4X 与 fontdb)。

- Add fontdb=error to the log filter so fontdb's harmless "malformed font" WARN for
  fonts it skips (e.g. Windows mstmc.ttf) is suppressed while real errors stay.
- Demote the "using system CJK font" / MEATSHELL_UI_FONT override lines in
  resolve_ui_font_family from warn to debug; only the fallback failure path stays
  at warn (still lands in error.log for diagnostics).
- Rename silence_icu -> quiet_noise (now covers both ICU4X and fontdb).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r SFTP key, show import result (jeff141#133)

问题1:私钥带口令时,终端能连上但 SFTP 目录树加载失败,报「failed to load
key... the key is encrypted」。原因是 SFTP 单独建连时调用 load_secret_key 传了
None,没把口令传进去;而 shell 会话用的是 Some(password)。改为和 shell 一致:
复用会话 password 字段作口令(空=未加密)。

问题2:「导入 ~/.ssh/config」点了像没反应。其实导入有执行,但结果提示
(ssh-import-hint)只显示在欢迎页;用户开着会话时欢迎页被终端盖住,看不到反馈。
改为:点击后不再关闭设置菜单,并把结果(已导入 N / 没有新主机 / 未找到)直接显示
在菜单项下方;打开设置时清空旧提示。

Problem 1: with a passphrase-protected private key the shell connects but the
SFTP tree fails with "failed to load key... the key is encrypted". SFTP's
dedicated connection called load_secret_key with None instead of the passphrase
(the shell passes Some(password)). Reuse the session password field as the
passphrase (empty = unencrypted), matching the shell.

Problem 2: "Import ~/.ssh/config" looked like a no-op. The import ran, but its
result hint only rendered on the welcome page, which is hidden behind an active
session. Keep the settings menu open on click and show the result (imported N /
no new hosts / not found) inline under the item; clear the stale hint when the
menu opens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ble process window, redesigned settings

- 侧栏:在资源面板与主区之间加可拖动分隔条,宽度可在 160–520px 间调节并持久化到配置(重启保留);折叠时分隔条隐藏,拖动期间禁用折叠动画以跟手。
- 窗口:初始尺寸 1200×760 → 1440×900,对齐同类客户端的舒适默认值。
- 进程监视:从内嵌浮层提升为真正的独立 OS 窗口(可拖出父窗口/拖到第二屏),无边框自绘标题栏 + 缩放手柄;两窗口共享同一 VecModel 实时刷新;修复独立窗口主题(Theme 为每窗口 global)与 CJK 字体未同步的问题。
- 设置-界面:右侧改为「分区 + 标签左·控件右」的简约布局(iOS 风开关、[− 值 +] 步进器、固定紧凑字号,不随界面缩放放大);保留内嵌模态浮层,打开时遮罩吞鼠标 + 抢焦点吞键盘,禁止任何输入,卡片仅能在窗口内拖动。
- Sidebar: draggable splitter between the resource panel and main area; width adjustable within 160–520px and persisted to config (survives restart); the splitter hides when collapsed and the collapse animation is disabled while dragging for 1:1 tracking.
- Window: initial size 1200×760 → 1440×900, matching the comfortable default of comparable clients.
- Process monitor: promoted from an in-app overlay to a real top-level OS window (draggable outside the parent / onto a second monitor) with a frameless custom titlebar and resize grip; both windows share one VecModel for live updates; fixes detached-window theme (Theme is a per-window global) and CJK font not being synced.
- Settings → Interface: right pane redesigned into a clean "section + label-left · control-right" layout (iOS-style switches, [− value +] steppers, fixed compact typography that ignores UI scale); kept as an embedded modal overlay that blocks all input while open (veil swallows mouse, focus scope swallows keys) and whose card can only be dragged within the window.
… back to keyboard-interactive on password-auth failure (jeff141#86)

JumpServer 等堡垒机默认只放行 keyboard-interactive、不放行 password 方法,导致
直接"认证失败"——Xshell/MobaXterm/WindTerm 能登正是因为会自动回退。本次:
密码认证失败后,断开并重连一条全新连接,再用 keyboard-interactive 以密码应答
服务器提示。注意:russh 在一次失败的认证后无法在同一句柄上切换认证方法(会
卡死),因此必须重连;为此抽出 connect_ssh() 复用连接逻辑。已在真实的
keyboard-interactive-only sshd 上验证登录成功。
JumpServer and many bastions disable the `password` SSH method and only accept
keyboard-interactive, so plain password auth fails outright — other clients get
in because they fall back automatically. Now: on password-auth failure,
disconnect and reconnect on a fresh handle, then authenticate via
keyboard-interactive answering each prompt with the password. russh hangs if a
second auth method is attempted on a handle whose first attempt already failed,
so a reconnect is required; connect_ssh() is factored out to reuse the connect
path. Verified against a real keyboard-interactive-only sshd.
… back to keyboard-interactive on password-auth failure (jeff141#86)

JumpServer 等堡垒机默认只放行 keyboard-interactive、不放行 password 方法,导致
直接"认证失败"——Xshell/MobaXterm/WindTerm 能登正是因为会自动回退。本次:
密码认证失败后,断开并重连一条全新连接,再用 keyboard-interactive 以密码应答
服务器提示。注意:russh 在一次失败的认证后无法在同一句柄上切换认证方法(会
卡死),因此必须重连;为此抽出 connect_ssh() 复用连接逻辑。已在真实的
keyboard-interactive-only sshd 上验证登录成功。
JumpServer and many bastions disable the `password` SSH method and only accept
keyboard-interactive, so plain password auth fails outright — other clients get
in because they fall back automatically. Now: on password-auth failure,
disconnect and reconnect on a fresh handle, then authenticate via
keyboard-interactive answering each prompt with the password. russh hangs if a
second auth method is attempted on a handle whose first attempt already failed,
so a reconnect is required; connect_ssh() is factored out to reuse the connect
path. Verified against a real keyboard-interactive-only sshd.
…e + SFTP) + layout persistence

- 停靠:资源面板与 SFTP 面板均可拖到四条边(上/下/左/右);拖动手柄时四边浮现高亮放置
  区,松手即吸附。两个面板都可拖动调大小,折叠后缩成停靠边缘的一个小展开按钮(彻底隐藏)。
- 自适应:资源面板横向停靠时小组件改为横排;SFTP 竖向(窄)停靠隐藏目录树,并随宽度
  渐进隐藏「大小→时间」列(名称将被挤断时才让位),横向(宽)停靠则恒显示全部列;SFTP
  工具栏新增专用拖动手柄,密集控件下也好抓。
- 持久化:两面板的停靠边/尺寸 + 父窗口大小,退出保存、启动恢复,保留用户偏好布局。
- 修复:macOS 欢迎页标题/副标题/卡片被拉开错位——Welcome 显式填满内容区、头部钉顶。
- 发版:0.4.13 → 0.4.14,更新 CHANGELOG。

实现要点:用绝对几何把面板按 dock 状态铺到边缘(资源面板在窗口级 dock-area、SFTP 在
终端区级 dock-region),中央内容包裹进 clip 容器避免溢出;拖动吸附复用「绝对光标 −
容器原点」的稳定坐标;resize 直接以光标在容器内的位置定位面板边缘(避免增量累积);
持久化集中在退出路径的 save_layout()。

- Docking: both the resource panel and the SFTP panel can be dragged to any edge
  (top/bottom/left/right); dragging the handle highlights drop zones on all four edges
  and releasing snaps it there. Both are drag-resizable and collapse to a small expand
  button on the docked edge (fully hiding the panel).
- Responsive: the resource panel lays its widgets out in a row when docked horizontally;
  the SFTP panel hides its directory tree when docked vertically (narrow) and
  progressively drops the Size → Modified columns as it narrows (only once Name would
  elide), while a horizontal (wide) dock always shows every column. A dedicated drag
  grip was added to the SFTP toolbar so it's grabbable despite the busy toolbar.
- Persistence: each panel's docked edge/size and the window size are saved on exit and
  restored next launch, keeping the user's preferred layout.
- Fix: macOS welcome page had its title/tagline/card spread apart — Welcome now fills
  the content area explicitly and the header is pinned to the top.
- Release: bump 0.4.13 → 0.4.14, update CHANGELOG.

Implementation: panels are placed by absolute geometry per their dock state (the
resource panel in a window-level dock-area, SFTP in a terminal-level dock-region), with
the central content wrapped in a clipping container to prevent overflow; drag-to-dock
reuses the stable "absolute cursor − container origin" coordinate; resize positions the
panel edge directly at the cursor within the container (no incremental accumulation);
persistence is centralised in save_layout() on every exit path.
…0.61 with API migration

- Cargo.toml: russh 0.49 → 0.61(features=[ring]), 移除 russh-keys, ssh-key 0.6 → 0.7.0-rc.10
- Handler 不再用 #[async_trait](trait 用 fn -> impl Future)
- Auth 返回 AuthResult → 末尾加 .success()
- PrivateKeyWithHashAlg::new() 返回 struct 而非 Result
- Kb::Failure 改为 struct variant → Kb::Failure { .. }
- Handler 新增方法(extended_data / exit_status / channel_eof / channel_close)有默认实现,无需覆盖
- 移除已删除方法留下的 unused import(ChannelId)
@kodingsky kodingsky changed the title chore(deps): russh 升级 0.49 → 0.61 + 对应 API 迁移 / upgrade russh 0.49 → … chore(deps): russh 升级 0.49 → 0.61 + 对应 API 迁移 Jun 22, 2026
@kodingsky

Copy link
Copy Markdown
Author

@jeff141 russh 0.49 包含几个高危漏洞 https://www.cve.org/CVERecord/SearchResults?query=russh ,是否有计划合并PR或升级 russh?

jeff141 added a commit that referenced this pull request Jun 25, 2026
0.4.17 之后的改动汇总(详见 CHANGELOG):
- Win11 无边框窗口圆角 + 投影 (#162/#166)
- macOS 上 app 快捷键改用 Cmd,释放 Ctrl 给终端 (#158)
- SFTP 闲置失效修复(加 keepalive)(#160)
- git clone/curl 输出乱折行修复(PTY resize 防抖)(#163)
- 设置/下载下拉跟随按钮、浅色文字加深、展开按钮旁黑块等界面修复
- 记录 RUSTSEC-2026-0154 不可达、暂不升级 russh (#151)

chore(release): release 0.4.18

Changes since 0.4.17 (see CHANGELOG):
- Win11 frameless rounded corners + drop shadow (#162/#166)
- macOS app shortcuts use Cmd, freeing Ctrl for the terminal (#158)
- fix SFTP dying after idle via a connection keepalive (#160)
- fix git clone/curl output wrapping at ~10 cols via PTY-resize debounce (#163)
- UI fixes: dropdowns follow their buttons, darker light-mode text, no wallpaper
  black block behind the expand button
- document RUSTSEC-2026-0154 as unreachable; hold russh (#151)
jeff141 added a commit that referenced this pull request Jul 3, 2026
0.4.17 之后的改动汇总(详见 CHANGELOG):
- Win11 无边框窗口圆角 + 投影 (#162/#166)
- macOS 上 app 快捷键改用 Cmd,释放 Ctrl 给终端 (#158)
- SFTP 闲置失效修复(加 keepalive)(#160)
- git clone/curl 输出乱折行修复(PTY resize 防抖)(#163)
- 设置/下载下拉跟随按钮、浅色文字加深、展开按钮旁黑块等界面修复
- 记录 RUSTSEC-2026-0154 不可达、暂不升级 russh (#151)

chore(release): release 0.4.18

Changes since 0.4.17 (see CHANGELOG):
- Win11 frameless rounded corners + drop shadow (#162/#166)
- macOS app shortcuts use Cmd, freeing Ctrl for the terminal (#158)
- fix SFTP dying after idle via a connection keepalive (#160)
- fix git clone/curl output wrapping at ~10 cols via PTY-resize debounce (#163)
- UI fixes: dropdowns follow their buttons, darker light-mode text, no wallpaper
  black block behind the expand button
- document RUSTSEC-2026-0154 as unreachable; hold russh (#151)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants