Commit Graph
100 Commits
Author SHA1 Message Date
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 91de0e2932 fix(native): give the "Files" guide row its glyph
The guide renders `entry.icon`, and the `files` kind takeover registered
without one — so after the editor type stopped contributing its own
duplicate row (b56ceec), the single "Files" row was the only entry in the
new-tab list with a blank icon slot. The takeover now carries the editor
descriptor's icon (the takeover IS that descriptor's page).

Extracted `guideIconOf` so both registration sites build the icon field
the same way; the lifecycle test asserts the takeover's guide entry has
an icon.
2026-09-09 15:18:04 +08:00
Menghuan1918 b56ceec41d fix(native): offer one "Files" row in the new-tab list
The editor type's page identity IS the `files` kind takeover (same
explorer, same title), so the guide/new-tab list showed two identical
"Files" entries — observed on a real profile after the takeover landed.
The editor type now contributes no guide entry of its own; it stays
registered as the file RESOURCE viewer. Covered by the registration
lifecycle test (editor: no guide, files takeover: guide).
2026-09-09 15:06:31 +08:00
Menghuan1918 4da2b43610 fix(native): title a file tab by its own name, not the descriptor's
The editor type serves both the files PAGE and every file RESOURCE; its
native `title` callback ignored the address, so every opened file showed
up in the strip as "Files" — indistinguishable tabs. Verified on a real
profile: after opening `AGENTS.md` from the tree and a chat file link,
the strip read ["文件", "文件", "文件"].

`title(address)` now returns the file name for a file address and keeps
the descriptor title for a page address (`sidebar://editor`). Covered by
the registration-lifecycle test in tests/native-surface.spec.ts.
2026-09-09 14:34:08 +08:00
Menghuan1918 aa1b9945d9 fix(native): register the plugin's tab types when the registry service arrives
The native seat declares `sidebar.right.pane.tab` BEFORE it provides
`sidebarRightTabs`, so driving the registration off the slot declaration
read the service as missing and registered nothing — permanently, since a
declaration never collapses. Observed on a real DSH profile (0.1.5-alpha.1,
`dsh web` on the web profile): the plugin host and its other slot
registrations mounted, the native guide page stayed empty, and a probe
printed `sidebarRightTabs= undefined` at declaration time with the registry
present three seconds later. The scratch mount lane never saw it because
there the plugin activates after the native package finished providing.

`registerNativeSurface` now waits on the SERVICE (`ctx.inject`), which
re-runs whenever the registry appears/reappears, and keeps the per-body
slot registrations on `slots.inject` (they only need the declaration).

Regression test: `tests/native-surface.spec.ts` models the real order
(slot callback fires first, registry arrives later) and fails on the old
implementation. Verified on the real web profile: all guide entries
(files / editor / git / subagent / sidechat / terminal / browser) appear
and the terminal tab renders.
2026-09-09 13:07:53 +08:00
Menghuan1918 d7e3e2b986 fix(test): give the native-surface terminal descriptor a component
TabDescriptor requires `component`; the fixture omitted it, so CI's
typecheck step failed on the phase-2 branch (vitest never compiled the
file's props strictly, so the local run stayed green).
2026-09-09 12:33:49 +08:00
Menghuan1918 51546f7433 feat(native): render the plugin's tabs in DSH's native right Sidebar
Phase 2 of the 0.1.5 adaptation: the plugin stops drawing its own right
panel and contributes tab TYPES to DSH's native right Sidebar, which owns
the right column. The bottom workbench (terminal dock) stays plugin-owned.

Registrations (src/client/native/):
- every TabDescriptor becomes a native tab type (kind = descriptor.id, the
  default `extension` band) plus a keyed body in `sidebar.right.pane.tab`
  and a live chip title in `sidebar.right.pane.tab.title`
- the `editor` type also claims `dsh-resource://file/**`, so it outranks
  the built-in `text` preview (fallback) and every file the product opens
  lands in the plugin's editor
- the built-in `files` page kind is TAKEN OVER by an extension
  registration of the same kind: `openTab('files')` draws the plugin's
  explorer, and the built-in resumes when the plugin unregisters
- registrations follow the plugin registry + the side-card settings: a
  type registered later (external plugin) gets its native type too, a
  disabled type is unregistered (absent from the guide, refuses opens)

Adapter (src/client/native/tab-adapter.tsx):
- one synthetic plugin `SidebarTab` per native tab id, minted from the
  native record + navigation params (or the descriptor's own createTab,
  which a native guide open bypasses), kept live across navigations and
  updateTab writes
- the explorer's per-tab expansion/reveal sets and the per-kind instance
  counter, which the plugin's own layout used to hold

Service routing (src/client/service.ts):
- `setSurface` installs the native write face; openTab/openFile land
  natively (a path becomes a resource address, a path-less editor open
  becomes the `files` page, the rest carry the seed as navigation params),
  closeTab/updateTab/activateTab recognise native tab ids
- new optional `OpenTabSeed.target` ('right' default, 'bottom' keeps the
  bottom workbench); the bottom panel's own + menu and auto-terminal pass
  'bottom'
- src/client/native/surface.ts probes the controller's per-session writes
  (`openTabIn`/`openResourceIn`/`closeIn`, not in `ISidebarRight`) and
  queues opens for a session whose surface is not mounted yet

Right panel retired behaviourally (Phase 3 deletes the code): Sidebar
renders its bottom workbench only, keeps the panel collapsed, and hides
the right-panel toggle. The panel-specific e2e specs are removed; the
mount lane now drives the native guide (tab sweep), the plugin explorer
inside the native pane (editor/mermaid/README chunks) and the sidechat
host routes.

Verified: pnpm typecheck / lint / build / test (126 files, 1349 tests) /
check:consumer-types, plus pnpm test:mount against a real
@deepseek-ai/dsh@0.1.5-alpha.1 host (7/7).
2026-09-09 11:47:31 +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
Atlas Zeng d88dcfc3a5 Merge pull request #595 from omdsh-dev/feat/wait-for-regex-needle
feat(terminal): wait_for needle 支持 regex,found 返回实际匹配文本
2026-09-08 23:27:45 +08:00
Atlas Zeng eecf9dc536 Merge pull request #593 from omdsh-dev/feat/terminal-shell-quoted-args-probe
feat(terminal): 终端 shell 配置加固——引号解析、POSIX 预检、本地化报错
2026-09-08 22:28:22 +08:00
Menghuan1918 ad22abcfe6 docs(plans): 修正实施偏差记录的 commit 引用(cbbed88 → 8704994) 2026-09-08 22:20:15 +08:00
Menghuan1918 81919516d8 fix(terminal): WS close reason 按 UTF-8 字节截断,避免超 123 字节抛错
ws 用 Buffer.byteLength 校验 close reason 的 123 字节上限(ws/lib/sender.js),
原 slice(0, 100) 按 UTF-16 code unit 截断,非 ASCII shell 名可达 316 字节
(实测 200 个 CJK 字符),ws.close() 会抛 RangeError 顶掉原始错误。
改为按字节截断且不切碎 code point,并补回归测试。
2026-09-08 22:20:15 +08:00
Atlas Zeng 146840bb4f Merge pull request #586 from omdsh-dev/chore/release-0.18.1
chore(release): 0.18.1
2026-09-08 16:20:03 +08:00
Menghuan1918 364a48df98 chore(release): 0.18.1
- package.json / dsh.plugin.json / SIDEBAR_SERVICE_VERSION → 0.18.1(三者锁步由
  tests/service.spec.ts 与 tests/manifest-consistency.spec.ts 断言)
- README / README_EN:新增 v0.18.1 条目并同步三处徽章版本号——自 v0.18.0 起的
  11 个 PR:#499 变更面板预览增强(md 阅读模式 / html·pdf 渲染 / 密钥脱敏)、
  #550 文件树重命名与删除、#535 目录名词典化、#562 侧边对话种子补 fork 标记、
  #569 markdown 本地图片改写、#576 git 折叠上下文按需展开(含 git.show 路由
  rev:path 修正),以及 #524/#526/#527/#534/#536/#540/#541/#542 的内部收敛
