Files
Windows-Gotify-Client-Rust/AGENTS.md
T
KennethCheng a3a6a7c91d chore: 初始化 Windows-Gotify-Client-Rust 项目骨架
使用 Rust + Slint 重建一个轻量、现代、Windows 原生体验良好的 Gotify
桌面客户端。本次只搭目录骨架与占位文件,未进入 Phase 1(Bootstrap)。

本次提交内容
------------
- 保留 AGENTS.md(1363 行定制 Agent 实施计划,含 8 阶段路线图)
- 新建 Cargo.toml([package] 元数据 + AGENTS.md §44 依赖模板 + §42
  release profile;无 [[bin]],等 Phase 1 才加 src/main.rs)
- 新建 README.md(项目导览,引用 AGENTS.md)
- 新建 .gitignore(Rust / Slint / Windows / IDE / 密钥)
- 新建 LICENSE(MIT 占位)
- 按 AGENTS.md §4 建好全部 10 个目录,每个空目录放 .gitkeep:
    assets/        assets/fonts/
    ui/            scripts/
    src/           src/gotify/
    src/storage/   src/windows/
    src/services/  tests/

未提交(Phase 1 起才落地)
------------------------
- src/main.rs 及所有 .rs 业务代码
- ui/*.slint 视图
- scripts/build-release.ps1 等构建脚本
- Cargo.lock(当前 Cargo.toml 是占位、无可编译代码)

技术栈约束(来自 AGENTS.md §1 / §2)
------------------------------------
- Rust stable + Slint 1.17.x + Windows 10/11
- 显式禁用:Tauri / Wails / Electron / WebView2 / WPF / WinUI 3 / Node 运行时
- 单一主程序,无浏览器运行时依赖;UI 由 Slint 原生渲染

仓库
----
远程:https://gitea.kennethcheng.cc/KennethCheng/Windows-Gotify-Client-Rust
2026-09-19 04:02:51 +08:00

2237 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Windows-Gotify-Client-Rust — Agent Implementation Plan
> 目标:使用 **Rust + Slint** 重建一个轻量、现代、Windows 原生体验良好的 Gotify 桌面客户端。
>
> 目标不是把现有 C# 项目逐行翻译成 Rust,而是以现有项目的功能为产品基线,重新设计更轻、更清晰、更容易长期维护的架构。
---
## 0. Agent 必须先读懂的上下文
现有项目:`Windows-Gotify-Client`
当前实现已经包含这些核心能力:
- Gotify HTTP API
- WebSocket 实时消息
- WebSocket 断线重连
- 新消息 Windows 通知
- 应用/Token 管理
- 配置保存
- 最近消息状态保存
- Windows 托盘
- 深色/浅色主题
- 消息列表与详情
- 点击通知 URL
- 开机启动相关能力
本次 Rust 版本应保留成熟的核心功能,但不复制当前代码的类结构。
### 产品定位
这是一个 **Windows-only、后台常驻、托盘型 Gotify 客户端**。
核心原则:
1. 小体积
2. 低内存占用
3. 后台低 CPU
4. 无浏览器运行时依赖
5. 单一主程序
6. UI 精致现代
7. 网络层与 UI 完全解耦
8. Windows 能力原生调用
9. 配置/状态简单可靠
10. 默认不做过度工程化
---
# 1. 技术栈
## 必选
- Rust stable
- Slint 1.17.x 或当前稳定兼容版本
- Windows 10 x64
- Windows 11 x64 / ARM64(条件允许时支持)
- Cargo
Slint 当前官方文档将 Windows 10 x86-64 与 Windows 11 x86-64 / ARM64 列为测试平台,并支持 Windows 子系统以隐藏控制台窗口。具体版本以开发时锁定的稳定版为准。
## Windows 原生能力
优先使用 `windows-rs` 的 focused crates;只有 focused crate 不覆盖目标 API 时,才使用 `windows` / `windows-sys`。
候选:
- `windows-notifyicon`:托盘图标
- `windows-registry`:开机启动等 Registry 操作
- `windows-window`:只有确实需要时使用
- `windows`:Toast/WinRT/其它必要 Win32/COM API
不要为了方便把整个 Windows API 集合无脑全部引入。
## 网络
推荐:
- `reqwest`:HTTP
- `tungstenite`:WebSocket
- `serde`
- `serde_json`
- `url`
默认优先使用 blocking HTTP + 独立网络线程,而不是为了一个 WebSocket 客户端引入完整 async runtime。
只有当实现明确证明 async 能降低复杂度或改善功能时,才引入 `tokio`。
## 日志
推荐:
- `tracing`
- `tracing-subscriber`
Release 默认降低日志等级,不允许每条消息都产生大量磁盘 IO。
## 本地数据
V1 默认使用 JSON 文件:
```text
config.json
state.json
```
不要一开始引入 SQLite。
只有产品明确需要:
- 大规模历史记录
- 搜索历史
- 消息标签
- 长期存档
才考虑 SQLite。
---
# 2. 明确禁止的技术路线
除非出现无法解决的技术限制,否则禁止:
- Tauri
- Wails
- Electron
- WebView2 作为 UI 主体
- Web 前端 + WebView
- Windows App SDK
- WPF
- WinUI 3
- 大型前端框架
- Node.js runtime
- 浏览器 runtime
原因不是这些技术不好,而是本项目目标是一个 **极轻量 Windows 常驻工具**。
UI 应由 Slint 原生渲染,业务逻辑由 Rust 完成。
---
# 3. 核心架构
采用:
```text
┌──────────────────────────┐
│ Slint UI │
│ │
│ Dashboard / Message │
│ Detail / Settings │
└────────────┬─────────────┘
│
UI callbacks
│
▼
┌──────────────────────────┐
│ AppCore │
│ │
│ Config │
│ Connection state │
│ Message state │
│ User actions │
└───────┬─────────┬────────┘
│ │
AppCommand │ │ AppEvent
│ │
▼ ▼
┌──────────────────────────┐
│ Network Worker │
│ │
│ REST API │
│ WebSocket │
│ reconnect / backoff │
└────────────┬─────────────┘
│
▼
Gotify Server
┌──────────────────── AppCore ────────────────────┐
│ │ │
▼ ▼ ▼
Storage Worker Windows Host Logger
│ │
│ ┌─────┼─────┐
│ ▼ ▼ ▼
│ Tray Toast Startup
▼
config.json
state.json
```
### 关键设计
UI 永远不能直接:
- 调用 HTTP
- 管理 WebSocket
- 读取配置文件
- 修改 Registry
- 调用 Toast
- 操作托盘
UI 只通过 `AppCore` 触发操作和接收状态。
---
# 4. 目录结构
目标结构:
```text
Windows-Gotify-Client-Rust/
├── Cargo.toml
├── Cargo.lock
├── README.md
├── LICENSE
├── AGENTS.md
├── .gitignore
│
├── assets/
│ ├── icon.ico
│ ├── tray.ico
│ ├── app-icon.png
│ └── fonts/ # 可选;尽量不内置字体
│
├── ui/
│ ├── app.slint
│ ├── theme.slint
│ ├── colors.slint
│ ├── components.slint
│ ├── message-card.slint
│ ├── message-list.slint
│ ├── empty-state.slint
│ ├── settings-page.slint
│ ├── server-card.slint
│ └── dialogs.slint
│
├── src/
│ ├── main.rs
│ ├── app.rs
│ ├── events.rs
│ ├── commands.rs
│ ├── models.rs
│ ├── error.rs
│ │
│ ├── gotify/
│ │ ├── mod.rs
│ │ ├── api.rs
│ │ ├── websocket.rs
│ │ ├── reconnect.rs
│ │ └── model.rs
│ │
│ ├── storage/
│ │ ├── mod.rs
│ │ ├── config.rs
│ │ └── state.rs
│ │
│ ├── windows/
│ │ ├── mod.rs
│ │ ├── tray.rs
│ │ ├── notification.rs
│ │ ├── startup.rs
│ │ ├── single_instance.rs
│ │ └── url.rs
│ │
│ └── services/
│ ├── mod.rs
│ └── lifecycle.rs
│
├── tests/
│ ├── gotify_api.rs
│ ├── reconnect.rs
│ ├── state.rs
│ └── parsing.rs
│
└── scripts/
├── build-release.ps1
└── package.ps1
```
---
# 5. UI 产品设计
## 总体视觉方向
关键词:
> **Fluent / 极简 / 安静 / 高级 / 信息密度适中 / 长时间使用舒服**
不要做成:
- 老式 Windows 管理软件
- 浏览器 Admin Dashboard
- 过多渐变
- 大面积阴影
- 过大的圆角
- 花哨动画
整体感觉应该接近:
- Windows 11 Fluent
- Linear
- Raycast
- Arc
- 现代桌面通知工具
但不要直接复制任何现有产品。
---
# 6. 主窗口布局
推荐默认尺寸:
```text
960 × 680 px
```
最小尺寸:
```text
760 × 520 px
```
布局:
```text
┌─────────────────────────────────────────────────────────────┐
│ Gotify ● Connected ─ □ × │
├──────────────┬──────────────────────────────────────────────┤
│ │ │
│ ◉ All │ Messages │
│ │ ───────────────────────────────────────── │
│ Servers │ │
│ ├ Server A │ ┌────────────────────────────────────────┐ │
│ └ Server B │ │ ● Build succeeded │ │
│ │ │ CI / Production │ │
│ │ │ 2 min ago │ │
│ │ └────────────────────────────────────────┘ │
│ │ │
│ Filters │ ┌────────────────────────────────────────┐ │
│ ○ High │ │ ● Backup finished │ │
│ ○ Normal │ │ NAS / Backup │ │
│ ○ Low │ │ 8 min ago │ │
│ │ └────────────────────────────────────────┘ │
│ │ │
│──────────────│ │
│ ⚙ Settings │ │
│ ℹ About │ │
└──────────────┴──────────────────────────────────────────────┘
```
注意:上图只表达结构,不是要求做成 ASCII 风格。
---
# 7. UI 组件规范
## Window
- 圆角:轻度,约 10~14 px
- 不要夸张玻璃效果
- 不要大面积半透明
- 标题栏简洁
- 支持 Windows 深浅主题
## Sidebar
宽度:约 220~240 px。
内容:
1. App logo
2. All messages
3. Server 列表
4. Priority filter
5. Settings
6. About
Sidebar 应该视觉安静,不抢主内容焦点。
## Message Card
每一条消息显示:
- priority 状态
- title
- app name
- message preview
- relative time
例如:
```text
● Build succeeded
GitLab Runner · production
2 min ago
```
Priority 使用小面积视觉标记,不允许整张卡片使用高饱和背景。
推荐表现:
```text
Low neutral
Normal subtle accent
High amber/orange accent
Max red accent
```
但是所有颜色都必须进入主题系统统一管理。
---
# 8. 消息详情页
点击消息后进入右侧详情视图,或使用可折叠 Detail Panel。
结构:
```text
┌───────────────────────────────────────────────┐
│ ← Message │
│ │
│ Build succeeded │
│ GitLab Runner · Production │
│ 2026-09-19 03:42:18 │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ │ │
│ │ Deploy completed successfully. │ │
│ │ │ │
│ │ Version: v1.4.2 │ │
│ │ Server: production │ │
│ │ │ │
│ └───────────────────────────────────────────┘ │
│ │
│ [ Copy ] [ Open Link ] │
└───────────────────────────────────────────────┘
```
正文:
- 默认 plain text
- 根据 Gotify `client::display` extras 支持 markdown 时,再提供 Markdown 展示
- 不要为了 Markdown 引入浏览器
- V1 可以先纯文本,再扩展 Markdown
Gotify 官方文档定义了 `client::display` extras,并支持 `text/plain` 与 `text/markdown` 等 content type。实现时以服务器实际 API 行为为准。
---
# 9. 空状态设计
没有消息时不要只显示:
```text
No messages.
```
应提供漂亮的空状态:
```text
✦
You're all caught up
New Gotify messages will
appear here automatically.
[ Test Notification ]
```
不使用外部图片资源,优先用 Slint 自绘简单图形/图标。
---
# 10. 设置页面
设置页面必须精简,不做企业软件式的巨型表单。
分为:
### Connection
- Server URL
- App Token
- Connection test
- Auto reconnect
- Connection status
### Notifications
- Enable desktop notification
- Sound
- Notify on High priority
- Notify on Max priority
- Click action
### Startup
- Start with Windows
- Start minimized
- Minimize to tray
- Close to tray
### Appearance
- System
- Light
- Dark
- Compact message density
### Advanced
- Log level
- Clear local state
- Reset settings
- Open config directory
默认隐藏 Advanced 部分。
---
# 11. 设置交互要求
必须做到:
```text
修改配置
↓
UI 本地状态变化
↓
点击 Save 或自动保存
↓
AppCore 更新 Network Worker
↓
必要时重连
```
不要让 UI 直接修改 `config.json`。
---
# 12. 托盘体验
应用关闭主窗口时,默认:
```text
Window hidden
App process remains alive
Tray remains alive
```
托盘菜单:
```text
Open
New messages: 3
────────────
Pause notifications
Settings
────────────
Restart connection
Quit
```
托盘图标必须:
- 有正常态
- 可选 disconnected 状态
- 不能使用模糊的小图标
- Windows 11 任务栏大小下仍能识别
Slint 1.17 已提供 `SystemTrayIcon`,可以优先评估 Slint 原生托盘能力;若 Windows 上需要更完整的原生行为,再使用 `windows-rs` focused crate。
---
# 13. 通知设计
新消息到来:
```text
Gotify
Build succeeded
GitLab Runner · Production
```
要求:
- 标题来自 Gotify message title
- 正文来自 message message
- priority 用于通知层级
- click.url 存在时支持点击打开
Gotify 官方 extras 机制中,`client::*` 为客户端保留命名空间,`client::notification` 用于通知行为定制;实现应该严格按照 Gotify 官方 extras 语义处理,不要自行发明兼容格式。
URL 打开逻辑必须:
1. 只接受显式字符串 URL
2. 解析 URL
3. 至少允许 `https://` / `http://`
4. 拒绝 `file://`
5. 拒绝 `javascript:`
6. 拒绝任意自定义 scheme,除非未来明确增加白名单
---
# 14. Gotify 网络设计
## HTTP
封装:
```rust
GotifyApi
```
负责:
- 获取消息
- 获取应用
- 创建/读取必要资源
- 删除消息(如功能需要)
- Server health / version(如功能需要)
不要让 UI 直接知道 URL 拼接方式。
## WebSocket
封装:
```rust
GotifyWebSocket
```
状态:
```text
Disconnected
Connecting
Connected
Reconnecting
Stopped
```
事件:
```rust
enum NetworkEvent {
Connected,
Disconnected,
Message(GotifyMessage),
Error(NetworkError),
}
```
---
# 15. WebSocket 重连
必须支持指数退避:
```text
1s
2s
4s
8s
16s
30s
30s...
```
服务器成功连接后重置退避时间。
不要:
```text
loop {
connect();
}
```
禁止高频重连打爆服务器。
---
# 16. 离线补偿
不要简单照搬现有 C# 的 `since=lastId` 逻辑。
实现前先验证目标 Gotify Server 版本的分页语义。
目标行为:
```text
程序正常运行
↓
保存最后处理消息 ID
↓
程序退出 / 网络断开
↓
重新启动 / 重连
↓
HTTP 补拉缺失消息
↓
去重
↓
更新 UI
↓
继续 WebSocket
```
必须测试:
- 断网 10 秒
- 断网 5 分钟
- 程序重启
- Gotify Server 重启
- 多条消息同时到达
- 消息 ID 非连续
- 重复消息
---
# 17. 消息去重
V1 不要建立复杂数据库。
采用:
```rust
VecDeque<i64>
HashSet<i64>
```
维护最近 N 条 ID,例如:
```text
200
```
规则:
```text
if seen(id) {
ignore
}
```
超过上限后删除最老 ID。
如果未来消息历史量扩大,再换 SQLite。
---
# 18. 状态设计
`state.json` 只保存真正需要跨进程持久化的状态。
建议:
```json
{
"last_message_id": 123456,
"selected_server": "default",
"last_window_width": 960,
"last_window_height": 680,
"version": 1
}
```
不要每收到一条消息就立刻 fsync / rewrite 整个 JSON。
采用:
```text
memory state
↓
mark dirty
↓
debounce 2~5s
↓
safe atomic write
```
或者在应用正常退出时保存,并定时 checkpoint。
写文件采用:
```text
state.json.tmp
↓
flush
↓
atomic replace
↓
state.json
```
避免异常退出导致 JSON 损坏。
---
# 19. 配置设计
配置文件路径建议:
```text
%APPDATA%/WindowsGotify/config.json
```
不要写入安装目录。
配置模型:
```rust
struct AppConfig {
servers: Vec<GotifyServerConfig>,
selected_server: Option<String>,
appearance: AppearanceMode,
notifications: NotificationConfig,
startup: StartupConfig,
}
```
V1 可以只支持一个 Server,但数据结构直接按多 Server 设计,避免未来再改 schema。
---
# 20. Token 安全
不要把 App Token 打印到日志。
禁止:
```text
Authorization: Bearer xxx
```
进入日志。
HTTP 错误日志最多打印:
```text
HTTP 401 Unauthorized
```
而不要打印完整请求头。
V1 可以继续用明文 JSON 保存 token,但必须:
- 限制文件访问权限到当前用户
- 日志中永不泄漏 Token
- UI 中默认密码样式隐藏
未来需要再考虑 Windows Credential Manager。
---
# 21. 多 Server 设计
产品层支持:
```text
Server A
Server B
Server C
```
每个 Server:
```json
{
"id": "server-a",
"name": "Home",
"url": "https://gotify.example.com",
"token": "...",
"enabled": true
}
```
V1 可以只同时连接一个 Server,以减少复杂度。
架构上不要把 single server 写死。
---
# 22. UI 与 Rust 通信
Slint 只绑定 Presentation Model。
推荐:
```text
Rust Model
↓
Slint properties/models
```
不要:
```text
Slint callback
↓
HTTP request
↓
JSON parse
↓
file IO
```
UI callback 应类似:
```rust
on_connect_clicked
on_message_clicked(id)
on_open_link(url)
on_save_settings()
on_quit()
```
然后统一发送给 AppCore。
---
# 23. AppCore 状态机
建议有一个明确的应用状态:
```rust
enum ConnectionState {
Disabled,
Connecting,
Connected,
Reconnecting { attempt: u32 },
Error,
}
```
UI 根据状态显示:
```text
Connected → green dot / Connected
Connecting → spinner / Connecting
Reconnecting → amber dot / Reconnecting
Error → red dot / Connection error
```
不要在 UI 中自己推断网络状态。
---
# 24. 错误处理
业务错误统一:
```rust
AppError
```
分类:
```text
ConfigError
NetworkError
WebSocketError
StorageError
WindowsError
ParseError
```
用户看到的错误必须是人话,例如:
```text
Unable to connect to Gotify server.
Check the server URL and token.
```
日志中保存技术细节。
---
# 25. 日志策略
Debug:
```text
详细连接过程
WebSocket 状态变化
消息解析
```
Release:
```text
INFO
WARN
ERROR
```
不要默认记录所有消息正文。
例如:
```text
[INFO] Gotify connected
[INFO] Received message id=1024 priority=5
[WARN] WebSocket disconnected
[INFO] Reconnecting attempt=3
```
而不是:
```text
[INFO] token=xxx message="这里是一大段隐私内容"
```
---
# 26. 单实例
必须支持:
```text
双击第二次
↓
检测已有实例
↓
激活现有窗口
```
不要启动两个 WebSocket 实例。
Windows 单实例可以放在:
```text
src/windows/single_instance.rs
```
---
# 27. 开机启动
设置:
```text
Start with Windows
Start minimized
```
优先使用当前用户 Run 注册表项,不要创建系统服务。
示例:
```text
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
```
安装/卸载要做到可重复执行。
---
# 28. 窗口行为
关闭按钮默认行为:
```text
X
↓
隐藏窗口
↓
继续托盘运行
```
真正退出:
```text
Tray → Quit
```
设置里允许用户选择:
```text
Close to tray
```
未来可支持:
```text
Minimize to tray
```
---
# 29. 动画规范
允许少量微动画:
- 页面切换
- 连接状态变化
- Toast indicator
- Sidebar hover
禁止:
- 长时间 loading 动画
- 无限循环纯装饰动画
- 消息列表每条都弹入
- 高耗能动画
动画目标:
```text
100~180ms
```
原则:
> 让 UI 感觉“活着”,但不要让它“喧闹”。
---
# 30. 字体
优先使用 Windows 系统字体。
默认:
```text
Segoe UI
```
不要为了 UI 美观强制带一个大型字体文件。
中文显示必须正常。
消息正文要支持中文、Emoji 与常见 Unicode 字符。
---
# 31. 图标
优先:
- 简单 SVG/Path
- Slint 内置或项目内矢量资源
- Windows Shell 图标
避免:
- 大量 PNG
- 网络下载图标
- 图标字体
- 大型 icon package
---
# 32. 轻量化目标
Release 目标:
```text
单 exe
无 Node
无 WebView2
无 .NET runtime
无 Java
无 Python
```
实际 EXE 大小不人为硬性规定,但 Agent 必须记录最终结果。
记录:
```text
Raw EXE size
UPX 后大小(如果使用)
Installed size
Startup memory
Idle memory
Idle CPU
```
注意:不要为了体积盲目 UPX;先评估安全软件误报和启动开销。
---
# 33. 性能基线
启动后空闲:
目标:
```text
CPU:接近 0%
Memory:尽量 < 50 MB
```
说明:这是目标值而不是硬性 ABI 承诺,最终结果必须实测记录。
收到大量消息时,不允许:
- 每条消息触发 UI 全量刷新
- 每条消息写整个 state.json
- 每条消息产生大量同步文件 IO
应批量更新模型。
---
# 34. UI 数据模型
消息:
```rust
struct MessageItem {
id: i64,
app_id: i64,
app_name: String,
title: Option<String>,
message: String,
priority: i32,
timestamp: i64,
click_url: Option<Url>,
}
```
注意:不要让 Slint 直接持有复杂网络对象。
UI 使用简化后的 ViewModel。
---
# 35. 时间显示
消息列表:
```text
just now
2 min ago
1 hour ago
Yesterday
Sep 18
```
详情显示完整时间:
```text
2026-09-19 03:42:18
```
时间转换必须统一使用明确的时区策略。
---
# 36. 搜索与过滤
V1 可以支持:
- title
- message
- app name
搜索应在 UI model 层处理,而不是每按一个键都请求 Gotify Server。
过滤:
```text
All
Low
Normal
High
Max
```
V1 不需要复杂 query language。
---
# 37. 消息列表性能
不要为 1000 条消息一次性创建 1000 个复杂控件。
使用 Slint model/view 机制,只维护必要的可视数据。
目标:
```text
100
500
1000
```
条消息滚动不卡顿。
默认只保留最近 N 条,例如:
```text
200
```
以后再开放配置。
---
# 38. 安全边界
输入永远视为不可信:
- Server URL
- App Token
- Message title
- Message body
- click URL
- extras
特别注意:
```text
click URL
message extras
markdown
```
不能直接把未验证内容当 HTML、脚本或 shell 命令执行。
---
# 39. Markdown 策略
第一阶段:
```text
text/plain
```
第二阶段:
```text
text/markdown
```
禁止为了 Markdown 引入 WebView。
优先:
- 纯 Rust Markdown parser
- Slint 富文本能力
- 或安全降级为纯文本
若 Slint 当前版本无法优雅支持完整 Markdown,V1 直接 plain text 是可接受的。
---
# 40. 测试
必须有自动化测试:
## Unit
- JSON config parse
- state serialization
- URL validation
- priority mapping
- deduplication
- reconnect backoff
- time formatting
## Integration
- Gotify API mock
- WebSocket mock
- connection loss
- reconnect
- message ordering
- duplicate message
## Manual Windows
- Windows 10 x64
- Windows 11 x64
- 高 DPI 125%
- 高 DPI 150%
- 中文 Windows 用户环境
- 系统深色模式
- 系统浅色模式
- 开机启动
- 托盘
- 第二次启动单实例
- Toast 点击
- 断网重连
- 睡眠/唤醒
---
# 41. CI/CD
GitHub/Gitea 均可。
至少:
```text
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo build --release
```
Windows Release 构建:
```text
x86_64-pc-windows-msvc
```
ARM64:
```text
aarch64-pc-windows-msvc
```
是否最终发布 ARM64,取决于依赖兼容性。
---
# 42. Release 构建
推荐:
```toml
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
```
但每项设置都需要实际 benchmark。
不要仅为了“代码更小”牺牲明显的启动速度。
推荐先构建两份:
```text
release-balanced
release-small
```
实测后选默认发布配置。
---
# 43. Windows 子系统
Release 版本不显示控制台窗口。
可以采用 Slint 官方推荐的:
```rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
```
Debug 保留 console 方便排错。
---
# 44. Cargo 依赖最小化原则
Cargo.toml 目标类似:
```toml
[dependencies]
slint = "1.17"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.x", default-features = false, features = ["blocking", "json", "rustls-tls"] }
tungstenite = "0.x"
url = "2"
tracing = "0.x"
tracing-subscriber = "0.x"
anyhow = "1"
thiserror = "2"
```
Windows-specific dependency只引入确实使用的功能。
注意:版本号只是起始模板,Agent 在初始化项目时必须根据当前稳定版本与 Rust toolchain 做兼容验证,并提交 Cargo.lock。
---
# 45. 为什么 HTTP 默认考虑 rustls
不要无脑依赖平台 OpenSSL。
目标:
```text
single Windows executable
```
使用 rustls 可以避免在 Windows 上额外依赖 OpenSSL runtime 的复杂部署。
但必须在 release 构建实际验证证书兼容性和体积。
---
# 46. 网络线程设计
V1 推荐一个 network worker:
```text
NetworkWorker
├── HTTP client
└── WebSocket
```
消息方向:
```text
AppCore
↓ AppCommand
NetworkWorker
NetworkWorker
↓ NetworkEvent
AppCore
```
Channel:
```rust
std::sync::mpsc
```
或者当前 Rust 项目中更适合的 channel,但避免无意义引入完整 async runtime。
---
# 47. 生命周期
启动:
```text
main
↓
single instance
↓
load config
↓
load state
↓
initialize AppCore
↓
start NetworkWorker
↓
create Slint UI
↓
create tray
↓
run UI event loop
```
关闭:
```text
Quit
↓
stop accepting UI actions
↓
stop NetworkWorker
↓
flush state
↓
remove temp files
↓
destroy tray
↓
exit
```
---
# 48. 首次启动体验
没有配置时:
```text
Welcome to Gotify
Connect your first Gotify server
Server URL
[ https://gotify.example.com ]
Application Token
[ ••••••••••••••• ]
[ Test Connection ]
[ Continue ]
```
成功:
```text
✓ Connected
You're ready to receive notifications.
```
不要首次启动就弹出复杂设置窗口。
---
# 49. 连接测试
点击 `Test Connection`:
```text
Testing...
```
成功:
```text
✓ Server reachable
✓ Token valid
```
失败:
```text
✕ Unable to connect
Check URL / token / TLS / network
```
UI 不显示底层 panic 或 stack trace。
---
# 50. Message Notification 去重
如果:
```text
WebSocket 收到消息
```
已经显示过一次后,不允许因为:
```text
HTTP catch-up
```
再次通知用户。
正确流程:
```text
incoming message
↓
Dedup check
↓
new?
┌───┴───┐
no yes
│ │
ignore store
│
├── UI
└── Notify
```
---
# 51. 日志与隐私
敏感数据:
```text
token
full Authorization header
private message body(可选)
```
默认不记录。
配置中加入:
```text
[ ] Include message body in debug logs
```
默认关闭。
---
# 52. 颜色系统
不要在 `.slint` 每个组件里直接写大量颜色值。
统一定义:
```text
Background
Surface
SurfaceElevated
Border
TextPrimary
TextSecondary
TextMuted
Accent
Success
Warning
Danger
```
Light:
```text
明亮、低饱和、中性
```
Dark:
```text
深灰,而不是纯黑
```
Accent 只用于:
- selected item
- connection status
- primary button
- focus
不要整个 UI 变成“蓝色海洋”。
---
# 53. 圆角与间距
建立统一 spacing scale:
```text
4
8
12
16
20
24
32
```
Radius:
```text
6
8
12
16
```
遵循统一系统。
---
# 54. 可访问性
最低要求:
- 键盘可导航
- Tab 顺序合理
- Enter 执行主要按钮
- Escape 关闭对话框
- 文本对比度可读
- 不仅依靠颜色表达 priority
未来如果 Slint/Windows Accessibility 支持允许,再扩展完整无障碍。
---
# 55. 国际化
V1 默认中文 + English 架构可扩展。
不要把所有 UI 字符串硬编码到业务 Rust 逻辑里。
至少做到:
```text
UI strings
↓
centralized resource
```
默认语言:
```text
简体中文
```
英文作为 fallback。
如果实现成本过高,V1 先 English/Chinese 单语言构建也可,但字符串必须集中管理。
---
# 56. README 要写清楚
README 至少包含:
1. 截图
2. 特性
3. 系统要求
4. 下载
5. 配置方式
6. Gotify 兼容性
7. 开发环境
8. 编译方式
9. Release 构建
10. 隐私说明
11. License
不要堆大量理论说明。
---
# 57. Agent 的实施顺序
严格按照以下阶段执行。
## Phase 1 — Bootstrap
完成:
- Cargo project
- Slint hello window
- release profile
- Windows subsystem
- logging
- error handling
验收:
```text
cargo run
cargo build --release
```
可以打开无 console 的 Windows GUI。
## Phase 2 — UI Shell
完成:
- MainWindow
- Sidebar
- Message list
- Empty state
- Settings
- Dark/Light/System
- Window state
此阶段先使用 mock messages,不接网络。
## Phase 3 — Gotify API
完成:
- Server config
- HTTP client
- connection test
- fetch applications if needed
- message fetch
## Phase 4 — WebSocket
完成:
- connection state
- realtime message
- reconnect backoff
- graceful shutdown
## Phase 5 — State
完成:
- config.json
- state.json
- atomic save
- debounce checkpoint
## Phase 6 — Windows Integration
完成:
- tray
- toast
- URL open
- startup
- single instance
## Phase 7 — Polish
完成:
- animation
- typography
- icons
- empty states
- error states
- loading states
- high DPI
- keyboard navigation
## Phase 8 — Performance
完成:
- profile release
- measure RAM
- measure CPU
- startup time
- executable size
- message burst test
## Phase 9 — Release
完成:
- installer / portable zip
- checksum
- version
- release notes
- clean machine validation
---
# 58. Agent 工作规则
## Rule 1
不要一次性写完整应用。
必须每阶段可运行、可编译。
## Rule 2
每次修改后运行:
```text
cargo fmt
cargo check
cargo clippy
cargo test
```
适用时再执行 Windows release build。
## Rule 3
如果 Slint 某个 UI API 与预期不一致,先查当前版本官方文档,再实现。
不要凭记忆发明 API。
## Rule 4
如果 windows-rs API 不确定,优先查 focused crate 文档和官方示例。
## Rule 5
不要新增依赖来解决一个可以用标准库解决的小问题。
## Rule 6
不要为了抽象而抽象。
V1 需要的是:
```text
简单
稳定
清晰
```
## Rule 7
任何网络线程都不能直接操作 UI。
## Rule 8
任何 Windows API 调用都尽量封装在 `src/windows/`。
## Rule 9
Token 绝不能进入普通日志。
## Rule 10
每一个新增第三方依赖都要回答:
```text
为什么标准库/Slint/windows-rs 不够?
```
---
# 59. Commit 规则
建议:
```text
feat(ui): add main window shell
feat(gotify): add REST client
feat(gotify): add websocket worker
feat(windows): add tray integration
feat(windows): add toast notifications
feat(storage): add atomic state persistence
fix(gotify): handle reconnect correctly
perf(ui): batch message model updates
refactor(core): decouple network from UI
```
每个 commit 尽可能保持单一职责。
---
# 60. 最终验收清单
## 产品
- [ ] 首次启动可完成配置
- [ ] 可以连接 Gotify
- [ ] WebSocket 正常收消息
- [ ] 断线自动重连
- [ ] 重启后可以补消息
- [ ] 不重复通知
- [ ] Windows Toast 正常
- [ ] 点击 URL 正常
- [ ] 托盘正常
- [ ] 关闭窗口默认不退出
- [ ] 托盘 Quit 正常退出
- [ ] 开机启动可用
- [ ] 第二次启动不创建第二实例
## UI
- [ ] Light
- [ ] Dark
- [ ] System
- [ ] Empty state
- [ ] Loading state
- [ ] Error state
- [ ] Message detail
- [ ] Settings
- [ ] High DPI
- [ ] 中文
- [ ] Emoji
- [ ] 键盘操作
## 工程
- [ ] `cargo fmt`
- [ ] `cargo clippy`
- [ ] `cargo test`
- [ ] `cargo build --release`
- [ ] 无 Node
- [ ] 无 WebView2
- [ ] 无 .NET runtime
- [ ] 无 Windows App SDK
- [ ] 无 OpenSSL runtime 依赖(如果选 rustls)
## 性能
- [ ] 记录 Release EXE 大小
- [ ] 记录冷启动时间
- [ ] 记录 Idle RAM
- [ ] 记录 Idle CPU
- [ ] 记录 1000 条消息测试结果
- [ ] 记录 reconnect 压测结果
---
# 61. 推荐的最终产品形态
最终应该形成:
```text
Windows-Gotify-Client-Rust
┌─────────────────────────────┐
│ │
│ Beautiful UI │
│ Slint │
│ │
└──────────────┬──────────────┘
│
AppCore
│
┌───────────┼───────────┐
│ │ │
Gotify Storage Windows
│ │ │
REST + WS JSON Tray/Toast
│ Startup
│ Mutex
▼
Gotify Server
```
产品气质:
> 打开时精致,后台时安静。
不要追求“功能最多”,而是让它成为一个 **安装后几乎不用管,但消息来了马上可靠提醒** 的 Windows 小工具。
---
# 62. 第一版完成定义(Definition of Done)
当以下条件全部满足时,V1 才算完成:
1. 全部核心网络逻辑与 UI 解耦。
2. Slint 负责桌面 UI。
3. Rust 负责业务与网络。
4. Windows 原生能力集中在 Windows adapter 层。
5. WebSocket 有稳定自动重连。
6. 离线补偿经过实际 Gotify Server 验证。
7. 消息去重可靠。
8. Tray / Toast / Startup / Single Instance 可用。
9. 配置与状态写盘具备原子性。
10. Release 为单独的 Windows 原生应用,不依赖 WebView2、.NET、Node 或 Windows App SDK。
11. Light / Dark / System 三套主题视觉完整。
12. UI 在 1000 条消息规模下保持流畅。
13. 完成 Windows 10/11 实机验证。
14. README 与发布包完整。
---
# 63. 开始编码时的第一条任务
Agent 不要立即实现 Gotify。
第一步只做:
```text
Phase 1
+
Phase 2 UI Shell
+
Mock Message Data
```
先把下面这个界面做到成品级:
```text
┌─────────────────────────────────────────────────────────────┐
│ Gotify ● Connected │
├──────────────┬──────────────────────────────────────────────┤
│ All Messages │ Messages │
│ │ │
│ Servers │ Build succeeded │
│ Home │ GitLab Runner · Production │
│ NAS │ 2 min ago │
│ │ │
│ Filters │ Backup finished │
│ Normal │ NAS · Backup │
│ High │ 8 min ago │
│ Max │ │
│ │ │
│ Settings │ │
└──────────────┴──────────────────────────────────────────────┘
```
要求:
- 真实可操作
- 深色/浅色
- hover 状态
- selected 状态
- 空状态
- loading 状态
- error 状态
- 高 DPI 正常
- 中文正常
完成 UI Shell 后再进入网络层。
---
# 64. 最终设计判断
优先级顺序固定为:
```text
可靠性
>
简洁性
>
资源占用
>
可维护性
>
动画与装饰
```
任何新功能如果会显著增加:
- runtime
- binary size
- memory
- dependency count
- Windows deployment complexity
必须重新评估是否值得加入。
这个项目的核心竞争力不是功能数量,而是:
> **轻、快、稳、漂亮。**
---
## 官方参考
- Slint Desktop / Fluent / Native UI:https://slint.dev/desktop
- Slint Windows platform:https://docs.slint.dev/latest/docs/slint/guide/platforms/desktop/windows/general/
- Slint Rust API:https://docs.slint.dev/latest/docs/rust/slint/
- Slint SystemTrayIcon:https://docs.slint.dev/latest/docs/slint/reference/window/systemtrayicon/
- Microsoft windows-rs:https://github.com/microsoft/windows-rs
- windows-rs crate selection:https://github.com/microsoft/windows-rs/blob/master/docs/crates/windows.md
- Gotify Message Extras:https://gotify.net/docs/msgextras