Skip to main content
I

dsh-copy-document

ionaryu/dsh-copy-document

Adds a whole-document copy button to the DeepSeek Harness sidebar editor header, right of Save

Install

dsh plugin --profile web add github:ionaryu/dsh-copy-document

README

dsh-copy-document

Summary

The right sidebar's file editor shows a document one screen at a time; getting the whole thing into another tool means selecting past the viewport or scrolling to load every page. This plugin adds one control to that editor's header, immediately right of Save: it copies the document's entire text to the clipboard in one click.

It reads through dsh-better-sidebar's own /sidebar/api/fs.read route (and pages with fs.readPage above the editor's 5 MB threshold), so the session scope, the browser-trust fence, and the encoding detection stay owned by that plugin.

Requirements

  • DeepSeek Harness with the dsh-better-sidebar plugin mounted (the editor header this button joins belongs to it). Without that plugin there is no header and the button never appears.
  • A secure browser context for the async Clipboard API; any modern build of the Web GUI qualifies on localhost.

Install

Install it from the repository into a profile, then restart the harness:

dsh plugin --profile web add github:ionaryu/dsh-copy-document
dsh --profile web --dump-config   # shows a "# == dsh-copy-document" layer

The repository commits its built lib/, so this install needs no build step and no allowBuilds entry — pnpm fetches a package that is already runnable. An unpinned reference follows main; a later add of a newer commit updates it.

From a local checkout — the path used during development:

dsh plugin --profile web add file:/E:/dsh-plugins/dsh-copy-document

Manual install (what that command does):

  1. Add "dsh-copy-document": "github:ionaryu/dsh-copy-document" to the profile's package.json dependencies.
  2. Append "dsh-copy-document" to dsh.profile.bundles.
  3. Run pnpm install in the profile directory, then restart.

What the button does

StateIconMeaning
Idlecopy glyphCopies the whole document on click
Working—The button is disabled while the read and clipboard write are in flight
CopiedcheckThe document's text is on the clipboard (1.2 s)
UnavailablewarningThe document is binary — there is no text to copy
FailedwarningThe read or the clipboard write failed; the console carries the reason

The button is placed right of Save. Documents the editor shows read-only (above the 5 MB editable threshold) have no Save control; there it sits right of Refresh, in the same group.

Known limitations

  • The clipboard gets the file on disk, not the editor buffer. An unsaved draft is not copied; save first (Ctrl/Cmd+S) and copy afterwards.
  • Binary documents have nothing to copy. Images, PDFs, and office documents report Not a text document.
  • Placement is DOM-based. dsh-better-sidebar exposes tab and file-viewer registration only, with no toolbar extension point, so this plugin attaches to the rendered header (recognised by its path input and file-tree toggle) and re-attaches after every re-render. A future release that changes that header's structure can drop the button; nothing else in the editor is touched.
  • One active session. The read is scoped to the sidebar's active session, which is the session whose file the visible editor belongs to.

Development

pnpm install
pnpm run build     # writes lib/index.js and lib/client.js

pnpm 11 prints ERR_PNPM_IGNORED_BUILDS for esbuild's install script. It is advisory: esbuild's platform binary arrives as a regular optional dependency, and pnpm run build works without the script. Run pnpm approve-builds and pick esbuild to clear it.

A file: install is a copy in the profile's node_modules, not a link: after rebuilding, run pnpm install in the profile directory again (or dsh plugin --profile web add file:/E:/dsh-plugins/dsh-copy-document), then restart the GUI. A running GUI keeps the bundles it booted with. A github: install pins a commit, so rebuilding locally and pushing needs a fresh add to update the profile copy.

lib/client.js is wrapped in the client module system's registration protocol (window.__ModuleLoader__.load({ id, factory })) and imports nothing, so no module-table dependency is declared.

Related plugins