@tom2012/dsh-pomodoro-overlay 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +127 -2
- package/client/client.js +236 -0
- package/cordis.patch.yml +4 -0
- package/docs/architecture.md +84 -0
- package/docs/design.md +72 -0
- package/docs/verification.md +102 -0
- package/lib/index.js +28 -0
- package/package.json +77 -4
- package/src/browser-store.js +251 -0
- package/src/chat-geometry.js +49 -0
- package/src/client.js +235 -0
- package/src/font.js +18 -0
- package/src/settings.js +329 -0
- package/src/styles.js +230 -0
- package/src/timer-core.js +254 -0
- package/tests/browser-store.test.js +362 -0
- package/tests/chat-geometry.test.js +84 -0
- package/tests/client-bundle.test.js +106 -0
- package/tests/font.test.js +21 -0
- package/tests/host-config.test.js +67 -0
- package/tests/timer-core.test.js +205 -0
- package/tools/build.mjs +37 -0
- package/tools/check-package.mjs +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-pomodoro-overlay contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,128 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tom2012/dsh-pomodoro-overlay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
为 DeepSeek Harness Web 设计的自动循环番茄钟。新计时或已运行的计时无需点击,自动进行「专注 → 休息 → 专注」循环;一个尽量铺满聊天消息区的半透明大倒计时,不覆盖侧栏和底部输入框。旧版暂停快照的兼容行为见下文。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **自动循环**:默认专注 25 分钟、休息 5 分钟;没有有效快照时,原生配置读取完成后自动开始。已运行快照继续按时间线推进,旧版暂停快照保持暂停。
|
|
8
|
+
- **聊天区大倒计时**:按消息区实际宽高和文字宽度自适应,保持数字比例,不裁切。
|
|
9
|
+
- **可配置阶段外观**:默认专注浅灰 `#9ca3af` / 10% 不透明度,休息红 `#ef4444` / 30% 不透明度;颜色和不透明度均可调整。
|
|
10
|
+
- **可配置时钟字体**:专注与休息共用,默认等线(DengXian);可输入本机已安装字体,未安装时回退到本机无衬线字体,不下载字体。
|
|
11
|
+
- **DSH 原生设置页**:在 DSH 设置窗口的「番茄钟」页修改字体及两种阶段的时长、颜色和不透明度;七个字段一次原子保存,revision 防止并发覆盖。
|
|
12
|
+
- **纯展示聊天层**:聊天区只有大倒计时,没有“专注 / 休息”可见标签,也没有暂停、跳过、重置等控制按钮。
|
|
13
|
+
- **刷新恢复**:运行状态保存绝对截止时间;刷新、切标签页、休眠或重开页面后,运行中的计时按原时间线恢复,暂停快照保持暂停,不通过 interval 递减计时。
|
|
14
|
+
- **低干扰**:数字层鼠标穿透;没有声音、系统通知、网络请求、模型工具或额外后台服务。
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
从 npm 安装并注册到 DSH Web profile:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
dsh plugin --profile web add @tom2012/dsh-pomodoro-overlay
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
不要同时启用旧无作用域包 `dsh-pomodoro-overlay` 与 scoped 新包,以免两个实例读写同一运行状态。普通 `npm install` 只安装 Node 依赖,不会向 DSH profile 注册插件。
|
|
25
|
+
|
|
26
|
+
从源码安装到 DSH 时,在本目录执行:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run build
|
|
30
|
+
dsh plugin --profile web add "$(pwd)"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
按 `dsh plugin` 返回的应用结果操作:若 Host schema 变更要求重启,请在**外部 Terminal** 重载现有 DSH Web 服务并刷新原 GUI,不要从 DSH 会话内部终止自己的宿主;若支持客户端 HMR,则确认浏览器已收到新 bundle 后再刷新。不要把“Host 已应用”直接当作浏览器已加载成功。
|
|
34
|
+
|
|
35
|
+
安装会修改 `~/.dsh/profiles/web`,请通过正常的 DSH 安装命令完成,不要手改 profile 的 package/patch 文件。
|
|
36
|
+
|
|
37
|
+
## 设置与使用
|
|
38
|
+
|
|
39
|
+
1. 打开 DSH 左下角的「设置」窗口。
|
|
40
|
+
2. 在设置导航中选择 **番茄钟**。
|
|
41
|
+
3. 在「时钟字体」中保留默认**等线**,或输入其他本机已安装字体;也可从常用候选中选择。
|
|
42
|
+
4. 分别调整专注和休息:
|
|
43
|
+
- **时长**:整数 1–240 分钟,默认 25 / 5。
|
|
44
|
+
- **数字颜色**:原生颜色选择器,使用 `#RRGGBB`。
|
|
45
|
+
- **数字不透明度**:0–100%,默认 10% / 30%。
|
|
46
|
+
5. 查看使用所选字体的草稿预览,点击「保存设置」;如需还原未保存草稿,点击「放弃修改」。
|
|
47
|
+
|
|
48
|
+
**配置生效规则:不打断当前计时。** 字体、颜色和不透明度保存后立即应用;当前阶段的时长与截止时间不变,新时长从下一阶段开始生效。聊天区不提供控制按钮,运行中的计时会按配置自动循环;旧版暂停快照见下文。
|
|
49
|
+
|
|
50
|
+
## 恢复与多标签语义
|
|
51
|
+
|
|
52
|
+
- 页面关闭时没有浏览器计时器或通知在运行;运行中的绝对时间线仍有效,重开后会落到当前应有阶段,暂停快照则继续保持暂停。
|
|
53
|
+
- 运行中的计时长时间离线后按完整周期做算术跳跃,不逐分钟循环,也不补发历史提醒。
|
|
54
|
+
- **旧版暂停快照**:旧版本留下的 `running: false` 状态会原样恢复,当前聊天区又没有“继续”按钮,因此不会自行恢复自动循环。若要放弃旧暂停状态并重新开始,可在浏览器开发者工具控制台执行以下命令;这会重置当前计时,但不会修改 DSH 设置,也不要清空整个 localStorage:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
localStorage.removeItem('dsh-pomodoro-overlay:state:v1')
|
|
58
|
+
location.reload()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- localStorage 不可用时自动退化为当前页面内存计时,运行中的阶段切换仍继续;刷新后可能重置。
|
|
62
|
+
- 多个同源标签页通过 `storage` 事件尽力同步,暂停快照也按同一机制同步。没有 Web Locks,不承诺并发操作下的严格 exactly-once;本插件也不发送完成通知,因此不存在通知风暴。
|
|
63
|
+
- 原生周期与外观配置保存在 DSH profile;运行状态保存在当前浏览器同源 localStorage,二者用途不同。
|
|
64
|
+
|
|
65
|
+
## 兼容性边界
|
|
66
|
+
|
|
67
|
+
开发与历史 GUI 基线版本为 **DSH 0.2.1-rc.2**。历史真实 GUI 结果、当前自动化验证范围及尚未完成的 GUI 验收项均见 [验证记录](docs/verification.md),不将历史结果视为当前版本的完整验收。
|
|
68
|
+
|
|
69
|
+
当前 DSH 没有公开的“聊天消息区 additive overlay”slot,因此插件使用公开的 `shell.overlay`,再只读测量以下 Conversation DOM 标记,将实际覆盖范围限制在消息区:
|
|
70
|
+
|
|
71
|
+
- `[data-conversation-content][data-conversation-region="chat"]`
|
|
72
|
+
- `[data-composer-seat]`
|
|
73
|
+
- `[data-chat-flow]`
|
|
74
|
+
|
|
75
|
+
这些标记是内部 DOM,不是稳定 API。找不到活动消息流、切到其他视图、区域异常或过小时,插件会**隐藏而不是全屏遮挡**。升级 DSH 后必须重跑布局验证。
|
|
76
|
+
|
|
77
|
+
## 开发
|
|
78
|
+
|
|
79
|
+
运行时没有 npm React 依赖,React 由 DSH 宿主提供;`@deepseek-ai/schemastery` 用于 Host 配置。`esbuild` 仅是开发依赖。
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install --legacy-peer-deps
|
|
83
|
+
npm run verify
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
在文件写入沙箱中,如全局 npm 缓存不可写,可使用项目缓存:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install --legacy-peer-deps --cache .npm-cache
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
单独执行:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm run build # src/ → client/client.js(DSH ModuleLoader 格式)
|
|
96
|
+
npm run check # 语法、manifest、patch、schema 声明与 bundle 静态检查
|
|
97
|
+
npm test # node:test
|
|
98
|
+
node tools/build.mjs --check # 检查构建产物是否过时
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
测试覆盖:自动循环、精确边界、跨多个离线周期、旧/新配置语义、颜色、不透明度与字体校验、字体栈、store 层外观即时更新语义、暂停继续、跳过/重置、损坏快照、存储失败、多标签旧事件、dispose、聊天区几何、Host schema、原生设置 slot,以及构建后客户端加载、store 启动、slots 注册和 style / store 监听 / interval 清理冒烟。客户端冒烟不挂载 `ActiveOverlay`,不等同于完整 overlay DOM 与观察器清理测试。
|
|
102
|
+
|
|
103
|
+
## 验证结果
|
|
104
|
+
|
|
105
|
+
- 当前发布基线为 `@tom2012/dsh-pomodoro-overlay@0.1.2`;`npm run verify` 通过,**38/38 自动化测试通过**。
|
|
106
|
+
- 当前七字段设置、字体配置和移除可见阶段标签均有 store 级自动化测试或构建产物静态检查;新设置窗口、DOM 外观与字体重排的真实 GUI 验收状态以验证记录为准。
|
|
107
|
+
- **历史真实 GUI 基线**曾验证自动启动、1/1 分钟自动双向循环、刷新恢复、灰/红配色、旧版两字段设置保存与当前截止时间不变;这些结果不等同于当前版本的完整 GUI 验收。
|
|
108
|
+
- 历史桌面与 380px 窄窗口测试曾验证聊天区几何、输入框扣除、数字适配和非聊天视图隐藏;当时测试的 1/1 配置已恢复为 **25/5**。
|
|
109
|
+
- 历史 GUI 环境的 `file-tree` / 内置 `sidebar-files` 冲突与隔离方式见 [docs/verification.md](docs/verification.md),不表示该既有冲突已在日常 GUI 中解决。
|
|
110
|
+
|
|
111
|
+
## 文件
|
|
112
|
+
|
|
113
|
+
- `lib/index.js`:Host 配置 schema 与自定义设置策略。
|
|
114
|
+
- `src/timer-core.js`:纯计时状态机。
|
|
115
|
+
- `src/browser-store.js`:运行持久化、跨标签同步与订阅。
|
|
116
|
+
- `src/chat-geometry.js`:聊天区识别及 composer 扣除。
|
|
117
|
+
- `src/font.js`:本机字体候选与安全 CSS 字体栈。
|
|
118
|
+
- `src/client.js`:React overlay、slots 注册与生命周期。
|
|
119
|
+
- `src/settings.js`:DSH 设置窗口中的七字段表单与草稿预览。
|
|
120
|
+
- `src/styles.js`:主题令牌、响应式与 reduced-motion 样式。
|
|
121
|
+
- `client/client.js`:构建产物,请勿直接编辑。
|
|
122
|
+
- `docs/design.md`:交互和视觉验收标准。
|
|
123
|
+
- `docs/architecture.md`:接口证据与实现决策。
|
|
124
|
+
- `docs/verification.md`:当前自动化结果、历史真实 GUI 基线、验收边界与环境限制。
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/client/client.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/* @tom2012/dsh-pomodoro-overlay — generated by tools/build.mjs; do not edit directly. */
|
|
2
|
+
window.__ModuleLoader__.load({ id: "@tom2012/dsh-pomodoro-overlay", factory: (require) => {
|
|
3
|
+
var module = { exports: {} }; var exports = module.exports;
|
|
4
|
+
var Ne=Object.create;var H=Object.defineProperty;var Oe=Object.getOwnPropertyDescriptor;var Fe=Object.getOwnPropertyNames;var Ee=Object.getPrototypeOf,Ae=Object.prototype.hasOwnProperty;var Te=(e,t)=>{for(var n in t)H(e,n,{get:t[n],enumerable:!0})},se=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Fe(t))!Ae.call(e,r)&&r!==n&&H(e,r,{get:()=>t[r],enumerable:!(o=Oe(t,r))||o.enumerable});return e};var le=(e,t,n)=>(n=e!=null?Ne(Ee(e)):{},se(t||!e||!e.__esModule?H(n,"default",{value:e,enumerable:!0}):n,e)),Re=e=>se(H({},"__esModule",{value:!0}),e);var Qe={};Te(Qe,{apply:()=>Ye,inject:()=>Ke,name:()=>Je});module.exports=Re(Qe);var $=le(require("react"),1);var x="focus",Z="break";var T=Object.freeze({focusMinutes:25,breakMinutes:5,focusColor:"#9ca3af",breakColor:"#ef4444",focusOpacity:.1,breakOpacity:.3,fontFamily:"\u7B49\u7EBF"});function D(e){return typeof e=="number"?e:typeof e=="string"&&e.trim()!==""?Number(e):Number.NaN}function X(e){let t=D(e);return Number.isInteger(t)&&t>=1&&t<=240}function q(e){return typeof e=="string"&&/^#[0-9a-f]{6}$/i.test(e)}function ce(e){let t=D(e);return Number.isFinite(t)&&t>=0&&t<=1}function ee(e){return typeof e=="string"&&e.length<=80&&/^[\p{L}\p{N}][\p{L}\p{N} _.-]*$/u.test(e)}function U(e){if(!X(e?.focusMinutes)||!X(e?.breakMinutes))throw new RangeError("Durations must be integers from 1 to 240 minutes");let t={focusMinutes:D(e.focusMinutes),breakMinutes:D(e.breakMinutes),focusColor:e.focusColor===void 0?T.focusColor:e.focusColor,breakColor:e.breakColor===void 0?T.breakColor:e.breakColor,focusOpacity:e.focusOpacity===void 0?T.focusOpacity:e.focusOpacity,breakOpacity:e.breakOpacity===void 0?T.breakOpacity:e.breakOpacity,fontFamily:e.fontFamily===void 0?T.fontFamily:e.fontFamily};if(!q(t.focusColor)||!q(t.breakColor))throw new TypeError("Colors must use six-digit hex format (#rrggbb)");if(!ce(t.focusOpacity)||!ce(t.breakOpacity))throw new RangeError("Opacity values must be between 0 and 1");if(!ee(t.fontFamily))throw new TypeError("Font family must be a single installed font name");return t.focusOpacity=D(t.focusOpacity),t.breakOpacity=D(t.breakOpacity),t}function J(e){if(e===x)return Z;if(e===Z)return x;throw new TypeError(`Unknown Pomodoro phase: ${String(e)}`)}function $e(e,t){J(e);let n=U(t);return(e===x?n.focusMinutes:n.breakMinutes)*6e4}function V(e,t){if(!Number.isFinite(e))throw new TypeError(`${t} must be finite`)}function B(e,t,n,o){J(e),V(n,"now");let r=o??$e(e,t);if(V(r,"durationMs"),r<=0)throw new RangeError("durationMs must be positive");return{version:1,phase:e,running:!0,startedAt:n,endsAt:n+r,durationMs:r,remainingMs:null,updatedAt:n}}function j(e){return!e||e.version!==1||e.phase!==x&&e.phase!==Z||typeof e.running!="boolean"||!Number.isFinite(e.startedAt)||!Number.isFinite(e.updatedAt)||!Number.isFinite(e.durationMs)||e.durationMs<=0?!1:e.running?Number.isFinite(e.endsAt)&&e.endsAt>=0&&e.endsAt-e.startedAt===e.durationMs&&e.remainingMs===null:e.endsAt===null&&Number.isFinite(e.remainingMs)&&e.remainingMs>=0&&e.remainingMs<=e.durationMs}function te(e){if(typeof e!="string"||e==="")return null;try{let t=JSON.parse(e);return j(t)?t:null}catch{return null}}function I(e,t,n){if(!j(e))throw new TypeError("Invalid Pomodoro state");let o=U(t);if(V(n,"now"),!e.running||n<e.endsAt)return e;let r={...e,updatedAt:n},p=0,f=(o.focusMinutes+o.breakMinutes)*6e4;for(;n>=r.endsAt;){let h=r.endsAt;if(r.phase=J(r.phase),r.startedAt=h,r.durationMs=(r.phase===x?o.focusMinutes:o.breakMinutes)*6e4,r.endsAt=h+r.durationMs,r.remainingMs=null,r.running=!0,p+=1,p>=2){let l=Math.floor((n-r.startedAt)/f);l>0&&(r.startedAt+=l*f,r.endsAt+=l*f,p+=l*2)}}return r}function ne(e,t){if(!j(e))throw new TypeError("Invalid Pomodoro state");return V(t,"now"),e.running?Math.min(e.durationMs,Math.max(0,e.endsAt-t)):e.remainingMs}function ue(e,t,n){let o=I(e,t,n);return o.running?{...o,running:!1,endsAt:null,remainingMs:ne(o,n),updatedAt:n}:o}function de(e,t,n){if(!j(e))throw new TypeError("Invalid Pomodoro state");if(V(n,"now"),e.running)return e;let o=Math.max(0,e.durationMs-e.remainingMs),r={...e,running:!0,startedAt:n-o,endsAt:n+e.remainingMs,remainingMs:null,updatedAt:n};return I(r,t,n)}function pe(e,t,n){if(!j(e))throw new TypeError("Invalid Pomodoro state");return B(J(e.phase),t,n)}function fe(e,t){return B(x,e,t)}function me(e){V(e,"milliseconds");let t=Math.max(0,Math.ceil(e/1e3)),n=Math.floor(t/60),o=t%60;return`${String(n).padStart(2,"0")}:${String(o).padStart(2,"0")}`}var oe="dsh-pomodoro-overlay:state:v1";function Ie(){try{return typeof window>"u"?null:window.localStorage}catch{return null}}function _e(){return typeof window>"u"?null:window}function ze(){return typeof document>"u"?null:document}function ge(e={}){let t=e.now??(()=>Date.now()),n=e.storage===void 0?Ie():e.storage,o=e.events===void 0?_e():e.events,r=e.document===void 0?ze():e.document,p=e.timers??o,f=e.tickMs??250,h=new Set,l=U(e.initialConfig??T),s=B(x,l,t()),d=null,g=!1,y=!1,M=null,S=!!n;function C(){if(!n)return S=!1,null;try{let a=n.getItem(oe);return S=!0,te(a)}catch{return S=!1,null}}function N(){if(!n)return S=!1,!1;try{return n.setItem(oe,JSON.stringify(s)),S=!0,!0}catch{return S=!1,!1}}function R(a){let m=ne(s,a);return Object.freeze({active:g&&!y,phase:s.phase,running:s.running,text:me(m),remainingMs:m,config:l,state:s,storageAvailable:S,updatedAt:s.updatedAt})}function O(a,m=!1){let v=R(a);if(m||!d||v.active!==d.active||v.phase!==d.phase||v.running!==d.running||v.text!==d.text||v.config!==d.config||v.updatedAt!==d.updatedAt||v.storageAvailable!==d.storageAvailable){d=v;for(let Ce of[...h])Ce()}}function F(a=!1){if(!g||y)return;let m=t(),v=I(s,l,m);v!==s&&(s=v,N()),O(m,a)}function E(a,m){s=a,N(),O(m,!0)}function A(a){if(y||!g||a?.key!==oe)return;let m=te(a.newValue);if(!m||m.updatedAt<s.updatedAt)return;let v=t(),P=I(m,l,v);s=P,P!==m&&N(),O(v,!0)}function _(){r?.hidden||F(!0)}function z(){F(!0)}function Y(){if(g||y)return;g=!0;let a=C();s=a??B(x,l,t()),a||N(),o?.addEventListener?.("storage",A),o?.addEventListener?.("focus",z),o?.addEventListener?.("pageshow",z),r?.addEventListener?.("visibilitychange",_),M=p?.setInterval?.(F,f)??null,F(!0)}function c(a){if(typeof a!="function")throw new TypeError("Listener must be a function");return h.add(a),()=>h.delete(a)}function u(){return d||(d=R(t())),d}function b(a){if(y)return;let m=U(a),v=t();if(g){let P=I(s,l,v);P!==s&&(s=P,N())}l=m,O(v,!0)}function w(){if(!g||y)return;let a=t();E(ue(s,l,a),a)}function W(){if(!g||y)return;let a=t(),m=I(s,l,a);E(de(m,l,a),a)}function Q(){if(!g||y)return;let a=t(),m=I(s,l,a);E(pe(m,l,a),a)}function L(){if(!g||y)return;let a=t();E(fe(l,a),a)}function Se(){y||(y=!0,g=!1,M!==null&&p?.clearInterval?.(M),M=null,o?.removeEventListener?.("storage",A),o?.removeEventListener?.("focus",z),o?.removeEventListener?.("pageshow",z),r?.removeEventListener?.("visibilitychange",_),h.clear(),d=null)}return{start:Y,subscribe:c,getSnapshot:u,setConfig:b,pause:w,resume:W,skip:Q,reset:L,refresh:F,dispose:Se}}var Le='[data-conversation-content][data-conversation-region="chat"]',Ue="[data-composer-seat]",Pe="[data-chat-flow]";function ye(e){return e&&["left","top","right","bottom","width","height"].every(t=>Number.isFinite(e[t]))}function De(e){if(!e||e.isConnected===!1||e.closest?.("[hidden], [inert]")||!e.querySelector?.(Pe))return null;let t=e.getBoundingClientRect?.();if(!ye(t)||t.width<=0||t.height<=0)return null;let o=e.querySelector?.(Ue)?.getBoundingClientRect?.(),r=ye(o)?Math.min(t.bottom,o.top):t.bottom,p=r-t.top;return t.width<160||p<120?null:{left:t.left,top:t.top,right:t.right,bottom:r,width:t.width,height:p}}function be(e){if(!e?.querySelectorAll)return null;let t=null;for(let n of e.querySelectorAll(Le)){let o=De(n);o&&(!t||o.width*o.height>t.rect.width*t.rect.height)&&(t={chat:n,rect:o})}return t}var re=Object.freeze([{value:"\u7B49\u7EBF",label:"DengXian\uFF08\u9ED8\u8BA4\uFF09",stack:'"DengXian", "\u7B49\u7EBF", sans-serif'},{value:"system-ui",label:"\u7CFB\u7EDF\u65E0\u886C\u7EBF",stack:'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'},{value:"monospace",label:"\u7B49\u5BBD\u5B57\u4F53",stack:'ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace'},{value:"Arial",label:"Arial",stack:"Arial, Helvetica, sans-serif"},{value:"Courier New",label:"Courier New",stack:'"Courier New", Courier, monospace'},{value:"Georgia",label:"Georgia",stack:'Georgia, "Times New Roman", serif'}]);function K(e){if(typeof e!="string"||e.trim()==="")return"sans-serif";let t=e.trim(),n=re.find(r=>r.value===t);return n?n.stack:`"${t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}", sans-serif`}var he=String.raw`
|
|
5
|
+
.dpo-overlay {
|
|
6
|
+
position: fixed;
|
|
7
|
+
z-index: 1;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
display: grid;
|
|
10
|
+
place-items: center;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
pointer-events: none;
|
|
13
|
+
container-type: inline-size;
|
|
14
|
+
container-name: dpo;
|
|
15
|
+
color: var(--dsw-alias-label-primary);
|
|
16
|
+
font-family: inherit;
|
|
17
|
+
}
|
|
18
|
+
.dpo-overlay[hidden] { display: none !important; }
|
|
19
|
+
.dpo-number {
|
|
20
|
+
font-family: var(--ds-font-family-code, ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace);
|
|
21
|
+
font-size: 120px;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
24
|
+
letter-spacing: -0.065em;
|
|
25
|
+
line-height: 1;
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
user-select: none;
|
|
28
|
+
transform: translateX(-0.0325em);
|
|
29
|
+
transition: color 160ms ease-out, opacity 160ms ease-out;
|
|
30
|
+
}
|
|
31
|
+
.dpo-button {
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
min-width: 44px;
|
|
34
|
+
min-height: 44px;
|
|
35
|
+
border: 0;
|
|
36
|
+
border-radius: 8px;
|
|
37
|
+
padding: 0 12px;
|
|
38
|
+
background: transparent;
|
|
39
|
+
color: var(--dsw-alias-label-secondary);
|
|
40
|
+
font: inherit;
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
line-height: 20px;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
transition: background-color 120ms ease-out, color 120ms ease-out;
|
|
45
|
+
}
|
|
46
|
+
.dpo-button:hover:not(:disabled) {
|
|
47
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
48
|
+
color: var(--dsw-alias-label-primary);
|
|
49
|
+
}
|
|
50
|
+
.dpo-button:active:not(:disabled) {
|
|
51
|
+
background: var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover));
|
|
52
|
+
}
|
|
53
|
+
.dpo-button:focus-visible,
|
|
54
|
+
.dpo-input:focus-visible,
|
|
55
|
+
.dpo-color-input:focus-visible,
|
|
56
|
+
.dpo-range:focus-visible {
|
|
57
|
+
outline: 2px solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
58
|
+
outline-offset: 2px;
|
|
59
|
+
}
|
|
60
|
+
.dpo-button:disabled,
|
|
61
|
+
.dpo-input:disabled {
|
|
62
|
+
opacity: 0.55;
|
|
63
|
+
cursor: default;
|
|
64
|
+
}
|
|
65
|
+
.dpo-sr-only {
|
|
66
|
+
position: absolute !important;
|
|
67
|
+
width: 1px !important;
|
|
68
|
+
height: 1px !important;
|
|
69
|
+
padding: 0 !important;
|
|
70
|
+
margin: -1px !important;
|
|
71
|
+
overflow: hidden !important;
|
|
72
|
+
clip: rect(0, 0, 0, 0) !important;
|
|
73
|
+
white-space: nowrap !important;
|
|
74
|
+
border: 0 !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dpo-settings {
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
max-width: 680px;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: 16px;
|
|
83
|
+
color: var(--dsw-alias-label-primary);
|
|
84
|
+
font-size: 14px;
|
|
85
|
+
line-height: 22px;
|
|
86
|
+
}
|
|
87
|
+
.dpo-settings h2,
|
|
88
|
+
.dpo-settings h3,
|
|
89
|
+
.dpo-settings p { margin: 0; }
|
|
90
|
+
.dpo-settings h2 { font-size: 18px; line-height: 28px; font-weight: 550; }
|
|
91
|
+
.dpo-settings h3 { font-size: 14px; line-height: 22px; font-weight: 500; }
|
|
92
|
+
.dpo-description { color: var(--dsw-alias-label-secondary); }
|
|
93
|
+
.dpo-hint { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; }
|
|
94
|
+
.dpo-form {
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: 16px;
|
|
98
|
+
}
|
|
99
|
+
.dpo-phase-card {
|
|
100
|
+
box-sizing: border-box;
|
|
101
|
+
min-width: 0;
|
|
102
|
+
margin: 0;
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
gap: 16px;
|
|
106
|
+
border: 1px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l2));
|
|
107
|
+
border-radius: var(--dsw-radius-xl, 16px);
|
|
108
|
+
padding: 16px;
|
|
109
|
+
background: var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-base));
|
|
110
|
+
}
|
|
111
|
+
.dpo-phase-card > legend {
|
|
112
|
+
padding: 0 4px;
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
}
|
|
116
|
+
.dpo-field {
|
|
117
|
+
display: grid;
|
|
118
|
+
grid-template-columns: minmax(0, 1fr) minmax(120px, 160px);
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: 16px;
|
|
121
|
+
}
|
|
122
|
+
.dpo-field-copy { display: flex; flex-direction: column; gap: 2px; }
|
|
123
|
+
.dpo-field-copy label { color: var(--dsw-alias-label-primary); font-weight: 500; }
|
|
124
|
+
.dpo-input {
|
|
125
|
+
box-sizing: border-box;
|
|
126
|
+
width: 100%;
|
|
127
|
+
min-height: 44px;
|
|
128
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
129
|
+
border-radius: var(--dsw-radius-md, 8px);
|
|
130
|
+
padding: 8px 12px;
|
|
131
|
+
background: var(--dsw-alias-bg-base);
|
|
132
|
+
color: var(--dsw-alias-label-primary);
|
|
133
|
+
font: inherit;
|
|
134
|
+
font-variant-numeric: tabular-nums;
|
|
135
|
+
}
|
|
136
|
+
.dpo-font-control { min-width: 0; }
|
|
137
|
+
.dpo-color-control {
|
|
138
|
+
min-height: 44px;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 12px;
|
|
142
|
+
}
|
|
143
|
+
.dpo-color-input {
|
|
144
|
+
box-sizing: border-box;
|
|
145
|
+
width: 52px;
|
|
146
|
+
height: 44px;
|
|
147
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
148
|
+
border-radius: var(--dsw-radius-md, 8px);
|
|
149
|
+
padding: 4px;
|
|
150
|
+
background: var(--dsw-alias-bg-base);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
}
|
|
153
|
+
.dpo-color-value {
|
|
154
|
+
color: var(--dsw-alias-label-secondary);
|
|
155
|
+
font-family: var(--ds-font-family-code, ui-monospace, monospace);
|
|
156
|
+
font-size: 12px;
|
|
157
|
+
}
|
|
158
|
+
.dpo-range-control {
|
|
159
|
+
min-height: 44px;
|
|
160
|
+
display: grid;
|
|
161
|
+
grid-template-columns: minmax(80px, 1fr) 48px;
|
|
162
|
+
align-items: center;
|
|
163
|
+
gap: 12px;
|
|
164
|
+
}
|
|
165
|
+
.dpo-range {
|
|
166
|
+
width: 100%;
|
|
167
|
+
min-height: 44px;
|
|
168
|
+
margin: 0;
|
|
169
|
+
accent-color: var(--dsw-alias-brand-primary);
|
|
170
|
+
cursor: pointer;
|
|
171
|
+
}
|
|
172
|
+
.dpo-output {
|
|
173
|
+
text-align: right;
|
|
174
|
+
font-variant-numeric: tabular-nums;
|
|
175
|
+
color: var(--dsw-alias-label-secondary);
|
|
176
|
+
}
|
|
177
|
+
.dpo-preview {
|
|
178
|
+
min-height: 88px;
|
|
179
|
+
display: flex;
|
|
180
|
+
flex-direction: column;
|
|
181
|
+
align-items: center;
|
|
182
|
+
justify-content: center;
|
|
183
|
+
gap: 4px;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
border: 1px dashed var(--dsw-alias-border-l3);
|
|
186
|
+
border-radius: var(--dsw-radius-lg, 12px);
|
|
187
|
+
background: var(--dsw-alias-bg-base);
|
|
188
|
+
}
|
|
189
|
+
.dpo-preview-number {
|
|
190
|
+
color: var(--dpo-preview-color);
|
|
191
|
+
opacity: var(--dpo-preview-opacity);
|
|
192
|
+
font-family: var(--dpo-preview-font, var(--ds-font-family-code, ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, monospace));
|
|
193
|
+
font-size: 42px;
|
|
194
|
+
font-weight: 600;
|
|
195
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
196
|
+
line-height: 1;
|
|
197
|
+
letter-spacing: -0.065em;
|
|
198
|
+
user-select: none;
|
|
199
|
+
}
|
|
200
|
+
.dpo-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
201
|
+
.dpo-settings .dpo-button { border: 1px solid var(--dsw-alias-border-l3); }
|
|
202
|
+
.dpo-settings .dpo-primary {
|
|
203
|
+
border-color: transparent;
|
|
204
|
+
background: var(--dsw-alias-brand-primary);
|
|
205
|
+
color: var(--dsw-alias-label-primary-inverted, #fff);
|
|
206
|
+
}
|
|
207
|
+
.dpo-settings .dpo-primary:hover:not(:disabled) {
|
|
208
|
+
background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));
|
|
209
|
+
color: var(--dsw-alias-label-primary-inverted, #fff);
|
|
210
|
+
}
|
|
211
|
+
.dpo-error { color: var(--dsw-alias-state-error-primary); font-size: 13px; line-height: 20px; }
|
|
212
|
+
.dpo-success { color: var(--dsw-alias-state-success-primary); font-size: 13px; line-height: 20px; }
|
|
213
|
+
.dpo-warning { color: var(--dsw-alias-state-warn-label); font-size: 13px; line-height: 20px; }
|
|
214
|
+
.dpo-current {
|
|
215
|
+
display: flex;
|
|
216
|
+
flex-wrap: wrap;
|
|
217
|
+
gap: 8px 16px;
|
|
218
|
+
color: var(--dsw-alias-label-secondary);
|
|
219
|
+
font-size: 13px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@media (max-width: 520px) {
|
|
223
|
+
.dpo-field { grid-template-columns: 1fr; gap: 8px; }
|
|
224
|
+
.dpo-actions .dpo-button { flex: 1 1 120px; }
|
|
225
|
+
}
|
|
226
|
+
@media (pointer: coarse) {
|
|
227
|
+
.dpo-button, .dpo-input, .dpo-range { min-height: 48px; }
|
|
228
|
+
.dpo-color-input { height: 48px; }
|
|
229
|
+
}
|
|
230
|
+
@media (prefers-reduced-motion: reduce) {
|
|
231
|
+
.dpo-number, .dpo-button { transition: none; }
|
|
232
|
+
}
|
|
233
|
+
`;var k=le(require("react"),1);var i=k.default.createElement,Ve=["focusMinutes","breakMinutes"],Ge=["focusColor","breakColor"],Be=["focusOpacity","breakOpacity"];function ve(e){return e===x?"\u4E13\u6CE8":"\u4F11\u606F"}function xe(e=T){let t={...T,...e};return{focusMinutes:String(t.focusMinutes),breakMinutes:String(t.breakMinutes),focusColor:t.focusColor,breakColor:t.breakColor,focusOpacity:String(Math.round(t.focusOpacity*100)),breakOpacity:String(Math.round(t.breakOpacity*100)),fontFamily:t.fontFamily}}function ke(e){if(typeof e=="string"&&e.trim()==="")return!1;let t=Number(e);return Number.isInteger(t)&&t>=0&&t<=100}function je(e){if(!Ve.every(t=>X(e[t])))throw new RangeError("\u5DE5\u4F5C\u548C\u4F11\u606F\u65F6\u957F\u5747\u9700\u4E3A 1\u2013240 \u4E4B\u95F4\u7684\u6574\u6570\u5206\u949F\u3002");if(!Ge.every(t=>q(e[t])))throw new TypeError("\u989C\u8272\u9700\u4F7F\u7528 #RRGGBB \u683C\u5F0F\u3002");if(!Be.every(t=>ke(e[t])))throw new RangeError("\u6570\u5B57\u4E0D\u900F\u660E\u5EA6\u9700\u4E3A 0\u2013100 \u7684\u6574\u6570\u767E\u5206\u6BD4\u3002");if(!ee(e.fontFamily.trim()))throw new TypeError("\u8BF7\u8F93\u5165\u4E00\u4E2A\u672C\u673A\u5B57\u4F53\u540D\u79F0\uFF081\u201380 \u4E2A\u5B57\u7B26\uFF0C\u4E0D\u5305\u542B\u5F15\u53F7\u6216\u9017\u53F7\uFF09\u3002");return U({focusMinutes:Number(e.focusMinutes),breakMinutes:Number(e.breakMinutes),focusColor:e.focusColor,breakColor:e.breakColor,focusOpacity:Number(e.focusOpacity)/100,breakOpacity:Number(e.breakOpacity)/100,fontFamily:e.fontFamily.trim()})}function we(e){let t=k.default.useCallback(o=>e.subscribe(o),[e]),n=k.default.useCallback(()=>e.getSnapshot(),[e]);return k.default.useSyncExternalStore(t,n)}function Me({form:e,store:t}){let n=we(e),o=we(t),[r,p]=k.default.useState(()=>xe(e.getSnapshot().value)),[f,h]=k.default.useState(e.getSnapshot().revision),[l,s]=k.default.useState(!1),[d,g]=k.default.useState(!1),[y,M]=k.default.useState(""),[S,C]=k.default.useState(""),N=k.default.useRef(!0);k.default.useEffect(()=>(N.current=!0,()=>{N.current=!1}),[]);let R=k.default.useCallback((c=!0)=>{let u=e.getSnapshot();u.status==="ready"&&u.value&&(p(xe(u.value)),h(u.revision),s(!1),c&&(M(""),C("")))},[e]);k.default.useEffect(()=>{!l&&n.status==="ready"&&n.value&&R(!1)},[n,l,R]);function O(c,u){p(b=>({...b,[c]:u})),s(!0),M(""),C("")}async function F(c){if(c.preventDefault(),d||!l||!n.writable)return;let u;try{u=je(r)}catch(b){M(b.message),C("");return}g(!0),M(""),C("");try{let b=await e.mutate([{op:"set",path:["focusMinutes"],value:u.focusMinutes},{op:"set",path:["breakMinutes"],value:u.breakMinutes},{op:"set",path:["focusColor"],value:u.focusColor},{op:"set",path:["breakColor"],value:u.breakColor},{op:"set",path:["focusOpacity"],value:u.focusOpacity},{op:"set",path:["breakOpacity"],value:u.breakOpacity},{op:"set",path:["fontFamily"],value:u.fontFamily}],f);if(!N.current)return;b?(s(!1),C("\u5DF2\u4FDD\u5B58\u3002\u5B57\u4F53\u3001\u989C\u8272\u548C\u4E0D\u900F\u660E\u5EA6\u5DF2\u5E94\u7528\uFF1B\u65B0\u65F6\u957F\u4ECE\u4E0B\u4E00\u9636\u6BB5\u751F\u6548\u3002")):M("\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u53D8\u5316\uFF0C\u672C\u6B21\u672A\u8986\u76D6\u3002\u8349\u7A3F\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u6838\u5BF9\u6700\u65B0\u503C\u540E\u91CD\u8BD5\u3002")}catch{N.current&&M("\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8FDE\u63A5\u540E\u91CD\u8BD5\u3002\u8349\u7A3F\u5DF2\u4FDD\u7559\u3002")}finally{N.current&&g(!1)}}let E=n.status==="ready"&&!!n.value,A=!E||!n.writable||d;function _(c,u,b,w){return i("div",{className:"dpo-field",key:c},[i("div",{className:"dpo-field-copy",key:"copy"},[i("label",{htmlFor:`dpo-settings-${c}`,key:"label"},u),i("span",{className:"dpo-hint",id:`dpo-settings-${c}-hint`,key:"hint"},b)]),w])}function z(c){let u=`${c}Minutes`,b=`${c}Color`,w=`${c}Opacity`,W=ve(c),Q=ke(r[w])?Number(r[w])/100:0;return i("fieldset",{className:"dpo-phase-card","data-dpo-phase-settings":c,key:c,disabled:A},[i("legend",{key:"legend"},W),i("p",{className:"dpo-hint",key:"phase-hint"},c===x?"\u5DE5\u4F5C\u9636\u6BB5\u7684\u5468\u671F\u4E0E\u5927\u6570\u5B57\u5916\u89C2":"\u4F11\u606F\u9636\u6BB5\u7684\u5468\u671F\u4E0E\u5927\u6570\u5B57\u5916\u89C2"),_(u,`${W}\u65F6\u957F`,"1\u2013240 \u5206\u949F",i("input",{key:"input",id:`dpo-settings-${u}`,className:"dpo-input","data-dpo-setting":u,type:"number",min:1,max:240,step:1,inputMode:"numeric",value:r[u],onChange:L=>O(u,L.currentTarget.value),"aria-describedby":`dpo-settings-${u}-hint`})),_(b,"\u6570\u5B57\u989C\u8272","\u516D\u4F4D\u5341\u516D\u8FDB\u5236\u989C\u8272",i("div",{className:"dpo-color-control",key:"input"},[i("input",{key:"color",id:`dpo-settings-${b}`,className:"dpo-color-input","data-dpo-setting":b,type:"color",value:r[b],onChange:L=>O(b,L.currentTarget.value),"aria-describedby":`dpo-settings-${b}-hint`}),i("code",{key:"value",className:"dpo-color-value"},r[b].toUpperCase())])),_(w,"\u6570\u5B57\u4E0D\u900F\u660E\u5EA6","0% \u5B8C\u5168\u900F\u660E\uFF0C100% \u4E0D\u900F\u660E",i("div",{className:"dpo-range-control",key:"input"},[i("input",{key:"range",id:`dpo-settings-${w}`,className:"dpo-range","data-dpo-setting":w,type:"range",min:0,max:100,step:1,value:r[w],onChange:L=>O(w,L.currentTarget.value),"aria-valuetext":`${r[w]}%`,"aria-describedby":`dpo-settings-${w}-hint`}),i("output",{key:"output",className:"dpo-output",htmlFor:`dpo-settings-${w}`,"data-dpo-opacity-value":c},`${r[w]}%`)])),i("div",{className:"dpo-preview","data-dpo-preview":c,key:"preview",style:{"--dpo-preview-color":r[b],"--dpo-preview-opacity":Q,"--dpo-preview-font":K(r.fontFamily)}},[i("span",{className:"dpo-hint",key:"label"},"\u8349\u7A3F\u9884\u89C8"),i("span",{className:"dpo-preview-number","aria-hidden":"true",key:"number"},c===x?"25:00":"05:00")])])}function Y(){return i("fieldset",{className:"dpo-phase-card","data-dpo-font-settings":"",key:"font",disabled:A},[i("legend",{key:"legend"},"\u65F6\u949F\u5B57\u4F53"),i("p",{className:"dpo-hint",key:"font-hint"},"\u4E13\u6CE8\u4E0E\u4F11\u606F\u5171\u7528\uFF1B\u53EA\u4F7F\u7528\u672C\u673A\u5B57\u4F53\uFF0C\u4E0D\u53D1\u8D77\u5B57\u4F53\u4E0B\u8F7D\u3002"),_("fontFamily","\u5B57\u4F53\u540D\u79F0","\u9ED8\u8BA4\u7B49\u7EBF\uFF08DengXian\uFF09\uFF0C\u53EF\u8F93\u5165\u5176\u4ED6\u672C\u673A\u5B57\u4F53",i("div",{className:"dpo-font-control",key:"input"},[i("input",{key:"font",id:"dpo-settings-fontFamily",className:"dpo-input","data-dpo-setting":"fontFamily",type:"text",list:"dpo-font-options",maxLength:80,autoComplete:"off",autoCapitalize:"off",spellCheck:!1,value:r.fontFamily,onChange:c=>O("fontFamily",c.currentTarget.value),"aria-describedby":"dpo-settings-fontFamily-hint"}),i("datalist",{id:"dpo-font-options",key:"options"},re.map(c=>i("option",{key:c.value,value:c.value,label:c.label})))]))])}return i("section",{className:"dpo-settings","data-dpo-settings":""},[i("h2",{key:"title"},"\u756A\u8304\u949F"),i("p",{className:"dpo-description",key:"description"},"\u81EA\u52A8\u8FDB\u884C\u300C\u4E13\u6CE8 \u2192 \u4F11\u606F\u300D\u5FAA\u73AF\u3002\u5012\u8BA1\u65F6\u4EC5\u8986\u76D6\u804A\u5929\u6D88\u606F\u533A\u57DF\uFF1B\u6B64\u5904\u9884\u89C8\u8349\u7A3F\uFF0C\u4FDD\u5B58\u540E\u5E94\u7528\u3002"),n.status==="loading"?i("p",{key:"loading",role:"status"},"\u6B63\u5728\u8BFB\u53D6\u756A\u8304\u949F\u8BBE\u7F6E\u2026"):null,n.status==="unavailable"?i("p",{key:"unavailable",className:"dpo-error",role:"alert"},"\u5F53\u524D\u5BA2\u6237\u7AEF\u65E0\u6CD5\u8BFB\u53D6\u756A\u8304\u949F\u914D\u7F6E\u3002"):null,E?i("form",{key:"form",className:"dpo-form",noValidate:!0,onSubmit:F},[Y(),z(x),z("break"),i("p",{className:"dpo-hint",key:"rule"},"\u4FDD\u5B58\u4E0D\u6253\u65AD\u5F53\u524D\u8BA1\u65F6\u3002\u5B57\u4F53\u3001\u989C\u8272\u548C\u4E0D\u900F\u660E\u5EA6\u7ACB\u5373\u751F\u6548\uFF1B\u65B0\u65F6\u957F\u4ECE\u4E0B\u4E00\u9636\u6BB5\u5F00\u59CB\u751F\u6548\u3002"),i("div",{className:"dpo-actions",key:"actions"},[i("button",{key:"save",type:"submit",className:"dpo-button dpo-primary","data-dpo-save":"",disabled:A||!l},d?"\u4FDD\u5B58\u4E2D\u2026":"\u4FDD\u5B58\u8BBE\u7F6E"),i("button",{key:"discard",type:"button",className:"dpo-button","data-dpo-discard":"",disabled:d||!l,onClick:()=>R()},"\u653E\u5F03\u4FEE\u6539")])]):null,!n.writable&&E?i("p",{key:"readonly",className:"dpo-warning"},"\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002"):null,y?i("p",{key:"error",className:"dpo-error",role:"alert"},y):null,S?i("p",{key:"notice",className:"dpo-success",role:"status"},S):null,o.storageAvailable?null:i("p",{key:"storage",className:"dpo-warning",role:"status"},"\u6D4F\u89C8\u5668\u5B58\u50A8\u4E0D\u53EF\u7528\uFF1A\u8BA1\u65F6\u4ECD\u5728\u8FD0\u884C\uFF0C\u4F46\u5237\u65B0\u540E\u53EF\u80FD\u91CD\u65B0\u5F00\u59CB\u3002"),o.active?i("div",{className:"dpo-current",key:"current"},[i("span",{key:"phase"},`\u5F53\u524D\u9636\u6BB5\uFF1A${ve(o.phase)}${o.running?"":"\uFF08\u5DF2\u6682\u505C\uFF09"}`),i("span",{key:"time"},`\u5269\u4F59\uFF1A${o.text}`)]):null])}var G=$.default.createElement,ae="@tom2012/dsh-pomodoro-overlay",We="pomodoro-overlay";function He(e){return e===x?"\u4E13\u6CE8":"\u4F11\u606F"}function ie(e,t){if(!e||!t)return;if(e.hidden||e.clientWidth===0||e.clientHeight===0){e.__dpoFitKey="";return}let n=getComputedStyle(t),o=t.textContent||"00:00",r=[e.clientWidth,e.clientHeight,o,n.fontFamily,n.fontWeight].join("|");if(e.__dpoFitKey===r)return;e.__dpoFitKey=r;let p=Math.max(32,e.clientWidth-32),f=Math.max(32,e.clientHeight-64),h=p/3,s=document.createElement("canvas").getContext("2d");s&&(s.font=`${n.fontWeight} 100px ${n.fontFamily}`,h=Math.max(1,s.measureText(o).width-(o.length-1)*6.5));let d=Math.max(24,Math.floor(Math.min(f,p*100/h)));t.style.fontSize=`${d}px`}function Xe({snapshot:e}){let t=$.default.useRef(null),n=$.default.useRef(null);$.default.useLayoutEffect(()=>{ie(t.current,n.current)},[e.text,e.config.fontFamily]),$.default.useEffect(()=>{let f=t.current;if(!f)return;let h=!1,l=null,s=null,d=null,g=null,y=typeof ResizeObserver=="function"?new ResizeObserver(()=>C()):null,M=typeof ResizeObserver=="function"?new ResizeObserver(()=>ie(f,n.current)):null;function S(){if(h)return;let O=be(document),F=O?.chat??null,E=F?.querySelector("[data-composer-seat]")??null;if((F!==d||E!==g)&&(d=F,g=E,y?.disconnect(),d&&y?.observe(d),g&&y?.observe(g)),!O){f.hidden=!0,f.dataset.dpoGeometry="hidden";return}let{rect:A}=O;f.style.left=`${A.left}px`,f.style.top=`${A.top}px`,f.style.width=`${A.width}px`,f.style.height=`${A.height}px`,f.hidden=!1,f.dataset.dpoGeometry="active",ie(f,n.current)}function C(){h||l!==null||(l=requestAnimationFrame(()=>{l=null,S()}))}function N(){h||s!==null||(s=window.setTimeout(()=>{s=null,S()},160))}let R=typeof MutationObserver=="function"?new MutationObserver(N):null;return R?.observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["data-conversation-content","data-chat-flow","data-composer-seat","hidden","inert"]}),M?.observe(f),window.addEventListener("resize",C),window.addEventListener("scroll",C,!0),S(),()=>{h=!0,y?.disconnect(),M?.disconnect(),R?.disconnect(),l!==null&&cancelAnimationFrame(l),s!==null&&clearTimeout(s),window.removeEventListener("resize",C),window.removeEventListener("scroll",C,!0)}},[]);let o=He(e.phase),r=e.running?o:`${o} \xB7 \u5DF2\u6682\u505C`,p=e.phase===x?{color:e.config.focusColor,opacity:e.config.focusOpacity}:{color:e.config.breakColor,opacity:e.config.breakOpacity};return G("div",{ref:t,className:"dpo-overlay",hidden:!0,role:"group","aria-label":`\u756A\u8304\u949F\uFF0C${r}`,"data-dpo-overlay":"","data-phase":e.phase,"data-running":String(e.running),"data-dpo-geometry":"hidden"},[G("div",{key:"number",ref:n,className:"dpo-number","data-dpo-number":"","data-dpo-color":p.color,"data-dpo-opacity":String(p.opacity),"data-dpo-font-family":e.config.fontFamily,"aria-hidden":"true",style:{color:p.color,opacity:p.opacity,fontFamily:K(e.config.fontFamily)}},e.text),G("span",{key:"accessible-time",className:"dpo-sr-only",role:"timer","aria-label":`${r}\uFF0C\u5269\u4F59 ${e.text}`},`${r}\uFF0C\u5269\u4F59 ${e.text}`)])}function qe({store:e}){let t=$.default.useCallback(r=>e.subscribe(r),[e]),n=$.default.useCallback(()=>e.getSnapshot(),[e]),o=$.default.useSyncExternalStore(t,n);return o.active?G(Xe,{snapshot:o}):null}var Je=ae,Ke=["slots","configForms"];function Ye(e){let t=e.configForms.get(We),n=ge();e.effect(()=>{let o=()=>{let p=t.getSnapshot();if(!(p.status!=="ready"||!p.value))try{n.setConfig(p.value),n.start()}catch(f){e.logger?.error?.("Pomodoro Overlay could not accept its configuration",f)}},r=t.subscribe(o);return o(),()=>{r(),n.dispose()}},"pomodoro-overlay: native configuration and timer"),e.effect(()=>{let o=document.createElement("style");return o.setAttribute("data-dpo-style",""),o.setAttribute("data-plugin",ae),o.textContent=he,document.head.appendChild(o),()=>o.remove()},"pomodoro-overlay: styles"),e.slots.inject("shell.overlay",()=>e.slots.register({name:"shell.overlay",id:ae,order:-100,label:"\u756A\u8304\u949F"},()=>G(qe,{store:n}))),e.slots.inject("settings.section",()=>e.slots.register({name:"settings.section",id:"pomodoro",order:30,label:"\u756A\u8304\u949F"},()=>G(Me,{form:t,store:n})))}
|
|
234
|
+
|
|
235
|
+
return module.exports;
|
|
236
|
+
} });
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# 技术方案(DSH 0.2.1-rc.2)
|
|
2
|
+
|
|
3
|
+
## 结论
|
|
4
|
+
|
|
5
|
+
采用**浏览器端计时 + DSH 全局 overlay + DSH 原生插件配置**。不修改 DSH 本体,不启动额外服务,不把计时状态放到模型会话或 TODO 板中。周期配置由 Host ConfigForm 持久化,运行状态才放浏览器 localStorage。
|
|
6
|
+
|
|
7
|
+
## 接口证据
|
|
8
|
+
|
|
9
|
+
1. **客户端打包格式**:DSH client bundle 通过 `window.__ModuleLoader__.load({ id, factory })` 注册;`require('react')` 由宿主提供。现有插件 `dsh-todo-board/client/client.js:1-2,18-19` 使用该格式。
|
|
10
|
+
2. **全局 UI 挂载点**:`ctx.slots.inject('shell.overlay', ...)` 后调用 `ctx.slots.register(...)`;现有插件 `dsh-todo-board/client/client.js:3644-3649` 是直接范例。
|
|
11
|
+
3. **原生配置与设置页**:Host 用 Schemastery 声明七个 `.volatile()` 字段(一个字体、两个时长、两个颜色、两个不透明度);客户端以 `ctx.configForms.get('pomodoro-overlay')` 读取/订阅,`mutate` 一次原子保存七个字段并携带 revision。设置 UI 注册到 `settings.section`,`id: 'pomodoro'`、`order: 30`、`label: '番茄钟'`。依据:`dsh-settings/README.zh.md:10-13,28-40`、`dsh-client-ui-settings/lib/types/client/config-form-types.d.ts:5-70`、`dsh-client-ui-settings/lib/types/client/contract/slots.d.ts:62-77`,以及 `dsh-client-ui-agent-preset/lib/client.js:2455-2462` 的原生注册范例。配置 ready 前不得启动计时。
|
|
12
|
+
4. **聊天区域锚点**:Conversation 根节点带 `data-conversation-content`、`data-conversation-region="chat"`,输入区带 `data-composer-seat`、`data-conversation-region="composer"`。DOM 结构见 `dsh-client-ui-conversation/lib/client.js:17855-17876`。
|
|
13
|
+
5. **样式生命周期**:样式标签标 `data-plugin="@tom2012/dsh-pomodoro-overlay"`,在 `ctx.effect` 内挂载并返回清理函数,避免 HMR 时误删或被误删;参考 `dsh-todo-board/client/client.js:3607-3619`。
|
|
14
|
+
6. **包激活**:包的 `cordis.patch.yml` 使用 `insert` 增加一个插件服务;`package.json` 的 `dsh.bundle.patch` 指向它。`dsh.client` 声明 `platform: 'web'`、`immediately: true`,并通过 `dsh.client.inject` 注入 layout 与 settings 客户端依赖;`shell.overlay` 和 `settings.section` 则在运行时通过 `ctx.slots.register` 注册。
|
|
15
|
+
|
|
16
|
+
## 布局算法
|
|
17
|
+
|
|
18
|
+
- 在 `shell.overlay` 渲染一个 `position: fixed` 根节点。
|
|
19
|
+
- 从可见的 `[data-conversation-content][data-conversation-region="chat"]` 中选择面积最大的主聊天区,避免误选嵌入式小聊天区。
|
|
20
|
+
- 读取聊天区矩形;若内部存在 `[data-composer-seat]`,倒计时区域底边取 `min(聊天区底边, 输入区顶边)`,因此不覆盖输入框。
|
|
21
|
+
- 侧栏和顶部导航不在聊天区矩形内,自然不会被覆盖。
|
|
22
|
+
- 仅在聊天区内存在 `[data-chat-flow]` 时显示;selector 缺失、Chat 非活动或矩形异常时 fail-closed 隐藏,绝不退化成全屏遮挡。
|
|
23
|
+
- 用 `ResizeObserver`、窗口 resize、捕获阶段 scroll 和受节流的 MutationObserver 保持位置准确;字体配置变化时也重新测量数字宽度,避免字宽差异导致裁切。
|
|
24
|
+
- 整个聊天 overlay 均为 `pointer-events: none`,不渲染可见阶段标签、工具条或控制按钮;仅显示大数字,屏幕阅读器状态保留,设置页表单保持正常交互。
|
|
25
|
+
- `data-*` 标记属于当前 DSH 内部 DOM,并非稳定选择器 API;本方案绑定历史 GUI 基线使用的 DSH 版本,升级后必须重跑布局验证。
|
|
26
|
+
|
|
27
|
+
## 文件结构
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
dsh-pomodoro-overlay/
|
|
31
|
+
├── .gitignore
|
|
32
|
+
├── package.json
|
|
33
|
+
├── package-lock.json
|
|
34
|
+
├── pnpm-workspace.yaml
|
|
35
|
+
├── cordis.patch.yml
|
|
36
|
+
├── LICENSE
|
|
37
|
+
├── lib/
|
|
38
|
+
│ └── index.js # 原生配置 schema 与设置策略
|
|
39
|
+
├── src/
|
|
40
|
+
│ ├── timer-core.js # 纯计时状态机
|
|
41
|
+
│ ├── browser-store.js # localStorage、跨标签页与订阅
|
|
42
|
+
│ ├── chat-geometry.js # 聊天区识别与 composer 扣除
|
|
43
|
+
│ ├── font.js # 本机字体候选与安全 CSS 字体栈
|
|
44
|
+
│ ├── client.js # React overlay、slots 与 DOM 锚定
|
|
45
|
+
│ ├── settings.js # DSH 设置窗口中的七字段表单与草稿预览
|
|
46
|
+
│ └── styles.js # 主题令牌与响应式样式
|
|
47
|
+
├── client/
|
|
48
|
+
│ └── client.js # 构建后的 DSH 浏览器 bundle
|
|
49
|
+
├── tests/
|
|
50
|
+
│ ├── timer-core.test.js
|
|
51
|
+
│ ├── browser-store.test.js
|
|
52
|
+
│ ├── chat-geometry.test.js
|
|
53
|
+
│ ├── client-bundle.test.js
|
|
54
|
+
│ ├── font.test.js
|
|
55
|
+
│ └── host-config.test.js
|
|
56
|
+
├── tools/
|
|
57
|
+
│ ├── build.mjs
|
|
58
|
+
│ └── check-package.mjs
|
|
59
|
+
├── docs/
|
|
60
|
+
│ ├── design.md
|
|
61
|
+
│ ├── architecture.md
|
|
62
|
+
│ └── verification.md
|
|
63
|
+
└── README.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 状态与配置
|
|
67
|
+
|
|
68
|
+
- npm 包、Host 插件服务、浏览器 ModuleLoader 与样式生命周期标识均为 `@tom2012/dsh-pomodoro-overlay`;Cordis patch ID 和 ConfigForm ID 保持为 `pomodoro-overlay`,浏览器存储键保持为 `dsh-pomodoro-overlay:state:v1`,因此作用域迁移不改变既有配置与时间线的读取键。
|
|
69
|
+
- DSH 原生配置:`{ fontFamily, focusMinutes, breakMinutes, focusColor, breakColor, focusOpacity, breakOpacity }`,由 ConfigForm 读取、跨页面订阅和原子写入。字体默认等线;不透明度在 profile 中为 0–1,在设置 UI 中为 0–100%。
|
|
70
|
+
- `dsh-pomodoro-overlay:state:v1`:当前阶段、是否运行、阶段开始时间、绝对截止时间、阶段时长、暂停剩余时间和更新时间。
|
|
71
|
+
- 客户端收到 `status === 'ready'` 的有效配置后才初始化 store;之后配置变更通过订阅送入同一 store。
|
|
72
|
+
- 显示 tick 不写入 localStorage;只有初始化、内部状态机操作、自然阶段切换和外部状态同步才写入。
|
|
73
|
+
- 所有运行显示都读取当前时间,不在 tick 中做 `remaining -= 1`。
|
|
74
|
+
- 配置修改不重建当前阶段;状态机在下一次阶段切换时读取新时长,overlay 则在保存后立即读取新字体、颜色与不透明度。
|
|
75
|
+
- 旧版 `running: false` 快照会原样恢复并保持暂停;当前聊天区没有继续按钮。需要重新开始时,删除该插件的 localStorage 运行状态键并刷新,即可按当前配置新建运行中的专注阶段;DSH 配置不受影响。
|
|
76
|
+
- 阶段过期追赶先最多进行两次逐边界切换;若仍然过期且存在完整过去周期,则按完整「专注 + 休息」周期做算术跳跃,必要时再逐边界切换一次以落到当前阶段,避免长时间关闭后逐分钟循环。
|
|
77
|
+
|
|
78
|
+
## 构建与测试
|
|
79
|
+
|
|
80
|
+
- 浏览器客户端零 npm 运行时依赖,React 由 DSH 宿主提供;Host 使用 `@deepseek-ai/schemastery` 声明配置。
|
|
81
|
+
- `esbuild` 仅作开发依赖,把 `src/` 打成单个 client bundle,React 保持 external。
|
|
82
|
+
- Node 内置 `node:test` 的六个测试文件共 38 项测试,覆盖计时核心、浏览器存储、聊天区几何、Host 配置、字体栈,以及构建后客户端加载、store / slots 和部分清理的有限冒烟;完整范围见 `docs/verification.md`。
|
|
83
|
+
- `tools/check-package.mjs` 验证入口、patch 与 bundle 的静态约束,不做逐字新鲜度比较;`npm run build` 负责重建产物,另可运行 `node tools/build.mjs --check` 逐字检查产物是否过时。`npm run verify` 先构建再执行静态检查和测试。
|
|
84
|
+
- 通过 `dsh plugin --profile web add @tom2012/dsh-pomodoro-overlay` 从 npm 安装,或通过 `dsh plugin --profile web add <本地目录>` 从源码安装。Host schema 变更后必须由用户在**外部 Terminal** 重载同一 DSH Web 服务,再在既有 URL 做 GUI 验证;禁止从 DSH 会话内部终止自己的宿主进程。
|
package/docs/design.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# DSH Pomodoro Overlay — 设计说明
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
为 DeepSeek Harness Web 设计一个低干扰、自动运行的番茄钟插件:
|
|
6
|
+
|
|
7
|
+
- 新计时或已运行的计时无需点击即自动进入「专注 → 休息 → 专注」循环;旧版暂停快照按原状态保留,不自动恢复运行。
|
|
8
|
+
- 在 DSH 设置窗口的独立「番茄钟」页配置时钟字体,以及两种阶段的时长、颜色与数字不透明度。
|
|
9
|
+
- 倒计时铺满当前聊天消息区域,但不覆盖侧栏、顶部导航和底部输入框。
|
|
10
|
+
- 默认字体为等线(DengXian),专注为浅灰 10% 不透明度,休息为红色 30% 不透明度;用户可保存自定义外观。
|
|
11
|
+
|
|
12
|
+
## 界面范围与层级
|
|
13
|
+
|
|
14
|
+
“铺满对话框”定义为聊天消息区域,而非整个浏览器窗口。大数字居中并以不裁剪为前提尽量放大,保持数字原有长宽比。
|
|
15
|
+
|
|
16
|
+
倒计时是低干扰装饰层:
|
|
17
|
+
|
|
18
|
+
- 整个聊天 overlay 均为 `pointer-events: none`,不阻断消息选择、滚动、链接和输入操作。
|
|
19
|
+
- 聊天区不提供暂停、跳过、重置或工具条,也不显示“专注 / 休息”可见阶段标签;只保留大倒计时。
|
|
20
|
+
- 设置页控件使用 DSH 主题语义令牌,保证正文、焦点和控件对比度。
|
|
21
|
+
- 不用 emoji 作为结构图标;设置页优先文字按钮或内联 SVG。
|
|
22
|
+
- 不用闪烁、缩放或持续脉冲动画。阶段切换仅允许短暂颜色过渡,并尊重 `prefers-reduced-motion`。
|
|
23
|
+
|
|
24
|
+
## 视觉令牌
|
|
25
|
+
|
|
26
|
+
| 角色 | 实现方向 |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| 专注大数字 | 默认 `#9ca3af` + 10% 不透明度;颜色与不透明度均可配置 |
|
|
29
|
+
| 休息大数字 | 默认 `#ef4444` + 30% 不透明度;颜色与不透明度均可配置 |
|
|
30
|
+
| 字体 | 默认等线(DengXian),支持自定义单本机字体名称与常用候选;无远程字体请求,保留 `font-variant-numeric: tabular-nums` |
|
|
31
|
+
| 数字尺寸 | 根据聊天区实际宽高和文本测量结果自适应;可用宽度为区域宽度减 32px,可用高度为区域高度减 64px,两者最低均按 32px 计算 |
|
|
32
|
+
| 控件 | 使用 `--dsw-alias-*` 背景、边框、正文和品牌色令牌 |
|
|
33
|
+
| 动效 | 默认克制;减少动态效果偏好下取消非必要过渡 |
|
|
34
|
+
|
|
35
|
+
大数字按用户要求保持半透明,因此它被当作装饰性视觉信息,不作为唯一的无障碍信息来源。屏幕阅读器仍可获取阶段名称和剩余时间,设置页控件使用正常对比度;剩余时间不以 `aria-live` 每秒播报,避免读屏干扰。
|
|
36
|
+
|
|
37
|
+
## 状态与交互
|
|
38
|
+
|
|
39
|
+
默认配置:字体等线;专注 25 分钟 / `#9ca3af` / 10% 不透明度;休息 5 分钟 / `#ef4444` / 30% 不透明度。
|
|
40
|
+
|
|
41
|
+
- **首次启动**:没有有效快照时,原生配置 ready 后按默认 25 分钟开始专注阶段;已保存快照按原状态恢复。
|
|
42
|
+
- **自然结束**:运行中的计时自动切换到另一阶段,无限循环。
|
|
43
|
+
- **纯展示**:聊天区不提供暂停、继续、跳过或重置入口;运行中的阶段按时间线推进,旧版暂停快照保持暂停。
|
|
44
|
+
- **修改配置**:当前计时不打断;字体、颜色和不透明度保存后立即生效,新时长从下一阶段生效。
|
|
45
|
+
- **刷新、切标签页、浏览器休眠**:运行中显示值由 `Date.now()` 与绝对截止时间计算;暂停时使用保存的剩余时间。二者都不依赖 `setInterval` 累减。
|
|
46
|
+
- **关闭后重开**:恢复持久化状态;运行中已过期的阶段按时间线推进到当前应有阶段,暂停快照不自动推进。
|
|
47
|
+
- **多标签页**:通过 `storage` 事件同步运行或暂停状态,各标签页不分别累计计时。
|
|
48
|
+
|
|
49
|
+
## 配置约束
|
|
50
|
+
|
|
51
|
+
- 专注时长:整数,1–240 分钟,默认 25。
|
|
52
|
+
- 休息时长:整数,1–240 分钟,默认 5。
|
|
53
|
+
- 两种阶段颜色:六位十六进制 `#RRGGBB`,使用原生颜色选择器并显示色值文本。
|
|
54
|
+
- 两种阶段数字不透明度:0–100% 整数滑杆,显示当前百分比;0% 完全透明,100% 不透明。
|
|
55
|
+
- 时钟字体:默认等线(DengXian),两种阶段共用;允许 1–80 字符的单个本机字体名称,不接受引号、逗号或 CSS 声明,未安装时回退本机字体栈。
|
|
56
|
+
- 设置位于 DSH 设置窗口独立「番茄钟」页;七个字段一次原子保存并携带 revision。
|
|
57
|
+
- 草稿预览只影响设置页,保存前不得改变聊天区;配置保存应有明确反馈,非法输入不能污染运行状态。
|
|
58
|
+
- 超长阶段仍使用“分钟:秒钟”格式(例如 `120:00`),数字尺寸随字符数重新适配。
|
|
59
|
+
|
|
60
|
+
## 验收场景
|
|
61
|
+
|
|
62
|
+
1. 首次安装启动后自动开始,不需要额外点击。
|
|
63
|
+
2. 默认配置下,工作阶段为浅灰半透明,休息阶段为红色半透明;不显示可见阶段文字,屏幕阅读器仍可获取阶段。
|
|
64
|
+
3. 大数字仅覆盖聊天消息区,不遮挡侧栏和输入框,不阻断消息操作。
|
|
65
|
+
4. 修改周期后当前阶段不被打断,下一阶段采用新值。
|
|
66
|
+
5. 聊天区不显示暂停、跳过、重置或工具条,且 overlay 全部鼠标穿透。
|
|
67
|
+
6. 运行中的计时在页面切到后台再返回、刷新页面后,剩余时间与实际经过时间一致;暂停快照仍显示保存的剩余时间。
|
|
68
|
+
7. 模拟运行中的一次性跨阶段,恢复到当前正确阶段,不重复补发历史提醒;暂停快照不被自动推进。
|
|
69
|
+
8. 两个标签页中的阶段、剩余时间和运行或暂停状态通过 `storage` 事件同步。
|
|
70
|
+
9. 浅色、深色、窄屏和减少动态效果模式下均可用;设置页表单可键盘操作。
|
|
71
|
+
10. DSH 设置窗口出现独立「番茄钟」导航;七个字段可键盘编辑,草稿预览正确,保存后外观立即应用且当前 `endsAt` 不变;字体切换后数字重新适配且不裁切。
|
|
72
|
+
11. 插件卸载或 HMR 清理后,计时器、事件监听器、样式和 DOM 均被释放。
|