作品 / Morrow · 桌面健康提醒
桌面工具 · DESKTOP2026NO.19个人项目

Morrow · 桌面健康提醒

把「该喝水、该起身」交给桌角一只会跟着完成率长芽的宠物——它会数你坐了多久、该安静的时候真的安静,提醒与打卡一步都不离开这台机器

GoWailsVue3Element PlusSQLiteGORM
Morrow · 桌面健康提醒 界面截图
1 / 5
仪表盘

缘起

番茄钟、喝水提醒、拉伸提示,这些年装过不少,最后都关在同一个地方:通知权限。

不是它们提醒得不准。是它们在该安静的时候响了——改一个 bug 改到一半,全屏弹窗盖住代码;跟人共享屏幕讲方案,桌角突然冒出一只宠物、或者屏幕正中炸开一个"该喝水了"。健康提醒这个品类的失败,多数不是漏提醒,而是它自己变成了新的干扰源。

另一件事是我不太愿意为"多喝水"注册账号。想把这套事凑齐——喝水、起身、走动、看看远处——得装三个应用,每个都要登录,打卡数据全在别人的云上。为了记得喝水,把每天坐在电脑前的生活节奏上传出去,这笔交换不划算。

所以想做的东西很具体:一只住在桌角的宠物,把这几件事一起承担下来;该安静的时候它真的安静,数据一步都不出这台机器。

背景

把桌面健康提醒这个赛道在 2026 年横向看了一遍(Stretchly、LookAway、Time Out、Shui、Viraam 这一类),有个结论比功能清单更重要:这个品类的下限已经变了。"定时弹窗 + 跳过/推迟"不再够用,自动暂停、休息引导、常驻入口、统计,至少得占两项。

对照下来是三个缺口:

  • 免打扰基本只做到"静默时段":能设"晚上十点后别吵",但不知道你现在在开会、在演示、还是根本不在座位上。而用户口碑里最被认可的恰恰是这一块。
  • 响过之后没有下一步:提醒弹出去了,但"起身"这件事本身没人管。用户点了"知道了",然后继续坐着。
  • 要么是单点计时器,要么是移动端记录本:桌面端没有谁把"喝水 / 起身 / 走动 / 护眼"这类多域编排做在一起,更没有人给它一个能长期看的情感载体。

这三条决定了产品要有哪些能力,也决定了它不该长成什么样——不做摄像头监测、不做强制锁屏的"严格模式"、不做云端统计。补基础能力的时候很容易补成"又一个 Stretchly",那就没意义了。

v1.1 补的是第四个缺口,它不在上面三条里,是做完前面三轮才显出来的:固定排程接不住"你现在到底坐了多久"。每小时提醒一次,可能你刚站起来接完水回来它又响了;也可能你已经连着坐了九十分钟,而下一条提醒还在四十分钟之外。时间表是死的,人是活的。

方案

按一个人在工位上的一天来组织:

装完就能开始。 首次打开只问三件事:你的工作时段、最想解决的两件事、挑一套现成计划。五个内置模板(久坐办公、会议密集日、备考冲刺、居家办公、护眼优先)都是确定性内容——不调用模型、不需要密钥、不联网。选中即复制成一份属于你的计划,之后在计划页逐条改间隔、时段与每日目标。想要更个性化的,再走"用一段话描述处境 → 生成整套计划"那条路。

提醒由一个角色承担,而不是一串弹窗。 桌角一只小宠物常驻,头顶一株芽按今天的完成率分档生长,当天生长、次晨归零。有待处理时它进等待姿态并冒出气泡;提醒清空后庆祝一下;休息时它陪着你倒计时。一眼扫过去就知道"今天做得怎么样",不需要打开主窗口。

它会数你坐了多久,而不是只看表。 一个连续的用机时钟,切应用不清零——在三个编辑器之间来回切不算休息,得真的离开十五分钟以上才归零,锁屏也算休息。坐满一小时、而十五分钟内又没有正式站立提醒,它才开口一次:每段久坐最多两次、每天最多五次、两次之间至少隔半小时。真去休息过的,接下来十分钟内到点的站立提醒会静默跳过,不算错过也不进待处理;锁屏超过五分钟、且这次已经坐了半小时以上,解锁时自动算你完成一次。提醒还是你的计划,久坐感知只做加减法——不另起一套计时器,也不推翻你自己定的间隔。

它说话不好听,但每句都留台阶。 这一版把宠物人格从"温和陪伴"改成了"毒舌健康搭子",默认就是损友式的那一档:坐满时会说"椅子快以为你长在上面",周统计变差时会说"椅子很高兴,你的腿未必"。硬边界写在内容里:只吐槽久坐行为与统计,不评价外貌、智力、家庭与健康状况,不做健康判断,每句话后面必须跟一个能执行的台阶(起来走两步、再坐十分钟、今天先这样)。嫌难听随时能换——设置里三档(毒舌 / 活泼 / 温和),任何气泡里点一下「别这样说」就切成温和并记住,还有「今天先这样」让它当天闭嘴。

