@jaxzhou/dsh-file-explorer 0.1.2 → 0.1.3
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 +14 -2
- package/README.zh.md +10 -2
- package/lib/client.js +150 -3
- package/lib/client.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,8 @@ The right pane picks each tab's body from the file:
|
|
|
41
41
|
|
|
42
42
|
| Category | Preview |
|
|
43
43
|
|---|---|
|
|
44
|
-
| **Markdown** | Rendered GFM — headings, tables, task lists, quotes, math, footnotes, highlighted code fences — with a **Source** toggle |
|
|
44
|
+
| **Markdown** | Rendered GFM — headings, tables, task lists, quotes, math, footnotes, highlighted code fences — with a **Source** toggle, and **Export PDF** |
|
|
45
|
+
| **HTML** | Drawn as a page in a sandboxed frame: the file's own CSS applies, and its scripts run in an opaque origin that cannot reach this application. With a **Source** toggle, and **Export PDF** |
|
|
45
46
|
| **JSON** | A collapsible tree with per-value copy, and the same toggle |
|
|
46
47
|
| **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
48
|
| **Images** | PNG, JPEG, GIF, WebP, AVIF, BMP, ICO and SVG, drawn to the pane. An SVG goes through `<img>`, so its scripts never run |
|
|
@@ -50,7 +51,10 @@ The right pane picks each tab's body from the file:
|
|
|
50
51
|
Text previews wrap at their spaces and keep every word whole; an image reads its
|
|
51
52
|
complete bytes. Every text body carries a **Copy** control in the pane's toolbar,
|
|
52
53
|
which copies the file's own text — a rendered Markdown document copies its
|
|
53
|
-
Markdown source.
|
|
54
|
+
Markdown source. A rendered Markdown or HTML file also carries **Export PDF**,
|
|
55
|
+
which hands the page to the browser's print dialog: every browser offers "Save as
|
|
56
|
+
PDF" there, and its own layout engine keeps the text as text rather than
|
|
57
|
+
rasterising the page the way a canvas-based PDF library would.
|
|
54
58
|
|
|
55
59
|
## Requirements
|
|
56
60
|
|
|
@@ -112,6 +116,10 @@ restart the profile.
|
|
|
112
116
|
state lives in memory and is discarded with the session.
|
|
113
117
|
- **Inert Host half.** The package's Node side registers no service, tool, prompt
|
|
114
118
|
section, or event.
|
|
119
|
+
- **HTML runs sandboxed.** A previewed page's scripts execute in an opaque origin,
|
|
120
|
+
which cannot read this application's DOM, storage, or session; an exported page
|
|
121
|
+
is printed with its scripts removed. An SVG draws through `<img>`, so its scripts
|
|
122
|
+
never run at all.
|
|
115
123
|
|
|
116
124
|
## Known limitations
|
|
117
125
|
|
|
@@ -131,6 +139,10 @@ restart the profile.
|
|
|
131
139
|
Host refuses directory listings outside the workspace root.
|
|
132
140
|
- **Markdown has no workspace vocabulary.** Relative image paths and file
|
|
133
141
|
mentions stay inert; only absolute `http(s)` images load.
|
|
142
|
+
- **HTML previews do not resolve relative assets.** A page drawn from a Blob
|
|
143
|
+
document has no base to resolve its own `style.css` or images against, so only
|
|
144
|
+
absolute URLs load there too. An export prints the page as it renders without
|
|
145
|
+
its scripts, which a static PDF has no use for anyway.
|
|
134
146
|
|
|
135
147
|
## Contributing
|
|
136
148
|
|
package/README.zh.md
CHANGED
|
@@ -37,7 +37,8 @@ Markdown 与 JSON,并提供「预览/源码」切换](media/demo.gif)](media/d
|
|
|
37
37
|
|
|
38
38
|
| 类别 | 预览 |
|
|
39
39
|
|---|---|
|
|
40
|
-
| **Markdown** | 直接渲染为 GFM —— 标题、表格、任务列表、引用、公式、脚注、高亮的代码围栏 —— 并带 **源码**
|
|
40
|
+
| **Markdown** | 直接渲染为 GFM —— 标题、表格、任务列表、引用、公式、脚注、高亮的代码围栏 —— 并带 **源码** 切换与 **导出 PDF** |
|
|
41
|
+
| **HTML** | 在沙箱 iframe 中当作页面绘制:文件自己的 CSS 生效,其脚本运行在不透明源(opaque origin)里,无法触达本应用。带 **源码** 切换与 **导出 PDF** |
|
|
41
42
|
| **JSON** | 可折叠树,每个值可单独复制,同样带 **源码** 切换 |
|
|
42
43
|
| **源码** | 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
44
|
| **图片** | PNG、JPEG、GIF、WebP、AVIF、BMP、ICO、SVG,自动适配窗格。SVG 经 `<img>` 绘制,其中的脚本不会执行 |
|
|
@@ -45,7 +46,9 @@ Markdown 与 JSON,并提供「预览/源码」切换](media/demo.gif)](media/d
|
|
|
45
46
|
|
|
46
47
|
文本预览在空格处折行、保持单词完整;图片读取完整字节。每一种文本预览在窗格工具栏
|
|
47
48
|
里都有 **复制** 按钮,复制的是文件自身的文本——渲染态的 Markdown 复制的是它的
|
|
48
|
-
Markdown
|
|
49
|
+
Markdown 源码。渲染态的 Markdown 与 HTML 还带 **导出 PDF**:它把页面交给浏览器的
|
|
50
|
+
打印对话框,那里每个浏览器都提供「另存为 PDF」,而排版由浏览器自己的引擎完成
|
|
51
|
+
——文字仍是文字,不像基于 canvas 的 PDF 库那样把整页栅格化。
|
|
49
52
|
|
|
50
53
|
## 环境要求
|
|
51
54
|
|
|
@@ -101,6 +104,8 @@ profile。
|
|
|
101
104
|
Host 的文件系统决定。插件自身不持有文件权限、不做路径解析、不接触凭据。
|
|
102
105
|
- **不落数据**:不写磁盘、不写会话日志;视图状态保存在内存中,随会话一起释放。
|
|
103
106
|
- **Host 半边是空实现**:包的 Node 侧不注册任何服务、工具、提示词片段或事件。
|
|
107
|
+
- **HTML 在沙箱中运行**:预览页面的脚本执行于不透明源,无法读取本应用的 DOM、
|
|
108
|
+
存储或会话;导出时会先剥除脚本再打印。SVG 经 `<img>` 绘制,脚本完全不执行。
|
|
104
109
|
|
|
105
110
|
## 已知限制
|
|
106
111
|
|
|
@@ -119,6 +124,9 @@ profile。
|
|
|
119
124
|
目录列表。
|
|
120
125
|
- **Markdown 不解析工作区词汇**:相对图片路径与文件提及保持原样,只有绝对
|
|
121
126
|
`http(s)` 图片会加载。
|
|
127
|
+
- **HTML 预览不解析相对资源**:由 Blob 文档绘制出的页面没有可用来解析自身
|
|
128
|
+
`style.css` 或图片的 base,所以同样只有绝对 URL 能加载。导出时按渲染结果打印,
|
|
129
|
+
且不带脚本——静态 PDF 本来也用不上脚本。
|
|
122
130
|
|
|
123
131
|
## 参与开发
|
|
124
132
|
|
package/lib/client.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
FILES_VIEW_ID: () => FILES_VIEW_ID,
|
|
26
26
|
RETAINED_PREVIEWS: () => RETAINED_PREVIEWS,
|
|
27
27
|
apply: () => apply,
|
|
28
|
+
canExportPdf: () => canExportPdf,
|
|
28
29
|
extensionOf: () => extensionOf,
|
|
29
30
|
hasSourceToggle: () => hasSourceToggle,
|
|
30
31
|
inject: () => inject,
|
|
@@ -100,10 +101,14 @@ function previewFormatFor(path) {
|
|
|
100
101
|
if (lang === void 0) return { kind: "text", lang: void 0, mediaType: void 0 };
|
|
101
102
|
if (lang === "json") return { kind: "json", lang, mediaType: void 0 };
|
|
102
103
|
if (lang === "markdown") return { kind: "markdown", lang, mediaType: void 0 };
|
|
104
|
+
if (lang === "html") return { kind: "html", lang, mediaType: void 0 };
|
|
103
105
|
return { kind: "code", lang, mediaType: void 0 };
|
|
104
106
|
}
|
|
105
107
|
function hasSourceToggle(format) {
|
|
106
|
-
return format.kind === "markdown" || format.kind === "json";
|
|
108
|
+
return format.kind === "markdown" || format.kind === "html" || format.kind === "json";
|
|
109
|
+
}
|
|
110
|
+
function canExportPdf(format) {
|
|
111
|
+
return format.kind === "markdown" || format.kind === "html";
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
// src/client/FilesView.tsx
|
|
@@ -279,10 +284,30 @@ function HighlightedSource({
|
|
|
279
284
|
}
|
|
280
285
|
) });
|
|
281
286
|
}
|
|
287
|
+
function HtmlFrame({ html, title }) {
|
|
288
|
+
const url = (0, import_react.useMemo)(
|
|
289
|
+
() => URL.createObjectURL(new Blob([html], { type: "text/html" })),
|
|
290
|
+
[html]
|
|
291
|
+
);
|
|
292
|
+
(0, import_react.useEffect)(() => () => {
|
|
293
|
+
URL.revokeObjectURL(url);
|
|
294
|
+
}, [url]);
|
|
295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
296
|
+
"iframe",
|
|
297
|
+
{
|
|
298
|
+
className: "dsh-fe-html-frame",
|
|
299
|
+
src: url,
|
|
300
|
+
sandbox: "allow-scripts",
|
|
301
|
+
title,
|
|
302
|
+
"data-preview-html": true
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
}
|
|
282
306
|
function FormattedBody({
|
|
283
307
|
page,
|
|
284
308
|
format,
|
|
285
309
|
jsonDocument,
|
|
310
|
+
proseRef,
|
|
286
311
|
t
|
|
287
312
|
}) {
|
|
288
313
|
const copyLabel = t("code.copy");
|
|
@@ -327,7 +352,10 @@ function FormattedBody({
|
|
|
327
352
|
t
|
|
328
353
|
]);
|
|
329
354
|
if (format.kind === "markdown") {
|
|
330
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-fe-prose", "data-preview-format": "markdown", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.MarkdownText, { text: page.text, labels: markdownLabels }) });
|
|
355
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-fe-prose", ref: proseRef, "data-preview-format": "markdown", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.MarkdownText, { text: page.text, labels: markdownLabels }) });
|
|
356
|
+
}
|
|
357
|
+
if (format.kind === "html") {
|
|
358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-fe-html", "data-preview-format": "html", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HtmlFrame, { html: page.text, title: t("preview.htmlFrame") }) });
|
|
331
359
|
}
|
|
332
360
|
if (jsonDocument === void 0) return null;
|
|
333
361
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-fe-json", "data-preview-format": "json", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -340,6 +368,61 @@ function FormattedBody({
|
|
|
340
368
|
}
|
|
341
369
|
) });
|
|
342
370
|
}
|
|
371
|
+
function printableHtml(source, title) {
|
|
372
|
+
const parsed = new DOMParser().parseFromString(source, "text/html");
|
|
373
|
+
for (const script of [...parsed.querySelectorAll("script")]) script.remove();
|
|
374
|
+
for (const element of [...parsed.querySelectorAll("*")]) {
|
|
375
|
+
for (const attribute of [...element.attributes]) {
|
|
376
|
+
const name2 = attribute.name.toLowerCase();
|
|
377
|
+
if (name2.startsWith("on")) element.removeAttribute(attribute.name);
|
|
378
|
+
else if (/^\s*javascript:/i.test(attribute.value)) element.removeAttribute(attribute.name);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (parsed.title === "") {
|
|
382
|
+
const tag = parsed.createElement("title");
|
|
383
|
+
tag.textContent = title;
|
|
384
|
+
parsed.head.append(tag);
|
|
385
|
+
}
|
|
386
|
+
return `<!doctype html>${parsed.documentElement.outerHTML}`;
|
|
387
|
+
}
|
|
388
|
+
function appStyleTags() {
|
|
389
|
+
return [...document.querySelectorAll('style, link[rel="stylesheet"]')].map((node) => node.outerHTML).join("\n");
|
|
390
|
+
}
|
|
391
|
+
function styledDocument(title, body) {
|
|
392
|
+
const safeTitle = title.replace(/[<&]/g, (character) => character === "<" ? "<" : "&");
|
|
393
|
+
return [
|
|
394
|
+
'<!doctype html><html><head><meta charset="utf-8">',
|
|
395
|
+
`<title>${safeTitle}</title>`,
|
|
396
|
+
appStyleTags(),
|
|
397
|
+
"<style>body{margin:0;padding:28px 32px;background:#fff;color:#111;",
|
|
398
|
+
"print-color-adjust:exact;-webkit-print-color-adjust:exact}</style>",
|
|
399
|
+
`</head><body>${body}</body></html>`
|
|
400
|
+
].join("");
|
|
401
|
+
}
|
|
402
|
+
function createPrintFrame() {
|
|
403
|
+
const frame = document.createElement("iframe");
|
|
404
|
+
frame.setAttribute("aria-hidden", "true");
|
|
405
|
+
frame.setAttribute("data-preview-print", "");
|
|
406
|
+
frame.style.cssText = "position:fixed;left:-10000px;top:0;width:794px;height:1123px;border:0";
|
|
407
|
+
document.body.appendChild(frame);
|
|
408
|
+
return frame;
|
|
409
|
+
}
|
|
410
|
+
function printDocument(html) {
|
|
411
|
+
for (const stale of document.querySelectorAll("iframe[data-preview-print]")) stale.remove();
|
|
412
|
+
const frame = createPrintFrame();
|
|
413
|
+
const doc = frame.contentDocument;
|
|
414
|
+
if (doc === null) return;
|
|
415
|
+
doc.open();
|
|
416
|
+
doc.write(html);
|
|
417
|
+
doc.close();
|
|
418
|
+
const view = frame.contentWindow;
|
|
419
|
+
if (view === null) return;
|
|
420
|
+
view.addEventListener("afterprint", () => {
|
|
421
|
+
frame.remove();
|
|
422
|
+
}, { once: true });
|
|
423
|
+
view.focus();
|
|
424
|
+
view.print();
|
|
425
|
+
}
|
|
343
426
|
function bodyKindOf(content, format, mode, jsonWalkable) {
|
|
344
427
|
if (content !== null && content.kind === "image") return "image";
|
|
345
428
|
if (!hasSourceToggle(format)) return "source";
|
|
@@ -355,6 +438,7 @@ function PreviewBody({
|
|
|
355
438
|
jsonDocument,
|
|
356
439
|
jsonFallback,
|
|
357
440
|
wrap,
|
|
441
|
+
proseRef,
|
|
358
442
|
t
|
|
359
443
|
}) {
|
|
360
444
|
if (content.kind === "image") {
|
|
@@ -385,7 +469,16 @@ function PreviewBody({
|
|
|
385
469
|
"data-preview-body": body,
|
|
386
470
|
children: [
|
|
387
471
|
jsonFallback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-fe-note dsh-fe-note-error", "data-preview-row": "invalid-json", children: t("preview.jsonInvalid") }),
|
|
388
|
-
body === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
472
|
+
body === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
473
|
+
FormattedBody,
|
|
474
|
+
{
|
|
475
|
+
page,
|
|
476
|
+
format,
|
|
477
|
+
jsonDocument,
|
|
478
|
+
proseRef,
|
|
479
|
+
t
|
|
480
|
+
}
|
|
481
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HighlightedSource, { page, lang: format.lang, wrap, t }),
|
|
389
482
|
!page.eof && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-fe-note", "data-preview-row": "truncated", children: t("preview.truncated", { lines: page.lines }) })
|
|
390
483
|
]
|
|
391
484
|
}
|
|
@@ -403,6 +496,7 @@ function FilesView({
|
|
|
403
496
|
const cwd = useSessions((sessions) => sessions.byId[sessionId]?.cwd);
|
|
404
497
|
const state = useStore((store) => store);
|
|
405
498
|
const activeTabRef = (0, import_react.useRef)(null);
|
|
499
|
+
const proseRef = (0, import_react.useRef)(null);
|
|
406
500
|
const [copied, setCopied] = (0, import_react.useState)(null);
|
|
407
501
|
const copyTimer = (0, import_react.useRef)(null);
|
|
408
502
|
(0, import_react.useEffect)(() => () => {
|
|
@@ -468,6 +562,18 @@ function FilesView({
|
|
|
468
562
|
}, 1e3);
|
|
469
563
|
});
|
|
470
564
|
};
|
|
565
|
+
const exportPdf = () => {
|
|
566
|
+
if (active === null || format === null || content === null || content.kind !== "text") return;
|
|
567
|
+
if (!canExportPdf(format)) return;
|
|
568
|
+
const name2 = pathParts(active).name;
|
|
569
|
+
if (format.kind === "html") {
|
|
570
|
+
printDocument(printableHtml(content.page.text, name2));
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const prose = proseRef.current;
|
|
574
|
+
if (prose === null) return;
|
|
575
|
+
printDocument(styledDocument(name2, prose.outerHTML));
|
|
576
|
+
};
|
|
471
577
|
const closeTab = (path) => {
|
|
472
578
|
actions.closeFile(path);
|
|
473
579
|
};
|
|
@@ -582,6 +688,18 @@ function FilesView({
|
|
|
582
688
|
children: body === "source" ? t("preview.rendered") : t("preview.source")
|
|
583
689
|
}
|
|
584
690
|
),
|
|
691
|
+
active !== null && body === "rendered" && format !== null && canExportPdf(format) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
692
|
+
"button",
|
|
693
|
+
{
|
|
694
|
+
type: "button",
|
|
695
|
+
className: "dsh-fe-tool",
|
|
696
|
+
"aria-label": t("preview.pdf"),
|
|
697
|
+
title: t("preview.pdf"),
|
|
698
|
+
"data-preview-pdf": true,
|
|
699
|
+
onClick: exportPdf,
|
|
700
|
+
children: t("preview.pdf")
|
|
701
|
+
}
|
|
702
|
+
),
|
|
585
703
|
active !== null && body === "source" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
586
704
|
"button",
|
|
587
705
|
{
|
|
@@ -626,6 +744,7 @@ function FilesView({
|
|
|
626
744
|
jsonDocument,
|
|
627
745
|
jsonFallback,
|
|
628
746
|
wrap,
|
|
747
|
+
proseRef,
|
|
629
748
|
t
|
|
630
749
|
}
|
|
631
750
|
)
|
|
@@ -745,6 +864,8 @@ var zh = {
|
|
|
745
864
|
"preview.loading": "\u6B63\u5728\u8BFB\u53D6\u2026",
|
|
746
865
|
"preview.copy": "\u590D\u5236",
|
|
747
866
|
"preview.copied": "\u5DF2\u590D\u5236",
|
|
867
|
+
"preview.pdf": "\u5BFC\u51FA PDF",
|
|
868
|
+
"preview.htmlFrame": "HTML \u9884\u89C8",
|
|
748
869
|
"preview.reload": "\u91CD\u65B0\u8BFB\u53D6",
|
|
749
870
|
"preview.truncated": "\u6587\u4EF6\u8F83\u957F\uFF0C\u53EA\u663E\u793A\u524D {lines} \u884C\u3002",
|
|
750
871
|
"preview.wrap": "\u81EA\u52A8\u6362\u884C",
|
|
@@ -794,6 +915,8 @@ var en = {
|
|
|
794
915
|
"preview.loading": "Reading\u2026",
|
|
795
916
|
"preview.copy": "Copy",
|
|
796
917
|
"preview.copied": "Copied",
|
|
918
|
+
"preview.pdf": "Export PDF",
|
|
919
|
+
"preview.htmlFrame": "HTML preview",
|
|
797
920
|
"preview.reload": "Reload",
|
|
798
921
|
"preview.truncated": "This file is long, showing the first {lines} lines.",
|
|
799
922
|
"preview.wrap": "Wrap long lines",
|
|
@@ -1283,6 +1406,30 @@ var CSS = `
|
|
|
1283
1406
|
opacity: 0.6;
|
|
1284
1407
|
}
|
|
1285
1408
|
|
|
1409
|
+
/* An HTML file is drawn by the browser, in a frame that fills the pane. The
|
|
1410
|
+
white canvas is the page's own, not this pane's: an unstyled document still
|
|
1411
|
+
reads as a document rather than as a hole in the app.
|
|
1412
|
+
|
|
1413
|
+
The host takes its height from the scrollport's content box, so the frame ends
|
|
1414
|
+
exactly where the pane's bottom inset begins and the scrollport itself never
|
|
1415
|
+
scrolls \u2014 a page scrolls inside its own frame. */
|
|
1416
|
+
.dsh-fe-html {
|
|
1417
|
+
display: flex;
|
|
1418
|
+
height: 100%;
|
|
1419
|
+
min-height: 0;
|
|
1420
|
+
min-width: 0;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
.dsh-fe-html-frame {
|
|
1424
|
+
flex: 1 1 auto;
|
|
1425
|
+
width: 100%;
|
|
1426
|
+
height: 100%;
|
|
1427
|
+
min-width: 0;
|
|
1428
|
+
min-height: 0;
|
|
1429
|
+
border: 0;
|
|
1430
|
+
background: #fff;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1286
1433
|
/* Rendered Markdown: prose lays out in normal white space, with the pane's
|
|
1287
1434
|
insets and no monospace inheritance from the source view. */
|
|
1288
1435
|
.dsh-fe-prose {
|
package/lib/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.ts", "../src/client/FilesView.tsx", "../src/client/format.ts", "../src/client/face.ts", "../src/client/locales.ts", "../src/client/store.ts", "../src/client/styles.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Browser half: register `files` as one Conversation View.\n *\n * The public path, unmodified: the view into the `conversation.view` list slot\n * that `@deepseek-ai/dsh-client-ui-conversation` owns and renders as the tab\n * strip beside Chat and Trajectory. The registration carries the exclusive\n * per-Session store, the injected Remote face, and the locale namespace; the\n * slot service's effect wrapper means plugin unload removes the tab, its\n * dictionary, and its stylesheet, and disposal of the declaration removes the\n * contribution again if the Conversation shell is remounted.\n *\n * The file split is this package's layering: what the explorer keeps\n * (`store.ts`), how it lists and reads (`face.ts`), what it draws\n * (`FilesView.tsx`), what it looks like (`styles.ts`), what it says\n * (`locales.ts`), and this module, which only wires them together.\n */\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\n// Type-only: pulls the Client Remote face (`ctx.remote`).\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\n// Type-only: pulls the locale service's Context merge (`ctx.locale`).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: the 'conversation.view' SlotMap row and the owner props the view\n// component derives must be in the program for the register call to type.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the slot service's Context merge (`ctx.slots`).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport { FilesView } from './FilesView.tsx'\nimport { filesFace } from './face.ts'\nimport type { FilesInjected } from './face.ts'\nimport { en, NS, zh } from './locales.ts'\nimport { createFilesStore } from './store.ts'\nimport { installStyles } from './styles.ts'\n\nexport { parseJsonDocument, previewLines, tabLabels } from './FilesView.tsx'\nexport { extensionOf, hasSourceToggle, previewFormatFor } from './format.ts'\nexport type { PreviewFormat, PreviewFormatKind } from './format.ts'\nexport type { FileExplorerKey } from './locales.ts'\nexport { RETAINED_PREVIEWS } from './store.ts'\nexport type {\n DirLevel, FilesState, LevelState, PreviewContent, PreviewImage, PreviewMode, PreviewState,\n PreviewText,\n} from './store.ts'\nexport type { FilesInjected, WorkspaceFilesRemote } from './face.ts'\nexport type { FilesViewProps } from './FilesView.tsx'\n\n/** This plugin's identity inside the browser plugin tree. */\nexport const name = 'file-explorer'\n\n/**\n * Required browser services: the slot registry, copy, and the Remote carrier\n * with the workspace-files namespace the explorer reads through.\n */\nexport const inject = ['slots', 'locale', 'remote', 'remote.workspaceFiles']\n\n/**\n * The view's id inside `conversation.view`. It is the tab's identity, the key\n * a stored View preference names, and the entry id the header selects by.\n */\nexport const FILES_VIEW_ID = 'files'\n\n/**\n * Client plugin body: register the stylesheet, the dictionaries, and the view.\n * @param ctx - client root context carrying the slot registry, copy, and Remote face.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(installStyles, '@jaxzhou/dsh-file-explorer: stylesheet')\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), '@jaxzhou/dsh-file-explorer: dictionaries')\n // Registration-time text (the view tab label) reads through the bound\n // translate as a thunk, so it follows the active locale without\n // re-registration.\n const t = ctx.locale.bind(NS)\n const store = createFilesStore()\n const lifetime = new AbortController()\n ctx.effect(() => () => { lifetime.abort() }, '@jaxzhou/dsh-file-explorer: request lifetime')\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register({\n name: 'conversation.view',\n id: FILES_VIEW_ID,\n // After Chat (default) and Trajectory (10), so the strip reads\n // Chat | Trajectory | Files.\n order: 20,\n locale: NS,\n label: () => t('view.files'),\n store,\n inject: (\n sessionId: SessionId,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n ): FilesInjected => filesFace(ctx.remote, sessionId, lifetime.signal, actions),\n }, FilesView))\n}\n", "/**\n * The Files Conversation View: the session workspace on the left, one preview\n * tab per opened file on the right.\n *\n * The view is one `conversation.view` entry, so it sits beside Chat and\n * Trajectory in the same tab strip and the shell renders it one at a time.\n * Everything the tree keeps lives in its store; everything it asks for goes\n * through its injected face. The component only decides what to draw for each\n * absolute path and what a click means: a directory toggles, a file opens a\n * preview tab (or focuses the one already open for it), and anything else is\n * shown but refuses to open.\n *\n * A tab's body follows the file's format (see `format.ts`): Markdown renders as a\n * document with a source view behind a toggle, JSON as a collapsible tree with\n * the same toggle, a recognized source suffix through the shared\n * syntax-highlighted code block, an image as the image, and everything else as\n * plain numbered text. All of it comes from\n * `@deepseek-ai/dsh-client-ui-primitives`, which the browser shell shares into its\n * frozen module table \u2014 the one way this bundle may use another package's values\n * at runtime.\n *\n * Switching to another tab unmounts this component but not its Session-scoped\n * store, so the tree, the open tabs, and the active tab survive the round trip. A\n * read left in flight when that happens settles into the store anyway, because\n * the face's requests ride the plugin's lifetime, not the component's.\n */\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport type { ConvViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { InjectFace, PropsLocale, PropsStore, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport {\n CodeBlock, FileTypeIcon, IconFolderClose16, IconFolderOpen16, IconRefreshOutline16, JsonTree,\n MarkdownText, classifyFileType, fileSizeText, writeClipboard,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { JsonTreeLabels, MarkdownLabels } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\nimport { hasSourceToggle, previewFormatFor } from './format.ts'\nimport type { PreviewFormat } from './format.ts'\nimport type { FilesInjected } from './face.ts'\nimport type {\n FilesState, LevelState, PreviewContent, PreviewMode, PreviewState, PreviewText,\n createFilesStore,\n} from './store.ts'\nimport type {} from './locales.ts'\n\n/** The view's composed props: the Conversation View seat, its store, its face, and its copy. */\nexport type FilesViewProps =\n & ConvViewProps\n & PropsStore<ReturnType<typeof createFilesStore>>\n & InjectFace<FilesInjected>\n & PropsLocale<'fileExplorer'>\n\n/** Natural, case-insensitive name order, so `file2` precedes `file10`. */\nconst byName = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })\n\n/**\n * Order one level's entries for display: directories first, then everything\n * else, each group by name. The endpoint's order is a listing fact; this is the\n * reader's.\n * @param entries - the listing as the endpoint returned it.\n * @returns a new array, directories first, then by name within each group.\n */\nexport function orderEntries(entries: readonly WorkspaceDirectoryEntry[]): WorkspaceDirectoryEntry[] {\n return [...entries].sort((left, right) => {\n const group = Number(right.type === 'directory') - Number(left.type === 'directory')\n return group !== 0 ? group : byName.compare(left.name, right.name)\n })\n}\n\n/**\n * The absolute path of one child entry.\n *\n * Joined with `/` whatever the parent's separators: the host resolves mixed\n * separators, and the tree only needs a stable key.\n * @param parent - absolute path of the listed directory.\n * @param name - the entry's basename.\n * @returns the child's absolute path.\n */\nexport function childPath(parent: string, name: string): string {\n return `${parent.replace(/[/\\\\]+$/, '')}/${name}`\n}\n\n/**\n * Split a path into its directory prefix and its last segment, for a header\n * that greys the prefix and inks the name.\n * @param path - absolute path.\n * @returns the trailing-slash directory prefix and the basename.\n */\nexport function pathParts(path: string): { directory: string; name: string } {\n const at = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'))\n if (at < 0) return { directory: '', name: path }\n return { directory: path.slice(0, at + 1), name: path.slice(at + 1) }\n}\n\n/**\n * The display label of every open tab.\n *\n * A tab shows its basename, which is what a strip can afford. Two open files\n * that share a basename are ambiguous on their own \u2014 `index.ts` in three\n * directories is the ordinary case \u2014 so those tabs carry their parent directory\n * as a prefix, and only those. The full path stays in each tab's tooltip.\n * @param open - open tab paths, in strip order.\n * @returns a label per path.\n */\nexport function tabLabels(open: readonly string[]): Record<string, string> {\n const twins = new Map<string, number>()\n for (const path of open) {\n const { name } = pathParts(path)\n twins.set(name, (twins.get(name) ?? 0) + 1)\n }\n const labels: Record<string, string> = {}\n for (const path of open) {\n const { directory, name } = pathParts(path)\n if ((twins.get(name) ?? 0) < 2) {\n labels[path] = name\n continue\n }\n const parent = pathParts(directory.replace(/[/\\\\]+$/, '')).name\n labels[path] = parent === '' ? name : `${parent}/${name}`\n }\n return labels\n}\n\n/**\n * Split one loaded page into its source lines.\n *\n * The page's own line count decides: `0` is a page past the file's last line\n * (an empty file on the first read), which has no lines to draw and must not\n * become one empty row. Otherwise the text is the page's lines joined with\n * `\\n`, so splitting returns exactly what `lines` promised.\n * @param page - the loaded text page.\n * @returns the page's source lines, in order.\n */\nexport function previewLines(page: PreviewText): string[] {\n return page.lines === 0 ? [] : page.text.split('\\n')\n}\n\n/**\n * Parse one complete JSON document for the tree view.\n * @param text - the file's text.\n * @returns the parsed container, or undefined when the text is not JSON a tree\n * can walk (a syntax error, or a bare scalar).\n */\nexport function parseJsonDocument(text: string): object | undefined {\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch {\n return undefined\n }\n return typeof value === 'object' && value !== null ? value : undefined\n}\n\n/**\n * Say why a directory could not be listed, in terms of the directory.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show under the directory.\n */\nexport function treeFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('tree.error.notFound')\n case 'workspace-file/outside-workspace': return t('tree.error.outsideWorkspace')\n case 'workspace-file/not-directory': return t('tree.error.notDirectory')\n // Carrier and unclassified host failures reach the reader as themselves:\n // this tree knows nothing useful to add to a transport-level message.\n default: return t('tree.error.unavailable', { message: failure.message })\n }\n}\n\n/**\n * Say why a file could not be previewed, in terms of the file.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show in the preview pane.\n */\nexport function previewFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('preview.error.notFound')\n case 'workspace-file/not-text': return t('preview.error.notText')\n case 'workspace-file/too-large': return t('preview.error.tooLarge')\n case 'workspace-file/not-regular-file': return t('preview.error.notRegularFile')\n default: return t('preview.error.unavailable', { message: failure.message })\n }\n}\n\n/** What every level shares: the explorer's state and the two gestures. */\ninterface TreeContext {\n readonly state: FilesState\n readonly open: ReadonlySet<string>\n readonly onToggle: (path: string) => void\n readonly onOpen: (path: string) => void\n readonly t: TranslateNS<'fileExplorer'>\n}\n\n/** One directory's rows: its state while listing, its entries once listed. */\nfunction Level({ path, tree }: { path: string; tree: TreeContext }): ReactNode {\n const { state, t } = tree\n const level: LevelState | undefined = state.levels[path]\n if (level === undefined || level.kind === 'loading') {\n return <li className=\"dsh-fe-note\" data-files-row=\"loading\">{t('tree.loading')}</li>\n }\n if (level.kind === 'failed') {\n return (\n <li\n className=\"dsh-fe-note dsh-fe-note-error\"\n data-files-row=\"failed\"\n data-files-code={level.failure.code}\n >\n {treeFailureLine(t, level.failure)}\n </li>\n )\n }\n const entries = orderEntries(level.level.entries)\n return (\n <>\n {entries.length === 0 && <li className=\"dsh-fe-note\" data-files-row=\"empty\">{t('tree.empty')}</li>}\n {entries.map(entry => <Entry key={entry.name} parent={path} entry={entry} tree={tree} />)}\n {level.level.truncated && (\n <li className=\"dsh-fe-note\" data-files-row=\"truncated\">{t('tree.truncated')}</li>\n )}\n </>\n )\n}\n\n/** One entry's row, and its children when it is an expanded directory. */\nfunction Entry({\n parent,\n entry,\n tree,\n}: {\n parent: string\n entry: WorkspaceDirectoryEntry\n tree: TreeContext\n}): ReactNode {\n const path = childPath(parent, entry.name)\n if (entry.type === 'directory') {\n const expanded = tree.state.expanded.includes(path)\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"directory\" data-files-path={path}>\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-expanded={expanded}\n onClick={() => { tree.onToggle(path) }}\n >\n {expanded\n ? <IconFolderOpen16 className=\"dsh-fe-icon\" />\n : <IconFolderClose16 className=\"dsh-fe-icon\" />}\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </button>\n {expanded && <ul className=\"dsh-fe-level\"><Level path={path} tree={tree} /></ul>}\n </li>\n )\n }\n if (entry.type === 'file') {\n const isOpen = tree.open.has(path)\n return (\n <li\n className=\"dsh-fe-item\"\n data-files-entry=\"file\"\n data-files-path={path}\n data-files-open={isOpen || undefined}\n >\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-current={tree.state.active === path}\n onClick={() => { tree.onOpen(path) }}\n >\n <FileTypeIcon kind={classifyFileType(entry.name)} size={16} />\n <span className=\"dsh-fe-name\">{entry.name}</span>\n {isOpen && <span className=\"dsh-fe-open-dot\" aria-hidden=\"true\" />}\n </button>\n </li>\n )\n }\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"other\" data-files-path={path}>\n <span className=\"dsh-fe-row dsh-fe-row-static\" title={tree.t('tree.other')}>\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </span>\n </li>\n )\n}\n\n/**\n * One file's source text through the shared syntax highlighter.\n *\n * `CodeBlock` owns the grammar lookup, the numbered gutter, and the copy\n * control; the owning class only replaces the chat card's chrome (margin,\n * radius, grey fill) with the pane's, and states the wrap preference: lines wrap\n * at their spaces and keep every word whole, and a token that cannot fit on a\n * line by itself still breaks rather than sliding under a scrollbar.\n */\nfunction HighlightedSource({\n page,\n lang,\n wrap,\n t,\n}: {\n page: PreviewText\n lang: string | undefined\n wrap: boolean\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n return (\n <div className=\"dsh-fe-source\" data-wrap={wrap} data-preview-lang={lang ?? 'plain'}>\n <CodeBlock\n code={page.text}\n lang={lang}\n lineNumbers\n copyLabel={t('code.copy')}\n copiedLabel={t('code.copied')}\n />\n </div>\n )\n}\n\n/**\n * A Markdown file as a document, and a JSON file as a collapsible tree.\n *\n * The label objects are memoized on the translated strings rather than on `t`:\n * the bound translate keeps one identity across a locale change while its output\n * changes, and a fresh labels object on every render would discard the\n * primitives' parse memos.\n */\nfunction FormattedBody({\n page,\n format,\n jsonDocument,\n t,\n}: {\n page: PreviewText\n format: PreviewFormat\n /** Parsed JSON for a `json` format, decided by the pane before it elected this body. */\n jsonDocument: object | undefined\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n const copyLabel = t('code.copy')\n const copiedLabel = t('code.copied')\n const footnotes = t('footnotes')\n const markdownLabels = useMemo<MarkdownLabels>(\n () => ({ code: { copyLabel, copiedLabel }, footnotes }),\n [copyLabel, copiedLabel, footnotes],\n )\n const copyValue = t('json.copyValue')\n const copyJson = t('json.copyJson')\n const copyPath = t('json.copyPath')\n const copyPrettyJson = t('json.copyPrettyJson')\n const copyCompactJson = t('json.copyCompactJson')\n const copied = t('json.copied')\n const copyFailed = t('json.copyFailed')\n const collapseNode = t('json.collapseNode')\n const expandNode = t('json.expandNode')\n const jsonLabels = useMemo<JsonTreeLabels>(() => ({\n copyValue,\n copyJson,\n copyPath,\n copyPrettyJson,\n copyCompactJson,\n copied,\n copyFailed,\n collapseNode,\n expandNode,\n // Reads the live translate at call time, so the tooltip follows a locale\n // change without rebuilding this object; `t` is stable by contract.\n copyButtonTitle: action => t('json.copyTitle', { action }),\n }), [\n collapseNode, copyCompactJson, copyFailed, copyJson, copyPath, copyPrettyJson,\n copyValue, copied, expandNode, t,\n ])\n\n if (format.kind === 'markdown') {\n return (\n <div className=\"dsh-fe-prose\" data-preview-format=\"markdown\">\n <MarkdownText text={page.text} labels={markdownLabels} />\n </div>\n )\n }\n // The pane elects this body for `json` only once the document parses.\n if (jsonDocument === undefined) return null\n return (\n <div className=\"dsh-fe-json\" data-preview-format=\"json\">\n <JsonTree\n data={jsonDocument}\n label={t('preview.jsonLabel')}\n labels={jsonLabels}\n expandTopLevel={false}\n />\n </div>\n )\n}\n\n/** Which body a tab is drawing, which is also which controls apply. */\ntype PreviewBodyKind = 'rendered' | 'source' | 'image'\n\n/**\n * Resolve the body a file's format and the tab's mode choice agree on.\n * @param content - what the read delivered, or null while it has not settled.\n * @param format - the file's format.\n * @param mode - the tab's explicit choice, or undefined to follow the default.\n * @param jsonWalkable - whether a `json` format's document parsed into a tree.\n * @returns the body kind.\n */\nfunction bodyKindOf(\n content: PreviewContent | null,\n format: PreviewFormat,\n mode: PreviewMode | undefined,\n jsonWalkable: boolean,\n): PreviewBodyKind {\n if (content !== null && content.kind === 'image') return 'image'\n if (!hasSourceToggle(format)) return 'source'\n if ((mode ?? 'rendered') === 'source') return 'source'\n // JSON the tree cannot walk falls back to its source, so the controls offer the\n // body that is actually on screen.\n if (format.kind === 'json' && !jsonWalkable) return 'source'\n return 'rendered'\n}\n\n/**\n * The body of one settled preview tab.\n * @param props - the tab's content, its format, the elected body, and copy.\n * @returns the scrollport holding that body.\n */\nfunction PreviewBody({\n path,\n content,\n format,\n body,\n jsonDocument,\n jsonFallback,\n wrap,\n t,\n}: {\n path: string\n content: PreviewContent\n format: PreviewFormat\n body: PreviewBodyKind\n jsonDocument: object | undefined\n /** The reader asked for the tree and the file does not parse; say so above the source. */\n jsonFallback: boolean\n wrap: boolean\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n if (content.kind === 'image') {\n const { name } = pathParts(path)\n return (\n <div className=\"dsh-fe-scroll dsh-fe-imagehost\" data-preview-state=\"ready\" data-preview-format=\"image\">\n <img className=\"dsh-fe-image\" src={content.image.dataUrl} alt={name} data-preview-image />\n </div>\n )\n }\n const page = content.page\n const lines = previewLines(page)\n if (lines.length === 0) {\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines=\"0\"\n >\n <p className=\"dsh-fe-note\" data-preview-row=\"empty\">{t('preview.empty')}</p>\n </div>\n )\n }\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines={lines.length}\n data-preview-body={body}\n >\n {jsonFallback && (\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-row=\"invalid-json\">\n {t('preview.jsonInvalid')}\n </p>\n )}\n {body === 'rendered'\n ? <FormattedBody page={page} format={format} jsonDocument={jsonDocument} t={t} />\n : <HighlightedSource page={page} lang={format.lang} wrap={wrap} t={t} />}\n {!page.eof && (\n <p className=\"dsh-fe-note\" data-preview-row=\"truncated\">\n {t('preview.truncated', { lines: page.lines })}\n </p>\n )}\n </div>\n )\n}\n\n/**\n * The Files view: the workspace tree, the open preview tabs, and the active tab.\n * @param props - the Conversation View seat, store, injected face, and copy.\n * @returns the two-pane explorer.\n */\nexport function FilesView({\n sessionId, useSessions, useStore, actions, list, read, t,\n}: FilesViewProps): ReactNode {\n const cwd = useSessions(sessions => sessions.byId[sessionId]?.cwd)\n const state = useStore(store => store)\n const activeTabRef = useRef<HTMLButtonElement | null>(null)\n /** The tab whose text was just copied, so only its button confirms. */\n const [copied, setCopied] = useState<string | null>(null)\n const copyTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n useEffect(() => () => {\n if (copyTimer.current !== null) clearTimeout(copyTimer.current)\n }, [])\n\n useEffect(() => {\n if (cwd === undefined || state.root === cwd) return\n actions.start(cwd)\n list(cwd)\n }, [actions, cwd, list, state.root])\n\n const active = state.active\n const format: PreviewFormat | null = active === null ? null : previewFormatFor(active)\n const content = active !== null && state.previews[active]?.kind === 'ready'\n ? (state.previews[active] as { content: PreviewContent }).content\n : null\n\n // A JSON document is parsed here, once per settled read, because both the\n // controls' body election and the tree body need the answer. Parsing stays a\n // function of the pane's own decision, not of the read: a file the tree cannot\n // walk is still previewed, as source.\n const jsonDocument = useMemo(() => {\n if (active === null || content === null || content.kind !== 'text') return undefined\n if ((state.modes[active] ?? 'rendered') === 'source') return undefined\n if (previewFormatFor(active).kind !== 'json') return undefined\n return parseJsonDocument(content.page.text)\n }, [active, content, state.modes])\n\n /**\n * Content follows the active tab, not the click that opened it: a tab whose\n * content was dropped for retention reads again the moment it is shown, and a\n * tab that is already loaded costs nothing.\n */\n useEffect(() => {\n if (active === null) return\n if (state.previews[active] !== undefined) return\n read(active)\n }, [active, read, state.previews])\n\n useEffect(() => {\n activeTabRef.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }, [active])\n\n if (cwd === undefined) {\n return (\n <div className=\"dsh-fe-root\" data-files-state=\"no-workspace\">\n <div className=\"dsh-fe-status\">{t('tree.noWorkspace')}</div>\n </div>\n )\n }\n if (state.root === null) return null\n\n const openSet = new Set(state.open)\n const tree: TreeContext = {\n state,\n open: openSet,\n onToggle: (path) => {\n const loaded = state.levels[path] !== undefined\n actions.toggled(path)\n if (!loaded) list(path)\n },\n onOpen: (path) => { actions.openFile(path) },\n t,\n }\n // Reload drops every level and asks again for the expanded ones; a collapsed\n // level is fetched again the next time it opens.\n const reloadTree = (): void => {\n actions.reset()\n for (const path of state.expanded) list(path)\n }\n const reloadPreview = (): void => {\n if (active !== null) read(active)\n }\n // One copy control serves every body that has text: a rendered document's\n // source, a JSON tree's document, highlighted code, and plain text all copy\n // the file's own text, which is what a reader pastes somewhere else.\n const copyActive = (): void => {\n if (active === null || content === null || content.kind !== 'text') return\n const path = active\n const text = content.page.text\n void writeClipboard(text).then((ok: boolean) => {\n if (!ok) return\n setCopied(path)\n if (copyTimer.current !== null) clearTimeout(copyTimer.current)\n copyTimer.current = setTimeout(() => { setCopied(null) }, 1000)\n })\n }\n const closeTab = (path: string): void => {\n actions.closeFile(path)\n }\n const root = pathParts(state.root)\n const labels = tabLabels(state.open)\n const wrap = active === null ? true : state.wraps[active] ?? true\n const preview: PreviewState | undefined = active === null ? undefined : state.previews[active]\n const body = bodyKindOf(content, format ?? { kind: 'text', lang: undefined, mediaType: undefined }, active === null ? undefined : state.modes[active], jsonDocument !== undefined)\n const jsonFallback = format?.kind === 'json'\n && (active === null ? 'rendered' : state.modes[active] ?? 'rendered') === 'rendered'\n && jsonDocument === undefined\n && content !== null\n && content.kind === 'text'\n const meta = content === null\n ? null\n : content.kind === 'text'\n ? [\n t('preview.lines', { lines: content.page.lines }),\n content.page.bytes === undefined ? null : fileSizeText(content.page.bytes),\n ].filter(part => part !== null).join(' \u00B7 ')\n : content.image.bytes === undefined\n ? null\n : fileSizeText(content.image.bytes)\n\n return (\n <div\n className=\"dsh-fe-root\"\n data-files-state=\"tree\"\n data-files-root={state.root}\n data-conversation-composer-overlay=\"\"\n >\n <div className=\"dsh-fe-panes\">\n <div className=\"dsh-fe-tree\" data-files-pane=\"tree\">\n <div className=\"dsh-fe-head\">\n <span className=\"dsh-fe-path\" title={state.root}>\n <span>\n <span className=\"dsh-fe-path-muted\">{root.directory}</span>\n {root.name}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('tree.reload')}\n title={t('tree.reload')}\n data-files-reload\n onClick={reloadTree}\n >\n <IconRefreshOutline16 />\n </button>\n </div>\n <div className=\"dsh-fe-scroll\">\n <ul className=\"dsh-fe-level\"><Level path={state.root} tree={tree} /></ul>\n </div>\n </div>\n <div className=\"dsh-fe-preview\" data-files-pane=\"preview\">\n <div className=\"dsh-fe-head dsh-fe-tabhead\">\n {state.open.length === 0\n ? <span className=\"dsh-fe-path dsh-fe-path-muted\">{t('preview.title')}</span>\n : (\n <div className=\"dsh-fe-tabs\" role=\"tablist\" aria-label={t('preview.tabs')} data-preview-tabs>\n {state.open.map(path => {\n const isActive = path === active\n return (\n <span className=\"dsh-fe-tab\" key={path} role=\"presentation\" data-preview-tab={path} data-active={isActive || undefined}>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n className=\"dsh-fe-tab-label\"\n title={path}\n ref={isActive ? activeTabRef : undefined}\n data-preview-tab-open={path}\n onClick={() => { actions.activate(path) }}\n onAuxClick={(event) => {\n // Middle click closes, as it does in every editor.\n if (event.button === 1) closeTab(path)\n }}\n >\n <FileTypeIcon kind={classifyFileType(path)} size={14} />\n <span className=\"dsh-fe-tab-name\">{labels[path]}</span>\n </button>\n <button\n type=\"button\"\n className=\"dsh-fe-tab-close\"\n aria-label={t('preview.close', { name: labels[path] })}\n title={t('preview.close', { name: labels[path] })}\n data-preview-tab-close={path}\n onClick={() => { closeTab(path) }}\n >\n <span aria-hidden=\"true\">\u00D7</span>\n </button>\n </span>\n )\n })}\n </div>\n )}\n {active !== null && body === 'source' && format?.lang !== undefined && (\n <span className=\"dsh-fe-lang\" data-preview-lang-badge>{format.lang}</span>\n )}\n {content !== null && meta !== null && (\n <span className=\"dsh-fe-meta\" data-preview-meta>{meta}</span>\n )}\n {content !== null && content.kind === 'text' && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.copy')}\n title={t('preview.copy')}\n data-preview-copy\n onClick={copyActive}\n >\n {copied === active ? t('preview.copied') : t('preview.copy')}\n </button>\n )}\n {active !== null && format !== null && hasSourceToggle(format) && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={body === 'source'}\n aria-label={body === 'source' ? t('preview.rendered') : t('preview.source')}\n title={body === 'source' ? t('preview.rendered') : t('preview.source')}\n data-preview-mode-toggle\n onClick={() => { actions.setMode(active, body === 'source' ? 'rendered' : 'source') }}\n >\n {body === 'source' ? t('preview.rendered') : t('preview.source')}\n </button>\n )}\n {active !== null && body === 'source' && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={wrap}\n aria-label={wrap ? t('preview.nowrap') : t('preview.wrap')}\n title={wrap ? t('preview.nowrap') : t('preview.wrap')}\n data-preview-wrap-toggle\n onClick={() => { actions.setWrap(active, !wrap) }}\n >\n {wrap ? '\u21B5' : '\u2192'}\n </button>\n )}\n {active !== null && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.reload')}\n title={t('preview.reload')}\n data-preview-reload\n onClick={reloadPreview}\n >\n <IconRefreshOutline16 />\n </button>\n )}\n </div>\n {active === null && <div className=\"dsh-fe-status\">{t('preview.placeholder')}</div>}\n {active !== null && (preview === undefined || preview.kind === 'loading') && (\n <div className=\"dsh-fe-status\">{t('preview.loading')}</div>\n )}\n {active !== null && preview?.kind === 'failed' && (\n <div className=\"dsh-fe-scroll\" data-preview-state=\"failed\">\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-code={preview.failure.code}>\n {previewFailureLine(t, preview.failure)}\n </p>\n <button type=\"button\" className=\"dsh-fe-tool\" onClick={reloadPreview}>\n {t('preview.reload')}\n </button>\n </div>\n )}\n {active !== null && content !== null && format !== null && (\n <PreviewBody\n path={active}\n content={content}\n format={format}\n body={body}\n jsonDocument={jsonDocument}\n jsonFallback={jsonFallback}\n wrap={wrap}\n t={t}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n", "/**\n * What a workspace file is, for preview purposes.\n *\n * The pane picks a display from the file's own name before it reads anything,\n * so the read itself can differ: an image needs its complete bytes, everything\n * else needs a page of text. The decision is a pure function of the path, which\n * keeps it testable and keeps the read path honest \u2014 the pane never reads bytes\n * it will not draw.\n *\n * Only grammars the shared syntax highlighter actually carries are mapped. An\n * unrecognized suffix is plain text on purpose: a wrong grammar colours the\n * file misleadingly, while plain numbered text is merely unadorned.\n */\n\n/** The display a file's contents get. */\nexport type PreviewFormatKind = 'markdown' | 'json' | 'code' | 'image' | 'text'\n\n/** One file's preview format. */\nexport interface PreviewFormat {\n /** Which body the pane draws. */\n readonly kind: PreviewFormatKind\n /** Grammar hint for the highlighted source view; absent when no grammar is known. */\n readonly lang: string | undefined\n /** Media type the read bytes are labelled with; present only for `image`. */\n readonly mediaType: string | undefined\n}\n\n/** Image suffixes and the media type their bytes carry. */\nconst IMAGE_MEDIA_TYPES = new Map<string, string>([\n ['png', 'image/png'],\n ['apng', 'image/apng'],\n ['jpg', 'image/jpeg'],\n ['jpeg', 'image/jpeg'],\n ['jfif', 'image/jpeg'],\n ['gif', 'image/gif'],\n ['webp', 'image/webp'],\n ['avif', 'image/avif'],\n ['bmp', 'image/bmp'],\n ['ico', 'image/x-icon'],\n // An SVG draws as an image here; `<img>` neither runs its scripts nor lets it\n // reach the application origin, so the markup stays inert.\n ['svg', 'image/svg+xml'],\n])\n\n/**\n * Suffix \u2192 grammar id, restricted to the ids the shared highlighter resolves\n * (its own alias list). A suffix whose grammar is absent stays unmapped so the\n * file falls through to plain text rather than a wrong colouring.\n */\nconst GRAMMAR_EXTENSIONS: Readonly<Record<string, readonly string[]>> = {\n typescript: ['ts', 'tsx', 'mts', 'cts', 'js', 'jsx', 'mjs', 'cjs'],\n shellscript: ['sh', 'bash', 'zsh', 'ksh', 'shell'],\n json: ['json', 'jsonc', 'jsonl', 'ndjson', 'map', 'webmanifest'],\n python: ['py', 'pyw', 'pyi'],\n ruby: ['rb', 'rake', 'gemspec', 'ru'],\n go: ['go'],\n rust: ['rs'],\n java: ['java'],\n c: ['c', 'h'],\n cpp: ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx', 'ipp'],\n csharp: ['cs', 'csx'],\n kotlin: ['kt', 'kts'],\n swift: ['swift'],\n php: ['php', 'phtml', 'php3', 'php4', 'php5'],\n yaml: ['yaml', 'yml'],\n toml: ['toml'],\n ini: ['ini', 'cfg', 'conf', 'properties', 'desktop', 'service'],\n markdown: ['md', 'markdown', 'mkd', 'mdown', 'mdwn'],\n mdx: ['mdx'],\n html: ['html', 'htm', 'xhtml', 'shtml'],\n css: ['css'],\n scss: ['scss'],\n less: ['less'],\n sql: ['sql', 'ddl', 'dml'],\n xml: ['xml', 'xsd', 'xsl', 'xslt', 'rss', 'atom', 'plist'],\n lua: ['lua'],\n}\n\nconst GRAMMAR_BY_EXTENSION = new Map(Object.entries(GRAMMAR_EXTENSIONS)\n .flatMap(([grammar, suffixes]) => suffixes.map(suffix => [suffix, grammar] as const)))\n\n/**\n * The lowercase suffix a file name ends in.\n *\n * A name that starts with its dot has no suffix: `.gitignore` is a whole name,\n * not a `gitignore` file. A trailing dot has none either.\n * @param path - absolute or relative path, in either separator style.\n * @returns the suffix without its dot, or undefined.\n */\nexport function extensionOf(path: string): string | undefined {\n const normalized = path.replaceAll('\\\\', '/')\n const name = normalized.slice(normalized.lastIndexOf('/') + 1)\n const at = name.lastIndexOf('.')\n if (at <= 0 || at === name.length - 1) return undefined\n return name.slice(at + 1).toLowerCase()\n}\n\n/**\n * Decide how one file is previewed, from its name alone.\n * @param path - absolute or relative path of the file.\n * @returns its preview format.\n */\nexport function previewFormatFor(path: string): PreviewFormat {\n const extension = extensionOf(path)\n const mediaType = extension === undefined ? undefined : IMAGE_MEDIA_TYPES.get(extension)\n if (mediaType !== undefined) return { kind: 'image', lang: undefined, mediaType }\n const lang = extension === undefined ? undefined : GRAMMAR_BY_EXTENSION.get(extension)\n if (lang === undefined) return { kind: 'text', lang: undefined, mediaType: undefined }\n if (lang === 'json') return { kind: 'json', lang, mediaType: undefined }\n // Markdown gets a rendered body and a source body; MDX stays on the source\n // side, because its JSX would render as prose.\n if (lang === 'markdown') return { kind: 'markdown', lang, mediaType: undefined }\n return { kind: 'code', lang, mediaType: undefined }\n}\n\n/**\n * Whether the pane offers a rendered/source switch for a format.\n * @param format - the file's format.\n * @returns whether both bodies exist.\n */\nexport function hasSourceToggle(format: PreviewFormat): boolean {\n return format.kind === 'markdown' || format.kind === 'json'\n}\n", "/**\n * The explorer's asynchronous half: listing directories, and reading whichever\n * file a tab asks for in the form its format needs.\n *\n * The component never awaits anything. It calls `list` / `read`, and this face\n * performs the Remote call and writes the outcome through the store's own\n * actions \u2014 the Slot-standard `inject` shape, so the Session id is resolved by\n * the framework and the write set stays the store's.\n *\n * One level has one listing in force: asking for a level again (the reload\n * gesture, a directory reopened after a reset) retires the listing still in\n * flight for it, whose settlement then writes nothing. Each tab has one read in\n * force in the same way, and the store additionally drops a settlement for a tab\n * that has since been closed. Requests carry the plugin's lifetime signal:\n * unloading the plugin abandons everything in flight, and no settlement writes\n * after that.\n */\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport { previewFormatFor } from './format.ts'\nimport type { PreviewText, createFilesStore } from './store.ts'\n\n/** The slice of the Client Remote face this plugin calls. */\nexport type WorkspaceFilesRemote = Pick<ClientRemote, 'workspaceFiles'>\n\n/**\n * Largest number of leading lines one text preview asks for. The host's own\n * `maxLines` default is higher; keeping the page small is this viewer's choice,\n * and a file that does not reach `eof` says so in the header.\n */\nexport const PREVIEW_LINES = 2000\n\n/** The explorer's injected business face, as the component receives it. */\nexport interface FilesInjected {\n /**\n * List one directory into the store, retiring any listing in flight for it.\n * @param path - absolute directory path.\n */\n readonly list: (path: string) => void\n /**\n * Read one open tab into the store: a page of text, or complete bytes when the\n * file's format draws an image.\n * @param path - absolute file path of an open tab.\n */\n readonly read: (path: string) => void\n}\n\n/**\n * Bind the explorer's face to one Session's Remote namespace.\n * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.\n * @param sessionId - the Session whose workspace root authorizes every call.\n * @param signal - the plugin's lifetime signal; aborting abandons in-flight work.\n * @param actions - the bound store actions this face writes through.\n * @returns the listing and preview operations the component calls.\n */\nexport function filesFace(\n remote: WorkspaceFilesRemote,\n sessionId: SessionId,\n signal: AbortSignal,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n): FilesInjected {\n /** Per absolute path: the listing generation a settlement must match; the latest request wins. */\n const listingGenerations = new Map<string, number>()\n /** Per open tab: the read generation a settlement must match; the latest request wins. */\n const readGenerations = new Map<string, number>()\n\n return {\n list(path) {\n if (signal.aborted) return\n const generation = (listingGenerations.get(path) ?? 0) + 1\n listingGenerations.set(path, generation)\n actions.loading(path)\n void remote.workspaceFiles.list(sessionId, path, signal).then((result) => {\n if (signal.aborted || listingGenerations.get(path) !== generation) return\n if (result.ok) {\n actions.loaded(path, {\n entries: result.value.entries,\n truncated: result.value.truncated,\n })\n } else {\n actions.failed(path, result.error)\n }\n })\n },\n read(path) {\n if (signal.aborted) return\n const generation = (readGenerations.get(path) ?? 0) + 1\n readGenerations.set(path, generation)\n const format = previewFormatFor(path)\n actions.reading(path)\n const settle = (write: () => void): void => {\n if (signal.aborted || readGenerations.get(path) !== generation) return\n write()\n }\n // An image needs its bytes whole; everything else needs a page of lines,\n // because the source view can show a bounded prefix of any text file.\n if (format.kind === 'image') {\n void remote.workspaceFiles.readAll(sessionId, path, signal).then((result) => {\n settle(() => {\n if (result.ok) {\n actions.previewLoaded(path, {\n kind: 'image',\n image: {\n dataUrl: `data:${format.mediaType};base64,${result.value.data}`,\n bytes: result.value.bytes,\n },\n })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n return\n }\n void remote.workspaceFiles.read(sessionId, path, { offset: 1, limit: PREVIEW_LINES }, signal)\n .then((result) => {\n settle(() => {\n if (result.ok) {\n const page: PreviewText = {\n text: result.value.text,\n lines: result.value.lines,\n eof: result.value.eof,\n bytes: result.value.bytes,\n }\n actions.previewLoaded(path, { kind: 'text', page })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n },\n }\n}\n", "/**\n * `fileExplorer` namespace dictionaries, and the namespace declaration.\n *\n * The failure lines name what the tree could not list, one code each, because a\n * directory that is gone, one outside the workspace, and a path that is not a\n * directory each suggest a different next step. The preview lines do the same\n * for the read failures a viewer can meet: not text, too large, not a regular\n * file.\n *\n * The namespace merge lives with its key set so that any module naming\n * `TranslateNS<'fileExplorer'>` or `PropsLocale<'fileExplorer'>` needs only this\n * file, whichever entry a program loads first.\n */\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'fileExplorer'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** View tab label, tree states, and preview states. */\n fileExplorer: FileExplorerKey\n }\n}\n\n/** Simplified Chinese dictionary and key-set source of truth. */\nexport const zh = {\n 'view.files': '\u6587\u4EF6',\n 'tree.root': '\u5DE5\u4F5C\u533A',\n 'tree.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'tree.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'tree.empty': '\u7A7A\u76EE\u5F55',\n 'tree.truncated': '\u6761\u76EE\u8FC7\u591A\uFF0C\u53EA\u663E\u793A\u4E86\u4E00\u90E8\u5206\u3002',\n 'tree.noWorkspace': '\u8FD9\u4E2A\u4F1A\u8BDD\u6CA1\u6709\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002',\n 'tree.other': '\u65E2\u4E0D\u662F\u6587\u4EF6\u4E5F\u4E0D\u662F\u76EE\u5F55\uFF0C\u65E0\u6CD5\u6253\u5F00\u3002',\n 'tree.error.notFound': '\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'tree.error.outsideWorkspace': '\u8BE5\u76EE\u5F55\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u3002',\n 'tree.error.notDirectory': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55\u3002',\n 'tree.error.unavailable': '\u8BFB\u53D6\u5931\u8D25\uFF1A{message}',\n 'preview.placeholder': '\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u8FDB\u884C\u9884\u89C8\u3002',\n 'preview.empty': '\u7A7A\u6587\u4EF6',\n 'preview.rendered': '\u9884\u89C8',\n 'preview.source': '\u6E90\u7801',\n 'preview.jsonLabel': 'JSON \u6587\u6863',\n 'preview.jsonInvalid': '\u8FD9\u4E0D\u662F\u53EF\u5C55\u5F00\u7684 JSON\uFF0C\u5DF2\u663E\u793A\u6E90\u7801\u3002',\n 'code.copy': '\u590D\u5236',\n 'code.copied': '\u5DF2\u590D\u5236',\n 'footnotes': '\u811A\u6CE8',\n 'json.copyValue': '\u590D\u5236\u503C',\n 'json.copyJson': '\u590D\u5236 JSON',\n 'json.copyPath': '\u590D\u5236\u8DEF\u5F84',\n 'json.copyPrettyJson': '\u590D\u5236\u683C\u5F0F\u5316 JSON',\n 'json.copyCompactJson': '\u590D\u5236\u538B\u7F29 JSON',\n 'json.copied': '\u5DF2\u590D\u5236',\n 'json.copyFailed': '\u590D\u5236\u5931\u8D25',\n 'json.collapseNode': '\u6536\u8D77',\n 'json.expandNode': '\u5C55\u5F00',\n 'json.copyTitle': '{action}',\n 'preview.title': '\u9884\u89C8',\n 'preview.tabs': '\u6253\u5F00\u7684\u6587\u4EF6',\n 'preview.close': '\u5173\u95ED {name}',\n 'preview.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'preview.copy': '\u590D\u5236',\n 'preview.copied': '\u5DF2\u590D\u5236',\n 'preview.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'preview.truncated': '\u6587\u4EF6\u8F83\u957F\uFF0C\u53EA\u663E\u793A\u524D {lines} \u884C\u3002',\n 'preview.wrap': '\u81EA\u52A8\u6362\u884C',\n 'preview.nowrap': '\u4E0D\u6362\u884C',\n 'preview.lines': '{lines} \u884C',\n 'preview.error.notFound': '\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'preview.error.notText': '\u8FD9\u4E0D\u662F\u53EF\u4EE5\u9884\u89C8\u7684\u6587\u672C\u6587\u4EF6\u3002',\n 'preview.error.tooLarge': '\u6587\u4EF6\u592A\u5927\uFF0C\u65E0\u6CD5\u9884\u89C8\u3002',\n 'preview.error.notRegularFile': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u666E\u901A\u6587\u4EF6\u3002',\n 'preview.error.unavailable': '\u9884\u89C8\u5931\u8D25\uFF1A{message}',\n} satisfies Record<string, string>\n\n/** Files dictionary key union. */\nexport type FileExplorerKey = keyof typeof zh\n\n/** English dictionary, checked against the Chinese key set. */\nexport const en = {\n 'view.files': 'Files',\n 'tree.root': 'Workspace',\n 'tree.reload': 'Reload',\n 'tree.loading': 'Reading\u2026',\n 'tree.empty': 'Empty directory',\n 'tree.truncated': 'Too many entries, showing only some of them.',\n 'tree.noWorkspace': 'This session has no workspace directory.',\n 'tree.other': 'Not a file or a directory, so it cannot be opened.',\n 'tree.error.notFound': 'That directory is gone. It may have been moved or deleted.',\n 'tree.error.outsideWorkspace': 'That directory is outside the workspace, so it cannot be read.',\n 'tree.error.notDirectory': 'That is not a directory.',\n 'tree.error.unavailable': 'Read failed: {message}',\n 'preview.placeholder': 'Pick a file on the left to preview it.',\n 'preview.empty': 'Empty file',\n 'preview.rendered': 'Rendered',\n 'preview.source': 'Source',\n 'preview.jsonLabel': 'JSON document',\n 'preview.jsonInvalid': 'That is not JSON a tree can walk, so the source is shown.',\n 'code.copy': 'Copy',\n 'code.copied': 'Copied',\n 'footnotes': 'Footnotes',\n 'json.copyValue': 'Copy value',\n 'json.copyJson': 'Copy JSON',\n 'json.copyPath': 'Copy path',\n 'json.copyPrettyJson': 'Copy pretty JSON',\n 'json.copyCompactJson': 'Copy compact JSON',\n 'json.copied': 'Copied',\n 'json.copyFailed': 'Copy failed',\n 'json.collapseNode': 'Collapse',\n 'json.expandNode': 'Expand',\n 'json.copyTitle': '{action}',\n 'preview.title': 'Preview',\n 'preview.tabs': 'Open files',\n 'preview.close': 'Close {name}',\n 'preview.loading': 'Reading\u2026',\n 'preview.copy': 'Copy',\n 'preview.copied': 'Copied',\n 'preview.reload': 'Reload',\n 'preview.truncated': 'This file is long, showing the first {lines} lines.',\n 'preview.wrap': 'Wrap long lines',\n 'preview.nowrap': 'Do not wrap long lines',\n 'preview.lines': '{lines} lines',\n 'preview.error.notFound': 'That file is gone. It may have been moved or deleted.',\n 'preview.error.notText': 'That is not a text file this preview can show.',\n 'preview.error.tooLarge': 'That file is too large to preview.',\n 'preview.error.notRegularFile': 'That is not a regular file.',\n 'preview.error.unavailable': 'Preview failed: {message}',\n} satisfies Record<FileExplorerKey, string>\n", "/**\n * The file explorer's view state: what each listed directory holds, which\n * directories are open, which files are open in preview tabs, and what each tab\n * is showing.\n *\n * The explorer is not one resource. A directory listing per level, expanded\n * lazily, plus one read per open preview tab, is state the view owns \u2014 so it\n * lives in a Slot-standard exclusive store: the framework mints one instance per\n * Session, and the state survives switching to Chat or Trajectory and back, which\n * unmounts the component.\n *\n * Write actions run only after `start`; the store's keys are absolute paths,\n * because a child is its parent joined with the entry name and the host accepts\n * the absolute form for both listing and reading.\n *\n * Tabs are unbounded but loaded content is not: an image preview holds the\n * file's complete bytes as a base64 data URL, so keeping every tab's content\n * alive would let a reader grow the tab's memory to an arbitrary size. The store\n * therefore retains content for the active tab and the most recently visited\n * ones, and drops the rest \u2014 the tab stays open and simply reads again when it\n * is next activated. See {@link RETAINED_PREVIEWS}.\n */\nimport { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-store'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\n\n/**\n * How many tabs keep their loaded content. A dropped tab is still open: it reads\n * again when activated, which shows as a brief loading state.\n *\n * Five covers the working set \u2014 the tab being read, plus the few it was reached\n * through \u2014 while bounding what a reader who opens everything in a directory can\n * pin in memory.\n */\nexport const RETAINED_PREVIEWS = 5\n\n/** One directory's contents, as one expanded level of the tree. */\nexport interface DirLevel {\n /** The directory's entries, in the endpoint's order. */\n readonly entries: readonly WorkspaceDirectoryEntry[]\n /** The listing hit the endpoint's entry cap, so entries are missing. */\n readonly truncated: boolean\n}\n\n/** What one directory level is doing right now. */\nexport type LevelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly level: DirLevel }\n | { readonly kind: 'failed'; readonly failure: RemoteFailure }\n\n/** One page of text, as a preview keeps it. */\nexport interface PreviewText {\n /** The page's lines joined with `\\n`. */\n readonly text: string\n /** How many lines the page holds. */\n readonly lines: number\n /** Whether the page includes the file's last line. */\n readonly eof: boolean\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/** Complete image bytes, addressed as a URL the browser can draw directly. */\nexport interface PreviewImage {\n /** `data:` URL carrying the file's complete bytes under its media type. */\n readonly dataUrl: string\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/**\n * What one read delivered. A tab decides which arm it needs from the file's name\n * before reading, so the two never mix: a page of text for everything readable\n * as text, complete bytes for an image.\n */\nexport type PreviewContent =\n | { readonly kind: 'text'; readonly page: PreviewText }\n | { readonly kind: 'image'; readonly image: PreviewImage }\n\n/**\n * Which body a tab draws for a file that has more than one. An absent entry\n * leaves the choice to the format's own default, so a mode picked for one file\n * never leaks into another.\n */\nexport type PreviewMode = 'rendered' | 'source'\n\n/** What one tab is showing right now. */\nexport type PreviewState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly content: PreviewContent }\n | { readonly kind: 'failed'; readonly failure: RemoteFailure }\n\n/** The explorer's whole state for one Session. */\nexport interface FilesState {\n /** Absolute path of the workspace root this explorer is rooted at; `null` before it is known. */\n root: string | null\n /** Level state by absolute directory path; a path absent here was never asked for. */\n levels: Record<string, LevelState>\n /** Expanded absolute directory paths, root included, in expansion order. */\n expanded: string[]\n /** Open preview tabs by absolute path, in open order. */\n open: string[]\n /** The tab being shown, or `null` when nothing is open. */\n active: string | null\n /** Loaded content by absolute path; a path absent here was never read or has been dropped. */\n previews: Record<string, PreviewState>\n /** Absolute paths whose content is retained, most recently used first. */\n retained: string[]\n /** Body choice by absolute path; an absent entry follows the format's default. */\n modes: Record<string, PreviewMode>\n /** Line-wrap preference by absolute path; an absent entry wraps. */\n wraps: Record<string, boolean>\n}\n\n/** The explorer store's write set. */\ntype FilesActions = {\n /** Seed the explorer at its workspace root, with the root expanded and no tabs. */\n start: (draft: FilesState, root: string) => void\n /** Mark one directory as being listed. */\n loading: (draft: FilesState, path: string) => void\n /** Record one directory's contents. */\n loaded: (draft: FilesState, path: string, level: DirLevel) => void\n /** Record why one directory could not be listed. */\n failed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Open a collapsed directory, or collapse an open one. */\n toggled: (draft: FilesState, path: string) => void\n /** Drop every listed level, keeping what is expanded: the reload gesture's first half. */\n reset: (draft: FilesState) => void\n /** Open a file in a tab and show it; an already-open file is focused, not duplicated. */\n openFile: (draft: FilesState, path: string) => void\n /** Show an open tab, keeping its position in the strip. */\n activate: (draft: FilesState, path: string) => void\n /** Close a tab, showing its right neighbour, else its left, else nothing. */\n closeFile: (draft: FilesState, path: string) => void\n /** Mark one tab as being read. */\n reading: (draft: FilesState, path: string) => void\n /** Record one tab's contents. */\n previewLoaded: (draft: FilesState, path: string, content: PreviewContent) => void\n /** Record why one file could not be read. */\n previewFailed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Forget one tab's loaded content, leaving the tab open. */\n dropPreview: (draft: FilesState, path: string) => void\n /** Choose whether one tab wraps long lines. */\n setWrap: (draft: FilesState, path: string, wrap: boolean) => void\n /** Choose which body one tab shows. */\n setMode: (draft: FilesState, path: string, mode: PreviewMode) => void\n}\n\n/** The state a freshly created explorer starts from. */\nfunction emptyState(): FilesState {\n return {\n root: null,\n levels: {},\n expanded: [],\n open: [],\n active: null,\n previews: {},\n retained: [],\n modes: {},\n wraps: {},\n }\n}\n\n/**\n * Record one path as most recently used, and drop the content of tabs that fell\n * past the retention cap. The active tab is always kept, so nothing the reader is\n * looking at is ever dropped.\n * @param d - draft state.\n * @param path - the path whose content was just loaded, or is being shown.\n */\nfunction retain(d: FilesState, path: string): void {\n d.retained = [path, ...d.retained.filter(candidate => candidate !== path)]\n const keep = new Set([...(d.active === null ? [] : [d.active]), ...d.retained.slice(0, RETAINED_PREVIEWS)])\n for (const candidate of Object.keys(d.previews)) {\n if (!keep.has(candidate)) delete d.previews[candidate]\n }\n d.retained = d.retained.filter(candidate => keep.has(candidate))\n}\n\n/**\n * Declare the explorer's store.\n *\n * A factory rather than a shared handle: the registration declares it as an\n * exclusive store, so the framework mints one instance per Session and disposes\n * it with that Session's binding.\n * @returns the store handle to declare on the registration.\n */\nexport function createFilesStore(): EngineStoreHandle<FilesState, FilesActions> {\n return defineStore({\n init: emptyState,\n actions: {\n start: (d, root) => {\n Object.assign(d, emptyState())\n d.root = root\n d.expanded = [root]\n },\n loading: (d, path) => {\n d.levels[path] = { kind: 'loading' }\n },\n loaded: (d, path, level) => {\n d.levels[path] = { kind: 'ready', level }\n },\n failed: (d, path, failure) => {\n d.levels[path] = { kind: 'failed', failure }\n },\n toggled: (d, path) => {\n const at = d.expanded.indexOf(path)\n if (at >= 0) d.expanded.splice(at, 1)\n else d.expanded.push(path)\n },\n reset: (d) => {\n d.levels = {}\n },\n openFile: (d, path) => {\n if (!d.open.includes(path)) d.open.push(path)\n d.active = path\n },\n activate: (d, path) => {\n if (!d.open.includes(path)) return\n d.active = path\n // Showing a retained tab makes it the most recently used one, so the tab\n // the reader just came from is the first candidate for eviction.\n retain(d, path)\n },\n closeFile: (d, path) => {\n const at = d.open.indexOf(path)\n if (at < 0) return\n d.open.splice(at, 1)\n delete d.previews[path]\n delete d.modes[path]\n delete d.wraps[path]\n d.retained = d.retained.filter(candidate => candidate !== path)\n if (d.active !== path) return\n // The neighbour that takes over: the tab that slid into this slot, else\n // the one before it, matching how editors close.\n d.active = d.open[at] ?? d.open[at - 1] ?? null\n },\n reading: (d, path) => {\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'loading' }\n },\n previewLoaded: (d, path, content) => {\n // A settlement for a tab the reader already closed writes nothing: the\n // store keys content by path, not by arrival order.\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'ready', content }\n retain(d, path)\n },\n previewFailed: (d, path, failure) => {\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'failed', failure }\n },\n dropPreview: (d, path) => {\n delete d.previews[path]\n d.retained = d.retained.filter(candidate => candidate !== path)\n },\n setWrap: (d, path, wrap) => {\n d.wraps[path] = wrap\n },\n setMode: (d, path, mode) => {\n d.modes[path] = mode\n },\n },\n })\n}\n", "/**\n * The explorer's stylesheet, injected as one owned `<style>` tag.\n *\n * A client bundle arriving through the module loader is a single script: the\n * loader fetches `lib/client.js` and nothing else, so a separate stylesheet\n * would never be requested. Injecting the sheet from the bundle is the same\n * mechanism the built-in packages' `clientBundle()` preset uses for global CSS,\n * and the tag is removed when the plugin unloads.\n *\n * Colours come from the shell's `--dsw-alias-*` theme tokens, so the page\n * follows the active appearance without owning any palette of its own.\n */\n\n/** Marker attribute for this plugin's style tag. */\nconst STYLE_ATTRIBUTE = 'data-dsh-file-explorer'\n\nconst CSS = `\n.dsh-fe-root {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: 1.5;\n}\n\n.dsh-fe-panes {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.dsh-fe-tree,\n.dsh-fe-preview {\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.dsh-fe-tree {\n flex: 0 0 auto;\n width: 280px;\n min-width: 160px;\n max-width: 60%;\n resize: horizontal;\n overflow: hidden;\n border-right: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-fe-preview {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.dsh-fe-head {\n display: flex;\n flex: 0 0 auto;\n gap: 4px;\n align-items: center;\n box-sizing: border-box;\n height: 38px;\n padding: 0 6px 0 14px;\n /* The row is filled rather than transparent, so the pane's chrome reads as\n chrome instead of as the first line of the file. The palette's layer tokens\n are all pure white in light mode (bg-base, bg-layer-1/2/3 share a value), so\n the only token that separates a surface from the content in BOTH themes is\n this wash: 4% ink in light, 8% in dark. See the note in CONTRIBUTING.md. */\n background: var(--dsw-alias-bg-skeleton);\n border-bottom: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n/* A grammar badge for the source body, which is where the language used to be\n named: the code block's own banner is hidden in this pane so there is exactly\n one copy control, in this row. */\n.dsh-fe-lang {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 11px;\n white-space: nowrap;\n}\n\n.dsh-fe-path {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: 12px;\n white-space: nowrap;\n text-overflow: ellipsis;\n direction: rtl;\n text-align: left;\n}\n\n.dsh-fe-path > span {\n direction: ltr;\n unicode-bidi: embed;\n}\n\n.dsh-fe-path-muted {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-meta {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n white-space: nowrap;\n}\n\n.dsh-fe-tool {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 11px;\n line-height: 1;\n background: transparent;\n border: none;\n border-radius: 14px;\n cursor: pointer;\n}\n\n.dsh-fe-tool:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-tool svg {\n width: 15px;\n height: 15px;\n}\n\n.dsh-fe-scroll {\n flex: 1 1 auto;\n min-height: 0;\n padding: 8px 8px calc(var(--dsh-composer-height, 152px) + 24px);\n overflow: auto;\n scrollbar-gutter: stable;\n}\n\n.dsh-fe-scroll::-webkit-scrollbar-track {\n margin: 2px;\n}\n\n.dsh-fe-level {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.dsh-fe-level .dsh-fe-level {\n padding-left: 16px;\n}\n\n.dsh-fe-row {\n display: flex;\n gap: 6px;\n align-items: center;\n width: 100%;\n min-width: 0;\n padding: 5px 10px;\n color: inherit;\n font: inherit;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.dsh-fe-row:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-row[aria-current='true'] {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dsh-fe-row-static {\n cursor: default;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-row-static:hover {\n background: transparent;\n}\n\n.dsh-fe-icon {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-name {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.dsh-fe-note {\n margin: 0;\n padding: 3px 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dsh-fe-note-error {\n color: var(--dsw-alias-label-error);\n}\n\n.dsh-fe-status {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n justify-content: center;\n padding: 24px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n}\n\n/* \u2500\u2500 format-aware bodies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Every body the pane can draw comes from the shared primitives, so this sheet\n carries no source-line rail of its own: the code block owns the numbered\n gutter, its counter, and its copy control. */\n\n/* The shared CodeBlock draws the highlighted source; this host only swaps the\n chat card's chrome for the pane's: no margin, no radius, no grey fill, and\n the pane's own scroll container. */\n.dsh-fe-source {\n --dsl-code-block-background: transparent;\n --dsl-code-block-border-radius: 0;\n --dsl-code-block-line-white-space: pre;\n}\n\n.dsh-fe-source .md-code-block {\n margin: 0;\n}\n\n/* One copy control per pane, in the header row above: the code block's own\n banner would be a second, inside the scrollport, and it scrolls away. The\n grammar it named is shown as the header's badge instead. Markdown fences keep\n their banners \u2014 there, a banner belongs to the fence, not to the file.\n\n The banner sits inside a sticky wrapper, hence the descendant match plus the\n :has rule that takes the wrapper out with it. */\n.dsh-fe-source [data-code-block-banner],\n.dsh-fe-source .md-code-block > div:has(> [data-code-block-banner]) {\n display: none;\n}\n\n.dsh-fe-source .md-code-block pre {\n box-sizing: border-box;\n min-width: 100%;\n padding: 8px 0;\n overflow: visible;\n white-space: pre;\n word-break: normal;\n overflow-wrap: normal;\n}\n\n.dsh-fe-source[data-wrap='true'] {\n --dsl-code-block-line-white-space: pre-wrap;\n}\n\n.dsh-fe-source[data-wrap='true'] .md-code-block pre {\n white-space: pre-wrap;\n /* The primitive's default is break-all; keep words whole instead. */\n word-break: normal;\n overflow-wrap: break-word;\n}\n\n/* The preview pane's header row carries the open tabs and, at its end, the\n active tab's controls. The tabs take the room that is left and scroll; the\n controls never shrink away. */\n.dsh-fe-tabhead {\n gap: 8px;\n padding: 0 6px 0 8px;\n}\n\n.dsh-fe-tabs {\n display: flex;\n flex: 1 1 auto;\n gap: 2px;\n align-items: stretch;\n min-width: 90px;\n height: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\n.dsh-fe-tabs::-webkit-scrollbar {\n height: 0;\n}\n\n.dsh-fe-tab {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n min-width: 0;\n max-width: 220px;\n border-radius: 8px;\n}\n\n.dsh-fe-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-tab[data-active] {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dsh-fe-tab-label {\n display: flex;\n gap: 6px;\n align-items: center;\n min-width: 0;\n padding: 5px 4px 5px 8px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.dsh-fe-tab[data-active] .dsh-fe-tab-label {\n color: var(--dsw-alias-label-primary);\n}\n\n.dsh-fe-tab-name {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* The close control appears for the tab under the pointer and for the active\n tab, so a full strip stays readable. */\n.dsh-fe-tab-close {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-right: 5px;\n padding: 0;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n font-size: 14px;\n line-height: 1;\n background: transparent;\n border: 0;\n border-radius: 5px;\n cursor: pointer;\n opacity: 0;\n}\n\n.dsh-fe-tab:hover .dsh-fe-tab-close,\n.dsh-fe-tab[data-active] .dsh-fe-tab-close,\n.dsh-fe-tab-close:focus-visible {\n opacity: 1;\n}\n\n.dsh-fe-tab-close:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* A file already open in a tab keeps a quiet marker in the tree, so the reader\n can see where the strip came from without it competing with the active row. */\n.dsh-fe-open-dot {\n flex: 0 0 auto;\n width: 5px;\n height: 5px;\n margin-left: auto;\n background: var(--dsw-alias-brand-primary);\n border-radius: 50%;\n opacity: 0.6;\n}\n\n/* Rendered Markdown: prose lays out in normal white space, with the pane's\n insets and no monospace inheritance from the source view. */\n.dsh-fe-prose {\n min-width: 0;\n padding: 4px 4px 0;\n font-family: var(--dsw-font-family);\n white-space: normal;\n}\n\n.dsh-fe-json {\n min-width: 0;\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 12px;\n white-space: normal;\n}\n\n/* An image keeps its intrinsic size, centres while it fits, and scales down to\n the pane rather than overflowing it. */\n.dsh-fe-imagehost {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n}\n\n.dsh-fe-image {\n display: block;\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n`\n\n/**\n * Install the plugin's stylesheet once, and hand back its disposer.\n * @returns a disposer removing the tag this call created, or a no-op when the sheet was already installed.\n */\nexport function installStyles(): () => void {\n if (typeof document === 'undefined') return () => {}\n if (document.querySelector(`style[${STYLE_ATTRIBUTE}]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.setAttribute(STYLE_ATTRIBUTE, '')\n tag.textContent = CSS\n document.head.appendChild(tag)\n return () => { tag.remove() }\n}\n"],
|
|
5
|
-
"mappings": "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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8ZL,SAAS,gBAA4B;AAC1C,MAAI,OAAO,aAAa,YAAa,QAAO,MAAM;AAAA,EAAC;AACnD,MAAI,SAAS,cAAc,SAAS,eAAe,GAAG,MAAM,KAAM,QAAO,MAAM;AAAA,EAAC;AAChF,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,aAAa,iBAAiB,EAAE;AACpC,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC7B,SAAO,MAAM;AAAE,QAAI,OAAO;AAAA,EAAE;AAC9B;;;ANrYO,IAAM,OAAO;AAMb,IAAM,SAAS,CAAC,SAAS,UAAU,UAAU,uBAAuB;AAMpE,IAAM,gBAAgB;AAMtB,SAAS,MAAM,KAAoB;AACxC,MAAI,OAAO,eAAe,wCAAwC;AAClE,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,0CAA0C;AAIhG,QAAM,IAAI,IAAI,OAAO,KAAK,EAAE;AAC5B,QAAM,QAAQ,iBAAiB;AAC/B,QAAM,WAAW,IAAI,gBAAgB;AACrC,MAAI,OAAO,MAAM,MAAM;AAAE,aAAS,MAAM;AAAA,EAAE,GAAG,8CAA8C;AAE3F,MAAI,MAAM,OAAO,qBAAqB,MAAM,IAAI,MAAM,SAAS;AAAA,IAC7D,MAAM;AAAA,IACN,IAAI;AAAA;AAAA;AAAA,IAGJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO,MAAM,EAAE,YAAY;AAAA,IAC3B;AAAA,IACA,QAAQ,CACN,WACA,YACkB,UAAU,IAAI,QAAQ,WAAW,SAAS,QAAQ,OAAO;AAAA,EAC/E,GAAG,SAAS,CAAC;AACf;",
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half: register `files` as one Conversation View.\n *\n * The public path, unmodified: the view into the `conversation.view` list slot\n * that `@deepseek-ai/dsh-client-ui-conversation` owns and renders as the tab\n * strip beside Chat and Trajectory. The registration carries the exclusive\n * per-Session store, the injected Remote face, and the locale namespace; the\n * slot service's effect wrapper means plugin unload removes the tab, its\n * dictionary, and its stylesheet, and disposal of the declaration removes the\n * contribution again if the Conversation shell is remounted.\n *\n * The file split is this package's layering: what the explorer keeps\n * (`store.ts`), how it lists and reads (`face.ts`), what it draws\n * (`FilesView.tsx`), what it looks like (`styles.ts`), what it says\n * (`locales.ts`), and this module, which only wires them together.\n */\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\n// Type-only: pulls the Client Remote face (`ctx.remote`).\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\n// Type-only: pulls the locale service's Context merge (`ctx.locale`).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: the 'conversation.view' SlotMap row and the owner props the view\n// component derives must be in the program for the register call to type.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the slot service's Context merge (`ctx.slots`).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport { FilesView } from './FilesView.tsx'\nimport { filesFace } from './face.ts'\nimport type { FilesInjected } from './face.ts'\nimport { en, NS, zh } from './locales.ts'\nimport { createFilesStore } from './store.ts'\nimport { installStyles } from './styles.ts'\n\nexport { parseJsonDocument, previewLines, tabLabels } from './FilesView.tsx'\nexport { canExportPdf, extensionOf, hasSourceToggle, previewFormatFor } from './format.ts'\nexport type { PreviewFormat, PreviewFormatKind } from './format.ts'\nexport type { FileExplorerKey } from './locales.ts'\nexport { RETAINED_PREVIEWS } from './store.ts'\nexport type {\n DirLevel, FilesState, LevelState, PreviewContent, PreviewImage, PreviewMode, PreviewState,\n PreviewText,\n} from './store.ts'\nexport type { FilesInjected, WorkspaceFilesRemote } from './face.ts'\nexport type { FilesViewProps } from './FilesView.tsx'\n\n/** This plugin's identity inside the browser plugin tree. */\nexport const name = 'file-explorer'\n\n/**\n * Required browser services: the slot registry, copy, and the Remote carrier\n * with the workspace-files namespace the explorer reads through.\n */\nexport const inject = ['slots', 'locale', 'remote', 'remote.workspaceFiles']\n\n/**\n * The view's id inside `conversation.view`. It is the tab's identity, the key\n * a stored View preference names, and the entry id the header selects by.\n */\nexport const FILES_VIEW_ID = 'files'\n\n/**\n * Client plugin body: register the stylesheet, the dictionaries, and the view.\n * @param ctx - client root context carrying the slot registry, copy, and Remote face.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(installStyles, '@jaxzhou/dsh-file-explorer: stylesheet')\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), '@jaxzhou/dsh-file-explorer: dictionaries')\n // Registration-time text (the view tab label) reads through the bound\n // translate as a thunk, so it follows the active locale without\n // re-registration.\n const t = ctx.locale.bind(NS)\n const store = createFilesStore()\n const lifetime = new AbortController()\n ctx.effect(() => () => { lifetime.abort() }, '@jaxzhou/dsh-file-explorer: request lifetime')\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register({\n name: 'conversation.view',\n id: FILES_VIEW_ID,\n // After Chat (default) and Trajectory (10), so the strip reads\n // Chat | Trajectory | Files.\n order: 20,\n locale: NS,\n label: () => t('view.files'),\n store,\n inject: (\n sessionId: SessionId,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n ): FilesInjected => filesFace(ctx.remote, sessionId, lifetime.signal, actions),\n }, FilesView))\n}\n", "/**\n * The Files Conversation View: the session workspace on the left, one preview\n * tab per opened file on the right.\n *\n * The view is one `conversation.view` entry, so it sits beside Chat and\n * Trajectory in the same tab strip and the shell renders it one at a time.\n * Everything the tree keeps lives in its store; everything it asks for goes\n * through its injected face. The component only decides what to draw for each\n * absolute path and what a click means: a directory toggles, a file opens a\n * preview tab (or focuses the one already open for it), and anything else is\n * shown but refuses to open.\n *\n * A tab's body follows the file's format (see `format.ts`): Markdown renders as a\n * document with a source view behind a toggle, JSON as a collapsible tree with\n * the same toggle, a recognized source suffix through the shared\n * syntax-highlighted code block, an image as the image, and everything else as\n * plain numbered text. All of it comes from\n * `@deepseek-ai/dsh-client-ui-primitives`, which the browser shell shares into its\n * frozen module table \u2014 the one way this bundle may use another package's values\n * at runtime.\n *\n * Switching to another tab unmounts this component but not its Session-scoped\n * store, so the tree, the open tabs, and the active tab survive the round trip. A\n * read left in flight when that happens settles into the store anyway, because\n * the face's requests ride the plugin's lifetime, not the component's.\n */\nimport {\n useEffect, useMemo, useRef, useState,\n type MutableRefObject, type ReactNode,\n} from 'react'\nimport type { ConvViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { InjectFace, PropsLocale, PropsStore, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport {\n CodeBlock, FileTypeIcon, IconFolderClose16, IconFolderOpen16, IconRefreshOutline16, JsonTree,\n MarkdownText, classifyFileType, fileSizeText, writeClipboard,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { JsonTreeLabels, MarkdownLabels } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\nimport { canExportPdf, hasSourceToggle, previewFormatFor } from './format.ts'\nimport type { PreviewFormat } from './format.ts'\nimport type { FilesInjected } from './face.ts'\nimport type {\n FilesState, LevelState, PreviewContent, PreviewMode, PreviewState, PreviewText,\n createFilesStore,\n} from './store.ts'\nimport type {} from './locales.ts'\n\n/** The view's composed props: the Conversation View seat, its store, its face, and its copy. */\nexport type FilesViewProps =\n & ConvViewProps\n & PropsStore<ReturnType<typeof createFilesStore>>\n & InjectFace<FilesInjected>\n & PropsLocale<'fileExplorer'>\n\n/** Natural, case-insensitive name order, so `file2` precedes `file10`. */\nconst byName = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })\n\n/**\n * Order one level's entries for display: directories first, then everything\n * else, each group by name. The endpoint's order is a listing fact; this is the\n * reader's.\n * @param entries - the listing as the endpoint returned it.\n * @returns a new array, directories first, then by name within each group.\n */\nexport function orderEntries(entries: readonly WorkspaceDirectoryEntry[]): WorkspaceDirectoryEntry[] {\n return [...entries].sort((left, right) => {\n const group = Number(right.type === 'directory') - Number(left.type === 'directory')\n return group !== 0 ? group : byName.compare(left.name, right.name)\n })\n}\n\n/**\n * The absolute path of one child entry.\n *\n * Joined with `/` whatever the parent's separators: the host resolves mixed\n * separators, and the tree only needs a stable key.\n * @param parent - absolute path of the listed directory.\n * @param name - the entry's basename.\n * @returns the child's absolute path.\n */\nexport function childPath(parent: string, name: string): string {\n return `${parent.replace(/[/\\\\]+$/, '')}/${name}`\n}\n\n/**\n * Split a path into its directory prefix and its last segment, for a header\n * that greys the prefix and inks the name.\n * @param path - absolute path.\n * @returns the trailing-slash directory prefix and the basename.\n */\nexport function pathParts(path: string): { directory: string; name: string } {\n const at = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'))\n if (at < 0) return { directory: '', name: path }\n return { directory: path.slice(0, at + 1), name: path.slice(at + 1) }\n}\n\n/**\n * The display label of every open tab.\n *\n * A tab shows its basename, which is what a strip can afford. Two open files\n * that share a basename are ambiguous on their own \u2014 `index.ts` in three\n * directories is the ordinary case \u2014 so those tabs carry their parent directory\n * as a prefix, and only those. The full path stays in each tab's tooltip.\n * @param open - open tab paths, in strip order.\n * @returns a label per path.\n */\nexport function tabLabels(open: readonly string[]): Record<string, string> {\n const twins = new Map<string, number>()\n for (const path of open) {\n const { name } = pathParts(path)\n twins.set(name, (twins.get(name) ?? 0) + 1)\n }\n const labels: Record<string, string> = {}\n for (const path of open) {\n const { directory, name } = pathParts(path)\n if ((twins.get(name) ?? 0) < 2) {\n labels[path] = name\n continue\n }\n const parent = pathParts(directory.replace(/[/\\\\]+$/, '')).name\n labels[path] = parent === '' ? name : `${parent}/${name}`\n }\n return labels\n}\n\n/**\n * Split one loaded page into its source lines.\n *\n * The page's own line count decides: `0` is a page past the file's last line\n * (an empty file on the first read), which has no lines to draw and must not\n * become one empty row. Otherwise the text is the page's lines joined with\n * `\\n`, so splitting returns exactly what `lines` promised.\n * @param page - the loaded text page.\n * @returns the page's source lines, in order.\n */\nexport function previewLines(page: PreviewText): string[] {\n return page.lines === 0 ? [] : page.text.split('\\n')\n}\n\n/**\n * Parse one complete JSON document for the tree view.\n * @param text - the file's text.\n * @returns the parsed container, or undefined when the text is not JSON a tree\n * can walk (a syntax error, or a bare scalar).\n */\nexport function parseJsonDocument(text: string): object | undefined {\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch {\n return undefined\n }\n return typeof value === 'object' && value !== null ? value : undefined\n}\n\n/**\n * Say why a directory could not be listed, in terms of the directory.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show under the directory.\n */\nexport function treeFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('tree.error.notFound')\n case 'workspace-file/outside-workspace': return t('tree.error.outsideWorkspace')\n case 'workspace-file/not-directory': return t('tree.error.notDirectory')\n // Carrier and unclassified host failures reach the reader as themselves:\n // this tree knows nothing useful to add to a transport-level message.\n default: return t('tree.error.unavailable', { message: failure.message })\n }\n}\n\n/**\n * Say why a file could not be previewed, in terms of the file.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show in the preview pane.\n */\nexport function previewFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('preview.error.notFound')\n case 'workspace-file/not-text': return t('preview.error.notText')\n case 'workspace-file/too-large': return t('preview.error.tooLarge')\n case 'workspace-file/not-regular-file': return t('preview.error.notRegularFile')\n default: return t('preview.error.unavailable', { message: failure.message })\n }\n}\n\n/** What every level shares: the explorer's state and the two gestures. */\ninterface TreeContext {\n readonly state: FilesState\n readonly open: ReadonlySet<string>\n readonly onToggle: (path: string) => void\n readonly onOpen: (path: string) => void\n readonly t: TranslateNS<'fileExplorer'>\n}\n\n/** One directory's rows: its state while listing, its entries once listed. */\nfunction Level({ path, tree }: { path: string; tree: TreeContext }): ReactNode {\n const { state, t } = tree\n const level: LevelState | undefined = state.levels[path]\n if (level === undefined || level.kind === 'loading') {\n return <li className=\"dsh-fe-note\" data-files-row=\"loading\">{t('tree.loading')}</li>\n }\n if (level.kind === 'failed') {\n return (\n <li\n className=\"dsh-fe-note dsh-fe-note-error\"\n data-files-row=\"failed\"\n data-files-code={level.failure.code}\n >\n {treeFailureLine(t, level.failure)}\n </li>\n )\n }\n const entries = orderEntries(level.level.entries)\n return (\n <>\n {entries.length === 0 && <li className=\"dsh-fe-note\" data-files-row=\"empty\">{t('tree.empty')}</li>}\n {entries.map(entry => <Entry key={entry.name} parent={path} entry={entry} tree={tree} />)}\n {level.level.truncated && (\n <li className=\"dsh-fe-note\" data-files-row=\"truncated\">{t('tree.truncated')}</li>\n )}\n </>\n )\n}\n\n/** One entry's row, and its children when it is an expanded directory. */\nfunction Entry({\n parent,\n entry,\n tree,\n}: {\n parent: string\n entry: WorkspaceDirectoryEntry\n tree: TreeContext\n}): ReactNode {\n const path = childPath(parent, entry.name)\n if (entry.type === 'directory') {\n const expanded = tree.state.expanded.includes(path)\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"directory\" data-files-path={path}>\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-expanded={expanded}\n onClick={() => { tree.onToggle(path) }}\n >\n {expanded\n ? <IconFolderOpen16 className=\"dsh-fe-icon\" />\n : <IconFolderClose16 className=\"dsh-fe-icon\" />}\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </button>\n {expanded && <ul className=\"dsh-fe-level\"><Level path={path} tree={tree} /></ul>}\n </li>\n )\n }\n if (entry.type === 'file') {\n const isOpen = tree.open.has(path)\n return (\n <li\n className=\"dsh-fe-item\"\n data-files-entry=\"file\"\n data-files-path={path}\n data-files-open={isOpen || undefined}\n >\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-current={tree.state.active === path}\n onClick={() => { tree.onOpen(path) }}\n >\n <FileTypeIcon kind={classifyFileType(entry.name)} size={16} />\n <span className=\"dsh-fe-name\">{entry.name}</span>\n {isOpen && <span className=\"dsh-fe-open-dot\" aria-hidden=\"true\" />}\n </button>\n </li>\n )\n }\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"other\" data-files-path={path}>\n <span className=\"dsh-fe-row dsh-fe-row-static\" title={tree.t('tree.other')}>\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </span>\n </li>\n )\n}\n\n/**\n * One file's source text through the shared syntax highlighter.\n *\n * `CodeBlock` owns the grammar lookup, the numbered gutter, and the copy\n * control; the owning class only replaces the chat card's chrome (margin,\n * radius, grey fill) with the pane's, and states the wrap preference: lines wrap\n * at their spaces and keep every word whole, and a token that cannot fit on a\n * line by itself still breaks rather than sliding under a scrollbar.\n */\nfunction HighlightedSource({\n page,\n lang,\n wrap,\n t,\n}: {\n page: PreviewText\n lang: string | undefined\n wrap: boolean\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n return (\n <div className=\"dsh-fe-source\" data-wrap={wrap} data-preview-lang={lang ?? 'plain'}>\n <CodeBlock\n code={page.text}\n lang={lang}\n lineNumbers\n copyLabel={t('code.copy')}\n copiedLabel={t('code.copied')}\n />\n </div>\n )\n}\n\n/**\n * One HTML file drawn as a page, in a sandboxed iframe over a Blob URL.\n *\n * `allow-scripts` without `allow-same-origin` puts the document in an opaque\n * origin: its scripts run, but nothing in it can reach this application's DOM,\n * storage, or session. The same pair is what the shipped Sidebar document\n * preview uses, so the two HTML surfaces in the harness behave alike.\n *\n * Relative assets are not fetched \u2014 a Blob document has no base to resolve them\n * against, and packing them would mean reading the workspace beside the file.\n * See the README's limitations.\n */\nfunction HtmlFrame({ html, title }: { html: string; title: string }): ReactNode {\n const url = useMemo(\n () => URL.createObjectURL(new Blob([html], { type: 'text/html' })),\n [html],\n )\n useEffect(() => () => { URL.revokeObjectURL(url) }, [url])\n return (\n <iframe\n className=\"dsh-fe-html-frame\"\n src={url}\n sandbox=\"allow-scripts\"\n title={title}\n data-preview-html\n />\n )\n}\n\n/**\n * A Markdown file as a document, a JSON file as a collapsible tree, and an HTML\n * file as a page.\n *\n * The label objects are memoized on the translated strings rather than on `t`:\n * the bound translate keeps one identity across a locale change while its output\n * changes, and a fresh labels object on every render would discard the\n * primitives' parse memos.\n */\nfunction FormattedBody({\n page,\n format,\n jsonDocument,\n proseRef,\n t,\n}: {\n page: PreviewText\n format: PreviewFormat\n /** Parsed JSON for a `json` format, decided by the pane before it elected this body. */\n jsonDocument: object | undefined\n /** The rendered Markdown document, which the PDF export clones. */\n proseRef: MutableRefObject<HTMLDivElement | null>\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n const copyLabel = t('code.copy')\n const copiedLabel = t('code.copied')\n const footnotes = t('footnotes')\n const markdownLabels = useMemo<MarkdownLabels>(\n () => ({ code: { copyLabel, copiedLabel }, footnotes }),\n [copyLabel, copiedLabel, footnotes],\n )\n const copyValue = t('json.copyValue')\n const copyJson = t('json.copyJson')\n const copyPath = t('json.copyPath')\n const copyPrettyJson = t('json.copyPrettyJson')\n const copyCompactJson = t('json.copyCompactJson')\n const copied = t('json.copied')\n const copyFailed = t('json.copyFailed')\n const collapseNode = t('json.collapseNode')\n const expandNode = t('json.expandNode')\n const jsonLabels = useMemo<JsonTreeLabels>(() => ({\n copyValue,\n copyJson,\n copyPath,\n copyPrettyJson,\n copyCompactJson,\n copied,\n copyFailed,\n collapseNode,\n expandNode,\n // Reads the live translate at call time, so the tooltip follows a locale\n // change without rebuilding this object; `t` is stable by contract.\n copyButtonTitle: action => t('json.copyTitle', { action }),\n }), [\n collapseNode, copyCompactJson, copyFailed, copyJson, copyPath, copyPrettyJson,\n copyValue, copied, expandNode, t,\n ])\n\n if (format.kind === 'markdown') {\n return (\n <div className=\"dsh-fe-prose\" ref={proseRef} data-preview-format=\"markdown\">\n <MarkdownText text={page.text} labels={markdownLabels} />\n </div>\n )\n }\n if (format.kind === 'html') {\n return (\n <div className=\"dsh-fe-html\" data-preview-format=\"html\">\n <HtmlFrame html={page.text} title={t('preview.htmlFrame')} />\n </div>\n )\n }\n // The pane elects this body for `json` only once the document parses.\n if (jsonDocument === undefined) return null\n return (\n <div className=\"dsh-fe-json\" data-preview-format=\"json\">\n <JsonTree\n data={jsonDocument}\n label={t('preview.jsonLabel')}\n labels={jsonLabels}\n expandTopLevel={false}\n />\n </div>\n )\n}\n\n/** Which body a tab is drawing, which is also which controls apply. */\ntype PreviewBodyKind = 'rendered' | 'source' | 'image'\n\n/**\n * The HTML of an HTML file, with its active content removed.\n *\n * A PDF is static, so scripts buy the export nothing \u2014 and dropping them is what\n * lets the print document be an ordinary same-origin frame instead of a sandboxed\n * one, which in turn is what lets this component drive the print. Parsing is done\n * by the browser rather than by a pattern, so markup inside an attribute or a\n * comment cannot smuggle a tag past the strip.\n * @param source - the file's text.\n * @param title - title to add when the file declares none.\n * @returns a complete document with no scripts, handlers, or javascript URLs.\n */\nfunction printableHtml(source: string, title: string): string {\n const parsed = new DOMParser().parseFromString(source, 'text/html')\n for (const script of [...parsed.querySelectorAll('script')]) script.remove()\n for (const element of [...parsed.querySelectorAll('*')]) {\n for (const attribute of [...element.attributes]) {\n const name = attribute.name.toLowerCase()\n if (name.startsWith('on')) element.removeAttribute(attribute.name)\n else if (/^\\s*javascript:/i.test(attribute.value)) element.removeAttribute(attribute.name)\n }\n }\n if (parsed.title === '') {\n const tag = parsed.createElement('title')\n tag.textContent = title\n parsed.head.append(tag)\n }\n return `<!doctype html>${parsed.documentElement.outerHTML}`\n}\n\n/**\n * The application's own style sheets, so a cloned preview prints as it reads.\n *\n * The copy carries the dark-theme block too (`body[data-ds-dark-theme]`), which\n * simply never matches a print document's body: the page is dark ink on white\n * whichever theme the reader is using.\n * @returns every style tag and stylesheet link, serialized.\n */\nfunction appStyleTags(): string {\n return [...document.querySelectorAll('style, link[rel=\"stylesheet\"]')]\n .map(node => node.outerHTML)\n .join('\\n')\n}\n\n/**\n * A standalone document holding one cloned preview body.\n *\n * The rules appended last are this page's own box, and the promise to keep the\n * document's fills \u2014 code-block greys and table rules \u2014 which browsers otherwise\n * drop when printing.\n * @param title - the document title, which the print dialog offers as a name.\n * @param body - the body markup.\n * @returns a complete document.\n */\nfunction styledDocument(title: string, body: string): string {\n const safeTitle = title.replace(/[<&]/g, character => (character === '<' ? '<' : '&'))\n return [\n '<!doctype html><html><head><meta charset=\"utf-8\">',\n `<title>${safeTitle}</title>`,\n appStyleTags(),\n '<style>body{margin:0;padding:28px 32px;background:#fff;color:#111;',\n 'print-color-adjust:exact;-webkit-print-color-adjust:exact}</style>',\n `</head><body>${body}</body></html>`,\n ].join('')\n}\n\n/**\n * Frame a document off-screen, laid out at page width, and hand it back.\n *\n * The frame is sized like a page rather than collapsed to nothing: the print\n * engine lays the document out from the viewport it finds, so a zero-width frame\n * would print a zero-width column.\n * @returns the appended frame.\n */\nfunction createPrintFrame(): HTMLIFrameElement {\n const frame = document.createElement('iframe')\n frame.setAttribute('aria-hidden', 'true')\n frame.setAttribute('data-preview-print', '')\n frame.style.cssText = 'position:fixed;left:-10000px;top:0;width:794px;height:1123px;border:0'\n document.body.appendChild(frame)\n return frame\n}\n\n/**\n * Write one document into a frame, and print it.\n *\n * Printing is how a reader gets a PDF: every browser's print dialog offers \"Save\n * as PDF\", and its own layout engine keeps the text as text rather than\n * rasterising the page the way a canvas-based PDF library would.\n *\n * The frame outlives the call: a browser may return from print() before its\n * preview has laid the document out, so the frame removes itself on afterprint\n * instead of on return. A frame left over from an earlier export \u2014 one whose\n * afterprint never arrived \u2014 is cleared first, so exports cannot accumulate off\n * screen.\n * @param html - a complete document.\n */\nfunction printDocument(html: string): void {\n for (const stale of document.querySelectorAll('iframe[data-preview-print]')) stale.remove()\n const frame = createPrintFrame()\n const doc = frame.contentDocument\n /* v8 ignore next -- a frame appended to a live document always has one. */\n if (doc === null) return\n doc.open()\n doc.write(html)\n doc.close()\n const view = frame.contentWindow\n /* v8 ignore next -- a frame appended to a live document always has one. */\n if (view === null) return\n view.addEventListener('afterprint', () => { frame.remove() }, { once: true })\n view.focus()\n view.print()\n}\n\n/**\n * Resolve the body a file's format and the tab's mode choice agree on.\n * @param content - what the read delivered, or null while it has not settled.\n * @param format - the file's format.\n * @param mode - the tab's explicit choice, or undefined to follow the default.\n * @param jsonWalkable - whether a `json` format's document parsed into a tree.\n * @returns the body kind.\n */\nfunction bodyKindOf(\n content: PreviewContent | null,\n format: PreviewFormat,\n mode: PreviewMode | undefined,\n jsonWalkable: boolean,\n): PreviewBodyKind {\n if (content !== null && content.kind === 'image') return 'image'\n if (!hasSourceToggle(format)) return 'source'\n if ((mode ?? 'rendered') === 'source') return 'source'\n // JSON the tree cannot walk falls back to its source, so the controls offer the\n // body that is actually on screen.\n if (format.kind === 'json' && !jsonWalkable) return 'source'\n return 'rendered'\n}\n\n/**\n * The body of one settled preview tab.\n * @param props - the tab's content, its format, the elected body, and copy.\n * @returns the scrollport holding that body.\n */\nfunction PreviewBody({\n path,\n content,\n format,\n body,\n jsonDocument,\n jsonFallback,\n wrap,\n proseRef,\n t,\n}: {\n path: string\n content: PreviewContent\n format: PreviewFormat\n body: PreviewBodyKind\n jsonDocument: object | undefined\n /** The reader asked for the tree and the file does not parse; say so above the source. */\n jsonFallback: boolean\n wrap: boolean\n /** Handed to the rendered Markdown container, which the PDF export clones. */\n proseRef: MutableRefObject<HTMLDivElement | null>\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n if (content.kind === 'image') {\n const { name } = pathParts(path)\n return (\n <div className=\"dsh-fe-scroll dsh-fe-imagehost\" data-preview-state=\"ready\" data-preview-format=\"image\">\n <img className=\"dsh-fe-image\" src={content.image.dataUrl} alt={name} data-preview-image />\n </div>\n )\n }\n const page = content.page\n const lines = previewLines(page)\n if (lines.length === 0) {\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines=\"0\"\n >\n <p className=\"dsh-fe-note\" data-preview-row=\"empty\">{t('preview.empty')}</p>\n </div>\n )\n }\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines={lines.length}\n data-preview-body={body}\n >\n {jsonFallback && (\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-row=\"invalid-json\">\n {t('preview.jsonInvalid')}\n </p>\n )}\n {body === 'rendered'\n ? (\n <FormattedBody\n page={page}\n format={format}\n jsonDocument={jsonDocument}\n proseRef={proseRef}\n t={t}\n />\n )\n : <HighlightedSource page={page} lang={format.lang} wrap={wrap} t={t} />}\n {!page.eof && (\n <p className=\"dsh-fe-note\" data-preview-row=\"truncated\">\n {t('preview.truncated', { lines: page.lines })}\n </p>\n )}\n </div>\n )\n}\n\n/**\n * The Files view: the workspace tree, the open preview tabs, and the active tab.\n * @param props - the Conversation View seat, store, injected face, and copy.\n * @returns the two-pane explorer.\n */\nexport function FilesView({\n sessionId, useSessions, useStore, actions, list, read, t,\n}: FilesViewProps): ReactNode {\n const cwd = useSessions(sessions => sessions.byId[sessionId]?.cwd)\n const state = useStore(store => store)\n const activeTabRef = useRef<HTMLButtonElement | null>(null)\n /** The rendered Markdown document, which the PDF export clones. */\n const proseRef = useRef<HTMLDivElement | null>(null)\n /** The tab whose text was just copied, so only its button confirms. */\n const [copied, setCopied] = useState<string | null>(null)\n const copyTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n useEffect(() => () => {\n if (copyTimer.current !== null) clearTimeout(copyTimer.current)\n }, [])\n\n useEffect(() => {\n if (cwd === undefined || state.root === cwd) return\n actions.start(cwd)\n list(cwd)\n }, [actions, cwd, list, state.root])\n\n const active = state.active\n const format: PreviewFormat | null = active === null ? null : previewFormatFor(active)\n const content = active !== null && state.previews[active]?.kind === 'ready'\n ? (state.previews[active] as { content: PreviewContent }).content\n : null\n\n // A JSON document is parsed here, once per settled read, because both the\n // controls' body election and the tree body need the answer. Parsing stays a\n // function of the pane's own decision, not of the read: a file the tree cannot\n // walk is still previewed, as source.\n const jsonDocument = useMemo(() => {\n if (active === null || content === null || content.kind !== 'text') return undefined\n if ((state.modes[active] ?? 'rendered') === 'source') return undefined\n if (previewFormatFor(active).kind !== 'json') return undefined\n return parseJsonDocument(content.page.text)\n }, [active, content, state.modes])\n\n /**\n * Content follows the active tab, not the click that opened it: a tab whose\n * content was dropped for retention reads again the moment it is shown, and a\n * tab that is already loaded costs nothing.\n */\n useEffect(() => {\n if (active === null) return\n if (state.previews[active] !== undefined) return\n read(active)\n }, [active, read, state.previews])\n\n useEffect(() => {\n activeTabRef.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }, [active])\n\n if (cwd === undefined) {\n return (\n <div className=\"dsh-fe-root\" data-files-state=\"no-workspace\">\n <div className=\"dsh-fe-status\">{t('tree.noWorkspace')}</div>\n </div>\n )\n }\n if (state.root === null) return null\n\n const openSet = new Set(state.open)\n const tree: TreeContext = {\n state,\n open: openSet,\n onToggle: (path) => {\n const loaded = state.levels[path] !== undefined\n actions.toggled(path)\n if (!loaded) list(path)\n },\n onOpen: (path) => { actions.openFile(path) },\n t,\n }\n // Reload drops every level and asks again for the expanded ones; a collapsed\n // level is fetched again the next time it opens.\n const reloadTree = (): void => {\n actions.reset()\n for (const path of state.expanded) list(path)\n }\n const reloadPreview = (): void => {\n if (active !== null) read(active)\n }\n // One copy control serves every body that has text: a rendered document's\n // source, a JSON tree's document, highlighted code, and plain text all copy\n // the file's own text, which is what a reader pastes somewhere else.\n const copyActive = (): void => {\n if (active === null || content === null || content.kind !== 'text') return\n const path = active\n const text = content.page.text\n void writeClipboard(text).then((ok: boolean) => {\n if (!ok) return\n setCopied(path)\n if (copyTimer.current !== null) clearTimeout(copyTimer.current)\n copyTimer.current = setTimeout(() => { setCopied(null) }, 1000)\n })\n }\n /**\n * Export the rendered body as a PDF, through the browser's own print dialog.\n *\n * Markdown prints from the live document node, so what lands on the page is\n * exactly what the reader sees \u2014 including the code fences the primitive\n * highlighted. HTML prints from its own document with the active content\n * stripped, which a static page has no use for.\n */\n const exportPdf = (): void => {\n if (active === null || format === null || content === null || content.kind !== 'text') return\n if (!canExportPdf(format)) return\n const name = pathParts(active).name\n if (format.kind === 'html') {\n printDocument(printableHtml(content.page.text, name))\n return\n }\n const prose = proseRef.current\n if (prose === null) return\n printDocument(styledDocument(name, prose.outerHTML))\n }\n const closeTab = (path: string): void => {\n actions.closeFile(path)\n }\n const root = pathParts(state.root)\n const labels = tabLabels(state.open)\n const wrap = active === null ? true : state.wraps[active] ?? true\n const preview: PreviewState | undefined = active === null ? undefined : state.previews[active]\n const body = bodyKindOf(content, format ?? { kind: 'text', lang: undefined, mediaType: undefined }, active === null ? undefined : state.modes[active], jsonDocument !== undefined)\n const jsonFallback = format?.kind === 'json'\n && (active === null ? 'rendered' : state.modes[active] ?? 'rendered') === 'rendered'\n && jsonDocument === undefined\n && content !== null\n && content.kind === 'text'\n const meta = content === null\n ? null\n : content.kind === 'text'\n ? [\n t('preview.lines', { lines: content.page.lines }),\n content.page.bytes === undefined ? null : fileSizeText(content.page.bytes),\n ].filter(part => part !== null).join(' \u00B7 ')\n : content.image.bytes === undefined\n ? null\n : fileSizeText(content.image.bytes)\n\n return (\n <div\n className=\"dsh-fe-root\"\n data-files-state=\"tree\"\n data-files-root={state.root}\n data-conversation-composer-overlay=\"\"\n >\n <div className=\"dsh-fe-panes\">\n <div className=\"dsh-fe-tree\" data-files-pane=\"tree\">\n <div className=\"dsh-fe-head\">\n <span className=\"dsh-fe-path\" title={state.root}>\n <span>\n <span className=\"dsh-fe-path-muted\">{root.directory}</span>\n {root.name}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('tree.reload')}\n title={t('tree.reload')}\n data-files-reload\n onClick={reloadTree}\n >\n <IconRefreshOutline16 />\n </button>\n </div>\n <div className=\"dsh-fe-scroll\">\n <ul className=\"dsh-fe-level\"><Level path={state.root} tree={tree} /></ul>\n </div>\n </div>\n <div className=\"dsh-fe-preview\" data-files-pane=\"preview\">\n <div className=\"dsh-fe-head dsh-fe-tabhead\">\n {state.open.length === 0\n ? <span className=\"dsh-fe-path dsh-fe-path-muted\">{t('preview.title')}</span>\n : (\n <div className=\"dsh-fe-tabs\" role=\"tablist\" aria-label={t('preview.tabs')} data-preview-tabs>\n {state.open.map(path => {\n const isActive = path === active\n return (\n <span className=\"dsh-fe-tab\" key={path} role=\"presentation\" data-preview-tab={path} data-active={isActive || undefined}>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n className=\"dsh-fe-tab-label\"\n title={path}\n ref={isActive ? activeTabRef : undefined}\n data-preview-tab-open={path}\n onClick={() => { actions.activate(path) }}\n onAuxClick={(event) => {\n // Middle click closes, as it does in every editor.\n if (event.button === 1) closeTab(path)\n }}\n >\n <FileTypeIcon kind={classifyFileType(path)} size={14} />\n <span className=\"dsh-fe-tab-name\">{labels[path]}</span>\n </button>\n <button\n type=\"button\"\n className=\"dsh-fe-tab-close\"\n aria-label={t('preview.close', { name: labels[path] })}\n title={t('preview.close', { name: labels[path] })}\n data-preview-tab-close={path}\n onClick={() => { closeTab(path) }}\n >\n <span aria-hidden=\"true\">\u00D7</span>\n </button>\n </span>\n )\n })}\n </div>\n )}\n {active !== null && body === 'source' && format?.lang !== undefined && (\n <span className=\"dsh-fe-lang\" data-preview-lang-badge>{format.lang}</span>\n )}\n {content !== null && meta !== null && (\n <span className=\"dsh-fe-meta\" data-preview-meta>{meta}</span>\n )}\n {content !== null && content.kind === 'text' && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.copy')}\n title={t('preview.copy')}\n data-preview-copy\n onClick={copyActive}\n >\n {copied === active ? t('preview.copied') : t('preview.copy')}\n </button>\n )}\n {active !== null && format !== null && hasSourceToggle(format) && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={body === 'source'}\n aria-label={body === 'source' ? t('preview.rendered') : t('preview.source')}\n title={body === 'source' ? t('preview.rendered') : t('preview.source')}\n data-preview-mode-toggle\n onClick={() => { actions.setMode(active, body === 'source' ? 'rendered' : 'source') }}\n >\n {body === 'source' ? t('preview.rendered') : t('preview.source')}\n </button>\n )}\n {active !== null && body === 'rendered' && format !== null && canExportPdf(format) && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.pdf')}\n title={t('preview.pdf')}\n data-preview-pdf\n onClick={exportPdf}\n >\n {t('preview.pdf')}\n </button>\n )}\n {active !== null && body === 'source' && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={wrap}\n aria-label={wrap ? t('preview.nowrap') : t('preview.wrap')}\n title={wrap ? t('preview.nowrap') : t('preview.wrap')}\n data-preview-wrap-toggle\n onClick={() => { actions.setWrap(active, !wrap) }}\n >\n {wrap ? '\u21B5' : '\u2192'}\n </button>\n )}\n {active !== null && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.reload')}\n title={t('preview.reload')}\n data-preview-reload\n onClick={reloadPreview}\n >\n <IconRefreshOutline16 />\n </button>\n )}\n </div>\n {active === null && <div className=\"dsh-fe-status\">{t('preview.placeholder')}</div>}\n {active !== null && (preview === undefined || preview.kind === 'loading') && (\n <div className=\"dsh-fe-status\">{t('preview.loading')}</div>\n )}\n {active !== null && preview?.kind === 'failed' && (\n <div className=\"dsh-fe-scroll\" data-preview-state=\"failed\">\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-code={preview.failure.code}>\n {previewFailureLine(t, preview.failure)}\n </p>\n <button type=\"button\" className=\"dsh-fe-tool\" onClick={reloadPreview}>\n {t('preview.reload')}\n </button>\n </div>\n )}\n {active !== null && content !== null && format !== null && (\n <PreviewBody\n path={active}\n content={content}\n format={format}\n body={body}\n jsonDocument={jsonDocument}\n jsonFallback={jsonFallback}\n wrap={wrap}\n proseRef={proseRef}\n t={t}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n", "/**\n * What a workspace file is, for preview purposes.\n *\n * The pane picks a display from the file's own name before it reads anything,\n * so the read itself can differ: an image needs its complete bytes, everything\n * else needs a page of text. The decision is a pure function of the path, which\n * keeps it testable and keeps the read path honest \u2014 the pane never reads bytes\n * it will not draw.\n *\n * Only grammars the shared syntax highlighter actually carries are mapped. An\n * unrecognized suffix is plain text on purpose: a wrong grammar colours the\n * file misleadingly, while plain numbered text is merely unadorned.\n */\n\n/** The display a file's contents get. */\nexport type PreviewFormatKind = 'markdown' | 'html' | 'json' | 'code' | 'image' | 'text'\n\n/** One file's preview format. */\nexport interface PreviewFormat {\n /** Which body the pane draws. */\n readonly kind: PreviewFormatKind\n /** Grammar hint for the highlighted source view; absent when no grammar is known. */\n readonly lang: string | undefined\n /** Media type the read bytes are labelled with; present only for `image`. */\n readonly mediaType: string | undefined\n}\n\n/** Image suffixes and the media type their bytes carry. */\nconst IMAGE_MEDIA_TYPES = new Map<string, string>([\n ['png', 'image/png'],\n ['apng', 'image/apng'],\n ['jpg', 'image/jpeg'],\n ['jpeg', 'image/jpeg'],\n ['jfif', 'image/jpeg'],\n ['gif', 'image/gif'],\n ['webp', 'image/webp'],\n ['avif', 'image/avif'],\n ['bmp', 'image/bmp'],\n ['ico', 'image/x-icon'],\n // An SVG draws as an image here; `<img>` neither runs its scripts nor lets it\n // reach the application origin, so the markup stays inert.\n ['svg', 'image/svg+xml'],\n])\n\n/**\n * Suffix \u2192 grammar id, restricted to the ids the shared highlighter resolves\n * (its own alias list). A suffix whose grammar is absent stays unmapped so the\n * file falls through to plain text rather than a wrong colouring.\n */\nconst GRAMMAR_EXTENSIONS: Readonly<Record<string, readonly string[]>> = {\n typescript: ['ts', 'tsx', 'mts', 'cts', 'js', 'jsx', 'mjs', 'cjs'],\n shellscript: ['sh', 'bash', 'zsh', 'ksh', 'shell'],\n json: ['json', 'jsonc', 'jsonl', 'ndjson', 'map', 'webmanifest'],\n python: ['py', 'pyw', 'pyi'],\n ruby: ['rb', 'rake', 'gemspec', 'ru'],\n go: ['go'],\n rust: ['rs'],\n java: ['java'],\n c: ['c', 'h'],\n cpp: ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx', 'ipp'],\n csharp: ['cs', 'csx'],\n kotlin: ['kt', 'kts'],\n swift: ['swift'],\n php: ['php', 'phtml', 'php3', 'php4', 'php5'],\n yaml: ['yaml', 'yml'],\n toml: ['toml'],\n ini: ['ini', 'cfg', 'conf', 'properties', 'desktop', 'service'],\n markdown: ['md', 'markdown', 'mkd', 'mdown', 'mdwn'],\n mdx: ['mdx'],\n html: ['html', 'htm', 'xhtml', 'shtml'],\n css: ['css'],\n scss: ['scss'],\n less: ['less'],\n sql: ['sql', 'ddl', 'dml'],\n xml: ['xml', 'xsd', 'xsl', 'xslt', 'rss', 'atom', 'plist'],\n lua: ['lua'],\n}\n\nconst GRAMMAR_BY_EXTENSION = new Map(Object.entries(GRAMMAR_EXTENSIONS)\n .flatMap(([grammar, suffixes]) => suffixes.map(suffix => [suffix, grammar] as const)))\n\n/**\n * The lowercase suffix a file name ends in.\n *\n * A name that starts with its dot has no suffix: `.gitignore` is a whole name,\n * not a `gitignore` file. A trailing dot has none either.\n * @param path - absolute or relative path, in either separator style.\n * @returns the suffix without its dot, or undefined.\n */\nexport function extensionOf(path: string): string | undefined {\n const normalized = path.replaceAll('\\\\', '/')\n const name = normalized.slice(normalized.lastIndexOf('/') + 1)\n const at = name.lastIndexOf('.')\n if (at <= 0 || at === name.length - 1) return undefined\n return name.slice(at + 1).toLowerCase()\n}\n\n/**\n * Decide how one file is previewed, from its name alone.\n * @param path - absolute or relative path of the file.\n * @returns its preview format.\n */\nexport function previewFormatFor(path: string): PreviewFormat {\n const extension = extensionOf(path)\n const mediaType = extension === undefined ? undefined : IMAGE_MEDIA_TYPES.get(extension)\n if (mediaType !== undefined) return { kind: 'image', lang: undefined, mediaType }\n const lang = extension === undefined ? undefined : GRAMMAR_BY_EXTENSION.get(extension)\n if (lang === undefined) return { kind: 'text', lang: undefined, mediaType: undefined }\n if (lang === 'json') return { kind: 'json', lang, mediaType: undefined }\n // Markdown and HTML each get a rendered body and a source body; MDX stays on\n // the source side, because its JSX would render as prose.\n if (lang === 'markdown') return { kind: 'markdown', lang, mediaType: undefined }\n if (lang === 'html') return { kind: 'html', lang, mediaType: undefined }\n return { kind: 'code', lang, mediaType: undefined }\n}\n\n/**\n * Whether the pane offers a rendered/source switch for a format.\n * @param format - the file's format.\n * @returns whether both bodies exist.\n */\nexport function hasSourceToggle(format: PreviewFormat): boolean {\n return format.kind === 'markdown' || format.kind === 'html' || format.kind === 'json'\n}\n\n/**\n * Whether the rendered body of a format can be exported as a PDF.\n *\n * Only the two formats whose rendered body is a *document*: a JSON tree and a\n * source listing have no page to print, and an image or plain text is already\n * what it is.\n * @param format - the file's format.\n * @returns whether the export control applies.\n */\nexport function canExportPdf(format: PreviewFormat): boolean {\n return format.kind === 'markdown' || format.kind === 'html'\n}\n", "/**\n * The explorer's asynchronous half: listing directories, and reading whichever\n * file a tab asks for in the form its format needs.\n *\n * The component never awaits anything. It calls `list` / `read`, and this face\n * performs the Remote call and writes the outcome through the store's own\n * actions \u2014 the Slot-standard `inject` shape, so the Session id is resolved by\n * the framework and the write set stays the store's.\n *\n * One level has one listing in force: asking for a level again (the reload\n * gesture, a directory reopened after a reset) retires the listing still in\n * flight for it, whose settlement then writes nothing. Each tab has one read in\n * force in the same way, and the store additionally drops a settlement for a tab\n * that has since been closed. Requests carry the plugin's lifetime signal:\n * unloading the plugin abandons everything in flight, and no settlement writes\n * after that.\n */\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport { previewFormatFor } from './format.ts'\nimport type { PreviewText, createFilesStore } from './store.ts'\n\n/** The slice of the Client Remote face this plugin calls. */\nexport type WorkspaceFilesRemote = Pick<ClientRemote, 'workspaceFiles'>\n\n/**\n * Largest number of leading lines one text preview asks for. The host's own\n * `maxLines` default is higher; keeping the page small is this viewer's choice,\n * and a file that does not reach `eof` says so in the header.\n */\nexport const PREVIEW_LINES = 2000\n\n/** The explorer's injected business face, as the component receives it. */\nexport interface FilesInjected {\n /**\n * List one directory into the store, retiring any listing in flight for it.\n * @param path - absolute directory path.\n */\n readonly list: (path: string) => void\n /**\n * Read one open tab into the store: a page of text, or complete bytes when the\n * file's format draws an image.\n * @param path - absolute file path of an open tab.\n */\n readonly read: (path: string) => void\n}\n\n/**\n * Bind the explorer's face to one Session's Remote namespace.\n * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.\n * @param sessionId - the Session whose workspace root authorizes every call.\n * @param signal - the plugin's lifetime signal; aborting abandons in-flight work.\n * @param actions - the bound store actions this face writes through.\n * @returns the listing and preview operations the component calls.\n */\nexport function filesFace(\n remote: WorkspaceFilesRemote,\n sessionId: SessionId,\n signal: AbortSignal,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n): FilesInjected {\n /** Per absolute path: the listing generation a settlement must match; the latest request wins. */\n const listingGenerations = new Map<string, number>()\n /** Per open tab: the read generation a settlement must match; the latest request wins. */\n const readGenerations = new Map<string, number>()\n\n return {\n list(path) {\n if (signal.aborted) return\n const generation = (listingGenerations.get(path) ?? 0) + 1\n listingGenerations.set(path, generation)\n actions.loading(path)\n void remote.workspaceFiles.list(sessionId, path, signal).then((result) => {\n if (signal.aborted || listingGenerations.get(path) !== generation) return\n if (result.ok) {\n actions.loaded(path, {\n entries: result.value.entries,\n truncated: result.value.truncated,\n })\n } else {\n actions.failed(path, result.error)\n }\n })\n },\n read(path) {\n if (signal.aborted) return\n const generation = (readGenerations.get(path) ?? 0) + 1\n readGenerations.set(path, generation)\n const format = previewFormatFor(path)\n actions.reading(path)\n const settle = (write: () => void): void => {\n if (signal.aborted || readGenerations.get(path) !== generation) return\n write()\n }\n // An image needs its bytes whole; everything else needs a page of lines,\n // because the source view can show a bounded prefix of any text file.\n if (format.kind === 'image') {\n void remote.workspaceFiles.readAll(sessionId, path, signal).then((result) => {\n settle(() => {\n if (result.ok) {\n actions.previewLoaded(path, {\n kind: 'image',\n image: {\n dataUrl: `data:${format.mediaType};base64,${result.value.data}`,\n bytes: result.value.bytes,\n },\n })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n return\n }\n void remote.workspaceFiles.read(sessionId, path, { offset: 1, limit: PREVIEW_LINES }, signal)\n .then((result) => {\n settle(() => {\n if (result.ok) {\n const page: PreviewText = {\n text: result.value.text,\n lines: result.value.lines,\n eof: result.value.eof,\n bytes: result.value.bytes,\n }\n actions.previewLoaded(path, { kind: 'text', page })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n },\n }\n}\n", "/**\n * `fileExplorer` namespace dictionaries, and the namespace declaration.\n *\n * The failure lines name what the tree could not list, one code each, because a\n * directory that is gone, one outside the workspace, and a path that is not a\n * directory each suggest a different next step. The preview lines do the same\n * for the read failures a viewer can meet: not text, too large, not a regular\n * file.\n *\n * The namespace merge lives with its key set so that any module naming\n * `TranslateNS<'fileExplorer'>` or `PropsLocale<'fileExplorer'>` needs only this\n * file, whichever entry a program loads first.\n */\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'fileExplorer'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** View tab label, tree states, and preview states. */\n fileExplorer: FileExplorerKey\n }\n}\n\n/** Simplified Chinese dictionary and key-set source of truth. */\nexport const zh = {\n 'view.files': '\u6587\u4EF6',\n 'tree.root': '\u5DE5\u4F5C\u533A',\n 'tree.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'tree.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'tree.empty': '\u7A7A\u76EE\u5F55',\n 'tree.truncated': '\u6761\u76EE\u8FC7\u591A\uFF0C\u53EA\u663E\u793A\u4E86\u4E00\u90E8\u5206\u3002',\n 'tree.noWorkspace': '\u8FD9\u4E2A\u4F1A\u8BDD\u6CA1\u6709\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002',\n 'tree.other': '\u65E2\u4E0D\u662F\u6587\u4EF6\u4E5F\u4E0D\u662F\u76EE\u5F55\uFF0C\u65E0\u6CD5\u6253\u5F00\u3002',\n 'tree.error.notFound': '\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'tree.error.outsideWorkspace': '\u8BE5\u76EE\u5F55\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u3002',\n 'tree.error.notDirectory': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55\u3002',\n 'tree.error.unavailable': '\u8BFB\u53D6\u5931\u8D25\uFF1A{message}',\n 'preview.placeholder': '\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u8FDB\u884C\u9884\u89C8\u3002',\n 'preview.empty': '\u7A7A\u6587\u4EF6',\n 'preview.rendered': '\u9884\u89C8',\n 'preview.source': '\u6E90\u7801',\n 'preview.jsonLabel': 'JSON \u6587\u6863',\n 'preview.jsonInvalid': '\u8FD9\u4E0D\u662F\u53EF\u5C55\u5F00\u7684 JSON\uFF0C\u5DF2\u663E\u793A\u6E90\u7801\u3002',\n 'code.copy': '\u590D\u5236',\n 'code.copied': '\u5DF2\u590D\u5236',\n 'footnotes': '\u811A\u6CE8',\n 'json.copyValue': '\u590D\u5236\u503C',\n 'json.copyJson': '\u590D\u5236 JSON',\n 'json.copyPath': '\u590D\u5236\u8DEF\u5F84',\n 'json.copyPrettyJson': '\u590D\u5236\u683C\u5F0F\u5316 JSON',\n 'json.copyCompactJson': '\u590D\u5236\u538B\u7F29 JSON',\n 'json.copied': '\u5DF2\u590D\u5236',\n 'json.copyFailed': '\u590D\u5236\u5931\u8D25',\n 'json.collapseNode': '\u6536\u8D77',\n 'json.expandNode': '\u5C55\u5F00',\n 'json.copyTitle': '{action}',\n 'preview.title': '\u9884\u89C8',\n 'preview.tabs': '\u6253\u5F00\u7684\u6587\u4EF6',\n 'preview.close': '\u5173\u95ED {name}',\n 'preview.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'preview.copy': '\u590D\u5236',\n 'preview.copied': '\u5DF2\u590D\u5236',\n 'preview.pdf': '\u5BFC\u51FA PDF',\n 'preview.htmlFrame': 'HTML \u9884\u89C8',\n 'preview.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'preview.truncated': '\u6587\u4EF6\u8F83\u957F\uFF0C\u53EA\u663E\u793A\u524D {lines} \u884C\u3002',\n 'preview.wrap': '\u81EA\u52A8\u6362\u884C',\n 'preview.nowrap': '\u4E0D\u6362\u884C',\n 'preview.lines': '{lines} \u884C',\n 'preview.error.notFound': '\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'preview.error.notText': '\u8FD9\u4E0D\u662F\u53EF\u4EE5\u9884\u89C8\u7684\u6587\u672C\u6587\u4EF6\u3002',\n 'preview.error.tooLarge': '\u6587\u4EF6\u592A\u5927\uFF0C\u65E0\u6CD5\u9884\u89C8\u3002',\n 'preview.error.notRegularFile': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u666E\u901A\u6587\u4EF6\u3002',\n 'preview.error.unavailable': '\u9884\u89C8\u5931\u8D25\uFF1A{message}',\n} satisfies Record<string, string>\n\n/** Files dictionary key union. */\nexport type FileExplorerKey = keyof typeof zh\n\n/** English dictionary, checked against the Chinese key set. */\nexport const en = {\n 'view.files': 'Files',\n 'tree.root': 'Workspace',\n 'tree.reload': 'Reload',\n 'tree.loading': 'Reading\u2026',\n 'tree.empty': 'Empty directory',\n 'tree.truncated': 'Too many entries, showing only some of them.',\n 'tree.noWorkspace': 'This session has no workspace directory.',\n 'tree.other': 'Not a file or a directory, so it cannot be opened.',\n 'tree.error.notFound': 'That directory is gone. It may have been moved or deleted.',\n 'tree.error.outsideWorkspace': 'That directory is outside the workspace, so it cannot be read.',\n 'tree.error.notDirectory': 'That is not a directory.',\n 'tree.error.unavailable': 'Read failed: {message}',\n 'preview.placeholder': 'Pick a file on the left to preview it.',\n 'preview.empty': 'Empty file',\n 'preview.rendered': 'Rendered',\n 'preview.source': 'Source',\n 'preview.jsonLabel': 'JSON document',\n 'preview.jsonInvalid': 'That is not JSON a tree can walk, so the source is shown.',\n 'code.copy': 'Copy',\n 'code.copied': 'Copied',\n 'footnotes': 'Footnotes',\n 'json.copyValue': 'Copy value',\n 'json.copyJson': 'Copy JSON',\n 'json.copyPath': 'Copy path',\n 'json.copyPrettyJson': 'Copy pretty JSON',\n 'json.copyCompactJson': 'Copy compact JSON',\n 'json.copied': 'Copied',\n 'json.copyFailed': 'Copy failed',\n 'json.collapseNode': 'Collapse',\n 'json.expandNode': 'Expand',\n 'json.copyTitle': '{action}',\n 'preview.title': 'Preview',\n 'preview.tabs': 'Open files',\n 'preview.close': 'Close {name}',\n 'preview.loading': 'Reading\u2026',\n 'preview.copy': 'Copy',\n 'preview.copied': 'Copied',\n 'preview.pdf': 'Export PDF',\n 'preview.htmlFrame': 'HTML preview',\n 'preview.reload': 'Reload',\n 'preview.truncated': 'This file is long, showing the first {lines} lines.',\n 'preview.wrap': 'Wrap long lines',\n 'preview.nowrap': 'Do not wrap long lines',\n 'preview.lines': '{lines} lines',\n 'preview.error.notFound': 'That file is gone. It may have been moved or deleted.',\n 'preview.error.notText': 'That is not a text file this preview can show.',\n 'preview.error.tooLarge': 'That file is too large to preview.',\n 'preview.error.notRegularFile': 'That is not a regular file.',\n 'preview.error.unavailable': 'Preview failed: {message}',\n} satisfies Record<FileExplorerKey, string>\n", "/**\n * The file explorer's view state: what each listed directory holds, which\n * directories are open, which files are open in preview tabs, and what each tab\n * is showing.\n *\n * The explorer is not one resource. A directory listing per level, expanded\n * lazily, plus one read per open preview tab, is state the view owns \u2014 so it\n * lives in a Slot-standard exclusive store: the framework mints one instance per\n * Session, and the state survives switching to Chat or Trajectory and back, which\n * unmounts the component.\n *\n * Write actions run only after `start`; the store's keys are absolute paths,\n * because a child is its parent joined with the entry name and the host accepts\n * the absolute form for both listing and reading.\n *\n * Tabs are unbounded but loaded content is not: an image preview holds the\n * file's complete bytes as a base64 data URL, so keeping every tab's content\n * alive would let a reader grow the tab's memory to an arbitrary size. The store\n * therefore retains content for the active tab and the most recently visited\n * ones, and drops the rest \u2014 the tab stays open and simply reads again when it\n * is next activated. See {@link RETAINED_PREVIEWS}.\n */\nimport { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-store'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\n\n/**\n * How many tabs keep their loaded content. A dropped tab is still open: it reads\n * again when activated, which shows as a brief loading state.\n *\n * Five covers the working set \u2014 the tab being read, plus the few it was reached\n * through \u2014 while bounding what a reader who opens everything in a directory can\n * pin in memory.\n */\nexport const RETAINED_PREVIEWS = 5\n\n/** One directory's contents, as one expanded level of the tree. */\nexport interface DirLevel {\n /** The directory's entries, in the endpoint's order. */\n readonly entries: readonly WorkspaceDirectoryEntry[]\n /** The listing hit the endpoint's entry cap, so entries are missing. */\n readonly truncated: boolean\n}\n\n/** What one directory level is doing right now. */\nexport type LevelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly level: DirLevel }\n | { readonly kind: 'failed'; readonly failure: RemoteFailure }\n\n/** One page of text, as a preview keeps it. */\nexport interface PreviewText {\n /** The page's lines joined with `\\n`. */\n readonly text: string\n /** How many lines the page holds. */\n readonly lines: number\n /** Whether the page includes the file's last line. */\n readonly eof: boolean\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/** Complete image bytes, addressed as a URL the browser can draw directly. */\nexport interface PreviewImage {\n /** `data:` URL carrying the file's complete bytes under its media type. */\n readonly dataUrl: string\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/**\n * What one read delivered. A tab decides which arm it needs from the file's name\n * before reading, so the two never mix: a page of text for everything readable\n * as text, complete bytes for an image.\n */\nexport type PreviewContent =\n | { readonly kind: 'text'; readonly page: PreviewText }\n | { readonly kind: 'image'; readonly image: PreviewImage }\n\n/**\n * Which body a tab draws for a file that has more than one. An absent entry\n * leaves the choice to the format's own default, so a mode picked for one file\n * never leaks into another.\n */\nexport type PreviewMode = 'rendered' | 'source'\n\n/** What one tab is showing right now. */\nexport type PreviewState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly content: PreviewContent }\n | { readonly kind: 'failed'; readonly failure: RemoteFailure }\n\n/** The explorer's whole state for one Session. */\nexport interface FilesState {\n /** Absolute path of the workspace root this explorer is rooted at; `null` before it is known. */\n root: string | null\n /** Level state by absolute directory path; a path absent here was never asked for. */\n levels: Record<string, LevelState>\n /** Expanded absolute directory paths, root included, in expansion order. */\n expanded: string[]\n /** Open preview tabs by absolute path, in open order. */\n open: string[]\n /** The tab being shown, or `null` when nothing is open. */\n active: string | null\n /** Loaded content by absolute path; a path absent here was never read or has been dropped. */\n previews: Record<string, PreviewState>\n /** Absolute paths whose content is retained, most recently used first. */\n retained: string[]\n /** Body choice by absolute path; an absent entry follows the format's default. */\n modes: Record<string, PreviewMode>\n /** Line-wrap preference by absolute path; an absent entry wraps. */\n wraps: Record<string, boolean>\n}\n\n/** The explorer store's write set. */\ntype FilesActions = {\n /** Seed the explorer at its workspace root, with the root expanded and no tabs. */\n start: (draft: FilesState, root: string) => void\n /** Mark one directory as being listed. */\n loading: (draft: FilesState, path: string) => void\n /** Record one directory's contents. */\n loaded: (draft: FilesState, path: string, level: DirLevel) => void\n /** Record why one directory could not be listed. */\n failed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Open a collapsed directory, or collapse an open one. */\n toggled: (draft: FilesState, path: string) => void\n /** Drop every listed level, keeping what is expanded: the reload gesture's first half. */\n reset: (draft: FilesState) => void\n /** Open a file in a tab and show it; an already-open file is focused, not duplicated. */\n openFile: (draft: FilesState, path: string) => void\n /** Show an open tab, keeping its position in the strip. */\n activate: (draft: FilesState, path: string) => void\n /** Close a tab, showing its right neighbour, else its left, else nothing. */\n closeFile: (draft: FilesState, path: string) => void\n /** Mark one tab as being read. */\n reading: (draft: FilesState, path: string) => void\n /** Record one tab's contents. */\n previewLoaded: (draft: FilesState, path: string, content: PreviewContent) => void\n /** Record why one file could not be read. */\n previewFailed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Forget one tab's loaded content, leaving the tab open. */\n dropPreview: (draft: FilesState, path: string) => void\n /** Choose whether one tab wraps long lines. */\n setWrap: (draft: FilesState, path: string, wrap: boolean) => void\n /** Choose which body one tab shows. */\n setMode: (draft: FilesState, path: string, mode: PreviewMode) => void\n}\n\n/** The state a freshly created explorer starts from. */\nfunction emptyState(): FilesState {\n return {\n root: null,\n levels: {},\n expanded: [],\n open: [],\n active: null,\n previews: {},\n retained: [],\n modes: {},\n wraps: {},\n }\n}\n\n/**\n * Record one path as most recently used, and drop the content of tabs that fell\n * past the retention cap. The active tab is always kept, so nothing the reader is\n * looking at is ever dropped.\n * @param d - draft state.\n * @param path - the path whose content was just loaded, or is being shown.\n */\nfunction retain(d: FilesState, path: string): void {\n d.retained = [path, ...d.retained.filter(candidate => candidate !== path)]\n const keep = new Set([...(d.active === null ? [] : [d.active]), ...d.retained.slice(0, RETAINED_PREVIEWS)])\n for (const candidate of Object.keys(d.previews)) {\n if (!keep.has(candidate)) delete d.previews[candidate]\n }\n d.retained = d.retained.filter(candidate => keep.has(candidate))\n}\n\n/**\n * Declare the explorer's store.\n *\n * A factory rather than a shared handle: the registration declares it as an\n * exclusive store, so the framework mints one instance per Session and disposes\n * it with that Session's binding.\n * @returns the store handle to declare on the registration.\n */\nexport function createFilesStore(): EngineStoreHandle<FilesState, FilesActions> {\n return defineStore({\n init: emptyState,\n actions: {\n start: (d, root) => {\n Object.assign(d, emptyState())\n d.root = root\n d.expanded = [root]\n },\n loading: (d, path) => {\n d.levels[path] = { kind: 'loading' }\n },\n loaded: (d, path, level) => {\n d.levels[path] = { kind: 'ready', level }\n },\n failed: (d, path, failure) => {\n d.levels[path] = { kind: 'failed', failure }\n },\n toggled: (d, path) => {\n const at = d.expanded.indexOf(path)\n if (at >= 0) d.expanded.splice(at, 1)\n else d.expanded.push(path)\n },\n reset: (d) => {\n d.levels = {}\n },\n openFile: (d, path) => {\n if (!d.open.includes(path)) d.open.push(path)\n d.active = path\n },\n activate: (d, path) => {\n if (!d.open.includes(path)) return\n d.active = path\n // Showing a retained tab makes it the most recently used one, so the tab\n // the reader just came from is the first candidate for eviction.\n retain(d, path)\n },\n closeFile: (d, path) => {\n const at = d.open.indexOf(path)\n if (at < 0) return\n d.open.splice(at, 1)\n delete d.previews[path]\n delete d.modes[path]\n delete d.wraps[path]\n d.retained = d.retained.filter(candidate => candidate !== path)\n if (d.active !== path) return\n // The neighbour that takes over: the tab that slid into this slot, else\n // the one before it, matching how editors close.\n d.active = d.open[at] ?? d.open[at - 1] ?? null\n },\n reading: (d, path) => {\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'loading' }\n },\n previewLoaded: (d, path, content) => {\n // A settlement for a tab the reader already closed writes nothing: the\n // store keys content by path, not by arrival order.\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'ready', content }\n retain(d, path)\n },\n previewFailed: (d, path, failure) => {\n if (!d.open.includes(path)) return\n d.previews[path] = { kind: 'failed', failure }\n },\n dropPreview: (d, path) => {\n delete d.previews[path]\n d.retained = d.retained.filter(candidate => candidate !== path)\n },\n setWrap: (d, path, wrap) => {\n d.wraps[path] = wrap\n },\n setMode: (d, path, mode) => {\n d.modes[path] = mode\n },\n },\n })\n}\n", "/**\n * The explorer's stylesheet, injected as one owned `<style>` tag.\n *\n * A client bundle arriving through the module loader is a single script: the\n * loader fetches `lib/client.js` and nothing else, so a separate stylesheet\n * would never be requested. Injecting the sheet from the bundle is the same\n * mechanism the built-in packages' `clientBundle()` preset uses for global CSS,\n * and the tag is removed when the plugin unloads.\n *\n * Colours come from the shell's `--dsw-alias-*` theme tokens, so the page\n * follows the active appearance without owning any palette of its own.\n */\n\n/** Marker attribute for this plugin's style tag. */\nconst STYLE_ATTRIBUTE = 'data-dsh-file-explorer'\n\nconst CSS = `\n.dsh-fe-root {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: 1.5;\n}\n\n.dsh-fe-panes {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.dsh-fe-tree,\n.dsh-fe-preview {\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.dsh-fe-tree {\n flex: 0 0 auto;\n width: 280px;\n min-width: 160px;\n max-width: 60%;\n resize: horizontal;\n overflow: hidden;\n border-right: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-fe-preview {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.dsh-fe-head {\n display: flex;\n flex: 0 0 auto;\n gap: 4px;\n align-items: center;\n box-sizing: border-box;\n height: 38px;\n padding: 0 6px 0 14px;\n /* The row is filled rather than transparent, so the pane's chrome reads as\n chrome instead of as the first line of the file. The palette's layer tokens\n are all pure white in light mode (bg-base, bg-layer-1/2/3 share a value), so\n the only token that separates a surface from the content in BOTH themes is\n this wash: 4% ink in light, 8% in dark. See the note in CONTRIBUTING.md. */\n background: var(--dsw-alias-bg-skeleton);\n border-bottom: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n/* A grammar badge for the source body, which is where the language used to be\n named: the code block's own banner is hidden in this pane so there is exactly\n one copy control, in this row. */\n.dsh-fe-lang {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 11px;\n white-space: nowrap;\n}\n\n.dsh-fe-path {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: 12px;\n white-space: nowrap;\n text-overflow: ellipsis;\n direction: rtl;\n text-align: left;\n}\n\n.dsh-fe-path > span {\n direction: ltr;\n unicode-bidi: embed;\n}\n\n.dsh-fe-path-muted {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-meta {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n white-space: nowrap;\n}\n\n.dsh-fe-tool {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 11px;\n line-height: 1;\n background: transparent;\n border: none;\n border-radius: 14px;\n cursor: pointer;\n}\n\n.dsh-fe-tool:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-tool svg {\n width: 15px;\n height: 15px;\n}\n\n.dsh-fe-scroll {\n flex: 1 1 auto;\n min-height: 0;\n padding: 8px 8px calc(var(--dsh-composer-height, 152px) + 24px);\n overflow: auto;\n scrollbar-gutter: stable;\n}\n\n.dsh-fe-scroll::-webkit-scrollbar-track {\n margin: 2px;\n}\n\n.dsh-fe-level {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.dsh-fe-level .dsh-fe-level {\n padding-left: 16px;\n}\n\n.dsh-fe-row {\n display: flex;\n gap: 6px;\n align-items: center;\n width: 100%;\n min-width: 0;\n padding: 5px 10px;\n color: inherit;\n font: inherit;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.dsh-fe-row:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-row[aria-current='true'] {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dsh-fe-row-static {\n cursor: default;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-row-static:hover {\n background: transparent;\n}\n\n.dsh-fe-icon {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-name {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.dsh-fe-note {\n margin: 0;\n padding: 3px 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dsh-fe-note-error {\n color: var(--dsw-alias-label-error);\n}\n\n.dsh-fe-status {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n justify-content: center;\n padding: 24px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n}\n\n/* \u2500\u2500 format-aware bodies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Every body the pane can draw comes from the shared primitives, so this sheet\n carries no source-line rail of its own: the code block owns the numbered\n gutter, its counter, and its copy control. */\n\n/* The shared CodeBlock draws the highlighted source; this host only swaps the\n chat card's chrome for the pane's: no margin, no radius, no grey fill, and\n the pane's own scroll container. */\n.dsh-fe-source {\n --dsl-code-block-background: transparent;\n --dsl-code-block-border-radius: 0;\n --dsl-code-block-line-white-space: pre;\n}\n\n.dsh-fe-source .md-code-block {\n margin: 0;\n}\n\n/* One copy control per pane, in the header row above: the code block's own\n banner would be a second, inside the scrollport, and it scrolls away. The\n grammar it named is shown as the header's badge instead. Markdown fences keep\n their banners \u2014 there, a banner belongs to the fence, not to the file.\n\n The banner sits inside a sticky wrapper, hence the descendant match plus the\n :has rule that takes the wrapper out with it. */\n.dsh-fe-source [data-code-block-banner],\n.dsh-fe-source .md-code-block > div:has(> [data-code-block-banner]) {\n display: none;\n}\n\n.dsh-fe-source .md-code-block pre {\n box-sizing: border-box;\n min-width: 100%;\n padding: 8px 0;\n overflow: visible;\n white-space: pre;\n word-break: normal;\n overflow-wrap: normal;\n}\n\n.dsh-fe-source[data-wrap='true'] {\n --dsl-code-block-line-white-space: pre-wrap;\n}\n\n.dsh-fe-source[data-wrap='true'] .md-code-block pre {\n white-space: pre-wrap;\n /* The primitive's default is break-all; keep words whole instead. */\n word-break: normal;\n overflow-wrap: break-word;\n}\n\n/* The preview pane's header row carries the open tabs and, at its end, the\n active tab's controls. The tabs take the room that is left and scroll; the\n controls never shrink away. */\n.dsh-fe-tabhead {\n gap: 8px;\n padding: 0 6px 0 8px;\n}\n\n.dsh-fe-tabs {\n display: flex;\n flex: 1 1 auto;\n gap: 2px;\n align-items: stretch;\n min-width: 90px;\n height: 100%;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n}\n\n.dsh-fe-tabs::-webkit-scrollbar {\n height: 0;\n}\n\n.dsh-fe-tab {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n min-width: 0;\n max-width: 220px;\n border-radius: 8px;\n}\n\n.dsh-fe-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-tab[data-active] {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dsh-fe-tab-label {\n display: flex;\n gap: 6px;\n align-items: center;\n min-width: 0;\n padding: 5px 4px 5px 8px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.dsh-fe-tab[data-active] .dsh-fe-tab-label {\n color: var(--dsw-alias-label-primary);\n}\n\n.dsh-fe-tab-name {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n/* The close control appears for the tab under the pointer and for the active\n tab, so a full strip stays readable. */\n.dsh-fe-tab-close {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-right: 5px;\n padding: 0;\n color: var(--dsw-alias-label-tertiary);\n font: inherit;\n font-size: 14px;\n line-height: 1;\n background: transparent;\n border: 0;\n border-radius: 5px;\n cursor: pointer;\n opacity: 0;\n}\n\n.dsh-fe-tab:hover .dsh-fe-tab-close,\n.dsh-fe-tab[data-active] .dsh-fe-tab-close,\n.dsh-fe-tab-close:focus-visible {\n opacity: 1;\n}\n\n.dsh-fe-tab-close:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* A file already open in a tab keeps a quiet marker in the tree, so the reader\n can see where the strip came from without it competing with the active row. */\n.dsh-fe-open-dot {\n flex: 0 0 auto;\n width: 5px;\n height: 5px;\n margin-left: auto;\n background: var(--dsw-alias-brand-primary);\n border-radius: 50%;\n opacity: 0.6;\n}\n\n/* An HTML file is drawn by the browser, in a frame that fills the pane. The\n white canvas is the page's own, not this pane's: an unstyled document still\n reads as a document rather than as a hole in the app.\n\n The host takes its height from the scrollport's content box, so the frame ends\n exactly where the pane's bottom inset begins and the scrollport itself never\n scrolls \u2014 a page scrolls inside its own frame. */\n.dsh-fe-html {\n display: flex;\n height: 100%;\n min-height: 0;\n min-width: 0;\n}\n\n.dsh-fe-html-frame {\n flex: 1 1 auto;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n border: 0;\n background: #fff;\n}\n\n/* Rendered Markdown: prose lays out in normal white space, with the pane's\n insets and no monospace inheritance from the source view. */\n.dsh-fe-prose {\n min-width: 0;\n padding: 4px 4px 0;\n font-family: var(--dsw-font-family);\n white-space: normal;\n}\n\n.dsh-fe-json {\n min-width: 0;\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 12px;\n white-space: normal;\n}\n\n/* An image keeps its intrinsic size, centres while it fits, and scales down to\n the pane rather than overflowing it. */\n.dsh-fe-imagehost {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n}\n\n.dsh-fe-image {\n display: block;\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n`\n\n/**\n * Install the plugin's stylesheet once, and hand back its disposer.\n * @returns a disposer removing the tag this call created, or a no-op when the sheet was already installed.\n */\nexport function installStyles(): () => void {\n if (typeof document === 'undefined') return () => {}\n if (document.querySelector(`style[${STYLE_ATTRIBUTE}]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.setAttribute(STYLE_ATTRIBUTE, '')\n tag.textContent = CSS\n document.head.appendChild(tag)\n return () => { tag.remove() }\n}\n"],
|
|
5
|
+
"mappings": "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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsbL,SAAS,gBAA4B;AAC1C,MAAI,OAAO,aAAa,YAAa,QAAO,MAAM;AAAA,EAAC;AACnD,MAAI,SAAS,cAAc,SAAS,eAAe,GAAG,MAAM,KAAM,QAAO,MAAM;AAAA,EAAC;AAChF,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,aAAa,iBAAiB,EAAE;AACpC,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC7B,SAAO,MAAM;AAAE,QAAI,OAAO;AAAA,EAAE;AAC9B;;;AN7ZO,IAAM,OAAO;AAMb,IAAM,SAAS,CAAC,SAAS,UAAU,UAAU,uBAAuB;AAMpE,IAAM,gBAAgB;AAMtB,SAAS,MAAM,KAAoB;AACxC,MAAI,OAAO,eAAe,wCAAwC;AAClE,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,0CAA0C;AAIhG,QAAM,IAAI,IAAI,OAAO,KAAK,EAAE;AAC5B,QAAM,QAAQ,iBAAiB;AAC/B,QAAM,WAAW,IAAI,gBAAgB;AACrC,MAAI,OAAO,MAAM,MAAM;AAAE,aAAS,MAAM;AAAA,EAAE,GAAG,8CAA8C;AAE3F,MAAI,MAAM,OAAO,qBAAqB,MAAM,IAAI,MAAM,SAAS;AAAA,IAC7D,MAAM;AAAA,IACN,IAAI;AAAA;AAAA;AAAA,IAGJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO,MAAM,EAAE,YAAY;AAAA,IAC3B;AAAA,IACA,QAAQ,CACN,WACA,YACkB,UAAU,IAAI,QAAQ,WAAW,SAAS,QAAQ,OAAO;AAAA,EAC/E,GAAG,SAAS,CAAC;AACf;",
|
|
6
6
|
"names": ["name", "name"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaxzhou/dsh-file-explorer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "DeepSeek Harness plugin: a workspace file browser with a per-format preview (rendered Markdown, a JSON tree, 24 syntax grammars, images), shown as a Conversation View tab beside Chat and Trajectory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|