@jaxzhou/dsh-file-explorer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,14 +5,18 @@
5
5
 
6
6
  English | [中文](README.zh.md)
7
7
 
8
- > Published on npm as **`@jaxzhou/dsh-file-explorer`**. The unscoped names
9
- > `dsh-file-explorer`, `dsh-files` and `dsh-workspace-files` belong to other
10
- > authors' plugins, so this one carries its publisher's scope.
8
+ > Published on npm as **`@jaxzhou/dsh-file-explorer`** the unscoped name
9
+ > `dsh-file-explorer` belongs to a different author's plugin.
11
10
 
12
- A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that
13
- adds a **Files** tab to the Conversation View strip the same level as **Chat**
14
- and **Trajectory** showing the session workspace as a tree, with a preview body
15
- chosen per file category beside it.
11
+ A **Files** tab for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness),
12
+ beside **Chat** and **Trajectory**: the session workspace as a tree, and a preview
13
+ that adapts to what the file is rendered Markdown, a JSON tree, highlighted
14
+ source, an image, or plain text. Read-only, no configuration, nothing stored.
15
+
16
+ ```sh
17
+ dsh plugin --profile web add @jaxzhou/dsh-file-explorer
18
+ dsh --profile web
19
+ ```
16
20
 
17
21
  ## Demo
18
22
 
@@ -20,74 +24,49 @@ chosen per file category beside it.
20
24
  on the right a preview that highlights source, renders Markdown and JSON, and
21
25
  offers a Rendered/Source toggle](media/demo.gif)](media/demo.mp4)
22
26
 
23
- *30-second recording — click it for the full-quality MP4.* Walking a Flutter +
24
- NestJS workspace, it previews TypeScript, Swift, Dart, HTML, YAML and JavaScript
25
- source with syntax highlighting and line numbers, shows a `README.md` rendered
26
- and then as source through the **Rendered / Source** toggle, and ends on a
27
- `tsconfig.json` that explains itself before falling back to highlighted source —
28
- its trailing comma makes it JSONC, not strict JSON, so no tree can walk it.
29
-
30
- ## What it does
31
-
32
- - **Workspace tree.** The session's working directory, listed one level at a
33
- time, directories first, then files by natural name order. A level is fetched
34
- the first time it is expanded and kept while collapsed.
35
- - **A preview body per file category.**
36
-
37
- | Category | Suffixes | Body |
38
- |---|---|---|
39
- | Markdown | `md` `markdown` `mkd` `mdown` `mdwn` | Rendered GFM document — headings, tables, task lists, quotes, KaTeX math, footnotes, and syntax-highlighted code fences — with a **Source** toggle |
40
- | JSON | `json` `jsonc` `jsonl` `ndjson` `map` `webmanifest` | Collapsible tree with per-value copy, with a **Source** toggle; a file the tree cannot walk says so above its highlighted source — a syntax error, a truncated page, a bare scalar, or the JSONC that `tsconfig.json` and friends actually contain |
41
- | Source code | 24 grammars: TypeScript/JavaScript, shell, Python, Ruby, Go, Rust, Java, C, C++, C#, Kotlin, Swift, PHP, YAML, TOML, INI, HTML, CSS, SCSS, Less, SQL, XML, Lua, MDX | Syntax-highlighted, numbered, with a copy control |
42
- | Images | `png` `apng` `jpg` `jpeg` `jfif` `gif` `webp` `avif` `bmp` `ico` `svg` | Drawn, centred and scaled to the pane. An SVG draws through `<img>`, so its scripts never run |
43
- | Anything else | every other suffix | Numbered, copyable plain text |
44
-
45
- An unmapped suffix (`.vue`, `.proto`, `.txt`, …) is deliberately plain text
46
- rather than a guess: a wrong grammar colours a file misleadingly.
47
- - **Preview reading.** Text bodies read the file's leading 2 000 lines; an image
48
- reads its complete bytes. Long lines wrap at their spaces and keep every word
49
- whole; only a token with no break opportunity inside it — a long URL, one
50
- minified run — is split, because the alternative is hiding it behind a
51
- horizontal scrollbar. A wrapped continuation hangs under the text, not under
52
- the line number. The wrap toggle applies to source bodies, where it means
53
- something.
54
- - **State that survives.** Expansion and the selected file live in an exclusive
55
- per-session store, so switching to Chat and back — which unmounts the view —
56
- does not lose your place.
57
-
58
- Everything is read-only. The plugin browses and previews; it never writes,
59
- renames, or deletes.
60
-
61
- ## Compatibility
62
-
63
- Built and verified against **DeepSeek Harness `0.1.5-rc.2`** on the **Web**
64
- surface (`dsh web`, or a profile composed from `@deepseek-ai/dsh-base` +
65
- `@deepseek-ai/dsh-web-app`). It needs the Composition's
66
- `@deepseek-ai/dsh-api-workspace-files` row, which the shipped Web bundle
67
- mounts; a headless or SDK profile has no browser and gets no tab.
68
-
69
- The plugin itself declares no configuration fields, so nothing in `cordis.yml`
70
- needs setting.
27
+ *30 seconds — click for the full-quality MP4.*
71
28
 
