Skip to content
dsh-market Browse plugins GitHub 中文

levodoubt/dsh-longtext-input

Adds a Long text button to the composer tool row: write or paste the body in an editor overlay, and on save it is written to <workspace>/.dsh-longtext/<timestamp>_<title>.md and an @ path reference to that file is appended to the composer, leaving the conversation free of the long text itself.

Stars ★ 0 Category UI Enhancements Listed 2026-09-15 npm dsh-longtext-input

Install

Inside DeepSeek Harness, with dsh-market

dsh plugin --profile web add dshmarket

Or from the command line

dsh plugin --profile web add dsh-longtext-input

Installing runs third-party code with your own permissions — it can read your files, use your credentials and reach the network. Review the source first, and pin a commit (github:owner/repo#sha) when you can.

Screenshots

README

A DeepSeek Harness plugin that moves long text out of the chat and into a workspace file, leaving a single @ reference in the composer.

简体中文 | English

MIT License

It adds a Long text button to the composer toolbar. Click it, write or paste into the editor, and on save:

writes <workspace>/.dsh-longtext/<YYYYMMDDHHmmss>_<title>.md
appends @.dsh-longtext/<filename>.md to the composer

The point is keeping the chat readable. When you need to hand over a stack trace, meeting notes, or a long draft, the body stays in a file and the conversation is not buried under a wall of text.

DSH's composer has no length limit, so this is not about working around one — it is about getting long prose out of the conversation flow.

Screenshots

The wallpaper and frosted-glass effect in these shots come from other plugins and are unrelated to this one.

Composer with the Long text button The long text editor

Behavior

Item Rule
File name 14-digit local time_title.md, e.g. 20260712153045_meeting-notes.md
Empty title 20260712153045.md (the _title part is dropped)
Title sanitizing Strips / \ < > : " | ? * and control characters, collapses whitespace to _, trims leading/trailing _ and ., caps at 60 characters
Save location .dsh-longtext/ under the session's working directory; created on first use
Existing composer draft Preserved. The reference is appended on its own new line; it is only placed directly when the composer is empty
Reference shape @ + workspace-relative path + one space. Plain text, not a reference chip
Saved body Lives only in the file; the editor keeps no copy
Success notice None. The @ reference appearing in the composer is the confirmation
Shortcuts Ctrl/⌘ + Enter to save, Esc to cancel

It never pre-fills, clears, or overwrites the draft you are writing.

Why the @ reference is plain text

DSH's setDraft() rebuilds the editor as plain-text paragraphs; it does not create reference chips. This plugin does not fake a chip — doing so would require a source registered in the input trigger registry, which is product-internal. The reference is therefore injected as literal text, @.dsh-longtext/xxx.md: the model can resolve that path, and you can delete it at any time.

Install

Requires DSH ≥ 0.1.5-rc.1 (web profile).

From npm (recommended)

dsh plugin --profile web add dsh-longtext-input

This installs a prebuilt artifact: no build step, no runtime dependencies, and it honours the npm registry or mirror you have configured.

From the GitHub source

dsh plugin --profile web add github:levodoubt/dsh-longtext-input

From a local checkout (development only)

dsh plugin --profile web add <path-to-your-checkout>

This installs a link dependency, so edits under lib/ take effect without reinstalling — at the cost of binding your profile to that directory: moving or deleting it makes DSH fail to start. Use one of the two options above for normal use.

Common notes

dsh plugin add automatically appends any dependency declaring dsh.bundle.patch to dsh.profile.bundles, so no config editing is needed. DSH must be restarted afterwards — the Host half only loads on a fresh process.

Version compatibility

This plugin declares the host versions it supports through dsh.compatibility.dsh (>=0.1.5-rc.1), the field the plugin storefront reads to display compatibility.

It deliberately declares no peerDependencies on @deepseek-ai/*: it imports none of those packages and relies only on DSH's own bundle, client-module and slot machinery. DSH validates every @deepseek-ai/dsh* peer range and, on a mismatch, warns that the plugin "may cause crashes or data loss" and requires a per-version exemption. Declaring peers for packages a plugin does not actually depend on would only cost users that warning and that manual step.

Uninstall

The first option is normally enough.

1. Normal removal

dsh plugin --profile web remove dsh-longtext-input

Removes both the profile dependency and the bundle layer. Takes effect on the next DSH restart.

2. Manual fallback (if the above fails, or DSH will not start)

Edit $DSH_HOME/profiles/web/package.json:

  • delete the "dsh-longtext-input" line from dependencies
  • delete "dsh-longtext-input" from dsh.profile.bundles

Then:

cd "$env:USERPROFILE\.dsh\profiles\web"
pnpm install

3. Disable without removing

In $DSH_HOME/profiles/web/package.json, replace "dsh-longtext-input" in dsh.profile.bundles with an object:

{ "id": "longtext-input", "name": "dsh-longtext-input", "disabled": true }

The dependency stays installed but is not mounted. Useful for "get DSH booting first, investigate after".

Diagnosing a startup problem

This plugin sits off the Host's critical boot path — it registers one HTTP route and two UI slots, and every external lookup is guarded — so it should not be able to block startup. If you suspect it anyway:

dsh --profile web --dump-config

That command composes the config tree and exits without starting any service; the # == dsh-longtext-input block in its output is this plugin's row.

Files already written to .dsh-longtext/ are never cleaned up automatically — delete that directory yourself.

Architecture

Half File Responsibility
Host lib/index.js Registers POST /longtext-input/save: validates the session and body, writes the file, returns the workspace-relative path
Client lib/client.js Registers the toolbar button in conversation.input.left and the editor overlay in shell.overlay

Both slots register under their own ids — purely additive, replacing nothing shipped.

Zero build, zero runtime dependencies — both halves are hand-written plain JavaScript. lib/client.js is a pre-built client bundle in window.__ModuleLoader__.load({id, factory}) form. Its id must be the package name, dsh-longtext-input: the boot graph keys every client row by package name, and using the cordis row id instead makes the whole plugin tree fail to load in the browser.

Why the write happens on the Host half

The browser half is sandboxed inside the page and has no filesystem access. The working directory comes from the Session header's cwd — not a path guessed by the client — so switching workspaces cannot write into the wrong tree. The file name is generated entirely on the Host side; the client supplies only a title, and that title is sanitized. Client input is never concatenated as a path segment.

Writes use node:fs/promises deliberately: fs.writeText refuses to create a missing parent directory, and .dsh-longtext/ does not exist on first use.

Wallpaper Engine frosted-glass adaptation

With dsh-plugin-wallpaper-engine installed and a wallpaper active, the editor overlay automatically takes on the same liquid-glass look as the composer, and follows that plugin's glass / glass-opacity / glass-color sliders live.

The implementation is pure CSS with no code coupling:

  • Every selector is scoped to body[data-we-wallpaper], the stable attribute that plugin puts on <body> while a wallpaper is active. With the plugin absent or the wallpaper off, not one of these rules matches and the overlay looks exactly as it does without the plugin.
  • It reuses that plugin's own custom properties (--we-blur, --we-saturate, --we-glass-alpha, --we-glass-color, …). They are declared on <body>, so they inherit into the overlay (which is portalled under document.body) — the sliders drive both surfaces with no coordination between the plugins.
  • Every var() carries a fallback.

Why an element selector is unavoidable: backdrop-filter cannot be expressed as a design token, and DSH ships no glass token. The wallpaper plugin hits the same wall — it targets the composer through [data-composer-card] and its better-sidebar adaptation through [data-dsh-better-sidebar]; this plugin targets its overlay through .dsh-longtext-panel. All three are the same technique.

Development

node scripts/check.mjs

27 offline checks covering: both halves' export shape, the module-loader registration contract (including registration-id-versus-package-name agreement), slot registration, route behavior (including a real write to a temp directory), file-name rules, draft-preservation semantics, button contrast, and the Wallpaper Engine variable contract and rule isolation.

The Wallpaper Engine checks read the installed plugin's source to cross-validate; they skip automatically when it is not installed.

After editing lib/:

  • Client half: bundle changes are detected and hot-loaded — a hard browser refresh is enough, no DSH restart.
  • Host half: needs a DSH restart.

Known limitations

  • The model does not read the body automatically. In DSH an @path is a path reference for the model, not inlined content. The model has to open the file itself; if it does not, just tell it "read .dsh-longtext/xxx.md".
  • The @ reference is text, not a chip (see above).
  • Injection uses the draft as of the save. setDraft is a whole-draft replacement, so the current draft is read and written back in full.
    • The draft comes from a continuous mirror the composer entry keeps of useInput (synced during render, re-read at save time), never a snapshot taken when the editor opened — the latter once wiped a user's text when it read back empty.
    • If you insert a reference chip into the composer while the editor is open, that chip degrades to its text projection on write-back. The window is very short.
  • The draft is not pre-filled. Per design, opening the editor does not carry over what is already in the composer.
  • The frosted glass is an optional enhancement. It applies only while the wallpaper plugin has a wallpaper active.

Relationship to the QwenPaw version

This is a rewrite of qwenpaw-longtext-input for DeepSeek Harness, not a port. The original is a QwenPaw frontend plugin that injects a button into the DOM and forges an <input type="file"> via DataTransfer to push the body into the attachment stream. DSH has a real slot system, and this plugin deliberately takes the "write to disk + @ reference" route instead, reusing none of the original's code. Only the behavior spec is inherited (button placement, title-to-filename mapping, discard confirmation on cancel).

License

MIT

Content from the project README on GitHub ↗

Comments

Comments live in GitHub Discussions. Sign in with GitHub to post or react.