xuender
dsh-history
Recall and re-run the current session's command history with ↑/↓ keys in the DSH Web composer.
- Stars
- 2
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
English · 简体中文
dsh-history — DSH Web command history plugin
Remembers the current session's command history in the DSH Web GUI: with the composer input focused, press ↑ / ↓ to step through previously sent commands, edit them, and press Enter to re-send.
✨ Features
- Recording — every message you send in the current session (plain messages and
/xxxslash commands) is recorded automatically, isolated per session. History is derived from the session log (user/commandnodes), so it is rebuilt automatically after a page reload — no extra storage needed. - ↑ — step back to the previous command; keep pressing to go further back. The first press saves the draft you were editing, and pressing ↓ afterwards returns to it.
- ↓ — step forward to a newer command; from the newest entry, one more ↓ returns to the saved draft.
- Edit & re-run — a recalled command lands in the composer draft; edit it directly and press Enter to re-send.
- Safe fallbacks — while a
/or@trigger menu is open, during IME composition (e.g. Chinese input), when the composer is busy/locked, or when the input is not focused, ↑/↓ keep their native behavior and never interfere.
📦 Installation
The plugin is a standard DSH static plugin (see the develop/basic tutorial). The entry name must be the bare package name dsh-history, and the package directory must be resolvable from both resolution trees (the host loader resolves the bare name from the DSH installation's node_modules; the client-modules scanner resolves dsh-history/package.json upward from the profile directory), so two symlinks are required:
# 1) profile side (client-modules scanner): $DSH_HOME defaults to ~/.dsh
ln -s /absolute/path/to/dsh-history ~/.dsh/profiles/node_modules/dsh-history
# 2) install side (host loader bare-name resolution): point to the dsh installation's node_modules
ln -s /absolute/path/to/dsh-history /home/ender/.npm/_npx/1e7f6d9597241db0/node_modules/dsh-history
Then launch with the patch:
dsh web --patch /absolute/path/to/dsh-history/cordis.yml
Or merge the cordis.yml contents (the insert stanza) into $DSH_HOME/profiles/web/cordis.patch.yml to load it permanently.
🗂️ Directory layout
dsh-history/
├── package.json # dsh.client declaration (platform: web) → scanned into __DSH_BOOT__ by client-modules
├── cordis.yml # patch: inserts the plugin entry into the loader
├── lib/
│ ├── index.js # host half: placeholder only (the loader needs a resolvable module)
│ └── client.js # browser bundle: history recording + ↑/↓ navigation + dock indicator
├── scripts/
│ └── smoke-test.mjs # Node smoke test (npm test)
├── README.md # this file (English)
└── README.zh-CN.md # 简体中文版
⚙️ How it works
- Client-module mechanism — the package declares
dsh.client: { platform: "web" }withexports["./client"]pointing atlib/client.js;dsh-client-modulesthen bundles that entry intowindow.__DSH_BOOT__and serves it at/plugins/<entry>/client.js. The browser side registers the plugin throughwindow.__ModuleLoader__.load({ id, factory }). - Recording — a component registered on the
conversation.input.dockslot subscribes to the session snapshot (useSession((s) => s.nodes)) and incrementally scansuser/commandnodes byseq(the list is seq-ascending and untruncated), collapsing adjacent duplicates, capped at 100 entries, stored in a module-levelMap<sessionId, …>. - Navigation — a capture-phase
keydownlistener ondocumenttakes over ↑/↓ only when the focus is inside[data-composer-card], no trigger menu is open (claimis empty), not composing IME input, no modifier keys are held, and the composer is not busy — writing the draft viainputActions.setDraft(text). The editing check returns you to the live position automatically after you type. - Indicator — while browsing, a small “history n/m” badge is shown above the composer.
⚠️ Limitations
- History is per-session and in-memory (rebuilt from the session log after a page reload, so nothing is lost within a session; it is discarded when the browser closes).
- Slash commands are recalled as
/name args(fromcommandnodes). - Image-only messages (no text) are not recorded.
🤝 Community
- Contributing Guide — how to report bugs, request features, and open pull requests (including beginner-friendly
good first issuetasks). - Code of Conduct — the expectations we hold for everyone in this community.
- Security Policy — how to report a vulnerability privately.
- Support — where to ask questions (Issues vs. Discussions).