Back to home@wu81313-lab

dsh-image-studio

Inline image card for DeepSeek Harness conversations: a bottom-left edit button that annotates, erases through a generated RGBA mask, and crops or resizes, then hands the edit back to the agent.

Stars
0
Language
JavaScript
Created
Sep 10, 2026
Updated
Sep 10, 2026
GitHub repo

Introduction

dsh-image-studio

A DeepSeek Harness plugin that shows a generated image inside the conversation, with an 编辑 (Edit) button pinned to the image's bottom-left corner. The button opens an editor with annotate / erase / resize; pressing 生成 writes the result back to disk as real PNG files and returns it to the agent as a new message, so the next turn is an ordinary image-editing request.

It was written alongside a gpt-image-2-5-sunburst skill, but the hand-off is plain text plus file paths, so any image generation workflow can consume it.

中文说明见 README.zh.md.

What it adds

PieceRegistration
sunburst_image_card toolHost: ctx.tools.register
Image card with the edit buttonClient slot tool.call.toolview, key sunburst_image_card
Editor overlayClient slot shell.overlay, id sunburst-image-editor
GET /dsh-image-studio/image?path=…Host route: streams the image bytes (no base64 through RPC)
POST /dsh-image-studio/stageHost route: writes the editor output, returns the hand-off message

The agent calls the tool after a generation finishes:

sunburst_image_card(path="<the path the script printed>", prompt="<the prompt used>",
                    model="gpt-image-2.5-sunburst", size="3840x2160")

The card renders the file through the plugin's own same-origin route, so multi-megabyte 4K PNGs load like any other image on the page.

The three editing tools

ToolThe user doesOutput
标注Draws freehand strokes, rectangles or arrows, with a colour picker, brush width, undo and clear.<stem>-edit-<stamp>-annotated.png — the original with the marks baked in.
擦除Paints over whatever should disappear.<stem>-edit-<stamp>-mask.png — an RGBA PNG at the original image's size whose transparent pixels are exactly the painted region (opaque black elsewhere).
调整尺寸Drags a crop box with aspect presets (free, 1:1, 3:2, 2:3, 16:9, 9:16), four corner handles, and an editable output size.<stem>-edit-<stamp>-crop.png plus a target WxH.

The output size is validated against the PackyAPI image rules while the user types: width and height multiples of 16, longest side ≤ 3840, aspect ratio ≤ 3:1, total pixels between 655360 and 8294400.

Press 生成 and the plugin:

  1. writes whichever of the three files the user produced next to the original image;
  2. composes a message listing the original path, the produced paths, the target size and the user's own wording, ending with the reminder that a mask must match the size of the base image — so a crop and an erase are done in separate passes;
  3. fills the composer with that message and submits it, so the agent picks the work up in the next turn.

Install

dsh plugin --profile web add github:wu81313-lab/dsh-image-studio

dsh plugin --profile <name> … proxies to pnpm inside that profile, and the CLI writes the package into both dependencies and dsh.profile.bundles of the profile's package.json — that second half is what makes DSH load it as a plugin bundle, and what makes it show up as installed in the plugin market.

A bundle's layers are composed at boot, so restart DSH once after installing, and refresh the browser page so the client bundle is picked up. The plugin's own cordis.patch.yml is what mounts the entry:

- insert:
    - id: sunburst-image-card
      name: "dsh-image-studio"

To mount it without a restart, put that same insert in $DSH_HOME/profiles/<profile>/cordis.patch.yml instead: the web profile reloads that file live (patchReload: live), the Host half (the tool and the two routes) activates immediately, and a page refresh loads the client bundle. Do not use both at once — dsh.profile.bundles and a patch-file insert must not mount the same entry id twice.

Requirements

  • DeepSeek Harness with the Web GUI.
  • No npm dependencies: the Host half imports Node builtins only, and the client half takes react from the page's module table (declared as an optional peer dependency).
  • The Web profile, because both halves are Web-platform: the card is a client slot registration and the image/stage routes are registered on ctx.webServer.

Notes and limits

  • The mask is always generated at the original image's size — that is what masked inpainting expects, and it is why the hand-off message tells the agent not to combine a crop with a mask.
  • GET /dsh-image-studio/image serves local files, restricted to image extensions (.png .jpg .jpeg .webp .gif .bmp .avif). The route answers on the same origin as the GUI.
  • POST /dsh-image-studio/stage accepts a JSON body with data URLs and writes the decoded bytes with fs.writeFileSync; the body cap is 128 MiB.
  • The card is keyed on the tool name, so older cards in an existing conversation re-render as soon as the plugin is installed.
  • fs is only used for statSync on the tool's path argument; nothing else is read.

License

MIT — see LICENSE.