# Changelog

## [0.0.25] - 2026-09-11

### Deprecated

- **停止更新（End of life）**：DSH `0.1.5` 起官方自带工作区文件树与文档预览
  （内置 bundle `@deepseek-ai/dsh-client-ui-sidebar-files` +
  `@deepseek-ai/dsh-client-ui-sidebar-documentpreview`，右侧栏文件树 +
  Markdown / 高亮代码 / 图片 / PDF / HTML / 纯文本预览），覆盖本插件核心能力
  且为第一方实现（无布局补丁、无非公开 API 依赖、随 dsh 自动升级）。
  本插件停止维护，README 顶部与 npm deprecate 提示均已注明迁移方式
  （卸载 + `patch-layout.mjs --restore` 还原布局 bundle）。已发布版本仍可安装
  （实测兼容 0.1.5-rc.1），但不再修复问题、不再适配后续 dsh 版本。

## [0.0.24] - 2026-09-10

### Fixed

- **raw 图片路由与 SSE 路由补齐鉴权 fence**：此前仅文件 RPC 通道经
  `ctx.connection.requestRejection` 做 Host/Origin + 浏览器鉴权检查，raw 图片
  （`/dsh-plugin-files/raw/<path>`）与 SSE 事件（`/dsh-plugin-files/events`）
  两条路由缺失；现三条路由统一过 connection 层鉴权 fence，行为一致
  （三者均不在 dsh 0.1.2+ token 墙覆盖范围内，故各自自带校验）。
- **FSWatcher 增加 `error` 监听**：被监视目录被删除 / 受权限限制时，降级清理该
  目录的监视状态并允许后续自动重试，不再可能因未捕获异常崩掉 dsh 进程。
- **启动路径布局补丁检查异步化**：补丁检测不再阻塞插件启动路径。
- **package.json `files` 收紧 `lib/` 产物**为 `lib/index.js` / `lib/client.js` /
  `lib/client.js.map` 三个文件，不再携带 lib 下其它多余产物。

## [0.0.23] - 2026-09-10

### Fixed

- **适配 DSH 0.1.5-rc.1（cordis 4.0.2）：启动即崩 "cannot get property "webServer"
  without inject"**。根因：文件通道此前经 `ctx.connection.rpc.handle()` 注册，该 API
  内部在 **connection 服务自己的上下文**上开 effect 访问 `owner.webServer`——cordis
  4.0.2 的服务解析下，connection 服务宿主上下文的 fiber 链上看不到 webServer 服务
  （0.1.5 官方树里没有任何第一方调用方走这条路径：dsh-api-gateway 用 `rpc.intercept`，
  dsh-client-connection 自己也只通过 `ctx.inject(["webServer"], …)` 授权分支访问），
  apply 一进通道注册就抛错，整个 profile 加载失败、dsh 进程退出。修复：不再依赖
  `rpc.handle`，改为在本插件自身（webServer 已授予 inject）的上下文直接
  `ctx.webServer.register` 前缀路由，内置与 `connection.rpc.call` 完全一致的线上协议
  （POST `CHANNEL/endpoint`、`client-request`/`server-response` 信封、404/415/400/413/
  500 状态语义、Host/Origin + 浏览器鉴权围栏经 `connection.requestRejection` 对齐），
  客户端零改动。已用 token-cookie 实测：list 返回真实目录树。
- **布局补丁针对 0.1.5 的 rightbar 框架整体重写**（`patch-layout.mjs` 新增 `npm-015`
  变体）：0.1.5 的 ui-layout 把 details 列重构为通用 rightbar 面板系统
  （`RightbarColumn`、track/fullscreen 状态机、keyed `main` 槽），旧 details 列锚点
  全部失配。新变体在 sidebar 与 center 之间重加 explorer 列：`computeColumns` 预算
  explorer 轨道、store/LayoutController 增 `setExplorer`/`setExplorerOccupied`、
  占位同步 effect、槽位声明与拖拽把手；显式打开 rightbar 面板时 explorer 自动让位
  （0.0.21 细节列优先规则的延续）。标记表按补丁代际拆分（`PATCHED_MARKERS` /
  `PATCHED_MARKERS_015`），新旧 dsh 版本均可检测与自动重打。
