dsh-theme-endfield 0.0.0-stage → 1.2.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.
@@ -0,0 +1,295 @@
1
+ # 设置项与功能
2
+
3
+ 设置项的行为、默认值、字段名与边界情况。设计取值见 [design-language.md](design-language.md),实现细节见 [engineering-notes.md](engineering-notes.md)。
4
+
5
+ 设置入口:**设置 › 终末地主题设置**。全部文案跟随 DSH 的语言设置(中/英)即时切换,无需刷新。
6
+
7
+ ## 持久化方式(不再用 localStorage)
8
+
9
+ 历史内存风格:这些开关最初存浏览器 `localStorage`。由于浏览器存储按「协议 + 主机 + 端口」的 origin 隔离,而 DSH Desktop 每次启动都在 127.0.0.1 绑定一个**随机临时端口**,端口一变 origin 就变,上次保存的设置永远读不到,表现为「重启后恢复默认」。已改用 DSH 官方的用户设置命名空间:
10
+
11
+ - **DSH 0.2.0-rc.2 / 0.1.7-rc.1 及以后(当前)**:**Host 端(index.js)** 导出 schemastery `Config`,31 个字段全部 `.volatile()`——这两代都只把 volatile 字段投影成可编辑表单;命名空间就是本插件在 `cordis.patch.yml` 里那一行的 profile entry id(`theme-endfield`)。**浏览器端(client.js)** 用 `ctx.configForms.get('theme-endfield')` 读/写/订阅,值由 DSH 的设置服务写进 profile patch `<profile>/cordis.patch.yml`。另外 Host 会调用 `ctx.settings.configure({ auto: false }, ctx.fiber)` 告诉 DSH 本插件自带设置页,不要再自动生成一份。0.2 的这套接缝与 0.1.7 完全相同,本插件侧不需要改动;0.2 真正改掉的是三个**应用侧**细节(回合状态标签的类名与着色机制、右侧栏列名、插件卡片文案来源),见 [engineering-notes.md § DSH 0.2.0-rc.2](engineering-notes.md#dsh-020-rc2-上失效的三处应用侧钩子v116-已跟进)。
12
+ - **≤ 0.1.5-rc.2(旧宿主,仍兼容)**:Host 通过 `ctx.settings.register('dsh-theme-endfield', schema)` 声明命名空间,由 `@deepseek-ai/dsh-settings-file` 落到 `<dshHome>/settings.yaml`;浏览器端用 `ctx.settingsScope` 的 `bind({ namespace, decode })` 读写。client 在找不到 `configForms` 时自动回落到这条路径。
13
+
14
+ 两代的落盘位置都由 DSH 决定(`$DSH_HOME` 或 `~/.dsh/...`),与浏览器 origin/端口无关,因此在 **dsh web(浏览器、固定/默认端口)** 和 **DSH Desktop(随机临时端口)** 两种运行方式下设置都能正确持久化——它们跑的都是 127.0.0.1 loopback 页面,DSH 会把连接解析为 `host` 持久化模式。
15
+
16
+ > **升级提示**:0.1.7-rc.1 废弃了 `settings.yaml`(启动时改名为 `settings.yaml.imported`),且其内置迁移只认少数几个段落名,旧的 `dsh-theme-endfield` 段落不在其中——**升级后请在「设置 › 终末地主题设置」里重设一次**。旧值仍可在 `settings.yaml.imported` 中手工对照。详见 [engineering-notes.md § DSH 0.1.7-rc.1 换掉了整套 settings API](engineering-notes.md#dsh-017-rc1-换掉了整套-settings-api-v110-已跟进)。
17
+
18
+ 下表「字段」(缩写)就是存储里的字段名:0.1.7-rc.1 起是 profile patch 里 Config 的 volatile 路径,旧世代是 `settings.yaml` 段落里的键,两者同名(都等价于旧 localStorage 键名的尾部)。
19
+
20
+ > **字段名是 camelCase,且必须与 Host schema 逐字一致**(复合字段尤其:`contourAnim` 而非 `contour-anim`)。字段名对不上时,值会**静默**落在没人读的地方(旧世代是 `settings.yaml` 里多一行未声明键;0.1.7 起是对非 volatile/未声明路径的写入被 schema 拒绝),声明字段仍是默认——表现为「开关刷新后复位」。成因、影响面与旧存档的迁移见 [engineering-notes.md § 存储字段名](engineering-notes.md#存储字段名必须来自-schema不能用去掉前缀推出来issue-15),回归测试见 [testing.md](testing.md#设置页)。
21
+
22
+ ## 总览
23
+
24
+ | 组 | 开关 | 默认 | 命名空间字段 |
25
+ | --- | --- | --- | --- |
26
+ | 01 主题 | 终末地主题 | 开 | `enabled`(旧键 `dsh-theme-endfield-enabled`)|
27
+ | | 主题配色 | 谷地黄 | `palette` |
28
+ | | 主题圆角 | 直角 | `radius` |
29
+ | | 磨砂玻璃 | 关 | `glass` |
30
+ | 02 背景 | 等高线背景 | 关 | `contour` |
31
+ | | 动态等高线 | 开 | `contourAnim` |
32
+ | | 鼠标轨迹 | 关 | `contourTrail` |
33
+ | | 等高线绘制 | Canvas | `contourRenderer` |
34
+ | | 动态帧率 | 24 FPS | `contourFps` |
35
+ | | 动态速度 | 标准 | `contourSpeed` |
36
+ | | 滚动暂停 | 开 | `contourScrollPause` |
37
+ | | 背景水印 | 开 | `watermark` |
38
+ | | 水印保持显示 | 关 | `watermarkPersist` |
39
+ | 03 动画 | 输出滚动动画 | 开 | `scrollAnim` |
40
+ | | 动画强度 | 标准 | `scrollAnimLevel` |
41
+ | | 启动加载动画 | 关 | `loader` |
42
+ | 04 娱乐 | 雷霆大字 | 关 | `thunder` |
43
+ | | 大字入场动画 | 关 | `thunderAnim` |
44
+ | | 顶部余额胶囊 | 关 | `balanceCapsule` |
45
+
46
+ 开启后默认显示 DeepSeek API 钱包余额与峰谷时段;会话使用 dsh-codearts-auth 渠道模型时自动切换为该渠道的剩余额度与积分消耗(详见「04 娱乐 › 顶部余额胶囊 › 渠道额度模式」)。
47
+ | 05 音频 | 音频通知 | **关** | `audioEnabled` |
48
+ | | 启动加载动画音 | 开 | `audioBoot` |
49
+ | | 任务开始音 | 开 | `audioTurnStart` |
50
+ | | 任务结束音 | 开 | `audioTurnDone` |
51
+ | | 需要你回应 | 开 | `audioAttention` |
52
+ | | 出错提示音 | 开 | `audioTurnFail` |
53
+ | | 音量 | 100 | `audioVolume` |
54
+ | | 同槽位最小间隔 | 2500 ms | `audioDebounceMs` |
55
+ | | 开始音仅认会话框 | 开 | `audioHumanOnly` |
56
+ | | 自定义音效目录 | (空) | `audioSoundDir` |
57
+ | | 诊断日志 | 关 | `audioDiag` |
58
+
59
+ 取值约定(与 localStorage 时代的极性完全一致,现在由 schema 默认值保证):**默认开启**的开关在该 schema 里默认存 `'1'`,客户端读作 `!== '0'`;**默认关闭**的存 `'0'`,读作 `=== '1'`。回滚/清除字段时客户端回到 schema 默认值;异常存储值被 schema 校验拒绝,不会悄悄改变出厂观感。
60
+
61
+ ---
62
+
63
+ ## 01 主题
64
+
65
+ ### 终末地主题(总开关)
66
+
67
+ 关闭后移除令牌层与样式表,界面回到应用原生外观,无需刷新。它同时是**所有其他功能的总闸**:关闭会一并拆掉水印、等高线、大字与订阅。
68
+
69
+ **关闭后设置页仍保持可读。** 这一点需要专门处理:开关按钮的强调色底来自主题自己的样式表,样式表随主题关闭而移除,所以关闭时按钮回退到应用原生令牌(选中态实心底、常态透明描边),而不是留下「黑字落在深色面板上」的不可读状态。
70
+
71
+ ### 主题配色
72
+
73
+ 在**谷地黄**(默认,`#fff500`)与**武陵青**(`#14d0d0`)之间切换。设置行带一个色块预览,而不只是写颜色名。
74
+
75
+ 切换只是 `<body>` 上加减一个 class,不重新注册令牌层、无 JS 重绘。唯一的例外是等高线画布(canvas 描边不能是 CSS 变量),由 `MutationObserver` 监听 class 变化触发重绘——顺带让「别的标签页改了配色」也能跟着变。
76
+
77
+ ### 主题圆角
78
+
79
+ 直角(默认)/ 圆角。同样是一个 class:`body.theme-endfield-round` 让整组 `border-radius: 0` 规则失效,恢复应用原生圆角。
80
+
81
+ ---
82
+
83
+ ## 02 背景
84
+
85
+ ### 等高线背景(默认关闭)
86
+
87
+ 参考等高线地形图:整屏铺满强调色细线,闭合环线相互嵌套成大小不一的「岛屿」。
88
+
89
+ 线不是贴图也不是手绘路径,而是**标量场的真等值线**——因为动画的连贯性来自场本身:场变形后重新提取,环线会像真实地形一样合并、分裂,平移一张贴图做不到这件事。
90
+
91
+ - **场** = 若干正负相间的高斯凸起之和(既有山峰也有洼地)+ 三道极缓的长波正弦
92
+ - **线** = marching squares 在 21 个等间距高度上取等值线
93
+ - **连线** = 线段按边 ID 缝合成连续折线,再以二次曲线绘制
94
+
95
+ **密度随视口自适应**:上面的 21 级 / 22 岛是按桌面窗口调的,按「每面积」施加在窄窗口就会读作一堆乱线。短边低于 640px 时,层级数向 10、岛屿数向 12 随宽度线性递减(320px 触底),线距随之拉开;**短边 ≥640px 逐字节回到出厂密度**,桌面输出不变。覆盖率验证器(accept-or-reroll)按「该视口实际将画的层级数」评分,小窗的无空白区保证不变。见 `contourLevelsFor` / `contourBumpsFor`。
96
+ 算法细节与实测数据见 [engineering-notes.md § 等高线](engineering-notes.md#等高线背景).
97
+
98
+ **图层挂在应用外框内部**,`inset:0; z-index:0`,落在「外框底色之上、所有定位子元素之下」。正文永远在图层之上,前景对比度不受影响。挂载期间会把外框、对话列、右侧栏列(0.1.x 叫详情列)与侧栏的不透明底色置为透明(`:has()` 守卫使功能关闭时全部规则失效)。
99
+
100
+ **关闭时零逐帧开销**——不是在 rAF 里空转再提前返回,而是整个循环停掉。
101
+
102
+ ### 动态等高线(默认开启,需先开启等高线背景)
103
+
104
+ 等高线流动变形。动态帧率提供 `24 FPS`、`60 FPS`、`120 FPS` 三档,默认 `24 FPS`;动态速度提供 `1x`、`2x`、`4x` 三档,分别对应慢速、标准、快速,默认 `2x`。两者写入 schema 字段 `contourFps` / `contourSpeed`(UI/存储键分别是 `dsh-theme-endfield-contour-fps` / `dsh-theme-endfield-contour-speed`,由 `PREFS_KEY_TO_FIELD` 映射;旧世代落盘时用的是 UI 键的尾部拼写),非法值自动回退到默认值。关闭则为静态图案,不做任何逐帧计算。
105
+
106
+ **尊重系统「减少动态效果」**:`prefers-reduced-motion` 下图案照常渲染(静态纹理不算动效),但场变形不启动——即使开关是开的。此时设置行会说明是系统偏好在生效,而不是让开关看起来失灵。
107
+
108
+ ### 背景水印(默认开启)
109
+
110
+ `ENDFIELD` 大字水印,默认只在**新建会话页**(hero)显示,居中跟随标题。
111
+
112
+ - `pointer-events: none`,不拦截点击与文本选择;
113
+ - 强度按配色分别声明为 CSS 变量 `--edge-wm-alpha`(亮 `0.13` / 暗 `0.085`),配色切换时自动重解析;
114
+ - 深色模式取更低的 alpha:在近黑底上**叠加**亮度,比在纸底上**减去**亮度显得响得多。
115
+
116
+ **防浏览器误翻译**:字形由 CSS `content`(`::before`)绘制,元素本身没有任何 DOM 文本节点,逐文本节点遍历的翻译器看不到它;另加 `translate="no"`、`class="notranslate"`、`lang="en"`、`aria-hidden="true"`。
117
+
118
+ ### 水印保持显示(默认关闭,需先开启背景水印)
119
+
120
+ 开启后,对话页等非新建会话页面也显示水印。
121
+
122
+ 此时水印挂载在**会话列内部**,以 `z-index: -1` 位于正文**之下**(该列在水印挂载期间获得 `isolation: isolate` 与 `position: relative`,卸载后自动还原)。之所以不挂 `<body>`:一个 fixed 的 body 子节点会画在消息文字**之上**,把用户正在读的内容洗白。
123
+
124
+ ---
125
+
126
+ ## 03 动画
127
+
128
+ ### 输出滚动动画(默认开启)
129
+
130
+ 模型流式输出时,应用会把对话栏**整段钉到底部**——一次一大格,中间没有过渡帧。这个开关把那一步摊成逐帧滑动:流式输出看起来是「滑」上去的,而不是「跳」上去的。
131
+
132
+ **它不改动应用自己的滚动位置。** 补偿只施加在对话栏的 `translateY` 上,随后逐帧衰减归零,因此应用读回的滚动位置分毫未变、它对「读者是否还在跟随」的判断不受影响,跟随不会中断。这也是它默认开启的原因:升级到带本功能的版本不会破坏任何既有行为,只是输出变顺滑。
133
+
134
+ 选型、实测数字与那条 `scroll-behavior: smooth` 否决见 [engineering-notes.md § 输出滚动动画](engineering-notes.md#输出滚动动画为什么不能改-scrolltop-的写后回读语义)。
135
+
136
+ **动画强度**(`soft` / `standard` / `snappy`,默认 `standard`)是同一个衰减曲线上的三个速度,选的是**顺滑与滞后的取舍**,不是三个不同的效果:
137
+
138
+ | 档位 | 观感 | 单步上限 |
139
+ | --- | --- | --- |
140
+ | 柔和 | 最顺滑,最新一行离底部最远 | 420px |
141
+ | 标准(默认) | 出厂档位:平滑与跟随之间的折中 | 300px |
142
+ | 轻快 | 最跟手,平滑幅度最小 | 200px |
143
+
144
+ 「单步上限」是**安全检查而非观感旋钮**:一次位移超过它的步进(恢复会话、跳到一个回合这类)按原样瞬时呈现。真实代价是**一次落一大块**时可能落空——一个几百 px 的代码块或工具结果在一步里落地,在 `标准` 下会退回硬跳(实测 400px:`标准` 瞬时、`柔和` 仍平滑)。把这一步摊成动画意味着内容要从 300px 以下滑上来三分之一秒,那比它要替代的硬跳更显眼,所以上限就设在这里;想让它也平滑就选 `柔和`,三个档位因此不是三份观感而是三个位置。
145
+
146
+ 滞后是这套做法的固有代价,不是可调掉的缺陷,而且**它取决于流速**:实测在真实流式速率(每 250ms 一行 24px,约 `96px/s`)下最新一行只落后视口底约 `3px`(不足一行行高);只有在刻意夸张的 `1600px/s` 合成速率下才会到约 `110–160px`。关闭该功能时两个速率都是 `0px`。三个档位只是在同一根「顺滑 ↔ 滞后」旋钮上取三个点。
147
+
148
+ **这些情形一律保持瞬时**,不做任何补偿:向上滚动、从阅读位置跳转(打开历史会话、跳到一个回合)、单步位移超过该档上限、用户自己的滚轮 / 触摸 / 指针 / 键盘手势,以及页面处于隐藏状态。
149
+
150
+ **展开思考 / 工具详情这类布局重排也一律瞬时**:折叠区展开会让整列在数百 px 量级上变高,应用的 `ResizeObserver` 随之重钉底——这一步在几何上和流式增长几乎一样(同样从底部出发、同样向下、同样可能小于上限),但**屏幕上既有内容其实没有被移动**,所以补偿它只会造出一次多余的下坠再滑回来。运行时因此不假设「滚动量就是视觉位移」,而是实测布局自己贡献了多少再相减(详见 [engineering-notes.md § 布局重排](engineering-notes.md#布局重排展开思考块为什么抽搐))。
151
+
152
+ **尊重系统「减少动态效果」**:`prefers-reduced-motion` 下完全不补偿,输出回到原生硬滚动——即使开关是开的。此时设置行会说明是系统偏好在生效,而不是让开关看起来失灵。系统偏好在运行中翻转也会即时生效并清掉残留位移。
153
+
154
+ **关闭即零开销**:关掉时不留监听、不留逐帧循环,位移立即释放。
155
+
156
+ 回归测试见 [testing.md § 输出滚动动画](testing.md#输出滚动动画)。
157
+
158
+ ### 启动加载动画(默认关闭)
159
+
160
+ 参考终末地官方主视觉:整屏黑底,左边缘 10px 强调色进度轨自上而下填充。**刻度 + 百分比 + 状态行跟随进度轨的填充端一起下移**,并做了下边界钳制,接近 100% 时不会滑出屏幕。
161
+
162
+ 播放时序(开启后每次刷新页面播放一次):
163
+
164
+ | 阶段 | 时长 | 表现 |
165
+ | --- | --- | --- |
166
+ | 进度 | 约 1750ms(`easeOutCubic`) | 强调色进度轨自上而下填充,读数随之下移 |
167
+ | 横向铺满 | 520ms | 到 100% 后进度轨从 10px 横向展开成整屏 |
168
+ | 整屏淡出 | 620ms | 连同整块遮罩淡至透明,节点随即从 DOM 移除 |
169
+
170
+ 设置行提供「预览」按钮,可不刷新页面直接重播一次。
171
+
172
+ **海报式排版**:品牌块**内部左对齐**(各行共用同一条左侧节奏线)但整体**摆在画面右侧**——这个组合才是参考图的观感。`END` / `FIELD` 上下两行紧排堆叠,不含中文字标。字标尺寸 `clamp(26px, min(5.2vh, 4.8vw), 64px)`,取较小轴,因此无论窗口「矮」还是「窄」品牌块都不会挤进左侧进度轨。已在 520×900 到 2560×1440 共 13 种视口逐一验证无溢出。
173
+
174
+ 工程要点:
175
+
176
+ - **进度由墙钟时间推导**(非逐帧累加),不会漂移;
177
+ - **双时钟驱动**:`requestAnimationFrame` 负责平滑更新,`setInterval` 兜底——rAF 在后台标签页与无头渲染器中会在首帧后停摆,仅靠它会让全屏遮罩永久留在屏幕上;
178
+ - **收尾动画也由 JS 驱动,不用 CSS transition**(原因见工程笔记);
179
+ - **硬性保险丝**:一个独立 `setTimeout` 终点,即使所有时钟停摆也必定移除遮罩;
180
+ - `prefers-reduced-motion` 下直接跳过铺满与淡出,立即移除遮罩;
181
+ - `pointer-events: none`:动画进行中也不拦截点击;
182
+ - 全部字形由 CSS `content` 绘制 + `translate="no"`,防浏览器翻译改写品牌名。
183
+
184
+ `z-index: 2147483000` —— 加载屏是唯一有资格独占整屏的表面。
185
+
186
+ ---
187
+
188
+ ## 04 娱乐
189
+
190
+ ### 雷霆大字(默认关闭)
191
+
192
+ 任务开始 / 结束时,在屏幕正中用白色粗体大字打出「任务开始」/「任务完成」,**3 秒后自动隐藏**,**期间点击屏幕任意处可立即关闭**。设置行提供「预览」按钮。
193
+
194
+ **状态信号取自唯一权威位**:`@deepseek-ai/dsh-client-runtime` 的 `ConversationSnapshot.running`——应用自己的回合状态标签与停止按钮切换的就是这个字段。不去监听转圈图标或类名:那些类名带构建哈希、并非契约,而且「出现了一个 spinner」和「一个回合开始了」并不是同一件事。
195
+
196
+ **只认边沿,不认电平。** 一个回合进行中,快照存储会以每秒几十次的频率推送;只有 `false → true` / `true → false` 的跳变才播报。每个会话**第一个可读到的值记为基线并保持静默**,因此「切进一个已经在跑的会话」不会误报「任务开始」——但该会话结束时仍会正常播报「任务完成」。
197
+
198
+ **点击关闭的监听挂在 `document` 的捕获阶段**,遮罩本身保持 `pointer-events: none`:
199
+
200
+ - 一次按下**同时**完成两件事——关掉大字,并正常抵达用户真正瞄准的元素;
201
+ - 用 `pointerdown` 而非 `click`:一个事件覆盖鼠标 / 触摸 / 触控笔,且按下即消失;
202
+ - 用**捕获阶段**:菜单、下拉、拖拽手柄常在自己的处理器里调 `stopPropagation`,冒泡阶段听不到那些按下,大字就会关不掉;
203
+ - 提前关闭会同时取消 3 秒定时器并摘掉监听器。
204
+
205
+ 如果改成给遮罩加 `pointer-events: auto` 再挂 `onclick`,它会变成**一块持续 3 秒的全屏点击黑洞**:用户为了关掉它而点下去的那次点击会被吃掉。
206
+
207
+ 层级 `z-index: 2147482000`,比加载遮罩低一档。整块 `aria-hidden`——纯装饰不该被读屏软件念出来。
208
+
209
+ **关闭即零开销**:关掉时不订阅、不留定时器、不建节点。
210
+
211
+ ### 大字入场动画(默认关闭,需先开启雷霆大字)
212
+
213
+ | 状态 | 表现 |
214
+ | --- | --- |
215
+ | **关闭(默认)** | 大字**直接出现**在屏幕正中,保持 3 秒后消失,无缩放、无淡入淡出 |
216
+ | 开启 | 大字由大缩小「砸」入(`scale(2.4) → 0.94 → 1`)并在末尾淡出 |
217
+
218
+ 与「等高线背景 → 动态等高线」是同一种结构:**图层是一个决定,让它动是另一个决定**。
219
+
220
+ 三个刻意的约束:
221
+
222
+ - **子开关只改入场表现,不改时长。** 3 秒由 JS 定时器持有,不挂在动画结束事件上——否则开关动画会顺带改掉停留时间。
223
+ - **未开启动画时,主开关关闭时该行为 disabled。** 一个对当前状态无意义的控件应该看起来就是不可用的。
224
+ - **系统「减少动态效果」仍然压过这个开关。** 两个独立原因(开关关闭、系统偏好)汇合到同一条静态分支;此时设置行会说明是系统偏好在生效。
225
+
226
+ ---
227
+
228
+ ### 顶部余额胶囊(默认关闭)
229
+
230
+ 开启后屏幕顶部悬浮一枚药丸:左边是 DeepSeek API 余额(¥ / $),右边是当前**峰谷时段**——`高峰` / `低谷`、时段剩余与已过百分比、一个圆环时钟,以及开场那帧「品牌 pose」(`/// DEEPSEEK API` + `DeepSeek 当前低谷`)。设置行提供「预览」按钮,按真实开场动画重播一次;没有配置 API Key 时按钮禁用并说明原因。
231
+
232
+ **峰谷规则在本地推算**(Host 不提供排班表):北京时间**周一至周五 9:00-12:00、14:00-18:00 是高峰**,价格为空闲时段的 2 倍。按 DeepSeek 2026-09-19 的「API 峰谷时间补充说明」,**中国法定节假日全天**以及**调休上班的周末**都按空闲计费——后一条天然落在「周末即低谷」上,所以胶囊只要保管一份法定节假日日历(按年抄录国务院通知;未收录的年份退回普通工作日规则,宁可显示高峰也不猜一个假的半价)。
233
+
234
+ 倒计时读的是**同价连续段**,而不是「今天还剩多少」:周五 18:00 到周一 09:00 是一整段 63 小时的低谷,七天国庆是一整段 183 小时,所以读数可能是 `剩余47:20:00`。节假日名只出现在开场 pose 的标题里(`DeepSeek 国庆节低谷`),正式行仍只写两三个字——胶囊宽 300px,多一个字就会撑破它。
235
+
236
+ **窄屏逐级让步**:视口 ≤480px(手机)时不显示「时段剩余hh:mm:ss」,只留 `高峰` / `低谷` 与已过百分比、圆环,并且胶囊同时放下 300px 的宽度地板、按剩余读数自然收紧;再窄到 ≤316px 才把整段灰色读数一并去掉。秒级倒计时是手机上最没人看、也是最占宽度的一串字符,所以它第一个让位。
237
+
238
+ #### 渠道额度模式(dsh-codearts-auth)
239
+
240
+ 当当前会话使用的模型来自 [dsh-codearts-auth](https://www.npmjs.com/package/dsh-codearts-auth)(Jet Hub)的某个渠道(CodeArts、CodeBuddy、WorkBuddy、LobsterAI、Qoder、Qoder CN、TRAE、Cline、Loomy、Raccoon、MiniMax、ZCode、OpenCode)时,胶囊自动切换为**渠道额度**显示,随切换渠道实时跟随:
241
+
242
+ - **显示内容**:`渠道名 剩余额度 单位`——渠道名(如 `TRAE`)小字在前,剩余量沿用插件自己的格式(credit 非整数保留两位小数;token 量纲显示 `12.35K` / `12.35M`),单位尾注 `积分` 或 `Token`。多账号取第一个可用账号的 active 额度包求和。
243
+ - **右侧读数可选已用或剩余**:设置面板「04 娱乐 › 渠道额度读数」可切换右侧百分比的含义——**已用**(默认,`已用xx%` = 已用/总额度,与插件徽章一致)或**剩余**(`剩余xx%` = 剩余/总额度)。右侧圆环与百分比**始终同步**:选「已用」圆环走已用份额,选「剩余」圆环走剩余份额。两种读法都需渠道报出总额度,否则右侧留空、圆环归零——没有总额度的份额无从判断,不编造读数。左侧主数字始终为剩余额度,不受此设置影响。切换在下次取数时生效(不强制额外 RPC,5 分钟取数下限保持诚实)。
244
+ - **右侧进度条显示积分消耗**:圆环时钟与百分比槽位改为 `已用xx%`——`Σ已用 / Σ总额度`,与左侧数字同源同账号。没有总额度信息时百分比留空、圆环归零,不编造读数。
245
+ - **不显示峰谷时段**:高峰/低谷是 DeepSeek API 的计费概念,渠道模式下整段时段读数隐藏。
246
+ - **取数与缓存**:走插件自己的管理 RPC(`usage.badge`),复用宿主侧 120s TTL 缓存(失败缓存 15s);主题侧另有**每渠道 5 分钟的取数下限**——余额是慢变量,胶囊是一眼可读的快照,不值得更密的请求。渠道**切换**绕过下限立即跟随;取数失败 30 秒后即允许重试。同一 `--endfield-balance-sweep` 变量在两种模式间切换含义,模式切换零重排。
247
+ - **失败语义**:渠道未登录、插件缺失或上游失败显示 `--`,绝不变灰成 0;切回 DeepSeek 模型则恢复钱包余额显示(DeepSeek 钱包轮询始终在跑,切回即刻有数)。
248
+
249
+ ---
250
+
251
+ ## 05 音频(默认关闭)
252
+
253
+ 四种提示音,**由宿主进程播放**而不是浏览器:页面最小化、切到别的应用时同样能听到——那恰好是最需要提示的场合。
254
+ 完整文档见 [audio-notifications.md](audio-notifications.md)。
255
+
256
+ | 槽位 | 触发时机 |
257
+ | --- | --- |
258
+ | `audioBoot` 启动加载动画音 | 播放 ENDFIELD 加载板时响一次(只认真正的页面加载,点「预览」重播不响) |
259
+ | `audioTurnStart` 任务开始音 | 从会话框提交指令后 |
260
+ | `audioTurnDone` 任务结束音 | 我产出最终结果后(中途报错、等待审批不响) |
261
+ | `audioAttention` 需要你回应 | 审批请求 / 我的提问 / 计划求批;锚点为面板自己的 `data-*` 属性 |
262
+ | `audioTurnFail` 出错提示音 | **不接事件**:不需要人工干预的错误保持静音,音效与开关只是预留 |
263
+
264
+ **总开关 `audioEnabled` 默认关闭**:音效是选择加入(opt-in)的功能,升级到带本功能的版本不会自己开始出声;
265
+ 打开总开关后,各槽位开关(默认开启)决定具体哪几种情形响,音量只缩放提示音本身、不改系统音量。
266
+
267
+ ---
268
+
269
+ ## 其他自动生效的修正
270
+
271
+ 不需要开关、随主题一起生效的可读性修正:
272
+
273
+ | 位置 | 问题 | 结果 |
274
+ | --- | --- | --- |
275
+ | 设置 › 模型 行操作(`编辑`) | 白字压在强调底上,1.05:1 | 16.50:1 |
276
+ | Cordis / 技能 / 工具 检查面板按钮 | 同上 | 16.50:1 |
277
+ | 附件轮播箭头 | 同上 | 16.50:1 |
278
+ | 亮色模式「移除」按钮 | `#ff3b30` 红字在面板底上 3.16:1 | 5.16:1 |
279
+ | 回合状态标签「Deep diving…」 | 渐变文字仍是品牌蓝 | 两配色四个色标全部达 AA(0.2 起标签改用遮罩扫光,换色走 `--dsw-alias-label-deep-diving*` 两个令牌,结论不变) |
280
+ | 提问卡片「推荐」徽标 | 前景与背景令牌被映射成同一值,完全不可见 | 16.50:1 |
281
+ | 提问卡片选项编号 | 暗色选中行上黑底黑字,1.25:1 | 11.69:1 |
282
+ | 新建会话页背景光晕 | 写死的 `#6187D8` | 改强调色,按亮度对齐原强度(`*_heroGlow` 模块自 0.1.2-rc.1 起已不存在、0.2 仍无,规则保留为自愈钩子) |
283
+ | 会话头部预设徽章(agent preset,如「创造模式」) | 上游把徽章移进 `_headerActions` 插槽的包裹层里,主题的 `>` 选择器失配,退回默认灰胶囊 | 恢复强调色填充 + 黑字,**尺寸沿用上游**(不撑宽) |
284
+ | 审批弹窗(「等待审批」) | 上游是塞在输入区里的一张圆角描边小卡 | 重绘为**贯穿屏幕左右白条 + 上下遮罩**的居中确认框,按钮改为胶囊(拒绝=深色+✕,允许一次=信号黄+◎),见下 |
285
+ | 完全权限确认框(「确认启用完全权限?」) | 共享 Modal 的居中圆角小卡 | 同款白条 + 上下遮罩,取消/启用胶囊按钮,见下 |
286
+
287
+ ### 审批弹窗:全屏白条 + 上下遮罩
288
+
289
+ 工具越权执行时弹出的 `[data-approval-key]` 面板被重绘成参考图那种形态:**不透明纸色横条从屏幕左缘贯到右缘、垂直居中,页面其余部分只有上下被压暗**——实现上没有加任何 DOM:面板自己的 root 变成 fixed 全屏 flex 层,它的半透明黑底就是遮罩,中间的白条不透明地盖住横带,遮罩自然只剩上下两截。文字居中,「等待审批」保留状态点但去掉警示底色。两个按钮改成终末地胶囊(左取消深灰配白 ✕、右确认信号黄配 ◎,墨字对比度与主题其它实心芯片同一条 AA 约束)。**圆角模式同样保持胶囊**(与余额药丸一致),且所有选择器都钉在语义锚点上(`data-approval-key` + `[class*='_card']` 等 CSS-module 后缀),上游重哈希不会失效——`test/selector-guard.test.js` 钉住这三个钩子。
290
+
291
+ ### 完全权限确认框:同一形态
292
+
293
+ 「确认启用完全权限?」(权限下拉切到完全权限时的风险确认框)是**另一个组件**——ui-primitives 的共享 Modal,portal 到 `<body>`,由 RiskConfirmation 包装。它收到同款重绘:贯穿左右的纸色白条、上下遮罩、标题居中、✕ 关闭移到白条右缘、取消/启用两个胶囊按钮(未勾选风险确认前,启用键保持暗化禁用态)。作用域钉在 `[role='dialog'][class*='_confirmation']`(RiskConfirmation 加在 dialog 上唯一的语义标记)之下,其它普通弹窗不受影响;外层共享 root 的 24px 侧 padding 用 `:has()` 精确归零。
294
+
295
+ 这些的成因分析见 [engineering-notes.md § 已修问题归档](engineering-notes.md#已修问题归档)。
package/docs/glass.md ADDED
@@ -0,0 +1,32 @@
1
+ # Optional frosted glass
2
+
3
+ The `glass` setting is `off` by default. `subtle`, `standard` and `strong` add
4
+ bounded local blur (8/14/22 px) to the docked right panel. The composer card
5
+ receives the same opaque glass fill **without** the backdrop filter, and the
6
+ sidebar receives a static tint/sheen only. Text, native geometry and the selected
7
+ palette remain under the theme's existing control. Dark surfaces use their own
8
+ opacity levels. Fullscreen panels, dialogs, code blocks and menus are excluded.
9
+
10
+ The composer card deliberately keeps no `backdrop-filter`: a backdrop filter
11
+ (like any filter) makes the element the containing block for `position: fixed`
12
+ descendants. The send-button tooltip is fixed-positioned from viewport
13
+ coordinates, so blurring the card resolved the tooltip's coords against the
14
+ shrunk card box — clipping it past the card's right edge and making its
15
+ ResizeObserver re-measure in a loop (visible layout jitter). Reported in
16
+ issue #19; keeping the opaque tint preserves the glass look at none of the cost.
17
+
18
+ Unsupported backdrop filters use a .96 opaque fill; reduced-transparency uses
19
+ an opaque fill and removes blur. Disabling the theme removes the material
20
+ attribute and stylesheet. Stronger tiers increase opacity as well as blur, so
21
+ background motion competes less with foreground text. Blur still has a GPU cost;
22
+ this feature is not a performance optimization and does not promise video FPS.
23
+
24
+ Table hover now uses a 15% accent tint over the base surface with normal text,
25
+ while text selection retains the full accent with black text. This addresses
26
+ issue #18 independently of whether glass is enabled.
27
+
28
+ Validation: `npm run test:glass` (Node 22+, Chrome/Edge; `CHROME_PATH` supported)
29
+ checks four light/dark × valley/Wuling combinations, all opacity/blur tiers,
30
+ stable geometry, AA text contrast on hovered/selected cells, reduced-transparency,
31
+ fullscreen exclusion and teardown. The browser fixture uses isolated temporary
32
+ profiles and no account or model requests.