72
- ## Install
29
+ ## What you get
73
30
 
74
- From npm:
31
+ The left pane is the session's working directory, listed one level at a time,
32
+ directories first. An expanded level stays expanded while you move between
33
+ preview tabs.
75
34
 
76
- ```sh
77
- dsh plugin --profile web add @jaxzhou/dsh-file-explorer
78
- dsh --profile web # restart the profile; bundle membership is a startup boundary
79
- ```
35
+ Clicking a file opens it in a tab, so several files stay open at once — each with
36
+ its own body and its own wrap setting. A file that is already open is focused
37
+ rather than reopened, tabs whose names collide show their directory, and the tab's
38
+ × or a middle click closes one.
80
39
 
81
- From this repository (a checkout or a git ref), which builds nothing — the
82
- runtime artifacts are committed:
40
+ The right pane picks each tab's body from the file:
41
+
42
+ | Category | Preview |
43
+ |---|---|
44
+ | **Markdown** | Rendered GFM — headings, tables, task lists, quotes, math, footnotes, highlighted code fences — with a **Source** toggle |
45
+ | **JSON** | A collapsible tree with per-value copy, and the same toggle |
46
+ | **Source code** | Syntax highlighting, line numbers, and a copy button for 24 grammars: TypeScript/JavaScript, shell, Python, Ruby, Go, Rust, Java, C, C++, C#, Kotlin, Swift, PHP, YAML, TOML, INI, HTML, CSS, SCSS, Less, SQL, XML, Lua, MDX |
47
+ | **Images** | PNG, JPEG, GIF, WebP, AVIF, BMP, ICO and SVG, drawn to the pane. An SVG goes through `<img>`, so its scripts never run |
48
+ | **Anything else** | Numbered plain text — an unmapped suffix (`.vue`, `.proto`, `.txt`) stays plain rather than guessing a wrong grammar |
49
+
50
+ Text previews wrap at their spaces and keep every word whole; an image reads its
51
+ complete bytes. Every text body carries a **Copy** control in the pane's toolbar,
52
+ which copies the file's own text — a rendered Markdown document copies its
53
+ Markdown source.
54
+
55
+ ## Requirements
56
+
57
+ DeepSeek Harness **0.1.5-rc.2** on the **Web** surface — `dsh web`, or a profile
58
+ composed from `@deepseek-ai/dsh-base` + `@deepseek-ai/dsh-web-app`. A headless or
59
+ SDK profile has no browser and gets no tab. The plugin declares no configuration,
60
+ so nothing in `cordis.yml` needs setting.
61
+
62
+ ## Install
83
63
 
84
64
  ```sh
85
- dsh plugin --profile web add /path/to/dsh-file-explorer
86
- dsh plugin --profile web add github:jaxzhou/dsh-file-explorer
65
+ dsh plugin --profile web add @jaxzhou/dsh-file-explorer
66
+ dsh --profile web # bundle membership is read at startup
87
67
  ```
88
68
 
89
- For a custom profile that is not the shipped `web` one, create it from the Web
90
- template first so the browser composition exists:
69
+ A custom profile needs the Web composition first:
91
70
 
92
71
  ```sh
93
72
  dsh --profile myprofile --from-default-profile web
@@ -95,146 +74,68 @@ dsh plugin --profile myprofile add @jaxzhou/dsh-file-explorer
95
74
  dsh --profile myprofile
96
75
  ```
97
76
 
98
- Then open a session that has a workspace and click the **Files** tab. A session
99
- with no workspace shows the tab strip but the view says there is no workspace
100
- directory.
101
-
102
- ### Verify the install
77
+ Prefer a checkout or a git ref? Nothing needs building the runtime artifacts are
78
+ committed:
103
79
 
104
80
  ```sh
105
- dsh --profile web --dump-config | grep -A 2 'jaxzhou-file-explorer'
81
+ dsh plugin --profile web add /path/to/dsh-file-explorer
82
+ dsh plugin --profile web add github:jaxzhou/dsh-file-explorer
106
83
  ```
107
84
 
