@lyhue1991/dsh-soup 0.4.11 → 0.5.1
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 +1 -0
- package/lib/client.js +145 -24
- package/lib/index.js +12 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ DSH(DeepSeek Harness)功能很强:会话、工具、轨迹、Goal 一应
|
|
|
40
40
|
- **类型图标**:按品牌色区分文件类型(notebook 橙、markdown 紫、json 黄、pdf 红…)。
|
|
41
41
|
- **沙箱围栏**:所有文件操作只能落在你的工作区与会话目录内,越界自动拒绝。
|
|
42
42
|
- **自动刷新**:每 3 秒轻量探测一次目录签名(mtime),有变化才重拉——agent 或终端里的文件增删改无需手动刷新即可出现;面板关闭且无预览时零开销;宿主不可达(如 DSH 重启中)自动指数退避(封顶 60s)。
|
|
43
|
+
- **新建文件夹**:顶部 🗂+ 在当前目录创建文件夹;工具栏顺序为 🗂+ 新建、⬆ 上传、⟳ 刷新、✕ 关闭。
|
|
43
44
|
- **一键刷新**:顶部 ⟳ 重拉整个文件树,已展开的子目录保持展开不收起。
|
|
44
45
|
|
|
45
46
|
### 🫚 会话内文件预览(主打 · 姜 · 去腥增香)
|
package/lib/client.js
CHANGED
|
@@ -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,9 +93,14 @@ 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
|
-
'.expl-menu-item{display:flex;align-items:center;
|
|
95
|
-
'.expl-menu-ico{flex:none;display:inline-flex;align-items:center;
|
|
101
|
+
'.expl-menu-item{display:flex;align-items:center;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;}' +
|
|
102
|
+
'.expl-menu-ico,.expl-menu-ico-ph{flex:none;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;margin-right:7px;}' +
|
|
103
|
+
'.expl-menu-ico svg{width:14px;height:14px;display:block;}' +
|
|
96
104
|
'.expl-menu-item:hover{background:var(--dsw-alias-interactive-bg-hover);}' +
|
|
97
105
|
'.expl-danger{color:var(--dsw-alias-state-error-primary);} .expl-menu-item.expl-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger);}' +
|
|
98
106
|
'.expl-menu-sep{height:1px;margin:4px 6px;background:var(--dsw-alias-border-l1);}' +
|
|
@@ -112,12 +120,13 @@ window.__ModuleLoader__.load({
|
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
// ------------------------------------------------------------------
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
123
|
+
// 工具栏图标:刷新图标取自 JupyterLab ui-components(BSD-3-Clause);
|
|
124
|
+
// fill 改为 currentColor 以跟随 DSH 主题 token。
|
|
125
|
+
// 注意:ICON_CLOSE / ICON_UPLOAD 与 GoalBar 段的同名 React 元素常量
|
|
126
|
+
// 冲突(var 提升覆盖),字符样式反而更稳。
|
|
119
127
|
// ------------------------------------------------------------------
|
|
120
128
|
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>'
|
|
129
|
+
var ICON_NEW_FOLDER = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"><path fill="currentColor" d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-2 7h-3v3h-2v-3h-3v-2h3V8h2v3h3v2z"/></svg>'
|
|
121
130
|
var ICON_MORE = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><circle cx="3" cy="8" r="1.3" fill="currentColor"/><circle cx="8" cy="8" r="1.3" fill="currentColor"/><circle cx="13" cy="8" r="1.3" fill="currentColor"/></svg>'
|
|
122
131
|
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>'
|
|
123
132
|
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>'
|
|
@@ -250,6 +259,8 @@ window.__ModuleLoader__.load({
|
|
|
250
259
|
'explorer.label': '资源管理器',
|
|
251
260
|
'explorer.refresh': '刷新',
|
|
252
261
|
'explorer.refreshTitle': '刷新(保持已展开目录)',
|
|
262
|
+
'explorer.newFolder': '新建文件夹',
|
|
263
|
+
'explorer.newFolderTitle': '在当前目录新建文件夹',
|
|
253
264
|
'explorer.upload': '上传文件',
|
|
254
265
|
'explorer.close': '关闭',
|
|
255
266
|
'explorer.closePanel': '关闭资源管理器',
|
|
@@ -260,6 +271,10 @@ window.__ModuleLoader__.load({
|
|
|
260
271
|
'explorer.uploading': '上传中 ',
|
|
261
272
|
'explorer.downloading': '下载中 ',
|
|
262
273
|
'explorer.uploaded': '已上传 {n} 个文件',
|
|
274
|
+
'explorer.overwriteConfirm': '文件“{name}”已存在,是否覆盖?',
|
|
275
|
+
'explorer.overwriteTitle': '确认覆盖',
|
|
276
|
+
'explorer.overwrite': '覆盖',
|
|
277
|
+
'explorer.cancel': '取消',
|
|
263
278
|
'explorer.filesCount': '{n} 个文件',
|
|
264
279
|
'explorer.filesCountPartial': '{ok}/{total} 个文件',
|
|
265
280
|
'explorer.downloaded': '已下载 {n} 个文件',
|
|
@@ -280,7 +295,7 @@ window.__ModuleLoader__.load({
|
|
|
280
295
|
'menu.trash': '🗑 移到废纸篓',
|
|
281
296
|
'menu.trashMulti': '🗑 移到废纸篓 ({n} 项)',
|
|
282
297
|
'menu.newFile': '📄 新建文件',
|
|
283
|
-
'menu.newFolder': '
|
|
298
|
+
'menu.newFolder': '新建文件夹',
|
|
284
299
|
'menu.copyPath': '⧉ 复制路径',
|
|
285
300
|
'menu.download': '⬇ 下载',
|
|
286
301
|
'menu.deselect': '取消选择',
|
|
@@ -314,6 +329,8 @@ window.__ModuleLoader__.load({
|
|
|
314
329
|
'explorer.label': 'Explorer',
|
|
315
330
|
'explorer.refresh': 'Refresh',
|
|
316
331
|
'explorer.refreshTitle': 'Refresh (keeps expanded folders)',
|
|
332
|
+
'explorer.newFolder': 'New folder',
|
|
333
|
+
'explorer.newFolderTitle': 'Create a folder in the current directory',
|
|
317
334
|
'explorer.upload': 'Upload files',
|
|
318
335
|
'explorer.close': 'Close',
|
|
319
336
|
'explorer.closePanel': 'Close explorer',
|
|
@@ -324,6 +341,10 @@ window.__ModuleLoader__.load({
|
|
|
324
341
|
'explorer.uploading': 'Uploading ',
|
|
325
342
|
'explorer.downloading': 'Downloading ',
|
|
326
343
|
'explorer.uploaded': 'Uploaded {n} file(s)',
|
|
344
|
+
'explorer.overwriteConfirm': 'The file "{name}" already exists. Overwrite it?',
|
|
345
|
+
'explorer.overwriteTitle': 'Confirm overwrite',
|
|
346
|
+
'explorer.overwrite': 'Overwrite',
|
|
347
|
+
'explorer.cancel': 'Cancel',
|
|
327
348
|
'explorer.filesCount': '{n} file(s)',
|
|
328
349
|
'explorer.filesCountPartial': '{ok}/{total} file(s)',
|
|
329
350
|
'explorer.downloaded': 'Downloaded {n} file(s)',
|
|
@@ -344,7 +365,7 @@ window.__ModuleLoader__.load({
|
|
|
344
365
|
'menu.trash': '🗑 Move to trash',
|
|
345
366
|
'menu.trashMulti': '🗑 Move to trash ({n})',
|
|
346
367
|
'menu.newFile': '📄 New file',
|
|
347
|
-
'menu.newFolder': '
|
|
368
|
+
'menu.newFolder': 'New folder',
|
|
348
369
|
'menu.copyPath': '⧉ Copy path',
|
|
349
370
|
'menu.download': '⬇ Download',
|
|
350
371
|
'menu.deselect': 'Clear selection',
|
|
@@ -387,6 +408,7 @@ window.__ModuleLoader__.load({
|
|
|
387
408
|
dropTarget: null, dragPaths: null,
|
|
388
409
|
files: { list: [], active: null, overlay: false, overlayMax: false, overlayReturn: null },
|
|
389
410
|
uploads: [],
|
|
411
|
+
uploadConflict: null,
|
|
390
412
|
}
|
|
391
413
|
var listeners = new Set()
|
|
392
414
|
// trackSession() is called by both the hero toggle and the details panel
|
|
@@ -1140,9 +1162,12 @@ window.__ModuleLoader__.load({
|
|
|
1140
1162
|
}
|
|
1141
1163
|
|
|
1142
1164
|
function finishUploads(count, okCount, failedName, down) {
|
|
1143
|
-
|
|
1165
|
+
// These translations own the `{n}` placeholder; passing no params leaves
|
|
1166
|
+
// the literal token visible in the green completion notice.
|
|
1167
|
+
var verb = down ? T('explorer.downloaded', { n: okCount }) : T('explorer.uploaded', { n: okCount })
|
|
1168
|
+
var suffix = okCount === count ? '' : ' · ' + T('explorer.filesCountPartial', { ok: okCount, total: count })
|
|
1144
1169
|
setUploadEntry({
|
|
1145
|
-
name: '✓ ' + verb +
|
|
1170
|
+
name: '✓ ' + verb + suffix + (failedName ? '(' + failedName + ')' : ''),
|
|
1146
1171
|
loaded: 0, total: 0, idx: 0, totalFiles: 0, done: true, down: !!down,
|
|
1147
1172
|
})
|
|
1148
1173
|
window.setTimeout(function () { setState({ uploads: [] }) }, 1600)
|
|
@@ -1157,10 +1182,17 @@ window.__ModuleLoader__.load({
|
|
|
1157
1182
|
var start = c * UPLOAD_CHUNK_SIZE
|
|
1158
1183
|
var blob = f.slice(start, Math.min(f.size, start + UPLOAD_CHUNK_SIZE))
|
|
1159
1184
|
var b64 = await fileToBase64(blob)
|
|
1160
|
-
var
|
|
1185
|
+
var args = {
|
|
1161
1186
|
dir: dir, name: f.name, data: b64,
|
|
1162
1187
|
chunk: chunked ? c + 1 : undefined,
|
|
1163
|
-
}
|
|
1188
|
+
}
|
|
1189
|
+
var res = await rpc('upload', args)
|
|
1190
|
+
if (res && res.conflict) {
|
|
1191
|
+
var overwrite = await askOverwrite(f.name)
|
|
1192
|
+
if (!overwrite) throw new Error('已取消上传')
|
|
1193
|
+
args.overwrite = true
|
|
1194
|
+
res = await rpc('upload', args)
|
|
1195
|
+
}
|
|
1164
1196
|
if (!res || !res.ok) throw new Error((res && res.error) || T('explorer.uploadFail'))
|
|
1165
1197
|
updateUpload(f.name, Math.min(f.size, start + UPLOAD_CHUNK_SIZE), f.size, idx, totalFiles)
|
|
1166
1198
|
}
|
|
@@ -1381,7 +1413,7 @@ window.__ModuleLoader__.load({
|
|
|
1381
1413
|
}
|
|
1382
1414
|
if (node.type === 'directory') {
|
|
1383
1415
|
items.push({ key: 'newfile', label: T('menu.newFile'), onClick: function () { startNew(node.path, false) } })
|
|
1384
|
-
items.push({ key: 'newfolder', label: T('menu.newFolder'), onClick: function () { startNew(node.path, true) }, separatorAfter: true })
|
|
1416
|
+
items.push({ key: 'newfolder', label: [create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_NEW_FOLDER } }), T('menu.newFolder')], onClick: function () { startNew(node.path, true) }, separatorAfter: true })
|
|
1385
1417
|
}
|
|
1386
1418
|
items.push({ key: 'copy', label: T('menu.copyPath'), onClick: function () { copyPath(node) } })
|
|
1387
1419
|
if (node.type !== 'directory') {
|
|
@@ -1389,7 +1421,7 @@ window.__ModuleLoader__.load({
|
|
|
1389
1421
|
}
|
|
1390
1422
|
} else {
|
|
1391
1423
|
items.push({ key: 'newfile', label: T('menu.newFile'), onClick: function () { startNew(state.cwd, false) } })
|
|
1392
|
-
items.push({ key: 'newfolder', label: T('menu.newFolder'), onClick: function () { startNew(state.cwd, true) } })
|
|
1424
|
+
items.push({ key: 'newfolder', label: [create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_NEW_FOLDER } }), T('menu.newFolder')], onClick: function () { startNew(state.cwd, true) } })
|
|
1393
1425
|
items.push({ key: 'refresh', label: [create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }), T('explorer.refresh')], onClick: refresh, separatorAfter: true })
|
|
1394
1426
|
if (state.selected.size > 0) items.push({ key: 'none', label: T('menu.deselect'), onClick: function () { setState({ selected: new Set(), lastIndex: null }) } })
|
|
1395
1427
|
}
|
|
@@ -1428,11 +1460,16 @@ window.__ModuleLoader__.load({
|
|
|
1428
1460
|
var items = buildMenuItems(menu.node)
|
|
1429
1461
|
var menuChildren = []
|
|
1430
1462
|
items.forEach(function (it) {
|
|
1463
|
+
var children = it.label instanceof Array ? it.label : (function () {
|
|
1464
|
+
var match = it.label.match(/^([\u2190-\u2BFF\u{1F000}-\u{1FAFF}])\s*/u)
|
|
1465
|
+
if (match) return [create('span', { className: 'expl-menu-ico' }, match[1]), it.label.slice(match[0].length)]
|
|
1466
|
+
return [create('span', { className: 'expl-menu-ico-ph' }), it.label]
|
|
1467
|
+
})()
|
|
1431
1468
|
menuChildren.push(create('button', {
|
|
1432
1469
|
key: it.key,
|
|
1433
1470
|
className: 'expl-menu-item' + (it.danger ? ' expl-danger' : ''),
|
|
1434
1471
|
onClick: function () { setState({ menu: null }); it.onClick() },
|
|
1435
|
-
},
|
|
1472
|
+
}, children))
|
|
1436
1473
|
if (it.separatorAfter) menuChildren.push(create('div', { key: it.key + '-sep', className: 'expl-menu-sep' }))
|
|
1437
1474
|
})
|
|
1438
1475
|
return create('div', { ref: menuRef, className: 'expl-menu', style: { left: pos.x, top: pos.y } }, menuChildren)
|
|
@@ -1492,6 +1529,13 @@ window.__ModuleLoader__.load({
|
|
|
1492
1529
|
var last = parseFloat(parts[parts.length - 1])
|
|
1493
1530
|
if (isNaN(last) || last <= 1) { setState({ open: false }); return }
|
|
1494
1531
|
setState({ open: true })
|
|
1532
|
+
// Official 0.1.5 rightbar defaults to a viewport ratio. Keep
|
|
1533
|
+
// it within dsh-soup's 300-520px explorer contract.
|
|
1534
|
+
if (last < DETAILS_MIN || last > DETAILS_MAX) {
|
|
1535
|
+
if (Math.round(last) !== DETAILS_DEFAULT) setFrameWidth(DETAILS_DEFAULT)
|
|
1536
|
+
widthRef.current = DETAILS_DEFAULT
|
|
1537
|
+
return
|
|
1538
|
+
}
|
|
1495
1539
|
if (Math.round(last) !== Math.round(widthRef.current)) {
|
|
1496
1540
|
// 宿主是宽度偏好的权威(/native drag/窗口求解都可能改它)。
|
|
1497
1541
|
// 插件只同步基准,不能把宿主拖出来的宽度强行拉回旧默认。
|
|
@@ -1571,20 +1615,29 @@ window.__ModuleLoader__.load({
|
|
|
1571
1615
|
create('div', { className: 'expl-head-btns' },
|
|
1572
1616
|
create('button', {
|
|
1573
1617
|
className: 'expl-btn',
|
|
1574
|
-
onClick: function () {
|
|
1575
|
-
title: T('explorer.
|
|
1576
|
-
'aria-label': T('explorer.
|
|
1577
|
-
}, create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html:
|
|
1618
|
+
onClick: function () { startNew(state.cwd, true) },
|
|
1619
|
+
title: T('explorer.newFolderTitle'),
|
|
1620
|
+
'aria-label': T('explorer.newFolder'),
|
|
1621
|
+
}, create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_NEW_FOLDER } })),
|
|
1578
1622
|
create('button', {
|
|
1579
1623
|
className: 'expl-btn',
|
|
1580
1624
|
onClick: function () { if (uploadInputEl) uploadInputEl.click() },
|
|
1581
1625
|
title: T('explorer.upload'),
|
|
1582
1626
|
'aria-label': T('explorer.upload'),
|
|
1583
1627
|
}, '⬆'),
|
|
1628
|
+
create('button', {
|
|
1629
|
+
className: 'expl-btn',
|
|
1630
|
+
onClick: function () { refreshAll() },
|
|
1631
|
+
title: T('explorer.refreshTitle'),
|
|
1632
|
+
'aria-label': T('explorer.refresh'),
|
|
1633
|
+
}, create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } })),
|
|
1584
1634
|
create('button', {
|
|
1585
1635
|
className: 'expl-btn',
|
|
1586
1636
|
onClick: function (event) {
|
|
1587
|
-
try {
|
|
1637
|
+
try {
|
|
1638
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
1639
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
1640
|
+
} catch (e) {}
|
|
1588
1641
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1589
1642
|
},
|
|
1590
1643
|
title: T('explorer.close'),
|
|
@@ -1615,6 +1668,16 @@ window.__ModuleLoader__.load({
|
|
|
1615
1668
|
container,
|
|
1616
1669
|
s.menu ? create('div', { className: 'expl-menu-mask', onMouseDown: function () { setState({ menu: null }) }, onContextMenu: function (e) { e.preventDefault(); setState({ menu: null }) } }) : null,
|
|
1617
1670
|
s.menu ? create(Menu, { menu: s.menu }) : null,
|
|
1671
|
+
s.uploadConflict ? create('div', { className: 'expl-confirm-mask', role: 'dialog', 'aria-modal': 'true' },
|
|
1672
|
+
create('div', { className: 'expl-confirm' },
|
|
1673
|
+
create('div', { className: 'expl-confirm-title' }, T('explorer.overwriteTitle')),
|
|
1674
|
+
create('div', { className: 'expl-confirm-text' }, T('explorer.overwriteConfirm', { name: s.uploadConflict.name })),
|
|
1675
|
+
create('div', { className: 'expl-confirm-actions' },
|
|
1676
|
+
create('button', { type: 'button', className: 'expl-confirm-btn', onClick: function () { s.uploadConflict.resolve(false); setState({ uploadConflict: null }) } }, T('explorer.cancel')),
|
|
1677
|
+
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')),
|
|
1678
|
+
),
|
|
1679
|
+
),
|
|
1680
|
+
) : null,
|
|
1618
1681
|
)
|
|
1619
1682
|
}
|
|
1620
1683
|
|
|
@@ -1631,7 +1694,15 @@ window.__ModuleLoader__.load({
|
|
|
1631
1694
|
return create('button', {
|
|
1632
1695
|
type: 'button',
|
|
1633
1696
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1634
|
-
onClick: function () {
|
|
1697
|
+
onClick: function () {
|
|
1698
|
+
try {
|
|
1699
|
+
if (layout && typeof layout.closeRightbar === 'function') {
|
|
1700
|
+
if (s.open) layout.closeRightbar()
|
|
1701
|
+
else layout.openRightbar(true, false)
|
|
1702
|
+
} else if (s.open) layout.closeDetails()
|
|
1703
|
+
else layout.openDetails()
|
|
1704
|
+
} catch (e) {}
|
|
1705
|
+
},
|
|
1635
1706
|
title: T('explorer.label'),
|
|
1636
1707
|
'aria-label': T('explorer.label'),
|
|
1637
1708
|
}, '📁')
|
|
@@ -1661,10 +1732,16 @@ window.__ModuleLoader__.load({
|
|
|
1661
1732
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1662
1733
|
onClick: function (event) {
|
|
1663
1734
|
if (s.open) {
|
|
1664
|
-
try {
|
|
1735
|
+
try {
|
|
1736
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
1737
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
1738
|
+
} catch (e) {}
|
|
1665
1739
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1666
1740
|
} else if (!setHeroDetailsWidth(event.currentTarget, DETAILS_DEFAULT)) {
|
|
1667
|
-
try {
|
|
1741
|
+
try {
|
|
1742
|
+
if (layout && typeof layout.openRightbar === 'function') layout.openRightbar(true, false)
|
|
1743
|
+
else if (layout && typeof layout.openDetails === 'function') layout.openDetails()
|
|
1744
|
+
} catch (e) {}
|
|
1668
1745
|
}
|
|
1669
1746
|
},
|
|
1670
1747
|
title: T('explorer.label'),
|
|
@@ -1719,6 +1796,12 @@ window.__ModuleLoader__.load({
|
|
|
1719
1796
|
} catch (_) {}
|
|
1720
1797
|
}
|
|
1721
1798
|
|
|
1799
|
+
function askOverwrite(name) {
|
|
1800
|
+
return new Promise(function (resolve) {
|
|
1801
|
+
setState({ uploadConflict: { name: name, resolve: resolve } })
|
|
1802
|
+
})
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1722
1805
|
function findFileEntry(path) {
|
|
1723
1806
|
for (var i = 0; i < state.files.list.length; i++) {
|
|
1724
1807
|
if (state.files.list[i].path === path) return state.files.list[i]
|
|
@@ -2972,6 +3055,30 @@ window.__ModuleLoader__.load({
|
|
|
2972
3055
|
var sessions = ctx.sessions
|
|
2973
3056
|
var remoteGoals = ctx.remote.goals
|
|
2974
3057
|
var disposers = []
|
|
3058
|
+
// 0.1.5's official Sidebar adds a header-corner expand button. Its
|
|
3059
|
+
// default handler opens the official surface, which would diverge from
|
|
3060
|
+
// dsh-soup's resource explorer. Route that button to the same layout
|
|
3061
|
+
// face used by our explorer controls instead.
|
|
3062
|
+
if (typeof document !== 'undefined') {
|
|
3063
|
+
var onOfficialExpand = function (event) {
|
|
3064
|
+
var target = event.target && event.target.closest
|
|
3065
|
+
? event.target.closest('[data-sidebar-right-expand]') : null
|
|
3066
|
+
if (!target) return
|
|
3067
|
+
event.preventDefault()
|
|
3068
|
+
if (typeof event.stopImmediatePropagation === 'function') event.stopImmediatePropagation()
|
|
3069
|
+
else event.stopPropagation()
|
|
3070
|
+
try {
|
|
3071
|
+
if (state.open) {
|
|
3072
|
+
if (layout && typeof layout.closeRightbar === 'function') layout.closeRightbar()
|
|
3073
|
+
else if (layout && typeof layout.closeDetails === 'function') layout.closeDetails()
|
|
3074
|
+
} else if (layout && typeof layout.openRightbar === 'function') {
|
|
3075
|
+
layout.openRightbar(true, false)
|
|
3076
|
+
} else if (layout && typeof layout.openDetails === 'function') layout.openDetails()
|
|
3077
|
+
} catch (e) {}
|
|
3078
|
+
}
|
|
3079
|
+
document.addEventListener('click', onOfficialExpand, true)
|
|
3080
|
+
disposers.push(function () { document.removeEventListener('click', onOfficialExpand, true) })
|
|
3081
|
+
}
|
|
2975
3082
|
// i18n:注册双语词典;模块级 T 供组件外回调(菜单/错误横幅)取当前语言文案。
|
|
2976
3083
|
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
2977
3084
|
ctx.effect(function () { return ctx.locale.register(NS, DICT) }, 'dsh-soup: dictionaries')
|
|
@@ -3002,6 +3109,20 @@ window.__ModuleLoader__.load({
|
|
|
3002
3109
|
},
|
|
3003
3110
|
))
|
|
3004
3111
|
})
|
|
3112
|
+
// DSH 0.1.5 moved the right column from the legacy `details` seat to
|
|
3113
|
+
// `rightbar`. Register at a lower priority so dsh-soup remains the
|
|
3114
|
+
// occupant when the official Sidebar plugin is present.
|
|
3115
|
+
slots.inject('rightbar', function () {
|
|
3116
|
+
disposers.push(slots.register(
|
|
3117
|
+
{ name: 'rightbar', priority: -1, locale: NS },
|
|
3118
|
+
function (props) {
|
|
3119
|
+
return create(React.Fragment, null,
|
|
3120
|
+
create(Panel, props),
|
|
3121
|
+
create(PreviewOverlay),
|
|
3122
|
+
)
|
|
3123
|
+
},
|
|
3124
|
+
))
|
|
3125
|
+
})
|
|
3005
3126
|
// 文件标签页:与 对话/轨迹 同级的原生 view tab(ui-trajectory 同款注册方式)。
|
|
3006
3127
|
// 槽位 entry 是静态的,多文件由 FilesView 内部子 tab 条管理。
|
|
3007
3128
|
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
3
|
"description": "DSH 项目资源管理器:右侧 details 列文件树(并列网格、宽度对齐宿主 details 合同 300-520px),有 Session log 时在其右侧、空白新会话在输入框上方右端提供 📁 切换按钮;支持多选、双击展开/打开、右键菜单(打开/系统打开/重命名/废纸篓/新建/复制路径)、拖拽移动、上传。文件「打开」在会话区新增「文件」标签页(原生 conversation.view,与对话/轨迹同级):文本可编辑 ⌘S 保存、图片预览、二进制提示。附带模型吞吐徽标:输入框正上方显示「正在等待模型...」与彩色 t/s 徽标(数据来自 llm/stream 真实流)。附带 GoalBar 多行展示:把原生单行截断 GoalBar 放开为多行完整显示,编辑用 textarea,工具与数据仍走原生 goal。",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|