# 变更记录
本文件记录 dsh-ui-font 的版本变化、新功能和问题修复。
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
## [0.9.6]
### 修复
- **DSH ≥ 0.1.2-rc.1 升级后全局字号再次不影响对话正文、准星只显示「主界面」**。
新版主题把正文字号体系整个重构为「内容轴」:
- 新增原生字号设置(设置 → 通用 → 字号,12–17px),由 ThemePresenter 以
**body 内联 style** 写入 `--dsh-content-font-size`
- `--dsw-font-markdown-*` 的值不再是 `14px/22px` 字面量,全部变为
`var(--dsh-content-font-size,…)` / `calc(21px + var(--dsh-content-font-delta))`
形式的表达式(h1–h3 与行高经 delta 派生、表格经 secondary 派生,派生变量
声明在 body 上)
三处连锁断裂与本版的修复:
- **库存解析失效**:`stockSizeOf`/stocks/`buildTokenCss` 只认 `^数字px` 字面量,
var()/calc() 形式全部解析失败 → 令牌库存为空 → body 覆盖不发。新增
`resolveTokenValue`:按当前原生字号(读 body 内联值,非 computed,避免被
本插件自己的覆盖污染)代入 content 变量并求值 calc,新旧两种值形态都兼容
(负 delta 亦正确)
- **令牌归属失效**:消费规则的字号声明从 `font-size:var(--x)` 长写法改为
`font:var(--x)` 简写后,Chromium 的 CSSOM 会把 font-size 长项序列化为解析
后的像素值,var() 被隐藏 → 家族提取失败 → 规则被整体丢弃 → 准星兜底
「主界面」。家族提取改从规则原文(cssText 永远保留 var() 原文)读取;直接
消费 `--dsh-content-font-size*` 的规则归入「聊天正文」家族;
`--dsw-font-family`(纯字体族引用)不再误登记为字号家族
- **覆盖通道失效**:对 var() 表达式内的字面量做缩放是无效的(var 仍解析到
内联值)。改为在 body 上以 `!important` 覆盖上游轴
`--dsh-content-font-size`(important 样式表声明压过非 important 内联样
式)——delta/secondary/h1–h3/行高整条派生链在 body 上自动跟随,一处覆盖
全族生效;净调整为零时不发该覆盖,原生字号设置保持可用。h1/h2/h3/表格的
perToken 独立偏移通过「代入计算后的具体 px 覆盖」实现分歧
- 新增 body style 属性的 MutationObserver(防抖 200ms):原生字号/主题变化
重写内联轴时自动重算叠加——本插件唯一的观察器,依然零轮询
- 新增 `tools/test-content-axis.cjs`:以安装的主题包与 shell CSS 的**真实**
令牌值/规则文本做解析与归属回归(含原生 12/17 的负/正 delta 跟踪)
## [0.9.5]
### 修复
- **插件加载失败:`Cannot find package '@deepseek-ai/dsh-settings'`**(0.9.3 引入)。
settings.yaml 持久化(PR #2)在 host 侧顶层静态 import 了
`@deepseek-ai/dsh-settings` 与 `@deepseek-ai/schemastery`,并以 peerDependencies
声明。但 `link:` 方式挂载的插件被 Node 以真实路径解析(E 盘目录链),既走不到
profile 的 node_modules,E 盘上也没有这两个包——模块链接阶段即失败,插件整体
无法加载,dsh 升级后首次以新挂载方式启动时暴露
修复:恢复零运行时依赖——
- 命名空间改用常量 `"ui-font"`(`settingsNamespace()` 仅是 kebab-case 规范化)
- `z.object(...)` 换成手写的规范化函数:补默认值、钳制区间、丢弃畸形条目,
镜像 client.js `validateSettings()` 的规则;挂 `toJSON()` 以满足
`ctx.settings.describe()` 的序列化契约
- 顺带消除了原 schema 的启动脆弱性:schemastery 的区间校验对脏数据是抛错
语义,手动编辑 settings.yaml 写出超范围值(如 `delta: 99`)会让插件在
register 阶段直接加载失败;新函数永不抛错,脏值读取时静默修正、下次保存
自愈写回
- 409 冲突识别从 `instanceof SettingsConflictError` 改为稳定机器码
`e.code === "SETTINGS_CONFLICT"`(该错误类本就无法从插件目录导入)
- package.json 移除这两个 peerDependencies
## [0.9.4]
### 修复
- **字号快捷键的三个疑难症状:「按了没反应要按两次」「按缩小反而变大」「偶尔正常」**。
根因是持久化层的 HTTP 竞态,三路叠加:
- 每次按键在 `bumpDelta` 里同步步进缓存后发 POST(连接 A),紧接着
`reapply()`→`loadSettings()` 发 GET(连接 B)。两个 fetch 并行,GET 常抢在
POST 落地前返回旧值,`loadSettings` 无条件 `localCache = 旧值` 把刚步进的
值覆盖回去 → 该次按键视觉上失效("按两次才生效")
- 页面刚加载时缓存是 DEFAULTS(delta=3)种子,首次 GET 未返回前按键会以 3
为基准步进:持久值 8 的用户按一下缩小直接落到 2(一次跳 6 格);持久值低
于 3 的用户按缩小反而落在 2 → 字变大("按 Ctrl+- 会增加")
- 快速连按 +/− 混合时,乱序到达宿主的 POST 让最终值偏离预期,下一次 GET
把偏离值载回渲染 → 方向翻转
修复:所有宿主往返(GET 与 POST)排进单一 promise 队列,本页内绝不交错;
引入写纪元(writeEpoch),加载期间发生过本地写则以本地为准、不用过期响应
覆盖缓存;`reapply` 改为渲染同步缓存不再 fetch;`bumpDelta` 的读-改-写整体
入队(必然运行在初始加载之后,种子竞态消除);设置页 `update` 的合并基准从
React 旧状态改为同步缓存(打开面板期间的旧状态覆盖问题一并消除)
- **keyCode 187/189 旧路径匹配自 0.9.2 起是死代码**:守卫
`e.code === "" → return false` 恰好拦掉了唯一需要走 keyCode 的场景(无
code 的按键事件永远到不了 keyCode 比较)。改为「有真实 code 且未匹配即不
匹配;无 code 才落 keyCode」
- **小键盘 +/- 不触发快捷键**:`Ctrl+NumpadAdd`/`Ctrl+NumpadSubtract` 现在
等同 `Ctrl+=`/`Ctrl+-`(仅当存储组合期望 = / - 时)
- **缩放接管兜底**:任一字号快捷键设置后,未被匹配的 Ctrl+ +/- 变体(小键
盘、多按 Shift、无 code 事件)一律 `preventDefault`,不再漏给浏览器原生页
面缩放——原生缩放一旦漏进来,页面视觉字号与本插件 delta 脱钩,表现为
"按了没反应/方向不对"
- **跨标签页漂移**:无轮询是设计使然,但后台标签的缓存会永久过期,回到该
标签后第一次按键会把 delta 跳回旧值。新增 visibilitychange/focus 时的防抖
重同步(走队列与写纪元,不会覆盖更新的本地写)
- 移除 `onHotkey` 在 document 上的重复注册(window 捕获已覆盖;匹配时
`stopImmediatePropagation` 本就阻止了 document 侧重复触发,重复挂载只在未
匹配按键上白跑一次)
- 新增 `tools/test-persistence-race.cjs`:以模拟宿主时序复现上述全部症状并
回归新旧逻辑(旧逻辑四场景全败、新逻辑全过)
## [0.9.3]
### 修复
- **DSH ≥ 0.1.1 升级后 AI 回复不再随全局偏移放大**:新版把 `--dsw-font-*` 令牌的
定义从外壳 `` 样式表挪进了 `dsh-client-ui-theme`
运行时注入的 `