108
- The dump must show a `# == @jaxzhou/dsh-file-explorer` layer and a row named
109
- `@jaxzhou/dsh-file-explorer`. Then, in the running Web UI, a browser devtools
110
- network panel shows the plugin bundle inside one `/plugins/??…` combo response,
111
- and the page head carries a `<style data-dsh-file-explorer>` tag once the plugin
112
- materializes.
113
-
114
- ## Disable and uninstall
115
-
116
- - **Disable without uninstalling** — add a row override to the profile's
117
- `cordis.patch.yml` (applied after every bundle layer):
118
-
119
- ```yaml
120
- - id: jaxzhou-file-explorer
121
- disabled: true
122
- ```
123
-
124
- Removing that entry and saving brings the tab back: a `patchReload: live`
125
- profile re-applies the file without a restart.
126
-
127
- - **Uninstall** — `dsh plugin --profile web remove @jaxzhou/dsh-file-explorer`,
128
- then restart the profile.
129
-
130
- ## Data path and permissions
131
-
132
- - **Reads** go through the harness's own `workspaceFiles` Remote namespace:
133
- `list(sessionId, path)` for one directory level and
134
- `read(sessionId, path, { offset, limit })` for one text page. The Host's
135
- composed filesystem decides readability, so the plugin holds no file access,
136
- no path resolution, and no credentials of its own.
137
- - **Writes**: none. The Remote namespace exposes no mutation operation.
138
- - **Storage**: none. The plugin persists nothing — not to disk, not to the
139
- session log. All state is in-memory view state, discarded with the session
140
- binding.
141
- - **Host authority**: the Host half of the package is inert; it registers no
142
- service, tool, prompt section, or event.
143
-
144
- ## Development
85
+ Then open a session that has a workspace and click **Files**. To confirm the layer
86
+ landed:
145
87
 
146
88
  ```sh
147
- npm install
148
- npm run check # typecheck, build both runtime artifacts, run the tests
89
+ dsh --profile web --dump-config | grep -A 2 jaxzhou-file-explorer
149
90
  ```
150
91
 
151
- `lib/index.js` and `lib/client.js` are **committed build artifacts**: a git or
152
- tarball install loads them directly and needs no build step or `allowBuilds`
153
- permission.
92
+ ## Disable or uninstall
154
93
 
155
- The browser half must be a lazy CommonJS factory handoff, because the dsh Web
156
- loader fetches it as a classic script:
94
+ Disable the tab without uninstalling by adding an override to the profile's
95
+ `cordis.patch.yml` it is applied after every bundle layer, and a
96
+ `patchReload: live` profile picks it up without a restart:
157
97
 
