Commit Graph
67 Commits
Author SHA1 Message Date
多玩幻灵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
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
HuanLinOTO 699ce448a1 fix: 处理 Copilot 二轮 review 的 10 条评论
- BrowserView: 即使 host 在白名单,GUI 自身 origin 永不给 allow-same-origin(裸 host 条目覆盖所有端口时可提权)
- intercept: revealInExplorer 相对路径经 resolveSidebarPath 解析后再 revealPaths(交付物路径是相对的)
- intercept: ctx.betterSidebar 改为 ctx.get(#357 纤维链读取约定)
- FileTree/EditorHost/TreePanel/tabs: 把 revealed 贯通到树行渲染 + 高亮类 + scrollIntoView(此前高亮永不渲染)
- state.revealPaths: 重建祖先目录时保留前导分隔符(POSIX/UNC 此前目录永远折叠)
- state.schedulePersist: 仅活动会话写入全局宽度(targeted open 不再用非活动会话的过期宽度覆盖拖拽值)
- browser.probe: GET 回退显式取消 body(否则流式/大响应挂住 socket)
- terminal-font: leading generic 前只放 symbols-only 字体;fully-patched 放 generic 后(避免 hijack 度量字体)
- sidebar.module.css: 恢复拖拽反馈(#130 的 silent resize 与 PR 描述冲突,取 HEAD)
- Sidebar: 剩余 4 处 ctx.betterSidebar 改 ctx.get
- tests: 补 revealPaths/iframeSandboxFor/terminal-font 回归测试 + EditorHost/FileTree 测试补 revealed prop
2026-08-24 18:40:04 +08:00
HuanLinOTO 9048dbe42d fix: revealInExplorer 折叠面板时自动展开(Copilot review)
type-only openTab 按契约永不展开面板,折叠侧栏下 show-in-folder 揭示高亮不可见。
遵循 subagent 自动打开的既有模式:面板折叠则展开,activePane 钉到右侧首叶,
再 openTab 聚焦文件窗口。
2026-08-24 18:04: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
HuanLinOTO f4e71c1257 fix(tests): prefs.spec 补齐 browserAllowedLoopback,与 main 的 agentOpenTools 合并共存 2026-08-24 16:46:02 +08:00
HuanLinOTO 15e90d936e merge: 合入最新 main(#353 agent-open-tools / #360 markdown-html-toc 等),解决 PR 合并性
# Conflicts:
#	src/client/EditorHost.tsx
#	tests/prefs.spec.ts
2026-08-24 16:41:02 +08:00
HuanLinOTO 2357fc08a5 fix: typecheck after PR train merge
- Add revealed prop to TabContentMemoKey + comparator (PR #94 threads
  it through TabContent but the memo key lacked it).
- Destructure revealed in TabContent body.
- openpath-intercept test: provide no-op revealInExplorer default.
- prefs test: add browserAllowedLoopback to setPrefs calls (PR #69).
- smoke test: drop duplicate encodeHtmlUrl import (PR #193 dup).
- index.ts: null-guard ptyManager in session/disposed (PR #130 revert).
- locales-fr/nl: avoid unescaped apostrophes in loopback desc (PR #69).
2026-08-24 01:39:53 +08:00
HuanLinOTO 504579e144 merge #94: show-in-folder reveals in explorer (GammaChineYov)
Adapted to HEAD's unified editor+explorer tab: the reveal gesture now
opens the editor home tab (the files window) instead of the deleted
explorer type. PR's revealInExplorer implementation, produced-files
engine-data selector, and tests are retained verbatim.
2026-08-24 01:36:29 +08:00
HuanLinOTO df489ccb45 merge #357: read betterSidebar via ctx.get() in all consumers (LuckVd) 2026-08-24 01:35:00 +08:00
HuanLinOTO b5eb71a136 merge #228: markdown manual preview refresh (MuziIsabel)
# Conflicts:
#	src/client/EditorHost.tsx
#	src/client/TextEditor.tsx
2026-08-24 01:34:55 +08:00
HuanLinOTO 867de61aca merge #215: editor preview refresh trio (Max-Null) 2026-08-24 01:34:24 +08:00
HuanLinOTO 1eb8e760f9 merge #202: editor Vue + 28 legacy language syntax highlight (ShelbyTang) 2026-08-24 01:34:19 +08:00
HuanLinOTO 64338f4244 merge #193: html preview UTF-8 encoding declaration (Qiuner) 2026-08-24 01:34:15 +08:00
HuanLinOTO abe9c4fc46 merge #190: terminal Nerd Font icon fallback (ShelbyTang) 2026-08-24 01:34:10 +08:00
HuanLinOTO 53f3a2c246 merge #182: trust-fence Origin hostname compare for Edge 151 (MXY) 2026-08-24 01:34:06 +08:00
HuanLinOTO e135332433 merge #180: fix panel drag layout flash (ChenRuoT)
# Conflicts:
#	src/client/Sidebar.tsx
2026-08-24 01:34:02 +08:00
HuanLinOTO 4feb5ef8de merge #130: drag-release bounce, session-delete terminal cleanup, silent resize (chalei1122-cell)
# Conflicts:
#	src/client/Sidebar.tsx
2026-08-24 01:33:44 +08:00
HuanLinOTO 8e04ee6f74 merge #69: probe embed signals via GET fallback when HEAD omits them (trinhthiminh650-tech)
# Conflicts:
#	src/client/builtins/tabs.tsx
#	src/client/locales.ts
#	src/client/prefs.ts
#	src/config.ts
#	src/index.ts
#	src/prefs-shared.ts
2026-08-24 01:31:54 +08:00
HuanLinOTO ddd3bed7cf merge #47: fix PowerShell installer BOM parse and pnpm version check (Cora093)
# Conflicts:
#	README.md
#	README_EN.md
#	scripts/install.ps1
2026-08-24 01:27:09 +08:00
HuanLinOTO 9340dc9702 merge #36: share side-card width across sessions (fsvov)
# Conflicts:
#	src/client/state.ts
#	tests/unit.spec.ts
2026-08-24 01:25:08 +08:00
HuanLinOTO 751fb954a2 fix: address Copilot review (9 comments)
#1 GitView: hide/disable 'open in editor' for external linked worktrees
   (path outside session workspace is rejected by the fence); add
   isWithinWorkspace client helper for the gate.
#2 DiffTab: untracked diff read resolves against diff.repoRoot (child-repo
   paths are repo-root-relative, not cwd-relative); fs.read route threads
   selectedRepoOf into resolveGitPath so the host resolves the right base.
#3 GitView: chooseRepo handler mirrors chooseWorktree — clears status/
   history/log and bumps generation before async refresh, preventing stale
   rows from acting on the newly selected repository.
#4 git.ts: repoRoots/repoRoot compare roots via pathIdentity() (platform-
   aware) instead of raw includes(), fixing win32 forward/backslash
   mismatch that silently fell back to roots[0].
#5 markdown-images: mask fenced code blocks and inline code spans before
   rewriting; only rewrite reference definitions whose label is actually
   referenced by image syntax (not plain links). +3 tests.
#6 package.json: downgrade unrun ^0.3.1 → ^0.2.39 (0.3.x requires Node
   ^22.13, incompatible with declared engines >=20).
#7 market-manifest.spec: shell:true on win32 so execFileSync resolves the
   pnpm .cmd shim.
#8 smoke.spec: build html-route URL via encodeHtmlUrl() (production
   encoder) so the Windows drive separator is correct.
#9 index.ts git.worktrees route: thread selectedRepoOf so a workspace
   container's selected child repo's linked worktrees appear.
2026-08-23 22:02:47 +08:00
HuanLinOTO 9410e5bb2a fix(i18n): add worktree translation to all 19 third-language dicts (#285)
PR #285 added the zh/en 'worktree' key but the 19 third-language dicts
(de/fr/pt/ko/ar/hi/id/tr/vi/th/ru/it/nl/sv/pl/zhHK/zhTW/zhMO) were missing
it — only ja was patched in the prior commit. The dicts are typed
Record<keyof typeof zh, string> via an unchecked 'as' cast, so tsc did
not catch the gap.

This commit:
- adds the worktree translation to all 19 dicts
- strengthens tests/locales.spec.ts to assert key-set equality for EVERY
  shipped dictionary (not just zh/en/ja), preventing future regressions
2026-08-23 20:58:33 +08:00
HuanLinOTO 0a21629fa3 test(market-manifest): raise pack timeout to 180s (#338)
pnpm pack triggers prepublishOnly: pnpm build (~60s tsdown rebuild);
the default 5s vitest timeout expired on CI.
2026-08-23 20:52:14 +08:00
HuanLinOTO e07d4ba5dd fix(i18n): add ja translation for worktree key (#285) 2026-08-23 20:39:46 +08:00
HuanLinOTO 72796ee46a chore: sync pnpm-lock.yaml for unrun devDep (#336) 2026-08-23 20:29:35 +08:00
HuanLinOTO 5951bae1ad integrate train/misc: mermaid + layout-push + fs-search + markdown-images 2026-08-23 20:28:49 +08:00
HuanLinOTO b4118a4c77 integrate train/git: worktree RC8 forward-port + multi-repo selector
# Conflicts:
#	src/index.ts
2026-08-23 20:28:17 +08:00
HuanLinOTO af8ec58f4c integrate train/css: panel z-index + overflow clip + mobile sidebar 2026-08-23 20:27:37 +08:00
HuanLinOTO a860462254 integrate train/base: cordis peer migration + unrun devDep + path security 2026-08-23 20:27:32 +08:00
HuanLinOTO d63de6f815 merge #326: support selecting child git repositories
# Conflicts:
#	src/client/GitView.tsx
#	src/client/state.ts
#	src/index.ts
2026-08-23 20:25:52 +08:00
HuanLinOTO 37a84815d1 merge #254: mobile explain unavailable sidebar and prevent overflow
# Conflicts:
#	README.md
#	README_EN.md
#	dsh.plugin.json
#	package.json
#	src/client/service.ts
2026-08-23 20:22:49 +08:00
HuanLinOTO 8f4d58dcc7 merge #292: markdown preview local/relative images
# Conflicts:
#	README.md
#	README_EN.md
2026-08-23 20:21:50 +08:00
HuanLinOTO 90584535d4 merge #345: 限制文件 API 访问会话工作区 2026-08-23 20:19:41 +08:00
HuanLinOTO e9a488024c merge #336: add unrun devDependency for tsdown config loading 2026-08-23 20:19:20 +08:00
HuanLinOTO ed3083d133 merge #338: 移除公开版 cordis peer,迁移类型基底到 @deepseek-ai/cordis
# Conflicts:
#	README.md
#	README_EN.md
#	package.json
2026-08-23 20:18:55 +08:00
HuanLinOTO 3445331494 merge #278: panel host clip viewport, hidden panel no longer causes bidirectional overflow 2026-08-23 20:17:58 +08:00
HuanLinOTO 60b9c867d8 merge #342: fs-search skip node_modules and other noise dirs 2026-08-23 20:17:22 +08:00
HuanLinOTO c88eccfadb merge #330: drop panel host below DSH cordis dynamic-plugin layer 2026-08-23 20:17:16 +08:00
HuanLinOTOandxxxg0001 5c88279d7a merge #285: forward-port linked worktrees to RC8
Co-authored-by: xxxg0001 <xxxg0001@users.noreply.github.com>
2026-08-23 20:17:14 +08:00
HuanLinOTO 23a72a5f64 merge #310: keep conversation composer in viewport under layout push 2026-08-23 20:16:59 +08:00
HuanLinOTO ba83dd33f7 merge #341: fix(mermaid): suppress global error rendering 2026-08-23 20:16:33 +08:00
多玩幻灵qwq d6d8db0416 Merge pull request #339 from HuanLinOTO/feat/better-locale-ja
feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 19 语言覆盖(ja/de/fr/...)
2026-08-23 19:52:15 +08:00
HuanLinOTO 4af8d3121a fix: devDependency 改用 npm registry 版本,去掉本机 link 路径 2026-08-23 19:42:24 +08:00
HuanLinOTO f58e361cbe fix(i18n): guard ctx.get call so absent better-locale is truly no-op
Sidebar.tsx read ctx.get('betterLocale') unconditionally. In real
cordis hosts ctx.get exists and returns undefined when better-locale
is absent — but the unit-test mocks build a minimal ctx without a
get method, throwing 'TypeError: ctx.get is not a function' inside
<Sidebar> render and taking down 7 tests
(bottom-auto-terminal + sidebar-crash suites).

Add a typeof guard so the optional contract holds even when ctx.get
is missing. The activation-order-safe ctx.effect path in index.tsx
is unaffected (real cordis Context, runs once).
2026-08-23 16:56:07 +08:00
HuanLinOTO 7f8f95a59b feat(i18n): 覆盖支持扩展到 19 语言(de/fr/pt/ko/ar/hi/id/tr/vi/th/ru/it/nl/sv/pl/zh-HK/zh-TW/zh-MO) 2026-08-23 14:57:48 +08:00
HuanLinOTO 16a809594c fix(i18n): better-locale 接入改为 activation-order-safe
ctx.get('betterLocale') 是非响应式读取(cordis 只对声明的 inject
依赖重新求值)。如果 better-locale 在 better-sidebar 之后激活,
apply 顶层的 ctx.get 返回 undefined,attachBetterLocale(undefined)
使整个覆盖接入变成 no-op,即使用户后来在设置页选了日语,
better-sidebar 的 t() 也不查询 override store。

改为在 ctx.effect 中订阅 ctx.locale.subscribe:better-locale 在
激活时(若有持久化的 override)和用户切换 override 时都会 bump
locale revision,subscribe 回调触发 sync(),重新读取 ctx.get
('betterLocale')。store 可用后 attachBetterLocale(store) + register
ja dict;store 不可用时 detach。

这解决了插件激活顺序不确定的问题:无论 better-locale 先于还是后于
better-sidebar 激活,override 接入都能正确建立。
2026-08-23 02:31:30 +08:00
HuanLinOTO 38dc807d15 refactor(i18n): 覆盖只在 DSH active=en 时生效(借用英文槽位)
better-locale 的覆盖本质上是借用 DSH 的英文槽位来渲染第三语言:
- store.getOverride(dshActive, ns, key) 改为只在 dshActive === 'en'
  时返回覆盖文本;DSH 在 zh 下覆盖完全惰性(保持原生 zh)
- 新增 store.isOverrideActive(dshActive):返回 active !== undefined
  && dshActive === 'en',供 better-sidebar 的 isZh() 判断
- isZh() 改用 isOverrideActive(dshActive) 判断(仅在 DSH=en 且有
  override 时返回 false),zh 下返回 true
- LanguageSwitcher 接收 dshActive prop,选了非 native 且 DSH 非 en
  时显示「请将 DSH 切换到英文以查看 [语言名]」提示
- 新增 switchToEnHint copy key(zh + en)
- 测试:better-locale +3 store tests(en-only / inert on zh /
  isOverrideActive),+1 patch test(inert on zh);better-sidebar
  +1 locales test(override inert while DSH on zh),FakeBetterLocale
  加 isOverrideActive 方法
2026-08-23 01:42:01 +08:00
HuanLinOTO 7ceee777b4 feat(i18n): 接入 @huanlin/dsh-plugin-better-locale 支持 ja 覆盖
新增 ja 词典(src/client/locales-ja.ts,326 keys,与 zh key-set-equal),
通过可选 peer @huanlin/dsh-plugin-better-locale 接入第三语言覆盖:

- locales.ts: 新增 attachBetterLocale(store) + ja dict export;t() 先查
  store.getOverride(active, 'betterSidebar', key),命中则返回覆盖文本,
  否则走原 zh/en 链;isZh() 在 override 激活时返回 false
- client/index.tsx: apply 中 ctx.get('betterLocale') 取得 override store
  (optional,未安装时为 undefined),attachBetterLocale 注入 t();同时
  register('betterSidebar', { ja }) 把 ja 词典注册进 better-locale 的
  store,让外部 ctx.locale.lookup 调用者也能拿到 ja 文本
- Sidebar.tsx: 新增 betterLocaleActive uSES 订阅 store.subscribe,覆盖
  切换时全树重渲染(DSH locale 的 active 字段不变,既有 localeRevision
  uSES 不会触发)
- package.json: @huanlin/dsh-plugin-better-locale 加为 optional peer
  + devDep link
- tests/locales.spec.ts: 新增 7 个 better-locale override 测试(命中/
  fallback/detach/interpolate/ja dict 完整性 smoke check)

未安装 better-locale 时 ctx.get 返回 undefined,整段为 no-op,zh/en
行为完全不变。
2026-08-23 00:52:20 +08:00
多玩幻灵qwq 29b8615bff fix(terminal): keep pty alive across conversation switches via park frame (#323)
Switching from session A to B unmounted A's TerminalView, whose socket
close the host treated as a transient drop and started the 30s reconnect-
grace countdown — leaving the user in B for >30s killed A's shell, so
switching back respawned a fresh one instead of reattaching.

Add a third control frame {type:'park'} distinct from refresh/crash
drops: the client sends it on unmount when the tab is still open in its
session's state but the active session has moved on; the host marks the
pty as parked and skips the grace countdown. Switching back (open()
cancels the parked state) or explicitly closing the tab (close frame's
scheduleClose clears it) resume the normal lifecycle. Agent terminals
skip park — their lifetime is already indefinite on a bare drop.
2026-08-22 17:21:27 +08:00
HuanLinOTO 7687204b43 docs: 服务化接入文档(AGENTS.md)+ README 标注
- AGENTS.md: 完整接入文档(消费插件开发者面向)
  - TabDescriptor / FileViewerDescriptor 全字段
  - fetchStrategy 对照 + matchFileViewer 匹配算法
  - HMR 生命周期 + 平台约束陷阱
  - 完整最小示例(package.json + client half + profile 注册)
- README.md: 加「服务化(v0.4.0+)」章节,链接到 AGENTS.md
2026-08-11 01:44:00 +08:00
HuanLinOTO e40039ceec refactor: 完全解耦内置 tab — dedupe 归 service,图标归 descriptor
- state.openTab 简化为 '添加到 pane + id dedupe',移除 isSingle/editor-path/diff-id 三分支
  (dedupe 策略归 service.openTab 通过 descriptor.dedupeKey 统一)
- intercept.tsx openSidebarFile 改走 service.openTab(editor descriptor 的 dedupeKey 接管 per-path 去重)
- Sidebar.tsx 两处 subagent 触发(auto-activation + jump-back)改走 service.openTab
- service.openTab seed 加可选 id 字段(editor 需要 path-derived id)
- TabBar.tsx 加 getTabIcon 回调,tab 标签图标查 descriptor.icon 替代 tabTypeIcon
- split-pane.tsx 移除 tabTypeIcon 兜底(buildNewTabOptions 已从 descriptor 填 icon)
- Workbench/NodeView/LeafView 透传 getTabIcon
- tests/unit.spec.ts 更新 openTab 测试(id dedupe 替代 type dedupe)
2026-08-11 01:39:20 +08:00
HuanLinOTO 291158a3ba feat: 服务化改造 — ctx.betterSidebar 注册表(registerTab/registerFileViewer),内置 6 tab + 6 viewer 迁移到 registry,v0.4.0
- 新增 src/client/service.ts: BetterSidebarService 接口 + createBetterSidebarService 工厂
  - registerTab/registerFileViewer 返回 disposer(HMR-safe,挂到 cordis fiber)
  - matchFileViewer 三段匹配: priority desc → detect(内容嗅探) → exts
  - openTab 走 descriptor.dedupeKey 统一去重(替代 isSingle/editor-path/diff-id 三分支)
  - createTab 回调支持自定义 tab 创建(terminal 的 nextTerminal 递增)
- 新增 src/client/builtins.tsx: 6 内置 tab + 6 内置 viewer 自我注册(吃狗粮)
  - tab: editor(hidden) / explorer / git / subagent / terminal / diff(hidden)
  - viewer: image / pdf / docx / xlsx / pptx / binary-download
  - code/markdown 保持 EditorView 内联兜底(matchFileViewer 返回 undefined 时走 fsRead+CodeMirror)
- 新增 src/client/OrphanedTab.tsx: 未注册 type 的占位组件(插件未加载时降级)
- 改造 src/client/index.tsx: ctx.provide('betterSidebar') + registerBuiltins
- 改造 src/client/Sidebar.tsx: TabContent/buildNewTabOptions/onNewTab 查表替代 switch
- 改造 src/client/EditorView.tsx: 预览路由查 matchFileViewer 替代 if/else 链
- 改造 src/client/state.ts: TabType 联合字面量→string,sanitizeNode 接受所有 string type
- 改造 src/context-types.ts: declare module 'cordis' 加 betterSidebar 字段
- 改造 src/index.ts: re-export Context + 服务类型(consumer import type 触发 augmentation)
- package.json: 加 ./client/service exports 子路径,version 0.4.0
- tests/service.spec.ts: 12 个新测试覆盖 register/dispose/matchFileViewer/dedupe/createTab
- tests/unit.spec.ts: sanitizeState 测试更新(未知 type 不再被拒绝,渲染 OrphanedTab)
2026-08-11 00:31:23 +08:00
HuanLinOTO fbebcf5138 docs: 服务化改造设计(registerTab/registerFileViewer + 内置迁移到 registry) 2026-08-11 00:09:46 +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
HuanLinOTO eb5059574e fix: route images before binary detection 2026-08-10 16:54:13 +08:00
HuanLinOTO a6befc8b6b feat: add Office, PDF, and PPTX previews 2026-08-10 16:46:01 +08:00