一款本地优先、低打扰的 Windows 桌面护眼提醒工具
按 20-20-20 法则帮你科学用眼:每用眼约 20 分钟,抬头远眺 20 英尺(6 米)外 20 秒。 它会感知你是否真的在看屏幕、会在开会/全屏时自动让路,并把用眼、提醒、症状自评沉淀成本地、可追溯的趋势数据。
长时间盯着屏幕会导致数字视疲劳(Digital Eye Strain):眼干、视物模糊、头痛、肩颈僵硬。眼科普遍推荐的缓解方法是 20-20-20 法则——每 20 分钟,看向 20 英尺(约 6 米)外的物体,持续 20 秒,让睫状肌放松、并通过完整眨眼补充泪膜。
市面上的提醒软件大多有两个毛病:
- 机械计时——不管你是在专注敲代码,还是早就离开了座位,它都照样弹窗,久而久之让人反感、直接关掉。
- 粗暴打断——一弹就是全屏黑幕盖住整个桌面,把"提醒"做成了"强制"。
远眺 Gaze20 针对这两点重新设计:只在你真正用眼时计时,只用一张屏幕中央的小卡片温柔提示,并且在每块显示器上同步出现。
| 特性 | 说明 |
|---|---|
| 🧠 活动感知计时 | 通过系统空闲时间 + 前台窗口判断你是在敲键盘、在阅读、还是已离开,按权重累计"有效用眼时间",而非死板的墙上时钟。 |
| 🖥️ 多屏同步提醒 | 有几块显示器,就在几块显示器的正中央同时弹出提醒卡片,倒计时、按钮状态和操作结果同步。 |
| 🌿 温柔不霸屏 | 提醒是约屏幕 1/10 的小卡片,带呼吸式倒计时环和轻量入场动画;四周桌面照常可见——是提醒,不是遮挡。 |
| 🤫 自动让路 | 检测到全屏/会议/演示(Zoom、Teams、PPT 放映、OBS、播放器、游戏等)时,到点的微休息会自动延后而不是打断你。 |
| ⏱️ 微休息 + 深休息 | 微休息(远眺 20 秒)防急性疲劳;深休息(起身 3–5 分钟)防久坐,二者独立计时。 |
| 🎚️ 三档强度 | 保守 / 平衡 / 激进,对应不同的提醒间隔与时长,按你的工作强度选。 |
| 📈 本地趋势统计 | 用眼时长、远眺完成率、风险评分、每小时用眼热力图、按 App 用眼、用眼构成(主动/阅读/会议)、症状趋势,全部本地留存、可回溯。 |
| 🩺 症状自评 | 记录眼睛红血丝、眼干、模糊、头痛、肩颈 5 项评分;弹窗会根据严重程度切换表情,且在休息提醒进行中会自动延后。 |
| 🔕 勿扰与暂停 | 一键勿扰、暂停记录、延后 5 分钟、立即远眺;托盘常驻,最小化运行。 |
| 🧪 工程模式 | 设置页内置隐藏工程工具,可手动预览微休息、深休息、症状自评弹窗,调节眼睛分数并开启快速演示模式。 |
| 💾 稳健的数据层 | 本地 SQLite(WAL)+ 显式 schema 迁移链 + 迁移前自动备份 + 损坏自动恢复 + 原始活动事实层 + 导出/导入迁移。 |
| 🔐 本地优先 | 所有数据只存在你自己的电脑上,不联网上传、不采集摄像头画面、不记录窗口标题。 |
护眼软件不该成为隐私黑洞。远眺 Gaze20 的设计原则:
- 不采集摄像头画面。 用眼检测完全基于系统空闲时间和前台进程名,不读取任何图像。
- 不持久化窗口标题。 前台窗口标题仅在内存中用于"是否会议/全屏"的关键词判断,绝不写入磁盘、绝不显示;落地与展示的只有进程名(如
chrome.exe)。 - 不联网。 数据全部存在本地 SQLite,没有云同步、没有遥测。(仅"检查更新"在你手动点击时才会访问你自行配置的更新地址。)
- 数据随时可带走。 设置页可一键导出全部数据为 JSON,换机/重装时再导入(幂等去重,不覆盖本机数据)。
详见源码
src-tauri/src/lib.rs中visible_process_label()的隐私注释。
核心是一个纯逻辑状态机(src-tauri/src/engine.rs),由 Rust 端每秒驱动一次:
每 1 秒
┌───────────────────────────────────────────────────────────┐
│ 采集活动快照 (Win32) 推进状态机 执行副作用 │
│ ───────────────── ────────── ────────── │
│ · 系统空闲秒数 · 累计有效用眼 · 弹出提醒卡 │
│ · 前台进程 / 全屏 · 判断微/深休息到点 · 写事件日志 │
│ · 会议/全屏 → 该让路吗 · 计算风险评分 · 落地聚合表 │
│ │ │ │ │
│ └──── eye_weight ──────┴──── Decision ─────┘ │
└───────────────────────────────────────────────────────────┘
有效用眼时间不是简单的开机时长,而是按活动状态加权累计:
| 状态 | 判断依据 | 计时权重 |
|---|---|---|
| 键鼠活跃 | 空闲 ≤ 60 秒 | ×1.0(满额计时) |
| 疑似阅读 | 无输入但在浏览器/编辑器/PDF 等,且未超过"阅读宽限" | ×0.7 |
| 全屏/会议中阅读 | 同上但正在放映/会议 | ×0.45(轻量计时并延后提醒) |
| 已离开 | 空闲 ≥ "离开判定"分钟 | ×0(暂停累计) |
三档强度预设:
| 模式 | 微休息间隔 | 深休息间隔 | 远眺时长 | 深休息时长 |
|---|---|---|---|---|
| 保守 | 30 分钟 | 90 分钟 | 20 秒 | 3 分钟 |
| 平衡(默认) | 20 分钟 | 60 分钟 | 20 秒 | 3 分钟 |
| 激进 | 15 分钟 | 45 分钟 | 30 秒 | 5 分钟 |
风险评分(0–100) 综合连续用眼时长、当日总时长、微休息完成率、远眺达成度、当前用眼压力和当天症状自评六项,映射为"眼睛状态"评分与配图,让你对疲劳程度一目了然。
状态机是纯函数、与 Tauri/数据库解耦,因此可被单元测试覆盖(见
engine.rs末尾的测试)。它的状态会持久化,重启后能恢复当天进度。
提醒弹窗由 public/overlay.html 渲染,Rust 侧的 OverlayCoordinator 负责协调弹窗优先级:休息提醒优先显示;如果症状自评刚好到点,会等休息提醒结束后再弹,避免多屏场景下出现两个弹窗互相覆盖。
- 今日概览:眼睛状态、实时用眼评分、当前连续专注时长、下次提醒倒计时、今日用眼时间轴、今日建议。
- 提醒中心:今日提醒记录(完成 / 延后 / 跳过 / 自动延后)。
- 趋势统计:多日用眼时长、远眺完成率、风险评分趋势、每小时用眼热力图、风险构成、症状趋势和趋势洞察。
- 症状记录:眼睛红血丝 / 眼干 / 模糊 / 头痛 / 肩颈五项自评打分与历史,支持单独导出。
- 设置:强度模式、检测参数(阅读宽限、离开判定)、勿扰、开机自启、检查更新、数据导出/导入、保留天数、每日症状提醒、工程模式。
- 提醒卡片(独立窗口):屏幕中央的小卡片,带 3D 感倒计时点、入场动效、开始休息 / 完成休息 / 延后 / 跳过。
- 症状自评弹窗(独立窗口):5 项评分 + 表情状态卡;越严重表情越难过,提交后写入本地 SQLite。
- 到 Releases 下载最新的
远眺 Gaze20_x.y.z_x64-setup.exe(NSIS 安装包)、远眺 Gaze20_x.y.z_x64_zh-CN.msi,或免安装版远眺_Gaze20_x.y.z_portable_x64.zip。 - 双击安装。首次运行后程序常驻系统托盘,最小化时仍在后台计时。
- 右键托盘图标可:打开主界面 / 立即远眺 / 暂停记录 / 退出。
⚠️ 当前安装包尚未代码签名,Windows SmartScreen 可能提示"未知发布者"。签名方案见doc/RELEASE.md。建议优先用安装包安装(而非直接拷贝 exe),否则系统通知(toast)可能因缺少开始菜单快捷方式而不弹。
平台说明:活动感知依赖 Windows API(
GetLastInputInfo、前台窗口、注册表自启),目前仅完整支持 Windows 10/11。非 Windows 平台可编译运行 UI,但活动检测为模拟桩。
所有数据都在你本机的应用数据目录下:
| 文件 | 路径 | 说明 |
|---|---|---|
| 主数据库 | %APPDATA%\com.gaze20.desktop\gaze20.db |
SQLite(WAL),全部用眼/提醒/症状/聚合数据 |
| 数据库备份 | 同目录 gaze20.db.bak* |
迁移前备份 + 滚动 24h 备份 |
| 运行日志 | %LOCALAPPDATA%\com.gaze20.desktop\logs\gaze20.log |
排障用 |
| 旧版数据 | %APPDATA%\com.gaze20.desktop\gaze20-data.json |
旧 JSON,首启自动迁移进 SQLite 后备份为 .json.bak |
卸载后如需彻底清除,删除上述 com.gaze20.desktop 目录即可。
┌──────────────────────────── 前端 (WebView2) ────────────────────────────┐
│ React 18 + TypeScript + Vite │
│ · 主窗口 index.html → src/App.tsx(概览/趋势/症状/设置) │
│ · 提醒卡 public/overlay.html(独立窗口,纯 HTML/CSS/JS,无框架) │
└───────────────▲───────────────────────────────────────▲────────────────┘
│ invoke 命令 / 监听 engine-state 事件 │ overlay-action
│ │ overlay-start
┌───────────────┴──────────────── Rust 核心 (Tauri 2) ─────┴────────────────┐
│ engine.rs 纯状态机:计时、微/深休息判定、风险评分(单元测试覆盖) │
│ lib.rs 1 秒权威循环、Win32 活动快照、托盘、命令、多屏提醒与弹窗调度 │
│ db.rs SQLite:迁移链 / 事实表 / 聚合表 / 备份 / 恢复 / 导出 │
│ 插件 single-instance · notification · log · updater │
└───────────────────────────────────▲─────────────────────────────────────┘
│
┌──────────┴──────────┐
│ gaze20.db (SQLite) │
└─────────────────────┘
技术选型:
- Tauri 2 —— 体积小、内存占用低,复用系统 WebView2,无需打包 Chromium。
- Rust 拥有状态机与数据 —— 计时与判定不依赖前端是否打开,重启不丢上下文,关键逻辑可单测。
- SQLite(rusqlite,bundled) —— SQLite 编译进二进制,免去系统依赖;WAL 模式 + 显式迁移链保证数据可演进、可恢复。
- 独立 overlay.html 提醒窗 —— 与主应用解耦的轻量卡片,多屏各一份、逻辑像素居中、不透明不霸屏,并支持休息提醒与症状自评两种模式。
- OverlayCoordinator 弹窗协调器 —— 休息提醒优先,症状自评自动排队,避免同一时间多屏弹窗互相覆盖或误关。
数据库表(SCHEMA_VERSION = 7): app_meta、settings、activity_sessions、reminder_events、reminder_sessions、symptom_records、daily_stats、hourly_stats。其中 symptom_records 已包含 redness(眼睛红血丝)维度,并通过当天最新自评参与风险评分。
Gaze20/
├─ index.html 主窗口入口
├─ src/ 前端 (React + TS)
│ ├─ App.tsx 全部页面与组件
│ ├─ main.tsx React 挂载
│ └─ styles.css 样式
├─ public/
│ ├─ overlay.html 提醒卡片(独立窗口)
│ └─ eyes/ 眼睛状态配图 (webp,构建产物用)
├─ src-tauri/ Rust + Tauri
│ ├─ src/
│ │ ├─ lib.rs 主逻辑:循环/命令/托盘/活动快照/提醒窗/弹窗调度
│ │ ├─ engine.rs 纯状态机(含单元测试)
│ │ ├─ db.rs SQLite 数据层(含单元测试)
│ │ ├─ native_reminder.rs 备用的原生 GDI 渲染器(当前未启用)
│ │ └─ main.rs 入口
│ ├─ capabilities/ Tauri 权限能力
│ ├─ tauri.conf.json 应用与打包配置
│ └─ Cargo.toml
├─ scripts/ 构建辅助(图标生成、眼图优化)
├─ CHANGELOG.md 版本发布记录
├─ doc/ 发布与产品化文档
│ ├─ RELEASE.md 代码签名 + 自动更新配置
│ └─ PRODUCTION-READINESS.md 产品化待办清单
└─ .github/workflows/ Windows 构建与发版 CI
前置要求: Node.js ≥ 18、Rust stable、Windows 上需 WebView2 运行时(Win10/11 一般已内置)。
# 安装前端依赖
npm install
# 开发模式(热重载;同时起 Vite 与 Tauri)
npm run tauri dev
# 仅跑前端(浏览器里调 UI)
npm run dev:web运行测试:
# Rust 单元测试(状态机 + 数据库 + 弹窗调度,当前 26 个)
cargo test --manifest-path src-tauri/Cargo.toml
# 前端类型检查
npx tsc --noEmit
# Rust 静态检查(CI 同步执行)
cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
# 前端生产构建
npm run build自检提醒卡片(无需等到点):设置环境变量 GAZE20_SELF_TEST_REMINDER=1 后启动,程序会在 ~1.5 秒后弹出休息卡片;设置 GAZE20_SELF_TEST_SYMPTOM=1 可直接弹出症状自评卡片。设置页里的“工程模式”也能手动预览微休息、深休息和症状自评。
# 产出 Release 可执行文件 + MSI + NSIS 安装包
npm run tauri build
# 产物位于 src-tauri/target/release/ 及 bundle/{msi,nsis}/CI(.github/workflows/build.yml)会在向 main / develop 推送、提交 PR 或手动触发时,于 windows-latest 上执行 tsc、cargo test、clippy -D warnings 和 Tauri release 构建,并上传 exe 与安装包产物。发布记录见 CHANGELOG.md。
代码签名与自动更新的脚手架已接好,但证书、更新签名私钥、更新服务器属于你自己的基础设施,需自行补齐——完整步骤见 doc/RELEASE.md。
⚠️ 自动更新私钥绝不能提交进 git。
当前版本(0.3.0)是一个功能完整、可日常使用的版本,但作为面向大众的成熟产品仍有打磨空间。完整的产品化待办见 doc/PRODUCTION-READINESS.md,要点:
- 未代码签名 / 自动更新未配真实服务端 —— 安装会触发 SmartScreen 警告;更新链路就绪但指向占位地址。
- 风险模型仍是启发式模型 —— 已纳入症状自评,但权重仍来自产品判断,后续需要更多真实使用数据校准。
- 前端仍是单文件主应用 ——
App.tsx已承载多页面和数据转换,后续应拆分成页面、hooks 与 Tauri 命令层。 - 仅支持简体中文、无障碍能力有限 —— 暂无 i18n 与完整键盘导航。
- 仅 Windows —— 活动检测依赖 Win32,跨平台需补适配。
欢迎按 PRODUCTION-READINESS.md 的优先级提 issue / PR。
作者:yuanzihao