Bundle
@dsh-external/dsh-file-explorer-preview-code
CodeMirror 6 code preview and editor for DSH Web, overriding dsh-file-explorer's plain-text preview with per-language syntax highlighting
- Source
- wolfsonliu
- License
- MIT
- Updated
- Updated 3 days ago
Readme
# dsh-file-explorer-preview-code
[中文](README.zh.md) | English
A [CodeMirror 6](https://codemirror.net/) code preview and editor for DSH Web, built on `codemirror` (`basicSetup`) + `@codemirror/language-data` (per-language highlighting) + `@codemirror/theme-one-dark` (dark theme). It registers a named **Code Editor** viewer and overrides [dsh-file-explorer](https://github.com/wolfsonliu/dsh-file-explorer)'s built-in plain-text preview (priority `0`) at priority `10`, giving code files syntax highlighting plus in-place editing with autosave.
## Screenshots
| Dark theme | Light theme |
| --- | --- |
|  |  |
## Features
1. **Syntax highlighting**: resolves the language from the file name via `@codemirror/language-data` (~90 languages), so `.ts`/`.tsx`/`.js`/`.jsx`/`.json`/`.css`/`.html`/`.py`/`.yaml`/`.yml`/`.toml`/`.sh`/`.go`/`.rs`/`.java`/`.c`/`.cpp`/`.h`/`.xml`/`.sql`/`.ini` each get the right tokens.
2. **In-place editing**: the preview is a real CodeMirror editor (line numbers, undo/redo, line wrapping), not a read-only `<pre>`.
3. **Autosave**: edits save 500ms after the last keystroke, plus `Ctrl/Cmd+S` saves immediately.
4. **Save status bar**: a slim footer shows the language, `Ln/Col` cursor position, and the save state (`Unsaved` / `Saving…` / `Saved` / `Save failed`) with a manual Save button.
5. **Theme-aware**: highlighting follows DSH's dark/light toggle (`data-ds-dark-theme`) live.
6. **Selectable viewer**: registered as one named "Code Editor" viewer via `registerViewer`, so it appears alongside the built-in "Text" viewer in the file row's **Open with…** menu and the preview-panel switcher.
## Dependencies
This plugin **requires** [`@dsh-external/dsh-file-explorer`](https://github.com/wolfsonliu/dsh-file-explorer) — it injects the `fileExplorer` cordis service, which provides `registerViewer` (named viewers), `registerPreview` (legacy anonymous override), `readRawFile`, and `writeFile` (the save path). The named "Code Editor" viewer needs core v0.9.0+; older cores fall back to the anonymous per-extension override. Install and enable `dsh-file-explorer` before this plugin:
```sh
git clone https://github.com/wolfsonliu/dsh-file-explorer.git
cd dsh-file-explorer
npm install && npm run build
dsh plugin --profile web add .
```
> `@dsh-external/dsh-file-explorer` is installed from git (`github:wolfsonliu/dsh-file-explorer`) so `tsc` resolves its `./client` type definitions. To develop against an unpublished local checkout instead, point that dependency at your own path.
## Install
From the git repository:
```sh
git clone https://github.com/wolfsonliu/dsh-file-explorer-preview-code.git
cd dsh-file-explorer-preview-code
npm install && npm run build
dsh plugin --profile web add .
dsh web
```
## How it works
The client entry injects `fileExplorer` and `locale`, then registers one `CodePreview` component for every code extension at priority `10`. On core v0.9.0+ it uses `registerViewer`, exposing a single named **Code Editor** viewer in **Open with…**; on older cores it falls back to the anonymous per-extension `registerPreview` loop:
```typescript
export const inject = ['fileExplorer', 'locale']
export function apply(ctx) {
ctx.effect(() => {
const readRaw = typeof ctx.fileExplorer.readRawFile === 'function'
? ctx.fileExplorer.readRawFile.bind(ctx.fileExplorer)
: undefined
const component = makeCodePreview(ctx.fileExplorer.writeFile, readRaw, ctx.locale.bind(CODE_NS))
const dispose = registerCodeViewer(ctx.fileExplorer, component)
return () => dispose()
})
}
function registerCodeViewer(fileExplorer, component) {
if (typeof fileExplorer.registerViewer === 'function') {
return fileExplorer.registerViewer({
id: 'code-editor', label: 'Code Editor', exts: CODE_EXTS, component, priority: 10,
})
}
const disposers = CODE_EXTS.map(ext => fileExplorer.registerPreview(ext, component, 10))
return () => { for (const d of disposers) d() }
}
```
Registered extensions (`CODE_EXTS`): `ts tsx js jsx json css html py yaml yml toml env sh go rs java c cpp h xml sql graphql cfg ini`.
The editor component handles four preview kinds:
| Kind | Behavior |
|------|----------|
| `text` | Uses `preview.content` directly (files ≤ 2 MiB) |
| `text-large` | Calls `readRawFile(filePath)`, decodes the `ArrayBuffer` as UTF-8, and opens the editor (text files over 2 MiB) |
| `binary` | Same as `text-large` — `readRawFile` + decode |
| `too-large` | Same as `text-large` — `readRawFile` + decode (images over their cap; not delivered to this plugin's registered code extensions) |
When `readRawFile` is unavailable (older dsh-file-explorer core), `text-large` and `binary` files show an upgrade prompt. Edits always flow back through `fileExplorer.writeFile(filePath, content)`.
## Configuration
The bundle inserts a single roster row (no host-side configuration):
```yaml
- insert:
- id: file-explorer-preview-code
name: '@dsh-external/dsh-file-explorer-preview-code'
```
## Language coverage
`@codemirror/language-data` matches 21 of the 24 extensions to a language. `env` and `graphql` have no language support and open as a plain (un-highlighted) editable buffer; `cfg` matches language-data's legacy `TTCN_CFG` (a matching artifact, not a semantic `ini` match). All still open in the editor.
## Known Limitations
- **Bundle size**: all `@codemirror/*` language packages are inlined into a single `lib/client.js` (~2.7 MB raw, loaded lazily on demand).
- **No Markdown**: `.md`/`.mdx` stay with dsh-file-explorer's built-in markdown preview.
- **Write-through**: editing writes directly back to the workspace file; there is no diff/preview-before-save or multi-tab.
- **Large files**: files above dsh-file-explorer's `maxTextBytes` (2 MiB) arrive as `preview.kind === 'text-large'` and are fetched via `readRawFile` then loaded into the editor. Very large files (hundreds of MiB) may cause browser performance issues due to the single-buffer CodeMirror model. `readRawFile` is part of the stable service contract (added in v0.1.0).
## Developing preview plugins
This repo is the reference implementation for building a preview plugin. See [docs/developing-preview-plugins.md](docs/developing-preview-plugins.md) ([中文](docs/developing-preview-plugins.zh.md)) for the contract, a minimal skeleton, bundling notes, and i18n.
## Related
- [dsh-file-explorer](https://github.com/wolfsonliu/dsh-file-explorer) — the core file explorer this plugin extends.
- [dsh-file-explorer-preview-code](https://github.com/wolfsonliu/dsh-file-explorer-preview-code) — this repository.
- [dsh-file-explorer-preview-molstar](https://github.com/wolfsonliu/dsh-file-explorer-preview-molstar) — a Mol* structure preview for `.cif`/`.pdb`, built on the same `fileExplorer` contract.
## Development
```sh
npm install
npm run check # tsc type check
npm test # vitest unit tests
npm run build # tsc + tsdown (host ESM stub + client CJS bundle)
```
> After `npm run build`, hard-refresh the browser (`Ctrl/Cmd+Shift+R`): `dsh web` may keep serving a cached plugin bundle, so a soft reload can leave your latest build unused.
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:wolfsonliu/dsh-file-explorer-preview-code#52c686b65b1460ea30d3b0fc8d3f3c825ed897e5
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-external-dsh-file-explorer-preview-code from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.