158
- ```js
159
- window.__ModuleLoader__.load({ id: '@jaxzhou/dsh-file-explorer', factory: (require) => { … } })
98
+ ```yaml
99
+ - id: jaxzhou-file-explorer
100
+ disabled: true
160
101
  ```
161
102
 
162
- `scripts/build.mjs` emits that shape with esbuild and then verifies it. Only the
163
- shell's frozen module table stays external (`react`, `react/jsx-runtime`,
164
- `@deepseek-ai/dsh-client-store`, `@deepseek-ai/dsh-client-ui-primitives`, …);
165
- every other dsh package is imported type-only and erased, so no `require` in the
166
- artifact can miss the table.
103
+ Uninstall with `dsh plugin --profile web remove @jaxzhou/dsh-file-explorer`, then
104
+ restart the profile.
167
105
 
168
- The rendered Markdown, the JSON tree, the syntax-highlighted code block, its
169
- numbered gutter, its copy control, and the shiki grammars all come from
170
- `@deepseek-ai/dsh-client-ui-primitives`, which the shell shares into that table.
171
- This plugin contributes the format decision and the pane, not a second
172
- renderer.
173
-
174
- ### Layout
175
-
176
- | Path | Role |
177
- |---|---|
178
- | `src/index.ts` | Host half: an inert Loader module |
179
- | `src/client/index.ts` | Client plugin: stylesheet, dictionaries, and the `conversation.view` registration |
180
- | `src/client/FilesView.tsx` | The two-pane page: tree, headers, and the per-format preview bodies |
181
- | `src/client/format.ts` | Suffix → preview format, and the grammar/media-type tables |
182
- | `src/client/store.ts` | The exclusive per-session view store |
183
- | `src/client/face.ts` | The injected face: Remote listing, paged text reads, and complete-byte image reads |
184
- | `src/client/styles.ts` | The plugin-owned stylesheet |
185
- | `src/client/locales.ts` | `fileExplorer` dictionaries (zh, en) and the namespace declaration |
186
- | `cordis.patch.yml` | The bundle layer: one inserted row || `tests/contract.test.mjs` | Contract tests over the built artifacts |
187
-
188
- Each source focus is a separate module with its own header comment; start there
189
- for the reasoning behind a choice.
190
-
191
- ### Regenerating the demo
192
-
193
- The README embeds a GIF and links the MP4 beside it. GitHub renders a committed
194
- video only on its own file page, so the animation is what plays inline; the
195
- poster frame and the link are the same recording at full quality.
196
-
197
- ```sh
198
- SRC="screen recording.mov"
199
- # Full-quality MP4: half the capture's width, which is a 2x screenshot's worth.
200
- ffmpeg -i "$SRC" -vf scale=1680:-2:flags=lanczos -r 30 \
201
- -c:v libx264 -preset slow -crf 24 -pix_fmt yuv420p -movflags +faststart -an media/demo.mp4
202
- # Inline GIF: fewer frames and a smaller palette, which is where the bytes go.
203
- ffmpeg -i "$SRC" -vf "fps=12,scale=1200:-2:flags=lanczos,split[a][b];\
204
- [a]palettegen=max_colors=128:stats_mode=diff[p];\
205
- [b][p]paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle" -loop 0 media/demo.gif
206
- ```
106
+ ## Privacy
207
107
 
208
- `media/` is documentation only: it is outside the package's `files` list, so it
209
- never ships in the npm tarball.
108
+ - **Read-only.** Reads go through the harness's own `workspaceFiles` Remote
109
+ namespace; the Host's filesystem decides what is readable. The plugin holds no
110
+ file access, no path resolution, and no credentials of its own.
111
+ - **Nothing stored.** No disk writes and nothing added to the session log; view
112
+ state lives in memory and is discarded with the session.
113
+ - **Inert Host half.** The package's Node side registers no service, tool, prompt
114
+ section, or event.
210
115
 
211
116
  ## Known limitations
212
117
 
213
- - **Preview only, no editing.** The viewer reads; it does not write, save, or
214
- diff.
215
- - **JSON is parsed strictly.** A `tsconfig.json` with a trailing comma is JSONC,
216
- which TypeScript accepts and `JSON.parse` does not; the pane explains that and
217
- shows the highlighted source instead of a tree. Comments and trailing commas
218
- are not stripped to guess at a tree.
219
- - **Markdown renders without workspace vocabulary.** Relative image paths and
220
- file mentions inside a Markdown file stay inert — only absolute `http(s)`
221
- images loadbecause resolving them would need the reader to vouch for real
222
- files, which this pane does not.
223
- - **Text bodies are capped at the first 2 000 lines.** Binary and non-UTF-8
224
- files report `workspace-file/not-text`; files past the Host's complete-file
225
- cap report `workspace-file/too-large`; a longer text file shows its leading
226
- page with a truncation note and no "load more". An image is capped at the
227
- Host's complete-file limit instead.
228
- - **No highlighting for unmapped grammars.** The shared highlighter carries a
229
- fixed grammar set; a suffix outside it (`.vue`, `.proto`, `.graphql`, …) shows
230
- as plain text rather than an approximation.
231
- - **Listing only.** No search, filter, rename, context menu, current-file
232
- highlight, or filesystem watching. A level changes only through **Reload**.
233
- - **One root.** The tree is rooted at the session's working directory; paths
234
- above it are not browsable, and the Host refuses directory listings outside
235
- the workspace root anyway.
236
- - **No editing.** Monospaced, numbered source text with a wrapping option;
237
- and rendered prose for Markdown — no folding, no search, no in-place edit.
118
+ - **Preview only** no editing, saving, or diffing.
119
+ - **Strict JSON.** Comments or a trailing comma make a file JSONC, which
120
+ `JSON.parse` rejects `tsconfig.json` is the common case. The pane says so and
121
+ shows the highlighted source instead of guessing at a tree.
122
+ - **Text previews stop at 2 000 lines**, with a truncation note and no "load
123
+ more"; binary files report why they cannot be shown.
124
+ - **Fixed grammar set.** A suffix the shared highlighter does not carry is plain
125
+ text, never an approximation.
126
+ - **Listing only** no search, rename, context menu, or file watching; a level
127
+ refreshes through **Reload**.
128
+ - **Loaded previews are bounded.** A working set of five tabs keeps its content;
129
+ an older tab stays open and reads again when you return to it.
130
+ - **One root.** The tree is rooted at the session's working directory, and the
131
+ Host refuses directory listings outside the workspace root.
132
+ - **Markdown has no workspace vocabulary.** Relative image paths and file
133
+ mentions stay inert; only absolute `http(s)` images load.
134
+
135
+ ## Contributing
136
+
137
+ Build, checks, the artifact model, and how the client bundle reaches the browser:
138
+ [CONTRIBUTING.md](CONTRIBUTING.md).
238
139
 
239
140
  ## License
240
141
 
package/README.zh.md CHANGED
@@ -5,77 +5,63 @@
5
5
 
6
6
  [English](README.md) | 中文
7
7
 
8
- > npm 上的包名是 **`@jaxzhou/dsh-file-explorer`**。无作用域的
9
- > `dsh-file-explorer`、`dsh-files`、`dsh-workspace-files` 已被其他作者的插件占
10
- > 用,因此本插件带上发布者作用域。
8
+ > npm 包名是 **`@jaxzhou/dsh-file-explorer`** —— 无作用域的 `dsh-file-explorer`
9
+ > 属于另一位作者的插件。
11
10
 
12
- 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:
13
- 在对话视图的标签栏中新增 **文件** 标签,与 **对话**、**轨迹** 同级,左侧是会话
14
- 工作区的目录树,右侧是按文件类别选择的预览体。
11
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 增加一个
12
+ **文件** 标签,与 **对话**、**轨迹** 并列:左侧是会话工作区的目录树,右侧的预览
13
+ 会按文件本身决定形态 —— 渲染后的 Markdown、JSON 树、语法高亮源码、图片,或纯
14
+ 文本。只读、无需配置、不落任何数据。
15
+
16
+ ```sh
17
+ dsh plugin --profile web add @jaxzhou/dsh-file-explorer
18
+ dsh --profile web
19
+ ```
15
20
 
16
21
  ## 演示
17
22
 
18
23
  [![文件标签与对话、轨迹并列:左侧是工作区目录树,右侧是预览——可高亮源码、渲染
19
24
  Markdown 与 JSON,并提供「预览/源码」切换](media/demo.gif)](media/demo.mp4)
20
25
 
21
- *30 秒录屏 —— 点击可打开完整画质的 MP4。* 录屏浏览了一个 Flutter + NestJS 工作
22
- 区,依次预览 TypeScript、Swift、Dart、HTML、YAML、JavaScript 源码(带语法高亮与
23
- 行号),用 **预览 / 源码** 切换展示同一个 `README.md` 的渲染结果与原文,最后停在
24
- 一个会先自我解释、再回退到高亮源码的 `tsconfig.json` 上——它尾部的逗号让它成为
25
- JSONC 而非严格 JSON,所以无法展开成树。
26
+ *30 秒录屏 —— 点击可打开完整画质的 MP4。*
26
27
 
27
- ## 功能
28
+ ## 能做什么
28
29
 
29
- - **工作区目录树**:以会话的工作目录为根,逐层读取;目录在前,文件按自然名
30
- 称排序。某一层在首次展开时读取,折叠后仍保留内容。
31
- - **按文件类别选择合适的预览体**:
30
+ 左栏是会话的工作目录,逐层展开,目录在前;展开过的层级在切换预览标签后仍然保持。
32
31
 
33
- | 类别 | 后缀 | 显示 |
34
- |---|---|---|
35
- | Markdown | `md` `markdown` `mkd` `mdown` `mdwn` | 直接渲染为 GFM 文档:标题、表格、任务列表、引用、KaTeX 公式、脚注,代码围栏带语法高亮;带 **源码** 切换 |
36
- | JSON | `json` `jsonc` `jsonl` `ndjson` `map` `webmanifest` | 可折叠树,每个值可单独复制;同样带 **源码** 切换。无法展开的文件会在高亮源码上方说明原因——语法错误、被截断、纯标量,或 `tsconfig.json` 这类文件里实际存在的 JSONC |
37
- | 源码 | 24 种语法:TypeScript/JavaScript、shell、Python、Ruby、Go、Rust、Java、C、C++、C#、Kotlin、Swift、PHP、YAML、TOML、INI、HTML、CSS、SCSS、Less、SQL、XML、Lua、MDX | 语法高亮 + 行号 + 复制按钮 |
38
- | 图片 | `png` `apng` `jpg` `jpeg` `jfif` `gif` `webp` `avif` `bmp` `ico` `svg` | 直接绘制,居中并在超出时缩放到窗格宽度。SVG 通过 `<img>` 绘制,其中的脚本不会执行 |
39
- | 其它 | 其余全部后缀 | 带行号、可复制的纯文本 |
32
+ 点击文件会在标签中打开它,因此可以同时打开多个文件——每个标签有自己的预览体、
33
+ 自己的换行设置。已打开的文件会被聚焦而不是重复打开;同名的标签会带上所在目录;
34
+ 点标签上的 × 或中键点击即可关闭。
40
35
 
41
- 未映射的语法(`.vue`、`.proto`、`.txt` 等)故意按纯文本显示,而不是猜测:错
42
- 误的语法着色比不着色更容易误导。
43
- - **预览读取**:文本类预览只读取前 2 000 行,图片类读取完整字节。超长行在空
44
- 格处折行,每个单词保持完整;只有内部没有任何断点的 token(超长 URL、压缩后的
45
- 一整段代码)才会被拆开,因为否则它只能被藏在横向滚动条之外。折行后的续行悬挂
46
- 在正文下方,而不是行号下方。换行开关只在源码视图出现(那里才有意义)。
47
- - **状态保留**:展开状态与当前选中文件保存在按会话独占的 store 中,因此切到
48
- 「对话」再切回来(组件会卸载)不会丢失浏览位置。
36
+ 右栏按文件类别选择每个标签的预览体:
49
37
 
50
- 全部为只读:插件只浏览与预览,不会写入、重命名或删除任何文件。
38
+ | 类别 | 预览 |
39
+ |---|---|
40
+ | **Markdown** | 直接渲染为 GFM —— 标题、表格、任务列表、引用、公式、脚注、高亮的代码围栏 —— 并带 **源码** 切换 |
41
+ | **JSON** | 可折叠树,每个值可单独复制,同样带 **源码** 切换 |
42
+ | **源码** | 24 种语法的语法高亮、行号与复制按钮:TypeScript/JavaScript、shell、Python、Ruby、Go、Rust、Java、C、C++、C#、Kotlin、Swift、PHP、YAML、TOML、INI、HTML、CSS、SCSS、Less、SQL、XML、Lua、MDX |
43
+ | **图片** | PNG、JPEG、GIF、WebP、AVIF、BMP、ICO、SVG,自动适配窗格。SVG 经 `<img>` 绘制,其中的脚本不会执行 |
44
+ | **其它** | 带行号的纯文本 —— 未映射的后缀(`.vue`、`.proto`、`.txt`)保持纯文本,而不是猜测一个错误的高亮 |
51
45
 
52
- ## 兼容性
46
+ 文本预览在空格处折行、保持单词完整;图片读取完整字节。每一种文本预览在窗格工具栏
47
+ 里都有 **复制** 按钮,复制的是文件自身的文本——渲染态的 Markdown 复制的是它的
48
+ Markdown 源码。
53
49
 
54
- 针对 **DeepSeek Harness `0.1.5-rc.2`** 的 **Web** 界面(`dsh web`,或由
55
- `@deepseek-ai/dsh-base` + `@deepseek-ai/dsh-web-app` 组合出的 profile)开发并
56
- 验证。插件依赖组合中的 `@deepseek-ai/dsh-api-workspace-files` 行,官方 Web
57
- bundle 已挂载;headless / SDK 等无浏览器界面不会出现该标签。
50
+ ## 环境要求
58
51
 
59
- 插件本身没有配置项,`cordis.yml` 无需任何设置。
52
+ DeepSeek Harness **0.1.5-rc.2** 的 **Web** 界面 —— `dsh web`,或由
53
+ `@deepseek-ai/dsh-base` + `@deepseek-ai/dsh-web-app` 组合出的 profile。headless
54
+ 或 SDK profile 没有浏览器,不会出现该标签。插件没有配置项,`cordis.yml` 无需
55
+ 任何设置。
60
56
 
61
57
  ## 安装
62
58
 
63
- 从 npm 安装:
64
-
65
59
  ```sh
