# Changelog

> 更新日志以中文记录。Changelog entries are written in Chinese.

## 0.2.13（2026-10-01）

**发布仓库隐私整改 + E2E 工具通用化（插件本体零改动）。**

- **发布仓库 commit 作者身份修正**：GitHub 仓库全部历史 commit 的作者已重写为 zeusxx（此前误用本机全局 git 身份）。
- **E2E runner 通用化**（`e2e/run-e2e.mjs`）：退役写死本机布局的目标表，改为环境变量推导——`DSH_CL_PORT`（默认 8124）+ `DSH_CL_DSH_HOME`（默认 `DSH_HOME`，再默认 `~/.dsh`）→ 自动定位部署副本；`--target <name>` 保留为纯别名（报告文件名/日志/传给部署脚本）。cookie 与报告文件统一按端口命名（`cookie-<端口>.txt` / `report-last-<端口>.json`）；部署副本不存在时跳过版本对照不阻塞；`--with-deploy` 在分发包内（不含部署脚本）给出明确提示。任何布局的实例都能测，不再绑定特定开发机。
- **文档修复**：CHANGELOG 0.2.3 条目的字面 `` `\n` `` 曾被写成真实换行导致 Markdown 列表损坏（本条已修）；0.2.4 条目对 CLAUDE.md 的失效引用改指 `e2e/README.md`；README 双语的 E2E 场景数 53→62 更正。

## 0.2.12（2026-10-01）

**E2E 测试体系升级为双环境 62 场景（插件本体零改动——本版是测试基建与发布配套）。** v0.2.11 的插件代码在 dsh 0.1.5-rc.2 与 0.2.0-rc.2 两个环境各跑一轮 62 场景全绿，本版把这套测试能力作为包内容发布。

- **`--target <8124|020>` 双测试目标**：一套脚本测多个 dsh 版本（0.1.5-rc.2 / 0.2.0-rc.2），`TARGETS` 表可扩展新版本；报告文件按目标分开（`report-last.json` / `report-last-020.json`）。
- **鉴权 cookie 全自动**：dsh 0.1.5/0.2.0 对无 cookie 的本机请求会在 Set-Cookie 里直接发一个 30 天授权 cookie——runner 自动获取并存 `cookie-<端口>.txt`，不再需要从 DevTools 手动复制。
- **0.2.0 首访 modal 弹窗自动关闭**：新浏览器 profile 首次打开 0.2.0 会弹「预览版说明」modal，其焦点陷阱会让 `input.focus()`、`execCommand`、合成 beforeinput 全部失效（症状是「时灵时不灵」的插入与删除）——runner 内置 `dismissDialogs` 自动点「继续」；**弹窗未关闭时的测试结果全部作废**。
- **新增 R 组 9 场景（53→62）**：图片附件在场（打字渲染 / 长文本滚动 / @ 候选菜单贴输入框——对应 v0.2.6-0.2.8 三个附件 bug 的回归）、全选替换 + Ctrl+Z 撤销、Ctrl+Y 重做、窄视口软折行、注入后改视口重排、长文滚动工具栏 sticky 钉顶、空文档基线。
- `inpage.js` op 集扩充：`attachImage` / `detachImages` / `toolbarClear` / `scrollToolbar` / `menuNearInput` / `snapshotClass`；op 改 async（runner 侧 awaitPromise）；`clearDraft` 改用 `Range.selectNodeContents`（`execCommand("selectAll")` 在焦点不在编辑器内时会溢出到页面级）。
- 视口控制：场景可声明 `viewport` / `resizeAfter`（Emulation.setDeviceMetricsOverride），覆盖窄窗口软折行与 resize 重渲染路径。
- INV-8 白名单：0.2.0-rc.2 刷新页面时 sidebar.footer slot 第三方插件的 React #130 噪音（官方错误边界兜住、与输入框插件无关）。
- 部署脚本 `deploy.mjs` 同步支持 `--target`（不进发布包，随私有仓库分发）。

## 0.2.11（2026-09-27）

