@lyhue1991/dsh-soup 0.4.11 → 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/lib/client.js +113 -9
- package/lib/index.js +12 -4
- package/package.json +1 -1
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,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;}' +
|
|
@@ -260,6 +267,10 @@ window.__ModuleLoader__.load({
|
|
|
260
267
|
'explorer.uploading': '上传中 ',
|
|
261
268
|
'explorer.downloading': '下载中 ',
|
|
262
269
|
'explorer.uploaded': '已上传 {n} 个文件',
|
|
270
|
+
'explorer.overwriteConfirm': '文件“{name}”已存在,是否覆盖?',
|
|
271
|
+
'explorer.overwriteTitle': '确认覆盖',
|
|
272
|
+
'explorer.overwrite': '覆盖',
|
|
273
|
+
'explorer.cancel': '取消',
|
|
263
274
|
'explorer.filesCount': '{n} 个文件',
|
|
264
275
|
'explorer.filesCountPartial': '{ok}/{total} 个文件',
|
|
265
276
|
'explorer.downloaded': '已下载 {n} 个文件',
|
|
@@ -324,6 +335,10 @@ window.__ModuleLoader__.load({
|
|
|
324
335
|
'explorer.uploading': 'Uploading ',
|
|
325
336
|
'explorer.downloading': 'Downloading ',
|
|
326
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',
|
|
327
342
|
'explorer.filesCount': '{n} file(s)',
|
|
328
343
|
'explorer.filesCountPartial': '{ok}/{total} file(s)',
|
|
329
344
|
'explorer.downloaded': 'Downloaded {n} file(s)',
|
|
@@ -387,6 +402,7 @@ window.__ModuleLoader__.load({
|
|
|
387
402
|
dropTarget: null, dragPaths: null,
|
|
388
403
|
files: { list: [], active: null, overlay: false, overlayMax: false, overlayReturn: null },
|
|
389
404
|
uploads: [],
|
|
405
|
+
uploadConflict: null,
|
|
390
406
|
}
|
|
391
407
|
var listeners = new Set()
|
|
392
408
|
// trackSession() is called by both the hero toggle and the details panel
|
|
@@ -1140,9 +1156,12 @@ window.__ModuleLoader__.load({
|
|
|
1140
1156
|
}
|
|
1141
1157
|
|
|
1142
1158
|
function finishUploads(count, okCount, failedName, down) {
|
|
1143
|
-
|
|
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 })
|
|
1144
1163
|
setUploadEntry({
|
|
1145
|
-
name: '✓ ' + verb +
|
|
1164
|
+
name: '✓ ' + verb + suffix + (failedName ? '(' + failedName + ')' : ''),
|
|
1146
1165
|
loaded: 0, total: 0, idx: 0, totalFiles: 0, done: true, down: !!down,
|
|
1147
1166
|
})
|
|
1148
1167
|
window.setTimeout(function () { setState({ uploads: [] }) }, 1600)
|
|
@@ -1157,10 +1176,17 @@ window.__ModuleLoader__.load({
|
|
|
1157
1176
|
var start = c * UPLOAD_CHUNK_SIZE
|
|
1158
1177
|
var blob = f.slice(start, Math.min(f.size, start + UPLOAD_CHUNK_SIZE))
|
|
1159
1178
|
var b64 = await fileToBase64(blob)
|
|
1160
|
-
var
|
|
1179
|
+
var args = {
|
|
1161
1180
|
dir: dir, name: f.name, data: b64,
|
|
1162
1181
|
chunk: chunked ? c + 1 : undefined,
|
|
1163
|
-
}
|
|
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
|
+
}
|
|
1164
1190
|
if (!res || !res.ok) throw new Error((res && res.error) || T('explorer.uploadFail'))
|
|
1165
1191
|
updateUpload(f.name, Math.min(f.size, start + UPLOAD_CHUNK_SIZE), f.size, idx, totalFiles)
|
|
1166
1192
|
}
|
|
@@ -1492,6 +1518,13 @@ window.__ModuleLoader__.load({
|
|
|
1492
1518
|
var last = parseFloat(parts[parts.length - 1])
|
|
1493
1519
|
if (isNaN(last) || last <= 1) { setState({ open: false }); return }
|
|
1494
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
|
+
}
|
|
1495
1528
|
if (Math.round(last) !== Math.round(widthRef.current)) {
|
|
1496
1529
|
// 宿主是宽度偏好的权威(/native drag/窗口求解都可能改它)。
|
|
1497
1530
|
// 插件只同步基准,不能把宿主拖出来的宽度强行拉回旧默认。
|
|
@@ -1584,7 +1617,10 @@ window.__ModuleLoader__.load({
|
|
|
1584
1617
|
create('button', {
|
|
1585
1618
|
className: 'expl-btn',
|
|
1586
1619
|
onClick: function (event) {
|
|
1587
|
-
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) {}
|
|
1588
1624
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1589
1625
|
},
|
|
1590
1626
|
title: T('explorer.close'),
|
|
@@ -1615,6 +1651,16 @@ window.__ModuleLoader__.load({
|
|
|
1615
1651
|
container,
|
|
1616
1652
|
s.menu ? create('div', { className: 'expl-menu-mask', onMouseDown: function () { setState({ menu: null }) }, onContextMenu: function (e) { e.preventDefault(); setState({ menu: null }) } }) : null,
|
|
1617
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,
|
|
1618
1664
|
)
|
|
1619
1665
|
}
|
|
1620
1666
|
|
|
@@ -1631,7 +1677,15 @@ window.__ModuleLoader__.load({
|
|
|
1631
1677
|
return create('button', {
|
|
1632
1678
|
type: 'button',
|
|
1633
1679
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1634
|
-
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
|
+
},
|
|
1635
1689
|
title: T('explorer.label'),
|
|
1636
1690
|
'aria-label': T('explorer.label'),
|
|
1637
1691
|
}, '📁')
|
|
@@ -1661,10 +1715,16 @@ window.__ModuleLoader__.load({
|
|
|
1661
1715
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1662
1716
|
onClick: function (event) {
|
|
1663
1717
|
if (s.open) {
|
|
1664
|
-
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) {}
|
|
1665
1722
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1666
1723
|
} else if (!setHeroDetailsWidth(event.currentTarget, DETAILS_DEFAULT)) {
|
|
1667
|
-
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) {}
|
|
1668
1728
|
}
|
|
1669
1729
|
},
|
|
1670
1730
|
title: T('explorer.label'),
|
|
@@ -1719,6 +1779,12 @@ window.__ModuleLoader__.load({
|
|
|
1719
1779
|
} catch (_) {}
|
|
1720
1780
|
}
|
|
1721
1781
|
|
|
1782
|
+
function askOverwrite(name) {
|
|
1783
|
+
return new Promise(function (resolve) {
|
|
1784
|
+
setState({ uploadConflict: { name: name, resolve: resolve } })
|
|
1785
|
+
})
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1722
1788
|
function findFileEntry(path) {
|
|
1723
1789
|
for (var i = 0; i < state.files.list.length; i++) {
|
|
1724
1790
|
if (state.files.list[i].path === path) return state.files.list[i]
|
|
@@ -2972,6 +3038,30 @@ window.__ModuleLoader__.load({
|
|
|
2972
3038
|
var sessions = ctx.sessions
|
|
2973
3039
|
var remoteGoals = ctx.remote.goals
|
|
2974
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
|
+
}
|
|
2975
3065
|
// i18n:注册双语词典;模块级 T 供组件外回调(菜单/错误横幅)取当前语言文案。
|
|
2976
3066
|
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
2977
3067
|
ctx.effect(function () { return ctx.locale.register(NS, DICT) }, 'dsh-soup: dictionaries')
|
|
@@ -3002,6 +3092,20 @@ window.__ModuleLoader__.load({
|
|
|
3002
3092
|
},
|
|
3003
3093
|
))
|
|
3004
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
|
+
})
|
|
3005
3109
|
// 文件标签页:与 对话/轨迹 同级的原生 view tab(ui-trajectory 同款注册方式)。
|
|
3006
3110
|
// 槽位 entry 是静态的,多文件由 FilesView 内部子 tab 条管理。
|
|
3007
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
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.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|