dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Draw on a pasted screenshot in the DeepSeek Harness web GUI: pen, arrow, rectangle, ellipse and text stamps, flattened into a PNG attachment at the image native resolution.
Draw on a pasted screenshot before it reaches the conversation — the "paste, mark it up, send" move from Cursor, as a DeepSeek Harness client plugin.
Paste a screenshot into the composer and an overlay opens over the picture. Press the pencil to get
the tools, mark whatever needs pointing at, and press Send: the strokes are flattened into a PNG
at the screenshot's native resolution and the file is filed into the composer as an ordinary
attachment. Esc files the image exactly as it was pasted, byte for byte.
Nothing upstream is patched. The plugin registers into the official slot, locale and Conversation faces of the web client, so it survives DSH upgrades and uninstalls by removing one row.
Ctrl+V with a single image (no text on the clipboard) opens the overlay.
A paste carrying text or several images is left to the shell.Enter inserts,
Shift+Enter starts a new line, Esc cancels the note (not the overlay).Ctrl+Z works too.Enter or the button: strokes are baked into a PNG 1:1 with the source image.Esc or the button: the original file is filed untouched, with no re-encode.The package declares dsh.client (browser half) and dsh.bundle.patch (loader row), so it installs
like any other DSH plugin:
# from a checkout, or from a published copy
dsh plugin --profile <profile> add /path/to/dsh-image-annotate
# after that, reload the page once (a NEW row is picked up when the page's module table is built)
Manual mounting into a profile, which is what this repository was developed against:
# 1. live link so edits are picked up by client HMR without reinstalling
New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\<profile>\node_modules\dsh-image-annotate" `
-Target (Resolve-Path .).Path
# 2. dependency of the profile: ~/.dsh/profiles/<profile>/package.json
# "dsh-image-annotate": "file:<path to this checkout>"
# 3. loader row: ~/.dsh/profiles/<profile>/cordis.patch.yml
# - insert:
# - id: image-annotate
# name: dsh-image-annotate
Rollback = drop the insert row, the dependency and the junction.
| File | Role |
|---|---|
index.js | Host half: an empty apply(), there only so the loader can mount the package and the client-modules scanner can find the dsh.client declaration |
lib/client.js | The whole feature: capture-phase paste listener, overlay portal, canvas, flatten, attachment hand-off |
cordis.patch.yml | The loader row a bundle install applies |
Four decisions worth keeping when editing:
document, before Lexical hands PASTE_COMMAND to
the shell. A "just an image" paste therefore never slips into the rail unannotated, and cancelling
returns the original File to the composer with no re-encode.document.body. The composer card is position: relative and the
app frame carries its own transforms, so a position: fixed layer inside that tree can get stuck
in an ancestor.conversation.createDrafts(sessionId, [file]) registers the draft (and its preview) and
inputActions.addAttachments(ids) puts it in the rail — the same two calls the composer bar makes
for a drop. If the rail refuses (a send is being adjudicated), the draft is released instead of
leaking. When those faces are unavailable the plugin falls back to a synthetic ClipboardEvent on
[data-composer-input], which is the path a real Ctrl+V takes.try/catch, and the standard session hooks are selector hooks
(useInput(selectAttachmentIds), never a bare useInput(), which throws).Strokes hold image coordinates; the canvas only scales them for display, which is why flatten
writes a PNG at the screenshot's native size. Text is a stroke too
({tool: 'text', points: [anchor], text}) with textFontSize(width) = max(10, width·4) image pixels,
so it participates in undo, clear and export like any other mark. The overlay palette is resolved in
JavaScript from the live theme tokens with explicit fallbacks — an empty theme custom property makes
the whole CSS declaration invalid, which is how an overlay ends up drawn but invisible.
# 1. syntax
node --check index.js && node --check lib/client.js
# 2. headless logic (64 checks): paste classification, stroke rasterisation, text stamps,
# the flatten pipeline, the official attachment route plus every fallback, the palette,
# the synthetic paste, slot registration and the diagnostics surface
node tools/smoke.mjs
# 3. every seam this plugin stands on, against a DSH build's own sources
# (--root <@deepseek-ai root>, or let it discover extracted builds and npx caches)
node tools/check-api.mjs --all
# 4. a running GUI is serving THIS revision (needs the host's token URL or its launcher log)
node tools/verify-live.mjs --url "http://127.0.0.1:<port>/?token=<token>"
# 5. end-to-end in a real headless Chromium against that GUI — no model call, no clicking:
# paste -> overlay -> pencil -> stroke -> Send -> draft in the rail -> Esc keeps the original
node tools/live-ui.mjs --url "http://127.0.0.1:<port>/?token=<token>"
# 6. no GUI at hand? stand up a throwaway host (own home, own port) and clean up after
node tools/iso-host.mjs up
node tools/live-ui.mjs --url "<URL printed above>"
node tools/iso-host.mjs down
# 7. the pre-publish gate: no personal data, no network/storage/eval in the payload
node tools/audit-public.mjs
tools/live-ui.mjs drives the real app: it dispatches a paste carrying a real PNG, checks that the
overlay opens with its canvas, presses the pencil, paints a stroke with pointer events, sends, and
asserts the composer rail gained a thumbnail; then it repeats with a broken image to prove the entry
survives a failure, and finally exercises the text stamp. It sends no prompt.
The plugin payload (index.js, lib/client.js, cordis.patch.yml) is deliberately boring, and
tools/audit-public.mjs fails the build if that changes:
fetch, XMLHttpRequest, WebSocket, sendBeacon, no dynamic import.localStorage, sessionStorage, indexedDB, no cookies: the drawing state
lives in React state and dies with the overlay.eval, no new Function, no innerHTML; all UI goes through
React elements, and user text is passed to canvas.fillText, never parsed as markup.<canvas>, and the resulting File is handed to the same
attachment path a normal paste uses. Nothing leaves the page on the plugin's initiative; whatever
the shell then does with an attachment is the shell's own behaviour.tools/ (read-only: the boot graph and the served bundle), and only at the URL you pass.Personal data: the repository contains no user paths, no e-mail addresses, no tokens, and no
screenshots. tools/audit-public.mjs scans both the payload and the whole tree for those, so a
re-run is a one-liner instead of a promise.
tools/check-api.mjs asserts the ten seams the plugin uses (slot declaration and scope, the
inputActions provide face, the shell's addAttachments, createDrafts, composer data-*
markers, the paste command's clipboard intake, the locale registry, standard-props spreading, the
selector-hook contract of the standard hooks, and the dsh.client module-table contract).check-api instead of half-working.applyStrokes, so adding another is cheap.Esc, Ctrl+Z and Enter; while a text field is open it hands
those keys to the field.window.__dshImageAnnotate in the page console answers "what happened":
mounted (the plugin applied), opened (pastes intercepted), lastRoute
(draft | paste | failed), lastName / lastBytes (what was filed), lastStroke,
lastStrokes, and lastError (decode, decode-timeout, insert, flatten: …, overlay: …).
If a paste seems to be ignored, check document.querySelector('[data-slot-error]') first: a value of
conversation.input.right means the slot entry was retired by an earlier crash, and only a page
reload brings it back. tools/live-ui.mjs sections 8–9 keep that from regressing.
MIT — see LICENSE.
Плагин для веб-интерфейса DeepSeek Harness: вставляете скриншот в композер (Ctrl+V), поверх
картинки открывается оверлей, карандаш раскрывает инструменты (карандаш, стрелка, прямоугольник,
эллипс, текст; 6 цветов, включая белый; 3 размера). «Отправить» впечатывает штрихи в PNG в
натуральном разрешении и вкладывает файл в диалог; Esc вкладывает исходный файл без
перекодирования. Уже вложенную картинку можно дорисовать — карандаш в панели композера.
Ничего в самом DSH не патчится: только официальные точки расширения. Проверки и аудит — командами
из раздела «Verify it yourself».