Commit Graph
31 Commits
Author SHA1 Message Date
figmar 5a7e2139a0 chore(sync): re-base the fork onto upstream dsh-better-sidebar v0.19.1
The fork was pinned at upstream v0.18.0. That is the entire reason the window
ran TWO sidebars and TWO near-identical panel toggles: DSH 2.0.10 ships its own
right sidebar (@deepseek-ai/dsh-client-ui-sidebar-right), and upstream only
adopted it in v0.19.0-alpha.1 — which arrived after our fork point. v0.19.1 is
also the one upstream line whose support range actually matches our host
(engines.dsh >=0.1.5-rc.1, 14 DSH peers ^0.1.5-rc.1, host = 0.1.5-rc.2), so it
is the only tag the host's startup precheck accepts.

What upstream v0.18.0 → v0.19.1 brings
- src/client/native/{index,surface,tab-adapter}.tsx: every tab the plugin can
  draw becomes an OFFICIAL native tab type + body, and an external plugin's
  descriptor registered later gets its native type too. This is what retires
  the fork's own right panel — and with it the duplicate toggle cluster
  (`toggleCluster` is gone from v0.19.1).
- native tab-chip icons via the `sidebar.right.pane.tab.title` seat, coloured
  built-in tab icons, file/folder glyphs from DSH's own `FileTypeIcon`.
- guide descriptions (DSH renders them while the guide lists <= 4 entries),
  tab bodies that fill the panel, and the 0.1.5-rc.2 adaptation work we were
  carrying by hand (peer/devDep pinning, primitives' transitive devDeps).

How the sync was done
Rebuilt the tree from the v0.19.1 tag rather than resolving 53 conflicted
files: our v0.18.0 delta was measured first and is only (a) one mechanical
rename `dsh-better-sidebar` -> `dsh-apes-sidebar` and (b) ten real edits. So:
tag tree + rename (scoped: code/config/scripts/tests/README, while the 46
historical docs/plans records and switch.sh's upstream package name stay
untouched) + re-applied deltas + our apes module.

Fork deltas re-applied on the new base
- src/client/apes/ (icons / locales / tabs): the three apes:* information-layer
  tabs (agents / apps / profile), registered through the same BetterSidebarService
  as the builtins — which is exactly why they now ride upstream's native adapter
  into the official sidebar for free.
- identity: package.json (name/version 1.2.0/description/forkedFrom), dsh.plugin.json
  (id/version), SIDEBAR_SERVICE_VERSION 1.2.0, README fork notice,
  docs/external-plugin-guide.md.
- scripts/switch.sh + the pnpm switch* scripts (apes <-> upstream two-line switch).
- tests/apes-tabs.spec.ts (unescaped: it starts from the v0.19.1 base).

Dropped
- the Phase A native-registration spike (`apes/native-tabs.tsx` + its spec): it
  re-derived by hand exactly what upstream's native layer already does — the
  hand-written bridge is dead weight now, and v0.19.1's own e2e/jsdom lanes
  cover the same seat contract far better.
- the unused apesProfileGuideDesc key and the two package.json.bak.* backups.

Verified on the new base: typecheck clean; build clean; 1351 passed / 9 skipped
(128 files), up from 1255 on the old base.
2026-10-03 22:51:09 +08:00
Menghuan1918 223b2545d9 refactor(shell): retire the plugin's own right panel and free windows
DSH 0.1.5 owns the right column (its native Sidebar hosts every tab type
this plugin registers), so the plugin's own right panel, its width drag /
new-session seed prefs, and the free-window feature are gone. The bottom
workbench is the only plugin-owned surface left, and its expand/collapse
control is registered into DSH's session-header utilities list slot (the
header's corner seat belongs to the native sidebar's own control).

- state: one `bottomSplits` tree; `makeDefaultState()` takes no arguments;
  `toggleBottomPanel` / `openTabInBottomPane` are the only landing
  reducers; `setBottomHeight` and `sanitizeState` share the
  `CONVERSATION_MIN` cap (a persisted tall height no longer snaps on the
  first drag).
- service: every landing in the workbench (creation or focus) expands it;
  `SIDEBAR_FEATURES` drops `'floatWindows'`; the open/close/activate tab
  pools cover the single tree.
- shell: `Sidebar.tsx` renders only the bottom workbench; the toggle lives
  in `sidebar/bottom-toggle.tsx`; layout-push pushes height only (no width,
  no `--dsh-sidebar-width`).
- prefs: `openByDefault` / `defaultWidthPercent` / `changesDiffFloat`
  removed from the schema, defaults, client parser, settings page, and all
  20 dictionaries; the retired keys in stored documents are ignored.
- tests: free-window specs deleted, panel/float assertions dropped,
  remaining suites adapted (124 files / 1287 tests); the mount lane asserts
  the header toggle and sweeps the native sidebar's guide page.
- docs: README / README_EN / guide §0,§11 / AGENTS test map.
2026-09-09 15:18:06 +08:00
Menghuan1918 ba0114749f feat(dsh): adapt to DSH 0.1.5-alpha.1
Move the plugin onto the DSH 0.1.5-alpha.1 baseline (peer floor
^0.1.5-alpha.1, CI pinned to the alpha dist-tag) and fix the two host
contract changes that alpha line introduced.

Baseline:
- all @deepseek-ai/dsh-* peer/dev deps -> 0.1.5-alpha.1; optional peer
  @deepseek-ai/dsh-client-ui-sidebar-right (declared, unused until the
  native-surface phase); devDep @deepseek-ai/dsh-agent-loop (inbox fold)
- dsh.plugin.json engines >=0.1.5-alpha.1; version 0.19.0-alpha.0;
  SIDEBAR_SERVICE_VERSION follows

Live assistant stream (assistant/chunk is gone):
- 0.1.5 publishes in-flight model deltas as process-local
  agent/assistant-stream frames instead of durable assistant/chunk events
  and embeds the settled stream in assistant/message / assistant/attempt.
  src/assistant-live.ts folds the frames into a bounded per-session buffer;
  sidechat.events serves the current attempt as `live` rows (replaced every
  poll, never a delta) and the transcript renders them; the inherited
  in-progress snapshot reads message content / attempt stream / live frames.

Cold session reads (persistence.inspect is gone):
- 0.1.5 reads a stored session through an explicit read handle
  (open(id,'read') -> handle.read() -> close()). src/session-store.ts owns
  that protocol; every cold read (sidechat transcript, cold-resume preset,
  session.cwd, changes replay) goes through it.

