# 变更说明 本文件记录可分发版本的变更。首版之前的 `0.1.0-alpha.0`/`0.1.0-beta.1` 是阶段性版本, `0.1.0-rc.*` 与 `0.1.0` 是首个交付版本线;`0.1.1` 在 `0.1.0` 之上修复两处体验问题、调整一处缺省语义, `0.1.2` 修复 `0.1.1` 的致命缺陷(重开总开关导致渲染进程崩溃)。 ## 0.1.2 修复两类问题:①`0.1.1` 的**致命缺陷**(关闭总开关后重新开启会使渲染进程崩溃、桌面端退出); ②`0.1.0` 起就存在的**恢复语义缺口**(重新开启总开关后宽度不自动回到记录值,且再次打开右栏时跳变)。 定位与修复案:`docs/plans/P12-disable-enable-reentrancy-fix-plan.md`、`docs/plans/P13-reenable-restore-and-flash-fix-plan.md`; 验收:`docs/verification/V-P12-01-reentrancy-fix.md`、`docs/verification/V-P13-01-reenable-restore-fix.md`。 ### 修复内容 1. **结算重入闸门**:`settle()` 中“观察器为空则重新登记”这条路径会与观察器 API 的**注册期同步回调** 形成无界互递归(`settle → 登记 → 同步回调 → settle`)。现在 `settle()` 带重入闸门, 嵌套调用直接返回、由最外层完成状态更新;被挡下的次数计入诊断(稳态应为 0)。 2. **注册期不再同步通知**:结构观察器的“注册”现在只对齐元素,初始通知推迟到微任务, 从根源消除“注册动作内部被回调重入”的陷阱;尺寸与拖拽观察器注册时也不再顺带触发结构通知。 3. **护栏与可观测量**:结构监听器数量设上限(达到上限即拒绝登记并在控制台报错——重复登记缺陷的早期信号); 诊断块新增「结构监听器」「结算重入」两行,异常可以直接在界面看到。 4. **预备应用触发点收敛**(针对 0.1.1 引入的功能):从 `settle()` 顶部移到显式触发点 (启动初始化、宿主接入、重新开启总开关、页签清单变化),减少每次结构事件都尝试读写偏好的代价。 5. **重新开启总开关后自动恢复记录尺寸(P13)**:停用总开关会做一次数值默认清理(把宿主偏好改成 DSH 默认值), 而此前有**两条门同时挡着恢复**——`stopForDisabled` 没有复位“就绪上升沿”标记(权威应用不触发), 预备应用的闸门又是“本次加载中是否曾就绪过”(一经就绪永久关闭,偏好被改坏也不补写)。 现在: - 停用与“摘除宿主接入”同类处理,复位就绪标记 ⇒ 面板处于停靠状态时**立即**应用记录值(无需手工重开右栏); - 预备应用的闸门改为显式的“**偏好是否已知与记录一致**”状态位(启动与停用清理后为不一致, 任一权威应用成功后为一致)⇒ 面板处于收起状态时先把记录值写回偏好, **再次打开右栏的首帧就是记录值,不再跳变**; - 与闸门配套的**幂等去重也一并失效**(否则同一个目标值会被误判为“写过了”而跳过补写)。 ### 验证 - 自动测试:新增 `tests/controller-p12-reentrancy.test.mjs`(4 项,构造“注册时同步回调”的宿主替身); 新增 `tests/controller-p13-reenable.test.mjs`(6 项,**记录值 437 与默认值 576 不同**,覆盖两种模式与两种面板起点)。 **对照结果**:P12 那组用例在 `0.1.1` 的产物上 3 项失败(抛 `RangeError: Maximum call stack size exceeded`); P13 那组用例在 `0.1.2` 修复前的产物上 3 项失败(`重开后应立即应用记录值 437,实际 []` 等)。 - 真机回归:右栏打开状态下「关闭 → 重新开启」总开关——渲染进程存活、可响应、无新崩溃日志, 且**列宽自动从默认 576 回到记录值 437**(日志同时出现 `预备应用…(重新开启总开关)` 与 `自动应用…`); 面板收起时重开总开关后再打开右栏,逐帧采样 `[0, 437]`、**全程未出现 576**; 最严格的一组(先关(清理写坏偏好)→ 收起面板 → 重开 → 首开)同样 `[0, 437]` 无跳变。 ## 0.1.1 > ⚠️ **已知严重缺陷(`0.1.2` 已修复)**:关闭总开关后**重新开启**会导致渲染进程崩溃、桌面端退出 > (用户侧表现为卡死与 CPU 飙升,只能任务管理器结束)。原因是 `settle()` 的条件重绑与观察器 > 注册期同步回调形成无界互递归;该缺陷**自 `0.1.0` 起即存在**(`13e34c3` 引入),不是本版本新增功能造成的。 > 在升级到 `0.1.2` 之前,请勿执行「关闭总开关 → 重新开启」;需要停用插件效果时请改用「重置」或卸载插件。 **这一版有可观察的行为变更**:未记录尺寸的内容页不再直接落到 DSH 默认,而是沿“上级内容页”继承。 如果你希望保持 0.1.0 的行为,请继续使用 0.1.0。 ### 新增与变更 1. **未记录内容改为沿上级内容页继承**(决定记录 [`D-002`](docs/decisions/D-002-inherit-parent-width.md), 需求侧附录见 `需求与边界.md` 的 S8/S9 变更节)。 解析顺序:`本类型记录 → 上级记录 → … → 根(引导页)记录 → DSH 默认`; 链为 `引导页 → 文件页 → 文档类(editor / text)`,终端、浏览器以及任何未登记的页签类型其上级是引导页。 - **继承值永不落盘**:只有真实拖拽才会为“当前最具体类型”创建记录,因此不会凭空多出记录; - 例:引导页拖到 400 后,进入文件页与 md 文档都会先显示 400;把 md 拖到 700 后才记录 md 自己的一份, 此后从引导页一路进入就是 `400 → 400 → 700`; - 设置页据此调整:「已保存的宽度」下会标注当前尺寸的来源(本类型记录 / 继承自 X / DSH 默认), 当前内容没有自己的记录时**默认选中“正在供应尺寸”的那一条**,修改它立即生效; - **数据格式未变**(schema 1、键名与结构相同),**旧数据无需迁移**,升级后直接沿用。 2. **修复首次打开右栏时的宽度跳变**:此前会先撑到 DSH 默认宽度再缩回记录值。现在在面板渲染之前 (会话页签清单变化时)就把目标宽度偏好写好,**打开右栏的第一帧就是记录值**。 该路径依赖 `ctx.sidebarRight.openTabs`(非公开成员,见下方“已知限制”);若该成员不可用, 仅退化为“先默认再回到记录值”,其余功能不受影响。 3. **设置页收敛为发布形态**:DOM 取证、U2/U3/U4-U5 等 9 个诊断与实验区块**默认隐藏**(不再删除, 便于排障)。运行状态块改为四项态(正常运行/降级/未就绪/已关闭)、当前活动页签、 当前侧边栏尺寸(带来源),并新增**「导出运行日志」**:给出一段可直接复制的只读文本 (版本、宿主、视口、状态、阶段、模式、活动页签、尺寸来源、存储状态、最近动作与控制器日志), **不含会话 id 与文件路径**。 - 需要看隐藏的诊断区块时,在控制台执行 `localStorage.setItem('dsh-sidebar-size.diagnostics','1')` (改回 `'0'` 即隐藏,**不需要重启**)。 ### 验证 - 自动测试 **144 项**全通过(新增 `tests/inheritance.test.mjs` 8 项、`tests/controller-p11.test.mjs` 5 项、 `tests/controller-p11-preapply.test.mjs` 5 项、`tests/status-report.test.mjs` 9 项);一致性检查 12/12。 - 宿主验收(隔离实例,宿主 `0.2.0-rc.2`,含 `dsh-better-sidebar@0.24.1`): - `docs/verification/V-P11-01-settings-convergence.md`(设置页收敛、免重启诊断开关、日志文本框) - `docs/verification/V-P11-02-inheritance.md`(继承链、继承不落盘、拖拽只写最具体类型) - `docs/verification/V-P11-03-preapply-first-frame.md`(首帧测量:`[0, 437]`,全程无默认值 576) - `docs/verification/V-P11-04-manual-verification-guide.md`(用户在自己实例上的逐项验证清单) - 已知限制与未实测情形见 `README.md` 的「说明与限制」。本次实现新增两处对宿主非公开成员的依赖 (宽度写入入口、首帧预备应用的触发点),两者都是“可用才用、失败只降级”,不影响数据安全; 若写入口不可用则自动应用整体停用(读取与设置仍可用),若触发点不可用则只失去首帧正确性。 ## 0.1.0 **与 `0.1.0-rc.1` 相同**:本次只有版本号与变更说明的变化,产物字节一致 (`lib/index.js` 与 `lib/client.js` 的 SHA-256 与 rc.1 完全相同,见 `docs/verification/V-P10-01-delivery.md`)。因此 rc.1 的产物验证证据适用于本版本, 并在最终提交上重新做了一轮产物加载与关键功能确认。 功能、数据格式、已知限制与 rc.1 一致,见下一节。 ## 0.1.0-rc.1 首版功能齐备的发布候选(DSH 桌面 `0.2.0-rc.2`,裸本体与 `+dsh-better-sidebar@0.24.1` 均验证)。 ### 功能 - **按内容类型记忆宽度**:记住用户对右侧边栏的拖拽结果,按当前页签类型(宿主 `kind`)分别保存, 下次打开同类内容时恢复。 - **统一模式**:关闭「按显示内容区分尺寸」后所有内容共用一份宽度。两套数据互不转换: 第一次关闭且还没有统一值时回到 DSH 默认尺寸,不会把某一类型的值搬过来。 - **四项设置**(设置 → 侧边栏尺寸):总开关、区分内容开关(附完整提示原文)、 按类型改值的下拉栏、重置按钮(二次确认弹窗,含指定文案与加粗)。 - **数据迁移**:导出当前数据、单独导出未采用的诊断原文、预览确认后导入。 - **诊断**:设置页提供运行状态、当前右栏、DOM 取证、控制器日志与实验动作; 控制器日志同时写入 `dsh-sidebar-size.controller.log`(最多 60 行)。 ### 数据 - 宽度数据放在插件独占的浏览器存储键 `dsh-sidebar-size.widths.v1`,schema 版本 1: `{ schema, split: { [类型键]: 宽度 }, unified: 宽度 | null }`; 类型键为 `kind:` 加可逆转义(`%XXXX`),不含文件路径或 `contentId`。 - 作用域为**全局**(跨会话共用),不随插件目录移动而丢失。 - 遇到无法解析的原文或更高的 schema:**保留原文并暂停自动写入**,可在设置页导出该原文; 非法条目只剔除该条并报告。 ### 已知限制(重要) 1. **默认恢复只有数值语义**:目标版本没有公开的“把宽度偏好写回未初始化状态”的动作, 写 `null` 只会被夹到下限 300。因此回到默认只能请求一次数值 (`max(300, round(框架宽 × 0.45))`),界面与日志一律标注 `恢复等级 numeric`, **不会**声称“等同未挂载”。(决定记录 D-001;S11 记为部分满足。) 2. **首版粒度按页签类型**:同一 `kind` 的不同文件共用一份宽度;按扩展名细分属于后续版本。 3. **浮窗未验证**;拖拽中切换内容/模式、分栏焦点切换、真实宿主存储不可写也没有真实宿主证据 (各有自动测试或替身覆盖)。 4. **不承诺断电级持久化**:存储接口已接受写入即算成功;硬杀进程可能丢失最近一次 localStorage 提交,正常退出不受影响。 ### 交互与兼容 - 宿主条目 id `sidebar-size`(`cordis.patch.yml`),客户端注册 id 同为 `sidebar-size`。 - 只使用宿主公开服务(`configForms` / `sidebarRight` / `layout`)与右栏 DOM 契约; 宽度写入经 `layout.panels.setRightbar`(非公开成员,已验证可达;不可达时停止自动应用并如实降级)。 - 浏览器半只从外壳冻结的平台模块表取依赖,产物不含第二份 React。 ### 回归修复(相对 beta.1) - 重新打开总开关后重新登记观察器:修复“关闭再打开后拖拽与保存静默失效”。 - 关闭总开关/摘除宿主时结束进行中的手势:不再遗留“永远拖拽”状态。 - 拖拽中手柄被移除或替换时发出取消,并保留最后可信值;另有手势看门狗兜底。 - 导入排在保存队列之后,旧快照不会覆盖导入结果。 - 卸载前冲刷有时间上限,存储写入不返回时不会挂住卸载。 - 全屏/折叠等非正常右栏期间不接受用户采样,不保存全屏尺寸。 - 宽度被布局约束时保留用户意图,窗口变大后按同一份意图重试。 - 结构契约破坏(找不到面板/手柄)时降级并暂停自动应用,恢复后继续。 - 读取失败或空表时不写入,避免用空表覆盖已有数据;读取成功后不再原样回写。 - 活动面板与选中页签必须唯一,否则暂停分类(不取多个选中页签中的第一个)。