# 更新记录 格式随版本号走;`package.json` 的 version 是唯一真相,tag 用 `v<版本>`。 ## v0.8.1 番茄钟走完的自动提醒会把整个会话打成跑不起来 —— 修掉那条写入。 - **会话消息的 source kind 带上生产者前缀**:番茄钟走完发回起钟会话的那条提醒,`source` 以前写成 `{ kind: 'plugin', plugin: 'dsh-study-desk', … }`。宿主会话格式 v4 把这种「kind 一律 `plugin`、 只靠 `plugin` 字段认来源」的写法判为退役语法,落盘时整条拒绝,报 `format v4 message requires a producer-owned source kind` —— 表现就是会话在番茄钟停止的那一刻 忽然**任何回复都发不出去**,历史还在、但每一轮都撞同一堵墙。现在 kind 写成 `plugin:dsh-study-desk`(宿主只要求「非空且不是裸 `plugin`」,前缀正是来源标记, `clearai` / `dsh-mneme` 等插件在现有会话里早已这么写),由 `MESSAGE_SOURCE_KIND` 与 `timerEndSource()` 统一产出,不再手写。 - **回归测试 + 静态扫**:新增 `test/message-source.test.js`(4 条)。除了断言工厂产出的 source 能过宿主同款准入,还静态扫源码禁止裸 `kind: 'plugin'` 回潮;`apply-probe.mjs` 里那条端到端断言 同步改成新 kind。单测 73 → 78。 ## v0.8.0 AI 把督促链路接上了:AI 起番茄钟必须先落卡、走完自动提醒开钟的那个会话;新增每日计划表,模型从系统提示里就知道「现在该做什么」。看板补上过卡模式、卡片截止日期与一批顺手的升级。 - **AI 起番茄钟走门禁**:工具 `focus` 起钟必须 `task=` 一张**「进行中」**的卡,不挂卡 / 卡不存在 / 卡不在 doing 都直接拒绝,报错文案教完整流程(`add` 建卡 → `move` 到 `doing` → `focus` 挂卡)。 目的是逼 AI 把它安排的任务真的落到待办墙上,周报和近 30 天统计才对得上。只限工具路径—— 客户端手动起钟不受限。 - **番茄钟走完,提醒发回起钟的会话**:AI 起钟时把会话 id 存进计时器(`by:'ai'` + `sessionId`), 专注走完自动摆的休息继承这条链。结账时用宿主的 `agents.get(sessionId).followup(消息)` 唤醒那个会话(官方 goal-round-driver 同款姿势):空闲就开新一轮,跑着就排下一轮。 消息正文自带「这是系统自动消息,不是用户发言」;手动停钟永不提醒;会话已关闭就静默放弃。 面板没开没人轮询时由宿主 10 秒看门狗兜底结账。设置页开关 `notifySessionOnTimerEnd`(默认开)。 - **每日计划表**:`HH:MM~HH:MM 事情 【科目】` 一行一条的每日固定模板,校验只在 desk.js 一处 (坏行忽略、起终写反丢弃、按开始时刻排序)。开了之后:主面板新分区(当前时段高亮 + 剩余分钟、 已过变暗)、系统提示注入「现在按计划该做什么 / 下一场」、还能像迷你条一样悬浮在桌面上—— 第二个 `shell.overlay` 悬浮件,坑位规矩照抄迷你条(portal / no-drag / z-index:900 / 钳在标题栏拖拽带之下),默认位叠在迷你条上方,落点单独存。「启用」与「悬浮」各自独立开关。 - **过卡模式**:复习队列可切全屏单卡——空格翻备注、1/2/3 打分、→ 跳过;行级「缓一天」走 `review.schedule`,难度与遍数保留;队列上按 1/2/3 直接评队首。 - **卡片截止日期**:新建框 `@YYYY-MM-DD` 语法、卡面 ⏰ 徽章、编辑器日期框;工具加 `dueDate` 参数、 系统提示带【截止】;Markdown 行尾 `ddl` 字段往返,注释里删掉即清空。 - **热力图点格子看当天明细**:展开那一格的原始专注 session 列表(聚合仍只在 desk.js)。 - **复制周报**:新增 `weekly.text` op + 面板按钮,与工具 `report` 同一份 `weeklyText`。 - **复盘 2 秒自动落盘**:停手 2 秒防抖保存,换面板卸载前补存。 - **设置页科目编辑器**:增删科目即时生效;最后一个不许删(宿主对空 subjects 不动旧值, 删光了她以为删掉了、desk.json 里还在)。 - **快捷键**:`/` 聚焦搜索、`n` 新建卡片。 - **视觉一批**:投入类图表统一绿族(暗色下品牌色解析成近白是病根)、列头状态点、计时卡运行态描边、 输入焦点环、打分键语义 hover;`style.test` 增补颜色守卫。 - **图标**:品牌番茄进 `package.json` 的 icon(插件管理页 + 设置侧栏),侧栏两枚重画 (工作台 = 任务卡 + 计时徽章、单词打卡 = 词书打勾)。 - **诊断分档**:悬浮件命中测试带 tag——盖的人住在宿主模态层(z≥1000)记 `*-under-modal` (设计内让位,不算事故),普通内容覆盖才是 `*-covered`。 ## v0.7.0 修了三个「数字看着正常、其实是错的」,界面按番茄ToDo 的调性重排了一遍,番茄钟可以自由设时长。 - **迷你条不再被宿主标题栏吞掉输入**:Windows 宿主在顶部画了一条满宽 40px 的 `-webkit-app-region:drag` 带,浮层没声明 `no-drag` 就进不了「把输入还给 DOM」的名单 —— 而 app-region 让洞不看层叠顺序,`z-index:900` 也救不了。症状是拖不动、展开和暂停都点不到, 偏偏唯一逃出来的两个手势(拖走、双击复位)本身也要点击,落点又存在 localStorage, 所以重载也还是坏的。现在两个浮层都声明 `no-drag`,并把落点钳制在那条带之下 (只在 `data-windows-titlebar` 且非全屏时留,高度读宿主的 CSS 变量),历史坏落点加载时自动顶回来。 - **不背单词的今日 / 累计时长取两边较新的**:本地 prefs 躺在 Androws 虚拟磁盘里、攒一会儿才刷盘, 实测滞后过几天(10-20 那次抽出来的其实就是 10-14 的快照),而原先的取舍是「有本地就用本地」, 落后的本地数每次都赢。现在累计(只增不减)取较大即取较新;**「今日」每天归零、不能取大** (过零点时本地和云端都还挂着前一天的数),有云端那一行就用云端,拿不到才退回本地。 分与秒改为同源 —— `index.js` 会拼「X 分钟(Y 秒)」注入 system prompt,混用会自相矛盾。 新增 `durationAsOf` / `durationMightBeStale`:只在「本地落后且云端没兜住」时, 面板标红提示「本地只刷到某一天」,system prompt 里也注明。取新发生在 `bbdcView()`, 所以旧缓存文件不用重采也能被裁成对的数。 - **番茄钟可以自由设定 1–240 分钟**:25 / 45 / 60 三个预设之外加了「自定义」数字框 (复用设置页的 `NumField`,失焦或回车才写回)。它只改这一段多长,起钟仍交给那颗 ▶。 闲置时字面直接显示这一段多长(原来显示 `00:00`)—— 自由值否则没有任何反馈途径。 设置页「专注时长」的上限同步从 180 提到 240,免得两个入口各定一个上限。 - **界面重排**:原来十几个散档字号(含 `9.5 / 10.5 / 11.5 / 12.5` 四个半像素,会有亚像素模糊) 收进一条八档阶梯 `--sd-fs-*`;标题从衬线(`ui-serif,Georgia,宋体`)换成宿主默认 sans, 大数字走品牌字体、倒计时仍用等宽;卡片圆角 14→16、加一层极轻阴影换掉「全靠边框撑层次」, 分区标题和看板列头不再画分隔线;间距统一到 4 的倍数。 - **图表分色**:饼图原来五个 tone 走语义色,展开成实际值有三个其实是灰的 (`brand-primary` 在默认主题里是 `neutral-bluish-1000` 近黑、`state-idle` 是灰), 环看着就是一团糊;现在换成八色 `--dsw-static-*` 分类色板 + `color-mix` 补齐, 扇区之间留一条与卡底同色的 1.5px 缺口。热力图 / 周报 / 今日进度条走绿色阶(已投入的分钟数), 「未来 7 天到期」走琥珀、今天那一根转红(欠着的债)。计时器那条进度条语义不同,保持品牌色。 **注意**:图表分类色不随明暗主题翻转,这是刻意取舍,见 README 的「已知限制」。 - **新增六条视觉层守卫**(`test/style.test.js`):CSS 里 hex 必须为 0、不许有零点几像素字号、 标题不许用衬线、饼图 8 色不许退回语义色、字阶必须挂在各根上(迷你条是 portal 到 body 的)、 图表配色归属。加载 bundle 用的桩抽到 `tools/client-loader.mjs`,两个测试文件共用一份。 - 工程:`files` 白名单点名 `tools/client-loader.mjs` —— 发布的包里带 `test/`,而测试现在 import 那个桩, 不带进包里跑不起来。**只点这一个文件**,不写 `tools/`:那个目录下还有写死本机路径的临时出图脚本, 跟着进包既漏信息又是噪音。 - **换行锁成 LF**:新增 `.gitattributes`(`* text=auto eol=lf`)。此前仓库没有这个文件,而 本机全局 `core.autocrlf=true`,导致磁盘上是 CRLF、git blob 里是 LF,于是 `npm pack` 在 本机与 CI 两条路径上产出字节不同的包(同 tag 下 client.js 149009 vs 146342 字节, unpacked 329.2 kB vs 322.9 kB)。现在两边一致。 - **CI 补一道换行守卫**:可移植性 job 新增「工作区换行必须是 LF」,用 `git ls-files --eol` 找 `w/crlf` / `w/mixed`。只看 git 认定的文本文件,png 这类二进制不会误报。 - 文档同步:`AGENTS.md` 的「发版」一节改写为「优先走 `release.yml`」,补上 Trusted Publisher 已于 2026-10-18 登记、认证实测可用,以及「未登记时的 404 不是版本冲突」这条区分特征; 新增「换行必须锁死」小节。`README` 的发布说明同步。 - 文档同步(本轮):`AGENTS.md` 的规则 1 加「图表分类色别拿语义色凑」并写明「图表色不随主题翻」 是刻意取舍;规则 9 加「时长两边取新、但『今日』归零不能取大」和 `dayKey` / `toISOString()` 的坑; 坑 3 从两条扩成三条,新增 Windows 顶部那条拖拽带会吞鼠标事件、且普通浏览器复现不了; 生效段补「整进程重启不需要 `--bump`」。测试条数、文件地图、`README` 的表格与已知限制同步。 ## v0.6.1 迷你条改成「拖到哪就停在哪」,并把两个会悄悄吃掉数据的坑补上。 - **迷你条不再吸附**:删掉 `snapMiniPos` 与 `snapping` 过渡,松手只做视口钳制(`clampMiniPos` 保留, 防拖出屏幕丢钟),落点照常写 `localStorage`。以前横坐标必然被拍成左右边距,中间值不可能存在。 同时补了 `onPointerCancel` / `onLostPointerCapture` 与 `event.buttons===0` 兜底,指针被弹窗抢走时 不会留下脏拖拽态。`README` 的说明同步改写。 - **「结束这一段」改成记账**:以前发的是 `timer.reset`,专注 20 分钟点结束记 0 分钟; 现在发 `timer.complete`,按真实跑过的时长落 `session`(「放弃」仍走 reset,不记账)。 - **复习开关不再清零历史**:`unscheduleTask` / `scheduleTask` 只改 `due`,难度、遍数、遗忘次数保留, 删卡重建才有清零风险这件事不用再靠用户自己躲。 - **卡片可内联编辑**:标题 / 科目 / 预计分钟 / 备注,走已有的 `task.update`(宿主白名单本来就有), 不用再删卡重建。 - **迷你条一键起钟挂到进行中的卡**:以前不带名字,全部落进「专注(未命名)」;现在优先挂 `doing` 里的卡, 没有就沿用上一段专注的名字。 - 前端细节:触屏下卡片操作按钮不再靠 hover 才现身(`@media (hover:none)`)、补 `prefers-reduced-motion` 与 `:focus-visible`、计时数字改 `clamp()` 适配窄面板。 - 单测 +5(迷你条停靠 3 条、计时记账 1 条、复习开关 1 条),共 48 个;CI 的 `total files` 断言改成 按 `package.json` 的 `files` 白名单逐个核对,不再因为多一个源文件就红。 ## v0.6.0 番茄钟从「只知道今天学了几分钟」变成「知道时间花去哪了」:**起钟要报名字,名字就是饼图的图例**。 - **番茄分布饼图**:`focusByName(state, days, now)` 在 `desk.js` 一处按 `session.title` 聚合专注时长 (返回 `{label, minutes, segments}` 降序数组,只算 `kind==='focus'` 且落在窗口内的), 快照多带 `focusPie: { today, month }`;客户端只画不算。面板上可切「今天 / 近 30 天」, 超过 8 项折成「其它 N 项」。取色只用 5 个主题 token 轮换,零硬编码颜色。 - **计时必须命名(番茄ToDo 式)**:`makeTimer` 的标题优先级是 **手写名 > 挂的卡片标题 > 兜底 `专注(未命名)`**(≤24 字,休息不兜底所以为空)。 命名发生在**起钟这一刻**——`session.title` 就是这个番茄的户口,饼图按它聚合。 - 整页工作台的计时卡顶部加了名字输入框(Enter 直接起钟),没填名字时播放按钮和时长档都点不动; 科目 pill 一点即填。迷你条保持一键快起,不强制命名,落进「专注(未命名)」,账不丢。 - `study_desk` 工具的 `focus` 支持 `title` 给这段命名,工具描述同步。 - 单测 +3(`focusByName` 聚合与窗口、未命名归类、命名优先级与截断),共 43 个。 ## v0.5.0 新增第二个面板**「单词打卡」**:把不背单词的打卡记录接进来,和工作台并列在侧栏。 - **数据从本机 App 里读,不登录网页端。** 不背单词的 Windows 版其实是跑在腾讯 Androws 安卓容器里的 App,它的数据库被自己的 native 库(爱加密)加密,`SQLite format 3` 都搜不到; 但 `shared_prefs` 是明文 XML —— 打卡日历、累计时长、连续签到、正在学的词书都在里面。 `bbdc.js` 复制一份运行中的虚拟磁盘(**共享读,App 开着也能拷**),用 Androws 自带的 `7z.exe` 把 `shared_prefs/*` 抽出来解析。 - **「累计学习 293 词」得绕一层。** 服务端的概览接口只回 `total_duration_num`, 压根没有 `total_learn_num` 这个字段(H5 的 JS 读它,读到的是 `undefined`——这是官方页面的缺陷)。 真数字要用 `type=learnByMonth` 拉 48 个月的 `learnNum` 再相加。 - **凭据是 `sid`,从 App 的 WebView 缓存里挖。** App 用 WebView 打开 H5 页面时会带上它。 这个 `sid` 在缓存里**已经是百分号编码过的**:必须原样拼进 query。 用 `URLSearchParams.set` 或 `encodeURIComponent` 会二次编码 `%`→`%25`,服务端不报错, 但返回的是**别人的**游客数据(`total_duration_num=175108`)——表现是「数字全对不上」而不是「报错」, 很容易误判成「这接口给的就是假数据」。 - **凭据不出门。** 缓存落盘、面板渲染、日志里 `sid` 一律只留前 10 位;`bbdcView()` 只放行面板要看的字段。 - **只读。** 不回写 App 任何文件,也不改 App 的数据库。 - 新增 10 分钟缓存 + 并发去重(`readBbdc()`),面板右上角有手动「刷新」。 - 打卡日历是**逐月**翻的:数据源按 `minMonth..maxMonth` 一次一个月给, 面板上的 `‹ 2026-10 ›` 走本地已拉到的全量日期数组,翻月不再打网络。 - 单测 +11(`test/bbdc.test.js`:prefs 解析、按月求和、`sid` 编码变体、月历网格),共 40 个。 ## v0.4.0 把「复习」从一条排队列表变成**看得见的规划**:曲线、预报、今日建议三块都算在 `desk.js` 一处, 客户端仍然只画不算。全部新增字段随快照发出,老 `desk.json` 不用迁移。 - **单卡记忆曲线**:`memoryCurve(review, now)` 按 `R(t) = e^(-t/S)` 画一张卡的衰减, `S` 由排期反推 —— 让曲线在 `due` 那天**正好落到 90%**,于是「掉到 90%」的位置就是你排的那天, 可视化与排期自洽,不是另算一套。横轴**固定 30 天**(逾期太久会自动放宽到 90 天封顶), 不按各自间隔缩放:缩放会让每张卡的曲线长得一样,等于没画;固定横轴之后 「稳的卡是近乎水平的线、刚忘过的卡肉眼可见往下掉」,一眼能挑出脆的那张。 没排过期的卡按「一天一过」画,忘过的卡(`lapses > 0`)稳定度再砍一半。 - **未来 7 天到期预报**:`reviewForecast(state, days, now)` 按天分桶,**逾期一律并进今天** (它们本来就要今天处理),柱状图回答的是「接下来每天要过几张」,用来把量摊平。 - **今日建议**:`reviewPlan(state, now, limit)` 在到期队列之上再排一次顺序, 打分只有四个可解释的来源:逾期天数、忘过次数与难度(`lapses` 多 / `ease` 低)、新卡、置顶卡; 每张都写明理由,**再做一次科目轮换**,避免连着三张都是同一科。到期量超过一轮的 1.5 倍时直接劝你 「别硬扛,剩下的顺延到明天」——曲线不会因为晚一天就塌掉。 - 复习队列每一行都带一条迷你曲线与「记忆 N%」,队列上下各挂今日建议与 7 天预报。 - 单测 +6(曲线不变量、逾期放宽、预报分桶、科目轮换、建议分档、理由优先级), `apply-probe.mjs` 增加一条快照契约断言:曲线点必须是 0..100 的整数、`dueIndex` 与今天重合。 ## v0.3.0 改名并把「考研」从代码里剥出来:显示名从「考研工作台」改成**学习工作台**, 倒计时的目标与节点改成用户在设置页自己填,非考研人群(答辩、投稿、考证、语言学习)开箱可用。 - 新增设置项 `goalLabel / goalDate / goalEnd / goalNote / milestonesText`。 节点是纯文本,一行一个 `名称 2026-10-15~2026-10-24`,日期拿不准就在行尾加「约」; 坏行忽略、不报错,**目标日期留空就不显示倒计时那一块**。 - 节点的语气(未来 / 进行中 / 已过)与人话文案统一收进 `desk.js` 的 `goalInfo()`, 客户端只渲染不算 —— 之前浏览器里有一套重复的 chip 文案逻辑。 - 默认值仍是一份 2027 届考研初试的示例(含预报名 / 正式报名 / 网上确认 / 打印准考证), 老 `desk.json` 读入时自动补齐,不用迁移。 - 导出 `desk.md` 的一级标题与前言随之改为「学习工作台」,并写入目标日期与剩余天数。 - `study_desk` 工具描述与每轮对话摘要不再假定用户是考研的。 ## v0.2.0 面向备考与学术人群补齐了「复习」这条主线,并把数据接进了 Markdown 生态。 - **间隔重复复习队列**:卡片带 `review{due, intervalDays, ease, reps, lapses}`, 用「忘了 / 记得 / 很简单」三档打分推进排期(简化版 SM-2)。已完成的卡照样会到期。 工作台顶部有今日队列,卡片右下角 ↻ 拉进或移出,设置页可调每次摆几张。 - **标签与过滤**:卡片带 `tags[]`,新建时写 `#名词解释` 就地打标;看板顶部可搜、 按科目、按标签、只看到期,列头显示「符合/总数」。 - **周报**:按周(周一起)统计时长、活跃天数、复习次数与环比,配本周科目分布。 - **每日复盘**:`journal{}` 按天存一条短记录,会随摘要进系统提示。 - **Markdown 导出 / 读回**:生成 Obsidian 友好的 `desk.md`(科目分节 + 复选框 + 行尾 HTML 注释存机器字段),读回按 id **合并**而不是覆盖。 - **`study_desk` 工具**从 7 个 action 扩到 10 个(新增 `review` / `journal` / `report`), `add`/`update` 认 `tags` 与 `review`。 - **修**:工作台面板自己出滚动容器(宿主中心列是 `overflow:hidden` 的 flex 列, 原先下半截永远看不见);常驻迷你条 portal 到 `document.body`(宿主 `shell.overlay` 是 `z-index:20` 的独立层叠上下文,原先被页面级浮层盖住导致点不动、拖不动)。 - 老 `desk.json` 无需迁移,读入时自动补齐新字段。 - CI:GitHub Actions 跑单测与离线冒烟(Node 22 / 24),外加一条 「`cordis.patch.yml` 必须是裸包名、代码里不许有本机路径」的可移植性检查。 - npm 发布改成手动触发的 workflow(发出去的版本删不掉,不跟 tag 自动联动)。 ## v0.1.0 首个版本:待办墙(Notion 式三列)、按复习分钟点亮的学习热力图、跨面板存活的番茄钟、 右下角常驻迷你条、`study_desk` 工具与每轮对话的今日摘要注入。