- **README 配图**：双语 README 的截图段落补齐三张实拍（docs/screenshots/）——实时渲染全景（行内格式/任务列表/引用块/链接）、代码块（语法高亮/语言标签/等宽）、选区浮动格式条。新增 `e2e/capture-shots.mjs` 截图脚本（复用 E2E 链路：独立 Chrome + CDP + cookie，注入演示草稿后按输入卡元素截取，画面不含侧栏）。
- 文档与发布基建：双语 README（英文主 + 中文完整版）、docs/architecture.md 架构深读、e2e/README.md、MIT LICENSE 修正版权行、package.json 补 repository 元数据；e2e/run-e2e.mjs 硬编码路径参数化（DSH_CL_PORT / DSH_CL_E2E_PROFILE / DSH_CL_DST_PKG 环境变量，默认值不变）。

## 0.2.10（2026-09-27）

**输入体验四件套（用户选定方向全做）：**

- **Ctrl+B / Ctrl+I / Ctrl+E 快捷格式**：选中即包裹加粗/斜体/行内代码，光标态插空标记（与工具栏按钮同通道 applyWrapAction）。拦截同时挡掉浏览器对 contenteditable 的默认 `execCommand('bold')`——那会插真 `<b>` 富文本标签进编辑器（隐性 bug 顺带修复）。
- **选区跨行 Tab/Shift+Tab 批量缩进**：整块每行行首 ±2 空格（空行跳过），缩进后选区恢复为新块（连按 Tab 逐级缩进）。实现坑：**有选区时直接跑 insertInline 的 beforeinput 序列与 Lexical 内部 selection 状态不同步（E2E 实测首段丢失）**——整块替换必须走「先合成删除选区 → commit → 插入新块 → 恢复选区」三步链。
- **任务列表勾选样式**：`- [ ] 待办` 渲染成淡胶囊方块、`- [x] 已办` 绿底+行内容划线（line-through paint-only 不改 advance；三字符标记整体进胶囊，字符流不变光标对齐不受影响）。
- **表格快捷补全**：首尾包夹的表格首行（`|列A|列B|`，至少 2 列）行尾 Enter/Shift+Enter 自动补 `|---|---|` 分隔行+空行，光标落空行；下一行已是分隔行不重复补。实现坑：`indexOf("\n", 行尾)` 从行尾搜会把行尾的 \n 自己找到（slice 起点大于终点得空串）——须从 行尾+1 搜。

单测 173→188（+15），E2E 49→53 场景（Q6-Q9：快捷键包裹/表格补全/任务样式/选区逐级缩进全链）全绿。

## 0.2.9（2026-09-27）

- **Tab 缩进体系（此前 Tab 在输入框内完全不可用——浏览器默认把焦点切出输入框）**：Tab 全境拦截不再跳焦点。①代码块内=光标处插两空格（既有）；②光标在列表/引用「标记头」内或行首空白区=**行首插两空格**（嵌套列表——列表续项后光标恰在标记后，Tab 即缩进成子列表）；③行中间=光标处插两空格；④**Shift+Tab=行首反缩进**（删最多 2 个空白，嵌套列表升回去；无缩进时只吞按键防跳焦点）。实现坑：反缩进/标记头判定必须用「完整行」——光标前缀在光标处于行首时是空串（单测当场抓住）。E2E 新增 Q4/Q5 键盘流场景（合成 Tab keydown + 投影断言：续项→Tab 嵌套→打字→Shift+Tab 反缩进全链）。11 项新增单测（173 总）、49 场景全绿。另：deploy.mjs 探活窗口 40s→60s（冷启动偶尔超 40s）。

## 0.2.8（2026-09-25）

- **工具栏 sticky 化（用户三报「工具栏现在是透明的」的根治重构）**：v0.2.7 的让位方案让工具栏下移到编辑器顶后，暴露出更深的两个问题——①滚动时编辑器 padding 让位随内容滚走，absolute 工具栏（钉输入卡不随滚动）叠上正文且无背景（用户看到的「透明」）；②占位逻辑与附件栏/滚动互相纠缠。v0.2.8 重构：**工具栏挂进滚动容器（grow 内、overlay 前）+ `position:sticky;top:0`**——永远钉在滚动视口顶，内容从它下方滚过永不重叠；附件栏在滚动容器外（实测 railInScroll=false），scroll 顶=附件栏底，**天然让位**（v0.2.7 的让位函数/变量整体删除）；新增不透明背景（暗 rgba(28,30,36,.95)/浅白，分隔线并入 border-bottom）；占位从「编辑器 padding 44px」改为「sticky 流内 38px + padding 6px」，总高不变；box-sizing:border-box 保证含边框精确 38px。**挂载 bug 修复**：首版改造后工具栏消失——appendChild 被移除而 grow 块用 querySelector 找未进 DOM 的元素，改 `card.__dshClToolbar` 引用传递 + 搬入逻辑移出 `if (!ov)` 块。
- **E2E INV-7 判定修复（测试基建）**：D1-cjk-300（中文软折行场景）在 v0.2.8 稳定误报——采样 offset 178 的字符恰落在软折行点，collapsed caret 在折行点产**两个等价 rect**（上一视觉行尾 + 本行首），原判定恒取 rr[0]（行尾）与行首期望比较必差一行。实时字符 rect 与测量记录完全一致（caret 无错，判定取错 rect）。修法：全部候选 rect 任一匹配即通过。v0.2.5→v0.2.8 的 1px 布局微调让折行点恰好挪到采样点，纯属踩中运气。
- 验证：滚动场景工具栏钉视口顶（卡内 y=80 恒定、内容滚 1200px 不叠）+ 有/无附件几何（让位/回位）+ computed 样式（sticky/白底/规则齐）+ 视觉确认（实心工具条、正文从下方经过）+ 单测 162 / E2E 47 全过。

