@lyhue1991/dsh-soup 0.4.3 → 0.4.4
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 +96 -7
- package/lib/index.js +2 -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 多行)。
|
|
@@ -181,6 +183,12 @@ window.__ModuleLoader__.load({
|
|
|
181
183
|
'.dfv-table{border-collapse:collapse;font-size:12px;font-family:var(--ds-font-family-code);}' +
|
|
182
184
|
'.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
185
|
'.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;}' +
|
|
186
|
+
// 兜底浮层:空会话时原生 tab 条不存在,预览以模态浮层展示
|
|
187
|
+
'.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));}' +
|
|
188
|
+
'.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);}' +
|
|
189
|
+
'.dfv-overlay-panel.dfv-overlay-max{width:100vw;height:100vh;max-width:none;border-radius:0;border:none;}' +
|
|
190
|
+
'.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;}' +
|
|
191
|
+
'.dfv-overlay-close:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}' +
|
|
184
192
|
// 纯阅读模式:预览 tab 激活时隐藏整个输入区(composerSeat:输入框 +
|
|
185
193
|
// GoalBar + 速度徽标所在的 input.dock)与轮次统计行(StatsLine)。
|
|
186
194
|
// class 来自 DSH 客户端编译产物(CSS module hash),升级后若失效仅
|
|
@@ -257,7 +265,7 @@ window.__ModuleLoader__.load({
|
|
|
257
265
|
'files.binary': '二进制文件({size}),请在资源管理器中用系统打开',
|
|
258
266
|
'files.imageTooLarge': '图片过大({size},上限 {limit})',
|
|
259
267
|
'files.pdfTooLarge': 'PDF 过大({size},上限 {limit}),请用系统打开',
|
|
260
|
-
'files.truncated': '文件超过
|
|
268
|
+
'files.truncated': '文件超过 8 MB,仅预览前 8 MB;完整内容请用系统编辑器打开。',
|
|
261
269
|
'files.jsonFail': 'JSON 解析失败,按纯文本显示',
|
|
262
270
|
'files.nbFail': 'ipynb 解析失败,按纯文本显示',
|
|
263
271
|
'files.nbEmpty': '(空笔记本)',
|
|
@@ -320,7 +328,7 @@ window.__ModuleLoader__.load({
|
|
|
320
328
|
'files.binary': 'Binary file ({size}) — open with the system explorer',
|
|
321
329
|
'files.imageTooLarge': 'Image too large ({size}, limit {limit})',
|
|
322
330
|
'files.pdfTooLarge': 'PDF too large ({size}, limit {limit}) — open with the system viewer',
|
|
323
|
-
'files.truncated': 'File exceeds
|
|
331
|
+
'files.truncated': 'File exceeds 8 MB — showing the first 8 MB; open in a system editor for the full content.',
|
|
324
332
|
'files.jsonFail': 'JSON parse failed — shown as plain text',
|
|
325
333
|
'files.nbFail': 'ipynb parse failed — shown as plain text',
|
|
326
334
|
'files.nbEmpty': '(empty notebook)',
|
|
@@ -348,7 +356,7 @@ window.__ModuleLoader__.load({
|
|
|
348
356
|
selected: new Set(), lastIndex: null,
|
|
349
357
|
renaming: null, newItem: null,
|
|
350
358
|
dropTarget: null, dragPaths: null,
|
|
351
|
-
files: { list: [], active: null },
|
|
359
|
+
files: { list: [], active: null, overlay: false, overlayMax: false, overlayReturn: null },
|
|
352
360
|
uploads: [],
|
|
353
361
|
}
|
|
354
362
|
var listeners = new Set()
|
|
@@ -1627,12 +1635,13 @@ window.__ModuleLoader__.load({
|
|
|
1627
1635
|
setFiles({ list: list, active: path })
|
|
1628
1636
|
} else if (entry.loaded) {
|
|
1629
1637
|
setFiles({ active: path })
|
|
1630
|
-
activateFilesView()
|
|
1638
|
+
if (activateFilesView()) return
|
|
1639
|
+
setFiles({ overlay: true, overlayMax: false, overlayReturn: null })
|
|
1631
1640
|
return
|
|
1632
1641
|
} else {
|
|
1633
1642
|
setFiles({ active: path })
|
|
1634
1643
|
}
|
|
1635
|
-
activateFilesView()
|
|
1644
|
+
if (!activateFilesView()) setFiles({ overlay: true, overlayMax: false, overlayReturn: null })
|
|
1636
1645
|
var res = await rpc('read', { path: path, sessionId: activeSessionId })
|
|
1637
1646
|
if (!res.ok && /超出允许范围/.test(res.error || '')) {
|
|
1638
1647
|
await new Promise(function (r) { setTimeout(r, 600) })
|
|
@@ -1831,12 +1840,25 @@ window.__ModuleLoader__.load({
|
|
|
1831
1840
|
// 可交互预览:给 allow-scripts/popups/forms/modals(对齐 JupyterLab
|
|
1832
1841
|
// trusted 档),但坚持不给 allow-same-origin——脚本跑在 opaque origin,
|
|
1833
1842
|
// 触不到 GUI 的 DOM/存储/Cookie;srcdoc 注入 + no-referrer 收敛外链。
|
|
1843
|
+
// 微信文章兼容:正文 #js_content 初始 visibility:hidden 靠 JS 展开,
|
|
1844
|
+
// 图片用 data-src 懒加载靠 JS 换 src——沙箱里两者都跑不起来。
|
|
1845
|
+
// 注入一段 CSS 强制显示正文,并预先把 data-src 落到 src。
|
|
1846
|
+
var text = props.text || ''
|
|
1847
|
+
if (text.indexOf('js_content') !== -1 || text.indexOf('data-src=') !== -1) {
|
|
1848
|
+
var inject = '<style>#js_content{visibility:visible!important;opacity:1!important;}</style>'
|
|
1849
|
+
if (/<\/head>/i.test(text)) {
|
|
1850
|
+
text = text.replace(/<\/head>/i, inject + '</head>')
|
|
1851
|
+
} else {
|
|
1852
|
+
text = inject + text
|
|
1853
|
+
}
|
|
1854
|
+
text = text.replace(/(<img\b[^>]*?)\sdata-src=/gi, '$1 src=')
|
|
1855
|
+
}
|
|
1834
1856
|
return create('iframe', {
|
|
1835
1857
|
className: 'dfv-frame',
|
|
1836
1858
|
sandbox: 'allow-scripts allow-popups allow-forms allow-modals',
|
|
1837
1859
|
referrerPolicy: 'no-referrer',
|
|
1838
1860
|
title: props.title || T('files.htmlFrame'),
|
|
1839
|
-
srcDoc:
|
|
1861
|
+
srcDoc: text,
|
|
1840
1862
|
})
|
|
1841
1863
|
}
|
|
1842
1864
|
|
|
@@ -2178,6 +2200,13 @@ window.__ModuleLoader__.load({
|
|
|
2178
2200
|
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2179
2201
|
T('files.reload'),
|
|
2180
2202
|
) : null,
|
|
2203
|
+
create('button', {
|
|
2204
|
+
type: 'button',
|
|
2205
|
+
className: 'dfv-btn',
|
|
2206
|
+
title: '最大化', 'aria-label': '最大化',
|
|
2207
|
+
onClick: function () { setFiles({ overlay: true, overlayMax: true, overlayReturn: 'tab' }) },
|
|
2208
|
+
dangerouslySetInnerHTML: { __html: EXPAND_SVG },
|
|
2209
|
+
}),
|
|
2181
2210
|
),
|
|
2182
2211
|
create(FileContent, { key: active.path, entry: active }),
|
|
2183
2212
|
)
|
|
@@ -2189,6 +2218,61 @@ window.__ModuleLoader__.load({
|
|
|
2189
2218
|
)
|
|
2190
2219
|
}
|
|
2191
2220
|
|
|
2221
|
+
/** 空会话兜底浮层:原生 tab 条不存在时以模态渲染 FileContent。 */
|
|
2222
|
+
function PreviewOverlay() {
|
|
2223
|
+
var s = useStore()
|
|
2224
|
+
var files = s.files
|
|
2225
|
+
if (!files.overlay || !files.active) return null
|
|
2226
|
+
var active = findFileEntry(files.active)
|
|
2227
|
+
if (!active) return null
|
|
2228
|
+
var close = function () { setFiles({ overlay: false, overlayMax: false, overlayReturn: null }) }
|
|
2229
|
+
var closeAndCloseTab = function () {
|
|
2230
|
+
close()
|
|
2231
|
+
closeFileTab(active.path)
|
|
2232
|
+
}
|
|
2233
|
+
var toggleMax = function () {
|
|
2234
|
+
if (files.overlayMax && files.overlayReturn === 'tab') {
|
|
2235
|
+
// 还原 → 回到 tab 模式
|
|
2236
|
+
close()
|
|
2237
|
+
} else {
|
|
2238
|
+
setFiles({ overlayMax: !files.overlayMax })
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return create('div', {
|
|
2242
|
+
className: 'dfv-overlay',
|
|
2243
|
+
onClick: function (e) { if (e.target === e.currentTarget) close() },
|
|
2244
|
+
},
|
|
2245
|
+
create('div', { className: 'dfv-overlay-panel' + (files.overlayMax ? ' dfv-overlay-max' : '') },
|
|
2246
|
+
create('div', { className: 'dfv-toolbar' },
|
|
2247
|
+
create('span', { className: 'dfv-toolbar-path', title: active.path }, active.path),
|
|
2248
|
+
typeof active.size === 'number' && active.size > 0 ? create('span', null, formatBytes(active.size)) : null,
|
|
2249
|
+
active.loaded ? create('button', {
|
|
2250
|
+
type: 'button',
|
|
2251
|
+
className: 'dfv-btn',
|
|
2252
|
+
title: T('files.reloadTitle'),
|
|
2253
|
+
onClick: function () { reloadFile(active.path) },
|
|
2254
|
+
},
|
|
2255
|
+
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2256
|
+
T('files.reload'),
|
|
2257
|
+
) : null,
|
|
2258
|
+
create('button', {
|
|
2259
|
+
type: 'button', className: 'dfv-overlay-close',
|
|
2260
|
+
title: files.overlayMax ? '还原' : '最大化', 'aria-label': files.overlayMax ? '还原' : '最大化',
|
|
2261
|
+
onClick: toggleMax,
|
|
2262
|
+
dangerouslySetInnerHTML: { __html: files.overlayMax ? SHRINK_SVG : EXPAND_SVG },
|
|
2263
|
+
}),
|
|
2264
|
+
create('button', {
|
|
2265
|
+
type: 'button', className: 'dfv-overlay-close',
|
|
2266
|
+
title: files.overlayReturn === 'tab' ? '关闭预览 tab' : '关闭',
|
|
2267
|
+
'aria-label': files.overlayReturn === 'tab' ? '关闭预览 tab' : '关闭',
|
|
2268
|
+
onClick: closeAndCloseTab,
|
|
2269
|
+
}, '✕'),
|
|
2270
|
+
),
|
|
2271
|
+
create(FileContent, { key: active.path, entry: active }),
|
|
2272
|
+
),
|
|
2273
|
+
)
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2192
2276
|
function findFrameEl(startEl) {
|
|
2193
2277
|
var cur = startEl && startEl.parentElement
|
|
2194
2278
|
while (cur) {
|
|
@@ -2605,7 +2689,12 @@ window.__ModuleLoader__.load({
|
|
|
2605
2689
|
// `details` is a single slot occupied by the shell at priority 0.
|
|
2606
2690
|
// Lower priorities render first, so this intentionally shadows it.
|
|
2607
2691
|
{ name: 'details', priority: -1, locale: NS },
|
|
2608
|
-
function (props) {
|
|
2692
|
+
function (props) {
|
|
2693
|
+
return create(React.Fragment, null,
|
|
2694
|
+
create(Panel, props),
|
|
2695
|
+
create(PreviewOverlay),
|
|
2696
|
+
)
|
|
2697
|
+
},
|
|
2609
2698
|
))
|
|
2610
2699
|
})
|
|
2611
2700
|
// 文件标签页:与 对话/轨迹 同级的原生 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 内联上限。 */
|
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.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|