Mobile-first Web UI quality-of-life pack for the DeepSeek Harness: a Chrome-style active-session tab bar, full-screen sidebar swipe open/close, overlay sidebar, collapsed-sidebar recents, keyboard-suppressing session switching, viewport/IME keyboard adaptation, touch feedback, code/table inner scrolling, hidden permission dropdown, a full-screen settings rewrite with tab memory, and a compositor-driven status pulse, no-touch-drag long-press protection — 14 features, each independently toggleable from the Settings QoL section with instant effect, persisted per browser in localStorage; pure client-side CSS/JS, mobile rules locked to viewports of 768px and below so desktop stays untouched.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-qol
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:john-walks-slow/dsh-qol
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
Quality-of-life tweaks for the DeepSeek Harness (dsh) Web GUI: a session tab bar, sidebar swipe gestures, IME/keyboard adaptation, touch feedback, a full-screen settings rewrite and more — 13 features, each independently toggleable from Settings → QoL, taking effect instantly and persisted per browser. Mobile-first; some features (tab bar, status animation, etc.) apply on desktop too.

Features
| Feature | Description | Default |
|---|---|---|
| Active-session Tab Bar | Horizontally shows active session tabs at the top of the page; unread/running tabs pinned first, one-tap switching, no accidental keyboard pull-up; fresh sessions show as "New Session"; middle-click/× to close (closing the last tab lands on a fresh session, and the fresh session itself cannot be closed); the + button sits right after the last tab (Chrome-style) | On |
| Sidebar swipe | Expand the sidebar by swiping right and collapse by swiping left anywhere on screen (64px threshold, non-following; the left 16px edge yields to the system back gesture; input fields / horizontal scrollers skipped; no response while a dialog is open; swiping over buttons is safe — it never triggers a click) | On |
| Sidebar overlay | On mobile the sidebar opens as an overlay covering content instead of squeezing the main area into a reflow | On |
| Collapsed-sidebar recents | When the sidebar is collapsed, circular first-letter icons of recently active sessions appear under the search box, with status badges | On |
| Collapse sidebar on switch | On narrow screens, picking a session in the sidebar auto-collapses it and returns to the conversation (≤768px only) | On |
| No keyboard on switch | Switching sessions never auto-focuses the input box, so the IME never pops up; covers sidebar session rows, active tabs, collapsed rail icons and archive jumps; tapping the input box directly still focuses it manually | On |
| IME/keyboard adaptation | viewport meta (viewport-fit=cover + interactive-widget=resizes-content), a 100dvh height chain, composer safe area, iOS visualViewport CSS-variable fallback (never changes element sizes/fonts) |
On |
| Touch feedback | touch-action: manipulation (kills the 300ms delay and double-tap zoom), disables the system tap highlight, :active press feedback, iOS :active fix, respects prefers-reduced-motion (never changes element sizes) |
On |
| Full-screen settings rewrite | The settings dialog stacks full-screen at ≤768px with horizontally scrolling tabs, a collapsed-tab-width bugfix and safe-area adaptation | On |
| Settings tab memory | Reopening settings restores the last selected tab instead of resetting to General | On |
| Code/table inner scroll | Long code blocks and tables scroll horizontally inside their containers; body text wraps without overflowing | On |
| Hide permission dropdown | Hides the permission (Access mode) dropdown trigger inside the input box to save horizontal space; model selection and context usage are unaffected | On |
| Status animation optimization | Replaces the SVG opacity chase-dot animation with a CSS transform pulse on the compositor thread, zero main-thread cost. Measured idle FPS via rAF: 35 → 55 | On |
Install
dsh plugin --profile web add dsh-qol
No manual configuration needed after install — the bundled cordis.patch.yml mounts automatically; once installed, a QoL section appears on the settings page after refreshing the web UI.
Install straight from GitHub (source install; lib/ is hand-written source and needs no local build, but the package declares a prepare syntax-check script, which pnpm ≥10 blocks the first time):
dsh plugin --profile web add github:john-walks-slow/dsh-qol
# Add the package name pnpm prints to allowBuilds in
# ~/.dsh/profiles/web/pnpm-workspace.yaml, then re-run
Usage
- Open the dsh Web GUI (best on mobile).
- Settings → QoL: 13 toggle rows (name + one-line description). Each click takes effect instantly, no page refresh needed.
- Toggles persist automatically in browser
localStorage(keydsh.qol.v1) for this browser only; deleting that key restores the all-on defaults.
What a saved toggle set actually looks like (localStorage["dsh.qol.v1"]):
{ "active-tabbar": true, "sidebar-gesture": true, "ime-viewport": true, "tap-feedback": true }
The implementation is an attribute total-gate: every feature maps to an html[data-qol-<feature-id>] attribute that both the CSS rules and the JS event handlers read — toggling just sets/removes the attribute, which is why it applies instantly with no reload.
Permissions & compatibility
- Pure client plugin: the host-side
applyis empty, zero npm runtime dependencies; all logic runs in the browser half (lib/client.js) - Zero permissions: no external services, no network requests, no filesystem writes, no reading of session content — it only touches browser-side CSS, DOM events and the viewport meta
- Config never leaves the browser: toggle state lives only in this browser's
localStorage; nothing is uploaded or written server-side - Zero desktop impact: all mobile-specific rules are locked inside
@media (max-width: 768px); cross-platform features (tab bar, rail, status animation) behave the same on both - Never changes element sizes/fonts: a deliberate design constraint (touch feedback and IME adaptation only touch behavior/compositor layers)
- Degrades, never blocks: every host-service lookup is wrapped in
ctx.get()+ try/catch; a missing service only logs aconsole.warn; if a structure-anchor selector stops matching after a host redesign, the matching rules silently stop applying and the page is unaffected - Coexists with dsh-web-mobile-fix (see below)
- Tested baseline: current dsh stable (0.1.x) web profile + Chromium/Firefox engine mobile emulation; real-device (iOS Safari / Android Chrome) touch feel and IME details are worth a manual pass
Relationship with dsh-web-mobile-fix
The two coexist: dsh-web-mobile-fix provides the compact mobile layout (32px session-header buttons, hidden breadcrumbs, etc.); this plugin adds the toggleable QoL layer (gestures / IME / tab bar, etc.). Their settings-dialog rules overlap but are visually equivalent — the union is safe. If you don't need mobile-fix's compact layout, you can remove it on its own — this plugin's settings-mobile covers the settings-page CSS.
How it works
- Pure client: the empty host-side
applyexists only to mount the package into the profile; the browser half is loaded through awindow.__ModuleLoader__.loadfactory (viaexports["./client"]and thedsh.clientdeclaration in package.json). - Attribute total-gate: see above — the key to instant toggles.
- Structure anchors: CSS uses structural selectors like
data-slot/:has(> nav)with zero hash-class dependency (the status-animation rule's hash-class match is a deliberate exception; a mismatch just means a silent fallback — see the comments in client.js). - Shape defense: every service lookup is try/catch-wrapped; any missing service degrades silently instead of blocking load.
Local development
npm install
npm run build # syntax-checks both artifacts: lib/index.js (host entry) + lib/client.js (browser bundle)
E2E (development only, targets a running dsh instance; tokens are read from environment variables so no credentials land in the repo):
export DSH_E2E_TOKEN_4175=<live instance token> # printed by `dsh web` on startup
export DSH_E2E_TOKEN_4176=<temp instance token>
node e2e/mobile.mjs mobile # Phase-1: mock harness against the real DOM
node e2e/mobile.mjs desktop # desktop zero-impact verification
node e2e/integration.mjs # Phase-2: real-plugin integration on a temp instance
Note: e2e depends on a local camoufox + playwright-core (paths are hardcoded at the top of each script; adjust for your machine). This repo has no unit tests; npm test is intentionally not provided.
- Feature docs:
docs/features/(research / plan / validation / summary). - Adding a feature: register one entry in the
FEATURESregistry inlib/client.jsplus its CSS block / JS hooks.
License
MIT
Release a new version
One command runs tests, bumps the version and packs (npm version also commits and tags):
npm run release # patch; for bigger changes: npm version minor or major
Then publish with the fingerprint flow and push:
node ~/.agents/skills/npm-publish/scripts/publish-webauthn.cjs /tmp/dsh-qol-<newver>.tgz
git push --follow-tags
Verify with npm view dsh-qol version. When releasing several packages, check "do not challenge for the next 5 minutes" on the webauthn page to publish them all with one fingerprint.
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 7809
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 7809
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
omdsh-dev/DSH-better-sidebar★ 3680
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
ccch1mneyyy/dsh-TUI★ 3110
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 2750
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1359
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.