DSH Media Preview
File previews DeepSeek Harness is missing: seekable media, archive listings, a real image editor, and one-click send to the conversation
中文 · Changelog · Apache-2.0
DSH Media Preview is a community-maintained DeepSeek Harness (DSH) plugin, not an official DeepSeek AI product.
This document describes 1.0.0. See the changelog for released changes.
DSH's built-in preview covers text, code and PDF only: no video or audio player, no archive listing, no binary view, images are read-only, and anything above 20 MiB fails outright. This plugin fills those gaps additively — it claims only its own extensions and leaves every other file to the built-in preview.
Install
# install into the web profile (use --profile desktop for the desktop app)
dsh plugin --profile web add @mileioo1/dsh-media-preview
Restart DSH once after installing; the plugin registers its five preview bodies and the details panel during boot.
Features
Video and audio that actually seek
Playback goes through the plugin's own /media-preview/raw, which implements Range / 206 Partial Content / 416, so the scrubber moves freely, playback rate works, and full screen works — it no longer snaps back to the start. The source is the loopback origin (http://127.0.0.1:<port>/media-preview/raw?...) whenever it is reachable; only then does it fall back to a whole-file blob:, and both paths have an error fallback.
- Video: mp4 mov m4v mkv webm avi flv wmv mpg mpeg ts 3gp ogv
- Audio: mp3 wav flac ogg oga opus m4a aac wma aif aiff amr mid midi, with sample rate, channels and bitrate
Images: a Windows-Photos-like editor
Zoom (wheel / + - / fit / 1:1), rotate, flip, crop, brightness / contrast / saturation, grayscale, invert, freehand annotation — and numbered notes: click 备注, then click anywhere on the picture to drop a red numbered pin; a matching text field appears under the image and the number is baked into the exported PNG.
- Save modes: "save as" (
name.edit-<timestamp>.png) or "overwrite + backup" (name.png.bak-<timestamp>first, then an atomic replace) - The editing canvas is capped at 4096 px on its longest side; oversized images are edited at working size with a notice, instead of allocating gigabytes of bitmaps
- Notes can be deleted one by one
Archives, binaries and the details panel
- Archives (zip jar tar gz tgz bz2 xz 7z rar): entry table with name / original size / compressed size / mtime plus a total
- Binaries (exe dll so dylib bin dat db sqlite wasm class pyc psd apk ipa msi…): hex view, paged at 64 KiB
- Details panel: name, location, MIME + extension, byte size, three timestamps, permission bits, image dimensions and EXIF, media duration / container / bitrate / codec / frame rate, archive entry count and unpacked size, SHA-256 on demand, and one-click copy of the path or the JSON
Send to conversation
The editor's send button exports the current canvas (all edits and notes included) as a real PNG attachment in the active session's composer: the conversation service creates a draft attachment, then its id goes to the input's InputActions.addAttachments. When the composer is busy the plugin says so instead of failing silently. Other previews insert a file reference instead.
Security and permissions
The plugin only needs "read files, write images, serve loopback HTTP":
- Every
/media-preview/*route serves loopback or same-origin requests only;Origin: nullis rejected raw/savepaths must live inside the session workspace root orDSH_HOME, otherwise403 path outside the workspace(code: OUTSIDE_ROOT); relative paths additionally get a traversal check (ESCAPE)- Saves are atomic:
*.tmp-<pid>-<rand>→fsync→rename, so a crash never leaves half an image behind; timestamps carry milliseconds, so back-to-back saves cannot collide - Archives are read incrementally (zip reads only the trailing central directory, tar skips payloads, tgz aborts past 96 MiB) — no whole-archive buffering
- No outbound network requests, no uploads
Compatibility
- DSH
>=0.2.0-rc.1 <0.3.0-0(developed and verified on0.2.1-rc.3) - Node
^22.19.0 || >=24.0.0 - Platform: DSH Web and the desktop app (
dsh.client.platform: "web")
Known limits
- Archives are listed, never extracted
- 7z / rar listing depends on tools available on the machine and may show "unsupported"; zip / tar / gz are stable
- The image editor has no non-destructive layers — saving bakes pixels
- No video thumbnails (to keep long jobs out of the preview path)
- Chat image thumbnails are now rendered by this plugin, so the built-in read-only lightbox no longer appears; clicking opens the editor
- Images opened from chat are not on disk yet, so saving creates a new file; overwriting applies to files inside the workspace only
Architecture (for contributors)
Host side (lib/host.js) registers four routes:
| Route | Purpose |
|---|---|
GET /media-preview/info |
metadata + image dimensions + ffprobe media info + archive listing + optional SHA-256 |
GET /media-preview/raw |
raw bytes with Range / 206 / 416 / HEAD, ETag and 304 |
POST /media-preview/save |
image save (save-as / backup-then-overwrite, atomic write) |
GET /media-preview/origin |
tells the client the loopback origin for direct media loading |
Client side (client.v12.js, a single hand-written bundle, no build step): registers five implementations through documentPreviews.register(...) with priority: "external" so they win over builtin, mounts one body per implementation into the keyed sidebar.right.tab.document slot, and adds the details action, the details overlay and the chat image editor.
Development
# syntax check + pack preview
npm run verify
The test scripts live in the development workspace (media-samples/): host-unit.mjs (14 host checks: zip/tar/tgz listings, large-zip trailing directory, absolute path allowed, OUTSIDE_ROOT / ESCAPE rejected), smoke-interact.mjs (client static assertions) and toolkit-test.mjs (info + Range regression over 19 real files).
Release
Releases go through OIDC Trusted Publishing — no npm token is stored in the repo or on any machine:
npm version patch # or minor / major; bumps the version and creates a tag
git push --follow-tags # pushing the tag triggers the publish
.github/workflows/release.yml then upgrades npm to ≥ 11.5.1, runs npm run verify, and finally npm publish --provenance --access public.
GitHub Actions exchanges a short-lived OIDC token for publish rights and attaches a provenance attestation, so no NPM_TOKEN secret is needed.
Trusted publisher bound on npm: repository mileioo1/dsh-media-preview, workflow release.yml, permissions "publish + manage dist-tags".
Uninstall
dsh plugin --profile web remove @mileioo1/dsh-media-preview
License
Apache-2.0 © 2026 MichengAI
No comments yet. Be the first to write one.