66
60
  dsh plugin --profile web add @jaxzhou/dsh-file-explorer
67
- dsh --profile web # 重启 profile;bundle 成员在启动时生效
61
+ dsh --profile web # bundle 成员在启动时读取
68
62
  ```
69
63
 
70
- 从本仓库安装(本地检出或 git ref),均无需构建——运行产物已提交:
71
-
72
- ```sh
73
- dsh plugin --profile web add /path/to/dsh-file-explorer
74
- dsh plugin --profile web add github:jaxzhou/dsh-file-explorer
75
- ```
76
-
77
- 如果使用自定义 profile(不是官方的 `web`),先基于 Web 模板创建,以保证浏览器
78
- 组合存在:
64
+ 自定义 profile 需要先具备 Web 组合:
79
65
 
80
66
  ```sh
81
67
  dsh --profile myprofile --from-default-profile web
@@ -83,129 +69,61 @@ dsh plugin --profile myprofile add @jaxzhou/dsh-file-explorer
83
69
  dsh --profile myprofile
84
70
  ```
85
71
 
86
- 打开一个已有工作区的会话,点击 **文件** 标签即可。若会话没有工作区目录,标签仍
87
- 会显示,但页面会提示没有工作区目录。
88
-
89
- ### 验证安装
72
+ 偏好本地检出或 git ref?无需构建 —— 运行产物已提交:
90
73
 
91
74
  ```sh
