mirror of
https://github.com/carey314/mio-plugin-worker.git
synced 2026-08-10 07:04:32 +00:00
docs: add 2026-05-19 product + code review
- product-review.md: 5 个 tab 拆评,"摸鱼侠"名字 vs 功能 mismatch 与 P0 上架前必修项(下班瞬间无事件 / 喝水无提醒 / 版本号硬编码) - code-review.md: 13 文件审视,标出 PomodoroView paused-in-break fraction 算错、notifAuthorized 未 wire 致状态 dot 永 dim 等 P0 项; 亮点是 wallclock-based pomodoro + input-idle sit accumulator Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0cff83fc38
commit
8022abca76
294
docs/reviews/2026-05-19-code-review.md
Normal file
294
docs/reviews/2026-05-19-code-review.md
Normal file
@ -0,0 +1,294 @@
|
||||
# 摸鱼侠 — 代码质量 Review
|
||||
|
||||
**日期**: 2026-05-19
|
||||
**审视分支**: main @ `0cff83fc` ("feat: v0.2.0 — sleep-aware timers + auto-loop pomodoro")
|
||||
**源码规模**: 13 Swift 文件,~1500 行;WorkerStore 700 行最大
|
||||
**测试**: 0(无 XCTest target,无测试文件)
|
||||
|
||||
---
|
||||
|
||||
## 一、架构 — 8/10
|
||||
|
||||
```
|
||||
engine/
|
||||
WorkerStore.swift 700-行单体 state + tick + 5 feature 逻辑
|
||||
NotificationCenter.swift UN wrapper + dock fallback
|
||||
SystemIdle.swift CGEvent input-idle 探测
|
||||
WorkerDebugLog.swift /tmp/worker-plugin.log
|
||||
ui/
|
||||
ExpandedView.swift 5-tab shell
|
||||
PomodoroView/SitView/WaterView/ClockoutView/WeekendView.swift
|
||||
Theme.swift 色彩 token + WorkerFormat helpers
|
||||
WorkerPlugin.swift 主类
|
||||
```
|
||||
|
||||
比看盘侠少一层 `data/` — 因为 100% 本地无网络,不需要 actor client。
|
||||
|
||||
### 问题 1:WorkerStore 是 god object
|
||||
|
||||
700 行包含 pomodoro state machine + sit accumulator + water counter + clockout time + weekend countdown + 全部持久化。每 feature 逻辑都在 store 里。当前 OK 因为单体跑得通,长期 `tickFire()` 会膨胀。
|
||||
|
||||
**重构方向 P2**(不阻塞 ship):
|
||||
```
|
||||
PomodoroStore : ObservableObject
|
||||
SitStore : ObservableObject
|
||||
WaterStore : ObservableObject
|
||||
ClockoutStore : ObservableObject
|
||||
WeekendCalculator
|
||||
WorkerStore 只负责 orchestrate 1Hz tick 分发 + persistence routing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、状态管理 — 9/10(最大亮点)
|
||||
|
||||
### 🌟 亮点 1:pomodoro 用 wallclock endsAt 而非 counter
|
||||
|
||||
`WorkerStore.swift:108 private var pomodoroPhaseEndsAt: Date?`。tick 每次算 `remaining = endsAt - now`。
|
||||
|
||||
**含义**:Mac 睡了 / quit / 关 panel / 重启 都不影响倒计时正确性(按现实流逝)。比天真版"每秒减 1"强多了。
|
||||
|
||||
```swift
|
||||
// WorkerStore.swift:203-207
|
||||
if let endsAt = pomodoroPhaseEndsAt,
|
||||
pomodoroPhase == .focus || pomodoroPhase == .rest {
|
||||
let remaining = max(0, Int(endsAt.timeIntervalSinceNow))
|
||||
pomodoroRemaining = remaining
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 🌟 亮点 2:paused 持久化
|
||||
|
||||
`WorkerStore.swift:584-624` — paused phase 在 launch 时检测到 `savedPhase == .paused` 跳过 endsAt 计算,直接 resume 到上次 pause 时的 remaining。**正确**。
|
||||
|
||||
### 🌟 亮点 3:sit accumulator 用 input-idle,不用 timestamp
|
||||
|
||||
`WorkerStore.swift:225-244` sit 增长依赖 `SystemIdle.seconds < 60`,> 5 min idle reset。配合 `didSleepWake` (wallclock gap > 30s) 检测 → 三层保护。
|
||||
|
||||
```swift
|
||||
// 三层 reset:
|
||||
if didSleepWake { ... } // 1. Mac 睡了
|
||||
else if idle >= sitBreakResetThresholdSec { ... } // 2. 离开 ≥ 5min
|
||||
else if idle < 60 { sitAccumActiveSec += 1 } // 3. 真在打字
|
||||
// idle 1-5 min:hold steady(开会/打电话不归零也不+)
|
||||
```
|
||||
|
||||
**比市面上"久坐提醒"app 都准**。
|
||||
|
||||
### 🔴 问题 1:PomodoroView 进度环 fraction 在 paused-in-break 算错
|
||||
|
||||
`PomodoroView.swift:118-128`:
|
||||
```swift
|
||||
private var progressFraction: CGFloat {
|
||||
let total: Int
|
||||
switch store.pomodoroPhase {
|
||||
case .focus: total = store.pomodoroFocusMin * 60
|
||||
case .rest: total = store.pomodoroBreakMin * 60
|
||||
case .paused, .idle: total = store.pomodoroFocusMin * 60 // ← BUG
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
paused 状态下 `total` 永远当 focus 时长。但用户可能在 **break 中按了暂停**(`pomodoroPause()` 在 focus/rest 都允许)。这时 `pausedRemaining` 是 break 的剩余,total 是 focusMin × 60 — fraction 算错(分母不对)。
|
||||
|
||||
**修法**:暴露 store.pausedPhase 给 view 或在 store 提供 `pomodoroPhaseTotalSec` computed:
|
||||
```swift
|
||||
var pomodoroPhaseTotalSec: Int {
|
||||
switch pomodoroPhase {
|
||||
case .focus: return pomodoroFocusMin * 60
|
||||
case .rest: return pomodoroBreakMin * 60 // 或长休
|
||||
case .paused: return pausedPhase == .rest ? pomodoroBreakMin * 60 : pomodoroFocusMin * 60
|
||||
case .idle: return pomodoroFocusMin * 60
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、并发 — 9/10
|
||||
|
||||
### 强项
|
||||
|
||||
- `@MainActor` 在 WorkerStore / WorkerNotificationCenter
|
||||
- `Timer.scheduledTimer` 在 main RunLoop,cb 内 `Task { @MainActor in }` 隔离正确
|
||||
- `@preconcurrency import UserNotifications` (`NotificationCenter.swift:13`) — 处理 UN API 非全 Sendable
|
||||
|
||||
### 🔴 问题 1:notifAuthorized 没 wire 到 store,状态 dot 永 dim
|
||||
|
||||
`NotificationCenter.swift:32-46` callback 内 `WorkerNotificationCenter.shared.isAuthorized = true` —— 但 `WorkerStore.shared.notificationsAuthorized` (`WorkerStore.swift:149`) **没人 wire**。`ExpandedView.swift:84` 读 `store.notificationsAuthorized` 永远是初始值 `false`。
|
||||
|
||||
**结果**:top bar 通知状态 dot 永远是 dim grey,即使用户实际授权了通知。
|
||||
|
||||
**修法**:`NotificationCenter.swift:34`
|
||||
```swift
|
||||
Task { @MainActor in
|
||||
WorkerNotificationCenter.shared.isAuthorized = true
|
||||
WorkerStore.shared.notificationsAuthorized = true // ← 加这行
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、通知 — 7/10
|
||||
|
||||
### 强项
|
||||
|
||||
- UN authorization 三态正确处理(authorized/denied/notDetermined)
|
||||
- 0.1s threshold trigger 处理 macOS 即时通知 flakiness 是 known workaround
|
||||
- 通知 denied → fallback `NSApp.requestUserAttention(.criticalRequest)` (dock bounce)
|
||||
|
||||
### 问题 1:notify() 即便授权也每次都 dock bounce
|
||||
|
||||
`NotificationCenter.swift:74` `NSApp.requestUserAttention(.criticalRequest)` 在 schedule 之后**无条件**调用,注释说"always bounce as backup"。但用户授权通知后 dock 还在 bounce 会 spammy。
|
||||
|
||||
**修法**:
|
||||
```swift
|
||||
if !isAuthorized {
|
||||
NSApp.requestUserAttention(.criticalRequest)
|
||||
}
|
||||
```
|
||||
|
||||
### 问题 2:没 snooze 机制
|
||||
|
||||
番茄结束通知 / 久坐警告 fire-and-forget。用户在开会,一次注意不到就丢。**建议**:5 min 后 retry 一次。
|
||||
|
||||
---
|
||||
|
||||
## 五、持久化 — 7/10
|
||||
|
||||
用 `UserDefaults(suiteName: "com.mioisland.plugin.worker")`。5 个 feature 数据全塞同一 suite。简单 key-value,OK。
|
||||
|
||||
### 问题 1:历史 dict 增长无 cap
|
||||
|
||||
`WorkerStore.swift:403-406` `pomodoroHistory` 每日 key 累积。一年 365 keys,五年 1825。**建议**:保留最近 90 天,rolloverIfNeeded 时 prune。
|
||||
|
||||
### 问题 2:lastSeenDay 不持久
|
||||
|
||||
`WorkerStore.swift:539 private var lastSeenDay: String = ""`. 重启后 init 时被设成 today (`loadPersisted` 末尾)。day rollover 检测靠这个。跨日运行(不重启)能 catch;新一天才启动则 `lastSeenDay = today` 直接,rolloverIfNeeded 永不 fire(OK,data 也确实新一天 0)。**逻辑 work 但脆**。
|
||||
|
||||
---
|
||||
|
||||
## 六、错误处理 — 6/10
|
||||
|
||||
### 问题 1:SystemIdle 失败返回 0 → silent 误 grow
|
||||
|
||||
`SystemIdle.swift:34`:
|
||||
```swift
|
||||
return minVal == .greatestFiniteMagnitude ? 0 : minVal
|
||||
```
|
||||
|
||||
若 `CGEventSource` 全部失败(理论上可能 sandbox 拒绝),返回 0 = "用户刚刚操作了" = sit counter 每秒 +1。**结果**:sit counter 误 grow,假警报触发。
|
||||
|
||||
**修法**:返回 `nil` 或 sentinel,store 里检测无效 idle 数据时 skip tick。
|
||||
|
||||
### 问题 2:UNUserNotificationCenter.add 错误只日志
|
||||
|
||||
`NotificationCenter.swift:67-69` — 错误只写 `WorkerDebugLog`,无 UI 信号。
|
||||
|
||||
---
|
||||
|
||||
## 七、UI — 7/10
|
||||
|
||||
### 强项
|
||||
|
||||
- 5 个 view 风格高度一致(statusBadge + hero + controls + divider + settings + tipText)
|
||||
- `WorkerTheme` 集中色彩 token,`WorkerFormat` 集中时间格式
|
||||
- 各 view emoji + 中文,跟产品 personality 匹配
|
||||
|
||||
### 🔴 问题 1:PomodoroView 进度环 paused-in-break 错
|
||||
|
||||
(见状态管理问题 1)
|
||||
|
||||
### 🔴 问题 2:ExpandedView 通知状态 dot 永 dim
|
||||
|
||||
(见并发问题 1)
|
||||
|
||||
### 问题 3:WaterView cup mask 不跟 trapezoid 收边
|
||||
|
||||
`WaterView.swift:99-108` 水位 mask 用 `Rectangle`,但 cupShape (`CupOutline`) 是 trapezoid (bottomInset 8%)。`Rectangle` mask 不跟着杯子底部收边收缩。
|
||||
|
||||
**视觉效果**:低水位时基本看不出来;满水位接近 outline 那里水位 fill 会越过杯壁 — 渲染上可见的 leak。
|
||||
|
||||
**修法**:mask 用 `CupOutline` 自己 clip(不是 Rectangle):
|
||||
```swift
|
||||
.mask(CupOutline().scale(y: fraction, anchor: .bottom))
|
||||
```
|
||||
|
||||
### 问题 4:没"今日总览"footer
|
||||
|
||||
5 个 tab 各管各的,没"今日 3 番茄 + 6 杯水 + 监控中" 一行 summary。footer 左侧 (`ExpandedView.swift:169-177 footerLeftText`) 每 tab 显示自己的,无跨 tab 整合。
|
||||
|
||||
---
|
||||
|
||||
## 八、安全 — 9/10
|
||||
|
||||
- 100% 本地,无网络
|
||||
- UserDefaults suite name 跟其它 plugin 隔离
|
||||
- `NSApp.requestUserAttention` 是 standard API
|
||||
- 无 entitlement 需求
|
||||
- 无用户输入直接 eval / shell
|
||||
|
||||
唯一关注:`SystemIdle` 用 `CGEventSource` 的 `.combinedSessionState` source 不需要 Accessibility 权限。Apple 列为 sandbox 友好 API。**OK**。
|
||||
|
||||
---
|
||||
|
||||
## 九、测试 — 0/10
|
||||
|
||||
**No tests at all**。
|
||||
|
||||
**v1 必须有的**:
|
||||
|
||||
| 测试场景 | 难度 |
|
||||
|---|---|
|
||||
| Pomodoro state machine:focus → rest → focus × 4 → long rest → focus(autoLoop)| 易(注入 mock Date)|
|
||||
| Pomodoro paused → resume 跨时间正确 | 易 |
|
||||
| Pomodoro phase end while away(`loadPersisted` 路径)| 中 |
|
||||
| Sit accumulator:idle <60 += 1; 60..300 hold; ≥300 reset | 易 |
|
||||
| Sit wakeup gap:tickGap > 30s reset | 易 |
|
||||
| Day rollover:跨 midnight 各 counter 归零 | 中 |
|
||||
| Weekend countdown 跨 Saturday 边界 | 易 |
|
||||
| ClockoutRemainingSec 跨 midnight roll | 易 |
|
||||
| WaterCupsToday 持久化 + restore | 易 |
|
||||
|
||||
---
|
||||
|
||||
## 十、Code smell 汇总
|
||||
|
||||
| 文件:行 | 问题 | 优先级 | 修复行数 |
|
||||
|---|---|---|---|
|
||||
| `PomodoroView.swift:118` | paused-in-break fraction 用 focusMin 当分母 | 🔴 P0 | 5 |
|
||||
| `ExpandedView.swift:84` + `NotificationCenter.swift:34` | notifAuthorized 未 wire 到 store,dot 永 dim | 🔴 P0 | 3 |
|
||||
| `NotificationCenter.swift:74` | 授权后还 dock bounce 体验吵 | 🟡 P1 | 3 |
|
||||
| `SystemIdle.swift:34` | 失败 fallback 0 → 误 grow sit counter | 🟡 P1 | 改 nil |
|
||||
| `WaterView.swift:99-108` | mask 不跟 trapezoid 收边 | 🟡 P1 | 改 mask |
|
||||
| `WorkerStore.swift:403` | pomodoroHistory / waterHistory 无 cleanup | 🟢 P2 | 90 天 prune |
|
||||
| `WorkerStore.swift` | 700 行 god object | 🟢 P2 | 拆 store |
|
||||
| 全 repo | 0 tests | 🟡 P1 | 加 XCTest |
|
||||
| `ExpandedView.swift:151` | footer 版本号硬编码"v0.1" | 🟢 P2 | 读 Plugin.version |
|
||||
|
||||
---
|
||||
|
||||
## 十一、总评
|
||||
|
||||
代码质量 **8/10** — 实际上比看盘侠更扎实。
|
||||
|
||||
### 亮点(值得保留 + 文档化)
|
||||
|
||||
1. **pomodoro wallclock-based timing** — Mac 睡/quit/手动改时钟都不影响
|
||||
2. **sit input-idle algorithm** — 比天真 startTimestamp 模型强一档
|
||||
3. **sleep-aware tick gap detection** — 三层保护
|
||||
4. **paused state 持久化** — 跨重启恢复正确
|
||||
5. **统一 view 结构** — 5 个 tab 风格高度一致
|
||||
|
||||
### 主要短板
|
||||
|
||||
1. **notifAuthorized 没 wire** — UI 上 notif 状态 dot 永远说谎
|
||||
2. **PomodoroView 进度环算错**(pause 在 break 中)
|
||||
3. **WorkerStore 700 行 god object** — 长期维护痛
|
||||
4. **0 tests** — 上架后崩了不知道哪坏
|
||||
|
||||
修完 P0 两项 + 加最小测试集(5 个 fixture),code quality 能上 9.5/10。
|
||||
|
||||
**整体判断**:v0.2.0 的代码扎实度足以支撑上架。两个 P0 bug(notif dot + paused fraction)一晚上能修完。比看盘侠 v0.3.0 的 Toast bug 更轻 — 后者是 typo 级 ship-blocker。
|
||||
161
docs/reviews/2026-05-19-product-review.md
Normal file
161
docs/reviews/2026-05-19-product-review.md
Normal file
@ -0,0 +1,161 @@
|
||||
# 摸鱼侠 — 产品 Review
|
||||
|
||||
**日期**: 2026-05-19
|
||||
**审视版本**: v0.2.0 main HEAD `0cff83fc`("sleep-aware timers + auto-loop pomodoro")
|
||||
**Tabs**: 番茄 / 久坐 / 喝水 / 下班 / 周末
|
||||
**面板**: 380×620
|
||||
|
||||
---
|
||||
|
||||
## 一、核心价值主张
|
||||
|
||||
| 维度 | 评分 | 说明 |
|
||||
|---|---|---|
|
||||
| 差异化 | 9/10 | 聚合 5 个上班场景,刘海常驻,"周末倒数 + 打工语录" 玩具感 |
|
||||
| 价值密度 | 8/10 | 单一 plugin 覆盖 4 个独立细分(番茄 / 久坐 / 喝水 / 倒计时)|
|
||||
| 上手摩擦 | 9/10 | 0 登录 0 网络 0 配置就能用 |
|
||||
|
||||
### 强项
|
||||
|
||||
1. **聚合"上班一天会做的微动作"** — 番茄钟 + 久坐 + 喝水 + 下班倒计时已经覆盖职场版"健康助手"大头。市面上要么单功能 menubar (Be Focused / Drink Water Reminder),要么大 app (Forest / Notion)。集中在刘海是新形态。
|
||||
2. **"周末倒数 + 打工语录"是 personality 担当** — 这一 tab 跟其它四个分开看是娱乐性的,但它是产品个性的来源。没这个 tab 就是健康提醒 app,有了它就是"打工人玩具"。
|
||||
3. **100% 本地** — 没网络、没账号、没 telemetry。比 Forest 联网+social pressure 更适合"我自己用"的人。
|
||||
|
||||
### 弱项
|
||||
|
||||
1. **"摸鱼侠"名字 vs 5 个 tab 全在认真工作 = 反差**
|
||||
名字暗示"摸鱼",实际:番茄(专注)、久坐(健康)、喝水(健康)、下班(期待)、周末(期待)。**整个工具是"老老实实打工 toolkit"**。要么改名("打工侠"更准),要么真加一个摸鱼 tab(藏老板模式 / 桌面伪装 / 假装写代码的 dummy editor)。
|
||||
2. **5 tab 在 380pt 太挤** — `ExpandedView.swift:97` spacing 6 + 5 个 emoji tab,单 tab ~64pt。看是看见,但触摸/点击 hit zone 比看盘侠 3-tab 明显窄。
|
||||
3. **README 与 footer/Info.plist 版本号不一致** — `ExpandedView.swift:151` footer 硬编码 "v0.1 · 本地运行",但 Info.plist + WorkerPlugin.swift 是 v0.2.0。每次发版都得手改文案。
|
||||
|
||||
---
|
||||
|
||||
## 二、UX 流程完整度(按 tab 拆)
|
||||
|
||||
### 番茄 tab — 9/10 (最完善)
|
||||
|
||||
✅ 25/5 + 长休息 cycle 完整
|
||||
✅ pause/resume 跨重启正确(`WorkerStore.swift:594-600` paused 持久化)
|
||||
✅ **sleep-aware**:`WorkerStore.swift:198-200` 用 tick 间隔检测 wallclock gap,Mac 睡眠后倒计时不错乱
|
||||
✅ 4-tomato dot 周期可视化(`PomodoroView.swift:64`)
|
||||
✅ autoLoop toggle — 经典 Pomodoro 节奏,一启动跑一上午
|
||||
|
||||
**短板**:
|
||||
- ⚠️ stepperPill 单位 +5/+1/+5 min,专注分钟想要 +1 step(27 分钟用例)
|
||||
- ⚠️ 没"中断当前番茄"button — 重置可以替代但语义不同
|
||||
- ⚠️ "专注时长 = todayCount × focusMin" 算法 simplistic — partial focus 丢失。无所谓,不算精确没事
|
||||
|
||||
### 久坐 tab — 9/10 (算法亮点)
|
||||
|
||||
✅ **input-idle 算法是核心亮点** — `SystemIdle.swift` 用 `CGEventSource.secondsSinceLastEventType` 判定真在打字 vs 离开。避免天真版"start timestamp - now"把午休/会议都算成坐着的 trap。
|
||||
✅ 5min idle 阈值 reset;wallclock gap >30s 也 reset(sleep/wake)— 双保险
|
||||
✅ threshold 之后 dock bounce + 红色 status pill + 通知(30s 持久化 cadence,崩溃最多丢 30s data)
|
||||
✅ tip 文案诚实:"只统计你真正在键盘前的时间。离开 5 分钟以上自动归零,午休/会议不会被算进去。"
|
||||
|
||||
**短板**:
|
||||
- ⚠️ 没"今日累计坐了 X 小时"统计 — 只显示当前 streak,无历史 retention。"健康报告"路线钩子缺
|
||||
- ⚠️ trigger 配置 +5 min step、下限 5 min — 想要 60min trigger 要点 3 次。可以加 +15 min step
|
||||
|
||||
### 喝水 tab — 7/10 (placeholder 级)
|
||||
|
||||
✅ Hero 大杯子 + 水位渐变填充动画细心(`WaterView.swift` CupOutline + LinearGradient mask)
|
||||
✅ 进度 dot 可视化今日 x/N 杯
|
||||
✅ 加 / 撤销 manual log,撤销 -∞ 守住(`WorkerStore.swift:455`)
|
||||
|
||||
**短板**:
|
||||
- 🔴 **没提醒** — 跟番茄/久坐对比最大短板。设了"目标 8 杯"但不提醒。一整天忘了喝就没了。**P0**:加 X 小时一杯水的 cadence + 通知(autoLoop pomodoro-style)
|
||||
- ⚠️ 历史不可见 — `waterHistory` dict 里其实存了过往天数据,没 UI
|
||||
- ⚠️ 没编辑"今早 8 点喝了 1 杯但忘了点"功能 — 只能 + / 撤销 当前
|
||||
|
||||
### 下班 tab — 7/10 (倒计时到 0 没事件)
|
||||
|
||||
✅ "X 时 Y 分 Z 秒" + 渐变进度条(红→深红)— 视觉感染力强
|
||||
✅ 9 小时 anchor 算 progress fraction —合理
|
||||
✅ 时间 hh:mm 可调(5 min step)
|
||||
|
||||
**短板**:
|
||||
- 🔴 **"下班到了"那一刻没事件** — `clockoutRemainingSec` 到 0 后 roll 到明天的同一时间(`WorkerStore.swift:491-495 if target <= now { target += 1 day }`)。所以下班到了下一秒变成"距明天下班 23:59:59"。**漏的体验**:应该有"已下班!" celebration overlay 或 panel 颜色变。**P0**
|
||||
- ⚠️ tipText 说"下班时间到不会响铃" — 这是文案 disclaimer 但产品上反而是缺陷。**应该响**
|
||||
- ⚠️ 没"周末忽略" — 周六也倒计时下班,应该周末显示"今天不上班"
|
||||
- ⚠️ 默认 18:00;9-9-6 / 灵活 OT 党需改。OK 可调,但没"工时模式"预设
|
||||
|
||||
### 周末 tab — 8/10 (personality 担当)
|
||||
|
||||
✅ "天/时/分 + 下个周六 00:00" + 旋转打工语录 — 形态独特
|
||||
✅ 周末打开倒计时 +7 天 — 决策合理(`WorkerStore.swift:518-519`)
|
||||
✅ day-of-year mod 7 的 quote rotation 一天一条稳定
|
||||
|
||||
**短板**:
|
||||
- ⚠️ **打工语录池只有 7 条** — 52 周看 52 次每条。重复感会重。**P1 扩到 30+**
|
||||
- ⚠️ **没"假期校准"** — 国庆/春节怎么办?周末倒计时还在算下周六。逻辑对但用户期待"放长假倒计时"。**P1 接入国务院 ICS feed**
|
||||
|
||||
---
|
||||
|
||||
## 三、信息架构
|
||||
|
||||
| 强项 | 弱项 |
|
||||
|---|---|
|
||||
| 每个 tab 一致 layout:statusBadge + hero + controls + divider + settings + tipText | 没"全局 dashboard" — 5 tab 间不互通 |
|
||||
| LiveDot 在 footer 闪 — plugin 在 active 计时 | 没"今日 X 番茄 + Y 杯水 + Z 分钟久坐" 一行 summary |
|
||||
| notif status dot 在 top bar — 通知未授权时的隐性提醒 | tab 之间数据点孤岛 |
|
||||
|
||||
---
|
||||
|
||||
## 四、与竞品对比
|
||||
|
||||
| | 摸鱼侠 | Be Focused | Drink Water | Forest |
|
||||
|---|---|---|---|---|
|
||||
| 桌面常驻 | ✅ 刘海 | ✅ menubar | ✅ menubar | ❌ App |
|
||||
| 番茄钟 | ✅ + auto-loop | ✅ | ❌ | ✅ |
|
||||
| 久坐提醒 | ✅ input-idle | ❌ | ❌ | ❌ |
|
||||
| 喝水追踪 | ✅(无提醒)| ❌ | ✅ | ❌ |
|
||||
| 下班倒计时 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 周末倒数 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 通知 | UN + dock | ✅ | ✅ | ✅ |
|
||||
| 价格 | 免费 | $1.99 | 免费 | $1.99 |
|
||||
| Social pressure | ❌ | ❌ | ❌ | ✅(种树)|
|
||||
| 中文体验 | ✅✅ | ❌ | ❌ | ⚠️ |
|
||||
|
||||
**差异化**:聚合 + 桌面常驻 + 中文文案 + 周末打工语录。"打工人玩具"比 Western productivity tool 中文用户更亲切。
|
||||
|
||||
---
|
||||
|
||||
## 五、上架前必修(P0)
|
||||
|
||||
| # | 项 | 文件 / 位置 |
|
||||
|---|---|---|
|
||||
| 1 | "下班到了"瞬间要有通知 + panel celebration overlay | `WorkerStore.swift:491-495` clockoutRemainingSec roll 逻辑 |
|
||||
| 2 | 喝水加可选定时提醒(每 X 小时) | 新增 water reminder timer |
|
||||
| 3 | footer 版本号去硬编码 | `ExpandedView.swift:151` 改读 `WorkerPlugin.version` |
|
||||
| 4 | tipText "不会响铃" 删掉或改写 — 跟未来要加的通知冲突 | `ClockoutView.swift:239` |
|
||||
|
||||
---
|
||||
|
||||
## 六、建议加(P1)
|
||||
|
||||
1. **真的"摸鱼" tab** — 跟名字呼应:藏老板模式 / 桌面伪装 / dummy editor。是 product personality 的兑现
|
||||
2. **打工语录池扩到 30+** — 现在 7 条一周一轮太短,季节性条目(春节前 / 国庆前 / 周一专属 / 周五专属)
|
||||
3. **假期/节假日校准** — 国务院假期 ICS pull 一次,下班 + 周末倒计时知道明天是法定休
|
||||
4. **今日历史 chart** — 7 天番茄 / 喝水 / 久坐 trend,鼓励 streak
|
||||
5. **快捷键** — `cmd-shift-1/2/3/4/5` 切 tab,`space` 番茄 start/pause
|
||||
6. **WaterView 历史可见** — `waterHistory` 数据已经在存,画个 7 天 bar chart
|
||||
|
||||
---
|
||||
|
||||
## 七、建议砍(P-1)
|
||||
|
||||
- README 提到 macOS 15.0+ / MioIsland v2.2.0+ 是 OK,但应在产品文案显式说"需要 Apple Silicon"(bundle arm64-only)— 这是 ecosystem 级问题,跟主程序 + 其它插件统一改
|
||||
|
||||
---
|
||||
|
||||
## 八、总评
|
||||
|
||||
| 问 | 答 |
|
||||
|---|---|
|
||||
| v0.2.0 上架免费版? | **是** |
|
||||
| 功能完成度? | 番茄 9/10 + 久坐 9/10 是亮点;喝水 7/10 + 下班 7/10 是 placeholder 级;周末 8/10 是 personality 担当 |
|
||||
| 上架前必修项数? | 4 个(见上)|
|
||||
| 最大产品风险 | "摸鱼侠"名字 vs 实际功能反差。要么改名要么真做摸鱼 tab |
|
||||
| 最大产品亮点 | input-idle 久坐算法 + sleep-aware 番茄 wallclock — 工程支撑产品体验,比同类强一档 |
|
||||
|
||||
**优先级 sequence**:修 P0 四项 → 上架免费 → 做"真摸鱼 tab"兑现名字 → 加节假日校准 + 历史 chart 增 retention。
|
||||
Loading…
Reference in New Issue
Block a user