- docs/external-plugin-guide.md:当前版本行 → v0.18.1
- DSH 基线不变(peer ^0.1.2-rc.1,CI 钉 0.1.2-rc.1),无运行时行为改动

验证:make check(typecheck → lint → build → test → check:consumer-types)全绿,
124 test files / 1319 passed, 9 skipped
2026-09-08 16:16:16 +08:00
Atlas Zeng df1f3b9666 Merge pull request #576 from LaoYueHanNi/fix/diff-fold-expand
fix(diff): git 折叠上下文按需展开,修复「点击展开」无反应
2026-09-08 16:14:53 +08:00
Atlas Zeng 6fbfeedc01 Merge pull request #499 from lhh010/feat/changes-md-reading
feat(changes): markdown reading mode + expanded syntax highlighting
2026-09-07 16:06:27 +08:00
Menghuan1918 9302d02cf5 refactor(changes): bring the PDF render toggle in line with the pane conventions
Escape the .pdf extension in the op-target regex (the unescaped /.pdf$/
matched any path ending in pdf) and fold the copy-pasted toggle button
into PaneToggle; record the sixth deliverable in the design doc.
2026-09-06 23:48:25 +08:00
Menghuan1918 0c3d1d6e32 docs(changes): design doc for the op-preview enhancements
Records the five features shipped on this PR (md reading mode incl.
mermaid, highlight expansion, secret redaction, html render mode), their
deliberate boundaries (redaction heuristics, saved-file render
semantics), and the 2026-09-06 standards pass.
2026-09-06 23:46:46 +08:00
Menghuan1918 157e02e768 refactor(changes): dedup the op-preview path and align conventions
- redaction useMemo: one redactText pass per field (the outcome carries
  both text and hit; was a hit-scan plus a mask pass), and the banner
  condition drops the redundant redactionOn check (hit implies on)
- three copy-pasted header toggles -> PaneToggle; the md reading body
  -> MdReadingView (absorbing the mermaid branch + codeLabels)
- redaction preference persisted under the repo's dsh-sidebar:v1: prefix
- render toggle limited to .html/.htm (the editor html viewer's ext set;
  .xhtml went beyond any claim)
- tsx/jsx share TS_WORDS/JS_WORDS; CSS word list loses its duplicate
  entries and its nonexistent true/false constants
- parseReadContent/parseReadLines share the contentLines() envelope core
  (content extraction + Showing-lines filter)
2026-09-06 23:46:46 +08:00
Menghuan1918 04693192df fix(redact): keep the credential heuristics off ordinary files
Path layer: patterns now match with a right boundary (must not continue
into [a-z0-9]) and list only store-plural forms (credentials/secrets/
passwords, not tokens), so tokenizer.ts / dev.environment.ts /
style.keys.ts / api-tokens.md are no longer masked whole while
.env.local / credentials.json / mytoken.yaml still are.

Content layer: drop the bare key/auth/token/pass/bearer field names —
they label far more ordinary content (keyboard keys, CSS custom
properties, lexical tokens) than secrets; qualified names stay and
Bearer schemes remain covered by BEARER_RE.

Adds the over-masking regression cases; fixes the spec's source import
extension to .ts (repo convention).
2026-09-06 23:46:46 +08:00
Menghuan1918 03e19c4cd4 fix(changes): use real skin tokens in the preview-pane additions
.htmlFrame hardcoded #fff -> var(--dsw-alias-bg-base) (mirrors the editor's
.editorHtml); .redactBanner referenced --dsw-alias-state-warning-primary,
which does not exist in the DSW vocabulary (it is state-warn-*), so the
var() always fell through to a hardcoded #b8860b — use the real token with
no literal fallback.
2026-09-06 23:46:46 +08:00
Atlas Zeng 2dc2dcf418 Merge pull request #569 from omdsh-dev/fix/markdown-preview-local-images
fix(editor): rewrite local images in markdown runs of the split renderer
2026-09-06 22:52:37 +08:00
Menghuan1918 2a99968fa3 fix(editor): rewrite local images in markdown runs of the split renderer
Every .md preview renders through MarkdownDocument (htmlInfo is ungated),
but its markdown segments passed the raw source into MarkdownText, whose
chat-security stance drops non-http(s) image destinations — so local
markdown-syntax images (![alt](./icon.svg), reference-style definitions)
always fell back to alt text. Only HTML <img src> tags were rewritten
(postProcessSanitized).

Apply rewriteLocalImageUrls to each markdown run (segment + appended
reference definitions) inside MarkdownDocument's prepared memo, through
the same media trust fence the sanitized HTML leaves already use.
Idempotent for already-absolute media URLs; remote destinations
untouched. Also correct the stale "non-null only for HTML documents"
comment on TextEditor's htmlInfo.
2026-09-06 21:53:49 +08:00
Atlas Zeng bb21657eae Merge pull request #562 from omdsh-dev/fix/sidechat-seed-inherited-inbox
fix(sidechat): 种子补 fork 标记,子会话不再继承父会话未领取的 inbox 消息
2026-09-06 21:43:13 +08:00
Menghuan1918 93ad87dfe7 fix(sidechat): stamp the seed with fork markers so the child inherits no pending inbox
sidechat.start passed `seed` without `meta.isSeeded: true` +
`inheritedEventCount`, so dsh-session treated the whole seed as the
child's OWN events and the child's Inbox constructor replayed the
parent's `agent/inbox/spliced` history — inheriting whatever input sat
UNCLAIMED in the parent at the click moment:

- next-turn: a queued follow-up behind a still-running turn;
- next-step (the reported long-context case, parent queue empty): a
  tool-result context / steering spliced between step boundaries.

The first side prompt then claimed [phantom, boundary, question] — the
stale parent message was sent to the model BEFORE the boundary and
logged as a user/message. The fix passes the exact marker pair the
host's own session.fork uses (api-session-controller); ownEvents() now
stops at the end-seed boundary and the inherited inbox replays empty.

