Commit Graph
577 Commits
Author SHA1 Message Date
Atlas Zeng f32149d7cb Merge pull request #405 from AnakinCao/feat/dsh-md-export-catalog
Add dsh-md-export to the file-viewer plugin catalog
2026-08-29 16:57:19 +08:00
Menghuan1918 8ab02a9f9d fix(i18n): add pluginMdExportDesc to third-language dictionaries (locale key-set test) 2026-08-29 16:56:01 +08:00
曹志鹏 185d56e202 Add dsh-md-export to the file-viewer plugin catalog 2026-08-29 16:56:01 +08:00
Atlas Zeng f70cb91d23 Merge pull request #413 from meyaomiao/feat/server-deck-catalog
feat(catalog): add dsh-server-deck (server card dashboard + xterm terminal) to the tab plugin catalog
2026-08-29 16:55:40 +08:00
meyaomiao 6a9a458849 feat(catalog): add dsh-server-deck to the tab plugin catalog
- npm-published dual-mount plugin (native Servers tab via
  ctx.betterSidebar.registerTab, standalone drawer fallback)
- card dashboard per host: online state, OS, uptime, CPU/mem/disk
  meters, latency; click-to-open xterm.js interactive terminal
- ~/.ssh/config import with git-hosting alias filtering
- description key pluginServerDeckDesc added to all 21 locale files
  (zh/en in locales.ts + 19 language files)
2026-08-29 16:31:52 +08:00
Atlas Zeng b2ab82cefd Merge pull request #379 from Johnblur/feat/add-bilingual-reader
feat: add dsh-bilingual-reader to tab plugin catalog
2026-08-29 16:29:32 +08:00
Atlas Zeng 2a4493db37 Merge pull request #410 from meyaomiao/feat/github-workbench-catalog
feat(catalog): 收录 dsh-github-workbench(GitHub 工作台)Tab 插件
2026-08-29 16:29:23 +08:00
Atlas Zeng 44ad014293 Merge pull request #451 from g-yixuan/feat/catalog-dsh-sidenote
feat(plugins): add dsh-sidenote to the tab-plugin catalog
2026-08-29 16:29:12 +08:00
Johnblur fd0329f82c feat: add dsh-bilingual-reader to tab plugin catalog
Registers dsh-bilingual-reader in the better-sidebar add-tab-plugin catalog:
native-PDF reading with LLM selection translation, isolated from the main
conversation. Adds the catalog entry plus pluginBilingualReaderDesc across
all 20 locale dictionaries (zh/en source + 18 mirrors).
2026-08-29 16:12:33 +08:00
meyaomiao c96ba20db0 fix(i18n): 补齐 pluginGithubWorkbenchDesc 其余 18 种语言词条
locales.spec.ts 的第三语言键位齐平守护测试要求所有 locales-*.ts 的
key 集合与 zh 完全一致;此前只补了 ja,导致 CI 单测失败。
本提交为 ar/de/fr/hi/id/it/ko/nl/pl/pt/ru/sv/th/tr/vi/zh-HK/zh-MO/zh-TW
补齐该词条,插入位置与其余键一致(GitForge 与 GitRemotes 之间)。
2026-08-29 16:12:29 +08:00
meyaomiao b0683a7c93 feat(catalog): 收录 dsh-github-workbench(GitHub 工作台)Tab 插件
- plugins-tabs.ts 新增条目(github: 形式钉住 v0.1.0,lib 已入库免本地构建)
- locales zh/en/ja 三语 description 同步
- tests/plugin-list.spec.ts 9/9 通过
2026-08-29 16:12:29 +08:00
gyixuan dd8b081665 feat(plugins): add dsh-sidenote to the tab-plugin catalog 2026-08-29 16:12:26 +08:00
North Wang a8f27c4270 fix(client): align collapsed toggle cluster with the DSH session header
- Open state keeps riding the panel's 34px tab strip (top: 3), unchanged.
- Collapsed, the cluster floats over DSH's session header, whose controls
  (title, "Session log" capsule) are vertically centered at 28px on both
  desktop and narrow viewports; top: 3 left the 28px buttons ~11px too
  high — re-center them at 14px.