- **修复首版 0.1.5 补丁的作用域错误**（浏览器端 "layout is not defined"、
  ui-layout 整体加载失败）：占位同步 effect 引用的 `layout` 在 0.1.5 声明于
  service+root registration effect 回调内部（0.1.2 时在 apply 层级），已把该 effect
  移入 registration effect 作用域内。
- **修复预览面板霸占整个中央列**（文件预览竖着堆在会话区上方、会话被挤到底部）：
  0.1.5 变体拆分旧补丁的 `css.centerSplit` 替换时漏掉了 `flex-direction: column →
  row` 的翻转，预览槽（CenterColumn 首个子元素）竖向堆叠占满全宽。已恢复行方向：
  预览在左、会话 seat 在右并排。

## [0.0.22] - 2026-09-07

### Fixed

- **与 maid-atelier 皮肤同装时，点文件树文件预览「弹出内容后立刻消失」**。
  根因是层叠冲突：皮肤把立绘舞台（palace + 角色）作为 `position:absolute; z-index:0`
  的层铺满整个中央列（centerCol），聊天内容靠 `position:relative` 浮在其上——而预览
  面板是**非定位元素**，画在立绘之下。点文件的瞬间 `fe-preview-in` 入场动画（含
  opacity/transform）临时创建层叠上下文，预览短暂可见（「弹出来内容」）；0.18s 动画
  一结束上下文消失，预览回落到立绘之下被整片盖住（「迅速关掉」），但它 775px 的占位
  实实在在挤窄了会话列（「放大缩小保留」）。修复：`.preview` 增加 `position: relative`。
  headless 复现与修复验证均通过（elementFromPoint + 截图）。
- **与 maid-atelier 皮肤同装时，预览面板顶部 76px 被皮肤顶帘盖住**（标签栏整条不可见、
  正文标题被切）。顶帘（`[data-skin-chrome='top-trim']`）是绝对定位、高 76px、
  `z-index:1` 的装饰带，铺在中央列顶部，且由皮肤在 React 子节点之后 append——与预览
  面板同层（z 1）时靠 DOM 顺序画在面板之上。会话头部有自己的 z 21 不受影响，预览
  面板没有。修复：`.preview` 抬到 `z-index: 2`（与相邻拖拽把手 `.handle` 一致），
  面板的不透明背景把预览半区的帘子自然挡掉，帘子仍完整覆盖会话半区；同时仍低于
  皮肤功能层（会话头部 21 / cordis 面板 40 / shell overlay 1000）。

## [0.0.21] - 2026-09-07

### Fixed

- **打开右侧详情栏闪一下就消失**。根因：布局补丁把 260px 文件列常驻进拥挤让位链后，
  打开 details 的宽度预算从原生 ~1220px（侧栏280 + 详情300 + 中央640）涨到
  ~1480px——窗口宽度在两者之间的用户点开详情：第一帧渲染 → 拥挤 solve 把 details
  挤成 0 → 面板闪现即消失。修复：**显式打开详情栏时文件列自动让位收起**（
  `panels.details > 0` → explorer 宽 0），宽度预算回到原生水平；关闭详情文件列自动
  恢复。补丁脚本相应新增 `npm-delta3` / `desktop-ci-delta3` 变体（针对已带 0.0.19/20
  occupancy 补丁的 bundle，仅更新 computeCall 一行）。

## [0.0.20] - 2026-09-07

### Fixed