Regression tests run the REAL Session.create + Inbox in both
directions (old shape inherits the phantom, marked shape stays empty);
routes tests pin the two create options. Verified: pnpm typecheck /
test (5 pre-existing unrelated UI-spec failures on main unchanged) /
build.
2026-09-06 00:59:03 +08:00
Atlas Zeng a5c52b3f1b Merge pull request #550 from omdsh-dev/feat/file-tree-rename-delete
feat(files): 文件树重命名/删除 + 右键菜单减重与子菜单钳制
2026-09-04 23:04:37 +08:00
Menghuan1918 e39ea92ade feat(files): tree rename (inline) + delete (confirmed) + submenu visual tether
资源管理器补全:文件树右键菜单新增「重命名」(行内编辑:预填+全选、
Enter/失焦提交、Esc 取消、IME 组合键守卫)与「删除」(Modal 确认,文件/
目录文案区分,永久删除——宿主无回收站)。根行(工作区根)不提供两项,
服务端 fs.rename/fs.remove 双保险拒绝;目标已存在 409;link-aware——
symlink 行只改/删链接本身(lstat 判定),fence 校验对解析后目标进行。

落定收尾:pruneTree 清旧前缀缓存 + 收起 stale expanded + 单层重载父目录;
tab 调解走 service(rename→updateTab 改路径改标题、delete→closeTab 关
受影响 tab,覆盖两侧分栏/底栏/浮动窗口,经 tree-mutations.ts)。

二级菜单视觉关系(layout.css,仍限 data-dsh-sidebar-submenu 作用域):
父行在子菜单打开期间保持高亮(宿主已渲染 aria-expanded="true"),
卡片间距 10px→4px(悬停走廊同步收窄),翻转规则一并 4px。

i18n 新增 7 key × 20 语言文件(locales.spec 的 key-set-parity 门禁)。
测试:fs-operations 追加 12 例(rename/remove 全失败面);新增
file-tree-rename-delete.spec.tsx 8 例(菜单可见性/重命名流/删除流/错误条);
menu-flip.spec.tsx 契约扩展覆盖 tether 规则。设计文档
docs/plans/2026-09-04-file-tree-rename-delete-design.md。
2026-09-04 22:08:02 +08:00
Menghuan1918 76b57508f4 fix(ui): compact context menus + viewport-clamped submenu flip
减重:全部右键菜单(FileTree 行菜单 / TabBar tab 菜单 / GitLens 文件与
历史菜单 / FreeWindow 标题菜单)与「+」菜单切到宿主 Menu 原语的 compact
密度(26px 行高 / 12px 字 / 164px 卡片),图标随槽位 16→14,「打开方式」
子菜单图钉 20→16,还原工具型右键菜单的趁手感。

越界:宿主子菜单纯 CSS 定位(从父行向上、向右生长)不受视口钳制——树顶
部右键时「打开方式 ▸」子菜单伸出视口上沿,窄面板(280–400px)时伸出右
沿。新增 menu-flip.ts:菜单打开期间在 <body> 发布 data-dsh-sidebar-submenu
="down|left" 方向 token(y < vh/2 → down;x + 400 > vw → left),layout.css
据此翻转子菜单生长方向与 ::before 悬停桥;属性存续期即作用域,宿主自有
菜单不受影响。刻意不做 max-height 滚动钳制——overflow 裁剪会切断悬停桥。

测试:menu-flip.spec.tsx(token 纯函数 + hook 生命周期 + CSS 契约跨文件
一致性);tab-bar / file-tree 两个 spec 追加翻转向量断言;mount.e2e 追加
compact 行高(≤30px)布局级证据。设计偏差记录同步至
docs/plans/2026-08-22-open-with-menu-design.md。
2026-09-04 20:28:35 +08:00
Atlas Zeng e1b9b539e4 Revise version details and update QR codes
Updated version information and fixed QR code image links in README.
2026-09-04 17:33:42 +08:00
Menghuan1918 f59ffd0741 docs(plans): 全方位代码防腐审查报告与 backlog(2026-09-03) 2026-09-03 21:21:22 +08:00
Atlas Zeng f7332f332b Merge pull request #542 from omdsh-dev/feat/split-sidebar
feat(client): Sidebar.tsx 按关注点增量拆分
2026-09-03 21:20:15 +08:00
Menghuan1918 e105b01f2b refactor(client): 抽取自由窗口拖出检测与浮窗层到 sidebar/free-windows.tsx
Sidebar.tsx 拆分第五块:useFloatDragout(document 捕获态 dragover/
drop/dragend/blur 监听、会话列矩形命中测试、hint 状态去抖)与
FreeWindowLayer(floats.map 渲染 + 拖出 hint 覆盖层,fragment 渲染
保证 DOM 顺序与原先逐字一致)原样迁出,注释逐字保留。

行为不变:effect 依赖与回调闭包相同;层组件接收的 renderTab/
getTabIcon/store/ctx/sessionId/cwd 即原先 JSX 内联闭包捕获的同一批
每 render 新建的值,FreeWindow 收到的 props 与回调语义不变。
2026-09-03 21:09:20 +08:00
Menghuan1918 b4ab3f154f refactor(client): 抽取 pinned 虚拟 tab 注入与动作包装到 sidebar/use-pinned-tabs.ts
Sidebar.tsx 拆分第四块:activePinnedTabId/pinnedRevision 状态、
pinnedEntries/pinnedVirtualTabs/augmentedTree 三个 memo(含
exhaustive-deps 豁免注释随迁)与 wrappedActions(虚拟 id 路由回
HOME 会话的 reduceFor + ptyClose 语义)原样迁出,注释逐字保留。
调用点移到 actions memo 之后(hook 无 effect,仅 useState/useMemo,
位置调整不改变行为)。

行为不变:memo 依赖数组逐字相同;wrappedActions 的 actions 入参即
原先闭包捕获的同一个 memo 产物。
2026-09-03 21:08:03 +08:00
Menghuan1918 d5b1201ad8 refactor(client): 抽取宿主推送与自动激活触发器到 sidebar/use-host-feeds.ts
Sidebar.tsx 拆分第三块:agent-terminals / agent-opens 两条 WS 推送
(含重连退避与失败上限)、子代理自动激活(去抖重估)、后台任务自动
激活、拓扑跳回(subagentJumpRef)连同 FAILURE_LIMIT /
AUTO_OPEN_DEBOUNCE_MS 常量原样迁出,注释逐字保留。Sidebar 侧仅
消费返回的 subagentJumpRef(renderTab 的 onSubagentJump 装填)。

行为不变:五个 effect 的依赖数组与闭包值逐字相同,hook 每 render
无条件调用,effect 挂载顺序保持(feeds → center → float →
bottomAuto → drag)。
2026-09-03 21:05:41 +08:00
Menghuan1918 6bccd21a49 refactor(client): 抽取中心列定位/测量链到 sidebar/use-center-column.ts
Sidebar.tsx 拆分第二块:centerRectRef/centerMeasured/centerColRef/
draggingRef、measureCenter(ref + 直写 DOM,避免动画帧级 re-render)
与整条 locate/ResizeObserver/MutationObserver/1.5s 重试链原样迁出到
src/client/sidebar/use-center-column.ts,注释逐字保留。bottomRef 与
state?.bottomOpen 由调用方传入(bottomRef 在主文件先建再共享,行为与
原先 measureCenter 闭包捕获一致)。

行为不变:hook 每次 render 全量重跑,effect 依赖与值完全相同;唯一
文字差异是三处依赖数组补列了跨 hook 边界后失去 useRef 溯源的稳定
ref 对象(centerColRef/draggingRef,标识恒定,不改变触发时机),
为 react-hooks/exhaustive-deps 零告警所需。
2026-09-03 21:03:14 +08:00
Menghuan1918 dbc73f0bf3 refactor(client): 抽取 TabContent 单元格与 + 菜单构建到 sidebar/TabContent.tsx
Sidebar.tsx 拆分第一块:memo 化的 TabContent(含 TabContentProps 与
tabContentCompare 比较器)和 buildNewTabOptions 原样迁出到
src/client/sidebar/TabContent.tsx,注释逐字保留。

