@lyhue1991/dsh-soup 0.4.3 → 0.4.5
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 +4 -20
- package/lib/client.js +129 -16
- package/lib/index.js +8 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,26 +97,10 @@ dsh plugin add @lyhue1991/dsh-soup
|
|
|
97
97
|
|
|
98
98
|
## 更新日志
|
|
99
99
|
|
|
100
|
-
- **0.4.
|
|
101
|
-
- **0.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- **0.3.0** —
|
|
105
|
-
- **Markdown 预览渲染相对路径图片**:`` 经同源 `/api/dsh-soup/img` 端点内联加载(confine 围栏 + 仅图片类型 + 拒跨站)。
|
|
106
|
-
- **自动刷新**:资源管理器与预览页每 3 秒轻量探测目录/文件签名(mtime),变了才重拉;面板关闭且无预览时零开销;宿主不可达时指数退避(封顶 60s)。
|
|
107
|
-
- **顶部 ⟳ 刷新按钮**:重拉整棵树并保持已展开子目录不收起;空白右键「刷新」项同步升级为同一枚图标。
|
|
108
|
-
- **工具栏按钮升级**:头部 ⟳/⬆/✕ 与预览「重载」对齐 JupyterLab 设计(26px 触达区 + SVG 图标,BSD-3 引用);修复关闭按钮渲染异常。
|
|
109
|
-
- **0.2.0** — 改名 `dsh-tree → dsh-soup`(npm 包 `@lyhue1991/dsh-soup`)。
|
|
110
|
-
- **0.1.18** — 下载进度行 + 完成通知自动消失。
|
|
111
|
-
- **0.1.17** — 大文件流式下载(一次性票据 URL + 流式发送),替代 base64-over-JSON,任意大小零内存放大。
|
|
112
|
-
- **0.1.16** — 下载菜单项 + 复制完整路径修复 + 目录展开态图标。
|
|
113
|
-
- **0.1.15** — 只读文件预览系统:会话区新增「预览」tab(md/html/json/csv·tsv 渲染 + 图片/PDF/Notebook/代码高亮 + 纯文本兜底);宿主新增 `read` 动作;路由安全加固。
|
|
114
|
-
- **0.1.12** — 吞吐徽标状态修复:流式即显 token 计数、滑动窗口瞬时 `t/s`、停顿检测回等待态。
|
|
115
|
-
- **0.1.11** — 空白新会话入口(输入框上方右端 📁)。
|
|
116
|
-
- **0.1.10** — GoalBar 改用原生 goal 工具 + 多行展示(textarea)。
|
|
117
|
-
- **0.1.7** — 速度徽标稳定化(贴附 Deep diving 状态行)。
|
|
118
|
-
- **0.1.5** — 跨平台:文件核心走 `node:fs`;新增模型吞吐徽标。
|
|
119
|
-
- **0.1.3** — 修复 details 插槽优先级与布局实例引用。
|
|
100
|
+
- **0.4.x** — Seti 图标字体(60 种字形覆盖 122 种扩展名)+ 空会话预览浮层(最大化/还原)+ 微信文章兼容 + 文本上限 8 MB + 关闭按钮尺寸修复 + 预览路径 rtl 修复 + i18n 语言适配 + 预览纯阅读模式 + 内部分区重排。
|
|
101
|
+
- **0.3.x** — Markdown 相对路径图片内联渲染 + 自动刷新(3s mtime 探测 + 指数退避)+ JupyterLab 风格工具栏图标。
|
|
102
|
+
- **0.2.0** — 改名 `dsh-tree` → `dsh-soup`。
|
|
103
|
+
- **0.1.x** — 只读文件预览系统(md/html/json/csv/pdf/notebook/图片/代码高亮)、大文件流式下载、多行 GoalBar、模型吞吐徽标、空白新会话入口、跨平台 node:fs 文件核心。
|
|
120
104
|
|
|
121
105
|
## 开发
|
|
122
106
|
|
package/lib/client.js
CHANGED
|
@@ -106,6 +106,8 @@ window.__ModuleLoader__.load({
|
|
|
106
106
|
// 同名 React 元素常量冲突(var 提升覆盖),字符样式反而更稳。
|
|
107
107
|
// ------------------------------------------------------------------
|
|
108
108
|
var ICON_REFRESH = '<svg xmlns="http://www.w3.org/2000/svg" width="16" viewBox="0 0 18 18"><path fill="currentColor" d="M9 13.5c-2.49 0-4.5-2.01-4.5-4.5S6.51 4.5 9 4.5c1.24 0 2.36.52 3.17 1.33L10 8h5V3l-1.76 1.76A6 6 0 0 0 9 3C5.69 3 3.01 5.69 3.01 9S5.69 15 9 15a5.98 5.98 0 0 0 5.9-5h-1.52c-.46 2-2.24 3.5-4.38 3.5"/></svg>'
|
|
109
|
+
var EXPAND_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>'
|
|
110
|
+
var SHRINK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/></svg>'
|
|
109
111
|
|
|
110
112
|
// GoalBar 多行版:沿用 DSH 原生 GoalBar 的结构和主题,仅放开单行截断。
|
|
111
113
|
// 布局分两行:head 行(图标 + 相位标签 + 操作按钮),body 行(objective 多行)。
|
|
@@ -135,7 +137,7 @@ window.__ModuleLoader__.load({
|
|
|
135
137
|
// 文件标签页视图(conversation.view 'dsh-soup-files'):子 tab 条 + 预览区。
|
|
136
138
|
// 纯预览(无编辑):markdown/html/json/csv 各有专属渲染器。
|
|
137
139
|
var FILEVIEW_CSS =
|
|
138
|
-
'.dfv-root{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);}' +
|
|
140
|
+
'.dfv-root{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);user-select:text;-webkit-user-select:text;}' +
|
|
139
141
|
'.dfv-tabbar{display:flex;align-items:center;gap:2px;flex:none;overflow-x:auto;padding:6px 10px 0;border-bottom:1px solid var(--dsw-alias-border-l1);scrollbar-width:none;}' +
|
|
140
142
|
'.dfv-tabbar::-webkit-scrollbar{display:none;}' +
|
|
141
143
|
'.dfv-tab{display:inline-flex;align-items:center;gap:5px;padding:5px 8px 5px 10px;border:none;border-radius:8px 8px 0 0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.4;cursor:pointer;white-space:nowrap;max-width:220px;}' +
|
|
@@ -155,11 +157,22 @@ window.__ModuleLoader__.load({
|
|
|
155
157
|
'.dfv-cap{padding:5px 14px;font-size:11px;color:var(--dsw-alias-label-caption);border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;}' +
|
|
156
158
|
'.dfv-code{flex:1;min-height:0;width:100%;box-sizing:border-box;margin:0;padding:10px 14px;overflow:auto;background:transparent;color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code);font-size:12.5px;line-height:1.55;white-space:pre;tab-size:2;user-select:text;}' +
|
|
157
159
|
'.dfv-code-wrap{flex:1;min-height:0;overflow:auto;padding:8px 10px;}' +
|
|
160
|
+
'.dfv-code-lines{flex:1;min-height:0;display:flex;overflow:auto;}' +
|
|
161
|
+
'.dfv-code-gutter{position:sticky;left:0;z-index:1;flex:none;padding:10px 8px 10px 12px;text-align:right;color:var(--dsw-alias-label-caption);font-family:var(--ds-font-family-code);font-size:12.5px;line-height:1.55;user-select:none;background:var(--dsw-alias-bg-layer-1);border-right:1px solid var(--dsw-alias-border-l1);}' +
|
|
162
|
+
'.dfv-code-main{flex:1;min-width:0;}' +
|
|
163
|
+
'.dfv-code-main .dfv-code{overflow:visible;width:auto;}' +
|
|
164
|
+
// CodeBlock(md-code-block)在行号布局内的适配:
|
|
165
|
+
// 隐藏 banner(js 标签 + 复制按钮),统一 padding/字体与 gutter 对齐。
|
|
166
|
+
'.dfv-code-main .md-code-block{margin:0;border-radius:0;background:transparent;}' +
|
|
167
|
+
'.dfv-code-main .md-code-block>div:first-child{display:none!important;}' +
|
|
168
|
+
'.dfv-code-main .md-code-block pre{padding:10px 14px!important;margin:0!important;font:12.5px/1.55 var(--ds-font-family-code)!important;background:transparent!important;border-radius:0!important;white-space:pre;word-break:normal;}' +
|
|
158
169
|
'.dfv-nb{flex:1;min-height:0;overflow:auto;background:var(--dsw-alias-bg-base);}' +
|
|
159
170
|
'.dfv-nb-cell{display:flex;border-bottom:1px solid var(--dsw-alias-border-l1);}' +
|
|
160
171
|
'.dfv-nb-gutter{flex:none;width:44px;padding:8px 6px;text-align:right;font-family:var(--ds-font-family-code);font-size:11px;color:var(--dsw-alias-label-caption);border-right:1px solid var(--dsw-alias-border-l1);user-select:none;background:var(--dsw-specific-sidebar-fill);}' +
|
|
161
172
|
'.dfv-nb-cellbody{flex:1;min-width:0;padding:4px 0;}' +
|
|
162
173
|
'.dfv-nb-cellbody .md-code-block,.dfv-nb-cellbody .dfv-code{margin:4px 10px;border-radius:8px;overflow:hidden;}' +
|
|
174
|
+
'.dfv-nb-cellbody .dfv-code-lines{border-radius:8px;overflow:hidden;}' +
|
|
175
|
+
'.dfv-nb-cellbody .dfv-code-gutter{padding:8px 6px 8px 10px;font-size:12px;}' +
|
|
163
176
|
'.dfv-nb-cell.dfv-nb-markdown .dfv-md-wrap{padding:2px 14px;}' +
|
|
164
177
|
'.dfv-nb-raw{color:var(--dsw-alias-label-secondary);}' +
|
|
165
178
|
'.dfv-nb-outputs{border-top:1px dashed var(--dsw-alias-border-l1);padding:6px 10px;display:flex;flex-direction:column;gap:8px;}' +
|
|
@@ -181,6 +194,12 @@ window.__ModuleLoader__.load({
|
|
|
181
194
|
'.dfv-table{border-collapse:collapse;font-size:12px;font-family:var(--ds-font-family-code);}' +
|
|
182
195
|
'.dfv-th{position:sticky;top:0;z-index:1;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-weight:600;text-align:left;padding:5px 10px;border-bottom:1px solid var(--dsw-alias-border-l2);border-right:1px solid var(--dsw-alias-border-l1);white-space:nowrap;}' +
|
|
183
196
|
'.dfv-td{padding:4px 10px;border-bottom:1px solid var(--dsw-alias-border-l1);border-right:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;max-width:360px;}' +
|
|
197
|
+
// 兜底浮层:空会话时原生 tab 条不存在,预览以模态浮层展示
|
|
198
|
+
'.dfv-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.24));backdrop-filter:var(--dsw-mask-blur,blur(2px));}' +
|
|
199
|
+
'.dfv-overlay-panel{display:flex;flex-direction:column;width:800px;height:min(800px,calc(100vh - 48px));max-width:calc(100vw - 48px);border-radius:24px;overflow:hidden;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3);user-select:text;-webkit-user-select:text;}' +
|
|
200
|
+
'.dfv-overlay-panel.dfv-overlay-max{width:100vw;height:100vh;max-width:none;border-radius:0;border:none;}' +
|
|
201
|
+
'.dfv-overlay-close{flex:none;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:1;cursor:pointer;padding:0;}' +
|
|
202
|
+
'.dfv-overlay-close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}' +
|
|
184
203
|
// 纯阅读模式:预览 tab 激活时隐藏整个输入区(composerSeat:输入框 +
|
|
185
204
|
// GoalBar + 速度徽标所在的 input.dock)与轮次统计行(StatsLine)。
|
|
186
205
|
// class 来自 DSH 客户端编译产物(CSS module hash),升级后若失效仅
|
|
@@ -257,7 +276,7 @@ window.__ModuleLoader__.load({
|
|
|
257
276
|
'files.binary': '二进制文件({size}),请在资源管理器中用系统打开',
|
|
258
277
|
'files.imageTooLarge': '图片过大({size},上限 {limit})',
|
|
259
278
|
'files.pdfTooLarge': 'PDF 过大({size},上限 {limit}),请用系统打开',
|
|
260
|
-
'files.truncated': '文件超过
|
|
279
|
+
'files.truncated': '文件超过 8 MB,仅预览前 8 MB;完整内容请用系统编辑器打开。',
|
|
261
280
|
'files.jsonFail': 'JSON 解析失败,按纯文本显示',
|
|
262
281
|
'files.nbFail': 'ipynb 解析失败,按纯文本显示',
|
|
263
282
|
'files.nbEmpty': '(空笔记本)',
|
|
@@ -320,7 +339,7 @@ window.__ModuleLoader__.load({
|
|
|
320
339
|
'files.binary': 'Binary file ({size}) — open with the system explorer',
|
|
321
340
|
'files.imageTooLarge': 'Image too large ({size}, limit {limit})',
|
|
322
341
|
'files.pdfTooLarge': 'PDF too large ({size}, limit {limit}) — open with the system viewer',
|
|
323
|
-
'files.truncated': 'File exceeds
|
|
342
|
+
'files.truncated': 'File exceeds 8 MB — showing the first 8 MB; open in a system editor for the full content.',
|
|
324
343
|
'files.jsonFail': 'JSON parse failed — shown as plain text',
|
|
325
344
|
'files.nbFail': 'ipynb parse failed — shown as plain text',
|
|
326
345
|
'files.nbEmpty': '(empty notebook)',
|
|
@@ -348,7 +367,7 @@ window.__ModuleLoader__.load({
|
|
|
348
367
|
selected: new Set(), lastIndex: null,
|
|
349
368
|
renaming: null, newItem: null,
|
|
350
369
|
dropTarget: null, dragPaths: null,
|
|
351
|
-
files: { list: [], active: null },
|
|
370
|
+
files: { list: [], active: null, overlay: false, overlayMax: false, overlayReturn: null },
|
|
352
371
|
uploads: [],
|
|
353
372
|
}
|
|
354
373
|
var listeners = new Set()
|
|
@@ -1627,12 +1646,13 @@ window.__ModuleLoader__.load({
|
|
|
1627
1646
|
setFiles({ list: list, active: path })
|
|
1628
1647
|
} else if (entry.loaded) {
|
|
1629
1648
|
setFiles({ active: path })
|
|
1630
|
-
activateFilesView()
|
|
1649
|
+
if (activateFilesView()) return
|
|
1650
|
+
setFiles({ overlay: true, overlayMax: false, overlayReturn: null })
|
|
1631
1651
|
return
|
|
1632
1652
|
} else {
|
|
1633
1653
|
setFiles({ active: path })
|
|
1634
1654
|
}
|
|
1635
|
-
activateFilesView()
|
|
1655
|
+
if (!activateFilesView()) setFiles({ overlay: true, overlayMax: false, overlayReturn: null })
|
|
1636
1656
|
var res = await rpc('read', { path: path, sessionId: activeSessionId })
|
|
1637
1657
|
if (!res.ok && /超出允许范围/.test(res.error || '')) {
|
|
1638
1658
|
await new Promise(function (r) { setTimeout(r, 600) })
|
|
@@ -1831,12 +1851,25 @@ window.__ModuleLoader__.load({
|
|
|
1831
1851
|
// 可交互预览:给 allow-scripts/popups/forms/modals(对齐 JupyterLab
|
|
1832
1852
|
// trusted 档),但坚持不给 allow-same-origin——脚本跑在 opaque origin,
|
|
1833
1853
|
// 触不到 GUI 的 DOM/存储/Cookie;srcdoc 注入 + no-referrer 收敛外链。
|
|
1854
|
+
// 微信文章兼容:正文 #js_content 初始 visibility:hidden 靠 JS 展开,
|
|
1855
|
+
// 图片用 data-src 懒加载靠 JS 换 src——沙箱里两者都跑不起来。
|
|
1856
|
+
// 注入一段 CSS 强制显示正文,并预先把 data-src 落到 src。
|
|
1857
|
+
var text = props.text || ''
|
|
1858
|
+
if (text.indexOf('js_content') !== -1 || text.indexOf('data-src=') !== -1) {
|
|
1859
|
+
var inject = '<style>#js_content{visibility:visible!important;opacity:1!important;}</style>'
|
|
1860
|
+
if (/<\/head>/i.test(text)) {
|
|
1861
|
+
text = text.replace(/<\/head>/i, inject + '</head>')
|
|
1862
|
+
} else {
|
|
1863
|
+
text = inject + text
|
|
1864
|
+
}
|
|
1865
|
+
text = text.replace(/(<img\b[^>]*?)\sdata-src=/gi, '$1 src=')
|
|
1866
|
+
}
|
|
1834
1867
|
return create('iframe', {
|
|
1835
1868
|
className: 'dfv-frame',
|
|
1836
1869
|
sandbox: 'allow-scripts allow-popups allow-forms allow-modals',
|
|
1837
1870
|
referrerPolicy: 'no-referrer',
|
|
1838
1871
|
title: props.title || T('files.htmlFrame'),
|
|
1839
|
-
srcDoc:
|
|
1872
|
+
srcDoc: text,
|
|
1840
1873
|
})
|
|
1841
1874
|
}
|
|
1842
1875
|
|
|
@@ -1966,9 +1999,10 @@ window.__ModuleLoader__.load({
|
|
|
1966
1999
|
})
|
|
1967
2000
|
var codeSrc = nbSource(cell.source)
|
|
1968
2001
|
body = create('div', { className: 'dfv-nb-cellbody' },
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2002
|
+
create(CodeWithLines, { text: codeSrc },
|
|
2003
|
+
CodeBlock
|
|
2004
|
+
? create(CodeBlock, { code: codeSrc, lang: codeLang })
|
|
2005
|
+
: create('pre', { className: 'dfv-code' }, codeSrc)),
|
|
1972
2006
|
outputs.length ? create('div', { className: 'dfv-nb-outputs' }, outputs) : null)
|
|
1973
2007
|
} else if (ct === 'markdown') {
|
|
1974
2008
|
body = MarkdownText
|
|
@@ -2046,6 +2080,18 @@ window.__ModuleLoader__.load({
|
|
|
2046
2080
|
return (n / 1073741824).toFixed(1) + ' GB'
|
|
2047
2081
|
}
|
|
2048
2082
|
|
|
2083
|
+
/** 代码内容 + 左侧行号列(源码文件 / notebook code cell 用)。 */
|
|
2084
|
+
function CodeWithLines(props) {
|
|
2085
|
+
var text = props.text || ''
|
|
2086
|
+
var lineCount = text.split('\n').length
|
|
2087
|
+
var nums = []
|
|
2088
|
+
for (var i = 1; i <= lineCount; i++) nums.push(create('div', { key: i }, String(i)))
|
|
2089
|
+
return create('div', { className: 'dfv-code-lines' },
|
|
2090
|
+
create('div', { className: 'dfv-code-gutter', 'aria-hidden': true }, nums),
|
|
2091
|
+
create('div', { className: 'dfv-code-main' }, props.children),
|
|
2092
|
+
)
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2049
2095
|
function FileContent(props) {
|
|
2050
2096
|
var entry = props.entry
|
|
2051
2097
|
if (entry.loading) {
|
|
@@ -2083,14 +2129,14 @@ window.__ModuleLoader__.load({
|
|
|
2083
2129
|
else if (pk === 'csv' || pk === 'tsv') view = create(DelimitedPreview, { text: entry.content, delim: pk === 'tsv' ? '\t' : ',' })
|
|
2084
2130
|
else {
|
|
2085
2131
|
// 代码文件:DSH CodeBlock(Shiki 高亮,语法包懒加载);无高亮条件回退纯文本
|
|
2132
|
+
// 都包在 CodeWithLines 里加左侧行号
|
|
2086
2133
|
var lang = Object.prototype.hasOwnProperty.call(CODE_LANG_BY_EXT, extOfName(entry.path))
|
|
2087
2134
|
? CODE_LANG_BY_EXT[extOfName(entry.path)]
|
|
2088
2135
|
: undefined
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
}
|
|
2136
|
+
var codeEl = (CodeBlock && lang && entry.content.length <= CODE_HIGHLIGHT_MAX_CHARS)
|
|
2137
|
+
? create(CodeBlock, { code: entry.content, lang: lang })
|
|
2138
|
+
: create('pre', { className: 'dfv-code' }, entry.content)
|
|
2139
|
+
view = create(CodeWithLines, { text: entry.content }, codeEl)
|
|
2094
2140
|
}
|
|
2095
2141
|
return create(React.Fragment, null,
|
|
2096
2142
|
entry.truncated ? create('div', { className: 'dfv-cap' },
|
|
@@ -2178,6 +2224,13 @@ window.__ModuleLoader__.load({
|
|
|
2178
2224
|
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2179
2225
|
T('files.reload'),
|
|
2180
2226
|
) : null,
|
|
2227
|
+
create('button', {
|
|
2228
|
+
type: 'button',
|
|
2229
|
+
className: 'dfv-btn',
|
|
2230
|
+
title: '最大化', 'aria-label': '最大化',
|
|
2231
|
+
onClick: function () { setFiles({ overlay: true, overlayMax: true, overlayReturn: 'tab' }) },
|
|
2232
|
+
dangerouslySetInnerHTML: { __html: EXPAND_SVG },
|
|
2233
|
+
}),
|
|
2181
2234
|
),
|
|
2182
2235
|
create(FileContent, { key: active.path, entry: active }),
|
|
2183
2236
|
)
|
|
@@ -2189,6 +2242,61 @@ window.__ModuleLoader__.load({
|
|
|
2189
2242
|
)
|
|
2190
2243
|
}
|
|
2191
2244
|
|
|
2245
|
+
/** 空会话兜底浮层:原生 tab 条不存在时以模态渲染 FileContent。 */
|
|
2246
|
+
function PreviewOverlay() {
|
|
2247
|
+
var s = useStore()
|
|
2248
|
+
var files = s.files
|
|
2249
|
+
if (!files.overlay || !files.active) return null
|
|
2250
|
+
var active = findFileEntry(files.active)
|
|
2251
|
+
if (!active) return null
|
|
2252
|
+
var close = function () { setFiles({ overlay: false, overlayMax: false, overlayReturn: null }) }
|
|
2253
|
+
var closeAndCloseTab = function () {
|
|
2254
|
+
close()
|
|
2255
|
+
closeFileTab(active.path)
|
|
2256
|
+
}
|
|
2257
|
+
var toggleMax = function () {
|
|
2258
|
+
if (files.overlayMax && files.overlayReturn === 'tab') {
|
|
2259
|
+
// 还原 → 回到 tab 模式
|
|
2260
|
+
close()
|
|
2261
|
+
} else {
|
|
2262
|
+
setFiles({ overlayMax: !files.overlayMax })
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return create('div', {
|
|
2266
|
+
className: 'dfv-overlay',
|
|
2267
|
+
onClick: function (e) { if (e.target === e.currentTarget) close() },
|
|
2268
|
+
},
|
|
2269
|
+
create('div', { className: 'dfv-overlay-panel' + (files.overlayMax ? ' dfv-overlay-max' : '') },
|
|
2270
|
+
create('div', { className: 'dfv-toolbar' },
|
|
2271
|
+
create('span', { className: 'dfv-toolbar-path', title: active.path }, active.path),
|
|
2272
|
+
typeof active.size === 'number' && active.size > 0 ? create('span', null, formatBytes(active.size)) : null,
|
|
2273
|
+
active.loaded ? create('button', {
|
|
2274
|
+
type: 'button',
|
|
2275
|
+
className: 'dfv-btn',
|
|
2276
|
+
title: T('files.reloadTitle'),
|
|
2277
|
+
onClick: function () { reloadFile(active.path) },
|
|
2278
|
+
},
|
|
2279
|
+
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2280
|
+
T('files.reload'),
|
|
2281
|
+
) : null,
|
|
2282
|
+
create('button', {
|
|
2283
|
+
type: 'button', className: 'dfv-overlay-close',
|
|
2284
|
+
title: files.overlayMax ? '还原' : '最大化', 'aria-label': files.overlayMax ? '还原' : '最大化',
|
|
2285
|
+
onClick: toggleMax,
|
|
2286
|
+
dangerouslySetInnerHTML: { __html: files.overlayMax ? SHRINK_SVG : EXPAND_SVG },
|
|
2287
|
+
}),
|
|
2288
|
+
create('button', {
|
|
2289
|
+
type: 'button', className: 'dfv-overlay-close',
|
|
2290
|
+
title: files.overlayReturn === 'tab' ? '关闭预览 tab' : '关闭',
|
|
2291
|
+
'aria-label': files.overlayReturn === 'tab' ? '关闭预览 tab' : '关闭',
|
|
2292
|
+
onClick: closeAndCloseTab,
|
|
2293
|
+
}, '✕'),
|
|
2294
|
+
),
|
|
2295
|
+
create(FileContent, { key: active.path, entry: active }),
|
|
2296
|
+
),
|
|
2297
|
+
)
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2192
2300
|
function findFrameEl(startEl) {
|
|
2193
2301
|
var cur = startEl && startEl.parentElement
|
|
2194
2302
|
while (cur) {
|
|
@@ -2605,7 +2713,12 @@ window.__ModuleLoader__.load({
|
|
|
2605
2713
|
// `details` is a single slot occupied by the shell at priority 0.
|
|
2606
2714
|
// Lower priorities render first, so this intentionally shadows it.
|
|
2607
2715
|
{ name: 'details', priority: -1, locale: NS },
|
|
2608
|
-
function (props) {
|
|
2716
|
+
function (props) {
|
|
2717
|
+
return create(React.Fragment, null,
|
|
2718
|
+
create(Panel, props),
|
|
2719
|
+
create(PreviewOverlay),
|
|
2720
|
+
)
|
|
2721
|
+
},
|
|
2609
2722
|
))
|
|
2610
2723
|
})
|
|
2611
2724
|
// 文件标签页:与 对话/轨迹 同级的原生 view tab(ui-trajectory 同款注册方式)。
|
package/lib/index.js
CHANGED
|
@@ -392,8 +392,8 @@ export function apply(ctx) {
|
|
|
392
392
|
// ------------------------------------------------------------------
|
|
393
393
|
// 文件只读预览:read(文本/图片/二进制三态);无 write——预览-only 不提供保存
|
|
394
394
|
// ------------------------------------------------------------------
|
|
395
|
-
/** 文本读取上限(超过则截断标记 truncated)。 */
|
|
396
|
-
const TEXT_MAX_BYTES =
|
|
395
|
+
/** 文本读取上限(超过则截断标记 truncated)。8 MB——HTML 常含内联 base64 图片,2 MB 太紧。 */
|
|
396
|
+
const TEXT_MAX_BYTES = 8 * 1024 * 1024
|
|
397
397
|
/** 图片内联上限。 */
|
|
398
398
|
const IMAGE_MAX_BYTES = 8 * 1024 * 1024
|
|
399
399
|
/** PDF 内联上限。 */
|
|
@@ -627,6 +627,12 @@ export function apply(ctx) {
|
|
|
627
627
|
if (typeof entry.size === 'number' && entry.size >= 0) item.size = entry.size
|
|
628
628
|
return item
|
|
629
629
|
})
|
|
630
|
+
// 目录在前、文件在后,各按名称排(与 VS Code / Finder 一致)。
|
|
631
|
+
out.sort((a, b) => {
|
|
632
|
+
const dirDiff = (a.type === 'directory' ? 0 : 1) - (b.type === 'directory' ? 0 : 1)
|
|
633
|
+
if (dirDiff !== 0) return dirDiff
|
|
634
|
+
return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
|
|
635
|
+
})
|
|
630
636
|
return { ok: true, root, path: basePath, entries: out }
|
|
631
637
|
}
|
|
632
638
|
case 'open': {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyhue1991/dsh-soup",
|
|
3
3
|
"description": "DSH 项目资源管理器:右侧 details 列文件树(并列网格、宽度与左侧工作区一致 264-420px),有 Session log 时在其右侧、空白新会话在输入框上方右端提供 📁 切换按钮;支持多选、双击展开/打开、右键菜单(打开/系统打开/重命名/废纸篓/新建/复制路径)、拖拽移动、上传。文件「打开」在会话区新增「文件」标签页(原生 conversation.view,与对话/轨迹同级):文本可编辑 ⌘S 保存、图片预览、二进制提示。附带模型吞吐徽标:输入框正上方显示「正在等待模型...」与彩色 t/s 徽标(数据来自 llm/stream 真实流)。附带 GoalBar 多行展示:把原生单行截断 GoalBar 放开为多行完整显示,编辑用 textarea,工具与数据仍走原生 goal。",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|