diff --git a/README.md b/README.md index 7d32b1f..a75a671 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,20 @@ ## 状态 -🚧 **从 0 起步** —— 当前仓库只有目录骨架与占位 `Cargo.toml`,尚未进入 Phase 1(Bootstrap)。 +✅ **Phase 1–7 完成**(Bootstrap → Polish)。Phase 8 性能基线已记录。 完整 8 阶段路线图见 `AGENTS.md` §57。 +| 阶段 | 内容 | Commit | +|---|---|---| +| 1 Bootstrap | Cargo + Slint hello + windows_subsystem + logging + error | `b30b2f0` | +| 2 C1–C3 | MainWindow + Sidebar + Mock 消息 + Settings + Dialogs + 窗口状态 | `49cbd9e` `11a1222` `680fbfa` | +| 3 C1–C2 | Gotify REST 客户端 + 真实数据接入 + Connection status | `c6ad388` `0cf7a4e` | +| 4 C1–C2 | WebSocket + 退避 + WS worker + UI drain | `319eeb4` `0b766f4` | +| 5 C1–C2 | AppConfig 持久化 + Theme + last_message_id | `07317e2` `28524b4` | +| 6 C1–C3 | 单实例 + 开机自启动 + close-to-tray + 托盘 + URL 打开 | `ac5d0cb` `e1622b5` `1df0723` | +| 7 C1–C3 | 托盘菜单增强 + Win32 Toast + URL 接线 + Priority 过滤 + 详情面板 | `1d5e4e3` `3010526` `97becf5` | +| 8 | 性能基线(本文档) | TBD | + ## 目标平台 - Windows 10 x64 @@ -19,11 +30,11 @@ | 层 | 选型 | |---|---| -| 语言 | Rust stable | -| UI | Slint 1.17.x | -| HTTP | `reqwest`(blocking + rustls) | -| WebSocket | `tungstenite` | -| Windows 集成 | `windows-rs` focused crates(按需引入) | +| 语言 | Rust stable (1.98.1 实测) | +| UI | Slint 1.18 | +| HTTP | `reqwest` 0.12(blocking + rustls) | +| WebSocket | `tungstenite` 0.21(rustls + handshake) | +| Windows 集成 | `windows-sys` 0.59 focused features | | 配置 / 状态 | JSON 文件(`%APPDATA%/WindowsGotify/`) | | 日志 | `tracing` + `tracing-subscriber` | @@ -34,30 +45,109 @@ ``` Windows-Gotify-Client-Rust/ -├── Cargo.toml # 占位 — 见 AGENTS.md §44 -├── AGENTS.md # AI Agent 实施计划(1363 行) +├── Cargo.toml +├── Cargo.lock +├── AGENTS.md # AI Agent 实施计划 ├── README.md -├── LICENSE # MIT(占位) +├── LICENSE # MIT ├── .gitignore -├── assets/ # 图标、字体等 -├── ui/ # Slint 视图 -├── src/ # Rust 业务代码 -│ ├── gotify/ # HTTP + WebSocket -│ ├── storage/ # config.json / state.json -│ ├── windows/ # 托盘 / Toast / 启动 / 单实例 / URL -│ └── services/ # 生命周期 -├── tests/ # 集成测试 -└── scripts/ # PowerShell 构建脚本 +├── assets/ # 图标占位 +├── ui/ # 8 个 .slint 文件 +│ ├── app.slint # MainWindow + Sidebar +│ ├── message-list.slint # 列表 + priority 过滤 +│ ├── message-card.slint # MessageItem + 单卡片 +│ ├── settings-page.slint # 5 分组设置 +│ ├── empty-state.slint +│ ├── dialogs.slint # AboutDialog +│ ├── server-card.slint +│ ├── theme.slint +│ └── components.slint # NavItem / SectionLabel / Divider +├── src/ +│ ├── main.rs # windows_subsystem + 单实例 +│ ├── lib.rs # 全部 wiring +│ ├── config.rs # ActiveConfig (Clone) +│ ├── models.rs # MessageItem 适配 + mock + try_fetch_initial_data +│ ├── error.rs +│ ├── logging.rs +│ ├── gotify/ # api + websocket + reconnect + model +│ ├── storage/ # config.json + state.json +│ └── windows/ # single_instance + startup + tray + url +├── tests/ # 22 测试(22 passed) +└── scripts/ # 预留 ``` -## 开发环境 +## 开发 -> 本仓库目前还没有源码,本节将在 Phase 1 完成后补全具体的 `cargo run` / `cargo build --release` 步骤。 +```powershell +# 编译(debug) +cargo build + +# 编译(release,单 exe,无控制台窗口) +cargo build --release + +# 测试(22 个) +cargo test + +# 运行(debug 含 console,release 无 console) +cargo run --release +``` + +## 性能基线(Phase 8 实测) + +在 Windows 11 x64 / Rust 1.98.1 / Slint 1.18 上: + +| 项 | 实测值 | AGENTS.md 目标 | +|---|---|---| +| Release EXE 大小 | **10.31 MB** | 记录实际,不硬性规定 | +| Release 编译时间(clean) | ~56 s | — | +| Tests | 22 passed / 0 failed | — | +| 启动后空闲 RSS | **153 MB** | < 50 MB(未达,受 Slint/Winit GUI 堆影响) | +| 启动后空闲线程数 | 28 | — | +| 启动后空闲 Handle 数 | 529 | — | +| 空闲 CPU(持续 30s) | ~0.15% | 接近 0%(达成) | +| Debug EXE | 35.66 MB / 控制台 | — | +| 启动 ~5s 后 CPU 累积 | ~0.77 s | — | + +**关于 RSS**:Slint 1.18 在 Windows 上使用 `femtovg` 路径(默认),自身初始化就占用 ~120–150 MB 堆。Winit 后端还会为 GPU surface 与窗口堆保留一块。本项目业务层(Gotify 客户端 + 托盘 + 状态机)的额外占用很少。要进一步压缩需要换 Slint 的 `software` 后端或自行精简 femtovg 资源;对一个常驻型客户端来说,150 MB 仍在 Windows 11 上合理范围内(Task Manager 里 150 MB 的常驻进程很常见)。 + +**关于 EXE 大小**:当前 `release` profile 用了 `opt-level = "z"` + `lto = true` + `codegen-units = 1` + `panic = "abort"` + `strip = true`。再进一步压可考虑 `wasm-opt` 类工具或拆分依赖,但 V1 不值得为几百 KB 引入复杂度。 + +## 功能清单(Phase 7 完成后) + +- ✅ Gotify HTTP REST(`/health`, `/application`, `/message?limit=`) +- ✅ Gotify WebSocket 实时消息 + 1s/2s/4s/8s/16s/30s 指数退避 +- ✅ 多 server 配置(V1 仍只连接一个 active server) +- ✅ `%APPDATA%/WindowsGotify/config.json` 持久化 +- ✅ `%APPDATA%/WindowsGotify/state.json` 持久化(窗口尺寸、主题、上次 last_message_id) +- ✅ 启动时 `since=last_message_id` 增量 catch-up +- ✅ 系统托盘(Open / New messages: N / Pause notifications / Settings / Restart connection / Quit) +- ✅ 单实例(`Local\` Mutex)+ 第二次启动激活现有窗口(`RegisterWindowMessageW`) +- ✅ close-to-tray + HKCU Run 开机自启动 +- ✅ 系统级 Toast 通知(Win32 `NIF_INFO` + `Shell_NotifyIconW`,Win10+ 走 Action Center) +- ✅ Toast 偏好:master switch / High / Max / 托盘菜单 pause +- ✅ 点击消息打开 `click_url`(严格 http/https scheme 校验) +- ✅ Priority 过滤(All / Low / Normal / High / Max,sidebar) +- ✅ 消息详情面板(右侧 360px,app + priority + title + timestamp + body + Open link 按钮) +- ✅ Dark / Light / System 主题(每次启动从 state.json 恢复) +- ✅ Settings:Appearance / Connection / Notifications / Startup / Appearance / Advanced 占位 +- ✅ Settings 切换实时持久化 + 通知 Toast 偏好同步到 TrayState +- ✅ Bearer Token 日志脱敏(`HeaderValue::set_sensitive(true)`) + +## 已知限制 / 未做 + +- ❌ Markdown 渲染(`client::display` text/markdown)—— V1 纯文本足够;后续可加纯 Rust parser +- ❌ 真正的图标资源(tray NULL + window 默认)—— 需要真 `.ico` 文件 +- ❌ Restart connection 仅 refetch,不真正断开 WebSocket worker +- ❌ 多 server UI 切换(仅支持 active_server 单连接) +- ❌ 搜索框(仅 Priority 过滤,无文本搜索) +- ❌ Copy message body 按钮 +- ❌ Windows Credential Manager 加密 Token +- ❌ 国际化(仅英文 UI) ## 隐私与安全 - App Token 默认以 JSON 明文存储在 `%APPDATA%/WindowsGotify/config.json`,仅当前用户可访问 -- 日志永不打印完整 `Authorization` 头 +- 日志永不打印完整 `Authorization` 头(`HeaderValue::set_sensitive(true)`) - 通知点击的 `click.url` 仅允许 `http://` / `https://`,拒绝 `file://` / `javascript:` / 自定义 scheme - 详细策略见 `AGENTS.md` §13 / §20 / §38 / §51