Dead code and docs:
- drop the remote.session.openWorkspacePath interception: 0.1.5's chat opens
  files through ctx.sidebarRight.openResource instead, so the wrapper had no
  caller and would hijack the host's "open in app" gesture; the
  interceptOpenPath pref, its settings row and its locale keys go with it
- AGENTS.md §3 + external-plugin-guide.md describe the new baseline
- vitest setup bridges Node's localStorage accessor to jsdom's store (the
  pre-existing 26 failures on Node >= 22)
- desktop-layout e2e seeds through host RPC and types into the Lexical
  composer (0.1.5 replaced the textarea) instead of driving the directory
  picker

Verified: pnpm typecheck / lint / build / test (124 files, 1327 tests) /
check:consumer-types, plus pnpm test:mount against a real
@deepseek-ai/dsh@0.1.5-alpha.1 host (18/18).
2026-09-09 11:15:30 +08:00
Menghuan1918 f024fbf092 fix(changes): 会话事件改走宿主 changes.ops 路由 + diff 展开二级设置 + 视觉迭代
CI 修复(plugin-mount 的 ctx.sessions.get TypeError ×1003):
- 客户端 runtime 无事件日志面——badge/SessionLens 原在客户端直读
  ctx.sessions.get(id).events / ctx.on('session/event'),全部不可达。
- 新增宿主路由 changes.ops:live 会话日志优先,冷会话回放
  sessionPersistence.inspect;afterSeq 增量(缺省游标含 seq 0,
  显式游标为 seq > afterSeq,同 sidechat.events 约定),窗口上限
  4000 与客户端累加器一致。
- ChangesTab 轮询(mount 一拉 + visible 2.5s 增量)折入 ops,写
  模块级 opCounts 缓存供 badge 同步读取;SessionLens 改纯展示。

diff 展开二级设置(changesDiffFloat,默认浮窗):
- prefs 四处接线 + git tab settings select(浮窗/面板,图标选项卡)
- 预览面板「展开为 diff tab」按设置落点:浮窗居中弹出或源 pane
  下方 split;i18n 全 21 词典同步。

视觉迭代(真机挂载截图审查后调整):
- Session 视角:kind 徽章为左轨,大小+相对时间右对齐元列;失败
  操作行红色内嵌条;文件组头降为三级标签并加组间距。
- Git 视角:untracked '?' 徽章与新增同绿色;stage 按钮改 plus 图标。
- diff 折叠行:可展开折叠带 › 箭头 + 次级对比度,静态 gap 不再
  显示 pointer/hover;预览头路径改尾部截断(RTL 技巧,保文件名)。

验证:typecheck ✓ / vitest 1123 ✓ / pnpm test:mount(钉 alpha.2 CLI)
14/14 ✓;真机挂载截图(badge=5、双视角、浮窗展开)0 console error。
2026-08-31 17:52:59 +08:00
Menghuan1918 a9353ce627 feat: workspace fence toggle (workspaceFence pref + one-click off in error surfaces)
- New persisted built-in pref workspaceFence (default true) under the
  files card's secondary settings modal; host reads it per request via
  the settings seam (fenceEnabledOf — absent/malformed stays ON).
- ensureWorkspacePath/ensureWorkspaceWritePath gain a fence param:
  disarmed skips the containment assert but keeps realpath resolution;
  wired through fs.tree/fs.read/fs.write, the media + html routes, and
  the upload path (writeWorkspaceUpload fence input).
- Editor load failures and file-tree level errors show the localized
  reason plus a one-click global off (FenceErrorNotice) that flips the
  pref, adopts it into the store, and auto-retries the failed operation
  (reloadSeq / retryDir) — the raw wire text is never shown.
- GitView's two client-side pre-checks (hidden open action + no-op
  defense) respect the pref; store threaded through TreePanel/FileTree.
- i18n: 4 new keys across all 21 dictionaries; tests: fence-off route
  coverage, prefs parsing, schema default, editor toggles list;
  docs: guide §8/§8.1 + plans/2026-08-29 design record.
2026-08-29 15:49:19 +08:00
HuanLinOTO 6001f0167c fix: 合并 main 后的测试回归修复
- index.ts: 移除 #130 遗留的 ctx.on('session/disposed')(fake ctx 无 on,
  且 #130 已按取 HEAD 决策,closeSession 调用方一并移除)