行为不变:组件体零改动,仅模块路径与 import 调整(相对路径加一层
../);Sidebar.tsx 改为 import 该模块,对外导出面不变(仍只有
Sidebar 具名导出)。
2026-09-03 21:00:20 +08:00
Atlas Zeng 68839aa5fb Merge pull request #541 from omdsh-dev/feat/unify-polling
feat(client): 收敛四处轮询习语到共享 use-polling 原语
2026-09-03 20:45:46 +08:00
Menghuan1918 1ec1565b13 refactor(client): 四处轮询站点迁移到 use-polling,语义逐一保持
- SubagentView useSubagentLive:self-scheduling(上一请求 settle 后才排
  下一次,不重叠)+ 每代 AbortSignal + signal.aborted 拦迟到写入,行为
  与原 disposed 标志等价(teardown 同步置 disposed 并 abort)。
- SideChatView:立即拉取 effect 与 2s 定时拆分——输入变化(含 running
  翻转)仍先拉一次转录,运行中间隔 tick 拉转录+徽章;fetchThread 自带
  的逐次 abort 与卸载 abort 原样保留。
- GitLens:可见时 2s 固定静默 tick,无初始突发(mount/scope 变更的完整
  刷新在别处);refresh 身份变化照旧重启间隔相位。
- ChangesTab:输入变化即拉一次(含隐藏态翻转),可见时 2.5s 固定间隔;
  pull 内部 pollGen 代际守卫不动。

守护 spec 全绿:subagent-live-polling + subagent-jobs-view 13/13、
sidechat-view + sidechat-transcript 29/29、git-view-worktree +
changes-tab + changes-ops 13/13;断言零改动。
2026-09-03 20:35:59 +08:00
Menghuan1918 6ef11ae010 feat(client): 引入共享 use-polling 轮询原语
四处站点(SubagentView 活动预览 / SideChatView 转录 / GitLens 状态 /
ChangesTab 会话事件)各自手写取消安全与代际失效,机制各异。先落地共享
原语:间隔与调度模式(fixed-interval / self-scheduling)参数化、每代一个
AbortSignal + 卸载/依赖变更即失效(迟到响应靠 signal.aborted 拦截,不信任
abort 必然触发 reject)、任务异常静默保留旧值由下一 tick 重试。
2026-09-03 20:35:59 +08:00
Atlas Zeng aa01140252 Merge pull request #540 from omdsh-dev/feat/drop-rc7-fallback
feat(client): 删除 rc.7 宿主的 __DSH_MODULES__ 回退路径
2026-09-03 20:35:46 +08:00
Menghuan1918 dc33087ce1 feat(client): 删除 rc.7 宿主的 __DSH_MODULES__ 回退路径
peer 基线已是 ^0.1.2-rc.1(全部 dsh-* peer),最后提供
window.__DSH_MODULES__ 页面全局的宿主是 0.1.0-rc.7,远低于下限,
装不上本插件——回退在受支持宿主上不可达。受支持宿主上 apply() 在
任何懒加载发生前调 setChunkModuleSystem(ctx.modules),moduleSystem()
前两跳(注入变量 + 插件全局 __dshSidebarModuleSystem__)必中。

回退的唯一活消费者是 vitest fixture:installModuleSystem() 原先经
rc.7 全局投递 fake 模块系统,现改为与生产同路径的 setChunkModuleSystem
注入;无任何专测回退分支的用例,全部断言保持不变。

- chunk-loader.ts: 删 moduleSystem() 第三跳,同步头注 /
  ChunkModuleSystem 接口注中的回退描述