- body[data-dsh-sidebar-collapsed] only exists while the right panel is
  closed, so the tab-strip alignment is untouched; with position
  compatibility active, compose the caption strip (strip + 14).
2026-08-29 15:49:39 +08:00
GeekRicardo 0504e3918b fix(terminal): 自定义字体解析失败时兜底到等宽,避免掉进比例字体毁掉网格
在设置页「侧边卡片」→ 终端卡片填入自定义「终端字体」后,若该族名在浏览器里
解析不到,终端不是退化成"没有想要的字体",而是整个网格崩掉:字符间距被撑开、
字形变成比例字形、Nerd Font 图标全部消失。

用户实测(macOS + Chrome,填 `Maple Mono NF CN`):

| 「终端字体」的值 | DevTools Rendered Fonts | 结果 |
| --- | --- | --- |
| `Maple Mono NF CN` | **PingFang SC** | cell 12.09px @ 13px 字号(0.93em),网格崩坏 |
| `Maple Mono NF CN, monospace` | **Menlo** | 间距恢复正常 |

`resolveTerminalFont` 对用户值(以及主题 `--ds-font-family-code` token 值)
原样返回,只有内置的 `DEFAULT_TERMINAL_FONT_FAMILY` 末尾带 `monospace`。

字体栈若不以通用族收尾,浏览器解析不到任何具名字体时会落到**标准字体**
(standard font,macOS 中文环境是 PingFang SC)——它是**比例字体**。
xterm 用它的推进宽度量 cell(0.93em,等宽字体应为 0.6em),网格随即失真。

这条路径比想象中容易踩:终端里跑的 shell 在 host 上,字体却由**浏览器所在机器**
提供,两者不同机时(远程/容器化的 DSH)用户按 host 侧装的字体填名字必然解析失败。

新增 `withMonospaceFallback()`,在 `resolveTerminalFont` 出口保证字体栈以通用族收尾。

- **不做字体嗅探**:终端字体设计文档 §2 把"字体合法性嗅探"列为非目标;本改动
  不检测具名字体是否存在,只补一个通用族尾项。
- **已有通用族则原样保留**:包括用户主动写的非等宽通用族(`sans-serif` 等)
  ——那是用户的选择,不该被覆盖。
- **主题 token 值同样兜底**:它掉进比例字体的后果与用户值完全一样。
- **CSS 全局关键字单独处理**:`inherit` / `initial` / `unset` / `revert` /
  `revert-layer` 追加逗号后是非法 CSS,会被 CSSOM 整条丢弃,反而让 xterm
  一个字体都拿不到;这类值直接回落内置栈。
- 字体栈按**顶层逗号**切分并感知引号、转义与括号,避免拆坏 `"Foo, Bar"`
  与 `var(--x, y)`;通用族匹配大小写不敏感且只认未加引号的项。

- `tests/terminal-font.spec.ts` 4 → 13 例(兜底追加、已有通用族免动、大小写与
  引号边界、顶层逗号切分、幂等性、CSS 全局关键字、空值)
- `pnpm typecheck` 零错误
- 全量 `pnpm test`:941 passed(改动前基线 932 passed,+9 为本次新增);
  另有 31 例失败在本分支基线(干净 main)上同样失败,与本改动无关

