description: "A vertical resize/collapse grip for the conversation top header: drag to resize, drag past the minimum to collapse."
@dsh/ui-panel-resize
English | 中文
A DSH (DeepSeek Harness) client plugin. Installed into a DSH profile, it adds a draggable bottom border to the Web GUI's conversation header. Topics:
dsh·dsh-plugin·deepseek-harness·cordis-plugin·dsh-client-plugin
The gap it fills
The conversation header (session title + subagent / mode / background-job chips + the 对话 / 轨迹 tabs) is sized purely by its content and has no adjustment entry point at all: the cursor does not change at its bottom border, and there is no way to squeeze or hide it. This plugin turns that border into a draggable grip.
The sidebar's "drag past the minimum width to collapse" used to live here too and was removed — see Why not the sidebar.
Interaction
| Action | Result |
|---|---|
| Move the pointer to the header's bottom border | cursor becomes row-resize and a small grip appears |
| Drag up | the header shrinks, overflowing content is clipped; floor is 44px (title row only) |
| Drag more than 40px above that floor | the grip turns blue and reads "release to collapse" |
| Release there | the header collapses to an 8px strip |
| While collapsed: click the strip | expands back to the previous height (or the natural one) |
| While collapsed: drag down more than 24px | same as above |
| Drag down | the header grows, up to 160px above its natural height |
| Double-click the grip | clears the manual height and returns to the natural one |
A pill beside the grip shows the live height (xx px) and switches to the hint text once the collapse
threshold is crossed.
Tuning (lib/client.js)
| Constant | Value | Meaning |
|---|---|---|
HEADER_MIN |
44 | lowest visible height (keeps the title row, clips the tabs) |
HEADER_COLLAPSE_SLACK |
40 | how far below that floor counts as "collapse" |
HEADER_COLLAPSED |
8 | strip height while collapsed |
HEADER_MAX_GROWTH |
160 | growth allowed above the natural height |
HEADER_DRAG_SLOP |
3 | travel that turns a click into a drag |
HEADER_RESTORE_TRAVEL |
24 | downward travel that expands a collapsed header |
Implementation
Only stable DOM hooks are used: no hashed class names, and no React-managed subtree is mutated.
- The header is located through
[data-conversation-header-leading]'s parent (fallback: the widestheader[data-window-drag]), marked with the plugin's owndata-dsh-panel-resize="header", and sized through the--dsh-panel-resize-header-heightcustom property (!important, overriding the shippedmin-height:76px). - The grip is a
position: fixedelement appended todocument.body. Its geometry comes only from the header's owngetBoundingClientRect()— deliberately: the header's parent is a slot host that may bedisplay: contents(a zero-size border box), which is how an early build produced an invisibleleft:0;width:0grip. The grip straddles the border: about 7px inside the header (inside the tab row's own bottom padding) and about 3px below. When a right panel owns its own track ([data-rightbar-col]wider than 0) the grip is clipped to the track's left edge. - The preference lives in
localStorage['dsh.ui-panel-resize.header']={"height":number|null,"collapsed":boolean}. - Every node and listener is owned by
ctx.effect(), so disabling or hot-replacing the plugin removes all of it. One<style data-plugin="@dsh/ui-panel-resize">tag carries the rules.
The plugin injects no service (inject: []); it is pure DOM chrome.
Install
git clone https://github.com/jiangshirong/dsh-ui-panel-resize.git
cd dsh-ui-panel-resize
powershell -ExecutionPolicy Bypass -File install.ps1
(pwsh -File install.ps1 is equivalent when PowerShell 7 is installed.)
The script copies this directory into
%USERPROFILE%\.dsh\profiles\desktop\node_modules\@dsh\ui-panel-resize and adds the loader entry to
%USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml (skipped when it is already there). It works from
any directory, so an offline copy or an extracted archive is fine too.
Manual install (two steps)
- Copy this repository's directory to
%USERPROFILE%\.dsh\profiles\desktop\node_modules\@dsh\ui-panel-resize. - Append to
%USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml:
- insert:
- id: ui-panel-resize
name: "@dsh/ui-panel-resize"
disabled: false
insert is required: a row that only names an id and a name overrides an existing entry and is silently
discarded when none exists.
The host Loader adds the entry immediately and the browser picks the bundle up through the module graph
(HMR), normally without a restart. Edits to lib/client.js also reload on their own; only lib/index.js
(the host half) needs a restart.
Uninstall
Remove the insert entry from cordis.patch.yml (or disable the entry in Settings → Plugins) and delete
node_modules\@dsh\ui-panel-resize. Every DOM node and style tag goes away with the plugin.
Diagnostics
The browser half appends short codes to its own localStorage ring dsh.ui-panel-resize.trace (reset on every
activation) and the host half mirrors it into
%USERPROFILE%\.dsh\profiles\<profile>\ui-panel-resize-trace.log. A page-side plugin has no devtools
console, so this is its only reporting channel.
| Marker | Meaning |
|---|---|
A:apply |
client half activated |
H:found:<px> |
header located, <px> = natural height, followed by anchors= / pos= |
H:handle |
grip created |
H:detach:<reason>:live=<0|1> |
the host replaced the header node; live=0 means the old one is detached |
H:collapsed / H:height:<px> / H:restore:* / H:reset |
collapsed / sized / restored / reset |
D:handle= / D:atHandle= |
one hit-test sample 1.2s after activation: grip geometry and the topmost element there |
E:* |
an error, with type and message |
Why not the sidebar
The first revision also collapsed the left sidebar when its width drag was released past the minimum (the ChatGPT gesture). It was removed because it does not hold up as a feature:
- The outcome was already one step away.
Ctrl+Band the sidebar's own collapse control both hide the column. The implementation's own fallback path was to synthesize aCtrl+Bkeypress — when a gesture's fallback is a single keystroke, the gesture adds no capability. - It had a cost, not just zero benefit. By the shell's contract, collapsing forgets the dragged width and reopening returns to the default 280px. A previously safe action ("drag the width") became a destructive one: overshoot once and the column is gone along with the width you just set.
- Narrow-window semantics differ. Below 1024px,
toggleSidebar()toggles the narrow "expanded over the centre" state instead, so the gesture and the shortcut are not equivalent. - It was the only part of this plugin that had to fight React's drag lifecycle — intercepting
pointerupin the capture phase,stopPropagationto stop the host writing its clamped width back, manually releasing the pointer capture, and re-asserting the collapse at 280/720ms. The riskiest, least maintainable code in the package, serving a duplicate feature.
The header has no equivalent entry point, so it stays.
Known limits
- The grip is 10px tall and straddles the border: about 7px inside the header (inside the tab row's own bottom padding) and 3px below.
- While the header is collapsed to its 8px strip the grip hides itself whenever the active main panel is not the conversation (for example the settings panel).
- The height override re-measures the natural height when the header's
classchanges (blank session ↔ session); a manual height is re-clamped into the new legal range.
No comments yet. Be the first to write one.