- 全链路代码审查（子代理逐变体模拟验证）后的三处收尾：
  1. **`npm-delta` 变体缺 watch 块**（中危）：0.0.16/17 旧补丁（无 occupancy）用户经
     delta 打完后没有 `slots/changed` 同步——运行时卸载插件列不即时收起（重启后才由
     inject hook 兜底）。delta 表补上 watch 块注入，已用构造的旧补丁态产物实测命中。
  2. **host 自愈 marker 是脚本的弱子集**（形式问题）：`src/index.ts` 的清单缺
     `renderSlot("explorer.preview"` 且 explorerCol 用短前缀——受损/半回退 bundle 可能
     过 host 检查却过不了脚本校验，自愈永不触发。host 清单已与脚本 `PATCHED_MARKERS`
     7 项逐字同步。
  3. **`--target` 污染共享 pristine 备份**（运维隐患）：脚本对任意 `--target` 文件也会
     刷新仓库内的 `client.js.orig`。现在备份簿记仅在默认 profile 路径下执行，
     `--target` 时明确跳过。

## [0.0.19] - 2026-09-07

### Fixed

- **0.0.18 的 occupancy 门控把列永久收起了（装着插件也不显示 explorer 列）**。
  根因是时序：布局 store 的 actions 要到 root entry **首次渲染组装**时才经
  `inject` hook 交给 `LayoutController`（`attachPanels`），而 0.0.18 把初始
  occupancy 同步放在了 ui-layout 的 apply 阶段——那时 `#panels` 还是 undefined，
  同步被空实现吞掉；workbench 的注册又恰好发生在同一调用栈内（声明提交触发），
  `slots/changed` 事件在监听器安装之前就已发完，之后再无事件 → `explorerOccupied`
  恒为 false → 列永不出现。修复：occupancy 同步**同时**放进 root 注册的 inject
  hook（actions 刚 bind、首帧渲染前，此时插槽查询已能拿到已注册条目），
  `slots/changed` 监听继续负责运行时装卸；补丁脚本对应新增 `npm-delta2` /
  `desktop-ci-delta2` 变体与 marker。

## [0.0.18] - 2026-09-07

### Fixed

