dsh-file-viewer
Curated pickMaintenance: Activeliguobao/dsh-file-viewer
Read-only Web client file-preview panel with image, PDF, CSV/TSV, text, source-code, Markdown, JSON and YAML renderers, large-file range loading, and a provider API for non-local content.
$ dsh plugin add dsh-file-viewerInstall
dsh has no central install command — add this plugin’s entry (documented in its README below) to your profile or patch config, then restart.
How installs work5
stars
0
forks
TypeScript
Language
MIT
License
2026-08-19
Created
2026-09-15
Last push
README
DSH File Viewer
English | 中文
Online: dsh.r2049.cn
A universal, read-only file preview layer for DeepSeek Harness: open and inspect files right inside the web UI — no external application needed.
Preview ≠ Execute. The viewer is strictly read-only: previewing a file never runs shell commands, scripts, macros, or HTML. SVG is rendered through
<img>, and Markdown is sanitized before display.
Supported file types
| Type | Renderer |
|---|---|
| PNG / JPG / GIF / WEBP / SVG / BMP | Image (fit, zoom, pan, dimensions) |
| PDF.js (page nav, page input, zoom, fit width/page) | |
| CSV / TSV | Data table (delimiter auto-detect, sticky header, row numbers, search, sort, column resize, windowed rows, chunked loading) |
| TXT / LOG / OUT / INI / CONF | Text (line numbers, wrap, search, font size, chunk navigation) |
| JS / TS / Python / Go / Rust / Java / C/C++ / C# / Shell / HTML / CSS / SQL / … | Code (highlight.js, read-only) |
| Markdown | Preview (sanitized) + Source |
| JSON / JSONL | Tree (expand/collapse, copy value/path) + Source |
| YAML | Source + parsed Tree |
| anything else | Fallback (metadata + Open externally / Reveal / Copy path / optional Open as text) |
How it works
- Host half (
dist/index.js) registers the/fileviewerauthenticated RPC channel and exposes both afileViewerContentprovider registry and afileViewerHostbounded service for trusted transport plugins. The viewer does not assume content lives in a local folder: other host plugins can register readers for locators such asartifact://run/report.json, object storage, generated output, or remote APIs. A boundary-checkedctx.fsprovider is installed only when that service is available; on the DSH v0.1.2+ package graph it discovers workspace roots fromctx.workspaceRegistry, live session cwd values fromctx.sessions, and native open support fromctx.sessionController. - Client half (
dist/client.js) provides thefileViewerservice (ctx.get('fileViewer')→openFile(path, { line, renderer })) and renders the viewer as aconversation.viewtab beside Chat and Trajectory. It opens from:- produced-file chips in the conversation (
conversation.chat.turnTail, priority -1 — clicking an agent-generated file previews it in-app), or - the "浏览文件 / Browse files" entry added to each workspace row's "…" menu (see the compatibility patch below).
- produced-file chips in the conversation (
- Workspace "…" menu patch (
scripts/patch-workspace-menu.mjs): the workspace browser renders its row menu from a hardcoded list with no slot hook, so this script applies guarded, idempotent edits either to adeepseek-harnesssource checkout (verified throughdsh-v0.1.5-rc.1) or to an installed@deepseek-ai/dsh-client-ui-workspaceclient bundle. It adds abrowseFilesmenu item (zh/en labels), anonSelectbranch callingwindow.__dsfvBrowseWorkspace(workspaceId), and the dictionary keys. It aborts loudly on version drift and can be re-run safely after Harness updates (DSH_HARNESS_SOURCE=/path/to/deepseek-harness node scripts/patch-workspace-menu.mjs). - Large-file strategy:
< 5 MBwhole-file,5–50 MBchunked streaming,> 50 MBhead-only with explicit "Load more / Go to end" navigation. Range reads are capped (8 MiB per call) and text/CSV rows are windowed, so a 500 MB log never lands in browser memory. - Theming: styles use
--dsw-alias-*tokens and match Harness's details panel proportions, so light/dark follow the Harness theme automatically.
Compatibility
dsh-file-viewer v0.3.4 and later support DSH v0.1.1-rc.2 plus the breaking
v0.1.2+ package graph, verified through dsh-v0.1.5-rc.1. On rc2 the host RPC
channel is registered with an explicit loopback authority and workspace
discovery uses the legacy apiProxy fallback. On v0.1.2+ the viewer
additionally uses ctx.workspaceRegistry, ctx.sessions, and
ctx.sessionController. The client metadata depends only on packages shared by
the supported graphs; their transitive dependencies provide the
generation-specific runtime services. On v0.1.2+ the viewer also accepts the
conversation.view focus request and treats its opaque focus value as a file
locator.
DSH and React packages are host-provided optional peer dependencies for this
plugin. This is required by v0.1.2+ profiles, which keep peer auto-install
disabled (autoInstallPeers: false): installing the plugin must not add or
require a second copy of the in-box platform packages. Because npm semver does
not match later prereleases such as 0.1.5-rc.1 through
>=0.1.2-alpha.1 <0.2.0, the peer ranges explicitly include
>=0.1.5-rc.1 <0.2.0.
The Workspace row Browse entry remains a compatibility patch because upstream
@deepseek-ai/dsh-client-ui-workspace has no third-party menu slot. Re-run
scripts/patch-workspace-menu.mjs after updating Harness; the script is
idempotent and aborts on version drift.
Public API
// client side, any web plugin:
const fileViewer = ctx.get('fileViewer')
fileViewer.openFile('/workspace/output/report.csv')
fileViewer.openFile('artifact://run-42/report.csv')
fileViewer.openFile('/workspace/src/main.ts', { line: 125 })
fileViewer.openFile('/workspace/data.bin', { renderer: 'text' }) // force a renderer
Provide content from another host plugin
Register a provider once, then open its locators from any client plugin. The provider owns locator matching, authorization, metadata, and range reads; the viewer owns preview selection, bounded RPC transfer, and rendering.
import type { FileViewerContentRegistry } from 'dsh-file-viewer'
const report = new TextEncoder().encode('{"status":"ok"}')
ctx.inject(['fileViewerContent'], runtime => {
const content = runtime.get<FileViewerContentRegistry>('fileViewerContent')!
runtime.effect(() => content.register({
id: 'run-artifacts',
supports: locator => locator.startsWith('artifact://'),
async stat(locator) {
if (locator !== 'artifact://run-42/report.json') return undefined
return {
name: 'report.json',
mime: 'application/json',
size: report.byteLength,
}
},
async read(locator, { offset, length }) {
if (locator !== 'artifact://run-42/report.json') throw new Error('Not found')
return report.slice(offset, offset + length)
},
}), 'register run artifact viewer')
})
Providers may additionally implement list() for directory-like locators and
openExternal() for source-specific hand-off. register() returns an
unregister function, making provider lifetime follow the supplying plugin.
Trusted transport plugins can inject fileViewerHost and forward an explicit
allowlist of its endpoints. This is how dsh-remote previews files on a Remote
Host: access checks remain owned by the selected File Viewer content provider,
while the transport applies its own authentication, size limits, and method
allowlist. openExternal is intentionally not part of that remote surface.
Browser-only plugins can register the same reader directly on the client service—no host RPC or local path is required:
import type { FileViewerClientService } from 'dsh-file-viewer'
const markdown = new TextEncoder().encode('# Live preview')
ctx.inject(['fileViewer'], runtime => {
const viewer = runtime.get<FileViewerClientService>('fileViewer')!
runtime.effect(() => viewer.registerContentProvider({
id: 'live-preview',
supports: locator => locator === 'memory://preview.md',
async stat() { return { name: 'preview.md', size: markdown.byteLength } },
async read(_locator, { offset, length }) {
return markdown.slice(offset, offset + length)
},
}), 'register live preview')
viewer.openFile('memory://preview.md')
})
Configuration
# cordis.patch.yml / settings
- id: dsh-file-viewer
name: dsh-file-viewer
config:
enabled: true
extraRoots:
- /srv/data # optional extra directories the viewer may read
Development
npm install # (use a reachable registry if npmjs TLS is flaky)
npm run build # declarations + esbuild → dist/types + dist/index.js + dist/client.js
npm run check # tsc (strict) over src and tests
npm test # vitest: mime, renderer, paths, large-file, csv, json, file-service
Install into a DSH profile
# from the repo root (the profile resolves relative specs from your cwd)
dsh plugin --profile web add /path/to/dsh-file-viewer
# compatibility patch: add "浏览文件" to each workspace's "…" menu
# with a v0.1.2+ source checkout:
DSH_HARNESS_SOURCE=/path/to/deepseek-harness node scripts/patch-workspace-menu.mjs
# or against the installed profile package:
node scripts/patch-workspace-menu.mjs
# then restart the web service (preflight on 43124 → 43123), see
# scripts/restart-dsh-web.sh for the safe pattern used in this repo.
Client-only changes hot-reload via dsh-client-hmr; node-half changes need a
web restart. Re-run scripts/patch-workspace-menu.mjs after any Harness
update that reinstalls @deepseek-ai/dsh-client-ui-workspace.
Security notes
- Path validation is enforced host-side on realpath'd targets against allowed
roots (DSH v0.1.2+ workspace paths, live session cwd paths, the host cwd, and
configured extra roots via
fs.contains) by the optional local-files provider. Custom providers are responsible for authorization within their own locator namespace. - Markdown is rendered with
html: falseand sanitized with DOMPurify (scripts, iframes, event handlers andjavascript:URLs removed). - SVG is never injected as HTML — it is displayed through
<img>. - Binary detection: NUL scan + magic bytes; "Open as text" is always an explicit user action.
- Per-renderer error boundaries: a broken PDF/JSON can never crash the Harness UI.
License
MIT
More in UI & Experience
dsh-better-sidebar
by omdsh-dev
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
dsh-tui
by ccch1mneyyy
Claude Code 风格全屏终端 TUI:像素鲸鱼顶栏、双流光大字、实时工作状态行、思考流式展开、双击 Esc 时间回溯、上下文进度条 + TPS 仪表;接入 Agent preset(standard/code/minimal/cordis)、Skills、MCP、Goals、Todos、子代理,/ 命令注册表与随附技能(audit/bug/review/practice/pr_comments/release-notes/vuln-check);纯插件挂载零核心改动。
dsh-tianshu-tui
by huiliyi37
DeepSeek Harness 交互式终端 UI 插件:会话工作区、图片端到端(剪贴板/视觉桥)、slash 下拉、审批卡片、推理可视化,纯展示层
dsh-popout-sidebar
by e2mcc
A sidebar can pop out a separate browser tab (drag it to another monitor)
