使用 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
2237 lines
39 KiB
Markdown
2237 lines
39 KiB
Markdown
# 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
|
||
|