- **卸载插件后残留一条空白侧边栏（[issue #2](https://github.com/Pasumao/dsh-plugin-workbench/issues/2)）**。
  根因：布局补丁给 `dsh-client-ui-layout` 注入的第四列（explorer）是无条件渲染的——
  网格轨道恒为 `panels.explorer`（默认 260px，带侧栏底色与右边框），列内容来自本插件注册的
  `explorer` 插槽；卸载本插件后插槽清空，但补丁仍留在布局 bundle 里（且插件的
  `ensureLayoutPatch` 自愈机制还会在下次启动把补丁打回去），于是永远残留一条空列，
  刷新/重启都无法消除。修复：布局 store 新增 `explorerOccupied` 标志，`ui-layout` 的
  apply 现在监听 `slots/changed` 事件，实时统计 `explorer` / `explorer.preview` 插槽的
  注册条目数——没有任何插件贡献时列宽归 0 自动收起，装回插件即刻恢复。
  补丁脚本新增 **delta 变体**（`npm-delta` / `desktop-ci-delta`）：已带旧版补丁的 bundle
  无需还原即可原地升级到带 occupancy 门槛的新补丁；全新 bundle 走原有完整变体。
  `src/index.ts` 的自愈 marker 列表同步追加 `explorerOccupied`。
  同时新增 `patch-layout.mjs --restore`（卸载恢复命令）：用首次打补丁时备份的
  pristine 副本把 `dsh-client-ui-layout` 还原为官方原始 bundle；备份策略改为
  「dsh 升级后首次重打时自动刷新 pristine」，并带两道防呆（目标是更新的原生 bundle、
  或备份属于更旧 dsh 版本时拒绝回滚，`--force` 覆盖）。

## [0.0.17] - 2026-09-05

### Fixed

- **与 maid-atelier 皮肤同装时，页面顶部/底部装饰横条从画面一半才开始显示**。
  根因：本插件 CSS 末尾一段旧「skin chrome fix」规则（`translate: calc(var(--maid-sidebar-width) +
  var(--dsh-explorer-width) + var(--dsh-preview-width))`）写于皮肤装饰条还是
  `position: fixed` 钉满整窗的时期，用来把它推过侧栏和文件列；maid-atelier 升级到
  上游 78722ef 后装饰条改为挂在会话列内部的绝对定位元素（本就从列左缘开始），
  这条规则反而造成双重偏移（列左缘 540px + translate 540px = 从 x≈1080 起画，
  右半截伸出屏幕外）。已整段删除——装饰条定位由皮肤自持，插件不再外部平移。

### Changed

- README：首屏新增横幅图（`docs/banner.svg`），徽章顺序调整并补充 npm downloads。

## [0.0.16] - 2026-09-04

### Fixed

- **适配 dsh 0.1.2-rc.1 布局 bundle**：两处补丁锚点失配导致插件启动时自动重打失败
  （启动日志 `ui-layout patch exited 1`，explorer 布局不生效）。⑴ `css.explorerCol.rule`：
  detailsCol 边框样式 `border-left:1px solid var(--dsw-alias-border-l2)` 在新版改为
  `.5px solid var(--dsw-alias-border-l3)`，锚点与注入规则同步更新；⑵
  `appframe.explorerColumn`：DetailsColumn 的 children 在新版被 `SessionProvider`
  包裹，锚点按新结构更新。其余 22 个锚点与 `computeColumns` 函数体逐字节未变。

## [0.0.15] - 2026-08-29

### Fixed

- **DSH Desktop 下文件树列不显示（[issue #1](https://github.com/Pasumao/dsh-plugin-workbench/issues/1)）**：
  布局补丁的锚点按 npm 构建的编译产物逐字节写死，而 DSH Desktop 安装包内置的
  `dsh-client-ui-layout` 是同一版本的另一构建产物（保留注释、缩进不同、折叠侧栏宽度用
  `COLLAPSED_SIDEBAR_WIDTH` 常量），`computeColumns` 锚点失配导致补丁每次启动整体放弃——
  插件加载正常但界面永远不出现。补丁脚本现按目标文件自动探测构建变体（npm / desktop-ci，
  后者锚点存于 `scripts/layout-anchors.desktop-ci.json`），两种产物都能正确打补丁。

## [0.0.14] - 2026-08-29

- 文档：对标生态头部插件优化 README（首屏新增「装完你会看到」；布局补丁话术改为开箱即用）；
- 元数据：npm description 重写（突出可编辑/右键文件操作/@引用），keywords 7 → 15；
- 新增英文版 README.en.md 与双语切换行。
## [0.0.13] - 2026-08-26

### Fixed

- **预览分栏宽度拖拽：一拖就骤然缩小到最窄，之后无法拖动、无法复原**。根因：
  拖拽计算"预览最大宽度"时用 `handle.parentElement` 测量中间列宽度，但 slot 系统把
  slot 内容包在一层 `display: contents` 的包装元素里——该包装层不参与布局，
  `getBoundingClientRect().width` 恒为 **0**，于是
  `max = max(240, 0 - 240) = 240`，第一次 `pointermove` 就把宽度钳到最小宽度 240，
  之后每次移动都被钳在 240，表现为骤然缩小且拖不回来（100% 复现，与窗口/鼠标操作
  无关）。修复：`laidOutParent()` 跳过所有 `display: contents` 层，取真正参与布局的
  flex 容器（中间列）再测量；持久化宽度恢复时同样用它校准。
- 拖拽健壮性（防同类"卡死"）：`setPointerCapture` 保证 `pointerup` 在指针移出窗口后
  也能派发并清理监听器；监听 `pointercancel`；每次 `pointerdown` 防御性移除上一次
  残留的监听器；组件卸载时清理窗口监听器；拖拽中每步实时重测中间列宽度。

### Added

- **预览宽度持久化**：拖拽后的分栏宽度写入 localStorage，刷新/重开会话后恢复
  （自动按当前窗宽 clamp 在有效区间内）。


## [0.0.12] - 2026-08-25

- 相关插件段新增 dsh-plugin-windows-guard（Windows 环境防坑守则 skill 插件，互相引流）。


本项目所有重要变更都会记录在此文件。
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)，
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。

## [0.0.11] - 2026-08-21

### Added

- **README 互相引用**：新增「相关插件」段，列出同系列已发布插件（npm / GitHub 链接 + 一句话说明），互相引流。

## [0.0.10] - 2026-08-21

### Fixed

- **「在资源管理器打开」不生效（窗口打开但不可见）**：Windows 下 `spawn` 传
  `windowsHide: true` 会让新开的资源管理器文件夹窗口以**隐藏状态**创建
  （CREATE_NO_WINDOW 沿请求一路传导，窗口存在但 `visible=False`，用户什么都
  看不到）。`explorer.exe` 是 GUI 子系统程序，不带 `windowsHide` 也不会闪
  控制台；`reveal` 端点现在对 explorer.exe 全部不传该标志（含 WSL 转译分支），
  资源管理器窗口正常弹出并选中目标文件

### Added

- **整列右键菜单**：不只文件行——在左栏任何空白处、头部、剪贴板条上右键
  都弹出当前工作区目录菜单（新建文件 / 新建文件夹 / 粘贴 / 全选 / 撤销 /
  复制路径 / 在资源管理器打开 / 在系统中打开 / 刷新）；右键菜单自身不再触发
  浏览器原生菜单
- **@ 在消息中引用（Claude Code 式文件引用）**：文件右键菜单新增
  「@ 在消息中引用」，把 `@相对工作区路径`（自动带尾随空格）插入聊天输入框
  光标处；发送后消息里的 `@相对路径` 渲染为超链接（点击在工作台预览中打开
  该文件），语法为 `@` + 相对工作区路径——`@src/a.ts`、`@README.md` 匹配，
  绝对路径 / 邮箱 / 无路径形状的普通 `@文本` 一律按原样显示、无特殊含义；
  中文句子标点跟尾视为普通文本
- **拖文件进会话**：把左栏文件（含多选）拖到聊天区任意位置（不在左栏内）
  即把路径文本插入聊天输入框光标处；拖到左栏目录行仍是原有的移动操作
- **任意位置点击取消选中**：选中文件/文件夹后，点击树外任何地方（会话、
  其他栏、左栏头部）都会清除选中；树内空白/提示行点击同样取消

## [0.0.9] - 2026-08-21

### Added

- **布局补丁自动修复（自愈）**：插件 host 端在启动时自动检测
  `dsh-client-ui-layout` 客户端 bundle 是否还带 explorer 列补丁；若已被
  DSH 升级 / `pnpm install` 覆盖还原（这正是 workbench 列无声消失的根因），
  会自动重跑 `scripts/patch-layout.mjs` 重打。幂等（已打则跳过）、非阻塞
  （spawn 后台执行，不影响启动）、安全（锚点不匹配时脚本 abort 不写文件，
  仅记录警告，插件照常启动）。

## [0.0.8] - 2026-08-20

### Fixed

- **删除/复制被占用文件夹不再报裸 "permission denied"**：Windows 上文件夹或其
  内部任一文件被其他程序占用（编辑器、资源管理器、终端 cd 其中、杀毒扫描）时，
  改名/删除会以 EPERM/EBUSY 失败。host 端 `rename` / `delete` / `copy` 端点
  现在先做**有界重试**（3 次、150/300ms 间隔），可自动扛过索引器/杀毒的瞬时
  占用；仍失败时返回可操作的提示「`名称` is in use by another program —
  close any editor/Explorer view of it (or a terminal inside it) and retry」，
  替代原先无指引的 "permission denied"
- **树内复制文本不再误触发文件复制**：在树中选中文件/文件夹后，再去选中树内
  的实际文本（如行内错误提示）按 Ctrl/Cmd+C/X 时，此前会拦截浏览器原生复制、
  把选中的文件放进插件剪贴板。现改为：存在非空文本选区或焦点在可编辑元素时，
  快捷键交给浏览器原生处理，复制的是文字而非文件

## [0.0.7] - 2026-08-19

### Fixed

- **跨工作区粘贴（复制冲突）不再报 Zod 校验错误**：复制/粘贴遇到目标已存在时，
  host 端此前返回自定义错误码 `code: 'exists'`，而通用 RPC 通道的错误体受核心
  封闭错误码联合校验（`rpcErrorSchema`）约束，客户端解析直接抛 `invalid_union`，
  覆盖确认对话框根本不会出现。现改为在**成功值**中返回 `exists: true` 标志
  （OS 文件管理器语义），客户端据此询问是否覆盖，行为不变、报错消除

## [0.0.6] - 2026-08-19

### Added

- **在资源管理器打开**：右键菜单新增「在资源管理器打开」
  （`menu.revealInExplorer`，同 VS Code 的 Reveal in File Explorer），经
  loopback RPC 的 `reveal` 端点把文件/文件夹定位到系统文件管理器——
  文件在所在文件夹中被选中（Windows `explorer /select,`、macOS `open -R`），
  文件夹直接打开（`explorer <dir>` / `open <dir>`）；WSL 先经 `wslpath -w`
  转译再交给 Windows 桌面，桌面 Linux 用 `xdg-open`（文件 → 父目录）；
  路径仍先经 `ctx.fs.resolve → stat`（沙箱一致的路径解析）再执行
- **Markdown 渲染预览**：打开 `.md` 文件默认渲染（markdown-it，`html: false`
  转义原始 HTML，代码块复用已注册的 highlight.js 语言高亮），标签栏按钮
  一键切换回可编辑源码视图，再次点击切回渲染；相对路径图片自动重写到同源
  字节路由 `/dsh-plugin-files/raw/<path>`，README 插图可直接显示；
  超过 256KB 的 md 默认进源码视图并提示
- **打开文件磁盘变更检测**：宿主按标签集合对每个文件所在目录做 `fs.watch`
  （父目录监听可存活编辑器原子重命名），事件按路径去抖后经同源 SSE 路由
  `/dsh-plugin-files/events` 推送；干净的标签页自动重读同步，带未保存编辑的
  标签页保留草稿并在标签上显示「⟳」徽标（点击重新加载、放弃未保存编辑）；
  插件自身保存会在短窗口内抑制回波，避免保存被当成外部变更
- **批量删除**：多选（Ctrl/⌘+点击、Ctrl+A）后，右键菜单显示「删除选中的
  N 项」，Delete 键同样生效；先删子项再删父项，已删除文件夹下的项自动跳过
- **拖拽移动**：把文件/文件夹（含整个多选）拖到任意目录行上松手即移动；
  拖到树空白区域 = 移入当前目录；目标目录高亮描边，文件夹拖进自身或自己
  子目录自动忽略，拖到所在目录本身为无操作
- **操作可撤销**：右上角「↩」按钮或 Ctrl+Z 撤销最近的操作（每个工作区
  独立、上限 30 条），覆盖：复制（撤销删除副本）、剪切/拖拽移动（撤销移回
  原位并重定向已打开标签）、重命名（撤销改名）、新建（撤销移除）、删除。
  删除改为「可撤销删除」：把项目瞬移（重命名）进其旁的隐藏 `.dsh-trash`
  文件夹（不复制字节），撤销即移回原位；撤销栈溢出时自动清空被挤出条目的
  回收站内容，回收站文件夹在树中隐藏显示

## [0.0.5] - 2026-08-17

### Added

- **图片预览**：打开 png/jpg/jpeg/gif/webp/avif/bmp/ico/svg 直接在预览栏渲染，
  不再显示「二进制文件」提示。字节经同源路由 `/dsh-plugin-files/raw/<path>`
  提供：宿主先经 `ctx.fs.resolve → stat`（沙箱一致的路径解析）再读字节，
  仅限图片扩展名、20MB 上限；加载失败显示内联提示，切换标签自动复位
- **编辑器行号栏**：左侧逻辑行号（VS Code 风格，软换行下仍按源行编号），
  与编辑区滚动逐帧锁定对齐；纯文本模式（md/txt/大文件）同样生效
- **右键菜单（VS Code 风格）**：
  - 文件：打开预览 / 复制路径 / 在系统中打开 / 重命名 / 删除
  - 文件夹：新建文件 / 新建文件夹 / 复制路径 / 在系统中打开 / 刷新 /
    重命名 / 删除（递归删除带确认）
  - 空白区域：新建文件 / 新建文件夹 / 复制路径 / 刷新
  - 菜单自动夹紧视口；点击外部 / Esc / 窗口失焦自动关闭；操作失败在
    对应目录行内联红色提示；重命名/删除自动维护已打开的标签页

### Changed

- 宿主 RPC 通道新增 `createFile` / `createDir` / `rename` / `delete` 四个
  端点（node:fs 实现，经 `ctx.fs.resolve` 统一解析；rename 拒绝覆盖已存在
  目标，delete 按 stat 决定递归）；`rpc.handle` 注册改为 effect 包裹，
  HMR/停用循环不再泄漏路由

### Fixed

- **右键菜单文字看不清**：maid-atelier 皮肤对 `[role='menu']` 有全局规则，
  会把菜单文字色劫持为「页面主题」色，而菜单背景跟随「workbench 主题」，
  两者不一致时文字与背景同色系。菜单颜色现已锁定到 workbench 主题变量
  （`!important`），任何皮肤组合下文字都与背景保持对比

## [0.0.4] - 2026-08-17

### Fixed

- 长中文文本（如小说 .txt）预览卡顿：无语法高亮的文件不再渲染高亮叠加层，
  直接由 textarea 显示文本；代码文件超过 64KB 自动降级为纯文本编辑，
  避免每次按键重新注入并布局几十万字符的两层文本
- **md 文件一律以纯文本模式显示**（不再做语法高亮叠加）：20KB+ 的 md 加载
  从约 60ms 降到约 10ms，彻底消除 md 的拖选错位与加载卡顿；大文件加载时
  先让浏览器绘制一帧再渲染内容，界面不再在打开文件的同一帧内冻结
- md 长文本：去掉悬停时反复弹出的「Ctrl+S 保存」提示；高亮层加粗改为
  纯颜色强调（粗体改变字宽会导致拖选时高亮层与文本换行错位），并增加
  每帧滚动锁定，保证拖选自动滚动时两层始终对齐
- **拖选错位根因**：textarea 底部会预留约一行给光标，导致其可滚动范围比
  高亮层大，长文件滚到接近底部时高亮层文本比选中框低约一行（选中框浮在
  显示文本上方）。已给高亮层补齐等量底部滚动余量，两层滚动范围完全一致
- 打开/切换含大量文件的目录不再卡顿：文件树行改为 memo 组件，点击文件
  只重渲染受影响的单行（此前每次点击都重渲染整棵文件树）；切换工作区时
  恢复已展开目录改为分批加载

## [0.0.3] - 2026-08-16

### Added

- 文件图标：常见格式按扩展名显示着色徽章（TS/JS/PY/HTML/CSS 等代码文件）、
  emoji（图片、音视频、压缩包、Office 文档、安装包等），目录展开/收起图标区分
- 自动换行开关：标签栏一键在「软换行（仅显示层换行，不改动文件）」与
  「长行横向滚动」之间切换；偏好写入 localStorage 持久化
- 高亮层与编辑层滚动槽对齐、tab 缩进对齐修复

## [0.0.1] - 2026-08-14

### Added

- 初始版本发布（https://github.com/Pasumao/dsh-plugin-workbench）：
  - VS Code 风格文件树列：跟随会话工作区、懒加载展开、每 2 秒自动刷新
  - 可编辑分栏预览：语法高亮层 + 透明 textarea、`Ctrl+S`/`Cmd+S` 保存、
    脏标记圆点、保存失败提示
  - 标签页：多文件同时打开、点选/拖拽排序/逐个关闭、收起/弹出
  - 明暗主题：VS Code 亮色/暗色配色一键切换（独立于 app 皮肤）
  - 按工作区隔离：每个 cwd 各自保存标签、激活文件、收起与展开状态
  - 内置 highlight.js：JS/TS、Python、JSON、HTML/XML、CSS、Markdown、Shell、
    Java、C/C++、C#、Go、Rust、SQL、YAML、INI
  - 布局补丁脚本 `scripts/patch-layout.mjs`：4 列布局 + explorer 槽位 +
    中心分栏（幂等、可回滚、锚点校验）
  - MIT 许可
