@lyhue1991/dsh-soup 0.4.10 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/client.js +146 -36
- package/lib/index.js +12 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ DSH(DeepSeek Harness)功能很强:会话、工具、轨迹、Goal 一应
|
|
|
32
32
|
|
|
33
33
|
### 🧅 右侧文件树(主打 · 葱 · 提鲜点缀)
|
|
34
34
|
|
|
35
|
-
一个真正和左侧工作区并列的文件浏览器——不悬浮、不遮挡,配色与宽度都与左侧一致(默认
|
|
35
|
+
一个真正和左侧工作区并列的文件浏览器——不悬浮、不遮挡,配色与宽度都与左侧一致(默认 360px,拖左缘在 300–520px 间调整)。
|
|
36
36
|
|
|
37
37
|
- **跟随会话**:自动展示当前会话的工作目录,切会话/工作区时跟着走。
|
|
38
38
|
- **右键菜单**:预览、用系统打开、重命名、移到废纸篓、复制完整路径、下载到本机。
|
|
@@ -97,7 +97,7 @@ dsh plugin add @lyhue1991/dsh-soup
|
|
|
97
97
|
|
|
98
98
|
## 更新日志
|
|
99
99
|
|
|
100
|
-
- **0.4.x** — Seti 图标字体(60 种字形覆盖 122 种扩展名)+ 空会话预览浮层(最大化/还原)+ 微信文章兼容 + 文本上限 8 MB + 关闭按钮尺寸修复 + 预览路径 rtl 修复 + i18n 语言适配 + 预览纯阅读模式 +
|
|
100
|
+
- **0.4.x** — Seti 图标字体(60 种字形覆盖 122 种扩展名)+ 空会话预览浮层(最大化/还原)+ 微信文章兼容 + 文本上限 8 MB + 关闭按钮尺寸修复 + 预览路径 rtl 修复 + i18n 语言适配 + 预览纯阅读模式 + 内部分区重排 + 预览头部冻结 + details 宽度对齐宿主合同。
|
|
101
101
|
- **0.3.x** — Markdown 相对路径图片内联渲染 + 自动刷新(3s mtime 探测 + 指数退避)+ JupyterLab 风格工具栏图标。
|
|
102
102
|
- **0.2.0** — 改名 `dsh-tree` → `dsh-soup`。
|
|
103
103
|
- **0.1.x** — 只读文件预览系统(md/html/json/csv/pdf/notebook/图片/代码高亮)、大文件流式下载、多行 GoalBar、模型吞吐徽标、空白新会话入口、跨平台 node:fs 文件核心。
|
package/lib/client.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 布局:注册进 shell 原生的 `details` 右列(并列网格,非悬浮),
|
|
5
5
|
* 有 Session log 时在它右侧放 📁;空白新会话则在输入框上方、
|
|
6
6
|
* workspace/preset 控件行的右端提供同一入口。
|
|
7
|
-
*
|
|
7
|
+
* 宽度对齐宿主 details 合同:默认 360px、可拖 300–520px(直接改写 shell 网格
|
|
8
8
|
* 的 details 轨,并用 MutationObserver 在 shell 重渲染后维持宽度)。
|
|
9
9
|
*
|
|
10
10
|
* 与宿主通信:走同源 HTTP 路由 `/api/dsh-soup`(POST JSON),
|
|
@@ -45,10 +45,13 @@ window.__ModuleLoader__.load({
|
|
|
45
45
|
// ------------------------------------------------------------------
|
|
46
46
|
// 样式:一次性注入 <style data-plugin>(与官方插件 css 内联约定一致)
|
|
47
47
|
// ------------------------------------------------------------------
|
|
48
|
-
var EXPL_CSS = '.expl-panel{position:relative;height:100%;min-width:
|
|
48
|
+
var EXPL_CSS = '.expl-panel{position:relative;height:100%;min-width:300px;max-width:520px;width:100%;display:flex;flex-direction:column;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);font-size:13px;font-family:var(--dsw-font-family);overflow:hidden;box-sizing:border-box;}' +
|
|
49
49
|
'.expl-resize{position:absolute;left:0;top:0;bottom:0;width:7px;cursor:col-resize;z-index:6;touch-action:none;}' +
|
|
50
50
|
'.expl-resize::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:3px;height:40px;border-radius:2px;background:var(--dsw-alias-interactive-bg-hover);opacity:0;transition:opacity .15s;}' +
|
|
51
51
|
'.expl-resize:hover::after{opacity:1;}' +
|
|
52
|
+
/* The official 0.1.5 expand control duplicates dsh-soup's folder
|
|
53
|
+
button; hide it so the explorer has one unambiguous entry point. */
|
|
54
|
+
'[data-sidebar-right-expand],[data-sidebar-right-expand-placeholder]{display:none!important;}' +
|
|
52
55
|
'[data-side="details"]{display:none!important;}' +
|
|
53
56
|
'.expl-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 14px;border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;}' +
|
|
54
57
|
'.expl-title{font-weight:600;color:var(--dsw-alias-label-primary);white-space:nowrap;font-size:14px;}' +
|
|
@@ -90,6 +93,10 @@ window.__ModuleLoader__.load({
|
|
|
90
93
|
'.expl-size{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;margin-left:8px;}' +
|
|
91
94
|
'.expl-muted{color:var(--dsw-alias-label-tertiary);padding:4px 14px;font-size:12px;}' +
|
|
92
95
|
'.expl-menu-mask{position:fixed;inset:0;z-index:1980;}' +
|
|
96
|
+
'.expl-confirm-mask{position:fixed;inset:0;z-index:2100;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.28);}' +
|
|
97
|
+
'.expl-confirm{width:min(360px,calc(100vw - 32px));padding:18px;border-radius:12px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3);}' +
|
|
98
|
+
'.expl-confirm-title{font-size:14px;font-weight:600;margin-bottom:10px;}.expl-confirm-text{font-size:13px;line-height:20px;overflow-wrap:anywhere;}.expl-confirm-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px;}' +
|
|
99
|
+
'.expl-confirm-btn{height:30px;padding:0 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font-size:13px;}.expl-confirm-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}.expl-confirm-primary{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary);color:#fff;}' +
|
|
93
100
|
'.expl-menu{position:fixed;z-index:1990;min-width:172px;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;padding:4px;box-shadow:var(--dsw-shadow-lv3);pointer-events:auto;}' +
|
|
94
101
|
'.expl-menu-item{display:flex;align-items:center;gap:7px;width:100%;text-align:left;background:transparent;border:none;color:var(--dsw-alias-label-primary);font-size:13px;padding:6px 10px;border-radius:8px;cursor:pointer;line-height:1.4;}' +
|
|
95
102
|
'.expl-menu-ico{flex:none;display:inline-flex;align-items:center;}.expl-menu-ico svg{width:14px;height:14px;display:block;}' +
|
|
@@ -151,6 +158,7 @@ window.__ModuleLoader__.load({
|
|
|
151
158
|
// 纯预览(无编辑):markdown/html/json/csv 各有专属渲染器。
|
|
152
159
|
var FILEVIEW_CSS =
|
|
153
160
|
'.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;}' +
|
|
161
|
+
'.dfv-header{position:sticky;top:0;z-index:2;flex:none;background:var(--dsw-specific-sidebar-fill);}' +
|
|
154
162
|
'.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;}' +
|
|
155
163
|
'.dfv-tabbar::-webkit-scrollbar{display:none;}' +
|
|
156
164
|
'.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;}' +
|
|
@@ -259,6 +267,10 @@ window.__ModuleLoader__.load({
|
|
|
259
267
|
'explorer.uploading': '上传中 ',
|
|
260
268
|
'explorer.downloading': '下载中 ',
|
|
261
269
|
'explorer.uploaded': '已上传 {n} 个文件',
|
|
270
|
+
'explorer.overwriteConfirm': '文件“{name}”已存在,是否覆盖?',
|
|
271
|
+
'explorer.overwriteTitle': '确认覆盖',
|
|
272
|
+
'explorer.overwrite': '覆盖',
|
|
273
|
+
'explorer.cancel': '取消',
|
|
262
274
|
'explorer.filesCount': '{n} 个文件',
|
|
263
275
|
'explorer.filesCountPartial': '{ok}/{total} 个文件',
|
|
264
276
|
'explorer.downloaded': '已下载 {n} 个文件',
|
|
@@ -323,6 +335,10 @@ window.__ModuleLoader__.load({
|
|
|
323
335
|
'explorer.uploading': 'Uploading ',
|
|
324
336
|
'explorer.downloading': 'Downloading ',
|
|
325
337
|
'explorer.uploaded': 'Uploaded {n} file(s)',
|
|
338
|
+
'explorer.overwriteConfirm': 'The file "{name}" already exists. Overwrite it?',
|
|
339
|
+
'explorer.overwriteTitle': 'Confirm overwrite',
|
|
340
|
+
'explorer.overwrite': 'Overwrite',
|
|
341
|
+
'explorer.cancel': 'Cancel',
|
|
326
342
|
'explorer.filesCount': '{n} file(s)',
|
|
327
343
|
'explorer.filesCountPartial': '{ok}/{total} file(s)',
|
|
328
344
|
'explorer.downloaded': 'Downloaded {n} file(s)',
|
|
@@ -386,6 +402,7 @@ window.__ModuleLoader__.load({
|
|
|
386
402
|
dropTarget: null, dragPaths: null,
|
|
387
403
|
files: { list: [], active: null, overlay: false, overlayMax: false, overlayReturn: null },
|
|
388
404
|
uploads: [],
|
|
405
|
+
uploadConflict: null,
|
|
389
406
|
}
|
|
390
407
|
var listeners = new Set()
|
|
391
408
|
// trackSession() is called by both the hero toggle and the details panel
|
|
@@ -439,9 +456,9 @@ window.__ModuleLoader__.load({
|
|
|
439
456
|
var uploadInputEl = null
|
|
440
457
|
var layout = null
|
|
441
458
|
|
|
442
|
-
var DETAILS_MIN =
|
|
443
|
-
var DETAILS_MAX =
|
|
444
|
-
var DETAILS_DEFAULT =
|
|
459
|
+
var DETAILS_MIN = 300
|
|
460
|
+
var DETAILS_MAX = 520
|
|
461
|
+
var DETAILS_DEFAULT = 360
|
|
445
462
|
function clampW(w) { return Math.max(DETAILS_MIN, Math.min(DETAILS_MAX, w)) }
|
|
446
463
|
|
|
447
464
|
function pathJoin(dir, name) { return String(dir || '/').replace(/\/+$/, '') + '/' + name }
|
|
@@ -1139,9 +1156,12 @@ window.__ModuleLoader__.load({
|
|
|
1139
1156
|
}
|
|
1140
1157
|
|
|
1141
1158
|
function finishUploads(count, okCount, failedName, down) {
|
|
1142
|
-
|
|
1159
|
+
// These translations own the `{n}` placeholder; passing no params leaves
|
|
1160
|
+
// the literal token visible in the green completion notice.
|
|
1161
|
+
var verb = down ? T('explorer.downloaded', { n: okCount }) : T('explorer.uploaded', { n: okCount })
|
|
1162
|
+
var suffix = okCount === count ? '' : ' · ' + T('explorer.filesCountPartial', { ok: okCount, total: count })
|
|
1143
1163
|
setUploadEntry({
|
|
1144
|
-
name: '✓ ' + verb +
|
|
1164
|
+
name: '✓ ' + verb + suffix + (failedName ? '(' + failedName + ')' : ''),
|
|
1145
1165
|
loaded: 0, total: 0, idx: 0, totalFiles: 0, done: true, down: !!down,
|
|
1146
1166
|
})
|
|
1147
1167
|
window.setTimeout(function () { setState({ uploads: [] }) }, 1600)
|
|
@@ -1156,10 +1176,17 @@ window.__ModuleLoader__.load({
|
|
|
1156
1176
|
var start = c * UPLOAD_CHUNK_SIZE
|
|
1157
1177
|
var blob = f.slice(start, Math.min(f.size, start + UPLOAD_CHUNK_SIZE))
|
|
1158
1178
|
var b64 = await fileToBase64(blob)
|
|
1159
|
-
var
|
|
1179
|
+
var args = {
|
|
1160
1180
|
dir: dir, name: f.name, data: b64,
|
|
1161
1181
|
chunk: chunked ? c + 1 : undefined,
|
|
1162
|
-
}
|
|
1182
|
+
}
|
|
1183
|
+
var res = await rpc('upload', args)
|
|
1184
|
+
if (res && res.conflict) {
|
|
1185
|
+
var overwrite = await askOverwrite(f.name)
|
|
1186
|
+
if (!overwrite) throw new Error('已取消上传')
|
|
1187
|
+
args.overwrite = true
|
|
1188
|
+
res = await rpc('upload', args)
|
|
1189
|
+
}
|
|
1163
1190
|
if (!res || !res.ok) throw new Error((res && res.error) || T('explorer.uploadFail'))
|
|
1164
1191
|
updateUpload(f.name, Math.min(f.size, start + UPLOAD_CHUNK_SIZE), f.size, idx, totalFiles)
|
|
1165
1192
|
}
|
|
@@ -1491,9 +1518,17 @@ window.__ModuleLoader__.load({
|
|
|
1491
1518
|
var last = parseFloat(parts[parts.length - 1])
|
|
1492
1519
|
if (isNaN(last) || last <= 1) { setState({ open: false }); return }
|
|
1493
1520
|
setState({ open: true })
|
|
1521
|
+
// Official 0.1.5 rightbar defaults to a viewport ratio. Keep
|
|
1522
|
+
// it within dsh-soup's 300-520px explorer contract.
|
|
1523
|
+
if (last < DETAILS_MIN || last > DETAILS_MAX) {
|
|
1524
|
+
if (Math.round(last) !== DETAILS_DEFAULT) setFrameWidth(DETAILS_DEFAULT)
|
|
1525
|
+
widthRef.current = DETAILS_DEFAULT
|
|
1526
|
+
return
|
|
1527
|
+
}
|
|
1494
1528
|
if (Math.round(last) !== Math.round(widthRef.current)) {
|
|
1495
|
-
|
|
1496
|
-
|
|
1529
|
+
// 宿主是宽度偏好的权威(/native drag/窗口求解都可能改它)。
|
|
1530
|
+
// 插件只同步基准,不能把宿主拖出来的宽度强行拉回旧默认。
|
|
1531
|
+
widthRef.current = last
|
|
1497
1532
|
}
|
|
1498
1533
|
} catch (e) {}
|
|
1499
1534
|
}
|
|
@@ -1582,7 +1617,10 @@ window.__ModuleLoader__.load({
|
|
|
1582
1617
|
create('button', {
|
|
1583
1618
|
className: 'expl-btn',
|
|
1584
1619
|
onClick: function (event) {
|
|
1585
|
-
try {
|
|
1620
|
+
try {
|
|
1621
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
1622
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
1623
|
+
} catch (e) {}
|
|
1586
1624
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1587
1625
|
},
|
|
1588
1626
|
title: T('explorer.close'),
|
|
@@ -1613,6 +1651,16 @@ window.__ModuleLoader__.load({
|
|
|
1613
1651
|
container,
|
|
1614
1652
|
s.menu ? create('div', { className: 'expl-menu-mask', onMouseDown: function () { setState({ menu: null }) }, onContextMenu: function (e) { e.preventDefault(); setState({ menu: null }) } }) : null,
|
|
1615
1653
|
s.menu ? create(Menu, { menu: s.menu }) : null,
|
|
1654
|
+
s.uploadConflict ? create('div', { className: 'expl-confirm-mask', role: 'dialog', 'aria-modal': 'true' },
|
|
1655
|
+
create('div', { className: 'expl-confirm' },
|
|
1656
|
+
create('div', { className: 'expl-confirm-title' }, T('explorer.overwriteTitle')),
|
|
1657
|
+
create('div', { className: 'expl-confirm-text' }, T('explorer.overwriteConfirm', { name: s.uploadConflict.name })),
|
|
1658
|
+
create('div', { className: 'expl-confirm-actions' },
|
|
1659
|
+
create('button', { type: 'button', className: 'expl-confirm-btn', onClick: function () { s.uploadConflict.resolve(false); setState({ uploadConflict: null }) } }, T('explorer.cancel')),
|
|
1660
|
+
create('button', { type: 'button', className: 'expl-confirm-btn expl-confirm-primary', autoFocus: true, onClick: function () { s.uploadConflict.resolve(true); setState({ uploadConflict: null }) } }, T('explorer.overwrite')),
|
|
1661
|
+
),
|
|
1662
|
+
),
|
|
1663
|
+
) : null,
|
|
1616
1664
|
)
|
|
1617
1665
|
}
|
|
1618
1666
|
|
|
@@ -1629,7 +1677,15 @@ window.__ModuleLoader__.load({
|
|
|
1629
1677
|
return create('button', {
|
|
1630
1678
|
type: 'button',
|
|
1631
1679
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1632
|
-
onClick: function () {
|
|
1680
|
+
onClick: function () {
|
|
1681
|
+
try {
|
|
1682
|
+
if (layout && typeof layout.closeRightbar === 'function') {
|
|
1683
|
+
if (s.open) layout.closeRightbar()
|
|
1684
|
+
else layout.openRightbar(true, false)
|
|
1685
|
+
} else if (s.open) layout.closeDetails()
|
|
1686
|
+
else layout.openDetails()
|
|
1687
|
+
} catch (e) {}
|
|
1688
|
+
},
|
|
1633
1689
|
title: T('explorer.label'),
|
|
1634
1690
|
'aria-label': T('explorer.label'),
|
|
1635
1691
|
}, '📁')
|
|
@@ -1659,10 +1715,16 @@ window.__ModuleLoader__.load({
|
|
|
1659
1715
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1660
1716
|
onClick: function (event) {
|
|
1661
1717
|
if (s.open) {
|
|
1662
|
-
try {
|
|
1718
|
+
try {
|
|
1719
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
1720
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
1721
|
+
} catch (e) {}
|
|
1663
1722
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1664
1723
|
} else if (!setHeroDetailsWidth(event.currentTarget, DETAILS_DEFAULT)) {
|
|
1665
|
-
try {
|
|
1724
|
+
try {
|
|
1725
|
+
if (layout && typeof layout.openRightbar === 'function') layout.openRightbar(true, false)
|
|
1726
|
+
else if (layout && typeof layout.openDetails === 'function') layout.openDetails()
|
|
1727
|
+
} catch (e) {}
|
|
1666
1728
|
}
|
|
1667
1729
|
},
|
|
1668
1730
|
title: T('explorer.label'),
|
|
@@ -1717,6 +1779,12 @@ window.__ModuleLoader__.load({
|
|
|
1717
1779
|
} catch (_) {}
|
|
1718
1780
|
}
|
|
1719
1781
|
|
|
1782
|
+
function askOverwrite(name) {
|
|
1783
|
+
return new Promise(function (resolve) {
|
|
1784
|
+
setState({ uploadConflict: { name: name, resolve: resolve } })
|
|
1785
|
+
})
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1720
1788
|
function findFileEntry(path) {
|
|
1721
1789
|
for (var i = 0; i < state.files.list.length; i++) {
|
|
1722
1790
|
if (state.files.list[i].path === path) return state.files.list[i]
|
|
@@ -2477,6 +2545,7 @@ window.__ModuleLoader__.load({
|
|
|
2477
2545
|
)
|
|
2478
2546
|
})
|
|
2479
2547
|
|
|
2548
|
+
var toolbar = null
|
|
2480
2549
|
var body = null
|
|
2481
2550
|
if (!active) {
|
|
2482
2551
|
body = create('div', { className: 'dfv-empty' },
|
|
@@ -2484,33 +2553,36 @@ window.__ModuleLoader__.load({
|
|
|
2484
2553
|
T('files.emptyHint'),
|
|
2485
2554
|
)
|
|
2486
2555
|
} else {
|
|
2556
|
+
toolbar = create('div', { className: 'dfv-toolbar' },
|
|
2557
|
+
create('span', { className: 'dfv-toolbar-path', title: active.path }, active.path),
|
|
2558
|
+
typeof active.size === 'number' && active.size > 0 ? create('span', null, formatBytes(active.size)) : null,
|
|
2559
|
+
active.loaded ? create('button', {
|
|
2560
|
+
type: 'button',
|
|
2561
|
+
className: 'dfv-btn',
|
|
2562
|
+
title: T('files.reloadTitle'),
|
|
2563
|
+
onClick: function () { reloadFile(active.path) },
|
|
2564
|
+
},
|
|
2565
|
+
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2566
|
+
T('files.reload'),
|
|
2567
|
+
) : null,
|
|
2568
|
+
create('button', {
|
|
2569
|
+
type: 'button',
|
|
2570
|
+
className: 'dfv-btn',
|
|
2571
|
+
title: '最大化', 'aria-label': '最大化',
|
|
2572
|
+
onClick: function () { setFiles({ overlay: true, overlayMax: true, overlayReturn: 'tab' }) },
|
|
2573
|
+
dangerouslySetInnerHTML: { __html: EXPAND_SVG },
|
|
2574
|
+
}),
|
|
2575
|
+
)
|
|
2487
2576
|
body = create('div', { className: 'dfv-body' },
|
|
2488
|
-
create('div', { className: 'dfv-toolbar' },
|
|
2489
|
-
create('span', { className: 'dfv-toolbar-path', title: active.path }, active.path),
|
|
2490
|
-
typeof active.size === 'number' && active.size > 0 ? create('span', null, formatBytes(active.size)) : null,
|
|
2491
|
-
active.loaded ? create('button', {
|
|
2492
|
-
type: 'button',
|
|
2493
|
-
className: 'dfv-btn',
|
|
2494
|
-
title: T('files.reloadTitle'),
|
|
2495
|
-
onClick: function () { reloadFile(active.path) },
|
|
2496
|
-
},
|
|
2497
|
-
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
2498
|
-
T('files.reload'),
|
|
2499
|
-
) : null,
|
|
2500
|
-
create('button', {
|
|
2501
|
-
type: 'button',
|
|
2502
|
-
className: 'dfv-btn',
|
|
2503
|
-
title: '最大化', 'aria-label': '最大化',
|
|
2504
|
-
onClick: function () { setFiles({ overlay: true, overlayMax: true, overlayReturn: 'tab' }) },
|
|
2505
|
-
dangerouslySetInnerHTML: { __html: EXPAND_SVG },
|
|
2506
|
-
}),
|
|
2507
|
-
),
|
|
2508
2577
|
create(FileContent, { key: active.path, entry: active }),
|
|
2509
2578
|
)
|
|
2510
2579
|
}
|
|
2511
2580
|
|
|
2512
2581
|
return create('div', { className: 'dfv-root', ref: rootRef },
|
|
2513
|
-
files.list.length > 0 ? create('div', { className: 'dfv-
|
|
2582
|
+
files.list.length > 0 ? create('div', { className: 'dfv-header' },
|
|
2583
|
+
create('div', { className: 'dfv-tabbar' }, tabs),
|
|
2584
|
+
toolbar,
|
|
2585
|
+
) : null,
|
|
2514
2586
|
body,
|
|
2515
2587
|
)
|
|
2516
2588
|
}
|
|
@@ -2966,6 +3038,30 @@ window.__ModuleLoader__.load({
|
|
|
2966
3038
|
var sessions = ctx.sessions
|
|
2967
3039
|
var remoteGoals = ctx.remote.goals
|
|
2968
3040
|
var disposers = []
|
|
3041
|
+
// 0.1.5's official Sidebar adds a header-corner expand button. Its
|
|
3042
|
+
// default handler opens the official surface, which would diverge from
|
|
3043
|
+
// dsh-soup's resource explorer. Route that button to the same layout
|
|
3044
|
+
// face used by our explorer controls instead.
|
|
3045
|
+
if (typeof document !== 'undefined') {
|
|
3046
|
+
var onOfficialExpand = function (event) {
|
|
3047
|
+
var target = event.target && event.target.closest
|
|
3048
|
+
? event.target.closest('[data-sidebar-right-expand]') : null
|
|
3049
|
+
if (!target) return
|
|
3050
|
+
event.preventDefault()
|
|
3051
|
+
if (typeof event.stopImmediatePropagation === 'function') event.stopImmediatePropagation()
|
|
3052
|
+
else event.stopPropagation()
|
|
3053
|
+
try {
|
|
3054
|
+
if (state.open) {
|
|
3055
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
3056
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
3057
|
+
} else if (layout && typeof layout.openRightbar === 'function') {
|
|
3058
|
+
layout.openRightbar(true, false)
|
|
3059
|
+
} else if (layout && typeof layout.openDetails === 'function') layout.openDetails()
|
|
3060
|
+
} catch (e) {}
|
|
3061
|
+
}
|
|
3062
|
+
document.addEventListener('click', onOfficialExpand, true)
|
|
3063
|
+
disposers.push(function () { document.removeEventListener('click', onOfficialExpand, true) })
|
|
3064
|
+
}
|
|
2969
3065
|
// i18n:注册双语词典;模块级 T 供组件外回调(菜单/错误横幅)取当前语言文案。
|
|
2970
3066
|
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
2971
3067
|
ctx.effect(function () { return ctx.locale.register(NS, DICT) }, 'dsh-soup: dictionaries')
|
|
@@ -2996,6 +3092,20 @@ window.__ModuleLoader__.load({
|
|
|
2996
3092
|
},
|
|
2997
3093
|
))
|
|
2998
3094
|
})
|
|
3095
|
+
// DSH 0.1.5 moved the right column from the legacy `details` seat to
|
|
3096
|
+
// `rightbar`. Register at a lower priority so dsh-soup remains the
|
|
3097
|
+
// occupant when the official Sidebar plugin is present.
|
|
3098
|
+
slots.inject('rightbar', function () {
|
|
3099
|
+
disposers.push(slots.register(
|
|
3100
|
+
{ name: 'rightbar', priority: -1, locale: NS },
|
|
3101
|
+
function (props) {
|
|
3102
|
+
return create(React.Fragment, null,
|
|
3103
|
+
create(Panel, props),
|
|
3104
|
+
create(PreviewOverlay),
|
|
3105
|
+
)
|
|
3106
|
+
},
|
|
3107
|
+
))
|
|
3108
|
+
})
|
|
2999
3109
|
// 文件标签页:与 对话/轨迹 同级的原生 view tab(ui-trajectory 同款注册方式)。
|
|
3000
3110
|
// 槽位 entry 是静态的,多文件由 FilesView 内部子 tab 条管理。
|
|
3001
3111
|
slots.inject('conversation.view', function () {
|
package/lib/index.js
CHANGED
|
@@ -690,14 +690,22 @@ export function apply(ctx) {
|
|
|
690
690
|
}
|
|
691
691
|
const dirRp = await confine(dir, '目录', sessionIdArg)
|
|
692
692
|
const target = await confine(joinPath(dirRp, fileName), '上传路径', sessionIdArg)
|
|
693
|
-
//
|
|
694
|
-
//
|
|
693
|
+
// 分块上传:首块先做同名冲突检查;客户端确认覆盖后才写入,
|
|
694
|
+
// 后续块追加到已创建的目标。
|
|
695
695
|
const chunk = Number((args && args.chunk) || 0)
|
|
696
696
|
if (chunk < 0 || !Number.isInteger(chunk)) return { ok: false, error: '无效的 chunk 序号' }
|
|
697
697
|
try {
|
|
698
698
|
const bytes = Buffer.from(data.replace(/\s+/g, ''), 'base64')
|
|
699
|
-
if (chunk >= 2)
|
|
700
|
-
|
|
699
|
+
if (chunk >= 2) {
|
|
700
|
+
await appendFile(target, bytes)
|
|
701
|
+
return { ok: true, path: target }
|
|
702
|
+
}
|
|
703
|
+
if (!(args && args.overwrite)) {
|
|
704
|
+
try { await stat(target); return { ok: false, conflict: true, error: '目标文件已存在' } } catch (err) {
|
|
705
|
+
if (!err || err.code !== 'ENOENT') throw err
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
await writeFile(target, bytes)
|
|
701
709
|
return { ok: true, path: target }
|
|
702
710
|
} catch (err) {
|
|
703
711
|
return { ok: false, error: `写入失败: ${String((err && err.message) || err)}` }
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyhue1991/dsh-soup",
|
|
3
|
-
"description": "DSH 项目资源管理器:右侧 details
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "DSH 项目资源管理器:右侧 details 列文件树(并列网格、宽度对齐宿主 details 合同 300-520px),有 Session log 时在其右侧、空白新会话在输入框上方右端提供 📁 切换按钮;支持多选、双击展开/打开、右键菜单(打开/系统打开/重命名/废纸篓/新建/复制路径)、拖拽移动、上传。文件「打开」在会话区新增「文件」标签页(原生 conversation.view,与对话/轨迹同级):文本可编辑 ⌘S 保存、图片预览、二进制提示。附带模型吞吐徽标:输入框正上方显示「正在等待模型...」与彩色 t/s 徽标(数据来自 llm/stream 真实流)。附带 GoalBar 多行展示:把原生单行截断 GoalBar 放开为多行完整显示,编辑用 textarea,工具与数据仍走原生 goal。",
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|