diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 00000000..0d6cb8cd --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,27 @@ +# cargo-audit 豁免配置(CI 使用 `cargo audit --deny warnings --file Cargo.lock`)。 +# +# 当前锁文件包含 10 条「unmaintained」警告级告警,均来自 Linux 托盘 +# 依赖链(tray-icon → libappindicator/muda → gtk-rs GTK3 绑定)及一条 +# 传递依赖(proc-macro-error)。该链是 Linux 托盘的事实标准实现,且 +# 仅 Linux 构建引入;Windows/macOS 产物不受影响。替换托盘后端 +# (如 ksni)前无法消除,详见 AUDIT_REPORT 第 5 节「GTK3 绑定告警」。 +# +# 注意:这些是"不再维护"告警而非可利用漏洞(0 vulnerabilities); +# 任何新的安全漏洞告警仍会被 CI 拦截。 + +[advisories] +ignore = [ + # gtk-rs GTK3 绑定整体停止维护(8 条,Linux 托盘链) + "RUSTSEC-2024-0412", # gdk + "RUSTSEC-2024-0413", # atk + "RUSTSEC-2024-0415", # gtk + "RUSTSEC-2024-0416", # atk-sys + "RUSTSEC-2024-0418", # gdk-sys + "RUSTSEC-2024-0419", # gtk3-macros + "RUSTSEC-2024-0420", # gtk-sys + "RUSTSEC-2024-0429", # glib + # gtk-rs 传递依赖,随 GTK3 链停止维护 + "RUSTSEC-2024-0436", # paste + # toml_edit 早期版本的传递依赖(构建期宏),上游无替代版本可用 + "RUSTSEC-2024-0370", # proc-macro-error +] diff --git a/.github/release-notes/v1.0.0.zh-CN.md b/.github/release-notes/v1.0.0.zh-CN.md new file mode 100644 index 00000000..feef9c7e --- /dev/null +++ b/.github/release-notes/v1.0.0.zh-CN.md @@ -0,0 +1,32 @@ +# JShell v1.0.0 + +JShell 首个正式版本。自 v0.1.0-beta 系列以来,SSH 主机密钥安全、代理体系、SFTP 稳定性、远程编辑器韧性、配置同步、系统托盘与发布工程逐步完善,本版本正式发布。 + +## 本次更新 + +- 系统托盘与后台运行:关闭窗口转为后台驻留,托盘菜单可显示窗口或退出;设置中可开关。 +- 单实例锁:重复启动时通知已运行实例并立即退出,避免多实例冲突;跨用户隔离。 +- 信号优雅退出:SIGTERM/SIGINT 走完整退出流程(含未保存文档提示与配置落盘);二次信号可强制终止。 +- 测试与质量体系:新增 30 余项自动化测试(目录归档解包穿越回归、远程命令引用对抗用例、`~/.ssh/config` 解析边界、日志高亮规则、中英文键对齐),测试总数达 400 项;新增 `.cargo/audit.toml` 依赖审计豁免配置,CI 质量门禁恢复绿灯;提供 `scripts/verify.sh`(本地一键验证)与 `scripts/smoke-linux.sh`(托盘/单实例/信号冒烟)。 +- 安全加固:日志目录权限收紧为仅属主可读写;归档解包路径穿越防护以回归测试锁定;远程路径引用无法逃逸单引号上下文。 +- 修复:英文界面缺失「确认」按钮文案;`~/.ssh/config` 支持 tab 分隔与端口 0 回退。 +- 新增 `docs/TEST_PLAN.md`(六类测试计划:功能、性能、兼容性、安全、回归、易用性)与 `docs/RELEASE_READINESS.md`(发布就绪清单与后续优化方向)。 + +## 连接与安全 + +- SSH 与 SFTP 采用 `accept-new` 语义:自动接受无历史记录的新主机密钥,绝不覆盖、删除或追加与现有记录冲突的密钥;`@revoked`、否定主机模式与损坏文件一律失败关闭。 +- 代理支持无代理/直连/继承/自定义四级策略,协议覆盖 SOCKS5、SOCKS5H、HTTP 与经证书校验的 HTTPS CONNECT。 +- 配置同步(WebDAV/S3/Cloudflare R2)载荷客户端加密,可移植载荷经白名单校验,不包含本机敏感字段。 +- 依赖审计 0 漏洞;Linux 托盘依赖链的 10 条「不再维护」告警已按编号豁免并记录原因(详见 `.cargo/audit.toml`)。 + +## 已知限制 + +- Linux 托盘左键不响应(appindicator 上游限制),请使用托盘菜单显示/隐藏窗口;Windows/macOS 左键为恢复窗口。 +- GNOME Wayland 下最小化为合成器请求,可能被忽略;应用仍安全驻留后台。 +- 首次连接不弹出主机指纹确认窗口,高安全环境请通过独立渠道核验服务器指纹。 +- Windows/macOS 发布包暂为未签名/临时签名状态,首次运行可能触发系统安全提示。 +- 系统关机/注销时 Windows 尚未处理 WM_ENDSESSION,请先手动退出以保存布局(详见发布审计)。 + +## 升级与故障处理 + +从任意 beta 版本升级无需迁移配置。旧版 `ashell` 配置目录在首次启动时自动迁移。若遇到主机密钥变更,请核验新指纹后运行 `ssh-keygen -R ` 清理记录并重新连接。 diff --git a/.gitignore b/.gitignore index a927f6be..7a505608 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ /target-release-*/ /.superpowers/ .DS_Store + +# makepkg artifacts +/packaging/arch/src/ +/packaging/arch/pkg/ +/packaging/arch/*.pkg.tar.* + +# local tooling +/.zcode/ diff --git a/AUDIT_REPORT.md b/AUDIT_REPORT.md index 55952edb..d23f29f3 100644 --- a/AUDIT_REPORT.md +++ b/AUDIT_REPORT.md @@ -1,95 +1,86 @@ # JShell 项目审计报告 -- 审计日期:2026-08-01 -- 审计对象:`0.1.0-beta.2` 本地发布候选 -- 审计范围:当前修改涉及的分屏、配置写入、R2 手动同步、凭据保护、载荷校验、发布工作流、文档与 Release 产物 -- 范围说明:本轮按用户要求只复审当前修改及其直接依赖,不重新审计未改动的全部业务模块。 +- 审计日期:2026-08-13 +- 审计对象:工作树中未提交的系统托盘 + 后台运行、单实例锁、信号优雅退出三组改动 +- 审计范围:`src/app/tray.rs`、`src/app/single_instance.rs`、`src/app/signals.rs`(全部新增)以及 `src/app/mod.rs`、`src/app/startup.rs`、`src/app/dialogs.rs`、`src/main.rs`、`src/session/config.rs`、`locales/*.yml`、`Cargo.toml/Cargo.lock` 的关联修改,及这些改动与既有关闭/退出流程、配置写入协调器、同步合并逻辑的交互 +- 范围说明:按惯例只复审本次改动及其直接依赖(tray-icon/muda、interprocess、signal-hook、gtk),未重新审计未改动的全部业务模块 +- 历史报告:上一轮 `0.1.0-beta.2` 发布候选审计记录保留在 [docs/RELEASE_AUDIT_0.1.0-beta.2.md](docs/RELEASE_AUDIT_0.1.0-beta.2.md) ## 1. 审计结论 -当前修改范围内未发现仍未处理的 P0 或 P1 问题。此前审查发现的嵌套分屏比例、配置并发写盘、同步凭据部分提交、旧预览/冲突误用、非法快捷键载荷崩溃风险和 Release 工作流权限过宽均已修复并加入回归测试。 +本次审计共发现 5 处问题,全部已修复并复验;当前无未处理的 P0/P1/P2 问题。核心结论: -`RUSTSEC-2023-0071` 对应的 RustCrypto `rsa` crate 不在最终锁定依赖图中。2026-08-01 获取的 RustSec 官方公告库提交 `685d32fd681b540aa64019820639613c5a4fd922` 已用于本地审计,1177 条公告扫描 1024 个锁定依赖后返回成功。 +- **无内存安全问题**:事件泵在 `remove_window()` 之后继续访问实体是安全的——该 fork 的 `Window::remove_window` 只置 `removed` 标记,窗口与实体在本次 update 闭包返回后才销毁(`app.rs` `trail()`);泵循环下一次迭代 `update_in` 报错即退出,无 use-after-drop。 +- **无死锁路径**:全局 `REQUEST_TX` 互斥锁仅覆盖一次非阻塞 `mpsc::send`;Linux 托盘线程 Drop 时置标志后 join,最多等待一个泵间隔(16ms),且与锁无嵌套。 +- **修复了 1 个功能性缺陷(P1)**:首次信号后信号处理器未恢复默认处置,若优雅退出卡死(如等待保存提示),后续 SIGTERM/SIGINT 将无法强制终止进程。 +- **修复了 1 个多用户正确性问题(P2)**:Linux 抽象命名空间套接字与 macOS `/tmp` 路径对全系统可见,多用户同机时第二个用户永远无法启动(连接会命中他人实例并退出);现按 UID 隔离。 +- 依赖审计 0 漏洞;新增依赖仅引入 8 条 GTK3 绑定"不再维护"告警(Linux 专用,Windows/macOS 构建不引入),属警告级且为托盘生态事实标准栈。 -本地 Release 已重新构建并通过隔离配置启动冒烟。当前仍未执行的是分批提交、推送后的 GitHub Actions、`v0.1.0-beta.2` 标签和 GitHub Release;因此本报告将该版本判定为“本地发布候选已通过门禁,待推送与托管发布验证”,不声明已经发布。 +## 2. 发现与修复 -## 2. 本轮修复 +### 2.1 P1:信号处理器未关闭,二次信号无法强制退出 -### 2.1 分屏与界面状态 +- `signals.rs` 原实现 `for signal in signals.forever() { ...; break; }` 除触发 clippy `never_loop`(deny 级,编译失败)外,`Signals` 迭代器析构不会自动注销处理器。 +- 修复:取首个信号 → 转发退出请求 → `handle.close()` 恢复默认处置。此后第一个信号优雅退出,第二个信号恢复默认行为可强制杀死,符合"优雅优先、可逃生"语义。 -- 每个嵌套 split 容器记录自己的实际 Bounds,拖动比例不再错误使用整个 pane 根区域。 -- 回归场景固定为:根宽 1000、嵌套宽 200、水平拖动 20 像素,ratio 变化 10%。 -- 保留 5 像素死区、10%-90% 限制、释放鼠标后清理拖动状态。 +### 2.2 P2:单实例套接字跨用户冲突 -### 2.2 配置写入一致性 +- Linux 抽象套接字无权限隔离,macOS 落盘于共享 `/tmp`:用户 A 的实例会让用户 B 的每次启动误判为"第二实例"并直接退出。 +- 修复:Unix 平台套接字名追加 `getuid()`(`libc` 提升为正式依赖,版本已在锁文件中);`remove_stale_socket` 使用同一命名函数。 +- Windows 命名管道保留固定名:跨用户连接失败时会走 3 次重试 → 降级无锁运行(仅失去互斥,不会无法启动)。 -- 新增统一的配置写入协调器,后台偏好保存与会话、文件夹、传输记录、导入配置、窗口布局和同步写盘共用串行锁与版本屏障。 -- 同步或其他直接写入会淘汰更早排队的偏好快照,避免旧快照覆盖新 sessions、ETag 或偏好。 -- 所有生产路径中的直接 `ConfigStore::save()` 已接入统一协调器;初始化迁移和单元测试除外。 +### 2.3 P2:单实例监听线程创建失败会 panic -### 2.3 R2、凭据与预览事务 +- 原实现 `spawn(...).expect(...)`。修复为失败时记日志并释放监听器(套接字随 Drop 释放),本实例降级无锁运行,不影响启动。 -- 上传成功和下载确认先验证并更新系统凭据库,再原子写入本地配置;任一步失败都会保留或恢复旧状态。 -- 本地配置写入失败时恢复先前的同步密码和 R2 Secret;先前不存在的条目会被删除,不留下孤儿凭据。 -- 关闭“记住同步密码”时,删除失败不会把本地标记写成关闭。 -- 下载预览采用确认时的当前“记住密码”选择。 -- provider 或同步表单发生变化时,旧下载预览和上传冲突操作立即失效。 -- 预览和覆盖确认同时校验目标指纹与完整远端连接快照;R2 Access Key ID 改变时,即使桶和对象路径相同,也不能应用旧预览。 +### 2.4 P2:事件泵信号分支防重入 -### 2.4 同步载荷校验 +- 同一 tick 内托盘"退出"与信号退出可能先后触发两次 `request_application_close`(幂等但冗余)。信号分支增加 `!closing_application` 守卫。 -- v1/v2 Session 和 v2 SessionFolder 使用 `deny_unknown_fields` 的严格 DTO,再显式转换为运行时类型。 -- 快捷键 action 仅允许应用实际支持的动作;按键序列逐 stroke 使用 GPUI `Keystroke::parse` 校验。 -- 合法 chord、空字符串和 `none` 保持兼容;未知动作、非法语法和仅空白值会被拒绝,不再进入可能 panic 的运行时绑定路径。 -- 预览中的偏好类别数改为与当前本地配置比较后的实际变化数,不再固定显示 3。 +### 2.5 P3:clippy 告警清零 -### 2.5 发布工作流 +- `tray.rs`:`send_request` 嵌套 if 合并为 let-chain;`MenuItem::with_id` 去除多余 `to_string()`。 +- `signals.rs` 的 `never_loop` 随 2.1 一并修复。 -- CI 和 Release 顶层权限收紧为 `contents: read`,仅发布 job 使用 `contents: write`。 -- 所有 checkout 均设置 `persist-credentials: false`。 -- 18 个 Action 引用全部固定到 40 位提交 SHA,并已根据官方 ref 复核。 -- `cargo-audit` 固定为 `0.22.0`。 -- Release 使用固定 RustSec 公告库提交并加 `--no-fetch`,历史标签重跑不会因公告库漂移而改变结果;日常 CI 仍检查当前公告库。 +## 3. 模块审查结论 -## 3. 最终本地验证 +| 模块 | 结论 | 要点 | +|---|---|---| +| `tray.rs` | 通过 | 全局事件处理器经 `Once` 只装一次,经可替换的 `REQUEST_TX` 转发(muda 的处理器 OnceLock 只能设一次);锁毒化容忍(`into_inner`/静默丢弃);Windows/macOS 在 gpui 主线程创建(macOS NSStatusItem 硬性要求);Linux 专用线程持有图标并自泵 GTK 事件,Drop 置位 + join 后在 gtk 线程内析构 AppIndicator;关闭转托盘时窗口实体不销毁,会话与文档完整保留 | +| `single_instance.rs` | 通过 | 绑定成功即首实例;绑定失败→连接→写 `activate`→退出(实测第二实例 11ms 内退出);启动竞态 3 次重试;stale 套接字(macOS)清理;全部失败降级无锁运行 | +| `signals.rs` | 通过 | SIGINT/SIGTERM → 通道 → 事件泵(≤16ms 延迟)→ `closing_application = true`(绕过转托盘拦截,并允许分离窗口关闭)→ 恢复窗口 → 走含未保存提示的完整退出流程 → `save_layout_state` 保存布局(实测退出时配置 mtime 同步更新) | +| `mod.rs` 事件泵 | 通过 | `spawn_in` + `update_in` 使泵闭包同时持有实体与窗口;托盘请求、实例激活、信号退出按序处理;窗口移除后同闭包内继续访问实体安全(延迟销毁,见结论 1);泵随实体释放自动终止 | +| `startup.rs` 关闭拦截 | 通过 | 托盘存在且非关停状态才最小化;`closing_application` 语义与 `document/window.rs` 既有用法一致(应用关停时允许分离窗口关闭) | +| `config.rs` | 通过 | `system_tray` serde 缺省 true;同步合并本地值优先(与相邻 UI 状态字段一致),已纳入本地字段保护快照测试并设互异值验证不被远端覆盖 | +| `dialogs.rs` 设置开关 | 通过 | 开→按需创建托盘(失败回退并回写 false);关→销毁托盘(Drop 内 join,主线程阻塞 ≤16ms);开关在 gpui 主线程执行,满足 macOS 约束 | +| `main.rs` | 通过 | 单实例判定早于应用构建;第二实例在创建任何窗口/托盘前退出(Windows `windows_subsystem` 下无闪烁) | +| locales | 通过 | 5 个新键中英双语齐全 | -以下命令均在 `D:\Git\ashell` 的最终工作树执行: +## 4. 验证结果 + +以下均在最终工作树执行: | 检查 | 结果 | |---|---| +| `cargo check --all-targets` | 通过,0 警告 | +| `cargo clippy --all-targets` | 通过,0 警告(`-D warnings` 等效) | | `cargo fmt --all -- --check` | 通过 | -| `cargo test --locked --quiet` | 316 passed,0 failed | -| `cargo check --locked --all-targets` | 通过 | -| `cargo clippy --locked --all-targets --all-features -- -D warnings` | 通过 | -| `cargo audit --deny warnings --file Cargo.lock --db --no-fetch` | 通过;1177 条公告,1024 个依赖 | -| `cargo tree -i rsa --locked` | 未匹配到 `rsa` crate | -| GitHub Actions YAML 与固定 SHA 静态检查 | 通过 | -| `git diff --check` | 通过 | -| `cargo build --locked --release` | 通过 | -| 隔离 HOME/USERPROFILE 启动冒烟 | 通过,测试进程已关闭 | - -最终 Windows 产物: - -- 路径:`D:\Git\ashell\target\release\jshell.exe` -- 大小:79,746,560 字节 -- SHA-256:`4E0EAFE6E676C2068EAFB13659C0373CA55A7998F192ED344D78BAA55EC229ED` -- 构建时间:2026-08-01 19:36:32(Asia/Shanghai) - -启动冒烟前后,用户现有配置文件 SHA-256 均为: - -`B6D937EA3591FCBE1C52465BCD8808F94491F7C9E7BDFBB4572FCA6A69872461` - -测试结束后不存在由本轮启动而残留的 JShell 进程。 - -## 4. 已知边界与发布前事项 +| `cargo test --bin jshell` | 368 passed,0 failed(含新增 `system_tray_defaults_to_enabled_and_serializes` 与同步本地字段保护用例) | +| `cargo audit` | 0 漏洞;9 条告警全部为警告级(8× gtk-rs GTK3 "no longer maintained",Linux 专用依赖链;1× `proc-macro-error` unmaintained,传递依赖),`cargo audit --deny warnings` 前需在审计配置中豁免 | +| 启动冒烟(Linux,NVIDIA/Vulkan) | 无 panic、无 GTK 错误;托盘创建成功 | +| 双实例冒烟 | 第二实例连接通知后 11ms 内退出(exit 0),首实例保持运行;首实例退出后套接字立即释放,新实例可重新绑定 | +| 信号退出冒烟 | `kill -TERM`/`kill -INT` 均 3 秒内优雅退出,日志记录 `received signal N, shutting down gracefully`;退出时配置落盘(`sessions.json` mtime 更新) | +| 残留进程检查 | 测试后无残留 `target/debug/jshell` 进程 | -- 系统凭据库与配置文件属于两个独立存储,代码已对可报告错误执行补偿回滚;若操作系统在两次存储操作之间被强制终止,仍可能需要用户重新输入同步凭据。 -- 本轮未使用真实 Cloudflare R2 账户写入对象;网络语义由本地 HTTP/AWS SigV4 集成测试验证,GUI 已验证 provider 切换、窄窗口布局和错误状态。 -- 真实 Linux SSH/SFTP 上传、删除、断线恢复以及远端编辑冲突仍建议在非生产测试主机上做发布前人工复核。 -- 推送后必须等待 CI 全部通过,再创建 `v0.1.0-beta.2` 标签;旧标签 `v0.1.0-beta.1` 不移动、不覆盖。 +## 5. 已知边界与平台风险 -## 5. 发布判定 +- **Wayland 最小化依赖合成器**:`set_minimized` 是请求而非命令,GNOME 忽略、KDE 支持;被忽略时关闭窗口表现为"窗口保持可见",属安全回退(应用仍在后台运行)。 +- **Linux 托盘左键无效**:tray-icon 的 appindicator 实现不发出 Click 事件,属上游 API 限制;Linux 请用托盘菜单"显示/隐藏窗口"。Windows/macOS 左键为"总是恢复窗口"(Windows 端 `activate_window` 自动 `IsIconic→SW_RESTORE`,不受任务栏最小化影响)。 +- **GTK3 绑定告警**:tray-icon→libappindicator→gtk-rs GTK3 链整体停止维护,但该链是 Linux 托盘的事实标准实现且仅 Linux 构建引入;Windows/macOS 产物不受影响。若需消除告警需替换托盘后端(如 ksni),暂不处理。 +- **未做单实例的跨平台信号(Windows WM_ENDSESSION)**:Windows 无控制台信号,会话结束路径不在本次范围。 +- 三个新增常驻线程(`jshell-tray`/`jshell-instance`/`jshell-signals`)均为进程生命周期 daemon 线程,随进程退出终止;`jshell-tray` 在设置中关闭托盘时按需回收。 +- 第二轮托盘创建(设置关闭→开启)在 Linux 上会在新线程创建 GTK 对象;gtk 进程级只初始化一次,新线程依赖自身的默认 GMainContext 泵事件,实测可用但属 GTK 文档未承诺的用法,长期运行场景建议重启应用后使用。 -当前判定:**本地发布候选门禁通过,可以分批提交并推送 `main`;托管 CI 通过后进入标签和 Release 流程。** +## 6. 结论 -详细清单见 [JShell v0.1.0-beta.2 发布候选审计与验收记录](docs/RELEASE_AUDIT_0.1.0-beta.2.md)。 +本次改动(系统托盘 + 后台运行、单实例锁、信号优雅退出)通过门禁:368 项测试、clippy/fmt 零告警、依赖审计零漏洞,三项功能冒烟与两项边界修复(跨用户隔离、二次信号逃生)均复验通过。改动尚未提交,建议按功能分批提交后走既有的 CI/发布流程。 diff --git a/Cargo.lock b/Cargo.lock index aec1b062..f2808adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ dependencies = [ "atspi-common", "phf 0.13.1", "serde", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -46,14 +46,24 @@ dependencies = [ "hashbrown 0.16.1", ] +[[package]] +name = "accesskit_consumer" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + [[package]] name = "accesskit_macos" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c5c87e8d94f2ec10cce590aadff24c76f576dab5502d45d0aed9fc3065d4451" +checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" dependencies = [ "accesskit", - "accesskit_consumer 0.36.0", + "accesskit_consumer 0.38.0", "hashbrown 0.16.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", @@ -75,7 +85,7 @@ dependencies = [ "futures-lite", "futures-util", "serde", - "zbus 5.16.0", + "zbus 5.19.0", ] [[package]] @@ -194,9 +204,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -251,9 +261,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -277,17 +287,17 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ - "object", + "object 0.39.1", ] [[package]] @@ -301,9 +311,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" dependencies = [ "rustversion", ] @@ -316,7 +326,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -345,9 +355,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "as-raw-xcb-connection" @@ -370,7 +380,7 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading", + "libloading 0.8.9", ] [[package]] @@ -384,7 +394,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.4", + "rand 0.9.5", "raw-window-handle", "serde", "serde_repr", @@ -392,25 +402,25 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", - "zbus 5.16.0", + "zbus 5.19.0", ] [[package]] name = "ashpd" -version = "0.13.11" +version = "0.13.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340e0f6bf7f9ee78549c61454f1460a3ed97c011902ee76b58301bbc6d502a32" +checksum = "fb8421aaa9644a5faf26735f258b669b15f063313ef8f8e2bdb28912a1a6f111" dependencies = [ "enumflags2", "futures-channel", "futures-util", - "getrandom 0.4.2", + "getrandom 0.4.3", "serde", "serde_repr", "wayland-backend", "wayland-client", "wayland-protocols", - "zbus 5.16.0", + "zbus 5.19.0", ] [[package]] @@ -439,9 +449,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ "compression-codecs", "compression-core", @@ -540,7 +550,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -569,13 +579,36 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", ] [[package]] @@ -609,11 +642,11 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 5.16.0", + "zbus 5.19.0", "zbus-lockstep", "zbus-lockstep-macros", "zbus_names 4.3.4", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -624,7 +657,7 @@ checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" dependencies = [ "atspi-common", "serde", - "zbus 5.16.0", + "zbus 5.19.0", ] [[package]] @@ -651,7 +684,7 @@ dependencies = [ "num-traits", "pastey 0.1.1", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "y4m", ] @@ -689,7 +722,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.37.3", "rustc-demangle", "windows-link 0.2.1", ] @@ -744,9 +777,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "shlex 1.3.0", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -888,22 +921,22 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", ] [[package]] name = "bstr" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", - "serde", + "serde_core", ] [[package]] @@ -920,22 +953,22 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -952,9 +985,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bzip2" @@ -965,6 +998,31 @@ dependencies = [ "libbz2-rs-sys", ] +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.1", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "calloop" version = "0.14.4" @@ -1021,16 +1079,16 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.117", + "syn 2.0.119", "tempfile", "toml 0.8.23", ] [[package]] name = "cc" -version = "1.2.63" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "jobserver", @@ -1047,6 +1105,16 @@ dependencies = [ "nom 7.1.3", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -1061,9 +1129,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "cgl" @@ -1147,13 +1215,13 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.9", ] [[package]] @@ -1187,7 +1255,7 @@ dependencies = [ "bitflags 2.13.1", "block", "cocoa-foundation 0.2.0", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics 0.24.0", "foreign-types 0.5.0", "libc", @@ -1216,7 +1284,7 @@ checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ "bitflags 2.13.1", "block", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics-types 0.2.0", "libc", "objc", @@ -1236,11 +1304,11 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "gpui_util", "indexmap", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", ] [[package]] @@ -1339,9 +1407,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1373,7 +1441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.13.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", "libc", @@ -1410,7 +1478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.13.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "libc", ] @@ -1423,7 +1491,7 @@ dependencies = [ "bitflags 2.13.1", "block", "cfg-if", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "libc", ] @@ -1433,7 +1501,7 @@ version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" dependencies = [ - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics 0.24.0", "foreign-types 0.5.0", "libc", @@ -1446,7 +1514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" dependencies = [ "block", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics2", "io-surface", "libc", @@ -1473,7 +1541,7 @@ dependencies = [ "linebender_resource_handle", "log", "rangemap", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "self_cell", "skrifa 0.40.0", "smol_str", @@ -1520,18 +1588,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1548,18 +1616,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -1575,7 +1643,7 @@ checksum = "96dacf199529fb801ae62a9aafdc01b189e9504c0d1ee1512a4c16bcd8666a93" dependencies = [ "cpubits", "ctutils", - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "num-traits", "rand_core 0.10.1", @@ -1601,16 +1669,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "hybrid-array", "rand_core 0.10.1", ] [[package]] name = "ctor" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" dependencies = [ "link-section", "linktime-proc-macro", @@ -1674,7 +1742,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1693,9 +1761,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "data-url" @@ -1741,7 +1809,7 @@ checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1760,9 +1828,6 @@ name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "derive_arbitrary" @@ -1772,7 +1837,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1794,18 +1859,18 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.119", "unicode-xid", ] [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1894,13 +1959,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -1909,9 +1974,15 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ - "libloading", + "libloading 0.8.9", ] +[[package]] +name = "doctest-file" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" + [[package]] name = "document-features" version = "0.2.12" @@ -1927,6 +1998,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + [[package]] name = "dunce" version = "1.0.5" @@ -1994,9 +2071,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "elliptic-curve" @@ -2023,14 +2100,14 @@ dependencies = [ [[package]] name = "embed-resource" -version = "3.0.9" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "vswhom", "winreg 0.55.0", ] @@ -2046,9 +2123,9 @@ dependencies = [ [[package]] name = "encoding_rs_io" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" dependencies = [ "encoding_rs", ] @@ -2076,7 +2153,7 @@ checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2088,7 +2165,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2109,7 +2186,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2120,7 +2197,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2140,7 +2217,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2211,14 +2288,16 @@ dependencies = [ [[package]] name = "exr" -version = "1.74.0" +version = "1.74.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +checksum = "711fe42c9964295e01ee3fba3f9fe0e1d24b98886950d68efe81b1c76e21adf3" dependencies = [ "bit_field", "half", "lebe", "miniz_oxide", + "num-complex", + "pulp", "rayon-core", "smallvec", "zune-inflate", @@ -2226,11 +2305,11 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", ] [[package]] @@ -2254,6 +2333,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + [[package]] name = "filedescriptor" version = "0.8.3" @@ -2277,9 +2366,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" [[package]] name = "fixedbitset" @@ -2359,9 +2448,9 @@ dependencies = [ [[package]] name = "font-types" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7299a780854a6d391be2ae1c8521c9368471b559dbfd6a8dbd9f407eaff100" +checksum = "75382bc7392ef10aad10935f92fc3db36d2d4dad0e5d96d8d65e04f89a07ec39" dependencies = [ "bytemuck", ] @@ -2409,13 +2498,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -2471,9 +2560,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -2486,9 +2575,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -2509,15 +2598,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -2526,9 +2615,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -2545,32 +2634,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -2583,6 +2672,64 @@ dependencies = [ "slab", ] +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2643,16 +2790,16 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", + "wasm-bindgen", ] [[package]] @@ -2690,6 +2837,38 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -2701,17 +2880,64 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", @@ -2764,6 +2990,17 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "gpu-allocator" version = "0.28.0" @@ -2774,7 +3011,7 @@ dependencies = [ "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.18", + "thiserror 2.0.20", "windows 0.62.2", ] @@ -2801,7 +3038,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "accesskit", "anyhow", @@ -2815,7 +3052,7 @@ dependencies = [ "cocoa 0.26.0", "cocoa-foundation 0.2.0", "collections", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "core-graphics 0.24.0", "core-text", @@ -2850,7 +3087,7 @@ dependencies = [ "pollster 0.4.0", "postage", "profiling", - "rand 0.9.4", + "rand 0.9.5", "raw-window-handle", "refineable", "regex", @@ -2868,7 +3105,7 @@ dependencies = [ "strum", "sum_tree", "taffy", - "thiserror 2.0.18", + "thiserror 2.0.20", "url", "usvg", "util_macros", @@ -2883,7 +3120,7 @@ dependencies = [ [[package]] name = "gpui-component" version = "0.5.2" -source = "git+https://github.com/JayhaShf/gpui-component.git?rev=e3d17318fcde6fde02c38d2c405761d59ce1aa69#e3d17318fcde6fde02c38d2c405761d59ce1aa69" +source = "git+https://github.com/JayhaShf/gpui-component.git?rev=0a4611d1428835c541474a853bf53692464b70c4#0a4611d1428835c541474a853bf53692464b70c4" dependencies = [ "aho-corasick", "anyhow", @@ -2950,7 +3187,7 @@ dependencies = [ [[package]] name = "gpui-component-assets" version = "0.5.1" -source = "git+https://github.com/JayhaShf/gpui-component.git?rev=e3d17318fcde6fde02c38d2c405761d59ce1aa69#e3d17318fcde6fde02c38d2c405761d59ce1aa69" +source = "git+https://github.com/JayhaShf/gpui-component.git?rev=0a4611d1428835c541474a853bf53692464b70c4#0a4611d1428835c541474a853bf53692464b70c4" dependencies = [ "anyhow", "gpui", @@ -2964,23 +3201,23 @@ dependencies = [ [[package]] name = "gpui-component-macros" version = "0.5.1" -source = "git+https://github.com/JayhaShf/gpui-component.git?rev=e3d17318fcde6fde02c38d2c405761d59ce1aa69#e3d17318fcde6fde02c38d2c405761d59ce1aa69" +source = "git+https://github.com/JayhaShf/gpui-component.git?rev=0a4611d1428835c541474a853bf53692464b70c4#0a4611d1428835c541474a853bf53692464b70c4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "accesskit", "accesskit_unix", "anyhow", "as-raw-xcb-connection", - "ashpd 0.13.11", + "ashpd 0.13.13", "bitflags 2.13.1", "bytemuck", "calloop", @@ -3026,7 +3263,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "accesskit", "accesskit_macos", @@ -3037,7 +3274,7 @@ dependencies = [ "cbindgen", "cocoa 0.26.0", "collections", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "core-graphics 0.24.0", "core-text", @@ -3075,18 +3312,18 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "console_error_panic_hook", "gpui", @@ -3099,7 +3336,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "schemars", "serde", @@ -3109,7 +3346,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "log", @@ -3119,7 +3356,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "console_error_panic_hook", @@ -3142,7 +3379,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "bytemuck", @@ -3172,7 +3409,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "accesskit", "accesskit_windows", @@ -3187,7 +3424,7 @@ dependencies = [ "itertools 0.14.0", "log", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "raw-window-handle", "smallvec", "uuid", @@ -3213,6 +3450,58 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "half" version = "2.7.1" @@ -3393,14 +3682,14 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "http" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -3408,9 +3697,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -3418,9 +3707,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -3432,7 +3721,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "async-compression", @@ -3457,9 +3746,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "ctutils", "subtle", @@ -3469,9 +3758,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -3633,12 +3922,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -3662,9 +3945,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.25" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", @@ -3717,9 +4000,9 @@ checksum = "65b27460c2c92b037f3f94c538ed9a3342f3fdf923606781629ccb35f82d042a" [[package]] name = "imgref" -version = "1.12.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" +checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" [[package]] name = "indexmap" @@ -3831,7 +4114,20 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "interprocess" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "798de1433ba514cc6c04c4144c2469af81396e4906195218737c776d47769572" +dependencies = [ + "doctest-file", + "libc", + "recvmsg", + "widestring", + "windows-sys 0.61.2", ] [[package]] @@ -3860,16 +4156,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" dependencies = [ "cgl", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "leaky-cow", ] [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is-docker" @@ -3948,34 +4244,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] [[package]] name = "jshell" -version = "0.1.0-beta.4" +version = "1.0.0" dependencies = [ "alacritty_terminal", "anyhow", @@ -3991,17 +4286,19 @@ dependencies = [ "gpui-component", "gpui-component-assets", "gpui_platform", + "gtk", "hex", "hmac 0.12.1", "httparse", "image", + "interprocess", "keyring", "libc", "menu", "notify 6.1.1", "open", "portable-pty", - "rand 0.8.6", + "rand 0.8.7", "rcgen", "reqwest", "rfd", @@ -4012,9 +4309,11 @@ dependencies = [ "rustls-native-certs", "serde", "serde_json", + "serde_yaml", "serialport", - "sha1 0.10.6", + "sha1 0.10.7", "sha2 0.10.9", + "signal-hook", "sys-locale", "sysinfo 0.33.1", "tar", @@ -4027,6 +4326,7 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", + "tray-icon", "uuid", "walkdir", "winreg 0.52.0", @@ -4037,9 +4337,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -4055,6 +4355,17 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.1", + "serde", + "unicode-segmentation", +] + [[package]] name = "keyring" version = "3.6.3" @@ -4079,7 +4390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading", + "libloading 0.8.9", "pkg-config", ] @@ -4091,9 +4402,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kqueue" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" dependencies = [ "kqueue-sys", "libc", @@ -4142,18 +4453,36 @@ dependencies = [ "leak", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lebe" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + [[package]] name = "libbz2-rs-sys" version = "0.2.5" @@ -4162,9 +4491,9 @@ checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libdbus-sys" @@ -4178,14 +4507,24 @@ dependencies = [ [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libloading" version = "0.8.9" @@ -4204,9 +4543,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" dependencies = [ "libc", ] @@ -4239,15 +4578,15 @@ checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" [[package]] name = "link-section" -version = "0.18.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "014e440054ce8170890229eeef5bcda955305e056ec713de40ed366944483f09" +checksum = "39c29a617ce3df32c08497bdc1ab6e2376e0b17948ac166a2fbe5977c5954cd9" [[package]] name = "linktime-proc-macro" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7b0a3383c2a1002d11349c92c85a666a5fb679e96c79d782cf0dbe557fd6ee" +checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" [[package]] name = "linux-raw-sys" @@ -4284,9 +4623,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" dependencies = [ "serde_core", "value-bag", @@ -4493,11 +4832,11 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "bindgen", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-video", "ctor", "foreign-types 0.5.0", @@ -4507,9 +4846,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" @@ -4532,7 +4871,7 @@ dependencies = [ [[package]] name = "menu" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "gpui", ] @@ -4551,6 +4890,22 @@ dependencies = [ "pastey 0.2.3", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -4581,9 +4936,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "log", @@ -4634,6 +4989,26 @@ dependencies = [ "pxfm", ] +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "once_cell", + "png", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + [[package]] name = "naga" version = "29.0.4" @@ -4644,7 +5019,7 @@ dependencies = [ "bit-set", "bitflags 2.13.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "codespan-reporting", "half", "hashbrown 0.16.1", @@ -4656,7 +5031,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.18", + "thiserror 2.0.20", "unicode-ident", ] @@ -4716,7 +5091,7 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.13.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "memoffset", ] @@ -4729,7 +5104,7 @@ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ "bitflags 2.13.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -4813,7 +5188,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio 1.2.1", + "mio 1.2.2", "notify-types", "walkdir", "windows-sys 0.60.2", @@ -4830,7 +5205,7 @@ dependencies = [ "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus 5.16.0", + "zbus 5.19.0", ] [[package]] @@ -4876,9 +5251,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -4895,7 +5270,7 @@ dependencies = [ "num-iter", "num-traits", "once_cell", - "rand 0.9.4", + "rand 0.9.5", "serde", "smallvec", "zeroize", @@ -4907,6 +5282,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ + "bytemuck", "num-traits", ] @@ -4924,25 +5300,24 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -5317,6 +5692,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -5330,7 +5714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78f2bfed90f1618b4b48dcad9307f25e14ae894e2949642c87c351601d62cebd" dependencies = [ "aes 0.8.4", - "ashpd 0.13.11", + "ashpd 0.13.13", "async-fs", "async-io", "async-lock", @@ -5341,7 +5725,7 @@ dependencies = [ "endi", "futures-lite", "futures-util", - "getrandom 0.4.2", + "getrandom 0.4.3", "hkdf 0.12.4", "hmac 0.12.1", "md-5", @@ -5352,10 +5736,10 @@ dependencies = [ "serde_bytes", "sha2 0.10.9", "subtle", - "zbus 5.16.0", - "zbus_macros 5.16.0", + "zbus 5.19.0", + "zbus_macros 5.19.0", "zeroize", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -5366,13 +5750,12 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.5" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c" +checksum = "f9cfef937e9c486488c7e3d949ae31c0f1d06bdacd75b99c086cb35356e30408" dependencies = [ "is-wsl", "libc", - "pathdiff", ] [[package]] @@ -5397,7 +5780,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5506,12 +5889,37 @@ dependencies = [ "log", "rand 0.10.2", "sha2 0.10.9", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "windows 0.62.2", "windows-strings 0.5.1", ] +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "parking" version = "2.2.1" @@ -5552,6 +5960,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pastey" version = "0.1.1" @@ -5564,12 +5978,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "pathfinder_geometry" version = "0.5.1" @@ -5646,7 +6054,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "collections", "serde", @@ -5690,7 +6098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -5713,7 +6121,7 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5757,7 +6165,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5906,9 +6314,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -6000,7 +6408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6026,20 +6434,63 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -6060,24 +6511,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", ] +[[package]] +name = "pulp" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", +] + +[[package]] +name = "pulp-wasm-simd-flag" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" + [[package]] name = "pxfm" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" [[package]] name = "qoi" @@ -6105,19 +6579,19 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "rustls", "socket2", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tracing", "web-time", @@ -6125,20 +6599,21 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.20", "tinyvec", "tracing", "web-time", @@ -6146,23 +6621,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -6181,9 +6656,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6192,9 +6667,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -6207,7 +6682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20 0.10.1", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -6255,6 +6730,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "range-alloc" version = "0.1.5" @@ -6293,10 +6777,10 @@ dependencies = [ "num-traits", "pastey 0.1.1", "profiling", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "simd_helpers", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "wasm-bindgen", ] @@ -6316,6 +6800,15 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.13.1", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -6385,7 +6878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" dependencies = [ "bytemuck", - "font-types 0.12.2", + "font-types 0.12.3", "once_cell", ] @@ -6396,10 +6889,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c69341f5f744623061a258e9d06a0213783a89ec23b6efb88a1c0c4ed285559" dependencies = [ "bytemuck", - "font-types 0.12.2", + "font-types 0.12.3", "once_cell", ] +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -6428,42 +6933,42 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "derive_refineable", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -6473,9 +6978,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -6484,9 +6989,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "renderdoc-sys" @@ -6697,7 +7202,7 @@ dependencies = [ "salsa20", "scrypt", "sec1", - "sha1 0.10.6", + "sha1 0.10.7", "sha1 0.11.0", "sha2 0.10.9", "sha2 0.11.0", @@ -6706,7 +7211,7 @@ dependencies = [ "spki", "ssh-encoding", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "typenum", "universal-hash 0.6.1", @@ -6726,9 +7231,9 @@ dependencies = [ [[package]] name = "russh-sftp" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed8949eca4163c18a8f59ff96d32cf61e9c13b9735e21ef32b3907f4aafa1a9" +checksum = "9de67aace74530a29086db0671fa200c470a58eb380081f28ad512ffb0c5356b" dependencies = [ "bitflags 2.13.1", "bytes", @@ -6738,7 +7243,7 @@ dependencies = [ "log", "serde", "serde_bytes", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tokio-util", "wasm-bindgen-futures", @@ -6757,9 +7262,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -6768,26 +7273,27 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097" dependencies = [ + "mime_guess", "proc-macro2", "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.117", + "syn 2.0.119", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0" dependencies = [ "globset", - "sha2 0.10.9", + "sha2 0.11.0", "walkdir", ] @@ -6832,7 +7338,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6847,7 +7353,7 @@ dependencies = [ "rust-i18n-support 4.2.1", "serde", "serde_json", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6894,9 +7400,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" [[package]] name = "rustc-hash" @@ -6906,9 +7412,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -7005,9 +7511,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "web-time", "zeroize", @@ -7015,9 +7521,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "ring", "rustls-pki-types", @@ -7026,9 +7532,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -7067,7 +7573,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "async-task", "backtrace", @@ -7075,15 +7581,15 @@ dependencies = [ "flume", "futures", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "web-time", ] [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "indexmap", @@ -7095,14 +7601,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -7185,7 +7691,7 @@ dependencies = [ "hkdf 0.12.4", "num", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "serde", "sha2 0.10.9", "zbus 4.4.0", @@ -7211,7 +7717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags 2.13.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -7229,9 +7735,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -7245,9 +7751,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -7284,33 +7790,33 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "serde_derive_internals" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -7324,9 +7830,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "indexmap", "itoa", @@ -7338,13 +7844,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -7402,9 +7908,9 @@ dependencies = [ [[package]] name = "serial2" -version = "0.2.37" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eb6ea5562eeaed6936b8b54e086aa0f88b9e5b1bef45beb038e2519fa1185b1" +checksum = "b16809bc35793b19ce4e0c53924bc0dce3937f15487997cfdaed936004180730" dependencies = [ "cfg-if", "libc", @@ -7419,7 +7925,7 @@ checksum = "a4d91116f97173694f1642263b2ff837f80d933aa837e2314969f6728f661df3" dependencies = [ "bitflags 2.13.1", "cfg-if", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "io-kit-sys", "libudev", @@ -7432,9 +7938,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -7568,9 +8074,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_helpers" @@ -7643,9 +8149,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smol" @@ -7676,9 +8182,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -7756,9 +8262,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -7858,7 +8364,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7870,7 +8376,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "heapless", "log", @@ -7881,34 +8387,35 @@ dependencies = [ [[package]] name = "sval" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb9efbae90f97301f4d25f3be63dfd99d6b7af9d088228a52ec960d649b2e7d" +checksum = "ec4a2a7d92fa86fcc6222e4c3845f8486cff899d9db32480b26c91a5dbf2e22d" [[package]] name = "sval_buffer" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff5c0280ea0af40b3a1fd0b532680b5067482ffb81412ee66ec04b1d9952b49a" +checksum = "f4324db9ac500c609d659b752edf9c8abbf2233f8afd61a503fd6f88ed625032" dependencies = [ "sval", "sval_ref", + "zerocopy", ] [[package]] name = "sval_dynamic" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b9067f2e68f58e110cf8019a268057e3791cf74b0fdb1b3c7c6e49104f44e6" +checksum = "4046add0eecf55e680b9e207edf5fc7737b18a1d950db363d97e7f1b2d7c629c" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ebdbf0e4b175884aa587fcf551c16dabe245ea04235abdd8cbbd160b27ed5a" +checksum = "911a3486b5984a0a4f25edefcf2c2dba23654c29f63e75493b671d338bf24243" dependencies = [ "itoa", "ryu", @@ -7917,9 +8424,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e448d9fa216a6c16670b28d624fbbcf5c04e41eb187bd7c52e01222ffa72a12" +checksum = "da53aae7c737b5b5f1be4bcb0ff20e057bf6b2ee4e9d025560075c5830d09f95" dependencies = [ "itoa", "ryu", @@ -7928,9 +8435,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021de5b5c26efd544c694cef9b8a9abe8633481bf3be1ea145a18a740818b291" +checksum = "df24df43cbdc4bb8c9f5ed19d0d57dc8f60a1a4259cdce52d597fe774ad3a71f" dependencies = [ "sval", "sval_buffer", @@ -7939,18 +8446,18 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ef5ffec8bc52ded04ee424ab8d959e25e64fd40a48a16d21f8991ce824c1bb" +checksum = "2bebc17f0f1fad060e57b778728d41ef87627e9111a6365d7463472cb58fc1b3" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.20.0" +version = "2.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b983833a8a2390f89ebcf9b9acd06883017014b4ffd72ee28e0c9de6852039f5" +checksum = "9f26fe3f6a68b40e6c8d654ea48c00e4316272fddf68c80493714c1b034ae70b" dependencies = [ "serde_core", "sval", @@ -7975,11 +8482,11 @@ dependencies = [ [[package]] name = "swash" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" dependencies = [ - "skrifa 0.40.0", + "skrifa 0.44.0", "yazi", "zeno", ] @@ -7992,9 +8499,19 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" [[package]] name = "syn" -version = "2.0.117" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -8029,7 +8546,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8069,6 +8586,19 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.23", + "version-compare", +] + [[package]] name = "taffy" version = "0.12.2" @@ -8104,13 +8634,19 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tauri-winrt-notification" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", "windows 0.61.3", "windows-version", ] @@ -8122,7 +8658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -8159,11 +8695,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -8174,25 +8710,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -8213,12 +8749,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -8230,15 +8765,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -8291,9 +8826,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -8306,13 +8841,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", - "mio 1.2.1", + "mio 1.2.2", "pin-project-lite", "socket2", "tokio-macros", @@ -8321,13 +8856,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -8354,9 +8889,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", @@ -8389,9 +8924,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap", "serde_core", @@ -8399,7 +8934,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -8420,6 +8955,28 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -8436,23 +8993,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -8463,9 +9020,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tower" @@ -8532,7 +9089,7 @@ checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", "symlink", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", "tracing-subscriber", ] @@ -8545,7 +9102,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8588,11 +9145,32 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tray-icon" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "once_cell", + "png", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + [[package]] name = "tree-sitter" -version = "0.26.9" +version = "0.26.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dab76d0b724ba557954125188cf0633a1ca43199ced82d95c7b9c32cc3de1f3" +checksum = "83c567a8e18ae93f20982c90370b16fd24023aeaf52f6052b96957ab253a0fec" dependencies = [ "cc", "regex", @@ -8790,9 +9368,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +checksum = "b40688ea6389c8171614b25491f71d4a27946e0c7ce2da1c6de27e25abf1a0ae" dependencies = [ "arc-swap", "serde", @@ -8834,9 +9412,15 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7285e83a80ce76f5e7bce79fa41f68d78ba62d1003cf27bf748ab24413808cf4" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -8984,20 +9568,20 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "perf", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "uuid" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "sha1_smol", @@ -9023,9 +9607,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +checksum = "068e763e8279de7ab94b6afebded2cb701678af094feb1c12ccb061b4783c1be" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -9033,9 +9617,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16530907bfe2999a1773ca5900a65101e092c70f642f25cc23ca0c43573262c5" +checksum = "417d6197dd0ee696783d6be4276ac6ea74b985e00024c85ccfb37aff4f2bed82" dependencies = [ "erased-serde", "serde_core", @@ -9044,9 +9628,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" +checksum = "c61f7251ecde2c9ed431bbe0659853e7991753447447bbf1ae59d8b31c578d4e" dependencies = [ "sval", "sval_buffer", @@ -9063,6 +9647,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + [[package]] name = "version_check" version = "0.9.5" @@ -9136,27 +9726,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -9167,9 +9748,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -9177,9 +9758,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9187,48 +9768,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -9253,23 +9812,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.1", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" dependencies = [ "cc", "downcast-rs", @@ -9281,9 +9828,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ "bitflags 2.13.1", "rustix 1.1.4", @@ -9304,9 +9851,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags 2.13.1", "wayland-backend", @@ -9365,9 +9912,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -9385,9 +9932,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -9408,7 +9955,7 @@ dependencies = [ "bitflags 2.13.1", "bytemuck", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "document-features", "hashbrown 0.16.1", "js-sys", @@ -9439,7 +9986,7 @@ dependencies = [ "bit-vec", "bitflags 2.13.1", "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "document-features", "hashbrown 0.16.1", "indexmap", @@ -9452,7 +9999,7 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", @@ -9502,7 +10049,7 @@ dependencies = [ "block2 0.6.2", "bytemuck", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "glow", "glutin_wgl_sys", "gpu-allocator", @@ -9511,7 +10058,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading", + "libloading 0.8.9", "log", "naga", "ndk-sys", @@ -9531,7 +10078,7 @@ dependencies = [ "raw-window-metal", "renderdoc-sys", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "wasm-bindgen", "wayland-sys", "web-sys", @@ -9578,6 +10125,12 @@ dependencies = [ "winsafe", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" @@ -9600,7 +10153,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -9662,7 +10215,7 @@ checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" dependencies = [ "parking_lot", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.20", "windows 0.61.3", "windows-future 0.2.1", ] @@ -9766,7 +10319,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9777,7 +10330,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9788,7 +10341,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9799,7 +10352,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9810,7 +10363,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9821,7 +10374,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -10189,6 +10742,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.15" @@ -10200,9 +10762,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -10260,100 +10822,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.1", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "workspace-hack" version = "0.1.0" @@ -10429,9 +10903,9 @@ dependencies = [ [[package]] name = "xcursor" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" [[package]] name = "xdg-home" @@ -10479,9 +10953,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" [[package]] name = "xml5ever" @@ -10551,7 +11025,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] @@ -10573,10 +11047,10 @@ dependencies = [ "hex", "nix 0.29.0", "ordered-stream", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_repr", - "sha1 0.10.6", + "sha1 0.10.7", "static_assertions", "tracing", "uds_windows", @@ -10589,9 +11063,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.16.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -10616,10 +11090,10 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 1.0.3", - "zbus_macros 5.16.0", + "winnow 1.0.4", + "zbus_macros 5.19.0", "zbus_names 4.3.4", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -10629,7 +11103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" dependencies = [ "zbus_xml", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -10640,10 +11114,10 @@ checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "zbus-lockstep", "zbus_xml", - "zvariant 5.13.1", + "zvariant 5.14.0", ] [[package]] @@ -10652,26 +11126,26 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "zvariant_utils 2.1.0", ] [[package]] name = "zbus_macros" -version = "5.16.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", "zbus_names 4.3.4", - "zvariant 5.13.1", - "zvariant_utils 3.5.0", + "zvariant 5.14.0", + "zvariant_utils 4.0.0", ] [[package]] @@ -10692,8 +11166,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", - "winnow 1.0.3", - "zvariant 5.13.1", + "winnow 1.0.4", + "zvariant 5.14.0", ] [[package]] @@ -10703,9 +11177,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" dependencies = [ "serde", - "winnow 1.0.3", + "winnow 1.0.4", "zbus_names 4.3.4", - "zvariant 5.13.1", + "zvariant 5.14.0", +] + +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", ] [[package]] @@ -10715,7 +11198,7 @@ source = "git+https://github.com/zed-industries/font-kit?rev=94b0f28166665e8fd2f dependencies = [ "bitflags 2.13.1", "byteorder", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics 0.24.0", "core-text", "dirs", @@ -10742,7 +11225,7 @@ dependencies = [ "core-graphics-helmer-fork", "log", "objc", - "rand 0.8.6", + "rand 0.8.7", "screencapturekit", "screencapturekit-sys", "sysinfo 0.31.4", @@ -10786,22 +11269,22 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -10821,28 +11304,28 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -10875,7 +11358,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -10891,14 +11374,14 @@ dependencies = [ "flate2", "indexmap", "memchr", - "thiserror 2.0.18", + "thiserror 2.0.20", "zopfli", ] [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "anyhow", "chrono", @@ -10908,9 +11391,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" @@ -10927,7 +11410,7 @@ dependencies = [ [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" dependencies = [ "tracing", "tracing-subscriber", @@ -10938,13 +11421,13 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/JayhaShf/zed.git?rev=8c555982deb9bfe7451ee40c4e393e048da98068#8c555982deb9bfe7451ee40c4e393e048da98068" +source = "git+https://github.com/JayhaShf/zed.git?rev=4644b231160398276b4e5aab90e3e396acf0b4aa#4644b231160398276b4e5aab90e3e396acf0b4aa" [[package]] name = "zune-core" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" [[package]] name = "zune-inflate" @@ -10979,18 +11462,19 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.13.1" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" +checksum = "b5e28c25bd8bb8da5a1f3e7065d0c156b9ee9a7973adf78b0e35eaefdf3b1b5c" dependencies = [ "endi", "enumflags2", "serde", "serde_bytes", "url", - "winnow 1.0.3", - "zvariant_derive 5.13.1", - "zvariant_utils 3.5.0", + "winnow 1.0.4", + "zcheapstr", + "zvariant_derive 5.14.0", + "zvariant_utils 4.0.0", ] [[package]] @@ -10999,24 +11483,24 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "zvariant_utils 2.1.0", ] [[package]] name = "zvariant_derive" -version = "5.13.1" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" +checksum = "d496a145685283b67e232bd9e47377f6b60ad9d51e3601b23867f77c42477f96" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.117", - "zvariant_utils 3.5.0", + "syn 3.0.3", + "zvariant_utils 4.0.0", ] [[package]] @@ -11027,18 +11511,18 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "629d80ece222cad20fe0e8741be493c4ab166acf3b85341bdc2cdbcfd8f3c2d6" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", - "winnow 1.0.3", + "syn 3.0.3", + "winnow 1.0.4", ] diff --git a/Cargo.toml b/Cargo.toml index 594ae57f..74e9ecdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jshell" -version = "0.1.0-beta.4" +version = "1.0.0" edition = "2024" authors = ["JShell contributors"] description = "A Windows-focused SSH and local terminal workspace" @@ -15,9 +15,9 @@ async-trait = "0.1" chrono = { version = "0.4", features = ["serde"] } directories = "5" flate2 = "1" -gpui = { git = "https://github.com/JayhaShf/zed.git", rev = "8c555982deb9bfe7451ee40c4e393e048da98068" } -gpui_platform = { git = "https://github.com/JayhaShf/zed.git", rev = "8c555982deb9bfe7451ee40c4e393e048da98068", features = ["font-kit", "runtime_shaders", "wayland", "x11"] } -gpui-component = { git = "https://github.com/JayhaShf/gpui-component.git", rev = "e3d17318fcde6fde02c38d2c405761d59ce1aa69", features = [ +gpui = { git = "https://github.com/JayhaShf/zed.git", rev = "4644b231160398276b4e5aab90e3e396acf0b4aa" } +gpui_platform = { git = "https://github.com/JayhaShf/zed.git", rev = "4644b231160398276b4e5aab90e3e396acf0b4aa", features = ["font-kit", "runtime_shaders", "wayland", "x11"] } +gpui-component = { git = "https://github.com/JayhaShf/gpui-component.git", rev = "0a4611d1428835c541474a853bf53692464b70c4", features = [ "tree-sitter-bash", "tree-sitter-c", "tree-sitter-cpp", @@ -36,8 +36,8 @@ gpui-component = { git = "https://github.com/JayhaShf/gpui-component.git", rev = "tree-sitter-typescript", "tree-sitter-yaml", ] } -gpui-component-assets = { git = "https://github.com/JayhaShf/gpui-component.git", rev = "e3d17318fcde6fde02c38d2c405761d59ce1aa69" } -menu = { git = "https://github.com/JayhaShf/zed.git", rev = "8c555982deb9bfe7451ee40c4e393e048da98068" } +gpui-component-assets = { git = "https://github.com/JayhaShf/gpui-component.git", rev = "0a4611d1428835c541474a853bf53692464b70c4" } +menu = { git = "https://github.com/JayhaShf/zed.git", rev = "4644b231160398276b4e5aab90e3e396acf0b4aa" } portable-pty = "0.9" rfd = "0.15" russh = { version = "0.60.3", git = "https://github.com/JayhaShf/russh.git", rev = "705e92dfb551e458d376016ecc83f43be71b0aa1", default-features = false, features = ["ring", "flate2", "rsa"] } @@ -59,6 +59,7 @@ zip = { version = "2", default-features = false, features = ["deflate"] } image = { version = "0.25.9", default-features = false, features = ["png"] } futures = "0.3.32" zeroize = "1.8" +libc = "0.2" keyring = { version = "3.6.3", features = ["apple-native", "windows-native", "sync-secret-service", "crypto-rust", "vendored"] } notify = "6.1.1" open = "5.1" @@ -78,19 +79,27 @@ rustls-native-certs = "=0.8.4" sha1 = "0.10" sha2 = "0.10" tokio-rustls = { version = "=0.26.4", default-features = false, features = ["ring", "tls12"] } +tray-icon = { version = "0.24", default-features = false, features = ["gtk"] } +interprocess = "2.4" +signal-hook = "0.4" [dev-dependencies] libc = "0.2" rcgen = "0.13" +serde_yaml = "0.9" [target.'cfg(target_os = "windows")'.dependencies] winreg = "0.52" +[target.'cfg(target_os = "linux")'.dependencies] +gtk = "0.18" + [patch.crates-io] metal = { git = "https://github.com/JayhaShf/metal-rs.git", rev = "d7512f0ac9d4b3b53a2677ebdaec3aaffe0908f7" } rav1e = { git = "https://github.com/JayhaShf/rav1e.git", rev = "6793853eb18fde2425aeab927fc3449268a41cbb" } xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "ae3b2cd080e78173223038ccbebdc644eaf4a9ad" } + [package.metadata.deb] maintainer = "JShell contributors" license-file = ["LICENSE"] diff --git a/README.en.md b/README.en.md index ff77547e..5a8b26df 100644 --- a/README.en.md +++ b/README.en.md @@ -2,7 +2,7 @@ # JShell -JShell is a Rust and GPUI desktop terminal workspace for local terminals, SSH sessions, SFTP file management, and remote text editing. The current source version is `0.1.0-beta.4`; the UI follows the system language by default and can be changed in settings, while Chinese remains the default documentation entry. +JShell is a Rust and GPUI desktop terminal workspace for local terminals, SSH sessions, SFTP file management, and remote text editing. The current source version is `1.0.0`; the UI follows the system language by default and can be changed in settings, while Chinese remains the default documentation entry. ![JShell workspace](assets/screenshots/compact-workspace.png) diff --git a/README.md b/README.md index ab94987f..ee574b94 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # JShell -JShell 是一个使用 Rust、GPUI 与 `alacritty_terminal` 构建的桌面终端工作区,面向需要同时管理本地终端、SSH 会话、SFTP 文件和远程文本的用户。当前源码版本为 `0.1.0-beta.4`;界面语言默认跟随系统,也可在设置中切换,项目默认文档入口为中文。 +JShell 是一个使用 Rust、GPUI 与 `alacritty_terminal` 构建的桌面终端工作区,面向需要同时管理本地终端、SSH 会话、SFTP 文件和远程文本的用户。当前源码版本为 `1.0.0`;界面语言默认跟随系统,也可在设置中切换,项目默认文档入口为中文。 ![JShell 工作区预览](assets/screenshots/compact-workspace.png) diff --git a/docs/RELEASE_READINESS.md b/docs/RELEASE_READINESS.md new file mode 100644 index 00000000..321420aa --- /dev/null +++ b/docs/RELEASE_READINESS.md @@ -0,0 +1,70 @@ +# JShell v1.0 发布就绪清单(修改意见 / 优化建议 / 上线前事项) + +> 本文档是 v1.0 正式版发布的检查依据,与 `docs/TEST_PLAN.md`(六类测试计划)、 +> `AUDIT_REPORT.md`(历史审计)配套使用。状态列在发布前逐项刷新。 + +--- + +## 1. 代码审查修改意见(按优先级) + +| 优先级 | 编号 | 发现 | 处置与状态 | +|---|---|---|---| +| P0 | R-001 | tar/tar.gz 目录归档解包疑似路径穿越(`sftp/mod.rs`) | **已核实为 crate 层防护**:`tar` 0.4.46 `unpack_in` 跳过 `..` 条目并经 canonicalize 拒绝符号链接逃逸;zip 分支有 `enclosed_name()`。本次以 4 项回归测试锁定行为 + 函数注释说明,防止未来重构破坏。✅ 已闭环 | +| P0 | R-002 | CI `cargo audit --deny warnings` 因 10 条「unmaintained」告警红灯(8× GTK3 链 + `paste` + `proc-macro-error`),无豁免配置 | ✅ 新增 `.cargo/audit.toml` 按 ID 豁免并注明理由(Linux 专用托盘链,见 AUDIT_REPORT 第 5 节);`--deny warnings` 本地复验通过 | +| P1 | R-003 | i18n 缺键:`confirm` 在 zh-CN 存在、en.yml 缺失(en 为 fallback,英文界面会显示键名) | ✅ en.yml 补 `confirm: "Confirm"`,新增 locale 键对齐测试防复发 | +| P1 | R-004 | TOFU 主机密钥首次自动接受、无用户确认弹窗(设计文档已记录该取舍) | ⏳ 保留现状;v1.0 安全评审项(TEST_PLAN S-010),后续版本评估"提示确认"设置项 | +| P1 | R-005 | v1 配置加密硬件 UUID 检测失败的兜底常量公开可知(`session/config.rs`) | ⏳ 仅限旧版 v1 迁移路径;建议后续 fail-closed 或告警,本次不动(避免迁移兼容风险) | +| P2 | R-006 | 日志目录未收紧权限(可能含主机名/用户名) | ✅ `app/startup.rs` 创建后 chmod 0700(Unix) | +| P2 | R-007 | `scripts/finalize-fork-hide.sh` 未跟踪且过期(rev/sed 目标均已失效) | ✅ 已删除 | +| P2 | R-008 | `ssh_config.rs` 不支持 tab 分隔、端口 0 被接受 | ✅ 支持 tab 分隔;端口 0 按默认 22 处理;补 12 项边界测试 | + +## 2. 优化建议(后续版本,按收益排序) + +| 编号 | 建议 | 说明 | +|---|---|---| +| O-001 | CI 平台矩阵补 `cargo test` | 现四平台只构建;单测多为纯逻辑,低成本发现平台差异(host_keys 权限、single_instance) | +| O-002 | llvm-cov 覆盖率接入 quality job + 下限阈值 | 防回归;当前无任何覆盖率度量 | +| O-003 | 高亮引擎性能 | `terminal/highlight.rs` 每帧正则:建议行级结果缓存/预编译;`render_snapshot` 评估 scrollback 上限;先按 TEST_PLAN 第 2 节实测再优化 | +| O-004 | cargo-deny 许可证合规 | GPL-3.0 项目依赖多,补 license 检查 | +| O-005 | fork 依赖安全跟踪 | gpui/russh/alacritty 均为 fork,cargo audit 覆盖不到 fork 内部改动;建立上游补丁跟踪流程 | +| O-006 | 发布工程 | Windows 代码签名(SmartScreen 提示)、macOS 公证(现 ad-hoc);发布审计清单模板化 | +| O-007 | 托盘后端迁移评估 | GTK3 链停维护(已豁免);长期评估 ksni 替代 | +| O-008 | i18n 硬编码文案扫描 | 键对齐已自动化;补硬编码字符串检测 | +| O-009 | CONTRIBUTING.md | 沉淀测试/审计规范与验证命令 | + +## 3. v1.0 上线前清单 + +### 3.1 阻塞级(发布前必须完成) + +| 编号 | 事项 | 状态 | +|---|---|---| +| B-001 | CI 红灯解除(audit 豁免)+ 全量门禁通过 | ✅ 已解除(本地复验);push 后确认 CI 绿 | +| B-002 | 安全测试落地(解包回归、shell_quote、i18n 对齐、ssh_config 边界) | ✅ 已落地(新增 30 项测试) | +| B-003 | beta.2 审计 4 项未验证关闭 | ⏳ ① CI/Release 托管验证:push+tag 后确认;② 非生产 Linux SSH/SFTP 主机复核权限/上传/删除/静默断线恢复;③ 远程编辑冲突/结果未知/断线保留内容复核;④ 真实 R2 上传/下载(需测试账户,不阻塞若提供不了) | +| B-004 | Windows `WM_ENDSESSION`(系统关机时托盘应用不保存状态) | ⏳ 需文档化降级方案(发布说明中明示);代码支持列 backlog | +| B-005 | 易用性关键路径实测:Linux 托盘左键无响应、GNOME Wayland 最小化被忽略 | ⏳ 发布说明中明示;UI 补偿("隐藏"代替"最小化")列 backlog | + +### 3.2 重要级 + +| 编号 | 事项 | 状态 | +|---|---|---| +| I-001 | Windows/macOS 代码签名与公证(分发体验) | ⏳ 需证书/Apple ID,列发布工程项 | +| I-002 | i18n 双语键完整性 | ✅ 键对齐自动化 + en.yml 补键 | +| I-003 | 发布审计清单模板化 + CONTRIBUTING | ⏳ O-006/O-009 合并执行 | +| I-004 | 性能基线首测(P-001 启动、P-002 大输出帧率、P-004 大文件) | ⏳ 发布前回填 TEST_PLAN 基线表 | + +### 3.3 可延后(backlog) + +TOFU 确认弹窗(R-004)、v1 兜底密钥加固(R-005)、WM_ENDSESSION、Wayland 最小化补偿、ksni 迁移、覆盖率、criterion bench、update-check 机制、平台矩阵测试。 + +--- + +## 4. 发布流程(v1.0 最终推送步骤) + +1. 版本升级:`Cargo.toml` → `1.0.0`,同步 `Cargo.lock`;`packaging/arch/PKGBUILD` `pkgver=1.0.0`、`pkgrel=1` +2. README.md / README.en.md 版本引用 → v1.0.0(历史审计文档保留原版本号) +3. 撰写 `.github/release-notes/v1.0.0.zh-CN.md`(发布必需) +4. `scripts/verify.sh` 全量门禁 + `scripts/smoke-linux.sh` 冒烟 +5. 提交 → push `main` → CI 绿 +6. tag `v1.0.0` 并 push(无 `-`,`release.yml` 自动发布为正式版 latest;tag 与 Cargo 版本一致性由 workflow 校验) +7. 发布后核查:Release 页非 prerelease、四平台产物齐全、PKGBUILD 版本一致 diff --git a/docs/TEST_PLAN.md b/docs/TEST_PLAN.md new file mode 100644 index 00000000..c5c7c398 --- /dev/null +++ b/docs/TEST_PLAN.md @@ -0,0 +1,216 @@ +# JShell 测试计划(v1.0) + +- 适用版本:v1.0.0(及后续版本) +- 测试依据:`README.md` 功能说明(需求基线)、`AUDIT_REPORT.md` 与 `docs/RELEASE_AUDIT_*.md`(历史审计与已知风险)、`docs/superpowers/specs/`(设计规格) +- 自动化现状:368+ 项内联单测(`cargo test --bin jshell`)、CI 质量门禁(fmt / test / check / clippy `-D warnings` / cargo audit `--deny warnings`) +- 执行工具:`scripts/verify.sh`(本地全量门禁)、`scripts/smoke-linux.sh`(Linux 冒烟) +- 本文档覆盖六类测试:功能、性能、兼容性、安全、回归、易用性。每类给出目标、用例清单(含自动化覆盖标注)、判定标准与执行频率。 + +--- + +## 1. 功能测试 + +**目标**:验证各功能点符合 README 需求与设计规格。纯逻辑以自动化单测覆盖;依赖 GUI/真实远端的功能以手工用例覆盖。 + +**自动化覆盖标注**:`[A]` 已有自动化测试;`[A+]` 本次新增自动化;`[M]` 手工用例(发布前执行)。 + +### 1.1 会话管理(`src/session/`、`src/app/dialogs.rs`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-001 | 新建/编辑/删除/克隆 SSH 会话并持久化到 `sessions.json` | 重启后会话保留;编辑不丢字段 | [A] `session/config.rs` 序列化往返测试;[M] GUI 表单流程 | +| F-002 | 会话文件夹:创建、重命名、批量连接、保存 | 文件夹层级正确持久化 | [M] | +| F-003 | 同一会话重复打开复用既有标签 | 不产生第二个连接标签 | [A] `connected_session_tab_id`;[M] 双开验证 | +| F-004 | 断线后原地重连,陈旧后端输出被抑制 | 旧 generation 事件不写入新会话 | [A] `terminal/mod.rs` generation 守卫 | +| F-005 | 端口/表单校验(非 0、1-65535) | 非法输入被拦截 | [A] `parse_non_zero_u16`;[M] 表单 | +| F-006 | `~/.ssh/config` 解析:Host/HostName/User/Port/IdentityFile、`=` 与 tab 分隔、注释、大小写 | 字段正确;Match/Include 安全忽略 | [A+] `ssh_config.rs` 14 项测试 | +| F-007 | SSH 认证四模式:密码、密钥文件、内联密钥、ssh-config | 各模式连通;默认密钥尝试顺序正确 | [A] `ssh_keys.rs`;[M] 真实主机 | +| F-008 | 代理:无/直连/继承/自定义(SOCKS5/SOCKS5H/HTTP/HTTPS) | 按策略解析、校验并建立通道 | [A] `config.rs` 代理解析测试(含 rcgen TLS);[M] 真实代理 | + +### 1.2 终端(`src/terminal/`、`src/backend/`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-010 | 本地终端(`$SHELL`,Windows 回退 powershell) | 交互正常 | [M] | +| F-011 | SSH 终端连接与交互 | 命令执行、输出回显正确 | [M] 非生产主机 | +| F-012 | 串口会话(波特率校验、CR 插入、虚拟口回退) | 参数异常有回退不崩溃 | [A] `serial.rs`;[M] 真实串口 | +| F-013 | 粘贴/选择/右键复制、鼠标选择 | 剪贴板行为正确 | [M] | +| F-014 | 终端搜索与高亮定位 | 匹配高亮、上下跳转 | [M] | +| F-015 | Ctrl+点击 URL 打开(仅 http/https 且边界正确) | 正确解析、跨行换行 URL 可解析 | [A+] `highlight.rs` URL 测试;[M] | +| F-016 | 日志关键词/状态码/IP/端口高亮 | 大小写不敏感、不重叠、边界正确 | [A+] `highlight.rs` 10 项测试 | +| F-017 | 命令历史拉取(bash/zsh/fish)与补全 | 解析正确、去重 | [A] `command_history.rs` | + +### 1.3 SFTP 文件管理(`src/sftp/`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-020 | 浏览/刷新/隐藏文件开关 | 列表正确 | [M] | +| F-021 | 上传/下载(单文件、目录 tar.gz 归档) | 完整性(大小一致)、进度显示 | [A] 传输状态测试;[M] | +| F-022 | 暂停/恢复/取消传输 | 状态机正确,取消不产生半成品 | [A] `TransferStateFlag` 测试 | +| F-023 | mkdir/删除(含多选、危险系统路径警告) | 提示与结果正确 | [M];[A] `shell_quote`(远程命令安全引用) | +| F-024 | 目录归档解包:正常条目、`..` 穿越条目、符号链接逃逸 | 正常解包;穿越被跳过;逃逸被拒绝 | [A+] 4 项解包回归测试 | +| F-025 | 静默断线检测与指数退避重连、手动重连、永久错误(认证/主机密钥)停止重试 | 状态图标与提示正确 | [A] `sftp_retry_policy`;[M] 断网演练 | +| F-026 | 终端 CWD 跟随(关闭/实时/切标签时) | 按所选模式跟随目录 | [A] `cwd_follow.rs` 状态机 | + +### 1.4 远程编辑器(`src/document/`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-030 | 双击打开远程文件、编辑、保存(临时文件+备份+原子替换) | 保存成功、权限保留 | [A] 控制器状态流;[M] 真实主机 | +| F-031 | 保存冲突检测(远端已变更) | 提示冲突,需显式覆盖 | [A] `run_save_check`;[M](beta.2 未验证项,v1.0 前必须) | +| F-032 | 保存结果未知(断线) | 保留脏状态、不盲目重试 | [A] `WriteFailureDisposition`;[M] 断网演练 | +| F-033 | 远端删除/断线后不自动保存 | 提示而非静默重建 | [M] | +| F-034 | 大文件分页只读、二进制/非 UTF-8 保护 | 不崩溃、提示只读 | [A] `large_file.rs`;[M] | +| F-035 | 分离窗口/重新吸附、全局软换行 | 窗口状态正确 | [M] | + +### 1.5 同步(`src/sync/`、`src/app/config_sync.rs`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-040 | WebDAV/S3/R2 上传下载、etag 冲突处理 | 冲突不静默覆盖 | [A] `sync/mod.rs`、`config_sync.rs` 29+27 项 | +| F-041 | 可移植载荷白名单(不含本机敏感字段) | 载荷仅含白名单字段 | [A] `payload.rs` | +| F-042 | 凭据持久化/清理(keyring) | 密钥环条目正确 | [A] `credential_store.rs` | +| F-043 | 真实 R2 对象上传/下载/条件覆盖 | 与本地模拟一致 | [M](beta.2 未验证项,需 R2 测试账户) | + +### 1.6 托盘 / 单实例 / 信号(`src/app/`) + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-050 | 关闭转托盘、托盘显示/退出、设置开关 | 状态保持、开关生效 | [M];[A] `system_tray` 配置默认值 | +| F-051 | 双实例:第二实例通知并立即退出 | 首实例保持运行 | [A] 逻辑测试;[M] `scripts/smoke-linux.sh` | +| F-052 | SIGTERM/SIGINT 优雅退出(3s 内、退出码 0、配置落盘) | 完整退出流程含未保存提示 | [M] `scripts/smoke-linux.sh` | +| F-053 | 二次信号强制终止 | 处理器恢复默认处置 | [A] 审计修复复验 | + +### 1.7 主题 / i18n / 键位 / 系统监控 + +| 编号 | 用例 | 预期 | 覆盖 | +|---|---|---|---| +| F-060 | 三套主题+跟随系统、字体/字号持久化 | 切换即时生效 | [A] `theme.rs` 解析;[M] | +| F-061 | 中英文切换、系统语言跟随 | 文案完整无缺键 | [A+] locale 键对齐测试(en/zh-CN 键集一致、值非空) | +| F-062 | 键位重绑与录制器 | 持久化、冲突提示 | [A] recorder 动作表;[M] | +| F-063 | 系统监控(CPU/内存/交换/网络/磁盘)、远端探针解析 | 数据合理、异常不崩溃 | [A] `remote_snapshot_from_kv`;[M] | + +**判定标准**:上述 [A]/[A+] 全部通过;[M] 用例在发布前按《发布就绪清单》(`docs/RELEASE_READINESS.md`)逐项执行并记录结果。 + +--- + +## 2. 性能测试 + +**目标**:确认交互流畅、内存受控、长时稳定。当前无自动化基准,采用手动测量规程;纯逻辑热点(高亮正则、加解密、known_hosts 解析)为后续 criterion bench 候选,本次不引入依赖。 + +**测量环境**:`cargo build --release` 产物;记录 OS/CPU/内存/GPU。工具:系统监视器 + `time` + 自测脚本。 + +| 编号 | 指标 | 步骤 | 目标阈值 | 基线(待回填) | +|---|---|---|---|---| +| P-001 | 启动时间 | 冷启动到窗口可交互(托盘/单实例初始化完成后) | < 2s | — | +| P-002 | 大输出终端帧率 | `yes` / `cat /dev/zero` 持续输出 60s,观察滚动流畅度与 CPU | 无明显掉帧;CPU 单核 < 60% | — | +| P-003 | scrollback 内存 | 10 万行输出后驻留内存增量 | < 300MB 增量 | — | +| P-004 | 大文件编辑器 | 打开 200MB 文本,分页翻页延迟 | 翻页 < 200ms,内存受控 | — | +| P-005 | SFTP 批量传输 | 1000 个小文件 + 1 个 1GB 大文件上传/下载吞吐 | 无内存线性增长;吞吐接近链路带宽 | — | +| P-006 | 多标签 16ms 事件泵 | 8 个活动 SSH 标签同时输出 | UI 事件延迟 < 16ms,泵无积压 | — | +| P-007 | 配置加载/加密 | 100 会话配置冷加载、v2 加解密耗时 | 加载 < 100ms | — | +| P-008 | 长时稳定性 | 连续运行 24h(含托盘驻留、SSH 空闲保活、日志轮转) | 无泄漏趋势(RSS 波动 < 10%) | — | + +**判定标准**:所有指标在阈值内;超出项在 `docs/RELEASE_READINESS.md` 记录并评估是否阻塞。每次发布前执行 P-001/P-002/P-004;大版本前执行全部。 + +--- + +## 3. 兼容性测试 + +**目标**:三平台 × 窗口系统 × 架构矩阵下功能一致。 + +| 平台 | 环境 | 必测项 | +|---|---|---| +| Windows | 10 / 11(x86_64) | 自定义标题栏与窗口控制、托盘左键恢复、命名管道单实例、`windows_subsystem` 无控制台、任务栏/开始菜单行为 | +| macOS | aarch64 / x86_64(ad-hoc 签名) | 红绿灯定位、`QuitMode::LastWindowClosed`、登录 shell 环境变量同步(代理变量)、NSStatusItem 托盘、Gatekeeper 提示(文档化) | +| Linux | X11(GNOME/KDE)、Wayland(GNOME/KDE) | GTK 托盘创建、appindicator 左键无响应(已知限制,菜单代替)、Wayland 最小化被忽略(GNOME,安全回退)、glibc 基线(ubuntu-22.04 构建产物) | + +**专项**: +- C-001 旧版 `ashell` 配置迁移:`~/.config/ashell/sessions.json` → 迁移后删除源([A] 迁移测试) +- C-002 配置前向兼容:未知字段/缺省字段以 serde 默认值加载不崩溃([A] serde 默认测试) +- C-003 已知平台风险回归(`AUDIT_REPORT.md` 第 5 节):每发布至少复核一次 +- C-004 CI 四平台 release 构建通过(自动化);平台矩阵当前只构建,建议后续补 `cargo test`(见 RELEASE_READINESS 优化项) + +**判定标准**:矩阵内无功能性阻塞;已知限制必须与文档一致。 + +--- + +## 4. 安全测试 + +**目标**:无凭据泄露、无注入、无越权写入、依赖无漏洞。 + +### 4.1 自动化(CI 常驻) +- `cargo audit --deny warnings`:0 漏洞;10 条「unmaintained」警告已按 `.cargo/audit.toml` 豁免(GTK3 托盘链,见注释与 `AUDIT_REPORT` 第 5 节) +- `cargo clippy -D warnings`、`cargo fmt --check` 常驻 +- 新增安全回归测试(本次): + - S-001 目录归档解包:tar/tar.gz `..` 条目跳过、符号链接逃逸拒绝、zip 正常解包 [A+] + - S-002 `shell_quote` 对抗输入(引号、换行、`$(...)`、反引号)单引号上下文不可逃逸 [A+] + - S-003 locale 键对齐与空值检查 [A+](防缺键回退显示键名) +- 既有:host-key 变更/撤销/排除拒绝且不修改 known_hosts(26 项)、密钥环锁定 fail-closed、v1→v2 迁移删源、同步载荷篡改/超限/重复 id 拒绝、密钥 Debug 脱敏、`zeroize` 使用 + +### 4.2 人工渗透/审计清单(发布前) +| 编号 | 检查项 | 依据位置 | +|---|---|---| +| S-010 | TOFU 主机密钥首次自动接受——确认是否需"提示确认"设置项(设计文档已记录该取舍) | `session/host_keys.rs`、`docs/superpowers/specs/2026-08-01-*` | +| S-011 | v1 加密硬件 UUID 兜底常量公开可知——评估 fail-closed | `session/config.rs:2213` | +| S-012 | HTTP 代理 Basic 凭据仅经 CONNECT 隧道发送(`http://` 代理为明文,文档化) | `session/config.rs:1951` | +| S-013 | 日志脱敏抽查:不得出现密码/密钥/代理凭据 | `~/.config/jshell/log` | +| S-014 | 配置目录/日志目录权限 0700(本次已收紧日志目录) | `session/config.rs`、`app/startup.rs` | +| S-015 | 远程命令执行路径复核:`tar`/`rm` 命令均经 `shell_quote`,300s 超时 | `sftp/mod.rs` | +| S-016 | 真实主机 host-key 变更/撤销全流程演练(非生产 Linux SSH/SFTP 主机) | beta.2 未验证项 | + +**判定标准**:4.1 全部通过;4.2 全部执行并记录结论,阻塞项见 RELEASE_READINESS。 + +--- + +## 5. 回归测试 + +**目标**:改动不破坏既有行为;368+ 项单测为回归基底。 + +- **触发策略**:每次提交前本地跑 `scripts/verify.sh`;PR/推送由 CI 全量执行;发布前追加 `scripts/smoke-linux.sh`。 +- **重点回归面**(历史缺陷多发区): + - 重连 stale 事件 generation 守卫(SSH/SFTP) + - 断线后不重放破坏性 SFTP 命令(上传/下载/删除) + - 保存结果未知状态机(`WriteFailureDisposition`) + - 托盘/单实例/信号:双实例退出、二次信号逃生、跨用户套接字隔离 + - 同步合并本地字段保护(`system_tray` 等 UI 状态不被远端覆盖) +- **发布审计衔接**:每次发布沿用 `AUDIT_REPORT` 流程(门禁结果 + 冒烟记录 + 已知风险更新);模板化建议见 RELEASE_READINESS。 + +**判定标准**:全量测试通过 + 冒烟通过;任何红项阻塞发布。 + +--- + +## 6. 易用性测试 + +**目标**:界面直观、交互顺畅、信息可达。 + +### 6.1 手工评估清单(发布前) +| 编号 | 检查项 | 通过标准 | +|---|---|---| +| U-001 | 首次启动:无配置时进入主页,引导创建会话 | 30s 内无文档即可完成首个本地会话 | +| U-002 | 会话/设置/传输对话框:Tab 顺序、必填校验提示 | 键盘可完成全流程 | +| U-003 | 窄窗口 960×700 布局无遮挡、可滚动 | 功能可达(beta.2 已验,回归确认) | +| U-004 | 标签颜色/状态语义(终端/文档/系统、连接/保存/错误) | 状态一眼可辨 | +| U-005 | 托盘可发现性:Windows/macOS 左键恢复;Linux 菜单显示/隐藏(左键为上游限制) | 行为与文档一致 | +| U-006 | 中英文文案完整、术语一致(缺键由自动化拦截) | 无键名回退显示 | +| U-007 | 错误提示可读可行动(连接失败、保存冲突、同步冲突) | 提示含下一步动作 | + +### 6.2 自动化 +- [A+] locale 键对齐测试:en/zh-CN 键集一致、值非空 +- [A] 主题解析、字体回退、布局状态持久化测试 + +**判定标准**:U-001~U-007 全部通过;自动化全绿。 + +--- + +## 附:执行频率汇总 + +| 类型 | 频率 | 载体 | +|---|---|---| +| 功能/回归(自动化) | 每次提交(CI)+ 本地 verify.sh | 内联单测 | +| 安全(自动化) | 每次提交(CI) | audit/clippy + 安全回归测试 | +| 冒烟(托盘/单实例/信号) | 每次发布前 | scripts/smoke-linux.sh | +| 功能/易用性手工用例 | 每次发布前 | 本文档 1/6 节清单 | +| 性能 | 发布前(P-001/002/004),大版本全量 | 本文档第 2 节规程 | +| 兼容性矩阵 | 发布前(三平台),平台专项按需 | 本文档第 3 节矩阵 | +| 渗透/审计清单 | 发布前 | 本文档第 4.2 节 | diff --git a/locales/en.yml b/locales/en.yml index f13e2f04..624a2123 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -96,6 +96,7 @@ system_path_warning: "WARNING: You are attempting to delete critical system path delete_success: "Successfully deleted %{count} items" delete_failed: "Failed to delete: %{err}" deleting_paths: "Deleting %{count} items..." +confirm: "Confirm" local_terminal: "Local Terminal" open_local_shell_tab: "Open a new local shell tab" connect: "Connect" @@ -218,6 +219,11 @@ keyword_highlight: "Terminal Keyword Highlighting" plugin_history_completion: "SSH History Completion" lock_layout: "Lock Window Layout" lock_layout_hint: "Lock the position of the sidebar and the SFTP panel." +system_tray: "System Tray" +system_tray_hint: "Minimize to the system tray on window close and keep sessions running in the background." +minimized_to_tray: "JShell is still running in the system tray." +tray_toggle_window: "Show / Hide Window" +tray_quit: "Quit" copy_paste_hint: "Selected text can also be copied by left-clicking." settings_copy: "Copy Selection" settings_paste: "Paste Clipboard" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 5ddc20b6..88ac6eef 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -219,6 +219,11 @@ keyword_highlight: "终端关键词高亮" plugin_history_completion: "SSH 历史命令补全" lock_layout: "锁定窗口布局" lock_layout_hint: "锁定侧边栏和 SFTP 面板的位置。" +system_tray: "系统托盘" +system_tray_hint: "关闭窗口时最小化到系统托盘,保持会话在后台运行。" +minimized_to_tray: "JShell 仍在系统托盘中运行。" +tray_toggle_window: "显示/隐藏窗口" +tray_quit: "退出" copy_paste_hint: "选中后左键点击也可复制。" settings_copy: "复制选区" settings_paste: "粘贴剪贴板" diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD new file mode 100644 index 00000000..363bb0f8 --- /dev/null +++ b/packaging/arch/PKGBUILD @@ -0,0 +1,42 @@ +# Maintainer: JShell contributors + +pkgname=jshell +pkgver=1.0.0 +pkgrel=1 +pkgdesc="SSH, SFTP and local terminal workspace built with GPUI" +arch=('x86_64') +url="https://github.com/JayhaShf/JShell" +license=('GPL-3.0-or-later') +depends=( + 'libxcb' + 'libxkbcommon' + 'libxkbcommon-x11' + 'mesa' + 'wayland' + 'libayatana-appindicator' +) +makedepends=( + 'clang' + 'git' + 'nasm' + 'rust' +) +options=(!lto !debug) +source=() + +build() { + cd "$startdir/../.." + cargo build --locked --release +} + +package() { + cd "$startdir/../.." + + install -Dm755 target/release/jshell "$pkgdir/usr/bin/jshell" + install -Dm644 assets/jshell.desktop \ + "$pkgdir/usr/share/applications/jshell.desktop" + install -Dm644 assets/icons/jshell.png \ + "$pkgdir/usr/share/icons/hicolor/256x256/apps/jshell.png" + install -Dm644 LICENSE \ + "$pkgdir/usr/share/licenses/$pkgname/LICENSE" +} diff --git a/scripts/smoke-linux.sh b/scripts/smoke-linux.sh new file mode 100755 index 00000000..404a65f9 --- /dev/null +++ b/scripts/smoke-linux.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# JShell Linux 冒烟测试:启动、双实例互斥、SIGTERM/SIGINT 优雅退出、 +# 退出时配置落盘与残留进程检查(对应 AUDIT_REPORT 第 4 节的冒烟项)。 +# +# 使用隔离 HOME(mktemp),不触碰真实用户配置;需要图形环境 +# (DISPLAY 或 WAYLAND_DISPLAY),无图形环境时自动跳过。 +# 用法: ./scripts/smoke-linux.sh + +set -u + +cd "$(dirname "$0")/.." || exit 1 + +if [[ -z "${DISPLAY:-}" && -z "${WAYLAND_DISPLAY:-}" ]]; then + echo "SKIP: no DISPLAY/WAYLAND_DISPLAY; smoke test needs a graphical session" + exit 0 +fi + +if ! cargo build --locked >/dev/null 2>&1; then + echo "FAIL: debug build failed" + exit 1 +fi +BIN="$(pwd)/target/debug/jshell" + +TMP_HOME="$(mktemp -d)" +APP_PID="" +cleanup() { + if [[ -n "$APP_PID" ]]; then + kill -KILL "$APP_PID" 2>/dev/null + fi + rm -rf "$TMP_HOME" +} +trap cleanup EXIT + +fails=0 +check() { + local name="$1" + shift + if "$@"; then + echo "PASS: $name" + else + echo "FAIL: $name" + fails=$((fails + 1)) + fi +} + +# 在 timeout 秒内等待进程退出;退出返回 0,超时返回 1。 +wait_until_gone() { + local timeout="$1" pid="$2" ticks=0 + while kill -0 "$pid" 2>/dev/null; do + sleep 0.2 + ticks=$((ticks + 1)) + if [[ $ticks -ge $((timeout * 5)) ]]; then + return 1 + fi + done + return 0 +} + +echo "==> smoke home: $TMP_HOME" + +# 1. 启动冒烟:进程存活 5 秒、日志无 panic +HOME="$TMP_HOME" "$BIN" >/dev/null 2>&1 & +APP_PID=$! +sleep 5 +check "app survives startup (5s, no crash)" kill -0 "$APP_PID" +LOG_DIR="$TMP_HOME/.config/jshell/log" +if [[ -d "$LOG_DIR" ]]; then + check "no panic in startup logs" sh -c "! grep -r panic \"$LOG_DIR\" 2>/dev/null | grep -q ." +fi + +# 2. 双实例:第二实例立即退出且不影响首实例 +SECOND_EXIT="$(HOME="$TMP_HOME" "$BIN" >/dev/null 2>&1; echo $?)" +check "second instance exits with code 0" test "$SECOND_EXIT" = "0" +check "first instance still running" kill -0 "$APP_PID" + +# 3. SIGTERM 优雅退出(3 秒内、退出码 0) +kill -TERM "$APP_PID" +if wait_until_gone 3 "$APP_PID"; then + check "SIGTERM exits within 3s" true +else + check "SIGTERM exits within 3s" false + kill -KILL "$APP_PID" 2>/dev/null +fi +wait "$APP_PID" 2>/dev/null +TERM_CODE=$? +check "SIGTERM exit code is 0" test "$TERM_CODE" = "0" + +# 4. SIGINT 优雅退出 +HOME="$TMP_HOME" "$BIN" >/dev/null 2>&1 & +APP_PID=$! +sleep 5 +kill -INT "$APP_PID" +if wait_until_gone 3 "$APP_PID"; then + check "SIGINT exits within 3s" true +else + check "SIGINT exits within 3s" false + kill -KILL "$APP_PID" 2>/dev/null +fi +wait "$APP_PID" 2>/dev/null +APP_PID="" + +# 5. 退出时配置落盘 +check "config persisted on exit" test -f "$TMP_HOME/.config/jshell/sessions.json" + +# 6. 无残留进程 +sleep 1 +if command -v pgrep >/dev/null 2>&1; then + check "no leftover jshell processes" sh -c "! pgrep -x jshell >/dev/null 2>&1" +fi + +echo +if [[ "$fails" -eq 0 ]]; then + echo "Smoke tests passed." +else + echo "$fails smoke check(s) failed." + exit 1 +fi diff --git a/scripts/verify.sh b/scripts/verify.sh new file mode 100755 index 00000000..d9b706e6 --- /dev/null +++ b/scripts/verify.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# JShell 本地一键验证套件(对应 README「验证」章节与 CI 质量门禁)。 +# 用法: ./scripts/verify.sh [--skip-build] +# 退出码非 0 表示某一项检查未通过;每个步骤独立报告结果,最后汇总。 + +set -u + +cd "$(dirname "$0")/.." || exit 1 + +SKIP_BUILD=0 +if [[ "${1:-}" == "--skip-build" ]]; then + SKIP_BUILD=1 +fi + +failures=0 +run_step() { + local name="$1" + shift + echo "==> $name" + if "$@"; then + echo " PASS" + else + echo " FAIL ($name)" + failures=$((failures + 1)) + fi +} + +run_step "cargo fmt --check" cargo fmt --check +run_step "cargo test --locked" cargo test --locked --quiet +run_step "cargo check --all-targets" cargo check --locked --all-targets +run_step "cargo clippy (-D warnings)" cargo clippy --locked --all-targets --all-features -- -D warnings +if [[ "$SKIP_BUILD" -eq 0 ]]; then + run_step "cargo build --release" cargo build --locked --release +fi +if command -v cargo-audit >/dev/null 2>&1 || cargo audit --version >/dev/null 2>&1; then + run_step "cargo audit" cargo audit --file Cargo.lock +else + echo "==> cargo audit: SKIPPED (cargo-audit not installed)" +fi +run_step "git diff --check" git diff --check + +echo +if [[ "$failures" -eq 0 ]]; then + echo "All verification steps passed." +else + echo "$failures verification step(s) failed." + exit 1 +fi diff --git a/src/app/dialogs.rs b/src/app/dialogs.rs index 5becd5cb..0d82bdf2 100644 --- a/src/app/dialogs.rs +++ b/src/app/dialogs.rs @@ -20,6 +20,7 @@ use rust_i18n::t; use crate::{ Ashell, + app::tray::TrayController, session::{ SessionProxyPolicy, can_submit_ssh_session, config::AuthMethod, parse_non_zero_u16, session_proxy_policy, supported_proxy_protocol, @@ -2392,6 +2393,40 @@ impl Ashell { }) ).description(t!("lock_layout_hint").to_string()) ) + .item( + SettingItem::new( + t!("system_tray").to_string(), + SettingField::render({ + let view = view_clone_for_general.clone(); + move |_, window, cx| { + Switch::new("system-tray") + .small() + .checked(view.read(cx).config.system_tray()) + .on_click(window.listener_for(&view, |this, checked, _, cx| { + this.config.set_system_tray(*checked); + if *checked && this.tray.is_none() { + match TrayController::new() { + Ok(tray) => this.tray = Some(tray), + Err(error) => { + tracing::warn!( + "failed to enable system tray: {error:#}" + ); + this.config.set_system_tray(false); + } + } + } + if !this.config.system_tray() { + this.tray = None; + this.tray_window_visible = true; + } + this.save_preferences_background(); + cx.notify(); + })) + .into_any_element() + } + }) + ).description(t!("system_tray_hint").to_string()) + ) .item( SettingItem::new( t!("monitoring_position").to_string(), diff --git a/src/app/mod.rs b/src/app/mod.rs index d321ee08..62cfd230 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -6,8 +6,11 @@ pub mod keybinding_recorder; pub mod pane_layout; pub mod resizable; pub mod search; +pub mod signals; +pub mod single_instance; pub mod startup; pub mod theme; +pub mod tray; pub mod ui; pub mod workspace_tabs; @@ -26,6 +29,7 @@ use std::{ use crate::app::resizable::ResizableState; use crate::app::startup::StartupConfig; +use crate::app::tray::{TrayController, TrayRequest}; use gpui::{ AnyWindowHandle, AppContext as _, Bounds, Context, Entity, FocusHandle, Pixels, Point, SharedString, Size, UniformListScrollHandle, Window, point, px, size, @@ -331,6 +335,10 @@ pub(crate) struct Ashell { pub(crate) window_close_prompt_open: bool, pub(crate) window_close_save_queue: Vec, pub(crate) window_close_save_current: Option, + pub(crate) tray: Option, + pub(crate) tray_window_visible: bool, + pub(crate) instance_events: Option>, + pub(crate) signal_events: Option>, pub(crate) selector_selection: usize, pub(crate) workspace_panels: Entity, pub(crate) body_panels: Entity, @@ -504,6 +512,8 @@ impl Ashell { window: &mut Window, cx: &mut Context, startup_config: StartupConfig, + instance_events: Option>, + signal_events: Option>, ) -> Self { let StartupConfig { config, @@ -871,6 +881,10 @@ impl Ashell { window_close_prompt_open: false, window_close_save_queue: Vec::new(), window_close_save_current: None, + tray: None, + tray_window_visible: true, + instance_events, + signal_events, pane_root: PaneLayout::empty(), focused_pane_path: Vec::new(), terminal_panel_bounds: None, @@ -964,9 +978,20 @@ impl Ashell { config_writes: ConfigWriteCoordinator::default(), }; + if this.config.system_tray() { + match TrayController::new() { + Ok(tray) => this.tray = Some(tray), + Err(error) => { + tracing::warn!( + "system tray unavailable, closing the window will quit: {error:#}" + ) + } + } + } + this.apply_theme_preferences(window, cx); // this.open_local(cx); - this.start_event_pump(cx); + this.start_event_pump(window, cx); this } @@ -1164,20 +1189,41 @@ impl Ashell { coordinator.run_exclusive(|| self.config.save()) } - pub(crate) fn start_event_pump(&self, cx: &mut Context) { - cx.spawn(async move |this, cx| { + pub(crate) fn start_event_pump(&self, window: &mut Window, cx: &mut Context) { + cx.spawn_in(window, async move |this, cx| { let mut last_blink_time = std::time::Instant::now(); loop { cx.background_executor() .timer(Duration::from_millis(16)) .await; if this - .update(cx, |this, cx| { + .update_in(cx, |this, window, cx| { let now = std::time::Instant::now(); let changed = this.drain_backend_events(); let cwd_follow_changed = this.expire_sftp_cwd_follow_requests(now); let system_sampled = this.sample_system_if_due(); this.sync_theme_if_due(cx); + this.poll_tray_requests(window, cx); + if this + .instance_events + .as_ref() + .is_some_and(|events| events.try_recv().is_ok()) + { + // Another launch asked this instance to show its window. + this.show_from_tray(window, cx); + } + if !this.closing_application + && this + .signal_events + .as_ref() + .is_some_and(|events| events.try_recv().is_ok()) + { + // A termination signal was received: restore the window and + // go through the regular quit flow, bypassing close-to-tray. + this.closing_application = true; + this.show_from_tray(window, cx); + this.request_application_close(window, cx); + } let is_blinking = matches!( this.cursor_style, crate::session::config::CursorStyle::Blink @@ -1202,6 +1248,52 @@ impl Ashell { .detach(); } + pub(crate) fn hide_to_tray(&mut self, window: &mut Window, cx: &mut Context) { + if self.tray.is_none() { + return; + } + self.tray_window_visible = false; + // Fully hide the window (no taskbar entry, no visible surface) while + // keeping the process and its sessions alive, then activate the tray + // so the window cannot be reached from alt-tab either. + window.set_visible(false); + self.status = t!("minimized_to_tray").into(); + cx.notify(); + } + + pub(crate) fn show_from_tray(&mut self, window: &mut Window, cx: &mut Context) { + self.tray_window_visible = true; + window.set_visible(true); + window.activate_window(); + cx.notify(); + } + + pub(crate) fn poll_tray_requests(&mut self, window: &mut Window, cx: &mut Context) { + let Some(tray) = self.tray.as_ref() else { + return; + }; + let requests: Vec = std::iter::from_fn(|| tray.try_recv()).collect(); + for request in requests { + match request { + TrayRequest::ToggleWindow => { + if self.tray_window_visible { + self.hide_to_tray(window, cx); + } else { + self.show_from_tray(window, cx); + } + } + TrayRequest::ShowWindow => { + self.show_from_tray(window, cx); + } + TrayRequest::Quit => { + // Restore the window first so the unsaved-document prompt is visible. + self.show_from_tray(window, cx); + self.request_application_close(window, cx); + } + } + } + } + pub(crate) fn drain_backend_events(&mut self) -> bool { let mut changed = false; let mut transfers_changed = false; diff --git a/src/app/signals.rs b/src/app/signals.rs new file mode 100644 index 00000000..2091dc8a --- /dev/null +++ b/src/app/signals.rs @@ -0,0 +1,46 @@ +use std::sync::mpsc::{self, Receiver}; + +/// Installs termination signal handling (SIGINT/SIGTERM on Unix) and returns a +/// channel that delivers a notification when the process is asked to shut down. +/// +/// The main event pump drains the channel and routes the request through the +/// regular quit flow, so layout state is saved and the tray icon is cleaned up +/// instead of the process being killed abruptly. +pub(crate) fn install() -> Option> { + #[cfg(unix)] + { + use signal_hook::consts::{SIGINT, SIGTERM}; + let mut signals = match signal_hook::iterator::Signals::new([SIGINT, SIGTERM]) { + Ok(signals) => signals, + Err(error) => { + tracing::warn!("failed to install termination signal handlers: {error}"); + return None; + } + }; + let (tx, rx) = mpsc::channel(); + if std::thread::Builder::new() + .name("jshell-signals".to_string()) + .spawn(move || { + let handle = signals.handle(); + let mut forever = signals.forever(); + if let Some(signal) = forever.next() { + tracing::info!("received signal {signal}, shutting down gracefully"); + let _ = tx.send(()); + // Restore the default disposition so a second signal can + // force-kill the process if the graceful quit gets stuck. + handle.close(); + } + }) + .is_err() + { + tracing::warn!("failed to spawn termination signal thread"); + return None; + } + Some(rx) + } + + #[cfg(not(unix))] + { + None + } +} diff --git a/src/app/single_instance.rs b/src/app/single_instance.rs new file mode 100644 index 00000000..feba6663 --- /dev/null +++ b/src/app/single_instance.rs @@ -0,0 +1,134 @@ +use std::{ + io::{Read, Write}, + sync::mpsc::{Receiver, Sender}, + time::Duration, +}; + +use interprocess::local_socket::{GenericNamespaced, ListenerOptions, prelude::*}; + +const INSTANCE_SOCKET_NAME: &str = "jshell-single-instance"; +const ACTIVATE_PAYLOAD: &[u8] = b"activate"; +/// How many times to retry the bind/connect handshake before running unlocked. +const ACQUIRE_ATTEMPTS: usize = 3; + +pub(crate) enum AcquireOutcome { + /// This process is the first instance and receives activation requests + /// from later launches. + First(Receiver<()>), + /// Another instance is already running and has been asked to show its window. + Second, +} + +fn instance_socket_name() -> String { + #[cfg(unix)] + { + // Linux abstract sockets and the shared `/tmp` namespace are visible to + // every user, so include the user id to keep instances apart. + let uid = unsafe { libc::getuid() }; + format!("{INSTANCE_SOCKET_NAME}-{uid}") + } + #[cfg(not(unix))] + { + INSTANCE_SOCKET_NAME.to_string() + } +} + +/// Ensures only one JShell instance runs per user session. +/// +/// The first instance binds a platform-local socket (`\\.\pipe\` on Windows, +/// the abstract namespace on Linux, `/tmp/` elsewhere). Later launches connect +/// to it, ask the running instance to activate its window, and exit. +pub(crate) fn acquire() -> AcquireOutcome { + for _ in 0..ACQUIRE_ATTEMPTS { + let name = match instance_socket_name().to_ns_name::() { + Ok(name) => name, + Err(error) => { + tracing::warn!("failed to build single instance socket name: {error}"); + return AcquireOutcome::First(closed_receiver()); + } + }; + match ListenerOptions::new().name(name.clone()).create_sync() { + Ok(listener) => { + let (tx, rx) = std::sync::mpsc::channel(); + spawn_accept_loop(listener, tx); + return AcquireOutcome::First(rx); + } + Err(bind_error) => { + tracing::info!( + "single instance listener busy ({bind_error}), asking the running instance to activate" + ); + match LocalSocketStream::connect(name.clone()) { + Ok(mut stream) => { + let _ = stream.write_all(ACTIVATE_PAYLOAD); + let _ = stream.flush(); + return AcquireOutcome::Second; + } + Err(connect_error) => { + tracing::warn!( + "failed to reach the running instance ({connect_error}), retrying" + ); + remove_stale_socket(); + std::thread::sleep(Duration::from_millis(200)); + } + } + } + } + } + + // The lock could not be established: run unlocked instead of failing to start. + tracing::warn!("could not establish single instance lock, running unlocked"); + AcquireOutcome::First(closed_receiver()) +} + +fn spawn_accept_loop(listener: LocalSocketListener, tx: Sender<()>) { + match std::thread::Builder::new() + .name("jshell-instance".to_string()) + .spawn(move || { + loop { + match listener.accept() { + Ok(stream) => { + // Handle each connection on a short-lived thread so a + // stalled peer cannot block further activation requests. + let tx = tx.clone(); + std::thread::spawn(move || { + let mut buffer = [0u8; ACTIVATE_PAYLOAD.len()]; + let mut stream = stream; + if stream.read_exact(&mut buffer).is_ok() && buffer == ACTIVATE_PAYLOAD + { + let _ = tx.send(()); + } + }); + } + Err(error) => { + tracing::warn!("single instance listener error: {error}"); + break; + } + } + } + }) { + Ok(_) => {} + Err(error) => { + // The listener is dropped here, which releases the socket so later + // launches can still bind it. + tracing::warn!("failed to spawn single instance listener thread: {error}"); + } + } +} + +/// On platforms where the socket is a filesystem entry, a crashed instance can +/// leave a stale file behind that no longer accepts connections. +fn remove_stale_socket() { + #[cfg(all(unix, not(target_os = "linux")))] + { + let path = std::path::Path::new("/tmp").join(instance_socket_name()); + if std::fs::remove_file(&path).is_ok() { + tracing::info!("removed stale single instance socket at {}", path.display()); + } + } +} + +fn closed_receiver() -> Receiver<()> { + let (tx, rx) = std::sync::mpsc::channel(); + drop(tx); + rx +} diff --git a/src/app/startup.rs b/src/app/startup.rs index 7a3f3bfa..50687de0 100644 --- a/src/app/startup.rs +++ b/src/app/startup.rs @@ -119,6 +119,19 @@ pub(crate) fn init_logging() { std::fs::create_dir_all(&log_dir).ok(); + // Logs may contain hostnames and usernames; keep the directory private + // like the config directory (init_logging runs before the config store + // exists, so this cannot rely on its chmod). + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if let Ok(meta) = std::fs::metadata(&log_dir) { + let mut perms = meta.permissions(); + perms.set_mode(0o700); + let _ = std::fs::set_permissions(&log_dir, perms); + } + } + let roller = LocalMinutelyRoller::new(log_dir.clone(), "jshell".to_string()); let (non_blocking, _guard) = tracing_appender::non_blocking(roller); @@ -212,10 +225,15 @@ pub(crate) fn sync_macos_launch_environment() {} pub(crate) fn open_main_window(cx: &mut App) { let startup_config = StartupConfig::load(); - open_main_window_with_config(cx, startup_config); + open_main_window_with_config(cx, startup_config, None, None); } -pub(crate) fn open_main_window_with_config(cx: &mut App, startup_config: StartupConfig) { +pub(crate) fn open_main_window_with_config( + cx: &mut App, + startup_config: StartupConfig, + instance_events: Option>, + signal_events: Option>, +) { let config = &startup_config.config; crate::session::config::initialize_env_proxy(); @@ -286,7 +304,8 @@ pub(crate) fn open_main_window_with_config(cx: &mut App, startup_config: Startup window.activate_window(); window.set_window_title("JShell"); gpui_component::Theme::sync_system_appearance(Some(window), cx); - let view = cx.new(|cx| Ashell::new(window, cx, startup_config)); + let view = + cx.new(|cx| Ashell::new(window, cx, startup_config, instance_events, signal_events)); tracing::info!("[ui] main application window opened"); let focus_handle = view.read(cx).focus_handle.clone(); @@ -308,6 +327,13 @@ pub(crate) fn open_main_window_with_config(cx: &mut App, startup_config: Startup }); return true; } + if view_clone.read(cx).tray.is_some() && !view_clone.read(cx).closing_application { + // Keep the window (and every session) alive in the background. + view_clone.update(cx, |this, cx| { + this.hide_to_tray(window, cx); + }); + return false; + } if view_clone.read(cx).has_dirty_documents() { view_clone.update(cx, |this, cx| { this.request_application_close(window, cx); diff --git a/src/app/tray.rs b/src/app/tray.rs new file mode 100644 index 00000000..dd3ab6d0 --- /dev/null +++ b/src/app/tray.rs @@ -0,0 +1,213 @@ +use std::sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, Sender}, +}; + +use rust_i18n::t; +use tray_icon::menu::{Menu, MenuEvent, MenuId, MenuItem, PredefinedMenuItem}; +use tray_icon::{Icon, TrayIconBuilder}; + +#[cfg(not(target_os = "linux"))] +use tray_icon::{MouseButton, MouseButtonState, TrayIcon, TrayIconEvent}; + +pub(crate) enum TrayRequest { + ToggleWindow, + /// Always restore/activate the window, regardless of how it was minimized. + #[cfg_attr(target_os = "linux", allow(dead_code))] + ShowWindow, + Quit, +} + +const TOGGLE_WINDOW_ID: &str = "tray-toggle-window"; +const QUIT_ID: &str = "tray-quit"; + +/// `muda`'s global event handler can only be installed once per process, so +/// requests are forwarded through a swappable sender that is replaced each +/// time the tray is recreated. +static REQUEST_TX: Mutex>> = Mutex::new(None); + +fn install_global_handlers() { + use std::sync::Once; + static INSTALLED: Once = Once::new(); + INSTALLED.call_once(|| { + MenuEvent::set_event_handler(Some(|event: MenuEvent| { + let request = if event.id() == &MenuId::new(TOGGLE_WINDOW_ID) { + Some(TrayRequest::ToggleWindow) + } else if event.id() == &MenuId::new(QUIT_ID) { + Some(TrayRequest::Quit) + } else { + None + }; + if let Some(request) = request { + send_request(request); + } + })); + #[cfg(not(target_os = "linux"))] + TrayIconEvent::set_event_handler(Some(|event: TrayIconEvent| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = event + { + // Left-click always restores the window so it never gets stuck + // re-minimizing after the taskbar was used to minimize it. + send_request(TrayRequest::ShowWindow); + } + })); + }); +} + +fn send_request(request: TrayRequest) { + if let Ok(mut tx) = REQUEST_TX.lock() + && let Some(tx) = tx.as_mut() + { + let _ = tx.send(request); + } +} + +fn set_request_tx(tx: Sender) { + *REQUEST_TX.lock().unwrap_or_else(|error| error.into_inner()) = Some(tx); +} + +fn clear_request_tx() { + *REQUEST_TX.lock().unwrap_or_else(|error| error.into_inner()) = None; +} + +fn build_menu() -> anyhow::Result { + let toggle_item = MenuItem::with_id( + MenuId::new(TOGGLE_WINDOW_ID), + t!("tray_toggle_window"), + true, + None, + ); + let quit_item = MenuItem::with_id(MenuId::new(QUIT_ID), t!("tray_quit"), true, None); + let menu = Menu::new(); + menu.append(&toggle_item)?; + menu.append(&PredefinedMenuItem::separator())?; + menu.append(&quit_item)?; + Ok(menu) +} + +fn load_tray_icon() -> anyhow::Result { + let image = image::load_from_memory(include_bytes!("../../assets/icons/jshell.png")) + .map_err(|error| anyhow::anyhow!("failed to decode tray icon: {error}"))? + .into_rgba8(); + let (width, height) = image.dimensions(); + Icon::from_rgba(image.into_raw(), width, height) + .map_err(|error| anyhow::anyhow!("failed to build tray icon: {error}")) +} + +/// Owns the platform tray icon and delivers user requests through a channel +/// that the main event pump drains on the UI thread. +pub(crate) struct TrayController { + #[cfg(not(target_os = "linux"))] + _icon: TrayIcon, + rx: Receiver, + #[cfg(target_os = "linux")] + shutdown: Arc, + #[cfg(target_os = "linux")] + _thread: Option>, +} + +impl TrayController { + #[cfg(not(target_os = "linux"))] + pub(crate) fn new() -> anyhow::Result { + install_global_handlers(); + let (tx, rx) = mpsc::channel(); + set_request_tx(tx); + let icon = load_tray_icon()?; + let icon = TrayIconBuilder::new() + .with_icon(icon) + .with_tooltip("JShell") + .with_menu(Box::new(build_menu()?)) + .build() + .map_err(|error| anyhow::anyhow!("failed to create system tray icon: {error}"))?; + Ok(Self { _icon: icon, rx }) + } + + /// On Linux the tray icon must live on the thread that pumps the GTK event + /// loop, so it is created and owned by a dedicated thread. + #[cfg(target_os = "linux")] + pub(crate) fn new() -> anyhow::Result { + install_global_handlers(); + let (tx, rx) = mpsc::channel(); + set_request_tx(tx); + let icon = load_tray_icon()?; + let (ready_tx, ready_rx) = mpsc::channel(); + let shutdown = Arc::new(AtomicBool::new(false)); + let thread_shutdown = shutdown.clone(); + let thread = std::thread::Builder::new() + .name("jshell-tray".to_string()) + .spawn(move || { + if !gtk::is_initialized() + && let Err(error) = gtk::init() + { + let _ = ready_tx.send(Err(anyhow::anyhow!( + "failed to initialize GTK for the system tray: {error}" + ))); + return; + } + let menu = match build_menu() { + Ok(menu) => menu, + Err(error) => { + let _ = ready_tx.send(Err(error)); + return; + } + }; + let icon = match TrayIconBuilder::new() + .with_icon(icon) + .with_tooltip("JShell") + .with_menu(Box::new(menu)) + .build() + { + Ok(icon) => icon, + Err(error) => { + let _ = ready_tx.send(Err(anyhow::anyhow!( + "failed to create system tray icon: {error}" + ))); + return; + } + }; + let _ = ready_tx.send(Ok(())); + // Pump the thread's main context so both GTK and D-Bus sources + // are processed: the StatusNotifier registration and menu events + // are delivered over D-Bus, which `gtk::events_pending()` ignores. + let main_context = gtk::glib::MainContext::default(); + while !thread_shutdown.load(Ordering::Relaxed) { + main_context.iteration(false); + std::thread::sleep(std::time::Duration::from_millis(8)); + } + drop(icon); + }) + .map_err(|error| anyhow::anyhow!("failed to spawn tray thread: {error}"))?; + + ready_rx + .recv() + .map_err(|_| anyhow::anyhow!("tray thread exited before creating the icon"))??; + + Ok(Self { + rx, + shutdown, + _thread: Some(thread), + }) + } + + pub(crate) fn try_recv(&self) -> Option { + self.rx.try_recv().ok() + } +} + +impl Drop for TrayController { + fn drop(&mut self) { + clear_request_tx(); + #[cfg(target_os = "linux")] + { + self.shutdown.store(true, Ordering::Relaxed); + if let Some(thread) = self._thread.take() { + let _ = thread.join(); + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 235ceb18..d64b62b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,16 @@ fn main() { app::startup::sync_macos_launch_environment(); app::startup::init_logging(); + let instance_events = match app::single_instance::acquire() { + app::single_instance::AcquireOutcome::First(events) => Some(events), + app::single_instance::AcquireOutcome::Second => { + // Another instance is already running and has been asked to show + // its window, so this process has nothing left to do. + return; + } + }; + let signal_events = app::signals::install(); + #[cfg(target_os = "macos")] let app = gpui_platform::application() .with_assets(Assets) @@ -39,6 +49,11 @@ fn main() { app.on_reopen(|cx| { if cx.windows().is_empty() { app::startup::open_main_window(cx); + } else if let Some(handle) = cx.windows().first().cloned() { + // Restore the window when the dock icon is clicked while hidden in the tray. + handle + .update(cx, |_, window, _| window.activate_window()) + .ok(); } }); app.run(move |cx| { @@ -60,6 +75,61 @@ fn main() { if let Err(err) = app::theme::load_fonts(cx) { tracing::warn!("failed to load theme fonts: {err:#}"); } - app::startup::open_main_window_with_config(cx, startup_config); + app::startup::open_main_window_with_config( + cx, + startup_config, + instance_events, + signal_events, + ); }); } + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + use std::path::Path; + + /// Loads a rust-i18n flat `key: "value"` locale file into a map. + fn load_locale(name: &str) -> BTreeMap { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("locales") + .join(name); + let content = std::fs::read_to_string(&path) + .unwrap_or_else(|err| panic!("failed to read {}: {err}", path.display())); + serde_yaml::from_str::>(&content) + .unwrap_or_else(|err| panic!("failed to parse {}: {err}", path.display())) + } + + #[test] + fn locale_keys_match_between_english_and_chinese() { + let en = load_locale("en.yml"); + let zh = load_locale("zh-CN.yml"); + + let missing_in_zh: Vec<&String> = en.keys().filter(|k| !zh.contains_key(*k)).collect(); + let missing_in_en: Vec<&String> = zh.keys().filter(|k| !en.contains_key(*k)).collect(); + + assert!( + missing_in_zh.is_empty(), + "keys present in en.yml but missing from zh-CN.yml: {missing_in_zh:?}", + ); + assert!( + missing_in_en.is_empty(), + "keys present in zh-CN.yml but missing from en.yml: {missing_in_en:?}", + ); + } + + #[test] + fn locale_values_are_non_empty_in_both_languages() { + for (name, map) in [ + ("en.yml", load_locale("en.yml")), + ("zh-CN.yml", load_locale("zh-CN.yml")), + ] { + let empty: Vec<&String> = map + .iter() + .filter(|(_, v)| v.is_empty()) + .map(|(k, _)| k) + .collect(); + assert!(empty.is_empty(), "empty translations in {name}: {empty:?}"); + } + } +} diff --git a/src/session/config.rs b/src/session/config.rs index 63d35e89..5822db53 100644 --- a/src/session/config.rs +++ b/src/session/config.rs @@ -270,6 +270,8 @@ pub struct ConfigFile { pub sidebar_collapsed: bool, #[serde(default)] pub sftp_panel_minimized: bool, + #[serde(default = "default_system_tray")] + pub system_tray: bool, #[serde(default)] pub sftp_cwd_sync_mode: SftpCwdSyncMode, #[serde(default)] @@ -338,6 +340,10 @@ fn default_monitoring_position() -> String { "Sidebar".to_string() } +fn default_system_tray() -> bool { + true +} + fn default_s3_region() -> String { "us-east-1".to_string() } @@ -451,6 +457,7 @@ impl Default for ConfigFile { monitoring_position: default_monitoring_position(), sidebar_collapsed: false, sftp_panel_minimized: false, + system_tray: default_system_tray(), sftp_cwd_sync_mode: SftpCwdSyncMode::default(), key_bindings: std::collections::HashMap::new(), sync_endpoint: String::new(), @@ -1395,6 +1402,14 @@ impl ConfigStore { self.cache.sftp_panel_minimized = val; } + pub fn system_tray(&self) -> bool { + self.cache.system_tray + } + + pub fn set_system_tray(&mut self, val: bool) { + self.cache.system_tray = val; + } + pub fn sftp_cwd_sync_mode(&self) -> SftpCwdSyncMode { self.cache.sftp_cwd_sync_mode } @@ -1679,6 +1694,7 @@ impl ConfigStore { disk_config.monitoring_position = local_config.monitoring_position; disk_config.sidebar_collapsed = local_config.sidebar_collapsed; disk_config.sftp_panel_minimized = local_config.sftp_panel_minimized; + disk_config.system_tray = local_config.system_tray; disk_config.sftp_cwd_sync_mode = local_config.sftp_cwd_sync_mode; disk_config.key_bindings = local_config.key_bindings; disk_config.use_proxy = local_config.use_proxy; @@ -2489,6 +2505,7 @@ mod tests { }], sidebar_collapsed: true, sftp_panel_minimized: true, + system_tray: false, sync_endpoint: "https://local-webdav.test/config".to_string(), sync_username: "local-webdav-user".to_string(), sync_etag: Some("old-etag".to_string()), @@ -2518,6 +2535,7 @@ mod tests { "transfers": &config.transfers, "sidebar_collapsed": config.sidebar_collapsed, "sftp_panel_minimized": config.sftp_panel_minimized, + "system_tray": config.system_tray, "sync_endpoint": &config.sync_endpoint, "sync_username": &config.sync_username, "sync_device_id": &config.sync_device_id, @@ -3381,6 +3399,22 @@ mod tests { assert_eq!(decrypted.sftp_cwd_sync_mode, SftpCwdSyncMode::Realtime); } + #[test] + fn system_tray_defaults_to_enabled_and_serializes() { + let legacy_config: ConfigFile = serde_json::from_str("{}").unwrap(); + assert!(legacy_config.system_tray); + + let config = ConfigFile { + system_tray: false, + ..ConfigFile::default() + }; + let serialized = serde_json::to_string(&config).unwrap(); + assert!(serialized.contains(r#""system_tray":false"#)); + + let restored: ConfigFile = serde_json::from_str(&serialized).unwrap(); + assert!(!restored.system_tray); + } + #[test] fn sync_target_fields_are_backward_compatible() { let legacy_config: ConfigFile = serde_json::from_str("{}").unwrap(); diff --git a/src/session/ssh_config.rs b/src/session/ssh_config.rs index 21b7c90c..076a98e6 100644 --- a/src/session/ssh_config.rs +++ b/src/session/ssh_config.rs @@ -55,10 +55,11 @@ pub fn parse_ssh_config_content(content: &str) -> Result> { } // Split into keyword and value - // OpenSSH supports both "keyword value" and "keyword=value" formats + // OpenSSH supports both "keyword value" and "keyword=value" formats, + // separated by either spaces or tabs. let (keyword, value) = if let Some(pos) = line.find('=') { (&line[..pos], line[pos + 1..].trim()) - } else if let Some(pos) = line.find(' ') { + } else if let Some(pos) = line.find([' ', '\t']) { (&line[..pos], line[pos..].trim()) } else { continue; @@ -111,7 +112,13 @@ pub fn parse_ssh_config_content(content: &str) -> Result> { } "port" => { if let Some(entry) = current_host.as_mut() { - entry.port = value.parse::().unwrap_or(22); + // Port 0 is not a valid TCP port; treat it like any other + // unparsable value and fall back to the default. + entry.port = value + .parse::() + .ok() + .filter(|port| *port > 0) + .unwrap_or(22); } } "identityfile" => { @@ -176,4 +183,158 @@ mod tests { assert_eq!(entries[1].host_alias, "anotherhost"); assert_eq!(entries[1].hostname, "5.6.7.8"); } + + #[test] + fn defaults_apply_when_host_has_no_options() { + let entries = parse_ssh_config_content("Host bare\n").unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].host_alias, "bare"); + assert_eq!(entries[0].hostname, "bare"); + assert_eq!(entries[0].user, ""); + assert_eq!(entries[0].port, 22); + assert!(entries[0].identity_files.is_empty()); + assert!(!entries[0].is_wildcard); + } + + #[test] + fn wildcard_and_question_mark_hosts_are_excluded() { + let content = "\ +Host * + HostName anything +Host prod?.example.com + User admin +Host real + User me +"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].host_alias, "real"); + assert_eq!(entries[0].user, "me"); + } + + #[test] + fn multi_pattern_host_is_treated_as_wildcard() { + let content = "\ +Host primary *.example.com + HostName 10.0.0.1 +Host solo +"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].host_alias, "solo"); + } + + #[test] + fn keyword_equals_value_format_is_supported() { + let content = "Host eq\nHostName=192.0.2.7\nPort=2200\nUser=bob\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].hostname, "192.0.2.7"); + assert_eq!(entries[0].port, 2200); + assert_eq!(entries[0].user, "bob"); + } + + #[test] + fn keywords_are_case_insensitive() { + let content = "host casey\n HOSTNAME 203.0.113.9\n USER alice\n PORT 2022\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].hostname, "203.0.113.9"); + assert_eq!(entries[0].user, "alice"); + assert_eq!(entries[0].port, 2022); + } + + #[test] + fn tab_separated_keywords_are_supported() { + let content = "Host\ttabbed\n\tHostName\t198.51.100.4\n\tPort\t2224\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].hostname, "198.51.100.4"); + assert_eq!(entries[0].port, 2224); + } + + #[test] + fn invalid_ports_fall_back_to_the_default() { + for bad in ["not-a-port", "70000", "-1", "0"] { + let content = format!("Host p\n Port {bad}\n"); + let entries = parse_ssh_config_content(&content).unwrap(); + assert_eq!(entries[0].port, 22, "port {bad:?}"); + } + } + + #[test] + fn multiple_identity_files_accumulate() { + let content = "Host multi\n IdentityFile ~/.ssh/a\n IdentityFile ~/.ssh/b\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!( + entries[0].identity_files, + vec!["~/.ssh/a".to_string(), "~/.ssh/b".to_string()], + ); + } + + #[test] + fn match_blocks_flush_and_detach_the_current_host() { + let content = "\ +Host before + User u1 +Match host x + HostName 1.2.3.4 +Host after + User u2 +"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 2); + assert_eq!(entries[0].host_alias, "before"); + assert_eq!(entries[1].host_alias, "after"); + // The HostName under Match must not attach to "after". + assert_eq!(entries[1].hostname, "after"); + } + + #[test] + fn include_directives_flush_the_current_host() { + let content = "Host h1\n User a\nInclude ~/.ssh/other\nHost h2\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 2); + assert_eq!(entries[0].host_alias, "h1"); + assert_eq!(entries[1].host_alias, "h2"); + } + + #[test] + fn comments_and_blank_lines_are_skipped() { + let content = "# leading comment\n\nHost c\n HostName 192.0.2.1\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].host_alias, "c"); + assert_eq!(entries[0].hostname, "192.0.2.1"); + } + + #[test] + fn unknown_keywords_are_ignored() { + let content = "Host u\n UnknownKeyword whatever\n HostName 192.0.2.2\n"; + + let entries = parse_ssh_config_content(content).unwrap(); + + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].hostname, "192.0.2.2"); + } } diff --git a/src/sftp/mod.rs b/src/sftp/mod.rs index ab084158..92077598 100644 --- a/src/sftp/mod.rs +++ b/src/sftp/mod.rs @@ -2348,6 +2348,14 @@ fn shell_quote(value: &str) -> String { format!("'{}'", value.replace('\'', "'\"'\"'")) } +/// Extracts a downloaded directory archive into `target_dir`. +/// +/// Path traversal protection relies on the archive crates themselves and must +/// not be weakened by future edits: for zip, `entry.enclosed_name()` rejects +/// escaping names; for tar, `Archive::unpack` skips entries with `..` +/// components and its canonicalization check refuses writes that resolve +/// outside the destination (including through symlinks). See the regression +/// tests in `handle_tests::extract_*`. async fn extract_archive_to(path: &Path, target_dir: &Path) -> Result<()> { let Some(file_name) = path .file_name() @@ -2856,4 +2864,256 @@ mod handle_tests { assert!(!list_dir_generation_matches(Some(8), generation)); assert!(list_dir_generation_matches(None, generation)); } + + // ---- Archive extraction traversal regression tests -------------------- + // + // Directory downloads extract a remotely created tar.gz on the local + // machine, so escaping entries must never write outside the target + // directory. tar 0.4.x guards `..` by skipping the entry and rejects + // writes that canonicalize outside the destination (symlink escapes + // included); zip is guarded by `enclosed_name()`. These tests pin that + // behavior against future refactors of `extract_archive_to`. + + fn append_tar_text_entry( + builder: &mut tar::Builder, + name: &str, + contents: &str, + ) { + let mut header = tar::Header::new_gnu(); + header.set_path(name).expect("set tar entry path"); + header.set_size(contents.len() as u64); + header.set_mode(0o644); + header.set_cksum(); + builder + .append(&header, contents.as_bytes()) + .expect("append tar entry"); + } + + /// `Header::set_path` refuses `..` components, so to craft a hostile + /// fixture we set a validated placeholder and then patch the raw name + /// field (checksum is recomputed afterwards). The reader under test must + /// therefore defend itself; `Builder::append` does not re-validate. + fn append_tar_entry_with_raw_name( + builder: &mut tar::Builder, + name: &str, + contents: &str, + ) { + let mut header = tar::Header::new_gnu(); + header + .set_path("placeholder") + .expect("set placeholder path"); + let name_bytes = name.as_bytes(); + assert!( + name_bytes.len() < 100, + "tar name field must fit in 100 bytes" + ); + let bytes = header.as_mut_bytes(); + bytes[..name_bytes.len()].copy_from_slice(name_bytes); + for slot in &mut bytes[name_bytes.len()..100] { + *slot = 0; + } + header.set_size(contents.len() as u64); + header.set_mode(0o644); + header.set_cksum(); + builder + .append(&header, contents.as_bytes()) + .expect("append tar entry"); + } + + fn write_tar_with_escaping_and_regular_entries(path: &Path) { + let file = fs::File::create(path).expect("create tar archive"); + let mut builder = tar::Builder::new(file); + append_tar_entry_with_raw_name(&mut builder, "../evil.txt", "pwned"); + append_tar_text_entry(&mut builder, "keep.txt", "safe"); + builder.finish().expect("finish tar archive"); + } + + #[tokio::test] + async fn extract_tar_skips_entries_with_parent_dir_components() { + let root = tempfile::tempdir().expect("create temporary extraction root"); + let target = root.path().join("target"); + fs::create_dir_all(&target).expect("create target dir"); + let archive = root.path().join("payload.tar"); + write_tar_with_escaping_and_regular_entries(&archive); + + extract_archive_to(&archive, &target) + .await + .expect("extract tar archive"); + + // The escaping entry must be skipped, never written outside target. + assert!(!root.path().join("evil.txt").exists()); + assert!(!target.join("evil.txt").exists()); + assert_eq!( + fs::read_to_string(target.join("keep.txt")).expect("read regular entry"), + "safe", + ); + } + + #[tokio::test] + async fn extract_tar_gz_skips_entries_with_parent_dir_components() { + let root = tempfile::tempdir().expect("create temporary extraction root"); + let target = root.path().join("target"); + fs::create_dir_all(&target).expect("create target dir"); + let archive = root.path().join("payload.tar.gz"); + let file = fs::File::create(&archive).expect("create tar.gz archive"); + let encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut builder = tar::Builder::new(encoder); + append_tar_entry_with_raw_name(&mut builder, "../evil.txt", "pwned"); + append_tar_text_entry(&mut builder, "keep.txt", "safe"); + builder.finish().expect("finish tar archive"); + builder + .into_inner() + .expect("unwrap gz encoder") + .finish() + .expect("finish gzip stream"); + + extract_archive_to(&archive, &target) + .await + .expect("extract tar.gz archive"); + + assert!(!root.path().join("evil.txt").exists()); + assert_eq!( + fs::read_to_string(target.join("keep.txt")).expect("read regular entry"), + "safe", + ); + } + + #[tokio::test] + async fn extract_tar_rejects_writes_through_symlinks_escaping_the_target() { + let root = tempfile::tempdir().expect("create temporary extraction root"); + let target = root.path().join("target"); + fs::create_dir_all(&target).expect("create target dir"); + let outside = root.path().join("outside"); + fs::create_dir_all(&outside).expect("create outside dir"); + let archive = root.path().join("symlink.tar"); + + let file = fs::File::create(&archive).expect("create tar archive"); + let mut builder = tar::Builder::new(file); + + // A symlink inside the target pointing at a sibling directory, + // followed by a file written through it. + let mut link = tar::Header::new_gnu(); + link.set_entry_type(tar::EntryType::Symlink); + link.set_path("escape").expect("set symlink entry path"); + link.set_link_name(outside.to_str().expect("absolute symlink target")) + .expect("set symlink target"); + link.set_size(0); + link.set_mode(0o777); + link.set_cksum(); + builder + .append(&link, std::io::empty()) + .expect("append symlink entry"); + + append_tar_text_entry(&mut builder, "escape/pwned.txt", "pwned"); + builder.finish().expect("finish tar archive"); + + let result = extract_archive_to(&archive, &target).await; + + // The write through the escaping symlink must be refused outright. + assert!(result.is_err()); + assert!(!outside.join("pwned.txt").exists()); + } + + #[tokio::test] + async fn extract_zip_writes_entries_inside_the_target_directory() { + let root = tempfile::tempdir().expect("create temporary extraction root"); + let target = root.path().join("target"); + fs::create_dir_all(&target).expect("create target dir"); + let archive = root.path().join("payload.zip"); + + let file = fs::File::create(&archive).expect("create zip archive"); + let mut writer = zip::ZipWriter::new(file); + writer + .start_file("nested/hello.txt", zip::write::SimpleFileOptions::default()) + .expect("start zip entry"); + std::io::Write::write_all(&mut writer, b"zip contents").expect("write zip entry"); + writer.finish().expect("finish zip archive"); + + extract_archive_to(&archive, &target) + .await + .expect("extract zip archive"); + + assert_eq!( + fs::read_to_string(target.join("nested").join("hello.txt")) + .expect("read extracted zip entry"), + "zip contents", + ); + assert!(!root.path().join("hello.txt").exists()); + } + + // ---- shell_quote adversarial tests ------------------------------------- + // + // Remote paths are embedded in `tar -C '...' -czf '...' ''` / + // `rm -f ''` command lines. The quoting must make it impossible for + // a path to terminate the surrounding single-quoted context. + + /// Parses the `'...'"'"'...'` single-quote escaping emitted by + /// `shell_quote` back into the original string; `None` means the quoted + /// form is malformed (i.e. the payload escaped its quoting context). + fn unquote_single(quoted: &str) -> Option { + let mut chars = quoted.chars().peekable(); + if chars.next() != Some('\'') { + return None; + } + let mut out = String::new(); + while let Some(c) = chars.next() { + if c == '\'' { + if chars.peek() == Some(&'"') { + for expected in ['"', '\'', '"', '\''] { + if chars.next() != Some(expected) { + return None; + } + } + out.push('\''); + } else if chars.peek().is_none() { + return Some(out); + } else { + return None; + } + } else { + out.push(c); + } + } + None + } + + #[test] + fn shell_quote_wraps_plain_names_in_single_quotes() { + assert_eq!(shell_quote("hello.txt"), "'hello.txt'"); + } + + #[test] + fn shell_quote_escapes_single_quotes() { + assert_eq!(shell_quote("it's.txt"), "'it'\"'\"'s.txt'"); + } + + #[test] + fn shell_quote_neutralizes_command_substitution_and_backticks() { + assert_eq!(shell_quote("$(rm -rf /)"), "'$(rm -rf /)'"); + assert_eq!(shell_quote("`id`"), "'`id`'"); + } + + #[test] + fn shell_quote_round_trips_arbitrary_input() { + for input in [ + "plain.txt", + "it's.txt", + "$(rm -rf /)", + "`id`", + "a\nb;c&d|e>f Vec { + (0..text.len() as i32).collect() + } + + fn test_color() -> Hsla { + hsla(10, 20, 30) + } + + #[test] + fn keyword_highlighting_is_case_insensitive() { + let mut map = HashMap::new(); + let text = "an Error occurred"; + let byte_to_col = identity_cols(text); + highlight_keywords(&mut map, text, &byte_to_col, 0, &["ERROR"], test_color()); + + // "Error" occupies columns 3..=7. + for c in 3..=7 { + assert!(map.contains_key(&(0, c)), "expected column {c} highlighted"); + } + assert_eq!(map.len(), 5); + } + + #[test] + fn keyword_highlighting_never_overlaps() { + let mut map = HashMap::new(); + let text = "ERR ERROR"; + let byte_to_col = identity_cols(text); + highlight_keywords( + &mut map, + text, + &byte_to_col, + 0, + &["ERROR", "ERR"], + test_color(), + ); + + // Both keywords highlight; every position written at most once. + for c in [0, 1, 2, 4, 5, 6, 7, 8] { + assert!(map.contains_key(&(0, c)), "expected column {c} highlighted"); + } + assert_eq!(map.len(), 8); + } + + #[test] + fn http_codes_highlight_only_known_codes_at_digit_boundaries() { + let colors = highlight_colors(); + let text = "GET 200 OK / 404 gone / 500 boom / 2000 bad"; + let byte_to_col = identity_cols(text); + let mut map = HashMap::new(); + highlight_http_codes(&mut map, text, &byte_to_col, 0, &colors); + + let col_200 = text.find("200").expect("200 present") as i32; + let col_404 = text.find("404").expect("404 present") as i32; + let col_500 = text.find("500").expect("500 present") as i32; + let col_2000 = text.find("2000").expect("2000 present") as i32; + + for c in col_200..col_200 + 3 { + assert_eq!(map.get(&(0, c)), Some(&colors.http_2xx)); + } + for c in col_404..col_404 + 3 { + assert_eq!(map.get(&(0, c)), Some(&colors.http_4xx)); + } + for c in col_500..col_500 + 3 { + assert_eq!(map.get(&(0, c)), Some(&colors.http_5xx)); + } + // 2000 is part of a longer number and must not be highlighted. + for c in col_2000..col_2000 + 4 { + assert!(!map.contains_key(&(0, c))); + } + } + + #[test] + fn ip_addresses_require_boundaries_and_valid_octets() { + let text = "from 192.168.1.10 to 10.0.0.1, not 999.1.1.1 nor in1.2.3.4."; + let positions = find_ip_addresses(text); + + let mut expected = vec![ + text.find("192.168.1.10").expect("first ip"), + text.find("10.0.0.1").expect("second ip"), + ]; + expected.sort_unstable(); + assert_eq!(positions, expected); + } + + #[test] + fn urls_are_found_only_at_boundaries() { + let text = "go to https://example.com and http://a.b or xhttps://no"; + let positions = find_urls(text); + + assert_eq!( + positions, + vec![ + text.find("https://example.com").expect("https url"), + text.find("http://a.b").expect("http url"), + ], + ); + } + + #[test] + fn url_length_stops_at_whitespace() { + let text = "https://a.b/path?q=1\tnext"; + assert_eq!(find_url_len(text), text.find('\t').expect("tab present")); + } + + #[test] + fn ports_are_validated_and_bounded() { + let text = ":8080 ok :0 bad :99999 bad :abc no"; + let positions = find_ports(text); + + assert_eq!(positions, vec![text.find(":8080").expect("valid port")]); + } + + #[test] + fn port_length_parses_the_full_number() { + assert_eq!(find_port_len(":8080 rest"), 5); + assert_eq!(find_port_len(":22"), 3); + } + + fn cell_at(row: i32, col: i32, c: char, wrap: bool) -> RenderCell { + let mut cell = Cell { + c, + ..Cell::default() + }; + if wrap { + cell.flags + .insert(alacritty_terminal::term::cell::Flags::WRAPLINE); + } + RenderCell { row, col, cell } + } + + #[test] + fn urls_spanning_wrapped_rows_are_highlighted_as_one_logical_line() { + // Note: http:// (not https://) so no security keyword overlaps the URL. + let first = "go http://exa"; + let second = "mple.com/x done"; + let mut cells: Vec = first + .chars() + .enumerate() + .map(|(col, c)| cell_at(0, col as i32, c, col == first.len() - 1)) + .collect(); + cells.extend( + second + .chars() + .enumerate() + .map(|(col, c)| cell_at(1, col as i32, c, false)), + ); + + let map = highlight_cells(&cells, 2); + let colors = highlight_colors(); + + // The URL runs from row 0 (cols 3..=12) into row 1 (cols 0..=9). + for c in 3..=12 { + assert_eq!(map.get(&(0, c)), Some(&colors.url), "row 0 col {c}"); + } + for c in 0..=9 { + assert_eq!(map.get(&(1, c)), Some(&colors.url), "row 1 col {c}"); + } + } + + #[test] + fn find_url_at_cell_resolves_urls_across_wrapped_rows() { + let first = "go http://exa"; + let second = "mple.com/x done"; + let mut cells: Vec = first + .chars() + .enumerate() + .map(|(col, c)| cell_at(0, col as i32, c, col == first.len() - 1)) + .collect(); + cells.extend( + second + .chars() + .enumerate() + .map(|(col, c)| cell_at(1, col as i32, c, false)), + ); + + // Clicking the first character of the URL on row 1 resolves it. + let found = find_url_at_cell(&cells, 2, 1, 0); + let (url, url_cells) = found.expect("URL spanning rows must be found"); + assert_eq!(url, "http://example.com/x"); + assert_eq!(url_cells.first(), Some(&(0, 3))); + assert_eq!(url_cells.last(), Some(&(1, 9))); + } +}