该安静的时候真的安静。 六路信号:连续几分钟没碰键盘鼠标、锁屏或休眠、会议应用在前台、全屏应用、系统专注、日历正在忙。每一路都能单独关。暂停期间到点的周期性提醒顺延不补发——恢复后按原间隔重新计时,而不是把错过的十来条一次性糊到脸上。

共享屏幕时不被看见。 演示模式一键进入:暂停提醒、隐藏宠物与遮罩,并在系统支持时把它们排除出屏幕共享与录屏。会议或全屏持续几秒后,宠物会自己提议进入;靠建议进入的,在会议结束后自动退出,手动进入的等你手动退出。退出后不补弹演示前的待处理。

响过之后要有下一步。 站立、走动、护眼三类提醒到点前一分钟先预警,到点进入倒计时覆盖层,配一条与类型匹配的动作:护眼是远眺加呼吸,站立是肩颈拉伸,走动是起身走动。倒计时结束自动记完成;提前结束可以选"算完成"或"跳过"。喝水、如厕这类保持简单打卡,不硬塞一套流程。

错过的要看得见,但不补弹。 一次性提醒("明天下午三点交材料")独立于场景,关掉某个习惯场景也不会让它消失;错过了记成"已错过",留在首页,不再响。

进度要有据可查。 每条提醒都能看到今日进度与连续达标天数;首页一屏读完待处理、已错过、今日进度与下一次触发的倒计时;久坐页能看到今天一共坐了多久、最长一口气坐了多久、主动起来几次,最近七天可以往回翻。

技术架构

模块技术选型
桌面运行时Wails v3(beta.23),WebView2 / WKWebView
后端Go 1.25 + GORM + SQLite(纯 Go 驱动,交叉编译不带 CGO)
前端Vue3 + Vite + Element Plus + Pinia,hash 路由
提醒引擎纯时间计算 + 调度循环 + 一次性提醒全局扫描三层分离
久坐感知全局连续时钟 + 分级空闲判定(5 / 15 分钟两档)+ 与提醒计划的加减法
管道契约错误码 / 分页 / 事件三类常量在 Go 单一真相,前端只做镜像
工程化Makefile 统一入口 + 架构护栏 + 依赖登记制 + OpenSpec 治理
交付macOS 美化 dmg / Windows NSIS 安装器,CI 覆盖 macOS 与 Windows

几个值得说的做法:

  • 单进程交付:根包用 //go:embed 把前端产物嵌进二进制,用户拿到的就是一个可执行体;frontend/dist 不入库,make test 会先检查它存不存在并给出修复命令,而不是让你对着 pattern all:frontend/dist: no matching files found 猜。
  • 分层由护栏强制:绑定方法层(app.go)不许 import gorm 或 database,必须经 service;model 层是叶子。越界会在 go test 阶段变红,不靠 code review 记性。
  • 一次性提醒复用了 reminders 表:加一列 once_at(0 表示周期项)、scenario_id 置空、按绝对时刻全局扫描。没有另建一张表加一套状态机——完成/跳过/推迟/去重的逻辑写两遍,迟早漂移。
  • 久坐时钟是全局的,不是按应用的:切窗口不重置计时,只有满十五分钟的空闲才归零,锁屏单独作为休息信号。否则"我在三个窗口之间来回切"会被读成"我休息了三次",而这个错误的表现是它从此再也不提醒你——不报错、不崩溃,只是默默失效。
  • 同一句话只有一处出处:休息预警在 60 秒里每一秒都会被读一次,剩 8 秒还说"1 分钟后"就是假话,所以措辞按剩余时间分三档(1 分钟 / 半分钟 / 马上)。分档只改措辞、不改投递时刻,且整仓只有一张表和一处函数,宠物气泡与菜单栏共用同一份快照,越界副本由护栏拦住。
  • 六路免打扰信号只读本机信息:空闲时长、锁屏状态、前台应用身份、全屏几何、系统专注与日历忙闲的布尔值。不读屏幕内容、不读窗口标题、不读日历事件详情、不碰摄像头麦克风。
  • 窗口捕获排除用系统能力兜底而不是更聪明的检测:macOS 走 NSWindow.sharingType = .none,Windows 走 SetWindowDisplayAffinity(WDA_EXCLUDEFROMCAPTURE);Linux 是 no-op,界面如实降级,不假装支持。
  • 宠物气泡出现时临时扩窗:底边不动、向上与两侧扩,以脚底所在屏幕的工作区夹取;退出时持久化的是基础几何而不是扩出来的矩形——否则下次启动宠物会凭空下移一段。
  • 开发态有一条给 Agent 用的通道:make dev-mcp 会在本机回环上开一个 MCP 服务,能调绑定方法、读 DOM、等事件。它只能在显式开启时存在,默认构建里一行代码都没有,由源码层、机制层、产物层三道检查共同保证。