- index.tsx: 删「falls back to the rc.7 global」注释
- README v0.14.0 changelog 与 docs/plans 设计文档中的提及属历史
  存档(描述 PR #232 当时的迁移),保留不改

验证:lint / typecheck / build / vitest 119 文件 1243 用例全绿;
真机挂载 lane(钉版 @deepseek-ai/dsh@0.1.2-rc.1 CLI)mount.e2e
全内置 tab sweep(含终端 / editor 懒加载 chunk)通过;lib/*.js
产物中无 __DSH_MODULES__ 残留。
2026-09-03 20:29:59 +08:00
Menghuan1918 8820efd6bf fix(tests): 补回 add-plugin-modal.spec 缺失的 builtinTabPlugins import
#535(i18n catalog names)给 tests/add-plugin-modal.spec.tsx 新增
sentinelEntry 行时漏了 import;#535 自身 CI run 被 cancel,#536 带病
合入,main HEAD 7b5c7dd 的 typecheck/test 双红(本地 stash 复现一致:
TS2304 / ReferenceError: builtinTabPlugins is not defined,CI run
33754439466 failure)。补上与 plugin-list.spec.ts 同源的 import。

与本 PR 主题无关,仅为本流验证门(typecheck/test 全绿)所需的最小
main 修复,独立成 commit 便于 review 剔除。
2026-09-03 20:29:50 +08:00
Atlas Zeng 7b5c7ddcc4 Merge pull request #536 from omdsh-dev/feat/eslint
feat(lint): 引入 ESLint flat config 并接入 CI 与 Makefile
2026-09-03 20:18:10 +08:00
Atlas Zeng f8bb3013ff Merge pull request #535 from omdsh-dev/feat/i18n-catalog-names
feat(i18n): 目录项与 shell 预设文案词典化,兜底错误条改皮肤令牌链
2026-09-03 20:18:05 +08:00
Atlas Zeng 463e96b6b4 Merge pull request #534 from omdsh-dev/feat/palette-single-source
refactor(theme): One Dark/Light 语法色板单源化
2026-09-03 20:18:01 +08:00
Menghuan1918 98e9a4ff0f docs(readme): 开发与构建命令清单补 pnpm lint(双语镜像) 2026-09-03 20:00:08 +08:00
Menghuan1918 5a1e75e46b fix(lint): 清零存量 lint 告警并接入 CI / Makefile 门禁
存量清理(首跑 141 errors / 21 warnings → 0 / 0):
- 删除孤儿 disable:SideChatView / Sidebar / selection-popup 三处 exhaustive-deps 豁免与 chunk-artifact 的 no-new-func 豁免(多行理由注释把 disable-next-line 顶离了实报行;selection-popup 改为提取 popupOpen 标志真修复)
- 死代码:未用导入(Sidebar×7、TextEditor、SideCardSection、config、fs-operations 及 6 个 spec)、sidechat-core 死累加器 total、chunk-loader.spec 死助手 registry、sidechat-seed-validation 死助手 toolResultMessage
- 真实小修:agent-opens 三处 throw 补 cause;lazy-chunk any → ComponentType<P>(P extends object);service.ts claimed 初值改定值赋值;chunk-loader task / spec 内 etag / pendingLoad / snapshot 改 const;terminal-links 去掉字符类内多余转义;SubagentView catalogs 空兜底 useMemo 稳定标识;editor-host.spec 组件提出大写 FakeViewer(rules-of-hooks);pty-helpers.spec 内联 require 提升为静态导入;panel-host-css 注释里的零宽空格改为可见写法
- 依赖数组豁免(均带理由):granular scope 字段(EditorHost / PdfView / ChangesTab / GitLens×2)、job.status 粒度(SubagentView×2)、清理期读 ref、mount-only 注册等;ctx(挂载期恒定)直接入 deps(Sidebar×3)、TextEditor previewScroll 补 scope(幂等)
- 刻意控制字符正则(conversation-draft / markdown-images)与宿主槽位 any 签名(context-types)以带理由豁免保留

接入:
- Makefile:lint 目标(typecheck 旁),check 聚合补 lint 保持「对齐 CI」语义
- ci.yml ci / ci-windows 两 job 与 release.yml 在 Typecheck 后加 Lint 步骤(release 注释同步顺序)
2026-09-03 20:00:08 +08:00
Menghuan1918 f418692ad0 build(lint): 引入 ESLint 工具链与 flat config 最小规则集
- devDependencies: eslint 10 / typescript-eslint 8 / eslint-plugin-react-hooks 7 / globals / @eslint/js(ESLint 10 起 @eslint/js 不再随 eslint 自带,需显式安装)
- eslint.config.js:@eslint/js recommended + typescript-eslint recommended(非 type-checked)+ react-hooks recommended(v7 的 React Compiler 语义规则档整体关闭——57 处存量误报,保留经典 rules-of-hooks / exhaustive-deps 双规则)
- globals 分域:src/client → browser;src 根 / scripts / 根级 config → node;tests → node+browser
- reportUnusedDisableDirectives: error 揪孤儿豁免;no-unused-vars 对齐仓库既有 _ 前缀占位约定
- package.json 增加 lint script(eslint .)
2026-09-03 20:00:07 +08:00
Menghuan1918 e8834bd349 feat(theme): drive the last-resort error bar through skin tokens
The host-anchor fallback diagnostic strip inlined hardcoded colors
(#f2a1a1 on #1b1b22). Route them through var() chains like the existing
SideChatView.module.css fallback pattern: text/border read
--dsw-alias-state-error-primary (the codebase's canonical error color,
23 existing usages) and the background reads
--dsw-alias-bg-layer-3,var(--dsw-alias-bg-base,...) (elevated surface,
then page base). The old hexes ride as the chain tails, so with no skin
tokens on the page the bar renders byte-identical to before, while any
--dsw-alias-* skin re-themes it (guide §12: no hardcoded colors). The
guide does not cover this strip and tests/theme.spec.ts asserts
effectiveTokenValue semantics only, so neither needs a follow-up.
2026-09-03 19:59:00 +08:00
Menghuan1918 040b182b1c refactor(theme): One Dark/Light 语法色板单源化
新增 src/client/one-dark-palette.ts,作为 one-dark / one-light 两套语法
色系的唯一来源:TerminalView 的 ANSI 16 与 cm-themes 的 CodeMirror 高亮
规则改为从色板常量组装各自的映射(ANSI 槽位 / lezer tag 到色相的映射
仍留在各消费方——两边的角色映射本就不同)。

行为不变的纯重构:每个 hex/rgba 迁移前后逐字节一致(旧新片段在桩
tags 下求值深度比对相等;产物 hex 清单无删除、非色板 hex 计数零变化)。
TerminalView 的 token 兜底色与 cm-themes 的 selection/activeLine rgba
与语法色系无关,保留原处未合并。

chunk 边界:色板为纯常量,被 terminal / editor 两个懒 chunk 各自内联
(每 chunk 约 200B 字面量),不引入跨 chunk import;核心 bundle
(client.js / client-registry.js)对该模块零引用(grep 产物验证)。
tests/one-dark-palette.spec.ts 钉住色板字面量防意外漂移。
2026-09-03 19:59:00 +08:00
Menghuan1918 fa301da29e feat(i18n): localize plugin catalog names and shell preset desc
Catalog entry names (15 tab + 4 viewer) and the dsh-desktop shell preset
desc were raw hardcoded zh strings rendered straight into the add-plugin
modals and the title-bar scheme dropdown. Mirror the existing desc
pattern exactly: PluginEntry.name / ShellPreset.desc widen to the
i18n-friendly string-or-() =>-string union, the catalogs carry lazy
t('pluginXxxName') / t('presetDshDesktopDesc') lookups, and the
add-plugin modal (render, search filter, aria-labels) plus the scheme
SelectMenu resolve them like every other copy. The plain-string arm of
the union stays as the raw-name fallback, and t() itself never yields
empty text (missing keys render the key), so a dict gap can never blank
a name.

20 new dictionary keys added to zh/en (locales.ts), ja (locales-ja.ts),
and all 18 third-language dictionaries — tests/locales.spec.ts enforces
key-set equality against zh for every shipped dict (precedent: 9cc642b),
so the 'leave other locales-* untouched' shortcut would fail the hard
parity gate; real translations are provided instead.

Spec follow-ups (behavior change: names/descs now render through t()):
- plugin-list.spec.ts: name non-empty assertion resolves the union.
- add-plugin-modal.spec.tsx: sentinel/office name assertions resolve
  from the catalog (en locale in tests) instead of pinning zh literals.
- shell-presets.spec.ts: desc non-empty assertion resolves the union.
2026-09-03 19:59:00 +08:00
Atlas Zeng 9e1a034527 Merge pull request #537 from omdsh-dev/feat/adapt-dsh-0.1.2-rc.1
feat: adapt to DSH 0.1.2-rc.1 and bump to v0.18.0 (stable)
2026-09-03 19:38:01 +08:00
Menghuan1918 ab6f960fe4 fix(ui): animate the toggle cluster's recenter between tab strip and session header
The viewport-corner toggle cluster sits at top:3 riding the right panel's
tab strip while expanded, and recenters to top:14 over DSH's session
header row when collapsed — an 11px snap on every expand/collapse. Ride
the panel slide's own duration/easing (--ds-transition-duration-slow +
--ds-ease-in-out) so both moves stay in lockstep; prefers-reduced-motion
already clears .toggleCluster transitions.
2026-09-03 18:15:41 +08:00
Menghuan1918 fbfd59bd1b feat: adapt to DSH 0.1.2-rc.1 and bump to v0.18.0 (stable)
Graduate from the alpha track to the stable channel:

- Baseline: CI mount pin, dsh.plugin.json engines floor, and every
  @deepseek-ai/* peer floor / devDependencies pin move to 0.1.2-rc.1
  (published on the `next` dist-tag). alpha.5 -> rc.1 is a pure version
  bump in the harness (all package.json "version" lines, zero source
  delta), so no code adaptation; every host contract was re-verified
  unchanged and the real-host mount smoke passes.
- dsh-client-locale resumed publishing; its trailing-pin exception is
  gone — peers and devDeps align at 0.1.2-rc.1 (pnpm peers check clean).
- Version: fold the unpublished v0.18.1-alpha.0 / v0.19.0-alpha.0 bumps
  into the v0.18.0 stable release; SIDEBAR_SERVICE_VERSION follows
  package.json (asserted by tests/service.spec.ts).
- Docs: README/README_EN badges + channel notes + a full "since v0.17.1"
  changelog entry (the two folded alpha numbers are annotated), AGENTS.md
  §2/§3 baseline wording, external-plugin-guide version line, and the
  e2e-host-protocol header's re-verified list gains 0.1.2-rc.1.
2026-09-03 17:32:04 +08:00
Atlas Zeng 60f9acdcbe Merge pull request #527 from omdsh-dev/fix/scripts-hardening
fix(scripts): e2e 脚本加固——tarball mtime 选取/DSH_CMD 统一/registry mermaid chunk/e2e-common.sh 去重
2026-09-03 16:30:12 +08:00
Atlas Zeng dd47a2c864 Merge pull request #526 from omdsh-dev/feat/dev-commands
feat(dev): 引入 Makefile 规范化命令面
2026-09-03 16:30:08 +08:00
Atlas Zeng f24f95f6d2 Merge pull request #525 from omdsh-dev/feat/dedup-core
feat(client): 收敛重复实现(api envelope/extOf/lineage/错误归一化)并删除死代码
2026-09-03 16:30:04 +08:00
Atlas Zeng ceed6b244e Merge pull request #524 from omdsh-dev/feat/test-utils
feat(tests): 收敛组件测试样板到共享 test-utils
2026-09-03 16:30:00 +08:00
Menghuan1918 3d23ceb467 fix(ci): pnpm 版本统一由 packageManager 字段供版,移除 action 显式 version 输入 2026-09-03 12:17:28 +08:00
Menghuan1918 c026676f41 refactor(e2e): 抽取 e2e-common.sh,收敛两挂载脚本的重复骨架
e2e-mount.sh 与 e2e-aggregate-mount.sh 约 40% 逐字重复:say/warn/die、
node/pnpm 前置校验、DSH_CMD 与 tarball 解析、scratch home + 清理 trap、
profile 三件套 heredoc、dsh web 启动与就绪轮询。收敛为 scripts/
e2e-common.sh 纯函数库(日志前缀 E2E_TAG 参数化),两脚本 source 之。

行为等价性(差异点全部参数化,既有注释语义原位保留):
- DSH_HOME 子路径:mount 用 $SCRATCH/home,aggregate 直接用 $SCRATCH
  (调用点各自 export);
- 就绪行 grep:mount 要带 token 的完整 URL(head -1 取首个),aggregate
  只取裸 origin(tail -1 取末个)——E2E_READY_RE / E2E_READY_PICK 传入,
  两侧论证注释保留在各自脚本;URL 提取统一为命中串取末段(mount 模式带
  `dsh web: ` 两段前缀故等价于原 awk '{print $3}',aggregate 模式不含
  空白故等价于原整行);
- 额外清理项:aggregate 的 fixture TMP_DIR(mount 不设该变量即跳过);
- 就绪失败文案/尾部行数差异(提前退出 tail -30 vs 超时 tail -40 vs 双
  日志 tail -5)留在调用点。

e2e-common.sh 为纯开发侧文件,不进 npm files 白名单;install.sh /
install.ps1 随 npm 包独立分发,保持自包含,不 source 它。
2026-09-03 12:08:43 +08:00
Menghuan1918 35111e163c docs(readme): 双语补 Makefile 用法、聚合挂载回归与一键安装入口
- 开发与构建:补 Makefile 薄封装示例(help/check/mount/clean)与
  pnpm check:consumer-types 的定位说明(浏览器-only 消费者视角)
- 参与贡献:提交前自检补 make check 与 pnpm test:mount:aggregate
- 安装:补方式三一键脚本(bash scripts/install.sh;Windows install.ps1,
  -h 查看参数);README 与 README_EN 同步镜像
2026-09-03 12:07:35 +08:00
Menghuan1918 6201a2c6e6 build(ci): 补 test:mount:aggregate / clean / packageManager 并对齐 release 校验顺序
- package.json 新增 test:mount:aggregate(scripts/e2e-aggregate-mount.sh 的
  npm 别名,CI plugin-mount lane 改经它驱动,保留 DSH_CMD/DSH_TELEMETRY_DISABLED
  env)与 clean(node rmSync 删 lib,与 build 开头清理同款、跨平台);
  只新增普通 script,不动任何生命周期钩子(market-manifest.spec.ts 对
  packed manifest 的 lifecycle 断言不受影响,已实跑验证)
- packageManager 钉 pnpm@11.8.0(本地实装版本;pnpm 10+ 运行时按该字段
  自切版本,CI action-setup 的 version: 11 输入与其兼容不冲突)
- release.yml 校验顺序统一为 typecheck → test → build → check:consumer-types
  (与 ci.yml 一致),补上缺失的 consumer 类型面守卫;tag 校验等非构建
  步骤位置不变
2026-09-03 12:07:29 +08:00
Menghuan1918 789696aa31 build(dev): 引入 Makefile 命令面薄封装与 .editorconfig 基线
- Makefile 仅转发 package.json scripts(唯一事实源),补 help 自说明、
  clean 清理与 check / mount / mount-aggregate / registry 四个聚合目标;
  check 用单条 recipe 串 typecheck → build → test → check:consumer-types,
  避免 make 前置依赖在 -j 下乱序;lint 目标留给引入 ESLint 的后续 PR
- .editorconfig:root + utf-8/lf/末尾换行/去尾随空格,默认 2 空格,
  Makefile recipe 保持 tab
2026-09-03 12:07:23 +08:00
Menghuan1918 bc1984b2f2 refactor(sidechat): 删除零引用的 sidechatSeed
src/sidechat-core.ts 的 sidechatSeed()(buildSidechatInheritance 的
seed 半边,doc 注明 test convenience)在 src+tests 全仓零引用,也无
barrel 转出口,删除。同文件的 SeedEvent 类型与 copyEvents 仍被
SidechatInheritance 使用,保留;其余导出不动。
2026-09-03 12:05:48 +08:00
Menghuan1918 4002f3cc5c refactor(client): 统一 GitLens 错误归一化与 SubagentView 日志分级
- GitLens.tsx:六处 catch 里的 reason instanceof Error ?
  reason.message : String(reason)(refreshTarget/refresh/loadMoreLog/
  commit/checkout/runConfirmed)收敛为模块级 errorMessage(),
  两处模板字符串插值场景(historyLoadError/checkoutError 前缀)
  同样复用;归一化表达式与输出文本逐字不变。
- SubagentView.tsx:openSubagent/open session 失败的 console.warn
  改为 console.error,与同严重度失败路径对齐(Sidebar.tsx tab
  badge、TerminalView.tsx xterm open、service.ts urlTarget/plugin
  callback 均为 console.error);前缀与消息文本不变。tests/ 无任何
  钉 console.warn 的断言,已核实。
2026-09-03 12:05:48 +08:00
Menghuan1918 69cc8014a7 refactor(client): subagent lineage 遍历收敛为共享 subagent-lineage 模块
rootAncestor / countSubagentDescendants / treeSessionIds 三份「沿
subagent-origin 链上行 + seen 集合防环 fail-soft」实现收敛到新模块
src/client/subagent-lineage.ts:核心走链是单个私有生成器
subagentOriginChain()(seen 防环、只在节点为 subagent 且有 parent 时
前行),三个公开函数迁入并基于它重写;isSideThreadSummary 一并迁入
(countSubagentDescendants 依赖它,迁入可避免 detect↔lineage 循环
依赖),subagent-detect.ts / subagent-jobs.ts re-export 全部迁出
符号,既有 import 调用点零变化。

语义逐字保留的等价性论证:
- rootAncestor:链为空返回 start.id(原循环条件不满足即返回
  current?.id);链非空返回 byId[末节点.parentId]?.id ?? sessionId
  (原循环退出时 current 恰为该行,断链/行缺失回退 sessionId 不变)。
- countSubagentDescendants:生成器在 seen.add 后 yield、消费方判
  parentId === sessionId 即 break——与原循环体内判断顺序一致;
  descendant 的 running 判定、side-thread 过滤位置不变。
- treeSessionIds:根行缺失(byId[rootId] 无行)时任何上行都不可能
  「访问到」根(原实现只匹配真实访问过的行),故提前返回空集;
  根行自身(非 subagent)经 summary.id === rootId 特判入集;其余
  行经链节点 parentId === rootId 判定,等价于原「上行走到 id ===
  rootId 的行」——两种表述指向同一行(链上节点的 next 行)。

directSubagentCount 不迁移:它是对直接子行的单层过滤,没有上行
walk,无共享逻辑(背景侦查将其归入 walk 家族与实际代码不符)。
collectBranchIds 遍历的是 catalog 结构而非 session 链,保持不动。
2026-09-03 12:05:41 +08:00
Menghuan1918 857646ea66 refactor(client): 收敛 /sidebar envelope 解析与取扩展名双实现
- api.ts:call 与 fetchUpload 里逐字相同的响应 envelope 解析
  (json().catch(null) + ok/value 判断 + SidebarApiError 构造)收敛为
  私有 readEnvelope<T>(),两处调用;判断顺序与错误码回退逻辑不变。
- 取扩展名双实现收敛:extOf 的实现迁至零依赖的 paths.ts(lang.ts 与
  service.ts 均可达的基础模块),lang.ts re-export 保持既有导入面
  (tests/file-types.spec.ts 从 lang.ts 导入 extOf);service.ts 删除
  模块私有的同体 extOfPath,matchFileViewer 改用 extOf。方向说明:
  service.ts 不能反向 import lang.ts——lang.ts 携带全部 @codemirror/*
  依赖且只经 editor 懒加载 chunk 进入浏览器,core bundle 静态引用它
  会把 CodeMirror 拉进核心包(构建产物已验证:client.js /
  client-registry.js 中 0 处 codemirror,client-editor.js 不变)。
2026-09-03 12:05:29 +08:00
Menghuan1918 0831505854 test(lib-guards): lib 产物缺失时 chunk-artifact / manifest-consistency 跳过而非 ENOENT
fresh clone 未 build 直接 pnpm test 时,两 spec 的 readFileSync 会
ENOENT 崩溃(此前靠 package.json prepare 钩子隐式兜底)。改为文件级
describe.skipIf 守卫:缺产物时整套显示为 skipped 并 console 提示先
pnpm build(exit 0);产物存在时行为与之前完全一致(本仓 prepare 钩
子产出 lib 后两 spec 照常执行,已验证 9 passed 非 skipped)。
2026-09-03 12:04:41 +08:00
Menghuan1918 936df662b0 test(specs): 收敛 28 个组件 spec 的渲染样板到共享 test-utils
- 7 个 spec 的本地 mount()(createRoot + act 容器循环)替换为
  renderRoot():sidechat-view、subagent-jobs-view、subagent-live-polling、
  lazy-chunk、side-card-section-rows(本地 mount 删除,调用点改名);
  editor-refresh、markdown-manual-refresh(EditorHost 包装 mount 改为
  委托 renderRoot,调用点不动)
- 24 个 spec 的内联 IS_REACT_ACT_ENVIRONMENT 赋值替换为 setupReactAct()
  (subagent-jobs-view / subagent-live-polling / lazy-chunk 原本未设
  flag,保持原样不引入)

全部为语义相同的样板替换:迁移后全量 118 files / 1241 passed +
9 skipped,与迁移前一致。
2026-09-03 12:04:35 +08:00
Menghuan1918 a627a4c84d test(utils): 新建共享 React 测试工具 setupReactAct / renderRoot
收敛组件 spec 两类样板的最小共享面:IS_REACT_ACT_ENVIRONMENT 幂等设置
(vitest 按文件隔离 global,逐 spec 调用与原内联语句语义一致)与
createRoot + act 的分离容器渲染循环。刻意不做查询 DSL / 自动清理 /
新依赖。
2026-09-03 12:04:24 +08:00
Menghuan1918 61c9c550d9 fix(registry): registry 打包补上 mermaid 懒加载 chunk
package.json files 白名单与 tsdown 产物都含 lib/client-mermaid.js(+
.map),registry 通道的复制清单却缺它——经 dsh registry 安装后 Markdown
预览首次加载 mermaid 会拿到 /sidebar/bundle 404。与 terminal/editor
两 chunk 同款补齐。
2026-09-03 12:03:05 +08:00
Menghuan1918 2052d0554b fix(e2e): 聚合 lane 按 mtime 取最新 tarball 并统一 DSH_CMD 缺省解析
e2e-aggregate-mount.sh 选 tarball 用字典序 `ls | head -1`,仓库根堆积
多个历史 tgz 时会把冒烟挂到过期产物上——完整移植 e2e-mount.sh 的
mtime 策略与多 tarball 告警。DSH_CMD 缺省从直连 npx 统一为「PATH dsh
优先、npx 回退」(CI 传 DSH_CMD=dsh 不受影响)。

顺带修一个移植中暴露的边角:候选计数行在零候选时因 pipefail+set -e
静默退出,吞掉下一行的友好报错——补 `|| true`(mount.sh 同步修复)。
2026-09-03 12:02:57 +08:00
Atlas Zeng ad392ceea4 Merge pull request #522 from wang-kaopu/fix/517-client-ssh-open
fix(editor): open SSH remote editor links on the client
2026-09-03 10:54:34 +08:00
Menghuan1918 5dbbde61ce docs(open-with): 同步 SSH 链接客户端打开后的注释与设计偏差记录 2026-09-03 10:50:26 +08:00
Atlas Zeng fe5aa449ac Merge pull request #520 from omdsh-dev/feat/ci-windows
ci: add a Windows lane for the unit/typecheck/build suite
2026-09-03 10:47:53 +08:00
Menghuan1918 c4c4d73389 fix(ci): resolve consumer-type link targets to their physical path
On the Windows lane the @deepseek-ai/cordis link produced TS2307 even
though the link was visibly created: under pnpm its target,
node_modules/@deepseek-ai/cordis, is itself a junction into .pnpm, and a
native symlink pointing AT that junction does not traverse for tsc.
Resolve both link targets with node's native realpath first
(GetFinalPathNameByHandle also expands junctions, which bash-level
resolution may leave untouched), and fail loudly with the link layer
listed when a created link does not resolve to a package root.
2026-09-03 10:42:56 +08:00
Menghuan1918 6d877471d4 fix(pty): contain node-pty's deferred Windows resize after pty exit
node-pty's WindowsTerminal queues resize calls that arrive before the
ConPTY control socket's first data flush (_deferNoArgs). If the pty exits
before the flush, the deferred resize throws 'Cannot resize a pty that
has already exited' inside the socket's 'data' handler — uncatchable by
any caller and fatal to the host process. The Windows CI lane caught
exactly this as an unhandled error while all 1233 tests passed.

Park pre-first-output resizes in our own gate (armed at every spawn
site) and replay them via setImmediate once output has flushed the
deferred list — by then node-pty executes resizes synchronously, so the
exited throw lands in tryResizePty's catch instead of the host. POSIX
terminals resize synchronously (no deferral), so the gate is armed on
Windows only and the other platforms keep their exact behavior.

Also route AgentPtyRegistry.resize through tryResizePty: it previously
called pty.resize bare, missing the just-exited throw guard the tool and
attach-socket paths already had.
2026-09-03 10:42:56 +08:00
Menghuan1918 24db20a796 test: make five suites Windows-runner clean
First ci-windows run exposed four platform assumptions in the fixtures:

- smoke: the scratch repo inherited the runner's system-wide
  core.autocrlf=true (Git for Windows default), so every index restore
  smudged LF to CRLF and broke the byte-exact assertions — pin
  core.autocrlf=false in the fixture (eol policy is not what these
  destructive-op tests are about).
- git / git-worktree: Windows TEMP is the 8.3 short form
  (C:\Users\RUNNER~1\...) while git reports long paths, and
  realpathSync does not expand 8.3 aliases — switch the canonicalizer
  to realpathSync.native (libuv resolves via GetFinalPathNameByHandle,
  which does); same trick as the existing macOS /var workaround.
- bundle-route: the ETag memo revalidates on stat (mtime+size) and
  Windows updates file timestamps lazily, so the equal-size rewrite
  still looked unchanged — change the length too.
- market-manifest: GNU tar (Git Bash) reads the drive-letter colon of
  absolute Windows paths as a remote-host spec — run it from the pack
  dir with the bare tarball name instead.
2026-09-03 10:42:56 +08:00
Menghuan1918 79d7165f91 ci: add a Windows lane for the unit/typecheck/build suite
The pure-logic code carries real win32 branches (shell parsing, WSL path
projection, the PowerShell installer) that mock-injection tests alone
cannot validate. Mirror the ci job on windows-latest (Git Bash as the
step shell) for typecheck / test / build / consumer-type-surface; the
plugin-mount lane (real DSH + Playwright) stays ubuntu-only.

check-consumer-types.sh: force MSYS winsymlinks:nativestrict so Git
Bash's ln -s creates real symlinks instead of deep-copying the repo
(node_modules + full .git history) — inert on Linux/macOS.
2026-09-03 10:42:56 +08:00
Atlas Zeng c78c6dcf25 Merge pull request #503 from jackie-cqz/fix/windows-custom-shell-resolution
fix(terminal): resolve Windows custom shell executables
2026-09-03 10:42:45 +08:00
Atlas Zeng 94c0a74054 Merge pull request #467 from elysia395/fix/markdown-preview-scroll-position
fix(editor): keep reading position across preview/edit switches
2026-09-03 10:42:32 +08:00
Atlas Zeng c9a960884f Merge pull request #461 from Guochaoo/fix/markdown-preview-toc-and-code-header
fix(markdown): preview TOC dismiss-on-outside-click, popover z-index, code-block banner stickiness
2026-09-03 10:42:20 +08:00
Atlas Zeng b4fd80f07b Merge pull request #417 from ch3n4y/fix/at-file-reference-link
fix: keep file basename in @-reference links
2026-09-03 10:42:07 +08:00
Menghuan1918 4742aa1b9c docs(guide): sync onReferenceFile(path, isDir) signature 2026-09-03 10:02:27 +08:00
Atlas Zeng 396017b459 Merge pull request #448 from chendefine/fix/panel-host-scroll-jump
fix(shell): keep an overflow:hidden fallback beneath clip (old-engine compat) + guard the cascade
2026-09-02 23:55:40 +08:00
Atlas Zeng 5337ef77dd Merge pull request #398 from SparkElf/fix/desktop-sidebar-layout-mode
fix: preserve desktop shell layout with side card
2026-09-02 23:44:15 +08:00
Atlas Zeng a446192008 Merge pull request #373 from Jstn-1g/fix/162-narrow-auto-activation
fix: avoid forcing mobile drawer on auto-activation
2026-09-02 23:44:06 +08:00
Atlas Zeng f61c161494 Merge pull request #455 from wang-kaopu/fix/issue-399-wsl-paths
fix: resolve Linux absolute paths in WSL workspaces
2026-09-02 23:43:58 +08:00
Atlas Zeng 0607517134 Merge pull request #394 from Beverly621/fix/markdown-frontmatter-preview
fix(editor): hide YAML frontmatter in Markdown preview(在 Markdown 预览中隐藏 YAML Front Matter)
2026-09-02 23:43:49 +08:00
Atlas Zeng b8d3b9ef56 Merge pull request #494 from Cyancat/fix/openpath-intercept-alpha
fix(client): intercept the alpha chat file-open funnel (remote.session.openWorkspacePath)
2026-09-02 23:43:41 +08:00
Atlas Zeng dde0923980 Merge pull request #441 from eg-bole/docs/readme-catalog-icons
docs: add dsh-better-sidebar-icons to the enhancements catalog
2026-09-02 23:11:24 +08:00
Atlas Zeng ec24f7f0fc Merge pull request #470 from g-yixuan/fix-ecosystem-table-sidenote
docs: ecosystem table entry dsh-sidechat → dsh-sidenote
2026-09-02 23:11:16 +08:00
Atlas Zeng 02b2310335 Merge pull request #456 from wang-kaopu/fix/issue-403-sidebar-locate-performance
perf(sidebar): reduce repeated center-column DOM queries
2026-09-02 23:11:10 +08:00
Atlas Zeng 051bab2f61 Merge pull request #361 from northwang-lucky/fix/collapsed-toggle-header-align
fix(client): align collapsed toggle cluster with DSH session header / 折叠态开关组对齐 DSH 会话标题行
2026-09-02 23:11:02 +08:00
Atlas Zeng b531dd8051 Merge pull request #469 from Hoemr/fix/explorer-refresh-on-focus
fix(files): auto-refresh the tree on window focus and via a dispatchable event
2026-09-02 23:10:54 +08:00
Atlas Zeng 3b5e255882 Merge pull request #385 from TaurusWood/fix/free-window-restored-id-collision
fix(state): seed restored free-window ids
2026-09-02 23:10:46 +08:00
Atlas Zeng ded3cf13b0 Merge pull request #454 from wang-kaopu/fix/issue-440-old-git-worktree
fix(git): support worktree listing on older Git versions
2026-09-02 23:10:37 +08:00
Atlas Zeng fc666eee47 Merge pull request #366 from GeekRicardo/fix/terminal-font-monospace-fallback
fix(terminal): 自定义字体解析失败时兜底到等宽,避免掉进比例字体毁掉网格
2026-09-02 23:10:30 +08:00
Atlas Zeng 2a8187431d Merge pull request #509 from Awesome-AI-Pedia/fix/438-reference-button-tail
fix(explorer): pin reference affordance to row tail
2026-09-02 23:09:41 +08:00
Atlas Zeng ffbd9d7882 Merge pull request #427 from eg-bole/fix/selection-popup-dismiss-cursor-insert
fix(editor/conversation): selection popup dismissal + caret-anchored draft insert (#425)
2026-09-02 23:09:15 +08:00