92
- dsh --profile web --dump-config | grep -A 2 'jaxzhou-file-explorer'
75
+ dsh plugin --profile web add /path/to/dsh-file-explorer
76
+ dsh plugin --profile web add github:jaxzhou/dsh-file-explorer
93
77
  ```
94
78
 
95
- 输出中应出现 `# == @jaxzhou/dsh-file-explorer` 层,以及名为
96
- `@jaxzhou/dsh-file-explorer` 的行。随后在运行中的 Web 界面里:开发者工具的网络
97
- 面板中,插件 bundle 位于某个 `/plugins/??…` 合并响应内;插件加载后页面 head 中会
98
- 出现 `<style data-dsh-file-explorer>` 标签。
99
-
100
- ## 停用与卸载
101
-
102
- - **临时停用**:在 profile 的 `cordis.patch.yml`(在所有 bundle 层之后应用)中
103
- 添加行覆盖:
104
-
105
- ```yaml
106
- - id: jaxzhou-file-explorer
107
- disabled: true
108
- ```
109
-
110
- 删掉该条目并保存即可恢复;`patchReload: live` 的 profile 无需重启。
111
-
112
- - **卸载**:`dsh plugin --profile web remove @jaxzhou/dsh-file-explorer`,然后重启
113
- profile。
114
-
115
- ## 数据路径与权限
116
-
117
- - **读取**走 Harness 自带的 `workspaceFiles` Remote 命名空间:
118
- `list(sessionId, path)` 读取一层目录,`read(sessionId, path, { offset, limit })`
119
- 读取一页文本。是否可读由 Host 组合出的文件系统决定,插件自身不持有文件权限、
120
- 不做路径解析、不接触任何凭据。
121
- - **写入**:没有。该 Remote 命名空间不提供任何写操作。
122
- - **存储**:没有。插件不落盘、不写会话日志;所有状态都是内存中的视图状态,
123
- 随会话绑定一起释放。
124
- - **Host 权限**:包的 Host 半边是空实现,不注册服务、工具、提示词片段或事件。
125
-
126
- ## 开发
79
+ 然后打开一个已有工作区的会话,点击 **文件** 标签。确认层已生效:
127
80
 
