cx2002302-lang/dsh-chat-collapse

dsh-chat-collapse

DSH Web GUI plugin — one-click collapse for the chat composer, with a floating restore ball. 聊天组件一键收缩:纯浏览器侧,零 DSH 源码改动。

Uncategorizedbrowser-pluginchat-uicollapsible

dsh-chat-collapse

One-click collapse for the DSH Web GUI chat component
Click once to hide it; a floating restore ball appears in the corner. Click the ball to bring it back — the ball disappears.

English | 中文

A browser-side plugin with zero changes to the DSH codebase. No patching the host, no bundle injection, no host-side service.

The problem it solves

On a small screen the composer and its toolbar are always there, and that eats a slice of the conversation you can actually read. This plugin puts a ⌄ button at the right end of the composer tool row: one click collapses the whole component and hands the vertical space back to the transcript. A restore ball stays in the corner, ready to bring it back.

Hiding is CSS-only — the DOM stays mounted — so your draft, pasted images and caret position all survive. Reopen it and keep typing.

Interactions

ActionResult
Click the ⌄ button at the right end of the composer tool rowThe target component collapses (draft / images / caret preserved)
After collapsingA restore ball (chat-bubble icon) floats in the viewport corner
Click the restore ballThe component returns and the ball hides itself
Right-click the restore ballCycles the collapse target between “composer only” and “whole conversation area”, with a brief toast
Alt+HKeyboard toggle for collapse / expand

State (collapsed or not, collapse target, ball position) lives in localStorage['dsh.chatCollapse.v1'] and survives a reload.

Collapse targets

targetWhat gets hiddenAnchored by
composer (default)Only the composer seat[data-composer-seat]
chatThe whole conversation area (transcript + composer + session header)the parent of [data-conversation-scroll]

Visibility is driven by two attributes on <html> (data-dsh-cc / data-dsh-cc-target) plus CSS selectors — no class names are added to official nodes, so a React re-render cannot wipe them.

Install

dsh plugin --profile web add github:cx2002302-lang/dsh-chat-collapse

Then restart dsh web once (a new client bundle has to enter the boot graph) and refresh the page:

# Adjust the unit name to your deployment; this is the systemd user-service form
systemctl --user restart dsh-web.service

After the refresh the ⌄ button appears at the right end of the composer tool row.

For local development you can also point straight at a directory:

dsh plugin --profile web add /path/to/dsh-chat-collapse

Uninstall

dsh plugin --profile web remove dsh-chat-collapse

On unload the ctx.effect disposer removes the injected <style>, the button, the restore ball and the <html> attributes — no residue (asserted in tests/unit-check.mjs).

Official hooks it relies on

HookSource
[data-composer-card]ui-conversation/skeleton/InputBar.tsx
[data-composer-seat]ui-conversation/skeleton/ConversationRoot.tsx
[data-conversation-scroll]same file

All three are literal attributes in the source, not CSS-module hashed class names, so they are stable within a version. Verified present on dsh 0.1.2-rc.1.

Privacy

  • No network requests, no telemetry, no host-side service.
  • The only thing persisted is localStorage['dsh.chatCollapse.v1'] (three boolean / enum values).
  • The host half lib/index.js is an empty apply() — it registers no service and reads no file.
  • The plugin never touches conversation content and never reads draft text.

Layout

lib/index.js          Host half: empty apply (only registers the dsh.client declaration)
lib/client.js         Browser half: everything (__ModuleLoader__ bundle, no JSX, no build step)
cordis.patch.yml      Inserts this plugin into the web profile's plugin roster
tests/unit-check.mjs  Pure-Node check: asserts the stylesheet it actually generates (17 cases, no browser)
tests/smoke.html      Browser functional test: click / restore / target switch / hotkey / unload (24 cases)
tests/visual.html     Expanded and collapsed states (append ?state=collapsed for the collapsed one)

Tests

# 1) Pure Node, no browser dependency
node tests/unit-check.mjs

# 2) Browser functional test (headless Chrome)
google-chrome --headless=new --no-sandbox --virtual-time-budget=6000 \
  --dump-dom "file://$PWD/tests/smoke.html" | grep -E 'PASS|FAIL|passed'

Configuration

Defaults live in DEFAULTS at the top of lib/client.js: collapsed / target / anchor / ball.

  • anchor: 'toolRow' (default) puts the button in the tool row; 'corner' pins it to the composer card's top-right corner instead.
  • ball: 'br' | 'bl' | 'tr' | 'tl' sets the restore ball's corner; default is bottom-right. The bottom of the two bottom variants is BALL_INSET(20) + BALL_SIZE(44)/2 = 42px — half the ball's height above the baseline, clearing whatever already sits in that corner once the composer is hidden. Move it by editing those two constants.

License

MIT