DeepSHPlugins
dsh-file-viewer 项目预览
GITHUB / COMMUNITY PROJECT

dsh-file-viewer

UI 增强TypeScriptMIT

Web 客户端只读文件预览面板,支持图片、PDF、CSV/TSV、文本、源代码、Markdown、JSON 与 YAML 渲染、大文件分段读取,以及面向非本地内容的 provider API。

6 Starsliguobao 更新时间 2026-09-10
访问 GitHub 仓库
dsh-file-viewer 项目预览

项目介绍

中文

Web 客户端只读文件预览面板,支持图片、PDF、CSV/TSV、文本、源代码、Markdown、JSON 与 YAML 渲染、大文件分段读取,以及面向非本地内容的 provider API。

English

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.

项目信息与收录信号

快照 2026-09-11
作者 / 组织
liguobao
仓库
liguobao/dsh-file-viewer
GitHub Stars
6
Forks
0
开发语言
TypeScript
许可证
MIT
最近推送
2026-09-10
Manifest
数据中检测到声明

公开仓库不自动等于可商用;许可证与当前兼容性以原项目文档为准。Manifest 和数据评分不构成安装安全认证。

数据评分

72/ 100
维护活跃
100
文档与实用信息
85
生态关注度
24
安装便捷信号
100
项目声明信号
100

近 1 天仍在更新;README 含完整安装与使用说明;关注度尚低(6)。

评分来自已有数据快照的五维模型,仅作浏览参考,不是实际使用评测或安全检测结论。

安装与使用

所有项目资料与许可均以对应 GitHub 仓库为准。

dsh plugin --profile web add dsh-file-viewer

复制命令不会自动执行。请先阅读仓库文档,确认环境要求与许可证。

项目截图

仓库 README

GitHub 原文

已保存的仓库文档快照;文档抓取时间未记录,可能与上方目录介绍处于不同版本。安装前请核对当前仓库说明。

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

TypeRenderer
PNG / JPG / GIF / WEBP / SVG / BMPImage (fit, zoom, pan, dimensions)
PDFPDF.js (page nav, page input, zoom, fit width/page)
CSV / TSVData table (delimiter auto-detect, sticky header, row numbers, search, sort, column resize, windowed rows, chunked loading)
TXT / LOG / OUT / INI / CONFText (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)
MarkdownPreview (sanitized) + Source
JSON / JSONLTree (expand/collapse, copy value/path) + Source
YAMLSource + parsed Tree
anything elseFallback (metadata + Open externally / Reveal / Copy path / optional Open as text)

How it works

  • Host half (dist/index.js) registers the /fileviewer authenticated RPC

channel and exposes both a fileViewerContent provider registry and a fileViewerHost bounded 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 as artifact://run/report.json, object storage, generated output, or remote APIs. A boundary-checked ctx.fs provider is installed only when that service is available; on the DSH v0.1.2+ package graph it discovers workspace roots from ctx.workspaceRegistry, live session cwd values from ctx.sessions, and native open support from ctx.sessionController.

  • Client half (dist/client.js) provides the fileViewer service

(ctx.get('fileViewer') → openFile(path, { line, renderer })) and renders the viewer as a conversation.view tab 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).

  • 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 a deepseek-harness source checkout (verified through dsh-v0.1.5-rc.1) or to an installed @deepseek-ai/dsh-client-ui-workspace client bundle. It adds a browseFiles menu item (zh/en labels), an onSelect branch calling window.__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 MB whole-file, 5–50 MB chunked streaming,

> 50 MB head-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: false and sanitized with DOMPurify

(scripts, iframes, event handlers and javascript: 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

使用前核对

安装步骤、兼容版本与授权范围以原项目当前文档为准。Stars 和评分是快照中的导航信号。

安装与排错 · 项目问题反馈

同类项目