## 0.2.7（2026-09-25）

- **图片附件在场时格式工具栏盖在缩略图上（用户二报「问题依旧」的真正主 bug）**：官方附件栏（粘贴/拖入图片后的缩略图行）渲染在输入卡顶部 y=10-74、编辑器上方（官方自身布局正确）；本插件常驻工具栏 absolute top:0 也在卡顶——附件在场时 B/«/链接等按钮直接悬浮盖在图片上（用户截图实锤）。修复 `offsetToolbarForAttachments`（挂 render 主循环）：检测「编辑器外的 img」→ 向上找全宽祖先取附件栏底边（几何法，不依赖官方 hash 类名，升级免疫）→ CSS 变量 `--dsh-cl-bar-top` 驱动工具栏下移到附件栏底 +6px。编辑器 44px padding-top 占位无需变（工具栏贴编辑器顶后底边距内容顶仍有 6px 呼吸）；附件移除变量清零回位。实测：有附件 toolbarTop 0→80 与附件栏零重叠、无附件回 0 变量未设；单测 162 / E2E 47 全过。

## 0.2.6（2026-09-25）

- **图片附件在场时 @// 候选菜单悬空溢出（官方定位 bug 兜底修复，用户报「标签溢出了，而且颜色也不对」）**：官方 input-trigger 候选菜单向上展开时锚定「输入组件容器顶」——输入框带图片附件（附件栏占编辑器上方 66px+）时锚点不随之下移，菜单悬空 84px、整体浮出输入卡 300px+ 盖在消息流上（白卡片悬在打字区上方，菜单内文件名条目全露在输入框外）。三组对照实测定责（e2e/diag-menu-pos.mjs）：无图 gap=12px 正常 / 有图 gap=84px / 抵消插件 padding-top 不变 → 官方自身问题非本插件引入。兜底 `fixTriggerMenuPos`（挂 render 主循环）：菜单底边与编辑器顶间隙 >30px 时用 **transform:translateY** 平移贴回编辑器上沿——实测官方菜单是 absolute + **bottom:4px 锚定**，bottom 锚定下 margin-top 零位移（首版 marginTop 方案实测不动、且 gap 不收敛导致叠加正反馈失控累积到 7704px，已废弃），top 会与 bottom 冲突拉伸高度，translateY 与官方无 transform 的定位零冲突且 rect 实测含 transform 天然幂等收敛（修正 72px 一轮到位）。600px 上限防不可控叠加；只管与输入卡横向重叠且向上展开的菜单。回归：diag-menu-pos.mjs B 组 gap 84→12px（与无图基线一致）。附：官方图片附件胶囊（64×64 缩略图）无文件名标签，用户说的「标签」=菜单里的文件名条目。
- 一次性诊断脚本四件（e2e/diag-image / diag-at / diag-longname / diag-img-menu，CDF 直连复刻「粘贴图片/@ 引用」真实管线并 dump DOM 几何）留档可复用；其中 diag-menu-pos.mjs 兼作本修复的回归验证（B 组 gap 应 ≤ ~12px）。

## 0.2.5（2026-09-25）

