# 更新记录 简体中文版;英文版见 [CHANGELOG.md](CHANGELOG.md)(两份一起维护)。 格式:[Keep a Changelog](https://keepachangelog.com/en/1.1.0/)。 版本口径:[语义化版本](https://semver.org/spec/v2.0.0.html)。 发布台账(tarball 名 + SHA-256):`RELEASES.txt`(只在发布归档目录里)。 > **维护规矩:每次改动都要升版本、先落一条记录,再打包;已发布的 tarball 永不删除。** ## [0.1.3] - 2026-10-24 这一版追的症状:DSH 先以内置的 1280×820 打开,**随后**才跳到设置好的尺寸。提前恢复(0.1.0 起就默认开的 `restoreEarly`)确实在跑,只是比应用的 `show()` 慢了大约一秒 —— 输就输在 Win32 那条传输路上。 ### 新增 - **在使用者本机现编的 Win32 助手。** C# 源码就写在 `lib/index.js` 的 `buildAgentCSharp()` 里;插件第一次 需要 Win32 时用**系统自带**的 `csc.exe` 编一次(实测约 110ms),产物落在 `~/.dsh/storages/window-size-keeper/wsk-agent-<源码摘要16位>.exe`,旁边同时写一份一模一样的 `.cs`, 谁都能打开看它到底在干什么。采信之前先要它答一声 `--protocol` 握手 —— 杀软把刚写的 exe 拦掉就在这一步 失败,且不影响后面。起这个 exe 实测**约 90ms**,同一台机器上 powershell 代理实测**约 1.5s**;进程常驻之后 一次枚举只要几毫秒。文件名里带源码摘要,等于"源码一改旧缓存自动作废":不用记版本、不用谁去清,两个不同 版本的插件也不会互相覆盖对方的助手。包里依旧**不含任何二进制**,也不引任何第三方工具链。 - **配置键 `win32Backend`**:`auto`(默认)先试助手、不行退 powershell;`powershell` 完全不编译、本机不落 exe;`helper` 只用助手(排障时才这么写)。 - 助手的一次性模式 `--get` / `--set` 与 `buildGetScript()` / `buildSetScript()` 的契约逐字对齐(可见窗口 + 工作区;stdout 出 `OK` / `FAIL`,`SetWindowPos` 说不也照样退出码 0),所以采样 cycle 与设置页「立即应用」 同样吃到这份提速。助手某次调用失败就在本代内**粘住**:这一代剩下的调用直接走 powershell,不会每次采样都 去撞同一堵墙。 - `GET /state` 下发 `win32Backend`、`win32HelperExe`、`win32HelperFallback` —— "到底编出来了没、现在走的是 哪一层"从宿主 API 就能读出来,不用猜。 - 自检新增 `[7b]` 组:选路的纯函数(编译器探测顺序、探测抛异常怎么办、缓存命名、后端口径归一),Windows 上 还真编一次(只落在自检自己的临时目录)并对真 exe 跑一遍协议 —— 包括"助手起不来必须退回 powershell、绝不 整个放弃"这条。离线自检项数 **88 → 99**。 - 自检脚本现在**保证自己会结束**:8 分钟看门狗(到点打红,并写明"日志里最后一条组头就是卡住的那组", 因为组头在跑该组之前打印)、收尾 `process.exit`、测试用的 http server 先 `closeAllConnections()` 再 `close()`,临时目录清理带重试、清不净就记成失败项而不是让异常飞出去。 起因与**已定位的真因**:0.1.3 推上去之后 CI 两台 Windows runner 的 selftest 步骤跑了 11 分钟没结束, 而**未结束的 job 取不到日志**(logs 接口回 `BlobNotFound`)——卡住的自检恰恰查不到。加上明确退出之后 日志能取了,真因浮出来,是两处:① `[7b]` 有一条断言在 runner 上必挂——CI 的 session 0 里没有 `Chrome_WidgetWin_1` 那个窗口类,探针枚举回**空数组**,而断言一抛就跳过了 `agent.close()`,于是常驻的 助手进程挂在事件循环上、node 永不退出(那 11 分钟就是这么来的);② 收尾 `fs.rmSync` 删临时目录时那个 exe 还在运行,Windows 删不掉正在运行的映像 → `EPERM: unlink` 抛出,把已经打好的结论盖掉。 所以断言改成"空数组是合法答复、有条目才要求字段齐",两条 `openWinAgent` 检查改成 `finally` 里关代理。 顺带把两组 Win32 的耗时打进日志:本机 2026-10-24 实测 `[7]` 组 9.2~10.6s、`[7b]` 组 2.9~4.4s、整轮 10.6~12.5s;runner 上分别是 5.8s 与 0.64s。 ### 修复 - 助手探针(`--protocol`)与助手代理单次请求的超时从 4s 提到 **8s**,与 powershell 那条对齐:刚写出来的 exe 第一次执行会被杀软整份扫一遍(本机首跑就要 0.8~2.2s),4s 太紧会把"其实能用"误判成"助手不可用", 白退回慢路。 - 能编出助手的机器上,改尺寸现在落在应用启动后约 **+1.9s**,也就是在窗口被 `show()` **之前**,而不是之后。 本机 2026-10-24 实测(主屏工作区 2560×1392、目标 2200×1200):Electron 主进程起来 → +0.26s 主窗口已创建 (此刻还藏着)→ +0.57s 宿主进程 → +1.66s 插件 `apply` → powershell 代理的第一次枚举再晚 1.24~1.32s → 改完已经是 **+3.15s**,正好排在 `show()` 后面。换成助手:起进程 + 第一次枚举合计 **0.098s**,`--get` / `--set` 一次性调用 0.083~0.084s。 - 常驻代理现在也接住自己 stdin/stdout 管道上的 `error`。以前若杀软把子进程当场杀掉,可能在宿主进程里冒出一个 没人接的流错误。 - 文档与代码注释里把 powershell 这条路估成"每次约 730ms"是错的:同一台硬件上光是起 `powershell.exe` 就约 0.8s,`Add-Type` 现场编译再加约 0.45s。 ## [0.1.2] - 2026-10-20 **只改文档**,插件行为与代码相对 0.1.1 一字未动。这条是**算出来的**:0.1.2 的 tarball 与 0.1.1 相比只有 五个随包文件不同(两份 README、两份更新记录与 `package.json` 的版本行),`lib/`、`scripts/`、`cordis.patch.yml` 逐字节 相同 —— 所以 0.1.1 那 88 项离线自检的结论对这一版同样逐字成立。 ### 变更 - README(中英两份):清掉与现状不符的说法 —— 与开发目录分开的"发布归档副本"目录、再开一个 `<名字>-<版本>` 目录当发布快照(这习惯已于 2026-10-20 退役,版本身份就是注解 tag)、"桌面端 profile 经 pnpm `link:` 链接本目录"、以及"81 项分十组"(0.1.1 起是 88 项分十二个编号组)。那一节改名叫 「本机上的东西各放在哪」:一份 checkout 装着代码、台账、投稿条目、CI 与 `.git`,另外还讲清 profile 真正吃的是 `~/.dsh/.plugin-sources/` 里那份 tarball。 - 「安装 / 重载」把普通用户那条路写全了(原先只有一行指向本地 `dist/` 的文件):从 GitHub Release 下载 附件**保持原文件名** → 与发布台账核 SHA-256 → `dsh plugin … add "<这个 .tgz 的绝对路径>"` → 完全退出 并重启 DSH(宿主那一半开机加载、客户端那一半页面加载时取)。`dev_reload_package` 也标明只换宿主那一半。 ## [0.1.1] - 2026-10-20 下面两个缺陷都是"把发布到 GitHub 的产物装上、在真桌面窗口上用"才暴露出来的。两条现在都有离线钉子; 第一条此前 81 项全绿也抓不到,因为假 DOM 那组从来没断言过坏掉的那一行。 ### 新增 - **自适应采样节奏**(`activeSampleMs: 1000`、`activeTailMs: 10000`):窗口没变时每 `saveIntervalMs` (15 秒)读一次外框;一旦这一轮采到与上一轮不同,下一次立刻改成 1 秒一次,并保持到距最后一次变化满 10 秒。而且**下一轮要等上一轮跑完才排**,PowerShell 慢时只会排队,不会叠出一串并发读取。在这之前, "拖完马上退出"会把这次拖动整个丢掉:状态文件(以及退出兜底写的那份同一份缓存)最多可能旧 15 秒, 于是下次启动回到拖动之前的尺寸。 - **`followManualResize`(默认开)**:采到的变化只要判定是**用户手动改了宽高**,插件就自己写一次 `settings.json`,把「恢复尺寸」选择变成 `自定义` = 那个尺寸 —— 下次启动跟着你真正拖出来的窗口走, 设置页也显示成选中了「自定义」。只有宽高算数(只挪位置不动大小不改选择);插件自己改的那一次 (启动恢复、设置页"立即应用")不算 —— 否则恢复你选的 `1920×1080(Full HD)` 会被自己改成 `自定义 1920×1080`。 - 自检新增第 `[5c]` 组:`nextSampleDelay` 与 `shouldAdoptManualSize` 逐分支钉(边界、写反、写 0、 写负数、写垃圾、脏输入),另在第 `[6]` 组钉住 `/state` 下发的是**生效的**节奏与开关。离线自检 81 项 -> 88 项。 - 自检第 `[10]` 组不再硬写"至少扫到 12 个文件" —— 那是仓库专属的文件数。覆盖本来就由那份逐文件 清单(11 个随包文件逐个点名)加嵌套件递归检查钉着,所以下限退化成"不是一遍空"。此前在**装好的 包**里跑自检(随包 11 个文件,仓库专属那几件都不在)会凭空红一项 —— 照 README 操作的普通用户 正好撞上;现在装好的包里跑出来也是 88/88。 ### 修复 - **设置页"当前主窗口外框"那一行永远停在"读取中…"** —— 2026-10-19 那轮 i18n(为了切语言不重建 DOM 而引入 `sinks`)带出来的回归。那一行当初是用 `setText(current, "current.loading")` 注册的,闭包 **永久**留在 `sinks` 里;而 `applyState` 的最后一步正是 `localizeStatic()`(重放全部 sinks),于是 刚写进去的真实尺寸每次成功取数后都被当场覆写回"读取中…"。`GET /state` 1 毫秒 200 正常返回、控制台 也干净,看起来完全像"请求没完成"。现在这一行自己是一条 sink,文案按最新载荷现算;新增的渲染检查 钉住三件事:首刷后显示真实尺寸、定时刷新后不改回、切语言重放全部 sinks 之后也不改回。 ### 变更 - `saveIntervalMs` 的语义从"唯一的轮询间隔"变成**待机节奏**;默认值与随包 `cordis.patch.yml` 里的值 都仍是 `15000`,没有变。 - 两份 README 各加了"采样节奏与跟随手动改尺寸"一节(两份章节一一对应本身就是自检钉着的),配置表 也补上这三个新键。 ## [0.1.0] - 2026-10-19 **首次公开发布。** 在这之前没有任何版本发布过,所以发出去的 0.1.0 就是插件现在的状态; 它内部的开发节点在下面单列,让版本号与历史对得上。因为 0.1.0 从没离开过这台机器 (没有远程、没有市场条目、没装进任何 profile),下面这些"发布前补齐"直接归进 0.1.0 并重打 tarball,不去凭空造一个 0.1.1;被换掉的旧 tarball 摘要记在 `RELEASES.txt`,重打这件事可查。 ### 新增 - 记录 DSH 桌面端(Electron)主窗口**最后一次的外框矩形**(物理像素)并在下次启动恢复、居中到主屏 工作区——应用本身每次启动都按固定 1280×820 重建主窗口,不这么做的话用户上次调的尺寸每次都丢。 - 设置页「窗口大小」分区(DSH 桌面端 → 设置,与「插件」「桌宠」同一层,`order: 65`):内置 12 个常用 分辨率(1024×768 … 3840×2160,含 1536×864 = 125% 缩放下的 1920×1080)、任意自定义宽高、 「恢复时居中」开关、「立即应用当前选择」;也可以在 patch 配置里用 `presets:` **整体覆盖**预选项。 选中即改窗口并持久化。 - 恢复优先级:**设置页选择(`settings.json`)> patch config 的 `preset` > 上次尺寸(`state.json`)**。 两个 JSON 都是原子写(临时文件 + rename),落在 `~/.dsh/storages/window-size-keeper/`。 - 本地 HTTP 路由 `/window-size-keeper/api/{state,select,apply,reset}`;非法输入会被拒绝并给出原因, 不会静默改设置。 - 生命周期卫生:清理经 `ctx.fiber.effect` 正规注册(卸载时杀常驻代理、清定时器、同步落盘); 代数文件 `state.json.gen` 让热重载的僵尸 fiber 永远不能用陈旧尺寸覆盖状态文件;启动恢复与 「立即应用」走同一条 `serialize()` 队列,不会两股 PowerShell 并发改同一窗口互相覆盖。 - **设置页文案中英双语、语言跟随环境(2026-10-19)。** 界面文案全部收进 `lib/client.js` 的 `zh` / `en` 词典,并按 DSH 客户端的 locale 契约注册(`ctx.locale.register(NS, { zh, en })`)。 语言判定顺序:patch config 写死的 `language` → DSH 客户端当前语言(**切语言立即生效**,不刷新 页面也不重启)→ 插件自带的 `navigator` / `Intl` 判定,而这一层**只有简体中文算中文** (`zh`、`zh-Hans*`、`zh-CN`、`zh-SG`、`zh-MY`),`zh-TW` / `zh-HK` / `zh-MO` / `zh-Hant*` 以及任何其它语言一律英文。切语言时**原地改文案、不重建 DOM**,你正在输入的自定义宽高不会被冲掉。 - 宿主侧不做界面翻译:失败回报改成带稳定机器码的 `code`(`enum_failed`、`main_window_not_found`、 `superseded`、`set_pos_failed`、`windows_only`、`no_target`、`bad_json_body`、`unknown_route`、 `internal_error`),校验提示带 `noteCodes`,两者都由客户端按当前语言渲染;每个内置预选项都同时给 中英两份别名(`label` + `labelEn`)。认不出的码退回宿主给的原文——**绝不把错误码原样甩给用户**; 你没写 `labelEn` 的自定义预选项原样显示你起的名字,**绝不机器翻译**。 - 新增 patch 配置键 `language`(默认 `auto`:跟随 DSH 当前语言;`zh` / `en` 钉死)。只认 `auto` / 强制中文 / 强制英文 三类,`zh-TW`、`zh-Hant` **不算**强制中文;写错的值退回 `auto`, 不会因为值奇怪就把设置页弄哑。 - 离线自检 `scripts/selftest.mjs`:**81 项**,覆盖纯函数(居中/夹取/优先级/输入校验/主窗口识别/ 设置读写)、HTTP 路由(假 ctx + 真 http server + 真 fetch)、真 `powershell.exe` 对不存在句柄的 代理协议(`SET` 必然失败 → 正好验"失败如实回报、不谎报已改窗口")、文案 i18n(中英词典逐 key 对齐、`{占位符}` 一模一样、英文表里不许残留中文、语言判定优先级表),以及**用假 DOM 真渲染这个 分区**:中英各渲染一遍,并验"切语言不重建 DOM、正在输入的自定义宽高不丢、上一条提示也跟着换成 新语言、卸载时刷新定时器与语言订阅都释放,以及"词典没注册成功时绝不把 page.title 这类内部 key 甩到界面上"。 - **脱敏扫描器 `scripts/check-sensitive.mjs`**(2026-10-19,发布前):9 条规则扫「发布要用的文件 + 只在仓库里的发布文档」——账号家目录绝对路径(`C:\Users\<某人>`、`/home/<某人>`)、个人工作区绝对路径、写死的本机回环端口、凭据样式(赋值式的 key/secret/password/token,以及前缀式 key:`ghp_`、`sk-`、`AKIA`、`xoxb-`、`AIza-`)、41–63 位的十六进制长串(40 位与 64 位是 git 对象号与 SHA-256 摘要的标准形状,放行它们台账才写得出校验和)、真实邮箱、机器指纹(SID、机器 GUID、主机名)。它自带「必须判红」与「必须判绿」两组对照(条数由扫描器自己运行时报出,并被自检第 [10] 组盯住),防止自己退化成盖章机器;接进 `npm test`(另有 `npm run scan:sensitive`)与自检第 [10] 组,CI 里也有独立的一个 job。 - 同轮清掉了扫描器自己逮到的东西:`scripts/selftest.mjs` 里那份假载荷的路径从真人账号路径改成 `/…` 占位;README / README_zh / RELEASES / PUBLISHING 里的本机工作区绝对路径与代理端口改成 `<工作区>` 与 `$GIT_PROXY` 写法。`ecbob` / `ecbob` 保留——仓库地址、市场投稿条目、版权行总得有个主人。 - **owner 与署名统一到真实 GitHub 账号(2026-10-19)**:仓库在 `ecbob/dsh-window-size-keeper` 下,所以 `package.json`(`repository.url` 与 `author`)、`LICENSE` 版权行、市场投稿条目(文件名改成 `submission/data/plugins/ecbob__dsh-window-size-keeper.yml`,`url:` 与 `name:` 跟着改)以及各处文档一律写 `ecbob`。提交身份建议用 `ecbob `——commit 挂在账号下,又不在公开历史里暴露个人邮箱。 - `PUBLISHING.md` 改写成 SSH 路线(本机走 SSH;前提是账号里加着本机公钥,没加时 22 端口和 443 端口都会回 `Permission denied (publickey)`,本机实测过),并补上 ssh over 443 的备用配法与 HTTPS + 令牌 的替代路线。 ### 同一个 0.1.0 内的开发节点 - **2026-10-11** —— 设置页分区、预选项与优先级链落地。 - **2026-10-16** —— 提前恢复:不再等宿主报 `ready`,`apply` 那一刻就起常驻 Win32 代理,赶在窗口 **还隐藏**时把尺寸改完(桌面端是"隐藏创建 → `loadURL` → 才 `show()`"),于是窗口第一次露面就是 目标尺寸,启动动画不再播在错误分辨率上;盯不到才退回 `restoreDelayMs` 的轮询路径。判据与实测 证据见工作区报告 `DSH-BOOT-ANIMATION-REPAIR-2026-10-16.md`(单次枚举往返中位数 29 ms)。 - **2026-10-19** —— 文档语言口径定了:`README.md` 改为**英文主文档**,`README_zh.md` 是它的简体中文 对应版(结构、表格、数字一致);包描述的 `description` 改英文——GitHub 的 About 和 npm 只显示这一条 字符串,而市场投稿条目里 `description.en` 与 `description.zh` 两份都有。`cordis.patch.yml` 的注释 保持中文(它是运行中 profile 实际装配的那份,注释之外的数据没动)。 ### 不包含的东西 没有任何依赖(只用 Node 内置模块 + 系统 `powershell.exe`),也没有任何外部网络请求。英文词典是 实打实写全的,不是机翻兜底;不提供第三种语言,因为 DSH 自己的语言目录就只有 `zh` 和 `en`。