Visual Sandbox puts your UI in the right half of DeepSeek Harness and turns review into pointing. Hover a block, see its selector, click to pin it, type one line — the agent gets an exact target instead of a paragraph of description.
It renders workspace HTML in four honest frames, lets you inspect real DOM elements, and queues every annotation until you send it. The agent then edits only the blocks you touched.
Highlights
| Studio header | The right panel opens on a three-field studio header: Device & Type, Version, and Source (Workspace or URL). A second row holds Inspect, Edit UI, Select (Element or Region), Preview scale, and Send All. |
| Four frames | Responsive (fluid), Mobile 390×844, Tablet 768×1024, Desktop 1024×720. Fixed frames fit to the available width and scroll vertically when needed, so Mobile is shown near actual size instead of shrinking to fit the height. Frames stay centered until they genuinely overflow the canvas. |
| Preview scale | Choose Fit, 50%, 75%, 100%, 110%, 120%, 125%, 150%, or 200%. Fixed frames pan and scroll when they exceed the canvas, so a mobile prototype can be reviewed at actual size or larger. Responsive stays full-bleed. |
| Edit UI | Hover outlines with live selector tags, click to pin, popover for the note. Enter sends, Shift+Enter breaks the line. |
| Surgical by design | [#1 mobile: button.seg] plus box, computed styles, HTML and text reach the agent with a standing rule: change only these targets. |
| Auto-detected sources | The newest workspace .html/.htm file loads itself; a restored source that no longer exists falls back to the newest discovered file instead of showing a 404. Localhost URLs work for dev servers, Expo web, Flutter web, Tauri or Electron renderers. |
| Open by default | The panel reveals itself when a Session opens; the sparkle mark toggles it shut and open again, always landing on the newest preview. |
| Version field | A real Version dropdown in the header; workspace files default to the latest render and can flip to an older one. With no history, the field is disabled and shows Latest. |
| DeepSeek blue | The studio accent, active controls, Send All gradient, empty-state art, hover highlight and focus rings use DeepSeek blue instead of purple. |
Install
Requires DeepSeek Harness (desktop app or dsh web) with a working profile.
The plugin ships as a bundle package — install it from your clone, not from npm.
git clone https://github.com/Surenkh/dsh-visual-sandbox.git
Then register it in ~/.dsh/profiles/<profile>/package.json:
{
"dependencies": {
"@local/dsh-visual-sandbox": "link:../../path/to/dsh-visual-sandbox"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"@local/dsh-visual-sandbox"
]
}
}
}
A git dependency works too, if you would rather not keep a local clone:
"@local/dsh-visual-sandbox": "github:Surenkh/dsh-visual-sandbox"
cd ~/.dsh/profiles/<profile>
pnpm install
Restart Harness and refresh the app page once so the client half loads. The plugin then appears as Visual Sandbox in the right sidebar's guide.
Upgrading:
git pull,pnpm install, restart, refresh.
Use it
- Load a source. Type a workspace path such as
index.html, pick one from the chevron list, or pastehttp://localhost:3000. Press Enter. - Pick a frame. Use Device & Type for Responsive, Mobile, Tablet or Desktop. Use Preview scale to enlarge a fixed frame (100%, 110%, 120% and above); the canvas scrolls when the prototype is larger than the panel.
- Switch to Edit UI. Choose Element or Region in Select. Hover a block — it outlines and names its selector. Click it.
- Write the note and press Enter. Repeat for each block that is wrong.
- Send All, add context in the chat, and let the agent make the surgical edit.
The panel can also be opened from the sparkle mark — the same button closes it again — or by
the agent through open_visual_sandbox. Opening always restores the newest preview for the
Session, so the workbench never lands empty.
Versions
The Version field is always part of the studio header. For a workspace file it lists that file's real render history — the first load, each agent refresh, each re-read from disk — newest first, with latest selected by default. Identical documents fold into the previous entry instead of piling up. Picking an older entry swaps the preview immediately, without touching the file on disk.
A URL preview, or a file that has no history yet, leaves the field inactive and disabled with the text Latest. Snapshots live in Host memory for the process lifetime, capped at 8 versions / 8 MB per Session; documents larger than 2 MB stay previewable but are not recorded.
What an annotation records
| Field | Content |
|---|---|
id + note |
Stable number and the text you typed |
platformMode + viewport |
Which frame it was measured in, e.g. mobile 390x844 |
selector |
Unique CSS path (#id when unique, otherwise tag:nth-of-type chains) |
html / text |
Cleanly truncated outerHTML and visible text |
box + normalized % |
Pixel rectangle plus x/y/width/height inside the app window |
styles |
Layout, color, typography, border, flex/grid and shadow values |
Cross-origin URL frames cannot be inspected, so there you drag a rectangle instead — the same numbered pin and note flow applies.
Sources
- Auto-detected workspace HTML — scans the workspace (skipping
node_modules,.git,.next,.nuxt, caches), lists newest first, loads the newest when nothing is active. - Local URL — any
http:///https://preview, including dev servers and native-webview targets.
Workspace previews inline local stylesheets, scripts, images, media, fonts and CSS
url(...) assets into one self-contained document; CSP and <base> tags are stripped.
Module imports that need the original server may still fail — use a live URL for those.
Expand Narrow Containers strips hardcoded mobile-demo max-width wrappers so a
mobile-first page fills the wider frames. Workspace previews only.
Agent integration
| Tool | Purpose |
|---|---|
open_visual_sandbox |
Reveals the workbench, optionally with a path, url or platformMode. |
set_sandbox_platform_mode |
Switches the preview frame from the agent. |
get_visual_annotations |
Returns the queue as structured targets. |
clear_visual_annotations |
Empties the queue after the edit lands. |
refresh_visual_sandbox |
Reloads the preview from disk. |
Every Session carries a prompt section with two mandatory preview rules — never draw a fake bezel, Dynamic Island or status bar in the previewed page, and re-open/refresh the sandbox automatically when a previewable file changes.
While annotations exist, the section also injects each target as
### Annotation #N with its selector, styles and text, plus the surgical-edit rule: modify
only those targets, then call refresh_visual_sandbox.
Architecture
client.js ── right sidebar tab ── studio header · frames · versions · preview iframe · queue
└─ header sandbox mark ─ toggle the panel
preview iframe (sandboxed, opaque origin)
└─ injected inspector ── postMessage ──▶ panel
index.js ── /api/plugin/visual-sandbox/{state,render,discover}
── host tools + per-agent prompt section + workspace inliner
── version snapshots (sha-256 deduped, capped)
| File | Role |
|---|---|
index.js |
Host half: Session state, the three routes, the five tools, prompt section, HTML inliner, file discovery, version snapshots. |
client.js |
Browser half: frames, studio header, version field, dotted canvas, inspector bridge, annotation tray, header toggle, host sync. |
cordis.patch.yml |
Registers the plugin under the dsh-visual-sandbox bundle id. |
locale/{en,zh}.json |
Plugin inventory title and description. |
icon.svg |
The mark used by the sidebar guide. |
Safety and limits
- Workspace previews run in a
sandboxiframe with an opaque origin — no access to Harness storage, cookies or the host page. The inspector talks only throughpostMessage. - Routes are authenticated Host routes on the shared
/apichannel, and workspace roots are validated before any file is read. - Captured selectors, HTML, text and styles reach the agent as untrusted page data, never as instructions.
- Limits:
4 MBsource ·12 MBinlined assets ·1 MBper asset ·300discovered files at depth8·30annotations ·12 000characters of prompt data. - Annotation state is per Session and lives in Host memory for the process lifetime.
Development
No bundler, no transpiler. Edit client.js or index.js and reload the Harness page.
git clone https://github.com/Surenkh/dsh-visual-sandbox.git
# link it into a profile, edit, reload — repeat
Ship a change by bumping version in package.json, adding a CHANGELOG.md entry and
tagging the commit.
License
MIT © 2026 Suren Khachatryan
No comments yet. Be the first to write one.