- **引用自动续项 / 空项退出**（与列表续项同款交互，用户需求）：引用行（`> ` 开头，`>text` 无空格也认）行尾按 Enter/Shift+Enter 自动续「> 」前缀（缩进保留）；空引用项上按 → 删除标记退出引用，行原地变普通行（**两次 Shift+Enter 结束引用**：第一次续出空引用行、第二次退出）。光标不在行尾放行官方；引用行内的列表标记（`> - 项`）按引用续（引用语境优先）。9 项新增单测（162 总）+ E2E 新增 Q 组键盘流场景（合成 Enter keydown 走插件捕获层 + 投影断言 op，顺带回归列表续项）。

## 0.2.4（2026-09-25）

**E2E 自动化测试体系建成 + 长文本五处真 bug 修复（全部由测试矩阵定位）。**

新增 `e2e/`：一条命令（`node e2e/run-e2e.mjs --with-deploy`）在真浏览器里跑 44 个场景 × 10 条不变量——六种注入通道复刻真实输入形态（字面 `\n`/字面 `\r\n`/逐行 br/insertParagraph/合成 paste/刷新恢复多 p），逐字符对比「渲染段内字符真实 rect vs 编辑器字符 rect」（INV-3）、行距均匀性（INV-6，多余空白探测器）、caret 采样对齐（INV-7）等。详细设计见 `e2e/README.md`。

测试暴露并修复的 bug（此前 25/44 场景失败）：

- **渲染文字整体偏低 3px**：段定位用了「字形盒顶」，但段内字符随段自身行盒排版（字形顶 = 段top + half-leading(行高27/字形21 → 3px)）。历史验证测的是「段定位值 vs 测量值」（定义上恒等），从未测过真实视觉位置。修法：段 top 减 half-leading。
- **CRLF 粘贴「多余空白/行错乱」（用户头号问题）**：段 text 里的 `\r` 经 `innerHTML` 被浏览器规范化成 `\n`（HTML 解析规则），pre-wrap 段内凭空多出真换行。修法：段 text 剔除 `\r`（视觉零宽，与编辑器换行语义一致）+ escapeHtml 补 `&#13;` 实体通道。
- **行内代码文字恒低 1px**：cd/img 胶囊 `padding:1px 5px` 横向有负 margin 补偿、垂直没有——绝对定位段的内容下移 padding-top。修法：`margin:-1px -5px` 四向补偿。
- **大段粘贴形态下代码块等宽完全失效**：MONO 打点循环的行号推进只认 br/para 锚，字面 \n 单文本锚跨全部行使 lineIdx 恒 0；且单锚跨 N 行时 DOM 无法按行换字体。修法：回退 v0.1.x「整框等宽」语义（文档含已闭合代码块 → 编辑器整框 + 渲染全段同步切等宽合成栈）。
- **Maple Mono NF CN 漏剔**：合成栈未滤名含 NF CN 的字体（自带中文 1.2em 字形），代码块中文 advance 两侧不一致。修法：CJK 关键词清单补 "nf cn"（与 0.1.x 版本的度量保护一致）。

## 0.2.3（2026-09-16）

- **长文本粘贴「一片空白」真正根因修复**：粘贴的大段文本在编辑器里是「单个文本节点内含字面 `\n`」（官方 paste 管线的 insertText 不把 `\n` 转成换行元素）——渲染侧行列表按 `\n` 切成了 N 行，但字符测量的行号推进只认 DOM 换行元素（br/段落边界），字面 `\n` 不推进——1669 个字符全被记到第 0 行，而第 0 行的文本只有 40 个字，后续字符取字越界返回空串 → 40 个渲染段只有第 1 段有字、其余全空 → 可见区一片空白（刷新后正常是因为草稿恢复走 setDraft 产多段落结构，不经此路径）。修法：测量的行号推进同样认文本节点内的字面 `\n`（与 `split("\n")` 的行边界一致，off 用负偏移补偿保持行内偏移语义）。

## 0.2.2（2026-09-14）

- **大段粘贴「一片空白」修复（三层防御）**：①锚表对未知元素（B/I/STRONG/DIV 等富文本粘贴产物）从「忽略内容」改为继续下钻收集文字——之前粘贴的富文本结构整个不进锚表，渲染层空转 + 透明化已生效 = 一片空白（文字在编辑器里透明不可见，发送时却在）；②锚表漏收检测兜底——编辑器有文字但投影文本全空时自动回退官方显示（不透明化）；③渲染抛错兜底——测量/解析异常时同样回退官方显示，另加 measureAnchorChars 逐锚容错（批量编辑中间态单个节点测量失败跳过该锚，下一轮自愈）。

