在 Windows 11 x64 / Rust 1.98.1 / Slint 1.18 上实测:Release EXE 10.31 MB / 测试 22 passed / 启动后 RSS 153 MB / 空闲 CPU 0.15%。RSS 高于 AGENTS.md §33 的 50 MB 目标,原因是 Slint 1.18 默认 femtovg 后端的 GUI 堆占用约 120–150 MB;业务层只占很小一部分。README 记录所有 Phase 1–7 状态 + 已知限制。
156 lines
7.2 KiB
Markdown
156 lines
7.2 KiB
Markdown
# Windows-Gotify-Client-Rust
|
||
|
||
轻量、现代、Windows 原生体验良好的 Gotify 桌面客户端,使用 **Rust + Slint** 构建。
|
||
|
||
> 详细的产品定位、技术栈约束、架构、UI 规范与分阶段实施计划全部记录在 [`AGENTS.md`](./AGENTS.md)。本文档只做项目导览。
|
||
|
||
## 状态
|
||
|
||
✅ **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
|
||
- Windows 11 x64(ARM64 条件允许时支持)
|
||
- 后台常驻、托盘型客户端
|
||
|
||
## 技术栈
|
||
|
||
| 层 | 选型 |
|
||
|---|---|
|
||
| 语言 | 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` |
|
||
|
||
显式禁用:Tauri / Wails / Electron / WebView2 / WPF / WinUI 3 / Node 运行时。
|
||
理由见 `AGENTS.md` §2。
|
||
|
||
## 仓库布局
|
||
|
||
```
|
||
Windows-Gotify-Client-Rust/
|
||
├── Cargo.toml
|
||
├── Cargo.lock
|
||
├── AGENTS.md # AI Agent 实施计划
|
||
├── README.md
|
||
├── LICENSE # MIT
|
||
├── .gitignore
|
||
├── 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/ # 预留
|
||
```
|
||
|
||
## 开发
|
||
|
||
```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` 头(`HeaderValue::set_sensitive(true)`)
|
||
- 通知点击的 `click.url` 仅允许 `http://` / `https://`,拒绝 `file://` / `javascript:` / 自定义 scheme
|
||
- 详细策略见 `AGENTS.md` §13 / §20 / §38 / §51
|
||
|
||
## License
|
||
|
||
MIT(见 [LICENSE](./LICENSE))。 |