- browser.probe: 405→GET 重试后跳过 embed-signals GET,避免多余第 3 次请求(#69)
- EditorHost: 移植 #228 的 dirty 确认守卫到 HEAD 的 reloadSeq 结构(refreshFile)
- tests: fake ctx 补 get()(#357 ctx.get 回归,free-window/editor-refresh/markdown)
- tests: prefs 期望对象补 browserAllowedLoopback(smoke/plugin-shape/state)
- tests: builtins 浏览器设置行补 browserAllowedLoopback(#69)
- tests: install-powershell BOM 门限仅对实际文档化远程调用的文件生效
- locales: 19 语言补齐 showInFolder / refreshUnsavedConfirm(#94 #215 #228)
2026-08-24 17:54:33 +08:00
Menghuan1918 68640101e1 feat: sidebar_open tool gates model-driven sidebar opens behind a global setting
New global Side card setting `agentOpenTools` (default off, 常规 group):
when enabled, one `sidebar_open` tool is injected into the model so it can
actively open a local file, a local folder (tree rooted there), or an
HTTP(S) page in the calling session's sidebar.

- host: AgentOpenRegistry + registerOpenTool (src/agent-opens.ts); the tool
  classifies http(s) URLs / stat-driven file/folder paths, refuses other
  schemes, resolves relative paths against the session cwd, and reports
  target-tab-disabled instead of silently no-oping. Gated like
  agentTerminalTools (register/drain on settings watch).
- delivery: /sidebar/ws/agent-opens push (consume-on-send queue, replay on
  attach); the tool result reports `delivered` so queued opens for
  non-active sessions are honest.
- client: Sidebar subscribes and routes pushes to editor (file), folder
  window (meta.dir, tree rooted at the folder — EditorHost), or browser
  (URL) tabs; settings row in 常规 + 20 locale dictionaries.
- tests: agent-opens registry/tool spec, smoke gating + WS route list,
  prefs/plugin-shape defaults, editor-host folder window, side-card rows.
- docs: design doc, AGENTS.md key lists + reference, README feature bullets.
2026-08-24 01:19:43 +08:00
Atlas Zeng 50a888845f feat(sidebar): 桌面兼容三方案——自动检测(WCO 标准几何)/壳预设/自定义 CSS (#284)
* feat(sidebar): 桌面兼容三方案——自动检测(WCO 标准几何)/壳预设/自定义 CSS

核心只保留 Web 标准机制,壳专属适配全部下沉到用户空间(KISS):
- prefs 新增 titleBarScheme(默认 auto)/titleBarPresetId/customCss,旧 titleBarCompat
  布尔自动迁移(true→custom 保留 stripPx,false/缺失→auto)
- wco.ts:navigator.windowControlsOverlay 反应式几何(geometrychange 实时更新;
  visible=false 的幽灵 API 视为缺失),实现 #257
- titlebar-strip.ts 唯一 strip 取值链:WCO → dsh-desktop-titlebar-inset 契约参数
  → 预设 stripFor → 手动 px → 0;desktop-env.ts 移除 win32 32px 硬编码
- shell-presets.ts 数据驱动预设(准入:issue/PR 提及 + 100+ star;v1 仅
  anywhere-labs DSH Desktop:darwin 20px / win32 无 WCO 时 32px),opt-in 手动
- 预设/自定义 CSS 注入(data-dsh-preset-css / data-dsh-custom-css 标签),
  chrome 补稳定寻址面 data-dsh-toggle-cluster / data-dsh-panel
- 交互 chrome 统一 -webkit-app-region: no-drag(吸收 #111/#153)
- layout.css 底栏推挤复合锚点([data-pane] 与 :has(> [data-slot]) 双保险,
  rc.8 实测前者失效、后者命中,吸收 #226 思路)
- 设置 UI:位置兼容模式齿轮弹窗改三方案(自动检测/壳兼容方案/自定义方案)
- 测试:wco/titlebar-strip/shell-presets 单测 + prefs 迁移 + e2e 5 场景
  (保守 auto/WCO 反应式/预设/自定义 CSS/复合锚点),挂载门禁 12/12 全绿

核心 grep 无壳专属类名;为某壳做的兼容在另一壳会再坏——预设与自定义 CSS
是唯一出路(AGENTS.md §8 契约已更新)。

* refine(sidebar): 位置兼容模式改主行下拉四选项——自动检测/DSH官方Web/壳预设/自定义

按用户反馈定稿 UI:
- 主行下拉:自动检测(默认)/ DSH官方Web(显式零适配,连 WCO 也强制 0)/
  各壳预设(命中环境带「已检测」后缀,仅提示)/ 自定义方案(保留齿轮弹窗:
  下移距离 + 自定义 CSS)
- 旧文档迁移:titleBarCompat=true 或 titleBarStripPx 非默认值(此前已有配置)
  → 默认落到 custom 方案保留用户数值;干净文档 → auto
- prefs 新增 'web' 方案;strip 取值链最优先处理 web→0
- 测试:prefs 迁移(stripPx 48→custom / 40→auto)、web 强制 0、下拉渲染断言
- 单测 693 通过,挂载 e2e 12/12 全绿

* refine(sidebar): 方案下拉复用 SelectMenu 组件,不用原生 <select>

- 抽出可复用 SelectMenu(primitives Menu:锚点按钮 + 下拉选项卡,支持
  icon/desc/多选),SelectRow 改为其薄壳
- 位置兼容模式方案下拉改用 SelectMenu;选项带描述(自动检测/DSH官方Web/
  各壳预设(命中带「已检测」后缀)/自定义方案)
- 移除原生 select 样式;单测 693 通过,挂载 e2e 12/12 全绿
2026-08-21 01:28:17 +08:00
Atlas Zeng 6c891514b5 feat(injection): 统一面板宿主注入重构——含块层+几何写路径+chunk缓存+锚点弱化+桌面/移动适配 (#232)
* feat(injection): 统一面板宿主——固定含块层 + 挂载自检降级 + body 锚点守卫

- Sidebar 渲染根包 [data-dsh-panel-host](fixed inset-0 pointer-events:none)
- 面板/开关簇由 fixed 改 absolute(层内视口坐标等价),免疫套壳中间层 transform 劫持
- 挂载后一次性几何自检:页面级 transform 时降级 absolute + rAF 视口同步
- MutationObserver(body childList)守卫锚点被宿主清空时重挂

* perf(injection): 几何写路径统一为 writeGeometry + 面板 layout 隔离

- 拖拽帧与打开态共用单一推挤写入器(--dsh-sidebar-width/height)
- .panel/.bottomPanel contain: layout style——内部重排不外泄
- 拖拽期(body[data-dsh-sidebar-dragging])才挂 will-change: transform

* perf(chunks): HMR 激活保留未变 chunk 的已解析 exports + ETag HEAD 重验证

- 新增 revalidateChunksOnReactivate():激活时 HEAD 每个已加载 chunk 对比 ETag,
  未变保留内存缓存(跳过重注入/重执行),变更或不可达则 fail-open 重取
- 测试夹具与孤儿缓存条目不跨激活存活
- index.tsx 激活路径由 resetChunks() 改为 revalidateChunksOnReactivate()

* fix(layout): 推挤锚点改 data 属性 + #root calc 宽度防桌面壳溢出

- nth-child(2) → #root [data-dsh-frame] > [data-pane="conversation"](实证同元素)
- #root 宽度 calc(100% - var) 与 margin 同步过渡:web 视觉不变(实测 1140+300=1440 无溢出),
  Desktop 壳(#208)width:100% 的加性溢出根因消除

* feat(desktop): 桌面信号自动探测 + win32 顶栏 overlay 自动避让 + 键盘/safe-area 适配

- 新增 desktop-env.ts:解析 dsh-desktop-mode/platform URL 信号与 preload 标记
- win32 advanced 壳自动启用 titleBarCompat(32px overlay),手动 pref 仍可覆盖
- visualViewport 键盘 inset:底部面板/窄屏抽屉键盘弹起时上移
- 面板与开关簇 safe-area-inset 适配(env() 桌面端为 0)
- 单测 7 例(含 URLSearchParams 前导 ? 剥除修复)

* docs+e2e: 注入方案实施记录(路线B关闭、偏差记录、基线数据)+ 契约同步 + e2e 新断言

- 设计文档:路线 B 标记关闭、§8.1 改单 PR 步骤(含 commit 对照)、拖拽基线数据、实施偏差记录
- AGENTS.md §8:data-dsh-panel-host 含块层、推挤锚点契约(禁 nth-child)、桌面信号
- mount.e2e:data-dsh-panel-host 断言 + win32 advanced 桌面信号场景

* fix(cr): 响应 #232 CR 三条 findings

- P1 降级同步:按未修正几何判定(跟踪自身补偿量),祖先 transform 消失才退出循环——
  修正后矩形不再被误判为无需修正(自清除缺陷)
- P1 chunk 重验证成为 loadChunk 屏障:重验证挂起期间不提供缓存,杜绝 HMR 竞态陈旧 exports;
  测试夹具加载不再被误记为生产加载
- P2 键盘 inset:innerHeight - (vv.height + vv.offsetTop),并监听 scroll(offsetTop 变化)
- 新增屏障单测 1 例;设计文档偏差记录更新

* fix(chunks): 屏障 HEAD 加超时兜底 + resetChunks 清 revalidation

- revalidateChunksOnReactivate 的 HEAD 与 recordEtag 均带
  AbortSignal.timeout(5s):路由挂起时 fail-open(drop + 重取),
  屏障不再可能无限期阻塞所有懒加载 chunk(CR #232 P1 修复的健壮性缺口)
- resetChunks 同步清 revalidation:重验证挂起期间重置后,
  loadChunk 不再等待已丢弃状态的孤儿任务(finally 身份判断 no-op)
- 单测 +2:HEAD 挂起超时 fail-open(fake AbortSignal.timeout 触发中止)、
  resetChunks 清屏障后 loadChunk 立即放行

* fix(deps): align @deepseek-ai/* peers with DSH 0.1.0-rc.8

- peer/devDependencies: 13 个 @deepseek-ai/* 从 ^0.1.0-rc.7 升至
  ^0.1.0-rc.8;删除随 rc.8 消失的 dsh-client-web-react / dsh-client-schema-form
  (无 rc.8 发布、rc.8 依赖树与壳 bundle 零引用、插件 src 零引用);
  cordis peer/devDep 同步升 ^4.0.0-rc.8
- 版本 0.13.1 -> 0.14.0(package.json / dsh.plugin.json /
  SIDEBAR_SERVICE_VERSION 三处同步)
- CHUNK_EXTERNALS / CLIENT_EXTERNALS / CLIENT_REQUIRE_ALLOWED 三处镜像
  同步删除两个死包(primitives/slots 仍是 rc.8 壳静态模块表种子,保留)
- CI 挂载冒烟钉版 @deepseek-ai/dsh@0.1.0-rc.7 -> rc.8
- pnpm clean --lockfile 全量重解析:lockfile 内 @deepseek-ai/* rc.7 残留
  清零(传递链同升 rc.8,杜绝 #206 式混用树);pnpm-workspace.yaml
  minimumReleaseAgeExclude 按 #207 模式整体换为 rc.8 列表
- 文档同步:README 安装说明、AGENTS.md CI 钉版、外部插件指南与设计文档
  cordis 示例、issue 模板版本示例

* feat(prefs): editorExplorer 默认独立(关)——文件各自新开 tab

- SIDEBAR_PREFS_DEFAULTS 与 host PrefsSchema 的 editorExplorer 默认
  true -> false:新会话树点击/打开文件按 path 新开 tab,无路径窗口即
  纯资源管理器;合并模式保留为可选手动开启(持久化过的用户设置不受影响)
- 测试同步:prefs.spec 默认断言翻转、plugin-shape.spec 与 smoke.spec
  默认对象翻转;editor-host 合并模式用例显式开启 editorExplorer(split
  用例本已显式);side-card-section-rows 的 select 行用例固定选中 Merged
- e2e 重写:mount.e2e 文件打开流程从 in-place 断言改为独立模式
  (新开 hello.txt tab、Files home tab 保留),文件头注释同步
- 契约文档同步:AGENTS.md §3.4 editor 行默认描述翻转 + CI 冒烟描述、
  README/README_EN v0.13.0 特性描述 + v0.14.0 changelog

* fix(client): rc.8 模块系统经 ctx.modules 注入 + chunk 副本全局共享

rc.8 不再暴露 window.__DSH_MODULES__ 页面全局(改由 ctx.modules 服务
提供),chunk 外部依赖解析全面失效(terminal/editor/mermaid 全部
"client module system unavailable")。修复:

- src/client/index.tsx: inject 增加 'modules'(Cordis 服务访问守卫,
  此前直接读 ctx.modules 抛 "cannot get property modules without
  inject");apply() 激活时 setChunkModuleSystem(ctx.modules)
- package.json dsh.client.inject 增加 @deepseek-ai/dsh-client-modules
- chunk-loader: 注入写插件自有页面全局 __dshSidebarModuleSystem__——
  懒加载 chunk bundle 各自内联一份 loader(rc.7 靠官方全局回退工作,
  rc.8 全局消失后副本注入变量恒空);moduleSystem() 按
  注入变量 → 插件全局 → rc.7 全局回退解析
- context-types: Context 镜像补 modules 服务声明(client half)
- e2e: assertNoCrash 失败时输出错误条文本;独立模式断言修正
  (Files 数量按存在性、mermaid 流程先切回 Files、md 预览 locator 去掉
  重复的 [data-dsh-better-sidebar] 嵌套)

验证:typecheck / 639 测试全绿;本地 rc.8 CLI + Chromium 挂载冒烟 3/3
通过(drag-layout、mount 深扫含 editor/mermaid chunk、桌面信号)

* feat(settings): 终端 shell/shellArgs 可在设置页配置(bash 管理)

- SidebarPrefs 新增 terminalShell / terminalShellArgs(默认空 = 保持
  yaml config.shell → $SHELL / 登录 shell / powershell.exe 的既有解析)
- 终端卡齿轮二级页面新增两行 text 配置(与字体行同列)
- host 侧生效链路:UI 终端 WS 打开与 agent terminal_create 均读取
  设置文档覆盖(ptyManager.open / AgentPtyRegistry.create 增加可选
  shell 覆盖参数;shellOverridesOf 解析设置文档);设置页写入即时
  影响之后打开的终端(既有 pty 保持原 shell)
- locales zh/en 文案;parsePrefs 校验新字段;测试同步

* feat(settings): 侧边卡片设置页加 DSH-better-sidebar 版本徽标

- 设置页 intro 下新增身份徽标:名称 + v{service.version}(服务实例
  版本,与 package.json 由 tests/service.spec.ts 保持同步)
- token 驱动样式(--dsw-alias-*),名称/版本 pill 布局

* fix(drag): 快速拖动提交最后位置 + HMR 后中心列重定位兜底

快速释放(浏览器合并/丢失 pointermove 突发,超快速 flick 甚至可能
完全无 move)时的回退问题:
- pointerup 优先采用最后一次已知拖动值(rAF pending),而非 up 位置
  (事件合并时可能等于按下位置)
- pointercancel / lostpointercapture 中断时同样提交最后已知位置;
  无 move 时用中断事件自身的坐标计算(clamp 后提交)——只有真正
  未产生位移的拖动才回滚
- commitDrag 统一 flush + 同值提交 + 立即重测中心列(消除提交后
  React 用旧 centerRect 覆盖底栏 right 的中间帧)

HMR 重激活后底栏空白(输入框被推挤上移、强制刷新恢复):
- 根因:新组件实例重跑 locate 时壳的 centerCol 尚未就绪,而壳重建
  复用 #root 子节点(childList 观察器不触发)→ centerRect 卡 {0,0}
  → 底栏 visibility:hidden 永久空白
- 修复:额外观察 <html> 的 style 属性(layout push 写回 --dsh-sidebar-*
  时重定位重测);底栏打开时 effect 重跑 locate/measure 兜底
- 底栏未测量时由「不渲染」改为 visibility:hidden(渲染链不被几何
  门控,jsdom 测试不回归)

e2e 新增:极快拖动(单 move 立即 up)提交拖动位置、稳定后不回退

* feat(plugins): 添加插件目录支持搜索/分组/独立滚动(多插件场景)

- PluginListBody 顶部加实时搜索框(按名称/id/描述过滤,空结果提示)
- PluginEntry 增加可选 category 分组字段(i18n 友好),目录条目按组
  渲染(未分类条目保持平铺);数量徽标跟随过滤结果
- 条目列表 max-height + overflow-y 独立滚动,插件多时弹窗不再无限
  增长;渲染数量由搜索天然收敛
- locales zh/en(搜索占位、无匹配);测试文件固定英文环境
  (vitest 4.1.11+ jsdom 跟随系统 locale,本机 zh-CN 导致英文文案
  断言漂移)

* chore(deps): 适配 pnpm 11.8 supply-chain 校验(exclude 通配)

pnpm 11.8.0 起对 lockfile 既有条目强制 minimumReleaseAge 校验,
且精确 exclude(@scope/pkg@version)对带 peer 依赖后缀的解析条目
匹配失效(44 条误报违规,CI 全红)。@deepseek-ai/* scope 为官方
可信 scope,minimumReleaseAgeExclude 改为 '@deepseek-ai/*' 通配
(保留 rc.1 历史与 stable 条目);通配经实测通过全部 504 条校验。

* docs: v0.14.0 changelog 补全(自 v0.13.1 全部更改 + 特别声明 rc.8 适配)

新增条目:终端 shell/shellArgs 设置页配置、设置页版本徽标、
添加插件目录搜索/分组/滚动、rc.8 模块系统迁移、拖拽健壮性修复;
版本说明块醒目声明本版适配 DSH 0.1.0-rc.8(rc.7 及更早无法解析依赖)
2026-08-20 02:09:36 +08:00
NuperAki c973d6ec1b feat(terminal): 支持配置 shell 与 shellArgs,终端标题使用 shell 名 (#125)
- 支持通过 cordis.patch.yml 指定终端 shell
- 支持 shellArgs 带参启动终端 shell
- 终端标题改用 shell 名,内部使用 uuid 管理
2026-08-18 19:56:04 +08:00
Atlas Zeng 6ee1ba11d0 feat(editor): 文件窗口与资源管理器二合一 + 声明式设置 select 行 (#151)
* feat(editor): merged explorer mode — path input, docked file tree, global name search

New editorExplorer pref (default on, editor card gear): the editor tab gains
a path-input header and a toggleable right-docked tree panel (per-tab state
in tab.meta.treeOpen; file tabs default closed, the seeded path-less home
tab defaults open). Fresh sessions seed that empty editor tab instead of
the explorer tab when merged mode is on; the standalone explorer tab stays
in the + menu unchanged.

- Extract FileTree.tsx from ExplorerView (shared by both hosts)
- Host fs.search route: recursive case-insensitive name search rooted at
  the session cwd, budget-capped (200 matches / 100k visited), skips .git,
  never descends symlink dirs
- EditorHost reads the pref reactively (boolean snapshot) and keeps the
  pre-merge layout verbatim when off

* feat(settings): select row type for declarative settings; editorExplorer as iconed select

- SidebarSettingToggle gains type 'select' with options {value,title,desc,icon}
  and multi (default false; stores the picked values as an array in options
  order). Any option carrying an icon renders big-icon option cards in the
  dropdown and the icon in the closed anchor; icon-less rows are one-line text.
- SelectRow in SideCardSection rides the primitives Menu (portal); single-pick
  commits and closes, multi toggles and stays open. Wired for both host-prefs
  toggles and plugin-owned pluginToggles via the new onSelectValue seam.
- The editorExplorer merged-mode pref is now an iconed select (merged vs
  separate) instead of a switch; the stored value stays boolean.
- Advertise the capability as 'settingSelect' in SIDEBAR_FEATURES.

* feat(editor): unify explorer into the editor surface, resizable tree dock, hoisted toolbar

- One tree implementation, two presentations: the shared TreePanel (global
  name search + refresh + FileTree) docks as the editor tab's right panel
  and — in merged mode — fills the explorer tab's window; the pref-off
  explorer keeps its legacy header shell.
- The docked panel drag-resizes from its left edge (160-480px clamp),
  persisted as tab.meta.treeWidth alongside treeOpen.
- The text editor's preview/edit/save toolbar moves into the path-input
  header row: FileViewerProps gains internal toolbar fields ('host' mode
  skips the viewer's own row, reports EditorToolbarState and registers
  EditorToolbarControls); non-merged mode is byte-identical to before.

* feat(editor): single files window with in-place switching; explorer descriptor removed

- editor tab replaces explorer on the + menu (order 10, Files); persisted
  explorer tabs migrate to the editor home tab via sanitizeState
- editorExplorer now only controls open behavior: merged = in-place
  updateTab, independent = openSidebarFile; both modes share the merged
  chrome (path input + hoisted preview/edit/save toolbar + tree toggle)
- file tree context menu gains 'open in new tab' / 'open to the side'
  (mintTabId avoids the id safety net; insertLeafAt splits right)
- tree dock resize uses pointer capture (touch-action none, 6px handle)
- defaults: openByDefault off, sidebar width 35%
- file tree clips horizontal overflow (no sideways scroll)
- e2e: mount lane arms the editor-chunk wait before goto and clicks the
  tree row's left edge (hover @-reference button sits mid-row); drag lane
  expands the panel first (collapsed push writes 0px), waits for the
  slide-in to settle, and picks the leftmost col-resize strip; 'Files'
  title assertions require draggable to skip the bottom panel's welcome
  cards

* feat(editor): split mode turns the path-less window into the standalone explorer

In independent (split) mode a path-less files window renders ONLY the
tree panel (search + FileTree, full-window form) — no path input, no
hoisted toolbar, no tree toggle. File clicks open per-path editor tabs
(full chrome, tree docked closed by default) through openSidebarFile,
unchanged. Merged mode is untouched (chrome + docked tree, in-place
switching). TreePanel's previously reserved 'full' prop now has its
consumer.
2026-08-17 15:00:03 +08:00
oy-g fd9544f16b feat: configurable terminal shell with Windows pwsh auto-probe (#95)
* feat: configurable terminal shell with Windows pwsh auto-probe

Windows previously hardcoded powershell.exe (5.1) in defaultShell(), ignoring PowerShell 7 installs. Resolve the shell in priority order: explicit shell config -> DSH_SIDEBAR_SHELL -> PATH/known-dir probe for pwsh.exe -> inbox 5.1 fallback. The same resolved shell feeds both the UI terminal tabs (PtyManager) and the model-facing terminal_* tools (AgentPtyRegistry).

defaultShell() now accepts injectable platform/env/exists options so the Windows chain is unit-testable on the ubuntu CI, which never executes win32 branches. The probe also checks ProgramW6432 for 32-bit Node, and machines without PowerShell 7 keep the 5.1 fallback.

Refs: #46

* fix: trim resolved SHELL and document ProgramW6432 in resolver contract

- POSIX SHELL is validated with trim() but was returned untrimmed; trailing whitespace could leak into the spawned executable path. Return the trimmed value like the explicit/config and Windows/env branches.
- ShellResolutionOptions.env comment now lists ProgramW6432, which the Windows probe prefers over ProgramFiles on 32-bit Node.
- Test pins the SHELL trimming behavior.
2026-08-16 16:34:21 +08:00
Menghuan1918 8ad1766390 feat: 位置兼容模式支持自定义下移距离(齿轮弹窗数字输入)
titleBarStripPx 新偏好(0–120px,默认 40):侧边栏按钮与内容的下移量改为
CSS 变量 --dsh-title-bar-strip 驱动,Sidebar shell 随 prefs 写入。设置页
「常规」的位置兼容模式行在开启时显示齿轮小图标,弹窗内用数字行设置下移
距离(复用 FeatureSettingsRows 行控件与提交钳制)。
2026-08-15 15:46:56 +08:00
Menghuan1918 1e7c72ccbe feat: 侧边栏新增位置兼容模式,为 Windows 原生标题栏预留顶部空间
新增 SidebarPrefs.titleBarCompat 设置项(默认关闭):开启后 toggle cluster
下移 40px、右侧面板内容从标题栏条带下方开始,避免被 Windows 无边框窗口
右上角的原生标题栏遮挡。底部面板不受影响。设置页「常规」新增一行开关,
zh/en 双语,读写下穿既有 settings 路由。测试覆盖 parsePrefs 回退、schema
默认值、设置页渲染与开关状态。
2026-08-15 15:37:31 +08:00
Menghuan1918 ad787ab22d Revert "feat: 侧边栏新增位置兼容模式,为 Windows 原生标题栏预留顶部空间"
This reverts commit ae71aecb83.
2026-08-15 15:36:12 +08:00
Menghuan1918 ae71aecb83 feat: 侧边栏新增位置兼容模式,为 Windows 原生标题栏预留顶部空间
新增 SidebarPrefs.titleBarCompat 设置项(默认关闭):开启后 toggle cluster
下移 40px、右侧面板内容从标题栏条带下方开始,避免被 Windows 无边框窗口
右上角的原生标题栏遮挡。底部面板不受影响。设置页「常规」新增一行开关,
zh/en 双语,读写下穿既有 settings 路由。测试覆盖 parsePrefs 回退、schema
默认值、设置页渲染与开关状态。
2026-08-15 15:24:46 +08:00
Menghuan1918 29f162dac0 feat: 服务化 API 新增 urlTarget 外链认领 + 浏览器 HTTP/HTTPS 二级设置开关
基于 PR #74 的颗粒度讨论向前兼容拓展服务化 API:
- TabDescriptor 新增可选 urlTarget(url) 谓词:聊天/界面外链被拦截时按注册
  顺序路由到第一个命中的插件 tab 类型(openTab({ type, url, title })),
  内置 browser 退居隐式兜底;SIDEBAR_FEATURES 追加 'urlTarget'
- 浏览器链接设置改为二级菜单(齿轮弹窗):新增 browserInterceptHttp
  (默认开)+ browserInterceptHttps(默认关);browserInterceptLinks
  保留为总闸(默认开,旧文档零迁移)
- 拦截管线:registerLinkInterception 的 takeoverEnabled 改收解析后的
  URL,协议/同源纯判定(shouldInterceptLink)签名不变
- 测试:matchUrlTarget(注册顺序/抛错吞掉/browser 永不命中)、jsdom
  点击接管门控、parsePrefs 新字段默认值;全默认字面量同步
- 文档:设计文档 + AGENTS.md(字段/示例/设置键)+ README(外链分流)
2026-08-15 15:05:06 +08:00
Rookie_Zoe ea250c9e87 fix: /sidebar 信任栅栏改用 webRuntime.trustedHosts,修复远程访问全部 403
根因:trustedHostsOf 按 entry.options.name === 'connection' 查找 connection
行,但该行 name 是模块标识符(@deepseek-ai/dsh-client-connection,id 才是
connection),查找永远落空 → trustedHosts 为空 → 栅栏只放行回环 Host → 通过
域名或局域网 IP 访问时 /sidebar/bundle chunk 加载失败、资源管理器/代码管理器
全部 403 forbidden(/api 不受影响,它读的是 apply 时已求值的配置)。

修复(按 review 建议):host half 直接注入 webRuntime 服务,栅栏每请求实时
读取 ctx.webRuntime.trustedHosts —— 与 /api 网关同一信任列表(启动时采样的
LAN IP 字面量 + --trusted-host 权威),不再依赖 Loader entry / fiber /
!!js 节点求值;自定义信任域名由 dsh web --trusted-host 提供。

新增 tests/trusted-hosts.spec.ts 回归:域名/局域网放行、空列表仅回环、
跨站标记拒绝、每请求实时读取;plugin-shape 断言同步更新。
2026-08-14 23:03:26 +08:00
Menghuan1918 82a69546d2 feat: 公共 API 基座化(v0.12.0,纯增量)— 类型/能力/设置 seam 全面补齐
- 类型生态:service 模块 re-export SidebarTab/SidebarState/SidebarStore/
  SidebarSnapshot/SidebarDiffRef/TabType/SessionScope/SidebarPrefs 等公共
  状态词汇(此前 TS2459 无法命名);context-types 去 Node 依赖(结构镜像
  SidebarHttpRequest/Response/UpgradeSocket/Head),client 可达声明图零
  node:* 引用,外部消费者无 @types/node + skipLibCheck:false 可编译
  (scripts/check-consumer-types.sh 守护)
- 服务能力:version/features 版本与能力探测(单调只增)、getSnapshot/
  subscribeState 状态订阅、updateTab(title/path/meta)、activateTab、
  openFile(路径派生 id 与内置拦截一致)、openTab(seed, scope?) 定向到
  指定 session(store.reduceFor 不扰动 UI)
- TabDescriptor:badge 角标(99+ 封顶,抛错吞掉)、onOpen/onActivate/
  onClose 生命周期回调(service 路径派发,dedupe 聚焦触发 onActivate
  而非 onOpen,抛错不打断流程);SidebarTab.meta JSON 可序列化跨刷新
  持久化(sanitize 宽松保留)
- 设置 seam 开放:PrefsSchema 增 pluginSettings 开放嵌套 map(z.dict),
  settings.pluginToggles(插件自有行,同 switch/text/number 控件)与
  settings.render(自定义面板,含 store/service/prefs/updatePluginSetting/
  close props);viewer 卡片 v0.12.0 起也有齿轮设置弹窗
- FileViewerDescriptor.load 增第三参 AbortSignal(EditorHost 生命周期中止)
- UI 接线:tab 激活/关闭改走 service(跨树激活修复 + 回调触发)、+ 菜单
  传 scope、TabBar badge pill 渲染;active-session scope 走原 reduce 路径
  保证 notify
- 测试 +23:生命周期/定向打开/updateTab/activateTab/openFile/meta 持久化/
  reduceFor/能力常量(与 package.json 锁步)、consumer-types.ts 编译门、
  api-surface.spec.ts(client 图零 node 依赖守护);既有 430 测试全绿
- 文档:AGENTS.md 修 available 三参示例并补全部新 API;external-plugin-
  guide.md 刷新至 v0.12.0(7 tab + 9 viewer、设置 seam 开放、类型导入
  路径建议);README×2 服务化小节同步

未发版 npm(用户决定);分支 feat/plugin-api-v012。
2026-08-14 20:44:43 +08:00
Menghuan1918 d83490c93e feat: 终端二级设置支持自定义字体(字体族 + 9–32px 字号,实时生效)
- SidebarPrefs 新增 terminalFontFamily(空 = 跟随主题等宽字体)/ terminalFontSize(9–32,默认 13),PrefsSchema 与 parsePrefs 同步校验
- 声明式设置 API 扩展:SidebarSettingToggle 支持 type: 'switch' | 'text' | 'number'(缺省 switch,向后兼容)+ min/max/placeholder/unit
- 终端卡片齿轮弹窗新增「终端字体」「终端字号」两行:blur/Enter 提交、number 钳制、提交失败回滚草稿
- TerminalView 经 store 订阅实时应用字体(options + re-fit + resize),懒加载 chunk 无新增依赖
- 新增 resolveTerminalFont 纯函数(回退链:用户字体 → 主题 token → 内置等宽栈)并单测
- 版本 0.11.0,README/AGENTS.md/设计文档同步
2026-08-14 14:36:49 +08:00
Menghuan1918 1bfabbbfc2 feat: 适配 DSH 命名契约重命名(jobs/terminal/webServer)并处理 Session log 按钮位置
- tasks→jobs:ctx.jobs / job_output / job_id / session/jobs / jobsBySession / Job* 词汇
- httpServer→webServer(inject + augmentation + register/registerUpgrade)
- autoOpenTasks→autoOpenJobs 偏好键
- 右上角开关簇回到角落,注入 CSS 挤开 header 的「Session log」胶囊
2026-08-13 09:06:55 +08:00
Menghuan1918 88f116338a feat: 任务管理页二级开关「有新后台任务时自动展开」(autoOpenTasks)
- 新 pref autoOpenTasks(默认开):当前会话出现新后台任务(任意新任务 id,
  非仅首个)时自动展开侧边栏并打开任务管理页,与 autoOpenSubagent 并列
- 注册为 subagent tab 的声明式二级开关(设置页齿轮弹窗,key 在 PrefsSchema)
- Sidebar 新增独立基线 effect(镜像子代理自动展开;刷新不触发、开关关闭/类型
  禁用不触发);detectNewTask 纯函数(settling/同 id/他会话不触发)
- 测试:detectNewTask 用例、prefs 解析断言、builtins 开关清单、schema 默认值
  断言;全量 371 通过
2026-08-12 23:04:30 +08:00
Menghuan1918 c7af7dccdb feat: 侧边栏双面板 — 右上角按钮簇、底部面板(首展自动终端/关闭按钮)、跨面板拖 Tab、拖动性能优化
- 右上角持久按钮簇:横向排列、独立 glyph(底栏/侧拉)、始终钉在视口右上角(侧边栏展开时位于其标签条右端,真实挤占 Tab 空间)
- 底部面板:独立工作台,只挤占中间 Agent 输出区(不覆盖左右侧边栏),右缘分割线、关闭 x、首展自动开终端(二级设置开关,默认开)
- 两面板拐角双向拖动调节尺寸;拖动改为 rAF 节流直写 DOM,消除卡顿;面板尺寸互钳制防丢失
- 跨面板拖 Tab(合并/拆分落点),moveTab/moveTabToEdge 支持跨树移动
- 欢迎卡片改响应式网格;布局/几何全部按会话持久化,旧状态平滑迁移
2026-08-12 16:43:24 +08:00
Menghuan1918 302c20109f feat: 内置 HTML 预览器 + 沙箱浏览器 tab(v0.5.0)
- html viewer:.html/.htm 沙箱 iframe 预览(相对资源经 /sidebar/html 路径编码路由加载,CSP sandbox + 信任围栏 + cwd 边界),预览/编辑切换
- browser tab:+ 菜单多开(browser:<n>),地址栏后退/前进/刷新/在浏览器中打开(15px 图标),URL 与标题按会话持久化(patchTab + nextBrowser)
- 安全:iframe 一律不透明源沙箱(无 allow-same-origin/top-navigation、no-referrer、权限策略全禁、禁 srcdoc);地址栏仅 http(s)、拒绝 localhost 等本机地址;界面实时沙箱状态(关闭红色警示)+ 临时解锁/恢复
- 设置:htmlViewerNoSandbox / htmlViewerDefaultUnsafe / browserNoSandbox / browserInterceptLinks 四个声明式二级设置(默认安全)
- 外链拦截:聊天/界面 http(s) 外链默认在侧边栏打开(openTab seed.url),Ctrl/Cmd 点击放行
- 嵌入拒绝面板:browser.probe 路由探测 X-Frame-Options/frame-ancestors,被拒站点显示原因 + 在浏览器中打开/仍然加载
- 设置页可滚动;内置 7 tab + 9 viewer;新增设计文档;268 个测试全绿
2026-08-11 19:28:48 +08:00
Menghuan1918 67ec136e98 feat: 拦截聊天区文件打开 — 聊天里的文件点击默认开进侧边栏编辑器
包装 ctx.workspaces.openPath(聊天区文件打开的唯一漏斗),接管时
经 openSidebarFile 开进 sidebar 编辑器而非系统默认应用;由
interceptOpenPath pref(默认开)与 editor tab 启用态双重门控,
拒接时原样放行,dispose 还原原始方法引用(HMR 安全)。

- prefs-shared/config/prefs: 新增 interceptOpenPath 字段(默认 true)
- openpath-intercept.ts: 零依赖包装器,纯插件层面实现,不改 DSH
- intercept.tsx: registerOpenPathInterception 接线(当前会话 scope)
- SideCardSection: 常规区新增开关卡片(zh/en 文案)
- 测试: 包装器单测 6 个 + 接线集成测试 + pref 解析/设置页断言更新
2026-08-11 16:48:59 +08:00
Menghuan1918 89ddf8a9e7 feat(settings): 侧边栏设置声明式化 — 注册表驱动的开关清单 + 内置支持插头化
- SidebarPrefs 新增 tabsEnabled / viewersEnabled 开放 map(缺省 = 启用),
  PrefsSchema 用 z.dict 校验,旧文档零迁移
- TabDescriptor/FileViewerDescriptor 新增声明式设置面:viewer 补 title/icon,
  两者补 settings.toggles(子代理声明 autoOpenSubagent、终端声明 agentTerminalTools)
- 服务新增 isTabEnabled/isViewerEnabled;openTab 拒绝禁用类型、matchFileViewer
  跳过禁用 viewer
- SideCardSection 重写为注册表驱动清单:侧边栏内容(图标+类型id+开关+嵌套开关)
  + 文件预览(图标+标题+扩展名+开关),开关持久化 + 乐观提交回滚
- 消费点 gating:+ 菜单过滤、子代理自动展开、agent 终端 reconcile、
  产出文件拦截(editor 关闭时回落默认行)、新会话 explorer 预置
- 内置 viewer 补 6 个手绘 outline 图标;store 快照携带 prefs 使开关变更即时生效
- 测试:service gating / builtins 声明 / parsePrefs map 校验 / schema 断言 /
  新增 SideCardSection renderToString 规格
- 文档:README 设置小节、AGENTS.md 描述符与服务方法、新增设计文档;版本 0.5.0
2026-08-11 14:08:55 +08:00
Menghuan1918 8edfbfde44 fix: 修复 agent 终端工具审查问题(terminal_list 输出契约、跨会话归属、UTF-8 截断边界等),工具注入改为设置面板开关(默认关闭) 2026-08-10 21:43:58 +08:00
HuanLinOTO 6f62d19905 feat: 为 agent 暴露 8 个终端工具(terminal_create/list/send/read/wait_for/resize/signal/close),侧边栏自动同步 agent 终端 tab,v0.2.0 2026-08-10 19:08:58 +08:00
Menghuan1918 61b18cbefe feat: Subagent 拓扑页面(主代理根节点 + 分层树 + 实时工具调用 + 自动激活设置),v0.1.9 2026-08-10 17:24:42 +08:00
Menghuan1918 9b00135de4 feat: DSH-native flat chrome + Side card settings (plugin-owned settings wire)
- Remove every box-shadow; panel uses the native sidebar fill surface
  (--dsw-specific-sidebar-fill) with a hairline border, the collapse
  control becomes the native 36px circular rail button, the active tab
  uses the native interactive-bg-active fill, and rows/typography follow
  the native 34px/14px session-row recipe.
- New 'Side card' settings section in the DSH Settings shell: open-by-
  default for new conversations (boolean) and default width share as a
  percent of the window (20-60). New sessions seed from these prefs;
  existing per-session layouts stay untouched.
- The settings namespace is read/written through the plugin's own fenced
  /sidebar routes calling the settings seam in-process: the DSH settings
  RPC domain only serves allowlisted namespaces to configuration clients,
  so third-party namespaces cannot use connection.api.settings (reads are
  silently filtered, writes refused with 'not exposed'). The seam still
  persists to the user settings document with revision-guarded writes.
2026-08-09 23:49:58 +08:00
Menghuan1918 e2ee427ea3 feat: per-session store + configurable host limits; fix divider drag runaway
- state: side-store per conversation, injected as a prop instead of a module singleton
- host: schemastery Config (read/media/list/terminal limits) resolved from cordis config
- shape: types/exports/peerDependencies for the web profile + plugin-shape spec
- fix: split divider emits incremental deltas so drags track the cursor 1:1
- docs: README refresh
2026-08-09 22:07:03 +08:00