互补、不重叠。#190 解决"字体解析成功、但栈里没显式列 Nerd Font 导致补充平面
PUA 图标缺字";本 PR 解决"字体压根没解析到、栈又没有通用族尾项导致落进比例
字体"。两者都落在 `terminal-font.ts` 的同一出口,合并时按先后 rebase 即可。
2026-08-29 15:49:35 +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
taurus 30879fff74 fix(state): seed restored free-window ids 2026-08-29 15:31:16 +08:00
taurus 1aba61a008 test(state): cover restored free-window id allocation 2026-08-29 15:31:16 +08:00
懿驹-伯乐 963560a361 fix(conversation): restore the composer caret after the draft insert (#425)
A programmatic setDraft commit resets the controlled textarea's caret
(observed landing at the start of the value), so every later insert probed
the reset position and the stack drifted (A|B + C + D became |DACB).
placeComposerCaretAfterInsert puts the caret right after the inserted text
once the value commit lands - the index accounts for the left-side
separating space - and is guarded by a value-sync check plus a two-frame
retry, so a newer edit or a different composer wins untouched.

spliceInsert now reports the caret index together with the spliced draft
so the placement math cannot drift from the join logic. Nothing else moves:
the pure insertAtCaret surface is unchanged.
2026-08-29 15:31:13 +08:00
懿驹-伯乐 a3379ef17d fix(conversation): insert the selection popup text at the composer caret (#425)
appendToDraft always spliced at the end of the draft. The conversation
service exposes no caret API, so probe the live composer textarea selection
from the DOM (value-sync guarded) and splice at that position with
whitespace-aware joins; an unknown or stale caret falls back to appending.
The explorer @-reference shares this path, so it inserts at the caret too.
2026-08-29 15:31:13 +08:00
懿驹-伯乐 d258f3f8bc fix(editor): dismiss the selection popup when its surface leaves the viewport (#425)
The portaled position:fixed add-to-conversation button stays pinned to its
viewport anchor while the sidebar keeps every tab mounted (tab switches only
set display:none; collapsing the panel translates it off-screen), so the
popup could outlive its editor surface. Extract the popup into
useSelectionPopup and close it on any mousedown outside the button, Escape,
document hidden, window blur, and - the tab-switch/collapse path - when an
IntersectionObserver reports the editor surface off-viewport.
2026-08-29 15:31:13 +08:00
Justin Skowyra cdd5706f5b fix(terminal): contain transient resize failures 2026-08-29 15:31:12 +08:00
Kunping Wang 5d8c33832c fix(git): 兼容旧版 Git 的 worktree 列表 2026-08-29 15:31:08 +08:00
Kunping Wang f1da0fe345 perf(sidebar): 保留兜底定位并限制全树查询频率
- 缓存节点连接期间按 1.5s safety-net 周期完整复核
- html style 变化时立即绕过 fast path 重新定位
- 覆盖流式热路径、定时复核、HMR 信号与节点替换行为
2026-08-29 15:31:05 +08:00
Kunping Wang a6c7cf63c1 perf(sidebar): 避免已定位会话列时重复查询 DOM
- 缓存会话列仍连接时跳过 #root 全树 querySelector
- 保留 detached/HMR 回退定位与 1.5s safety net
- 补充 locator 行为测试覆盖缓存复用与节点替换
2026-08-29 15:31:05 +08:00
Atlas Zeng 2088ef6891 Merge pull request #453 from omdsh-dev/fix/reveal-scroll-panel-shift
fix: scope the "Show in folder" reveal scroll to the tree body (panel shift on 0.1.2-alpha.1)
2026-08-29 11:48:58 +08:00
Menghuan1918 41d303623f style: restyle the reveal highlight as a merged business-blue block
The reveal tint now uses the DSH business-blue surface
(--dsw-alias-state-business-tertiary, the same source the host's nav
items use for their active accent, skin-aware) instead of a neutral
accent wash, and drops the 2px left bar. Consecutive revealed rows
square off their touching edges (sibling combinator + :has), so a
multi-file highlight renders as ONE rounded block — per-row radius
notches no longer fragment it; a solo row keeps the full radius.
2026-08-29 11:45:29 +08:00
Menghuan1918 052edb7685 fix: scope the "Show in folder" reveal scroll to the tree body
A reveal on a deep tree shifted the whole panel (tab bar included) out
of the viewport: the reveal used native scrollIntoView, which scrolls
every scrollable ancestor, and the fixed panel host
([data-dsh-panel-host], overflow hidden) is still a programmatically
scrollable container — the host scrolled instead of (or besides) the
tree, with no way to scroll back. Reported on DSH 0.1.2-alpha.1.

Two layers:
- FileTree: compute the centered, clamped target and scrollTo the tree
  body only; scrollIntoView is never called.
- panel host: overflow hidden -> clip. A hidden box remains
  programmatically scrollable; clip keeps the same viewport-edge
  clipping (collapsed panels sliding off-screen) while making the host
  impossible to scroll, killing the whole leak class (md-toc jumps,
  future consumer scrollIntoView calls).
2026-08-29 01:52:55 +08:00
Menghuan1918 50e05ecea0 docs(agents): slim AGENTS.md to repo-global rules, single-source the API reference
AGENTS.md (546 -> 63 lines) now carries only project-global development
rules: hard constraints, mount-smoke CI, DSH 0.1.2-alpha.1 adaptation
notes, npm release, a quick-rule list, and a docs/tests map. The
consumer plugin API reference lives solely in
docs/external-plugin-guide.md.

Reference sync:
- README/README_EN: collapse the dual 'full integration docs' links to
  the guide; AGENTS.md stays linked as the repo rules
- shell-presets.ts / mount.e2e.ts comments: skinning contract pointer
  moved from AGENTS.md §8 to guide §12
2026-08-28 21:42:39 +08:00
Menghuan1918 9bfe5ccb66 docs(guide): promote external-plugin-guide to the single canonical integration doc
Absorb the consumer-facing API reference previously duplicated in
AGENTS.md and refresh stale content to v0.17.1:

- TabDescriptor: full SettingRow shape (select/multi, v0.13.0+),
  urlTarget, richer dedupeKey/available/createTab semantics
- built-in tab table: replace stale explorer-based list with current
  editor/sidechat/pinned-terminal reality
- service: features list completed (urlTarget/settingSelect/
  floatWindows), OpenTabSeed meta null/undefined semantics, openFile
  path-derived-id caveat
- settings: full built-in prefs key list, select rows
- new sections: float windows (§11) and the token-driven skinning
  contract (§12, moved from AGENTS.md §8)
- traps: aionui right-panel mutex, i18n/better-locale, lazy chunks
- minimal example: drop removed @deepseek-ai/dsh-client-runtime peer
- references: 9-viewer typo fixed, plugins catalog / fs-search /
  markdown-html / agent-opens entries added
- fix duplicated §11 heading; sections now run §1-§15
2026-08-28 21:42:26 +08:00
Menghuan1918 3b1898f9cb docs: refresh issue-template DSH version examples to 0.1.1-rc.2 v0.17.1 2026-08-28 16:04:36 +08:00
Atlas Zeng c894163b17 Merge pull request #435 from omdsh-dev/feat/dsh-0.1.2-alpha.1-compat
feat: adapt to DSH 0.1.2-alpha.1 (Remote gateway + token auth + MarkdownText labels)
2026-08-28 16:04:27 +08:00
懿驹-伯乐 60a2f9026e docs: add dsh-better-sidebar-icons to the enhancements catalog
Adds eg-bole/dsh-better-sidebar-icons (VSCode-style file/folder icon
theme, DOM overlay, npm-published 0.1.2) to the '增强与工具' plugin
list in the README.
2026-08-28 14:59:15 +08:00
Menghuan1918 6cfe7a736f feat: adapt to DSH 0.1.2-alpha.1 (Remote gateway + token auth + MarkdownText labels)
Dual-version compatibility: the plugin now works identically on
0.1.0-rc.8 ~ 0.1.1-rc.2 and 0.1.2-alpha.1 (the latter verified against a
source build of the dsh-v0.1.2-alpha.1 tag: mount smoke 14/14 + aggregate
double-mount; 0.1.1-rc.2 re-verified 14/14).

- MarkdownText: alpha.1 renamed the flat optional `codeLabels` prop to a
  required nested `labels: { code: {copyLabel, copiedLabel}, footnotes }`;
  the old prop alone crashes fence rendering with "reading 'code'". All
  four render sites (mermaid / MarkdownHtml / TextEditor / SideChatView)
  now go through src/client/markdown-labels.tsx (dual-shape object passed
  under both prop names).
- e2e mount lanes speak both host dialects (tests/e2e/host-protocol.ts +
  host.ts, unit-locked by tests/e2e-host-protocol.spec.ts):
  - one-time-token browser auth: the launch URL grep extends past the
    `/?token=` query (truncating at `/` loses the token), goto navigates
    the token URL (browser cookie exchange), and stamped navigations
    pre-seed the context cookie via addCookies — the exchange 303 drops
    sibling query params;
  - Remote gateway: dot endpoints first, slash endpoints on 404 fallback,
    with args keyed by the controller parameter name (workspace/create ->
    {request}, session/list -> {_request}; verified on a live host).
- package.json: drop the @deepseek-ai/dsh-client-runtime peer (package
  removed upstream in alpha.1; zero references in the source — the chunk
  externals entry stays and is resolution-tolerant).
- scripts/e2e-mount.sh: fix a latent set -u crash in the npx fallback
  branch ($DSH_CMD followed by a full-width comma parsed as part of the
  variable name).
- CI pin stays at @deepseek-ai/dsh@0.1.1-rc.2 until alpha.1 publishes to
  npm (comment updated); README/AGENTS document the new contracts.
- Version lockstep bumped to 0.17.1.
2026-08-28 04:20:36 +08:00
多玩幻灵qwq 0314fd9b93 Merge pull request #419 from HuanLinOTO/feat/terminal-ctrl-click-url
feat(terminal): open URLs with Ctrl+Click (Cmd+Click on mac)
2026-08-27 21:44:32 +08:00
多玩幻灵qwq d71261ace3 fix(cwd): resolve detached session cwd via persistence index, not host process cwd (#372)
* fix(cwd): resolve detached session cwd via persistence index, not host process cwd

sessionCwdOf fell back to process.cwd() when a session was not yet
attached and the client did not send a cwd — on Windows the host
process cwd is the DSH source root (dsh.cmd's pushd %DSH_ROOT%),
so every user-project path failed the workspace containment guard
with 'path "..." is outside workspace'. The bug surfaced when
clicking chat file links at first paint (session still hydrating)
or any flow that hit a sidebar route before the client's
fetchedCwd effect resolved.

Fix: thread ctx.get('sessionPersistence') as the fallback between
the client cwd and the host process cwd. The persistence index
holds every persisted session's header (including cwd), so a cold
(detached) session resolves its real project cwd instead of the
DSH source root. The host process cwd remains the final fallback
for deployments without persistence (tests / stripped-down hosts);
production always provides persistence.

sessionCwdOf is now async; all 8 call sites await it. The two tool
resolvers (registerTools / registerOpenTool) take an async
resolveCwd callback; the one execute path that used it
(terminal_create) is now async.

Tests: two new regression cases in smoke.spec.ts cover the cold
session via persistence path and the persistence-without-cwd
fallback. Existing stubs updated to the async resolveCwd signature.

* Address Copilot review: fix JSDoc accuracy and validate persistence cwd

- tools.ts / agent-opens.ts: correct the resolveCwd JSDoc — the resolver
  does fall back to the host process cwd as a final tier (tests /
  stripped-down hosts); the previous "never falls back" wording
  contradicted the actual sessionCwdOf contract.
- index.ts: route the persistence meta.cwd through requireAbsolute,
  matching the clientCwd path. A buggy/corrupt persistence layer that
  stored a relative cwd would otherwise flow into the workspace guard,
  be resolved against the host process cwd, and potentially recreate
  the original "outside workspace" misclassification in a harder-to-
  debug way.
- smoke.spec.ts: use resolvePath() for the cold-cwd fixture so it is
  absolute on both POSIX and Windows; add a regression test for the
  relative-cwd rejection path.
2026-08-27 21:44:04 +08:00
Atlas Zeng 20da6479f1 Merge pull request #414 from omdsh-dev/feat/pinned-terminal
feat: 固定终端 (Pinned Terminals, v0.17.0)
2026-08-26 23:39:35 +08:00
HuanLinOTO df90e9d132 fix: bump SIDEBAR_SERVICE_VERSION to 0.17.0
Missed this constant in the v0.17.0 bump — service.spec.ts asserts it
stays in lockstep with package.json. CI caught it.
2026-08-26 22:49:07 +08:00
HuanLinOTO 02125deee8 fix: address Copilot PR review (4 comments)
1. getSessionStates() returns defensive copy (new Map) — callers can't
   mutate the live bySession map at runtime
2. closePinnedInHome passes home cwd to ptyClose — the host resolves
   the PTY in the correct workspace container (same scope the WS open
   used); previously only sessionId was passed
3. setTabPin walk uses findIndex + conditional slice instead of map() —
   only the leaf/path containing the target tab gets a new array;
   idempotent no-ops return the same tab reference (=== holds)
4. setTabPin guards non-terminal tabs (tab.type !== 'terminal' early
   return in apply); sanitizePersistedTab strips pin from non-terminal
   tabs on hydration — keeps the terminal-only invariant defensive
2026-08-26 22:46:56 +08:00
HuanLinOTO 31c675a00e feat: show pin icon on home session's pinned tabs too
TabBar's pin icon now shows for both virtual pinned tabs (injected
from other sessions) AND the home session's own pinned terminal tabs
(those with tab.pin !== undefined). Previously only virtual tabs got
the icon — the home tab looked like a regular terminal despite being
pinned.
2026-08-26 22:43:20 +08:00
HuanLinOTO e7c4578791 feat: inline pinned tabs in TabBar (replaces PinnedRail)
M4 rewrite per user feedback: pinned terminals should display in the
Tabs bar itself, not a separate rail. Clicking activates in-place
(cross-session terminal rendering), no session jump.

Changes:
- Delete PinnedRail.tsx + tests/pinned-rail.spec.tsx
- pinned.ts: add createPinnedVirtualTab (virtual id = pinned:<sess>:<id>,
  meta.__pinnedHome = { sessionId, cwd, tabId }), injectPinnedIntoTree
  (appends virtual tabs to first leaf, overrides active), isPinnedVirtualId,
  parsePinnedVirtualId, isPinnedVirtualTab, getPinnedHomeScope
- tab-content-memo.ts: add effectiveTabId to memo key — TabContent passes
  the original tab id (not the virtual id) to the descriptor component so
  TerminalView's WS URL resolves to the home PTY
- Sidebar.tsx: augmented tree (injectPinnedIntoTree), activePinnedTabId
  state, wrappedActions (intercept virtual ids for activate/close/unpin/
  drag/float), renderTab passes home scope + effectiveTabId for pinned tabs,
  pinnedRevision replaces railRevision
- TabBar.tsx: pinned virtual tabs non-draggable, pin icon prefix, stripped
  context menu (Unpin/Close only, no float/close-others/left/right)
- sidebar.module.css: remove rail styles, add .pinnedTab (italic + tertiary)
- Docs: README/README_EN/AGENTS.md/design doc updated for inline approach

157/157 client-side tests pass; typecheck + build clean
2026-08-26 21:40:32 +08:00
HuanLinOTO acf639471d feat: pinned terminal i18n + docs + v0.17.0 bump
M5 of pinned terminals (v0.17.0):
- README/README_EN: feature list adds pinned terminals
- AGENTS.md §3.4: terminal row documents pin capability
- Design doc: implementation deviations (M3 disconnect signal convergence,
  M4 railRevision state for reduceFor non-notify)
- package.json + dsh.plugin.json: 0.16.1 → 0.17.0
- i18n: 12 new keys synced across all 19 language dictionaries (done in M2)
- All client-side tests green (149/149); typecheck + build pass
2026-08-26 18:44:46 +08:00
HuanLinOTO 531bb53129 feat: pinned rail component + cross-session focus
M4 of pinned terminals (v0.17.0):
- New PinnedRail component: compact strip above the tab bar, renders
  pinned terminals from other sessions (pin icon + terminal icon + title
  + tooltip with kind/scope/cwd)
- Click navigates to the home session (ctx.sessions.open) and activates
  the tab (or raises its float) via the pinnedJumpRef effect
- Right-click menu: Unpin / Close Terminal; middle-click close
- IconPinOutline16 added to icons.tsx
- Rail styles in sidebar.module.css (token-driven, no-drag opt-out)
- Sidebar: collectPinnedTabs on store snapshot + session list, railRevision
  state forces re-render after reduceFor (which doesn't notify)
- Cross-session close: reduceFor(closeTab/closeFloatByTab) + ptyClose/
  agentPtyClose; cross-session unpin: reduceFor(setTabPin null)
2026-08-26 18:42:34 +08:00
HuanLinOTO c6a3e24599 feat: reconcile exempts pinned agent terminals
M3 of pinned terminals (v0.17.0):
- reconcileAgentTerminals skips removal of agent tabs with pin !== undefined
  (the agent closed them / pty exited, but the user pinned them)
- The xterm reconnect-failure banner is the user-visible 'disconnected'
  signal (design M3 convergence: no title suffix, no meta write)
- A later push reviving the same uuid is a no-op (id already exists)
- Unpinned agent tabs still remove as before; toAdd path unaffected
2026-08-26 18:33:52 +08:00
HuanLinOTO 5d019d78ac feat: tab context menu pin submenu
M2 of pinned terminals (v0.17.0):
- TabBar: onPinTab prop (optional, hidden when unset for legacy callers)
- Terminal tabs get a 'Pin ▸' submenu (workspace/global) or 'Unpin' row
- UI vs Agent label distinction via isAgentTabId
- Non-terminal tabs get no pin entry (5-item legacy menu preserved)
- WorkbenchActions.pinTab threaded through split-pane LeafView
- Sidebar.onPinTab wires store.reduce(setTabPin) with cwd snapshot
- 12 new i18n keys synced across all 19 language dictionaries
2026-08-26 18:33:02 +08:00
HuanLinOTO 68f771e65f feat: pin data model + cross-session resolver
M1 of pinned terminals (v0.17.0):
- SidebarTab.pin?: { scope: 'workspace' | 'global'; homeCwd?: string }
- setTabPin reducer (walks splits + bottomSplits + floats, idempotent)
- sanitizePersistedTab whitelist-validates pin (illegal scope drops pin, keeps tab)
- SidebarStore.getSessionStates() read-only view for the rail
- new src/client/pinned.ts: pinnedVisibleTo + collectPinnedTabs (excludes viewer's own session, stable tree order)
2026-08-26 18:27:32 +08:00
HuanLinOTO d052bd7fb2 docs: pinned terminal design + implementation plan 2026-08-26 18:21:43 +08:00
Menghuan1918 f9153dfc1c chore(release): 0.16.1
- bump package.json / dsh.plugin.json / SIDEBAR_SERVICE_VERSION to 0.16.1
- README / README_EN: add v0.16.1 changelog (git panel freeze + restart
  loop #369: status truncation, bounded repo discovery, ?dsh-sidebar-reset
  escape hatch) and update the npm-channel install pin to ^0.16.1
v0.16.1
2026-08-25 11:20:42 +08:00
Atlas Zeng bba7bb6073 Merge pull request #376 from omdsh-dev/fix/369-git-panel-freeze
fix(git): bound status payload and repo discovery; add reset escape hatch
2026-08-25 11:19:06 +08:00
Menghuan1918 eed40bb11a fix(git): raise the discovery probe timeout to 5s (#369)
3s was tight for cold caches on large home directories; 5s still bounds a
stalled mount well below the 30s command budget while cutting false
negatives on slow-but-healthy checkouts.
2026-08-25 11:15:16 +08:00
Menghuan1918 2cbcba8d11 fix(git): bound status payload and repo discovery; add reset escape hatch (#369)
Opening the source-control panel could freeze the whole sidebar, and the
frozen layout restored itself after every reload, leaving no way out
(issue #369). Three unbounded layers compounded:

- `git status --porcelain -z --untracked-files=all` shipped every row of a
  pathological untracked set into one response; the browser main thread
  froze on response.json() plus the unvirtualized change list, and the 2s
  poll replayed the freeze. status() now caps entries at 2000 and reports
  `truncated` (the panel shows a notice, mirroring fs.read's truncation
  semantics); worktree change counts inherit the bound for free.
- repoRoots() probed every visible child directory of a non-repo cwd
  (e.g. ~) serially, once per git.* call, each with the full 30s command
  budget. Discovery now uses a 3s probe timeout, caps probes at 200
  directories, and shares one in-flight scan plus a 60s TTL cache per cwd.
- A hung layout persisted `panelOpen: true` with the git tab active, so a
  reload remounted straight back into the freeze with no escape hatch.
  Loading with `?dsh-sidebar-reset` now drops the persisted layout (and
  the shared width) and starts from the default.

statusTruncated copy synced across all 19 shipped dictionaries.
2026-08-25 11:04:43 +08:00