# 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 HashSet ``` 维护最近 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, selected_server: Option, 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, message: String, priority: i32, timestamp: i64, click_url: Option, } ``` 注意:不要让 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