使用 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
39 KiB
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 客户端。
核心原则:
- 小体积
- 低内存占用
- 后台低 CPU
- 无浏览器运行时依赖
- 单一主程序
- UI 精致现代
- 网络层与 UI 完全解耦
- Windows 能力原生调用
- 配置/状态简单可靠
- 默认不做过度工程化
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:HTTPtungstenite:WebSocketserdeserde_jsonurl
默认优先使用 blocking HTTP + 独立网络线程,而不是为了一个 WebSocket 客户端引入完整 async runtime。
只有当实现明确证明 async 能降低复杂度或改善功能时,才引入 tokio。
日志
推荐:
tracingtracing-subscriber
Release 默认降低日志等级,不允许每条消息都产生大量磁盘 IO。
本地数据
V1 默认使用 JSON 文件:
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. 核心架构
采用:
┌──────────────────────────┐
│ 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. 目录结构
目标结构:
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. 主窗口布局
推荐默认尺寸:
960 × 680 px
最小尺寸:
760 × 520 px
布局:
┌─────────────────────────────────────────────────────────────┐
│ 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。
内容:
- App logo
- All messages
- Server 列表
- Priority filter
- Settings
- About
Sidebar 应该视觉安静,不抢主内容焦点。
Message Card
每一条消息显示:
- priority 状态
- title
- app name
- message preview
- relative time
例如:
● Build succeeded
GitLab Runner · production
2 min ago
Priority 使用小面积视觉标记,不允许整张卡片使用高饱和背景。
推荐表现:
Low neutral
Normal subtle accent
High amber/orange accent
Max red accent
但是所有颜色都必须进入主题系统统一管理。
8. 消息详情页
点击消息后进入右侧详情视图,或使用可折叠 Detail Panel。
结构:
┌───────────────────────────────────────────────┐
│ ← 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::displayextras 支持 markdown 时,再提供 Markdown 展示 - 不要为了 Markdown 引入浏览器
- V1 可以先纯文本,再扩展 Markdown
Gotify 官方文档定义了 client::display extras,并支持 text/plain 与 text/markdown 等 content type。实现时以服务器实际 API 行为为准。
9. 空状态设计
没有消息时不要只显示:
No messages.
应提供漂亮的空状态:
✦
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. 设置交互要求
必须做到:
修改配置
↓
UI 本地状态变化
↓
点击 Save 或自动保存
↓
AppCore 更新 Network Worker
↓
必要时重连
不要让 UI 直接修改 config.json。
12. 托盘体验
应用关闭主窗口时,默认:
Window hidden
App process remains alive
Tray remains alive
托盘菜单:
Open
New messages: 3
────────────
Pause notifications
Settings
────────────
Restart connection
Quit
托盘图标必须:
- 有正常态
- 可选 disconnected 状态
- 不能使用模糊的小图标
- Windows 11 任务栏大小下仍能识别
Slint 1.17 已提供 SystemTrayIcon,可以优先评估 Slint 原生托盘能力;若 Windows 上需要更完整的原生行为,再使用 windows-rs focused crate。
13. 通知设计
新消息到来:
Gotify
Build succeeded
GitLab Runner · Production
要求:
- 标题来自 Gotify message title
- 正文来自 message message
- priority 用于通知层级
- click.url 存在时支持点击打开
Gotify 官方 extras 机制中,client::* 为客户端保留命名空间,client::notification 用于通知行为定制;实现应该严格按照 Gotify 官方 extras 语义处理,不要自行发明兼容格式。
URL 打开逻辑必须:
- 只接受显式字符串 URL
- 解析 URL
- 至少允许
https:///http:// - 拒绝
file:// - 拒绝
javascript: - 拒绝任意自定义 scheme,除非未来明确增加白名单
14. Gotify 网络设计
HTTP
封装:
GotifyApi
负责:
- 获取消息
- 获取应用
- 创建/读取必要资源
- 删除消息(如功能需要)
- Server health / version(如功能需要)
不要让 UI 直接知道 URL 拼接方式。
WebSocket
封装:
GotifyWebSocket
状态:
Disconnected
Connecting
Connected
Reconnecting
Stopped
事件:
enum NetworkEvent {
Connected,
Disconnected,
Message(GotifyMessage),
Error(NetworkError),
}
15. WebSocket 重连
必须支持指数退避:
1s
2s
4s
8s
16s
30s
30s...
服务器成功连接后重置退避时间。
不要:
loop {
connect();
}
禁止高频重连打爆服务器。
16. 离线补偿
不要简单照搬现有 C# 的 since=lastId 逻辑。
实现前先验证目标 Gotify Server 版本的分页语义。
目标行为:
程序正常运行
↓
保存最后处理消息 ID
↓
程序退出 / 网络断开
↓
重新启动 / 重连
↓
HTTP 补拉缺失消息
↓
去重
↓
更新 UI
↓
继续 WebSocket
必须测试:
- 断网 10 秒
- 断网 5 分钟
- 程序重启
- Gotify Server 重启
- 多条消息同时到达
- 消息 ID 非连续
- 重复消息
17. 消息去重
V1 不要建立复杂数据库。
采用:
VecDeque<i64>
HashSet<i64>
维护最近 N 条 ID,例如:
200
规则:
if seen(id) {
ignore
}
超过上限后删除最老 ID。
如果未来消息历史量扩大,再换 SQLite。
18. 状态设计
state.json 只保存真正需要跨进程持久化的状态。
建议:
{
"last_message_id": 123456,
"selected_server": "default",
"last_window_width": 960,
"last_window_height": 680,
"version": 1
}
不要每收到一条消息就立刻 fsync / rewrite 整个 JSON。
采用:
memory state
↓
mark dirty
↓
debounce 2~5s
↓
safe atomic write
或者在应用正常退出时保存,并定时 checkpoint。
写文件采用:
state.json.tmp
↓
flush
↓
atomic replace
↓
state.json
避免异常退出导致 JSON 损坏。
19. 配置设计
配置文件路径建议:
%APPDATA%/WindowsGotify/config.json
不要写入安装目录。
配置模型:
struct AppConfig {
servers: Vec<GotifyServerConfig>,
selected_server: Option<String>,
appearance: AppearanceMode,
notifications: NotificationConfig,
startup: StartupConfig,
}
V1 可以只支持一个 Server,但数据结构直接按多 Server 设计,避免未来再改 schema。
20. Token 安全
不要把 App Token 打印到日志。
禁止:
Authorization: Bearer xxx
进入日志。
HTTP 错误日志最多打印:
HTTP 401 Unauthorized
而不要打印完整请求头。
V1 可以继续用明文 JSON 保存 token,但必须:
- 限制文件访问权限到当前用户
- 日志中永不泄漏 Token
- UI 中默认密码样式隐藏
未来需要再考虑 Windows Credential Manager。
21. 多 Server 设计
产品层支持:
Server A
Server B
Server C
每个 Server:
{
"id": "server-a",
"name": "Home",
"url": "https://gotify.example.com",
"token": "...",
"enabled": true
}
V1 可以只同时连接一个 Server,以减少复杂度。
架构上不要把 single server 写死。
22. UI 与 Rust 通信
Slint 只绑定 Presentation Model。
推荐:
Rust Model
↓
Slint properties/models
���要:
Slint callback
↓
HTTP request
↓
JSON parse
↓
file IO
UI callback 应类似:
on_connect_clicked
on_message_clicked(id)
on_open_link(url)
on_save_settings()
on_quit()
然后统一发送给 AppCore。
23. AppCore 状态机
建议有一个明确的应用状态:
enum ConnectionState {
Disabled,
Connecting,
Connected,
Reconnecting { attempt: u32 },
Error,
}
UI 根据状态显示:
Connected → green dot / Connected
Connecting → spinner / Connecting
Reconnecting → amber dot / Reconnecting
Error → red dot / Connection error
不要在 UI 中自己推断网络状态。
24. 错误处理
业务错误统一:
AppError
分类:
ConfigError
NetworkError
WebSocketError
StorageError
WindowsError
ParseError
用户看到的错误必须是人话,例如:
Unable to connect to Gotify server.
Check the server URL and token.
日志中保存技术细节。
25. 日志策略
Debug:
详细连接过程
WebSocket 状态变化
消息解析
Release:
INFO
WARN
ERROR
不要默认记录所有消息正文。
例如:
[INFO] Gotify connected
[INFO] Received message id=1024 priority=5
[WARN] WebSocket disconnected
[INFO] Reconnecting attempt=3
而不是:
[INFO] token=xxx message="这里是一大段隐私内容"
26. 单实例
必须支持:
双击第二次
↓
检测已有实例
↓
激活现有窗口
不要启动两个 WebSocket 实例。
Windows 单实例可以放在:
src/windows/single_instance.rs
27. 开机启动
设置:
Start with Windows
Start minimized
优先使用当前用户 Run 注册表项,不要创建系统服务。
示例:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
安装/卸载要做到可重复执行。
28. 窗口行为
关闭按钮默认行为:
X
↓
隐藏窗口
↓
继续托盘运行
真正退出:
Tray → Quit
设置里允许用户选择:
Close to tray
未来可支持:
Minimize to tray
29. 动画规范
允许少量微动画:
- 页面切换
- 连接状态变化
- Toast indicator
- Sidebar hover
禁止:
- 长时间 loading 动画
- 无限循环纯装饰动画
- 消息列表每条都弹入
- 高耗能动画
动画目标:
100~180ms
原则:
让 UI 感觉“活着”,但不要让它“喧闹”。
30. 字体
优先使用 Windows 系统字体。
默认:
Segoe UI
不要为了 UI 美观强制带一个大型字体文件。
中文显示必须正常。
消息正文要支持中文、Emoji 与常见 Unicode 字符。
31. 图标
优先:
- 简单 SVG/Path
- Slint 内置或项目内矢量资源
- Windows Shell 图标
避免:
- 大量 PNG
- 网络下载图标
- 图标字体
- 大型 icon package
32. 轻量化目标
Release 目标:
单 exe
无 Node
无 WebView2
无 .NET runtime
无 Java
无 Python
实际 EXE 大小不人为硬性规定,但 Agent 必须记录最终结果。
记录:
Raw EXE size
UPX 后大小(如果使用)
Installed size
Startup memory
Idle memory
Idle CPU
注意:不要为了体积盲目 UPX;先评估安全软件误报和启动开销。
33. 性能基线
启动后空闲:
目标:
CPU:接近 0%
Memory:尽量 < 50 MB
说明:这是目标值而不是硬性 ABI 承诺,最终结果必须实测记录。
收到大量消息时,不允许:
- 每条消息触发 UI 全量刷新
- 每条消息写整个 state.json
- 每条消息产生大量同步文件 IO
应批量更新模型。
34. UI 数据模型
消息:
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. 时间显示
消息列表:
just now
2 min ago
1 hour ago
Yesterday
Sep 18
详情显示完整时间:
2026-09-19 03:42:18
时间转换必须统一使用明确的时区策略。
36. 搜索与过滤
V1 可以支持:
- title
- message
- app name
搜索应在 UI model 层处理,而不是每按一个键都请求 Gotify Server。
过滤:
All
Low
Normal
High
Max
V1 不需要复杂 query language。
37. 消息列表性能
不要为 1000 条消息一次性创建 1000 个复杂控件。
使用 Slint model/view 机制,只维护必要的可视数据。
目标:
100
500
1000
条消息滚动不卡顿。
默认只保留最近 N 条,例如:
200
以后再开放配置。
38. 安全边界
输入永远视为不可信:
- Server URL
- App Token
- Message title
- Message body
- click URL
- extras
特别注意:
click URL
message extras
markdown
不能直接把未验证内容当 HTML、脚本或 shell 命令执行。
39. Markdown 策略
第一阶段:
text/plain
第二阶段:
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 均可。
至少:
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo build --release
Windows Release 构建:
x86_64-pc-windows-msvc
ARM64:
aarch64-pc-windows-msvc
是否最终发布 ARM64,取决于依赖兼容性。
42. Release 构建
推荐:
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true
但每项设置都需要实际 benchmark。
不要仅为了“代码更小”牺牲明显的启动速度。
推荐先构建两份:
release-balanced
release-small
实测后选默认发布配置。
43. Windows 子系统
Release 版本不显示控制台窗口。
可以采用 Slint 官方推荐的:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
Debug 保留 console 方便排错。
44. Cargo 依赖最小化原则
Cargo.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。
目标:
single Windows executable
使用 rustls 可以避免在 Windows 上额外依赖 OpenSSL runtime 的复杂部署。
但必须在 release 构建实际验证证书兼容性和体积。
46. 网络线程设计
V1 推荐一个 network worker:
NetworkWorker
├── HTTP client
└── WebSocket
消息方向:
AppCore
↓ AppCommand
NetworkWorker
NetworkWorker
↓ NetworkEvent
AppCore
Channel:
std::sync::mpsc
或者当前 Rust 项目中更适合的 channel,但避免无意义引入完整 async runtime。
47. 生命周期
启动:
main
↓
single instance
↓
load config
↓
load state
↓
initialize AppCore
↓
start NetworkWorker
↓
create Slint UI
↓
create tray
↓
run UI event loop
关闭:
Quit
↓
stop accepting UI actions
↓
stop NetworkWorker
↓
flush state
↓
remove temp files
↓
destroy tray
↓
exit
48. 首次启动体验
没有配置时:
Welcome to Gotify
Connect your first Gotify server
Server URL
[ https://gotify.example.com ]
Application Token
[ ••••••••••••••• ]
[ Test Connection ]
[ Continue ]
成功:
✓ Connected
You're ready to receive notifications.
不要首次启动就弹出复杂设置窗口。
49. 连接测试
点击 Test Connection:
Testing...
成功:
✓ Server reachable
✓ Token valid
失败:
✕ Unable to connect
Check URL / token / TLS / network
UI 不显示底层 panic 或 stack trace。
50. Message Notification 去重
如果:
WebSocket 收到消息
已经显示过一次后,不允许因为:
HTTP catch-up
再次通知用户。
正确流程:
incoming message
↓
Dedup check
↓
new?
┌───┴───┐
no yes
│ │
ignore store
│
├── UI
└── Notify
51. 日志与隐私
敏感数据:
token
full Authorization header
private message body(可选)
默认不记录。
配置中加入:
[ ] Include message body in debug logs
默认关闭。
52. 颜色系统
不要在 .slint 每个组件里直接写大量颜色值。
统一定义:
Background
Surface
SurfaceElevated
Border
TextPrimary
TextSecondary
TextMuted
Accent
Success
Warning
Danger
Light:
明亮、低饱和、中性
Dark:
深灰,而不是纯黑
Accent 只用于:
- selected item
- connection status
- primary button
- focus
不要整个 UI 变成“蓝色海洋”。
53. 圆角与间距
建立统一 spacing scale:
4
8
12
16
20
24
32
Radius:
6
8
12
16
遵循统一系统。
54. 可访问性
最低要求:
- 键盘可导航
- Tab 顺序合理
- Enter 执行主要按钮
- Escape 关闭对话框
- 文本对比度可读
- 不仅依靠颜色表达 priority
未来如果 Slint/Windows Accessibility 支持允许,再扩展完整无障碍。
55. 国际化
V1 默认中文 + English 架构可扩展。
不要把所有 UI 字符串硬编码到业务 Rust 逻辑里。
至少做到:
UI strings
↓
centralized resource
默认语言:
简体中文
英文作为 fallback。
如果实现成本过高,V1 先 English/Chinese 单语言构建也可,但字符串必须集中管理。
56. README 要写清楚
README 至少包含:
- 截图
- 特性
- 系统要求
- 下载
- 配置方式
- Gotify 兼容性
- 开发环境
- 编译方式
- Release 构建
- 隐私说明
- License
不要堆大量理论说明。
57. Agent 的实施顺序
严格按照以下阶段执行。
Phase 1 — Bootstrap
完成:
- Cargo project
- Slint hello window
- release profile
- Windows subsystem
- logging
- error handling
验收:
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
每次修改后运行:
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 需要的是:
简单
稳定
清晰
Rule 7
任何网络线程都不能直接操作 UI。
Rule 8
任何 Windows API 调用都尽量封装在 src/windows/。
Rule 9
Token 绝不能进入普通日志。
Rule 10
每一个新增第三方依赖都要回答:
为什么标准库/Slint/windows-rs 不够?
59. Commit 规则
建议:
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 fmtcargo clippycargo testcargo build --release- 无 Node
- 无 WebView2
- 无 .NET runtime
- 无 Windows App SDK
- 无 OpenSSL runtime 依赖(如果选 rustls)
性能
- 记录 Release EXE 大小
- 记录冷启动时间
- 记录 Idle RAM
- 记录 Idle CPU
- 记录 1000 条消息测试结果
- 记录 reconnect 压测结果
61. 推荐的最终产品形态
最终应该形成:
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 才算完成:
- 全部核心网络逻辑与 UI 解耦。
- Slint 负责桌面 UI。
- Rust 负责业务与网络。
- Windows 原生能力集中在 Windows adapter 层。
- WebSocket 有稳定自动重连。
- 离线补偿经过实际 Gotify Server 验证。
- 消息去重可靠。
- Tray / Toast / Startup / Single Instance 可用。
- 配置与状态写盘具备原子性。
- Release 为单独的 Windows 原生应用,不依赖 WebView2、.NET、Node 或 Windows App SDK。
- Light / Dark / System 三套主题视觉完整。
- UI 在 1000 条消息规模下保持流畅。
- 完成 Windows 10/11 实机验证。
- README 与发布包完整。
63. 开始编码时的第一条任务
Agent 不要立即实现 Gotify。
第一步只做:
Phase 1
+
Phase 2 UI Shell
+
Mock Message Data
先把下面这个界面做到成品级:
┌─────────────────────────────────────────────────────────────┐
│ 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. 最终设计判断
优先级顺序固定为:
可靠性
>
简洁性
>
资源占用
>
可维护性
>
动画与装饰
任何新功能如果会显著增加:
- 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