128
81
  ```sh
129
- npm install
130
- npm run check # 类型检查 + 构建两个运行产物 + 运行测试
82
+ dsh --profile web --dump-config | grep -A 2 jaxzhou-file-explorer
131
83
  ```
132
84
 
133
- `lib/index.js` 与 `lib/client.js` 是**提交进仓库的构建产物**:通过 git 或
134
- tarball 安装时直接加载,不需要构建步骤,也不需要 `allowBuilds` 授权。
85
+ ## 停用与卸载
135
86
 
136
- 浏览器半边必须是由模块加载器接收的惰性 CommonJS 工厂,因为 dsh Web 加载器以
137
- 经典脚本方式抓取它:
87
+ 不用卸载也能关掉这个标签:在 profile `cordis.patch.yml` 里加一条行覆盖
88
+ (它在所有 bundle 层之后应用;`patchReload: live` 的 profile 无需重启即可生效):
138
89
 
139
- ```js
140
- window.__ModuleLoader__.load({ id: '@jaxzhou/dsh-file-explorer', factory: (require) => { … } })
90
+ ```yaml
91
+ - id: jaxzhou-file-explorer
92
+ disabled: true
141
93
  ```
142
94
 
143
- `scripts/build.mjs` esbuild 产出该形式并随后校验。只有 shell 冻结的模块表保持
144
- 外部依赖(`react`、`react/jsx-runtime`、`@deepseek-ai/dsh-client-store`、
145
- `@deepseek-ai/dsh-client-ui-primitives` 等);其余 dsh 包都是 type-only 导入,
146
- 编译时即被擦除,因此产物中不会出现模块表无法回答的 `require`。
147
-
148
- Markdown 渲染、JSON 树、语法高亮代码块及其行号与复制控件、以及 shiki 语法本身,
149
- 全部来自 `@deepseek-ai/dsh-client-ui-primitives`——shell 已把它共享进模块表。本插
150
- 件贡献的是格式判定与窗格本身,而不是第二套渲染器。
95
+ 卸载:`dsh plugin --profile web remove @jaxzhou/dsh-file-explorer`,然后重启
96
+ profile。
151
97
 
152
- ### 目录结构
153
-
154
- | 路径 | 作用 |
155
- |---|---|
156
- | `src/index.ts` | Host 半边:空的 Loader 模块 |
157
- | `src/client/index.ts` | 客户端插件:样式表、字典与 `conversation.view` 注册 |
158
- | `src/client/FilesView.tsx` | 双栏页面:目录树、两处表头,以及按格式分发的预览体 |
159
- | `src/client/format.ts` | 后缀 → 预览格式,以及语法/媒体类型表 |
160
- | `src/client/store.ts` | 按会话独占的视图 store |
161
- | `src/client/face.ts` | 注入面:Remote 列目录、分页读文本、整块读图片字节 |
162
- | `src/client/styles.ts` | 插件自有样式表 |
163
- | `src/client/locales.ts` | `fileExplorer` 字典(zh、en)与命名空间声明 |
164
- | `cordis.patch.yml` | bundle 层:插入一行 |
165
- | `tests/contract.test.mjs` | 针对构建产物的契约测试 |
166
-
167
- 每个源文件都是一个独立模块,文件头注释说明了其中的取舍。
168
-
169
- ### 重新生成演示
170
-
171
- README 内嵌一个 GIF、并链接同一段录屏的 MP4。GitHub 只有在视频文件自己的页面上
172
- 才会播放提交进仓库的视频,所以内联动起来的是 GIF;而首帧与链接指向的是同一段
173
- 完整画质的录像。
174
-
175
- ```sh
176
- SRC="屏幕录制.mov"
177
- # 完整画质 MP4:取录制宽度的一半,相当于 2x 截图的清晰度。
178
- ffmpeg -i "$SRC" -vf scale=1680:-2:flags=lanczos -r 30 \
179
- -c:v libx264 -preset slow -crf 24 -pix_fmt yuv420p -movflags +faststart -an media/demo.mp4
180
- # 内联 GIF:降帧率、缩调色板,体积主要就省在这里。
181
- ffmpeg -i "$SRC" -vf "fps=12,scale=1200:-2:flags=lanczos,split[a][b];\
182
- [a]palettegen=max_colors=128:stats_mode=diff[p];\
183
- [b][p]paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle" -loop 0 media/demo.gif
184
- ```
98
+ ## 隐私
185
99
 
186
- `media/` 只用于文档:不在包的 `files` 清单里,因此永远不会进入 npm 包。
100
+ - **只读**:读取走 Harness 自带的 `workspaceFiles` Remote 命名空间,可读性由
101
+ Host 的文件系统决定。插件自身不持有文件权限、不做路径解析、不接触凭据。
102
+ - **不落数据**:不写磁盘、不写会话日志;视图状态保存在内存中,随会话一起释放。
103
+ - **Host 半边是空实现**:包的 Node 侧不注册任何服务、工具、提示词片段或事件。
187
104
 
188
105
  ## 已知限制
189
106
 
190
- - **只预览,不编辑**:不写入、不保存、不做 diff。
191
- - **JSON 按严格语法解析**:带尾逗号的 `tsconfig.json` 属于 JSONC——TypeScript 接
192
- 受、`JSON.parse` 不接受。此时窗格会说明原因并显示高亮源码,而不是勉强猜出一棵
193
- 树;注释与尾逗号不会被静默剥除。
194
- - **Markdown 不解析工作区词汇**:Markdown 里的相对图片路径与文件提及保持原样——
195
- 只有绝对 `http(s)` 图片会加载——因为解析它们需要阅读器为真实文件背书,而这个
196
- 窗格不做这件事。
197
- - **文本预览上限为前 2 000 行**:二进制或非 UTF-8 文件返回
198
- `workspace-file/not-text`;超过 Host 完整文件上限的返回
199
- `workspace-file/too-large`;更长的文本文件只展示首页并提示已截断,没有
200
- 「加载更多」。图片受 Host 的完整文件上限约束。
201
- - **未映射语法不高亮**:共享高亮器携带固定的语法集,集外后缀(`.vue`、
202
- `.proto`、`.graphql` 等)按纯文本显示,而不是近似高亮。
203
- - **仅目录列表**:没有搜索、过滤、重命名、右键菜单、当前文件高亮,也不监听文件
204
- 系统变化;某一层只通过 **重新读取** 更新。
205
- - **单一根目录**:目录树以会话工作目录为根,不能向上浏览;Host 本身也拒绝读取
206
- 工作区根之外的目录列表。
207
- - **不做编辑**:带行号与换行开关的等宽源码视图,Markdown 则为排版后的文档——
208
- 没有折叠、搜索和就地编辑。
107
+ - **只预览** —— 不编辑、不保存、不做 diff。
108
+ - **JSON 按严格语法解析**:注释或尾逗号会让文件成为 JSONC,`JSON.parse` 会拒绝
109
+ —— `tsconfig.json` 是最常见的情况。此时窗格会说明原因并显示高亮源码,而不是
110
+ 勉强猜出一棵树。
111
+ - **文本预览上限 2 000 行**,并提示已截断、没有「加载更多」;二进制文件会说明
112
+ 为何无法显示。
113
+ - **固定的语法集**:共享高亮器未包含的后缀按纯文本显示,绝不做近似高亮。
114
+ - **仅目录列表** —— 没有搜索、重命名、右键菜单,也不监听文件变化;某一层通过
115
+ **重新读取** 更新。
116
+ - **已加载的预览有上限**:最近使用的 5 个标签保留内容;更早的标签仍然开着,只是
117
+ 回到它时会重新读取。
118
+ - **单一根目录**:目录树以会话工作目录为根,Host 本身也拒绝读取工作区根之外的
119
+ 目录列表。
120
+ - **Markdown 不解析工作区词汇**:相对图片路径与文件提及保持原样,只有绝对
121
+ `http(s)` 图片会加载。
122
+
123
+ ## 参与开发
124
+
125
+ 构建、检查、产物模型,以及客户端 bundle 如何抵达浏览器:
126
+ [CONTRIBUTING.md](CONTRIBUTING.md)。
209
127
 
210
128
  ## 许可证
211
129