# dsh-hotkeys [![CI](https://github.com/csiroqa/dsh-hotkeys/actions/workflows/ci.yml/badge.svg)](https://github.com/csiroqa/dsh-hotkeys/actions/workflows/ci.yml) A **global keyboard shortcuts** plugin for DeepSeek Harness (DSH): session switching, send/clear draft, stop generation, fork and archive sessions. All actions go through the public `dsh-client-runtime` services; no dependency on the host DOM structure. 中文: [README.md](README.md) ## Features ### Session navigation - **Session switching**: wrap around the sidebar order (hold for continuous switching) - **New session**: same entry as the sidebar button for a quick start ### Draft & send - **Send draft**: `send` mirrors the composer's accelerated Enter (an empty draft with queued messages steers them into the running turn); submissions in flight are guarded - **Clear draft**: wipes text only, keeps attached images ### Session management - **Stop generation**: stops the running turn of the current session (composer-consistent: only plain sessions and continuable subagents are interruptible); an inline notice appears when nothing is running - **Fork session**: forks the current session (incremented title) and jumps to the child - **Archive session**: native `workspaces.archiveSession` (moves the current session into "Archived") ### Shortcut listing - `/hotkeys` command: lists the current effective binding of all 8 actions (including user overrides) inside the chat ### Guards - IME composition, AltGr chords and already-consumed (`defaultPrevented`) keys never trigger; plain-key chords do not fire inside inputs/textareas (`ignoreInputs`); key repeat only passes navigation actions; invalid or duplicate bindings log a `[hotkeys]` warning with the action name ## Configuration Bindings, `sendMode` and `ignoreInputs` are persisted through the **settings namespace** (the host half registers the schema; the browser half syncs via `settingsScope` and rebuilds bindings live — no restart needed). Two ways: 1. **Settings > General > Keyboard shortcuts**: visual editor for all bindings (**multiple bindings per action**, comma-separated, e.g. `mod+enter, ctrl+shift+enter`; empty binding disables an action, Reset restores the default), `sendMode` select and `ignoreInputs` toggle 2. Edit the `hotkeys:` section in `$DSH_HOME/settings.yaml` (binding fields also accept arrays): ```yaml hotkeys: newSession: 'mod+alt+n' prevSession: 'mod+alt+[' nextSession: 'mod+alt+]' stop: 'mod+.' send: 'mod+enter' clearComposer: 'mod+shift+u' fork: 'mod+shift+f' archive: 'mod+alt+a' sendMode: 'auto' ignoreInputs: true ``` Built-in default bindings (the table above; an empty string `''` disables an action): | Key | Default | Description | | --- | --- | --- | | `newSession` | `mod+alt+n` | New session (same entry as the sidebar button; `mod+n` is a browser-reserved shortcut) | | `prevSession` / `nextSession` | `mod+alt+[` / `mod+alt+]` | Previous / next session (`mod+shift+[ ]` is browser tab switching on macOS) | | `stop` | `mod+.` | Stop the current session's running turn | | `send` | `mod+enter` | Send the current draft (composer accelerated-Enter semantics) | | `sendMode` | `auto` | Busy submission mode: `auto` (identical to the composer's accelerated Enter, see below) / `queue` / `steer` | | `clearComposer` | `mod+shift+u` | Clear draft text (avoids Firefox's `mod+shift+k`) | | `fork` | `mod+shift+f` | Fork the current session | | `archive` | `mod+alt+a` | Archive the current session (native `workspaces.archiveSession`; avoids Chrome's `mod+shift+a` tab search) | | `ignoreInputs` | `true` | Plain-key chords do not fire inside inputs/textareas | Binding syntax: modifiers (`mod`/`ctrl`/`alt`/`shift`, any combination) + a single key or named key (`enter`/`esc`/`up`/`pageup`/`f5`/…). `mod` is Ctrl on Windows/Linux and ⌘ on macOS. Defaults avoid browser-reserved shortcuts. Note: the `config` block on the `cordis.patch.yml` insert row never reaches the browser half (patch config is host-only); configure bindings in `settings.yaml` instead. ## Install Requirements: Node.js >= 22, pnpm, a local checkout of `deepseek-harness` (dependencies use `link:` to `../deepseek-harness`). ```sh git clone https://github.com/csiroqa/dsh-hotkeys.git cd dsh-hotkeys pnpm install pnpm build # install into web profile (link: this directory) dsh plugin --profile web add link:$(pwd) # POSIX # Windows: dsh plugin --profile web add link:D:\path\to\dsh-hotkeys ``` Restart `dsh web` and hard-refresh the browser (**Ctrl+F5**). ## Usage 1. Open any session and use `Ctrl+Alt+[` / `Ctrl+Alt+]` to switch sessions; `Ctrl+Alt+N` starts a new one 2. Type a draft and press `Ctrl+Enter` to send; `Ctrl+.` stops generation; `Ctrl+Shift+F` forks the session 3. `Ctrl+Alt+A` archives the current session (moves it to the "Archived" group) 4. Run `/hotkeys` to list the current effective bindings 5. Misconfigured bindings print a `[hotkeys]` warning in the browser console ## Compatibility - **Platforms**: Windows / macOS / Linux (Node >= 22) — builds and smoke tests are verified on all three platforms via [GitHub Actions CI](https://github.com/csiroqa/dsh-hotkeys/actions) - Developed against a DSH `0.1.1-rc.2` source checkout and verified on `@deepseek-ai/dsh@0.1.1-rc.2` (npm global / npx install); depends on the `sessions` / `workspaces` public services (declared via bundle `inject`) plus one untyped `conversation.input` runtime face (actions degrade silently when ui-conversation is absent) - Bindings assume a **US keyboard layout** (shift character unshifting); `mod` detection prefers `userAgentData.platform`, falling back to `navigator.platform` - **Known limits**: slash-menu highlight arbitration is held privately by the composer, so `send` submits the draft directly instead of picking the highlighted item; `sendMode: 'auto'` is **identical** to the composer's accelerated Enter (reads the `busyEnter` preference from the `ui-conversation` namespace and resolves with the inverted semantics) - Build: `tsdown` (host `lib/index.js` + browser `lib/client.js`, standard `window.__ModuleLoader__.load` closure-factory format) ## Security notes - **Shortcuts only take effect on the local page** (DSH binds to 127.0.0.1 by default); no network listening - **Binding config lives in `$DSH_HOME/settings.yaml`** (local file); do not expose the DSH port to the public internet - The plugin is installed via `link:` to this directory — rebuild (`pnpm build`) after source changes and hard-refresh ## License **MIT License** (see [LICENSE](LICENSE)). Use, modify, reference, or include it in your own plugin collections — just keep the license notice and credit this repository. ## Related - [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) - Companion plugin: [dsh-schedule](https://github.com/csiroqa/dsh-schedule) (scheduled tasks + status monitoring) - Plugin form reference: [dsh-web-ui](https://github.com/zhu1090093659/dsh-web-ui) (`dsh.bundle.patch` + `dsh.client` declaration + slot registration + tsdown dual-half build)