dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Input Light — Tab cycles the DSH permission preset (Read Only ⇄ Workspace Write), ⌘S resumes interrupted work with two-step confirm, and the permission chip is color-coded by preset. Client-only DSH plugin.
Composer-side conveniences for DeepSeek Harness (dsh):
cycle the permission preset with Tab, resume interrupted work with two taps of ⌘S,
color-code the permission chip by preset, and show the workspace path in the composer placeholder.
Client-only plugin, zero config, bilingual (en / zh).
| Feature | Trigger | Behaviour |
|---|---|---|
| Cycle permission presets | Tab | Read Only ⇄ Workspace Write — never auto-enters Full access / Auto |
| Resume interrupted work | ⌘S ×2, or the ↻ button next to the chip ×2 | Sends 你的工作中断了,请你继续。 |
| Preset colors | automatic | Read Only = amber, Workspace Write = blue, Full access = red |
| Workspace path in the placeholder | automatic | The composer placeholder shows the workspace's full path — or the host's own text when no path is available |
Tab cycles the permission presetRead Only ⇄ Workspace Write
Why only two presets? As a command, /permission <preset> executes immediately, without
confirmation (only picking through the visible control asks you to acknowledge risk).
So the cycle deliberately stays inside the two safe presets — use the UI to reach Full access.
Pressing Tab while in a dangerous preset (Full access / Auto) falls back to Read Only.
⌘S resumes interrupted workSends a fixed phrase: 你的工作中断了,请你继续。 ("Your work was interrupted, please continue.")
It submits through the host's own input surface, so it behaves exactly like pressing Enter:
ctx.sessions.binding(sessionId) // → session binding
→ ctx.uiSession.sourceFor(binding) // → that session's provide channel
→ .props.inputActions // → { setDraft, submit, … }
→ setDraft(RECOVERY_TEXT); submit();
The result is a real user/message in the session log (source.kind === "user"), not a call that
resolves while nothing happens.
⚠️ Counter-example, for the record:
ctx.remote.session.prompt(...)is an ACP protocol method. It resolves, but it does not produce a user message (0 entries in the session log, measured). Do not switch back to it.
Two-step confirmation (anti mis-tap) — shared by ⌘S and the button:
| Step | ⌘S | Button | Effect |
|---|---|---|---|
| First | tap once | click once | Enters confirm state: the button turns amber ✓, the tooltip becomes "press again to confirm" — nothing is sent |
| Second (within 3 s) | tap again | click again | Sends |
⌘S once, then click the button" also confirms.↻.⌘ S keycaps).| Preset | Color | Host theme variable |
|---|---|---|
| Read Only | amber | --dsw-alias-state-warn-primary |
| Workspace Write | blue | --dsw-alias-state-business-primary |
| Full access | red | --dsw-alias-state-error-primary |
Theme variables (not hard-coded colors) ⇒ follows light/dark automatically. The chip's original inline styles are saved and fully restored on unload — no residue.
The placeholder is replaced by the current workspace's full path:
/Users/you/Develop/my-project
sessions.list.getSnapshot().byId[sessionId].cwd — the same field ui-conversation uses for
relativizeToCwd). The sidebar DOM carries no full path (the workspace row's title is only a
display name), so there is deliberately no DOM fallback.cwd), the
plugin puts the host's own text back. That text is read from the DOM and never hard-coded, so it
still follows the language and the session state (parentOffline / unavailable / …).white-space: nowrap; text-overflow: ellipsis, so an over-long path is ellipsised by the host's own
rule and the composer keeps its native height.textContent would swap the node out, stranding React's fiber — the placeholder would then
freeze the next time the host changed its text. A fake-DOM test pins this.Both shortcuts are registered with the host shortcut service, so they appear in the shortcut panel
(⌘/):
| Command id | Default | Action |
|---|---|---|
input-light.cycleMode | Tab | Cycle permission mode |
input-light.recover | ⌘S | Resume interrupted work (press twice) |
Change the keys in that panel — no code changes needed.
plugin_manager action=install_bundle
target=<path-to>/dsh-input-light
See INSTALL.md. In short: register the bundle in your DSH profile, then restart DSH (a newly added bundle is only picked up at startup; later code edits only need a page refresh).
Read index.md for the full engineering log. The four constraints that caused every rewrite along the way:
External client plugins can only render into root-scoped slots.
Seats like conversation.input.* are scope: "session": a plugin registration succeeds but
never renders (5/5 seats measured mounted: false). This plugin uses shell.overlay
(kind: "list", scope: "root").
Shortcuts must go through ctx.shortcuts.
Raw DOM keydown never sees Tab or ⌘S — the host's keyboard layer consumes them before the
DOM (plain characters arrive; those two never do). Registration also requires
regions: ['page', 'editable'], otherwise commands do not fire while the composer is focused.
Cordis services and their sub-namespaces must be injected.
Accessing an undeclared one throws (cannot get property "remote.commands" without inject).
Registration must be idempotent.
apply runs more than once; re-registering a command id throws Duplicate shortcut command, and
when that happens before the second command is registered, neither is registered — silently
breaking a shortcut that used to work.
node test/logic.test.mjs # 96 pure-logic cases, no DSH needed
In the browser console:
__dshInputLight.probe() // { chipFound, preset, sessionId, tooltip }
__dshInputLight.state() // result of the last cycle / recover
__dshInputLight.confirmStage() // 'idle' | 'confirm'
__dshInputLight.cycle() // cycle once, manually
__dshInputLight.confirm() // run the two-step confirm (same path as ⌘S)
__dshInputLight.send() // send without confirming (self-check only)
__dshInputLight.placeholderProbe() // { found, path, base, composed } — the two-line placeholder
__dshInputLight.restoreChip() // restore the chip's original styling
MIT © 2026 luzonghao