Adds a mobile hotkey mode to the DSH web composer that auto-detects touch and mobile devices and switches to Ctrl+Enter for send and plain Enter for a newline while leaving the desktop keymap untouched, with a Settings toggle, a /mobilekeys command and a window.__dshMobileKeys console API.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-mobile-keys
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:liujuntao123/dsh-mobile-keys
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
中文 | English
A mobile hotkey mode plugin for DeepSeek Harness (DSH) — a browser-side bundle plugin. It auto-detects mobile devices and switches the send/newline key bindings, fixing the fact that mobile soft keyboards cannot produce Shift+Enter, which makes the composer impossible to wrap lines on. Preferences persist in browser localStorage; no backend needed.
Compatibility: adapted to DSH
0.2.x(composer contract, settings slot and command API verified against0.2.1-rc.5); still compatible with earlier builds that only exposedata-lexical-editor.
⌨️ Key bindings
| Mode | Send | Newline | Notes |
|---|---|---|---|
| Desktop (default) | Enter |
Shift+Enter |
Identical to native DSH; the plugin stays out of the way |
| Mobile mode | Ctrl+Enter |
Enter |
Send rides DSH's native accelerated gesture; newline intercepts plain Enter and re-dispatches a synthetic Shift+Enter through DSH's own line-break path |
✨ Features
- 🔍 Auto-detection: defaults to
automode — classifies the device by UA / touch points / pointer type / viewport width and follows rotation or monitor changes live. Touch-screen laptops and kiosk touch displays are never misclassified. - 🎛️ Settings toggle: DSH Settings → General → "Mobile hotkey mode" switch, live status line, one-tap "restore auto-detect" after a manual override.
- 💬
/mobilekeyscommand: check status and switch Auto / On / Off right from the composer. - 🛠️ Console API:
window.__dshMobileKeys.getStatus() / setMode() / getBindings(). - 🛡️ Zero intrusion elsewhere: IME composition, open overlays (menus/dialogs/command palette), question-card inputs, and anything outside the composer all pass through untouched; in desktop mode the plugin does nothing at all.
- 💾 No backend: preference stored in
localStorage(keydsh.mobilekeys.v1), remembered per device.
🤖 AI install
Hand the following prompt to your AI (DeepSeek / Claude, etc.) and it will install the plugin for you:
Please install the
dsh-mobile-keysplugin into the DeepSeek Harness web profile: rundsh plugin --profile web add git+https://github.com/liujuntao123/dsh-mobile-keys.git, then restartdsh weband hard-refresh the browser (Ctrl+F5), then runwindow.__dshMobileKeys.getStatus()in the browser console to confirm it returns{ mode, detected, effective, bindings }, and confirm the "Mobile hotkey mode" toggle appears in Settings → General.
Full AI install instructions (manual method, troubleshooting, verification): AI_INSTALL.md.
📦 Installation
Method 1: install from GitHub (recommended)
dsh plugin --profile web add git+https://github.com/liujuntao123/dsh-mobile-keys.git
DSH will automatically: pnpm-install the git dependency → detect the dsh.bundle declaration → add dsh-mobile-keys to dsh.profile.bundles in package.json.
Requires
pnpm. If missing:npm install -g pnpm.
Method 2: manual install
cd ~/.dsh/profiles/web # Windows PowerShell: cd $env:USERPROFILE\.dsh\profiles\web
pnpm add git+https://github.com/liujuntao123/dsh-mobile-keys.git
Then you must add "dsh-mobile-keys" to the dsh.profile.bundles array in that directory's package.json:
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-mobile-keys" // ← required, otherwise it won't load
]
}
}
⚠️
pnpm adddoes not touch thebundleslist; DSH loads plugins from that list, so missing this line means it won't take effect.
Method 3: local clone + link (for development)
git clone https://github.com/liujuntao123/dsh-mobile-keys.git
Point a link dependency at it in the web profile's package.json:
"dependencies": {
"dsh-mobile-keys": "link:/absolute/path/to/dsh-mobile-keys"
}
and likewise add "dsh-mobile-keys" to bundles.
After installing: restart + hard refresh (all methods)
bash scripts/restart-dsh-web.sh # if you cloned this repo; otherwise restart dsh web your way
Then hard-refresh the browser with Ctrl+F5.
⚙️ Usage
Modes
| store.mode | Meaning |
|---|---|
auto (default) |
Detect the device and follow it |
on |
Always mobile bindings (Ctrl+Enter send / Enter newline) |
off |
Always desktop bindings (Enter send / Shift+Enter newline) |
Settings panel
DSH sidebar → gear (Settings) → General → "Mobile hotkey mode", with a two-line description:
Current keys: send Ctrl+Enter / newline Enter
Detected device: mobile · Recommended on mobile devices to avoid being unable to insert newlines
- The switch shows and controls the effective mode; flipping it writes a manual value (
on/off). - Line 1: the currently effective bindings, live with the mode.
- Line 2: the device verdict and a recommendation; after a manual override a "restore auto-detect" action appears at the end of this line, clearing the override back to
auto.
Command & console
- Type
/mobilekeysin the composer → pick Auto / On / Off. - Browser console:
window.__dshMobileKeys.getStatus() // { mode, detected, effective, bindings }
window.__dshMobileKeys.setMode('on') // 'auto' | 'on' | 'off'
window.__dshMobileKeys.getBindings() // { send, newline }
Verification checklist
- Settings → General shows the "Mobile hotkey mode" row with a working switch and live status.
/mobilekeysopens the mode menu.- With mobile mode on:
Enterinserts a newline,Ctrl+Entersends; turning it off restoresEnter-to-send. - With the command menu open,
Enterstill selects an item instead of inserting a newline.
🔍 How it works
The DSH composer is a Lexical editor. Enter semantics come from a KEY_ENTER_COMMAND handler registered at CRITICAL priority:
event.shiftKey === true→ the handler returnsfalse, falling through to@lexical/plain-text's default → line break inserted;- otherwise → submit (
ctrlKey/metaKeycount as an accelerated send).
So this plugin:
- changes nothing on the send side: mobile mode's
Ctrl+Enteris the native accelerated gesture; - touches the newline side minimally: only while mobile mode is active, it intercepts a plain
Enterinside the composer at thedocumentcapture phase (preventDefault + stopImmediatePropagation), then re-dispatches a synthetickeydownwithshiftKey: trueonto the Lexical root ([data-lexical-editor="true"]), reusing DSH's own line-break path (selection, undo stack, and controlled updates all behave exactly like a real Shift+Enter).
Guard rails:
- Only
key === 'Enter'is considered;Shift/Ctrl/Cmd/Alt+Enter, IME composition (isComposing/keyCode 229), and visible overlays ([role="menu"|"listbox"|"dialog"|…]) always pass through; - Targets outside
[data-lexical-editor="true"](buttons, question-card inputs, …) always pass through; - Synthetic events carry a
__dshMobileKeysSyntheticmarker to prevent re-entry.
Mobile detection (auto mode):
mobile ⇐ UA matches Android/iPhone/iPad/iPod/Mobile/Silk
⇐ iPadOS 13+ desktop UA (Macintosh + maxTouchPoints > 1)
⇐ (pointer: coarse) AND has touch AND (max-width: 820px)
🧪 Development
npm test # dependency-free Node tests: loads the real lib/client.js in a VM, end to end
node --check lib/client.js
Layout:
lib/index.js host-side minimal Cordis plugin (mount/unmount shell)
lib/client.js all browser logic (ModuleLoader bundle, no build step)
tests/ dependency-free Node tests (node --test)
scripts/ restart-dsh-web.sh: restart dsh web with its original command line
cordis.patch.yml bundle patch: inserts the plugin into a profile composition
No build step — edit lib/client.js, restart dsh web, hard-refresh.
❓ FAQ
Q: Relationship with dsh-hotkeys?
A: This plugin borrowed the bundle-plugin shape and conventions from ctenni/dsh-hotkeys but focuses on one thing: automatic per-device key-binding switching. No recording — the device is the switch. The two can coexist.
Q: Phones have no Ctrl key — how do I press Ctrl+Enter?
A: Tablets with keyboards and phones with external keyboards work out of the box; soft-keyboard-only users can turn the mobile mode off in Settings, or wait for a future "custom mobile send key".
Q: Does the ask_user_question card input change?
A: No. Its Enter means "submit/continue" and is unrelated to the composer; the plugin does not touch it.
Q: I don't see the toggle in Settings.
A: Make sure dsh-mobile-keys is listed in dsh.profile.bundles, dsh web was restarted, and the browser was hard-refreshed; window.__dshMobileKeys should resolve in the console.
📄 License
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 9925
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★ 9925
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.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 6712
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.
ccch1mneyyy/dsh-TUI★ 6538
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 4808
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1672
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.