## 0.2.1（2026-09-13）

- **列表自动续项 / 空项退出**：光标在列表行尾按 Enter/Shift+Enter 自动续项——无序列表保持符号（- * +）、有序列表递增编号（1. → 2.、3) → 4)）、缩进保留；空列表项（只有标记没内容）上按 → 删除标记退出列表，行原地变普通行（两次 Shift+Enter 即结束列表：第一次续出空项、第二次退出）。光标不在行尾放行官方（行内普通换行）。坑：退出删除不能走 execCommand("delete")——无 user activation 时静默 no-op（CDP/特殊环境不可靠），改合成 beforeinput deleteContentBackward（Lexical 不校验激活）。15 项新增单测（152 总）。

## 0.2.0（2026-09-13）

**架构级根治：字符锚定渲染。** 渲染层不再自己排版——逐字符测量编辑器的真实坐标（Range rect），渲染字符绝对定位画在同一坐标上。字体/字重/断行/行结构/等宽打点时序/任何度量维度怎么变，渲染物理跟随编辑器——漂移/抖动/错位从架构上不存在（历史 7 轮对齐修复的根因是「两套排版引擎各自计算」，现在只剩一套）。

- 渲染管线：锚表 → 等宽打点（提前，布局定型后再测量）→ 块级背景聚合（代码块/引用段整块一个矩形——容器内容宽、首行字符顶到末行字符底，垂直无缝右缘齐平；语言标签 CSS right 定位）→ 逐字符 Range rect 测量 → 按「同样式+同视觉行+横向连续」合并成定位段 → 绝对定位输出；代码块行背景/引用背景按行字符包围盒画背景块，语言标签定位到围栏行右侧
- 首行动态对齐偏移机制退役（字符坐标系天然吸收全局偏移）；围栏字符从「隐藏+光标行显示」改为统一淡化显示（消灭切换闪烁）；窗口 resize 重渲染监听
- 验证：行内格式/代码块/块后行/软换行（2 视觉行）全部偏差 ≤0.02px；真键盘连续打字 358 帧逐帧 delta=0.00；caret 与渲染字符 diffX=0；展开/选中/工具栏回归全过

## 0.1.1（2026-09-13）

五轮实测修复（层叠与对齐体系成型，137 项单测）：

- **代码块内光标不可见**：编辑器设 `position:relative;z-index:1` 提到渲染层（z:0）之上——光标/选区随编辑器内容自然盖在渲染文字上；`caret-color` 显式设色（透明化后 `caret-color:auto` 在部分 Chromium 跟随 text-fill 画透明光标）。中间踩过「渲染层压 z-index:-1」的反例：负 z 掉到输入框背景之下，渲染文字整体不可见。
- **击键抖动**：首行对齐偏移改增量算法（测量差 + 当前偏移 = 绝对目标，一步收敛）——「差值直接赋值」会在两个值间振荡，基准漂移大时振幅肉眼可见。
- **选中重影**：`::selection{color:transparent}` 防止 Chromium 选中时用反色把透明文字重绘成白字（双影）。
- **选中无色**：::selection 规则一旦匹配，未声明的属性取初始值——必须同时显式写 `background`（品牌蓝半透明，明暗两套）。
- **行内格式光标漂移/打字抖动（字符级对齐）**：标记字符（`**`、`` ` ``、`~~`、`[]()`）保留占位只淡化显示（`.dsh-cl-mk`），粗体/标题改 `-webkit-text-stroke` 伪粗（paint-only 零 advance 变化）——渲染行与源文字符流逐字符等宽，光标在格式区间精确贴齐（实测偏差 0~0.1px）。
- **展开键失效**：展开态补 `height:70vh`（只有 max-height 时高度仍由内容决定，内容少时点了纹丝不动）。

## 0.1.0（2026-09-12）

初版（129 项单测 + 实例实测 E2E 全过）：

- markdown 实时渲染（透明编辑器 + overlay 渲染层，源文原样保留）
- 代码块体验：未闭合围栏不渲染、Shift+Enter 自动补全封闭块、↓ 跳出、Tab 缩进、语法高亮、语言标签、行级等宽
- 常驻格式工具栏 + 选区浮动格式条
- 70vh 展开 / 大段粘贴自动包代码块 / Esc 分发 / @ 引用胶囊兼容
