Skip to content

fix(sidebar): drag-release bounce, session-delete terminal cleanup, silent resize - #130

Merged
Menghuan1918 merged 1 commit into
omdsh-dev:mainfrom
chalei1122-cell:fix/drag-bounce-and-session-cleanup
Aug 24, 2026
Merged

fix(sidebar): drag-release bounce, session-delete terminal cleanup, silent resize#130
Menghuan1918 merged 1 commit into
omdsh-dev:mainfrom
chalei1122-cell:fix/drag-bounce-and-session-cleanup

Conversation

@chalei1122-cell

Copy link
Copy Markdown
Contributor

PR 描述(复制到 GitHub Pull Request 使用)

标题

fix(sidebar): eliminate drag-release bounce, clean up terminals on session delete, silent resize drag

描述(中文版)

一、修复拖动松手时的布局弹跳(3 个根因,src/client/Sidebar.tsx

1. 聊天区弹回全宽再回归

  • 现象:拖动右栏拉宽/拉窄松手后,聊天区瞬间弹回全宽再缩回。
  • 根因:--dsh-sidebar-width/-height 的维护 effect 是 useEffect(paint 后异步执行),cleanup 的 removeProperty 与 setup 的 setProperty 在 passive-effect 分片 flush 时可能跨一次 paint——变量缺失帧里 #rootmargin-right: var(...,0px) 回退到 0,聊天区被画成全宽。
  • 修复:改为 useLayoutEffect(paint 前同步,cleanup+setup 同帧),卸载时的 removeProperty(issue Error-boundary swap leaves --dsh-sidebar-width/--dsh-sidebar-height behind: sidebar "cannot be hidden" after a render crash #31 语义)原样保留。

2. 底部面板右侧跳位

  • 现象:开着底部面板拖右栏宽度,松手后底部面板右侧跳位。
  • 根因:right: window.innerWidth - centerRect.right 依赖最近一次测量,松手提交那一帧 centerRect 还是拖动前旧值。
  • 修复:宽度/角部拖动 onPointerUp 提交时同步更新 centerRect.right(用最终宽度 + 保留 details 列偏移推导),与 applyDrag 拖动中的直写一致。

3. 右栏关闭时聊天区被误挤 + 松手微跳

  • 根因 A:applyDrag 无条件写 --dsh-sidebar-width,拖底部高度时传入的是 state.width 历史值(右栏已关闭仍非 0),聊天区被挤左。
  • 根因 B:onPointerUpstopDragScheduling() 丢弃挂起的 rAF,DOM 停留在倒数第二帧,与提交值差几像素。
  • 修复:applyDrag 宽度只在 panelOpen 时写(effWidth,保留 details 偏移语义);三个拖动(宽度/高度/角部)松手时先 flush 最终帧再提交。

二、会话删除时立即关闭该会话终端(src/index.ts + src/pty-manager.ts

  • 背景:UI 终端断开后由 reconnectGraceMs(默认 30s)宽限回收,用于"切换会话/刷新后重连同一 shell"。但删除会话后终端仍要等宽限到期才被回收。
  • 修复:新增 PtyManager.closeSession(sessionId),订阅 DSH 的 session/disposed 事件,会话销毁时立即关闭该会话全部 UI 终端(agent 终端由 agent 生命周期管理,不受影响)。

三、拖动时不高亮可拖动范围(src/client/sidebar.module.css

  • 本地偏好:右栏/底部/角部拖动手柄在拖动中不再显示高亮背景(panelResizeActive/bottomResizeActive/cornerHandle[data-dragging] 的 background 移除);角部 hover 提示保留。如与项目风格冲突可舍弃此条。

测试

本机 DSH 0.1.0-rc.6 + 0.12.2(Windows)实测:

  • 右栏/底部/角部拖动松手无弹跳、底部面板无跳位、右栏关闭时拖底部不挤聊天区
  • 删除会话后该会话终端立即释放
  • 切换会话后终端保留并在宽限期内重连同一条 shell

补丁文件

better-sidebar-contribution.patch(4 文件,93 增 22 删,git apply 干净通过)。


Description (English)

1. Fix drag-release layout bounce (src/client/Sidebar.tsx)

  • Conversation flashes full-width: the --dsh-sidebar-width/-height effect ran as a passive useEffect; during chunked passive-effect flushing, the cleanup's removeProperty and setup's setProperty can straddle a paint, so #root's margin-right falls back to 0px for one painted frame. Switching to useLayoutEffect keeps cleanup+setup in the same pre-paint frame; the unmount removeProperty (issue Error-boundary swap leaves --dsh-sidebar-width/--dsh-sidebar-height behind: sidebar "cannot be hidden" after a render crash #31 semantics) is preserved.
  • Bottom panel jumps on width drag release: right: window.innerWidth - centerRect.right used the pre-drag measurement for one frame. The width/corner onPointerUp now updates centerRect.right synchronously with the final width (details-column offset preserved).
  • Closed right panel still squeezed the conversation while dragging the bottom height (applyDrag wrote the stale state.width), and release snapped a few px because stopDragScheduling() dropped the pending rAF. applyDrag now writes the width only when panelOpen (effWidth), and all three drags flush the final frame before committing.

2. Close a session's terminals when the conversation is deleted (src/index.ts + src/pty-manager.ts)

UI terminals survive disconnects for reconnectGraceMs (default 30 s) so a tab switch/refresh reattaches the same shell — but deleting a session left them lingering until the grace expired. Added PtyManager.closeSession(sessionId) and subscribed to DSH's session/disposed to release them immediately. Agent terminals are agent-lifecycle-owned and untouched.

3. No highlight while dragging the resize handles (src/client/sidebar.module.css)

Local preference: removed the drag-state background on the width/height/corner handles; the corner hover hint stays. Drop this commit if it conflicts with the project style.

Tests

Verified on DSH 0.1.0-rc.6 + 0.12.2 (Windows): no bounce on any drag release, bottom panel stays put, closed right panel no longer squeezes the conversation, session deletion releases its terminals immediately, and tab switches keep the same shell within the grace window.

…ilent resize

- Sidebar.tsx: useLayoutEffect for the layout-push variables so cleanup+setup
  land in the same pre-paint frame (no full-width flash on drag release);
  applyDrag writes the width only while the panel is open (a closed right
  panel no longer squeezes the conversation when dragging the bottom height);
  flush the final drag frame before stopDragScheduling on all three handles;
  sync centerRect.right on width/corner release so the bottom panel does not
  jump.
- index.ts/pty-manager.ts: close a session's UI terminals immediately on
  session/disposed (they used to linger until reconnectGraceMs expired).
- sidebar.module.css: no highlight on the resize handles while dragging
  (local preference; the corner hover hint stays).
@KaramachiA217

Copy link
Copy Markdown

独立排查了同款问题,结论一致。补两点实测证据 + 一个可选替代修法,供参考。

实测证据:松手瞬间逐帧采样 #rootmargin-right(500ms):

[503, 0, 3.2, 15.5, 41, 85, 150, 225, 293, 346, 388, 419, 443, 462, 476, 486, 494, 499, 502, 503, 503...]

松手瞬间 503→0,再用 300ms 缓动爬回 503,正是聊天区弹回全宽再缩回的量化。而且 delta=0(停下再松手、指针没动)同样掉 0,说明它与尾差无关,纯粹是 removeProperty 回退 0px 兜底 + 过渡重放。

触发点细化:除 passive effect 跨 paint 外,更精确的触发是 measureCenter()getBoundingClientRect() 在 cleanup(removeProperty)与 setup(setProperty之间运行,强制一次同步重排,把 0px 兜底定格成过渡起点。所以 useLayoutEffect 能消(cleanup+setup 同帧),但先移除再重设这步在每次 width/height 依赖变化(松手、开合、切会话)时仍会发生。

替代修法(可选):把释放拆成独立的「仅卸载时」effect,依赖变化时只 setProperty、不 remove——0px 瞬态从机制上不存在,也不依赖 layout-effect 时序;卸载/HMR/崩溃边界交换时的释放语义(issue #31)原样保留:

useEffect(() => {
  // 只 setProperty --dsh-sidebar-width / --dsh-sidebar-height
}, [narrow, panelOpen, width, bottomOpen, bottomHeight])

useEffect(() => () => {
  document.documentElement.style.removeProperty('--dsh-sidebar-width')
  document.documentElement.style.removeProperty('--dsh-sidebar-height')
}, [])

尾差 flush(onPointerUp 先 flush 最终帧再提交)这部分结论一致。两种修法都成立,供取舍。

@HuanLinOTO HuanLinOTO added bug Something isn't working P3 低:体验/建议 area/sidebar Sidebar shell, panels, layout, tabs, workspace windows labels Aug 23, 2026
HuanLinOTO added a commit to HuanLinOTO/DSH-better-sidebar that referenced this pull request Aug 24, 2026
- index.ts: 移除 omdsh-dev#130 遗留的 ctx.on('session/disposed')(fake ctx 无 on,
  且 omdsh-dev#130 已按取 HEAD 决策,closeSession 调用方一并移除)
- browser.probe: 405→GET 重试后跳过 embed-signals GET,避免多余第 3 次请求(omdsh-dev#69)
- EditorHost: 移植 omdsh-dev#228 的 dirty 确认守卫到 HEAD 的 reloadSeq 结构(refreshFile)
- tests: fake ctx 补 get()(omdsh-dev#357 ctx.get 回归,free-window/editor-refresh/markdown)
- tests: prefs 期望对象补 browserAllowedLoopback(smoke/plugin-shape/state)
- tests: builtins 浏览器设置行补 browserAllowedLoopback(omdsh-dev#69)
- tests: install-powershell BOM 门限仅对实际文档化远程调用的文件生效
- locales: 19 语言补齐 showInFolder / refreshUnsavedConfirm(omdsh-dev#94 omdsh-dev#215 omdsh-dev#228
HuanLinOTO added a commit to HuanLinOTO/DSH-better-sidebar that referenced this pull request Aug 24, 2026
- BrowserView: 即使 host 在白名单,GUI 自身 origin 永不给 allow-same-origin(裸 host 条目覆盖所有端口时可提权)
- intercept: revealInExplorer 相对路径经 resolveSidebarPath 解析后再 revealPaths(交付物路径是相对的)
- intercept: ctx.betterSidebar 改为 ctx.get(omdsh-dev#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: 恢复拖拽反馈(omdsh-dev#130 的 silent resize 与 PR 描述冲突,取 HEAD)
- Sidebar: 剩余 4 处 ctx.betterSidebar 改 ctx.get
- tests: 补 revealPaths/iframeSandboxFor/terminal-font 回归测试 + EditorHost/FileTree 测试补 revealed prop
@Menghuan1918
Menghuan1918 merged commit 4feb5ef into omdsh-dev:main Aug 24, 2026
Menghuan1918 pushed a commit that referenced this pull request Aug 24, 2026
- 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).
Menghuan1918 added a commit that referenced this pull request Aug 24, 2026
- README / README_EN: add v0.16.0 changelog (free windows #354, model-driven
  sidebar opens #353, Markdown inline HTML + TOC #360, 19-language overlays
  #339, multi-repo git + linked worktrees #326/#285, browser loopback
  allowlist, Vue + 28 legacy languages #202, preview refresh trio #215/#228,
  local Markdown images #292, ego-browser catalog #340; fixes: market/cordis
  #338, ctx.get() reads #357, workspace boundary #345, panel-host layering
  #330/#278, layout-push hardening #310/#130/#180, width sharing #36, terminal
  cleanup #130, fs-search noise dirs #342, mermaid #341, Nerd Font #190,
  UTF-8 HTML #193, trust-fence #182, reveal-in-explorer #94, PowerShell #47,
  GET probe fallback #69, unrun devDep #336) and move v0.15.2 into history
- feature tour: free windows + Markdown inline HTML/TOC (EN), feature list
- plugin ecosystem: add ego-browser (24 tab plugins), install pin ^0.16.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sidebar Sidebar shell, panels, layout, tabs, workspace windows bug Something isn't working P3 低:体验/建议

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants