@jaxzhou/dsh-file-explorer 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-file-explorer contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # dsh-file-explorer
2
+
3
+ [![npm](https://img.shields.io/npm/v/@jaxzhou/dsh-file-explorer.svg)](https://www.npmjs.com/package/@jaxzhou/dsh-file-explorer)
4
+ [![license](https://img.shields.io/npm/l/@jaxzhou/dsh-file-explorer.svg)](LICENSE)
5
+
6
+ English | [中文](README.zh.md)
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.
11
+
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.
16
+
17
+ ## Demo
18
+
19
+ [![The Files tab beside Chat and Trajectory: a workspace tree on the left, and
20
+ on the right a preview that highlights source, renders Markdown and JSON, and
21
+ offers a Rendered/Source toggle](media/demo.gif)](media/demo.mp4)
22
+
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.
71
+
72
+ ## Install
73
+
74
+ From npm:
75
+
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
+ ```
80
+
81
+ From this repository (a checkout or a git ref), which builds nothing — the
82
+ runtime artifacts are committed:
83
+
84
+ ```sh
85
+ dsh plugin --profile web add /path/to/dsh-file-explorer
86
+ dsh plugin --profile web add github:jaxzhou/dsh-file-explorer
87
+ ```
88
+
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:
91
+
92
+ ```sh
93
+ dsh --profile myprofile --from-default-profile web
94
+ dsh plugin --profile myprofile add @jaxzhou/dsh-file-explorer
95
+ dsh --profile myprofile
96
+ ```
97
+
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
103
+
104
+ ```sh
105
+ dsh --profile web --dump-config | grep -A 2 'jaxzhou-file-explorer'
106
+ ```
107
+
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
145
+
146
+ ```sh
147
+ npm install
148
+ npm run check # typecheck, build both runtime artifacts, run the tests
149
+ ```
150
+
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.
154
+
155
+ The browser half must be a lazy CommonJS factory handoff, because the dsh Web
156
+ loader fetches it as a classic script:
157
+
158
+ ```js
159
+ window.__ModuleLoader__.load({ id: '@jaxzhou/dsh-file-explorer', factory: (require) => { … } })
160
+ ```
161
+
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.
167
+
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
+ ```
207
+
208
+ `media/` is documentation only: it is outside the package's `files` list, so it
209
+ never ships in the npm tarball.
210
+
211
+ ## Known limitations
212
+
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 load — because 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.
238
+
239
+ ## License
240
+
241
+ MIT — see [LICENSE](LICENSE).
package/README.zh.md ADDED
@@ -0,0 +1,212 @@
1
+ # dsh-file-explorer
2
+
3
+ [![npm](https://img.shields.io/npm/v/@jaxzhou/dsh-file-explorer.svg)](https://www.npmjs.com/package/@jaxzhou/dsh-file-explorer)
4
+ [![license](https://img.shields.io/npm/l/@jaxzhou/dsh-file-explorer.svg)](LICENSE)
5
+
6
+ [English](README.md) | 中文
7
+
8
+ > npm 上的包名是 **`@jaxzhou/dsh-file-explorer`**。无作用域的
9
+ > `dsh-file-explorer`、`dsh-files`、`dsh-workspace-files` 已被其他作者的插件占
10
+ > 用,因此本插件带上发布者作用域。
11
+
12
+ 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:
13
+ 在对话视图的标签栏中新增 **文件** 标签,与 **对话**、**轨迹** 同级,左侧是会话
14
+ 工作区的目录树,右侧是按文件类别选择的预览体。
15
+
16
+ ## 演示
17
+
18
+ [![文件标签与对话、轨迹并列:左侧是工作区目录树,右侧是预览——可高亮源码、渲染
19
+ Markdown 与 JSON,并提供「预览/源码」切换](media/demo.gif)](media/demo.mp4)
20
+
21
+ *30 秒录屏 —— 点击可打开完整画质的 MP4。* 录屏浏览了一个 Flutter + NestJS 工作
22
+ 区,依次预览 TypeScript、Swift、Dart、HTML、YAML、JavaScript 源码(带语法高亮与
23
+ 行号),用 **预览 / 源码** 切换展示同一个 `README.md` 的渲染结果与原文,最后停在
24
+ 一个会先自我解释、再回退到高亮源码的 `tsconfig.json` 上——它尾部的逗号让它成为
25
+ JSONC 而非严格 JSON,所以无法展开成树。
26
+
27
+ ## 功能
28
+
29
+ - **工作区目录树**:以会话的工作目录为根,逐层读取;目录在前,文件按自然名
30
+ 称排序。某一层在首次展开时读取,折叠后仍保留内容。
31
+ - **按文件类别选择合适的预览体**:
32
+
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
+ | 其它 | 其余全部后缀 | 带行号、可复制的纯文本 |
40
+
41
+ 未映射的语法(`.vue`、`.proto`、`.txt` 等)故意按纯文本显示,而不是猜测:错
42
+ 误的语法着色比不着色更容易误导。
43
+ - **预览读取**:文本类预览只读取前 2 000 行,图片类读取完整字节。超长行在空
44
+ 格处折行,每个单词保持完整;只有内部没有任何断点的 token(超长 URL、压缩后的
45
+ 一整段代码)才会被拆开,因为否则它只能被藏在横向滚动条之外。折行后的续行悬挂
46
+ 在正文下方,而不是行号下方。换行开关只在源码视图出现(那里才有意义)。
47
+ - **状态保留**:展开状态与当前选中文件保存在按会话独占的 store 中,因此切到
48
+ 「对话」再切回来(组件会卸载)不会丢失浏览位置。
49
+
50
+ 全部为只读:插件只浏览与预览,不会写入、重命名或删除任何文件。
51
+
52
+ ## 兼容性
53
+
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 等无浏览器界面不会出现该标签。
58
+
59
+ 插件本身没有配置项,`cordis.yml` 无需任何设置。
60
+
61
+ ## 安装
62
+
63
+ 从 npm 安装:
64
+
65
+ ```sh
66
+ dsh plugin --profile web add @jaxzhou/dsh-file-explorer
67
+ dsh --profile web # 重启 profile;bundle 成员在启动时生效
68
+ ```
69
+
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
+ 组合存在:
79
+
80
+ ```sh
81
+ dsh --profile myprofile --from-default-profile web
82
+ dsh plugin --profile myprofile add @jaxzhou/dsh-file-explorer
83
+ dsh --profile myprofile
84
+ ```
85
+
86
+ 打开一个已有工作区的会话,点击 **文件** 标签即可。若会话没有工作区目录,标签仍
87
+ 会显示,但页面会提示没有工作区目录。
88
+
89
+ ### 验证安装
90
+
91
+ ```sh
92
+ dsh --profile web --dump-config | grep -A 2 'jaxzhou-file-explorer'
93
+ ```
94
+
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
+ ## 开发
127
+
128
+ ```sh
129
+ npm install
130
+ npm run check # 类型检查 + 构建两个运行产物 + 运行测试
131
+ ```
132
+
133
+ `lib/index.js` 与 `lib/client.js` 是**提交进仓库的构建产物**:通过 git 或
134
+ tarball 安装时直接加载,不需要构建步骤,也不需要 `allowBuilds` 授权。
135
+
136
+ 浏览器半边必须是由模块加载器接收的惰性 CommonJS 工厂,因为 dsh Web 加载器以
137
+ 经典脚本方式抓取它:
138
+
139
+ ```js
140
+ window.__ModuleLoader__.load({ id: '@jaxzhou/dsh-file-explorer', factory: (require) => { … } })
141
+ ```
142
+
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
+ 件贡献的是格式判定与窗格本身,而不是第二套渲染器。
151
+
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
+ ```
185
+
186
+ `media/` 只用于文档:不在包的 `files` 清单里,因此永远不会进入 npm 包。
187
+
188
+ ## 已知限制
189
+
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
+ 没有折叠、搜索和就地编辑。
209
+
210
+ ## 许可证
211
+
212
+ MIT —— 见 [LICENSE](LICENSE)。
@@ -0,0 +1,16 @@
1
+ # Bundle layer of @jaxzhou/dsh-file-explorer.
2
+ #
3
+ # Applied after every earlier bundle layer when a profile lists this package in
4
+ # `dsh.profile.bundles`. The single row mounts the plugin's Host half (an inert
5
+ # module) and publishes the package's `dsh.client` declaration to the client
6
+ # module system, which serves `lib/client.js` to the browser. The browser half
7
+ # registers the "Files" Conversation View tab; unloading this row removes the
8
+ # tab again.
9
+ #
10
+ # The row `name` is the installed package name — scoped, so it cannot collide
11
+ # with another author's plugin. The `id` is what a profile's own patch layer
12
+ # overrides (to disable the tab without uninstalling), and it carries the scope
13
+ # for the same reason.
14
+ - insert:
15
+ - id: jaxzhou-file-explorer
16
+ name: '@jaxzhou/dsh-file-explorer'