成果

  • 能交付,不是能跑 demo:macOS 能打出带拖拽引导的美化 dmg,Windows 能打出免管理员的 NSIS 安装器;目标机器不需要 Go、Node 或数据库。两个版本都打了 tag(v1.0.0 / v1.1.0)。
  • 产出是可数的:从空目录到可打包安装,21 次提交、65 张实现票、224 个源文件共 45,036 行(Go 33,286 + 前端 11,750);394 个测试函数、12 份架构决策记录、7 份能力规格。
  • 自用中:现在每天由它管喝水、起身与久坐。它唯一让我愿意多点一下"完成"的地方是那株芽——进度从"一个数字"变成了"一个会长大的东西"。而久坐感知上线之后,最直接的变化是我开始在意"最长一口气坐了多久"这个数字。
  • 没对外发布:目前只有我自己在用,没有用户数可以报。这一栏我不打算填一个好看的假数字。

体验

源码在 github.com/Benny66/morrow,README 里有构建步骤与截图。它是本地优先的桌面应用,没有可点的网页版 demo——想用起来,要么装一个安装包,要么照 README 从源码构建一次。

想验两件事的话:

  1. 把喝水提醒的间隔设成两分钟,然后锁屏。等锁屏结束回来,你不会看到一串补发的弹窗——它们被顺延了,不补发。
  2. 什么都不做,连坐一小时。如果这一小时里没有正式的站立提醒,桌角那只小水滴会开口损你一句,然后等你选"起来走走"。
MANUAL · 说明书

它到底能做什么、怎么跑起来

散文讲的是「为什么做」,这一节讲的是「它到底是什么」——可直接对照评估。

01 / 规格
交付形态
一个安装包装进系统就能用,不需要另装运行环境,也不依赖外部数据库服务
数据存放
提醒、打卡、久坐统计与统计全部留在本机一个数据文件里;没有账号、没有云同步,不需要注册
提醒类型
周期性(每天某个时段内按间隔重复)、每日单点(每天同一时刻一次)、一次性(指定某天某时,只响一次)
久坐感知
一个连续的用机时钟(切应用不清零,离开 15 分钟以上才归零);坐满 60 分钟且 15 分钟内没有正式站立提醒,宠物才开口一次,每天最多 5 次
宠物人格
默认「毒舌健康搭子」,另有活泼与温和两档;任何气泡里都能一键切换并记住偏好
免打扰信号
空闲、锁屏休眠、会议应用在前台、全屏、系统专注、日历忙闲,六路各自可单独关闭
上手成本
干净安装后三步(工作时段 → 两个关注目标 → 场景模板)就能得到一套可编辑的计划,全程不联网
适用规模
面向一个人的工位,单机单用户
02 / 功能清单
  • 提醒与计划每天几点到几点、隔多久提醒一次、每天目标几次,逐条可改;也能建只响一次的事
  • 场景模板久坐办公、会议密集日、备考冲刺、居家办公、护眼优先五套现成计划,选中即复制成自己的,之后随便改
  • 首次引导装完只问三件事——工作时段、最想解决的两件事、用哪套模板,问完就有一套能用计划
  • 智能免打扰离开座位、锁屏、开会、全屏、系统专注或日历正忙时不打扰;恢复后不把错过的攒一堆一次性弹回来
  • 久坐感知只认真的休息,不看固定排程:离开十五分钟才算歇过,锁屏也算;坐了多久它心里有数
  • 休息模式站立、走动、护眼到点前先提醒一句,到点进入倒计时,配一条对应的动作(远眺呼吸、肩颈拉伸、起身走动)
  • 演示模式讲方案、共享屏幕时一键安静,宠物与遮罩不进共享画面;系统不支持时如实说明,不假装生效
  • 桌面宠物桌角一只有名字的小宠物,头顶的芽按今天完成率长;待处理、休息陪做、暂停原因都由它说话
  • 毒舌人格坐太久它会开口损你两句,每句都留一个能执行的台阶;嫌难听可以立刻切成温和
  • 久坐页今天坐了多久、最长一口气多久、主动起来几次,最近七天可以翻
03 / 部署与运行
  • 要什么环境:macOS 12 及以上,或 Windows 10/11;装完即可用,不需要 Go、Node 或数据库
  • 多久起得来:首次打开走三步向导,选完工作时段与模板就有一套可启用的计划;全程不需要联网,也不需要模型密钥
  • 怎么维护:数据与配置都在用户目录同一处,备份就是拷这个目录;应用内也提供数据库导出与恢复
04 / 已知限制
  • 不做医疗建议:只覆盖喝水、起身、走动、用眼放松、如厕这类日常保健,不涉及疾病诊断、用药方案、孕期或慢病管理
  • 只有本机这一份数据:没有账号与云同步,换机器要自己导出导入;也没有手机端
  • 应用画像默认关闭:它可以选择记录你大概在做什么类别(写代码、做设计、浏览、沟通、影音、其他),但要单独授权才开;只存应用身份与时长,不读窗口标题、不截图、不上传
  • 毒舌有硬边界:只吐槽久坐行为与统计,不评价外貌、智力、家庭与健康状况,不做健康判断,每句都留台阶;嫌难听可以换成温和
  • AI 是可选入口:想让模型帮你生成计划、或从一段话/一张图里解析出提醒,需要自备一个兼容接口的模型服务;不用它,模板与手动添加全部可用

有类似的事要管?

说说现在是怎么做的——Excel、微信群、纸质单子都行。我判断能不能做、大概怎么做。

聊聊你的需求