@lyhue1991/dsh-soup 0.3.0 → 0.4.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 +4 -1
- package/lib/client.js +264 -79
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,7 +97,10 @@ dsh plugin add @lyhue1991/dsh-soup
|
|
|
97
97
|
|
|
98
98
|
## 更新日志
|
|
99
99
|
|
|
100
|
-
- **0.
|
|
100
|
+
- **0.4.0(当前)** —
|
|
101
|
+
- **语言适配(i18n)**:dsh-soup 全部文案(资源管理器、预览页、右键菜单、上传/下载、速度徽标、GoalBar)跟随 DSH 通用设置的语言切换(zh/en),切换即时生效。
|
|
102
|
+
- **预览 tab 纯阅读模式**:激活预览时隐藏底部输入框、「N 轮 · M 步」统计行、GoalBar 与速度徽标——整屏干净内容区,切回对话/轨迹恢复。
|
|
103
|
+
- **0.3.0** —
|
|
101
104
|
- **Markdown 预览渲染相对路径图片**:`` 经同源 `/api/dsh-soup/img` 端点内联加载(confine 围栏 + 仅图片类型 + 拒跨站)。
|
|
102
105
|
- **自动刷新**:资源管理器与预览页每 3 秒轻量探测目录/文件签名(mtime),变了才重拉;面板关闭且无预览时零开销;宿主不可达时指数退避(封顶 60s)。
|
|
103
106
|
- **顶部 ⟳ 刷新按钮**:重拉整棵树并保持已展开子目录不收起;空白右键「刷新」项同步升级为同一枚图标。
|
package/lib/client.js
CHANGED
|
@@ -180,7 +180,13 @@ window.__ModuleLoader__.load({
|
|
|
180
180
|
'.dfv-table-wrap{flex:1;min-height:0;overflow:auto;}' +
|
|
181
181
|
'.dfv-table{border-collapse:collapse;font-size:12px;font-family:var(--ds-font-family-code);}' +
|
|
182
182
|
'.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
|
-
'.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;}'
|
|
183
|
+
'.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;}' +
|
|
184
|
+
// 纯阅读模式:预览 tab 激活时隐藏整个输入区(composerSeat:输入框 +
|
|
185
|
+
// GoalBar + 速度徽标所在的 input.dock)与轮次统计行(StatsLine)。
|
|
186
|
+
// class 来自 DSH 客户端编译产物(CSS module hash),升级后若失效仅
|
|
187
|
+
// 表现为「预览页仍显示输入区」,不影响其它功能——属可接受的降级。
|
|
188
|
+
'[data-dsh-soup-preview="1"] .wSkVaW_composerSeat{display:none !important;}' +
|
|
189
|
+
'[data-dsh-soup-preview="1"] .FJxK0a_root{display:none !important;}'
|
|
184
190
|
if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="@lyhue1991/dsh-soup/fileview.css"]') === null) {
|
|
185
191
|
var fileviewTag = document.createElement('style')
|
|
186
192
|
fileviewTag.dataset.plugin = '@lyhue1991/dsh-soup'
|
|
@@ -189,8 +195,146 @@ window.__ModuleLoader__.load({
|
|
|
189
195
|
document.head.appendChild(fileviewTag)
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
/** 需要的客户端服务:slots、layout、timer、sessions
|
|
193
|
-
var inject = ['slots', 'layout', 'timer', 'sessions', 'remote', 'remote.goals']
|
|
198
|
+
/** 需要的客户端服务:slots、layout、timer、sessions、locale(i18n 词典)+ remote.goals(goal 动作动词)。 */
|
|
199
|
+
var inject = ['slots', 'layout', 'timer', 'sessions', 'locale', 'remote', 'remote.goals']
|
|
200
|
+
|
|
201
|
+
// ------------------------------------------------------------------
|
|
202
|
+
// i18n:走 DSH 官方插件词典机制(ui-cordis / ui-conversation 同款)——
|
|
203
|
+
// ctx.locale.register(NS, DICT) 注册;槽位配置加 locale: NS 后组件 props
|
|
204
|
+
// 获得 t(语言切换铸造新 t 引用,靠浅比较自动重渲染整棵子树);组件外
|
|
205
|
+
// 回调(菜单 label、错误横幅)用模块级 T(bind 的 t 每次调用解析当前语言)。
|
|
206
|
+
// ------------------------------------------------------------------
|
|
207
|
+
var NS = 'dsh-soup'
|
|
208
|
+
var DICT = {
|
|
209
|
+
zh: {
|
|
210
|
+
'explorer.title': '📁 资源管理器',
|
|
211
|
+
'explorer.label': '资源管理器',
|
|
212
|
+
'explorer.refresh': '刷新',
|
|
213
|
+
'explorer.refreshTitle': '刷新(保持已展开目录)',
|
|
214
|
+
'explorer.upload': '上传文件',
|
|
215
|
+
'explorer.close': '关闭',
|
|
216
|
+
'explorer.closePanel': '关闭资源管理器',
|
|
217
|
+
'explorer.emptyDir': '(空目录)',
|
|
218
|
+
'explorer.bulkSelected': '已选 {n} 项 · 拖到文件夹可移动',
|
|
219
|
+
'explorer.readFail': '读取失败',
|
|
220
|
+
'explorer.actionFail': '失败',
|
|
221
|
+
'explorer.uploading': '上传中 ',
|
|
222
|
+
'explorer.downloading': '下载中 ',
|
|
223
|
+
'explorer.uploaded': '已上传 {n} 个文件',
|
|
224
|
+
'explorer.filesCount': '{n} 个文件',
|
|
225
|
+
'explorer.filesCountPartial': '{ok}/{total} 个文件',
|
|
226
|
+
'explorer.downloaded': '已下载 {n} 个文件',
|
|
227
|
+
'explorer.uploadFail': '上传失败',
|
|
228
|
+
'explorer.downloadFail': '下载失败',
|
|
229
|
+
'explorer.downloadFailWith': '下载失败: {reason}',
|
|
230
|
+
'explorer.downloadStart': '已开始下载 {name}(大文件,由浏览器接管)',
|
|
231
|
+
'explorer.renamed': '已复制路径',
|
|
232
|
+
'explorer.renameFail': '重命名失败',
|
|
233
|
+
'explorer.createFail': '创建失败',
|
|
234
|
+
'explorer.moveIntoSelf': '不能把文件夹移入自身: {path}',
|
|
235
|
+
'explorer.hostUnreachable': '宿主不可达: {reason}',
|
|
236
|
+
'explorer.hostBadResponse': '宿主响应解析失败',
|
|
237
|
+
'menu.preview': '👁 预览',
|
|
238
|
+
'menu.open': '🖥 用系统打开',
|
|
239
|
+
'menu.rename': '✎ 重命名',
|
|
240
|
+
'menu.trash': '🗑 移到废纸篓',
|
|
241
|
+
'menu.trashMulti': '🗑 移到废纸篓 ({n} 项)',
|
|
242
|
+
'menu.newFile': '📄 新建文件',
|
|
243
|
+
'menu.newFolder': '📁 新建文件夹',
|
|
244
|
+
'menu.copyPath': '⧉ 复制路径',
|
|
245
|
+
'menu.download': '⬇ 下载',
|
|
246
|
+
'menu.deselect': '取消选择',
|
|
247
|
+
'files.tab': '预览',
|
|
248
|
+
'files.emptyHint': '在右侧资源管理器中双击或右键「预览」文件',
|
|
249
|
+
'files.loading': '加载中…',
|
|
250
|
+
'files.readFail': '读取文件失败',
|
|
251
|
+
'files.reload': '重载',
|
|
252
|
+
'files.reloadTitle': '从磁盘重新读取(刷新预览)',
|
|
253
|
+
'files.binary': '二进制文件({size}),请在资源管理器中用系统打开',
|
|
254
|
+
'files.imageTooLarge': '图片过大({size},上限 {limit})',
|
|
255
|
+
'files.pdfTooLarge': 'PDF 过大({size},上限 {limit}),请用系统打开',
|
|
256
|
+
'files.truncated': '文件超过 2 MB,仅预览前 2 MB;完整内容请用系统编辑器打开。',
|
|
257
|
+
'files.jsonFail': 'JSON 解析失败,按纯文本显示',
|
|
258
|
+
'files.nbFail': 'ipynb 解析失败,按纯文本显示',
|
|
259
|
+
'files.nbEmpty': '(空笔记本)',
|
|
260
|
+
'files.nbCells': '共 {n} 个单元格',
|
|
261
|
+
'files.csvTruncated': '共 {total} 行,仅预览前 {n} 行',
|
|
262
|
+
'files.htmlFrame': 'HTML 预览',
|
|
263
|
+
'files.pdfFrame': 'PDF 预览',
|
|
264
|
+
'files.pdfLoading': 'PDF 加载中…',
|
|
265
|
+
'files.pdfFail': 'PDF 预览失败: {reason}',
|
|
266
|
+
'files.pdfUnsupported': '当前浏览器无法内嵌预览 PDF,请右键用系统打开。',
|
|
267
|
+
'files.nbHtmlOut': 'HTML 输出',
|
|
268
|
+
'speed.label': '速度徽标',
|
|
269
|
+
'speed.waiting': '正在等待模型',
|
|
270
|
+
'speed.operateFail': '操作失败',
|
|
271
|
+
},
|
|
272
|
+
en: {
|
|
273
|
+
'explorer.title': '📁 Explorer',
|
|
274
|
+
'explorer.label': 'Explorer',
|
|
275
|
+
'explorer.refresh': 'Refresh',
|
|
276
|
+
'explorer.refreshTitle': 'Refresh (keeps expanded folders)',
|
|
277
|
+
'explorer.upload': 'Upload files',
|
|
278
|
+
'explorer.close': 'Close',
|
|
279
|
+
'explorer.closePanel': 'Close explorer',
|
|
280
|
+
'explorer.emptyDir': '(empty)',
|
|
281
|
+
'explorer.bulkSelected': '{n} selected · drag onto a folder to move',
|
|
282
|
+
'explorer.readFail': 'Failed to read',
|
|
283
|
+
'explorer.actionFail': 'Failed',
|
|
284
|
+
'explorer.uploading': 'Uploading ',
|
|
285
|
+
'explorer.downloading': 'Downloading ',
|
|
286
|
+
'explorer.uploaded': 'Uploaded {n} file(s)',
|
|
287
|
+
'explorer.filesCount': '{n} file(s)',
|
|
288
|
+
'explorer.filesCountPartial': '{ok}/{total} file(s)',
|
|
289
|
+
'explorer.downloaded': 'Downloaded {n} file(s)',
|
|
290
|
+
'explorer.uploadFail': 'Upload failed',
|
|
291
|
+
'explorer.downloadFail': 'Download failed',
|
|
292
|
+
'explorer.downloadFailWith': 'Download failed: {reason}',
|
|
293
|
+
'explorer.downloadStart': 'Downloading {name} (large file, handled by the browser)',
|
|
294
|
+
'explorer.renamed': 'Path copied',
|
|
295
|
+
'explorer.renameFail': 'Rename failed',
|
|
296
|
+
'explorer.createFail': 'Create failed',
|
|
297
|
+
'explorer.moveIntoSelf': 'Cannot move a folder into itself: {path}',
|
|
298
|
+
'explorer.hostUnreachable': 'Host unreachable: {reason}',
|
|
299
|
+
'explorer.hostBadResponse': 'Failed to parse host response',
|
|
300
|
+
'menu.preview': '👁 Preview',
|
|
301
|
+
'menu.open': '🖥 Open with system',
|
|
302
|
+
'menu.rename': '✎ Rename',
|
|
303
|
+
'menu.trash': '🗑 Move to trash',
|
|
304
|
+
'menu.trashMulti': '🗑 Move to trash ({n})',
|
|
305
|
+
'menu.newFile': '📄 New file',
|
|
306
|
+
'menu.newFolder': '📁 New folder',
|
|
307
|
+
'menu.copyPath': '⧉ Copy path',
|
|
308
|
+
'menu.download': '⬇ Download',
|
|
309
|
+
'menu.deselect': 'Clear selection',
|
|
310
|
+
'files.tab': 'Preview',
|
|
311
|
+
'files.emptyHint': 'Double-click a file (or right-click → Preview) in the explorer',
|
|
312
|
+
'files.loading': 'Loading…',
|
|
313
|
+
'files.readFail': 'Failed to read file',
|
|
314
|
+
'files.reload': 'Reload',
|
|
315
|
+
'files.reloadTitle': 'Re-read from disk (refresh preview)',
|
|
316
|
+
'files.binary': 'Binary file ({size}) — open with the system explorer',
|
|
317
|
+
'files.imageTooLarge': 'Image too large ({size}, limit {limit})',
|
|
318
|
+
'files.pdfTooLarge': 'PDF too large ({size}, limit {limit}) — open with the system viewer',
|
|
319
|
+
'files.truncated': 'File exceeds 2 MB — showing the first 2 MB; open in a system editor for the full content.',
|
|
320
|
+
'files.jsonFail': 'JSON parse failed — shown as plain text',
|
|
321
|
+
'files.nbFail': 'ipynb parse failed — shown as plain text',
|
|
322
|
+
'files.nbEmpty': '(empty notebook)',
|
|
323
|
+
'files.nbCells': '{n} cells',
|
|
324
|
+
'files.csvTruncated': '{total} rows — showing the first {n}',
|
|
325
|
+
'files.htmlFrame': 'HTML preview',
|
|
326
|
+
'files.pdfFrame': 'PDF preview',
|
|
327
|
+
'files.pdfLoading': 'Loading PDF…',
|
|
328
|
+
'files.pdfFail': 'PDF preview failed: {reason}',
|
|
329
|
+
'files.pdfUnsupported': 'This browser cannot embed PDF previews — right-click and open with the system viewer.',
|
|
330
|
+
'files.nbHtmlOut': 'HTML output',
|
|
331
|
+
'speed.label': 'Speed badge',
|
|
332
|
+
'speed.waiting': 'Waiting for the model',
|
|
333
|
+
'speed.operateFail': 'Action failed',
|
|
334
|
+
},
|
|
335
|
+
}
|
|
336
|
+
/** 模块级翻译函数(apply 里由 ctx.locale.bind 填充)。 */
|
|
337
|
+
var T = function (key, params) { return key }
|
|
194
338
|
|
|
195
339
|
// ------------------------------------------------------------------
|
|
196
340
|
// 状态存储(模块级,供 Panel 与 HeaderAction 共享)
|
|
@@ -254,9 +398,9 @@ window.__ModuleLoader__.load({
|
|
|
254
398
|
headers: { 'content-type': 'application/json', 'x-dsh-soup': '1' },
|
|
255
399
|
body: JSON.stringify({ action: action, args: args || {} }),
|
|
256
400
|
}).then(function (res) {
|
|
257
|
-
return res.json().catch(function () { return { ok: false, error: '
|
|
401
|
+
return res.json().catch(function () { return { ok: false, error: T('explorer.hostBadResponse') } })
|
|
258
402
|
}).catch(function (err) {
|
|
259
|
-
return { ok: false, error: '
|
|
403
|
+
return { ok: false, error: T('explorer.hostUnreachable', { reason: String((err && err.message) || err) }) }
|
|
260
404
|
})
|
|
261
405
|
}
|
|
262
406
|
|
|
@@ -268,7 +412,7 @@ window.__ModuleLoader__.load({
|
|
|
268
412
|
return loadDir(path, (attempt || 0) + 1)
|
|
269
413
|
}
|
|
270
414
|
if (!res || !res.ok) {
|
|
271
|
-
setState({ error: (res && res.error) || '
|
|
415
|
+
setState({ error: (res && res.error) || T('explorer.readFail') })
|
|
272
416
|
return null
|
|
273
417
|
}
|
|
274
418
|
return res.entries || []
|
|
@@ -599,7 +743,7 @@ window.__ModuleLoader__.load({
|
|
|
599
743
|
var errors = []
|
|
600
744
|
for (var i = 0; i < paths.length; i++) {
|
|
601
745
|
var res = await rpc('trash', { path: paths[i] })
|
|
602
|
-
if (!res || !res.ok) errors.push(baseName(paths[i]) + ': ' + ((res && res.error) || '
|
|
746
|
+
if (!res || !res.ok) errors.push(baseName(paths[i]) + ': ' + ((res && res.error) || T('explorer.actionFail')) + ((res && res.hint) ? '(' + res.hint + ')' : ''))
|
|
603
747
|
}
|
|
604
748
|
if (errors.length) setState({ error: errors.join(';') })
|
|
605
749
|
setState({ selected: new Set(), lastIndex: null })
|
|
@@ -620,7 +764,7 @@ window.__ModuleLoader__.load({
|
|
|
620
764
|
setState({ renaming: null, selected: sel })
|
|
621
765
|
;(async function () {
|
|
622
766
|
var res = await rpc('move', { from: path, to: dest })
|
|
623
|
-
if (!res || !res.ok) setState({ error: (res && res.error) || '
|
|
767
|
+
if (!res || !res.ok) setState({ error: (res && res.error) || T('explorer.renameFail') })
|
|
624
768
|
await refresh()
|
|
625
769
|
})()
|
|
626
770
|
}
|
|
@@ -641,7 +785,7 @@ window.__ModuleLoader__.load({
|
|
|
641
785
|
setState({ newItem: null })
|
|
642
786
|
;(async function () {
|
|
643
787
|
var res = await rpc('create', { dir: parent, name: name, isDir: isDir })
|
|
644
|
-
if (!res || !res.ok) setState({ error: (res && res.error) || '
|
|
788
|
+
if (!res || !res.ok) setState({ error: (res && res.error) || T('explorer.createFail') })
|
|
645
789
|
await refresh()
|
|
646
790
|
})()
|
|
647
791
|
}
|
|
@@ -661,7 +805,7 @@ window.__ModuleLoader__.load({
|
|
|
661
805
|
setState({ menu: null })
|
|
662
806
|
try {
|
|
663
807
|
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
|
|
664
|
-
navigator.clipboard.writeText(p).then(function () { showNotice('
|
|
808
|
+
navigator.clipboard.writeText(p).then(function () { showNotice(T('explorer.renamed')) }).catch(function () {})
|
|
665
809
|
}
|
|
666
810
|
} catch (err) {}
|
|
667
811
|
}
|
|
@@ -703,9 +847,9 @@ window.__ModuleLoader__.load({
|
|
|
703
847
|
var name = baseName(p)
|
|
704
848
|
var dest = pathJoin(targetDir, name)
|
|
705
849
|
if (dest === p) continue
|
|
706
|
-
if (targetDir === p || targetDir.startsWith(p + '/')) { errors.push('
|
|
850
|
+
if (targetDir === p || targetDir.startsWith(p + '/')) { errors.push(T('explorer.moveIntoSelf', { path: name })); continue }
|
|
707
851
|
var res = await rpc('move', { from: p, to: dest })
|
|
708
|
-
if (!res || !res.ok) errors.push(name + ': ' + ((res && res.error) || '
|
|
852
|
+
if (!res || !res.ok) errors.push(name + ': ' + ((res && res.error) || T('explorer.actionFail')))
|
|
709
853
|
}
|
|
710
854
|
setState({ dragPaths: null })
|
|
711
855
|
if (errors.length) setState({ error: errors.join(';') })
|
|
@@ -716,7 +860,7 @@ window.__ModuleLoader__.load({
|
|
|
716
860
|
return new Promise(function (resolve, reject) {
|
|
717
861
|
var r = new FileReader()
|
|
718
862
|
r.onload = function () { var s = String(r.result || ''); var i = s.indexOf(','); resolve(i >= 0 ? s.slice(i + 1) : s) }
|
|
719
|
-
r.onerror = function () { reject(new Error('
|
|
863
|
+
r.onerror = function () { reject(new Error(T('files.readFail'))) }
|
|
720
864
|
r.readAsDataURL(file)
|
|
721
865
|
})
|
|
722
866
|
}
|
|
@@ -739,9 +883,9 @@ window.__ModuleLoader__.load({
|
|
|
739
883
|
}
|
|
740
884
|
|
|
741
885
|
function finishUploads(count, okCount, failedName, down) {
|
|
742
|
-
var verb = down ? '
|
|
886
|
+
var verb = down ? T('explorer.downloaded') : T('explorer.uploaded')
|
|
743
887
|
setUploadEntry({
|
|
744
|
-
name: '✓ ' + verb + ' ' + (okCount === count ?
|
|
888
|
+
name: '✓ ' + verb + ' ' + (okCount === count ? T('explorer.filesCount', { n: count }) : T('explorer.filesCountPartial', { ok: okCount, total: count }) + (failedName ? '(' + failedName + ')' : '')),
|
|
745
889
|
loaded: 0, total: 0, idx: 0, totalFiles: 0, done: true, down: !!down,
|
|
746
890
|
})
|
|
747
891
|
window.setTimeout(function () { setState({ uploads: [] }) }, 1600)
|
|
@@ -760,7 +904,7 @@ window.__ModuleLoader__.load({
|
|
|
760
904
|
dir: dir, name: f.name, data: b64,
|
|
761
905
|
chunk: chunked ? c + 1 : undefined,
|
|
762
906
|
})
|
|
763
|
-
if (!res || !res.ok) throw new Error((res && res.error) || '
|
|
907
|
+
if (!res || !res.ok) throw new Error((res && res.error) || T('explorer.uploadFail'))
|
|
764
908
|
updateUpload(f.name, Math.min(f.size, start + UPLOAD_CHUNK_SIZE), f.size, idx, totalFiles)
|
|
765
909
|
}
|
|
766
910
|
}
|
|
@@ -794,7 +938,7 @@ window.__ModuleLoader__.load({
|
|
|
794
938
|
res = await rpc('download', { path: path, sessionId: activeSessionId })
|
|
795
939
|
}
|
|
796
940
|
if (!res || !res.ok) {
|
|
797
|
-
setState({ error: (res && res.error) || '
|
|
941
|
+
setState({ error: (res && res.error) || T('explorer.downloadFail') })
|
|
798
942
|
return
|
|
799
943
|
}
|
|
800
944
|
var name = res.name || baseName(path)
|
|
@@ -807,7 +951,7 @@ window.__ModuleLoader__.load({
|
|
|
807
951
|
document.body.appendChild(a)
|
|
808
952
|
a.click()
|
|
809
953
|
a.remove()
|
|
810
|
-
showNotice('
|
|
954
|
+
showNotice(T('explorer.downloadStart', { name: name }))
|
|
811
955
|
return
|
|
812
956
|
}
|
|
813
957
|
// 中小文件:fetch 流式读字节计进度,完成后 Blob 另存(峰值≈文件体积)
|
|
@@ -836,7 +980,7 @@ window.__ModuleLoader__.load({
|
|
|
836
980
|
window.setTimeout(function () { URL.revokeObjectURL(blobUrl) }, 5000)
|
|
837
981
|
finishUploads(1, 1, '', true)
|
|
838
982
|
} catch (err) {
|
|
839
|
-
setState({ error: '
|
|
983
|
+
setState({ error: T('explorer.downloadFailWith', { reason: String((err && err.message) || err) }), uploads: [] })
|
|
840
984
|
}
|
|
841
985
|
}
|
|
842
986
|
|
|
@@ -852,7 +996,7 @@ window.__ModuleLoader__.load({
|
|
|
852
996
|
// markdown/html/json/csv 有专属渲染器;图片预览;其余按纯文本。
|
|
853
997
|
// 状态存模块级 store,切走 tab 再切回来内容不丢。
|
|
854
998
|
// ------------------------------------------------------------------
|
|
855
|
-
var FILES_TAB_LABEL = '
|
|
999
|
+
var FILES_TAB_LABEL = function () { return T('files.tab') }
|
|
856
1000
|
|
|
857
1001
|
/** 当前活跃会话:list/read/download 携带给宿主,供围栏直查该会话 cwd。 */
|
|
858
1002
|
var activeSessionId = null
|
|
@@ -901,7 +1045,7 @@ window.__ModuleLoader__.load({
|
|
|
901
1045
|
try {
|
|
902
1046
|
var tabs = document.querySelectorAll('[role="tab"]')
|
|
903
1047
|
for (var i = 0; i < tabs.length; i++) {
|
|
904
|
-
if (tabs[i].textContent === FILES_TAB_LABEL) { tabs[i].click(); return true }
|
|
1048
|
+
if (tabs[i].textContent === FILES_TAB_LABEL()) { tabs[i].click(); return true }
|
|
905
1049
|
}
|
|
906
1050
|
} catch (err) {}
|
|
907
1051
|
return false
|
|
@@ -939,7 +1083,7 @@ window.__ModuleLoader__.load({
|
|
|
939
1083
|
if (!cur) return
|
|
940
1084
|
cur.loading = false
|
|
941
1085
|
if (!res || !res.ok) {
|
|
942
|
-
cur.error = (res && res.error) || '
|
|
1086
|
+
cur.error = (res && res.error) || T('explorer.readFail')
|
|
943
1087
|
} else {
|
|
944
1088
|
cur.error = ''
|
|
945
1089
|
cur.loaded = true
|
|
@@ -1071,7 +1215,7 @@ window.__ModuleLoader__.load({
|
|
|
1071
1215
|
}
|
|
1072
1216
|
if (isDir && node.open) {
|
|
1073
1217
|
if (node.loading) {
|
|
1074
|
-
children.push(create('div', { key: '__loading__', className: 'expl-muted', style: { paddingLeft: 8 + (depth + 1) * 16 } }, '
|
|
1218
|
+
children.push(create('div', { key: '__loading__', className: 'expl-muted', style: { paddingLeft: 8 + (depth + 1) * 16 } }, T('files.loading')))
|
|
1075
1219
|
} else {
|
|
1076
1220
|
;(node.children || []).forEach(function (c) { children.push(create(Row, { key: c.path, node: c, depth: depth + 1 })) })
|
|
1077
1221
|
}
|
|
@@ -1086,29 +1230,29 @@ window.__ModuleLoader__.load({
|
|
|
1086
1230
|
if (node) {
|
|
1087
1231
|
if (!multi) {
|
|
1088
1232
|
if (node.type !== 'directory') {
|
|
1089
|
-
items.push({ key: 'open-tab', label: '
|
|
1090
|
-
items.push({ key: 'open', label: '
|
|
1233
|
+
items.push({ key: 'open-tab', label: T('menu.preview'), onClick: function () { openFileInTab(node.path) } })
|
|
1234
|
+
items.push({ key: 'open', label: T('menu.open'), onClick: openSelection, separatorAfter: true })
|
|
1091
1235
|
} else {
|
|
1092
|
-
items.push({ key: 'open', label: '
|
|
1236
|
+
items.push({ key: 'open', label: T('menu.open'), onClick: openSelection, separatorAfter: true })
|
|
1093
1237
|
}
|
|
1094
|
-
items.push({ key: 'rename', label: '
|
|
1095
|
-
items.push({ key: 'trash', label: '
|
|
1238
|
+
items.push({ key: 'rename', label: T('menu.rename'), onClick: function () { startRename(node) } })
|
|
1239
|
+
items.push({ key: 'trash', label: T('menu.trash'), danger: true, onClick: trashSelection, separatorAfter: true })
|
|
1096
1240
|
} else {
|
|
1097
|
-
items.push({ key: 'trash', label: '
|
|
1241
|
+
items.push({ key: 'trash', label: T('menu.trashMulti', { n: state.selected.size }), danger: true, onClick: trashSelection, separatorAfter: true })
|
|
1098
1242
|
}
|
|
1099
1243
|
if (node.type === 'directory') {
|
|
1100
|
-
items.push({ key: 'newfile', label: '
|
|
1101
|
-
items.push({ key: 'newfolder', label: '
|
|
1244
|
+
items.push({ key: 'newfile', label: T('menu.newFile'), onClick: function () { startNew(node.path, false) } })
|
|
1245
|
+
items.push({ key: 'newfolder', label: T('menu.newFolder'), onClick: function () { startNew(node.path, true) }, separatorAfter: true })
|
|
1102
1246
|
}
|
|
1103
|
-
items.push({ key: 'copy', label: '
|
|
1247
|
+
items.push({ key: 'copy', label: T('menu.copyPath'), onClick: function () { copyPath(node) } })
|
|
1104
1248
|
if (node.type !== 'directory') {
|
|
1105
|
-
items.push({ key: 'download', label: '
|
|
1249
|
+
items.push({ key: 'download', label: T('menu.download'), onClick: function () { downloadFile(node.path) } })
|
|
1106
1250
|
}
|
|
1107
1251
|
} else {
|
|
1108
|
-
items.push({ key: 'newfile', label: '
|
|
1109
|
-
items.push({ key: 'newfolder', label: '
|
|
1110
|
-
items.push({ key: 'refresh', label: [create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }), '
|
|
1111
|
-
if (state.selected.size > 0) items.push({ key: 'none', label: '
|
|
1252
|
+
items.push({ key: 'newfile', label: T('menu.newFile'), onClick: function () { startNew(state.cwd, false) } })
|
|
1253
|
+
items.push({ key: 'newfolder', label: T('menu.newFolder'), onClick: function () { startNew(state.cwd, true) } })
|
|
1254
|
+
items.push({ key: 'refresh', label: [create('span', { className: 'expl-menu-ico', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }), T('explorer.refresh')], onClick: refresh, separatorAfter: true })
|
|
1255
|
+
if (state.selected.size > 0) items.push({ key: 'none', label: T('menu.deselect'), onClick: function () { setState({ selected: new Set(), lastIndex: null }) } })
|
|
1112
1256
|
}
|
|
1113
1257
|
return items
|
|
1114
1258
|
}
|
|
@@ -1306,7 +1450,7 @@ window.__ModuleLoader__.load({
|
|
|
1306
1450
|
className: 'dfv-frame',
|
|
1307
1451
|
sandbox: 'allow-scripts allow-popups allow-forms allow-modals',
|
|
1308
1452
|
referrerPolicy: 'no-referrer',
|
|
1309
|
-
title: props.title || '
|
|
1453
|
+
title: props.title || T('files.htmlFrame'),
|
|
1310
1454
|
srcDoc: props.text,
|
|
1311
1455
|
})
|
|
1312
1456
|
}
|
|
@@ -1335,14 +1479,14 @@ window.__ModuleLoader__.load({
|
|
|
1335
1479
|
if (made) URL.revokeObjectURL(made)
|
|
1336
1480
|
}
|
|
1337
1481
|
}, [props.data])
|
|
1338
|
-
if (state.error) return create('div', { className: 'dfv-error' }, '
|
|
1339
|
-
if (!state.url) return create('div', { className: 'dfv-empty' }, '
|
|
1482
|
+
if (state.error) return create('div', { className: 'dfv-error' }, T('files.pdfFail', { reason: state.error }))
|
|
1483
|
+
if (!state.url) return create('div', { className: 'dfv-empty' }, T('files.pdfLoading'))
|
|
1340
1484
|
return create('object', {
|
|
1341
1485
|
className: 'dfv-frame dfv-pdf',
|
|
1342
1486
|
type: 'application/pdf',
|
|
1343
1487
|
data: state.url,
|
|
1344
|
-
'aria-label': '
|
|
1345
|
-
}, '
|
|
1488
|
+
'aria-label': T('files.pdfFrame'),
|
|
1489
|
+
}, T('files.pdfUnsupported'))
|
|
1346
1490
|
}
|
|
1347
1491
|
|
|
1348
1492
|
// ------------------------------------------------------------------
|
|
@@ -1395,7 +1539,7 @@ window.__ModuleLoader__.load({
|
|
|
1395
1539
|
return create('img', { className: 'dfv-nb-img', src: 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(nbSource(out.data[mime])))), alt: 'notebook output svg' })
|
|
1396
1540
|
}
|
|
1397
1541
|
if (mime === 'text/html') {
|
|
1398
|
-
return create('iframe', { className: 'dfv-nb-html', sandbox: '', title: '
|
|
1542
|
+
return create('iframe', { className: 'dfv-nb-html', sandbox: '', title: T('files.nbHtmlOut'), srcDoc: nbSource(out.data[mime]) })
|
|
1399
1543
|
}
|
|
1400
1544
|
if (mime === 'text/markdown' && MarkdownText) {
|
|
1401
1545
|
return create('div', { className: 'dfv-md-wrap dfv-nb-md' }, create(MarkdownText, { text: nbSource(out.data[mime]) }))
|
|
@@ -1411,7 +1555,7 @@ window.__ModuleLoader__.load({
|
|
|
1411
1555
|
try { nb = JSON.parse(props.text) } catch (err) { nb = undefined }
|
|
1412
1556
|
if (nb === undefined || typeof nb !== 'object' || nb === null) {
|
|
1413
1557
|
return create(React.Fragment, null,
|
|
1414
|
-
create('div', { className: 'dfv-cap' }, '
|
|
1558
|
+
create('div', { className: 'dfv-cap' }, T('files.nbFail')),
|
|
1415
1559
|
create('pre', { className: 'dfv-code' }, props.text))
|
|
1416
1560
|
}
|
|
1417
1561
|
var cells = Array.isArray(nb.cells) ? nb.cells : []
|
|
@@ -1458,8 +1602,8 @@ window.__ModuleLoader__.load({
|
|
|
1458
1602
|
|
|
1459
1603
|
return create('div', { className: 'dfv-nb' },
|
|
1460
1604
|
create('div', { className: 'dfv-cap' },
|
|
1461
|
-
'Jupyter Notebook' + (kernelName ? ' · ' + kernelName : '') + ' · ' + cells.length
|
|
1462
|
-
cellEls.length ? cellEls : create('div', { className: 'dfv-muted' }, '
|
|
1605
|
+
'Jupyter Notebook' + (kernelName ? ' · ' + kernelName : '') + ' · ' + T('files.nbCells', { n: cells.length })),
|
|
1606
|
+
cellEls.length ? cellEls : create('div', { className: 'dfv-muted' }, T('files.nbEmpty')))
|
|
1463
1607
|
}
|
|
1464
1608
|
|
|
1465
1609
|
function JsonPreview(props) {
|
|
@@ -1467,7 +1611,7 @@ window.__ModuleLoader__.load({
|
|
|
1467
1611
|
try { JSON.parse(props.text) } catch (err) { failed = true }
|
|
1468
1612
|
if (failed) {
|
|
1469
1613
|
return create(React.Fragment, null,
|
|
1470
|
-
create('div', { className: 'dfv-cap' }, '
|
|
1614
|
+
create('div', { className: 'dfv-cap' }, T('files.jsonFail')),
|
|
1471
1615
|
create('pre', { className: 'dfv-code' }, props.text))
|
|
1472
1616
|
}
|
|
1473
1617
|
return create('pre', { className: 'dfv-json dfv-code' }, jsonColorNodes(JSON.stringify(JSON.parse(props.text), null, 2)))
|
|
@@ -1484,7 +1628,7 @@ window.__ModuleLoader__.load({
|
|
|
1484
1628
|
cols = Math.max(cols, Math.min(rows[r].length, CSV_MAX_COLS))
|
|
1485
1629
|
}
|
|
1486
1630
|
var caps = []
|
|
1487
|
-
if (totalRows > CSV_MAX_ROWS + 1) caps.push('
|
|
1631
|
+
if (totalRows > CSV_MAX_ROWS + 1) caps.push(T('files.csvTruncated', { total: totalRows, n: CSV_MAX_ROWS }))
|
|
1488
1632
|
var trs = []
|
|
1489
1633
|
for (var ri = 1; ri <= Math.min(totalRows - 1, CSV_MAX_ROWS); ri++) {
|
|
1490
1634
|
var cells = rows[ri]
|
|
@@ -1520,18 +1664,18 @@ window.__ModuleLoader__.load({
|
|
|
1520
1664
|
function FileContent(props) {
|
|
1521
1665
|
var entry = props.entry
|
|
1522
1666
|
if (entry.loading) {
|
|
1523
|
-
return create('div', { className: 'dfv-empty' }, '
|
|
1667
|
+
return create('div', { className: 'dfv-empty' }, T('files.loading'))
|
|
1524
1668
|
}
|
|
1525
1669
|
if (entry.error) {
|
|
1526
1670
|
return create('div', { className: 'dfv-error' }, entry.error)
|
|
1527
1671
|
}
|
|
1528
1672
|
if (entry.kind === 'binary') {
|
|
1529
1673
|
return create('div', { className: 'dfv-muted' },
|
|
1530
|
-
'
|
|
1674
|
+
T('files.binary', { size: formatBytes(entry.size) }))
|
|
1531
1675
|
}
|
|
1532
1676
|
if (entry.kind === 'image-too-large') {
|
|
1533
1677
|
return create('div', { className: 'dfv-muted' },
|
|
1534
|
-
'
|
|
1678
|
+
T('files.imageTooLarge', { size: formatBytes(entry.size), limit: formatBytes(entry.limit) }))
|
|
1535
1679
|
}
|
|
1536
1680
|
if (entry.kind === 'image') {
|
|
1537
1681
|
return create('div', { className: 'dfv-image-wrap' },
|
|
@@ -1539,7 +1683,7 @@ window.__ModuleLoader__.load({
|
|
|
1539
1683
|
}
|
|
1540
1684
|
if (entry.kind === 'pdf-too-large') {
|
|
1541
1685
|
return create('div', { className: 'dfv-muted' },
|
|
1542
|
-
'
|
|
1686
|
+
T('files.pdfTooLarge', { size: formatBytes(entry.size), limit: formatBytes(entry.limit) }))
|
|
1543
1687
|
}
|
|
1544
1688
|
if (entry.kind === 'pdf') {
|
|
1545
1689
|
return create(PdfPreview, { data: entry.data })
|
|
@@ -1565,15 +1709,51 @@ window.__ModuleLoader__.load({
|
|
|
1565
1709
|
}
|
|
1566
1710
|
return create(React.Fragment, null,
|
|
1567
1711
|
entry.truncated ? create('div', { className: 'dfv-cap' },
|
|
1568
|
-
'
|
|
1712
|
+
T('files.truncated')) : null,
|
|
1569
1713
|
view,
|
|
1570
1714
|
)
|
|
1571
1715
|
}
|
|
1572
1716
|
|
|
1717
|
+
/**
|
|
1718
|
+
* 纯阅读模式标记:在会话根(含 composerSeat 的最近祖先,即 DSH
|
|
1719
|
+
* ConversationRoot 的 root 节点)上打 data-dsh-soup-preview,由上面
|
|
1720
|
+
* 注入的 CSS 隐藏输入区与轮次统计行。DSH 重渲染可能重建该节点——
|
|
1721
|
+
* MutationObserver 在 FilesView 存活期间持续确保标记存在。
|
|
1722
|
+
*/
|
|
1723
|
+
function usePreviewReadingMode(rootRef) {
|
|
1724
|
+
React.useEffect(function () {
|
|
1725
|
+
var observer = null
|
|
1726
|
+
var marked = null
|
|
1727
|
+
function apply() {
|
|
1728
|
+
var el = rootRef.current
|
|
1729
|
+
if (!el) return
|
|
1730
|
+
if (marked && marked.contains(el) && marked.getAttribute('data-dsh-soup-preview') === '1') return
|
|
1731
|
+
var host = el.parentElement
|
|
1732
|
+
while (host) {
|
|
1733
|
+
if (host.querySelector && host.querySelector('.wSkVaW_composerSeat')) break
|
|
1734
|
+
host = host.parentElement
|
|
1735
|
+
}
|
|
1736
|
+
if (!host) return
|
|
1737
|
+
host.setAttribute('data-dsh-soup-preview', '1')
|
|
1738
|
+
marked = host
|
|
1739
|
+
}
|
|
1740
|
+
apply()
|
|
1741
|
+
observer = new MutationObserver(function () { apply() })
|
|
1742
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
1743
|
+
return function () {
|
|
1744
|
+
if (observer) observer.disconnect()
|
|
1745
|
+
if (marked) marked.removeAttribute('data-dsh-soup-preview')
|
|
1746
|
+
marked = null
|
|
1747
|
+
}
|
|
1748
|
+
}, [])
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1573
1751
|
function FilesView(props) {
|
|
1574
1752
|
var s = useStore()
|
|
1575
1753
|
var files = s.files
|
|
1576
1754
|
var active = files.active ? findFileEntry(files.active) : null
|
|
1755
|
+
var rootRef = React.useRef(null)
|
|
1756
|
+
usePreviewReadingMode(rootRef)
|
|
1577
1757
|
|
|
1578
1758
|
var tabs = files.list.map(function (entry) {
|
|
1579
1759
|
return create('button', {
|
|
@@ -1587,7 +1767,7 @@ window.__ModuleLoader__.load({
|
|
|
1587
1767
|
create('span', {
|
|
1588
1768
|
className: 'dfv-close',
|
|
1589
1769
|
role: 'button',
|
|
1590
|
-
title: '
|
|
1770
|
+
title: T('explorer.close'),
|
|
1591
1771
|
onClick: function (e) { e.stopPropagation(); closeFileTab(entry.path) },
|
|
1592
1772
|
}, '✕'),
|
|
1593
1773
|
)
|
|
@@ -1597,7 +1777,7 @@ window.__ModuleLoader__.load({
|
|
|
1597
1777
|
if (!active) {
|
|
1598
1778
|
body = create('div', { className: 'dfv-empty' },
|
|
1599
1779
|
create('span', { style: { fontSize: 22 } }, '📄'),
|
|
1600
|
-
'
|
|
1780
|
+
T('files.emptyHint'),
|
|
1601
1781
|
)
|
|
1602
1782
|
} else {
|
|
1603
1783
|
body = create('div', { className: 'dfv-body' },
|
|
@@ -1607,18 +1787,18 @@ window.__ModuleLoader__.load({
|
|
|
1607
1787
|
active.loaded ? create('button', {
|
|
1608
1788
|
type: 'button',
|
|
1609
1789
|
className: 'dfv-btn',
|
|
1610
|
-
title: '
|
|
1790
|
+
title: T('files.reloadTitle'),
|
|
1611
1791
|
onClick: function () { reloadFile(active.path) },
|
|
1612
1792
|
},
|
|
1613
1793
|
create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } }),
|
|
1614
|
-
'
|
|
1794
|
+
T('files.reload'),
|
|
1615
1795
|
) : null,
|
|
1616
1796
|
),
|
|
1617
1797
|
create(FileContent, { key: active.path, entry: active }),
|
|
1618
1798
|
)
|
|
1619
1799
|
}
|
|
1620
1800
|
|
|
1621
|
-
return create('div', { className: 'dfv-root' },
|
|
1801
|
+
return create('div', { className: 'dfv-root', ref: rootRef },
|
|
1622
1802
|
files.list.length > 0 ? create('div', { className: 'dfv-tabbar' }, tabs) : null,
|
|
1623
1803
|
body,
|
|
1624
1804
|
)
|
|
@@ -1758,7 +1938,7 @@ window.__ModuleLoader__.load({
|
|
|
1758
1938
|
))
|
|
1759
1939
|
}
|
|
1760
1940
|
if (s.tree.length === 0 && !(state.newItem && state.newItem.parent === state.cwd)) {
|
|
1761
|
-
bodyChildren.push(create('div', { key: '__empty__', className: 'expl-muted' }, '
|
|
1941
|
+
bodyChildren.push(create('div', { key: '__empty__', className: 'expl-muted' }, T('explorer.emptyDir')))
|
|
1762
1942
|
} else {
|
|
1763
1943
|
s.tree.forEach(function (node) { bodyChildren.push(create(Row, { key: node.path, node: node, depth: 0 })) })
|
|
1764
1944
|
}
|
|
@@ -1775,19 +1955,19 @@ window.__ModuleLoader__.load({
|
|
|
1775
1955
|
return create('div', { ref: panelRef, className: 'expl-panel' },
|
|
1776
1956
|
create('div', { className: 'expl-resize', onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onPointerCancel: onResizeUp }),
|
|
1777
1957
|
create('div', { className: 'expl-head' },
|
|
1778
|
-
create('span', { className: 'expl-title' }, '
|
|
1958
|
+
create('span', { className: 'expl-title' }, T('explorer.title')),
|
|
1779
1959
|
create('div', { className: 'expl-head-btns' },
|
|
1780
1960
|
create('button', {
|
|
1781
1961
|
className: 'expl-btn',
|
|
1782
1962
|
onClick: function () { refreshAll() },
|
|
1783
|
-
title: '
|
|
1784
|
-
'aria-label': '
|
|
1963
|
+
title: T('explorer.refreshTitle'),
|
|
1964
|
+
'aria-label': T('explorer.refresh'),
|
|
1785
1965
|
}, create('span', { className: 'expl-icon', dangerouslySetInnerHTML: { __html: ICON_REFRESH } })),
|
|
1786
1966
|
create('button', {
|
|
1787
1967
|
className: 'expl-btn',
|
|
1788
1968
|
onClick: function () { if (uploadInputEl) uploadInputEl.click() },
|
|
1789
|
-
title: '
|
|
1790
|
-
'aria-label': '
|
|
1969
|
+
title: T('explorer.upload'),
|
|
1970
|
+
'aria-label': T('explorer.upload'),
|
|
1791
1971
|
}, '⬆'),
|
|
1792
1972
|
create('button', {
|
|
1793
1973
|
className: 'expl-btn',
|
|
@@ -1796,8 +1976,8 @@ window.__ModuleLoader__.load({
|
|
|
1796
1976
|
try { layout.closeDetails() } catch (e) {}
|
|
1797
1977
|
setHeroDetailsWidth(event.currentTarget, 0)
|
|
1798
1978
|
},
|
|
1799
|
-
title: '
|
|
1800
|
-
'aria-label': '
|
|
1979
|
+
title: T('explorer.close'),
|
|
1980
|
+
'aria-label': T('explorer.closePanel'),
|
|
1801
1981
|
}, '✕'),
|
|
1802
1982
|
),
|
|
1803
1983
|
),
|
|
@@ -1809,7 +1989,7 @@ window.__ModuleLoader__.load({
|
|
|
1809
1989
|
create('span', { className: 'expl-upload-glyph' }, u.done ? '✓' : (u.down ? '⬇' : '⬆')),
|
|
1810
1990
|
create('span', { className: 'expl-upload-text' },
|
|
1811
1991
|
u.done ? u.name
|
|
1812
|
-
: (u.down ? '
|
|
1992
|
+
: (u.down ? T('explorer.downloading') : T('explorer.uploading')) + (u.totalFiles > 1 ? '(' + u.idx + '/' + u.totalFiles + ') ' : '') + u.name,
|
|
1813
1993
|
),
|
|
1814
1994
|
!u.done && u.total ? create('span', { className: 'expl-upload-pct' }, pct + '%') : null,
|
|
1815
1995
|
)
|
|
@@ -1819,7 +1999,7 @@ window.__ModuleLoader__.load({
|
|
|
1819
1999
|
s.error ? create('div', { className: 'expl-error' }, s.error) : null,
|
|
1820
2000
|
s.notice ? create('div', { className: 'expl-notice' }, s.notice) : null,
|
|
1821
2001
|
s.selected.size > 1
|
|
1822
|
-
? create('div', { className: 'expl-bulk' }, '
|
|
2002
|
+
? create('div', { className: 'expl-bulk' }, T('explorer.bulkSelected', { n: s.selected.size }))
|
|
1823
2003
|
: null,
|
|
1824
2004
|
container,
|
|
1825
2005
|
s.menu ? create('div', { className: 'expl-menu-mask', onMouseDown: function () { setState({ menu: null }) }, onContextMenu: function (e) { e.preventDefault(); setState({ menu: null }) } }) : null,
|
|
@@ -1841,8 +2021,8 @@ window.__ModuleLoader__.load({
|
|
|
1841
2021
|
type: 'button',
|
|
1842
2022
|
className: 'expl-toggle expl-tool' + (s.open ? ' expl-active' : ''),
|
|
1843
2023
|
onClick: function () { try { if (s.open) layout.closeDetails(); else layout.openDetails() } catch (e) {} },
|
|
1844
|
-
title: '
|
|
1845
|
-
'aria-label': '
|
|
2024
|
+
title: T('explorer.label'),
|
|
2025
|
+
'aria-label': T('explorer.label'),
|
|
1846
2026
|
}, '📁')
|
|
1847
2027
|
}
|
|
1848
2028
|
|
|
@@ -1870,8 +2050,8 @@ window.__ModuleLoader__.load({
|
|
|
1870
2050
|
try { layout.openDetails() } catch (e) {}
|
|
1871
2051
|
}
|
|
1872
2052
|
},
|
|
1873
|
-
title: '
|
|
1874
|
-
'aria-label': '
|
|
2053
|
+
title: T('explorer.label'),
|
|
2054
|
+
'aria-label': T('explorer.label'),
|
|
1875
2055
|
}, '📁'))
|
|
1876
2056
|
}
|
|
1877
2057
|
|
|
@@ -1938,7 +2118,7 @@ window.__ModuleLoader__.load({
|
|
|
1938
2118
|
el.style.cssText = 'display:inline-flex;align-items:center;margin-left:10px;font-weight:400;font-size:13px;color:var(--dsw-alias-label-caption);'
|
|
1939
2119
|
el.style.setProperty('color', 'var(--dsw-alias-label-caption)', 'important')
|
|
1940
2120
|
el.style.setProperty('-webkit-text-fill-color', 'var(--dsw-alias-label-caption)', 'important')
|
|
1941
|
-
el.textContent = '
|
|
2121
|
+
el.textContent = T('speed.waiting') + new Array(dotsRef.current + 1).join('.')
|
|
1942
2122
|
return
|
|
1943
2123
|
}
|
|
1944
2124
|
// 流式阶段:token 计数立即显示;瞬时速率窗口未满(tps=0)时先不显示徽标,
|
|
@@ -2112,7 +2292,7 @@ window.__ModuleLoader__.load({
|
|
|
2112
2292
|
pendingRef.current = false
|
|
2113
2293
|
pending[1](false)
|
|
2114
2294
|
if (!result || !result.ok) {
|
|
2115
|
-
var err = result && result.error ? (result.error.message + ' (' + result.error.code + ')') : '
|
|
2295
|
+
var err = result && result.error ? (result.error.message + ' (' + result.error.code + ')') : T('speed.operateFail')
|
|
2116
2296
|
actionError[1](err)
|
|
2117
2297
|
}
|
|
2118
2298
|
return result
|
|
@@ -2230,6 +2410,11 @@ window.__ModuleLoader__.load({
|
|
|
2230
2410
|
var sessions = ctx.sessions
|
|
2231
2411
|
var remoteGoals = ctx.remote.goals
|
|
2232
2412
|
var disposers = []
|
|
2413
|
+
// i18n:注册双语词典;模块级 T 供组件外回调(菜单/错误横幅)取当前语言文案。
|
|
2414
|
+
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
2415
|
+
ctx.effect(function () { return ctx.locale.register(NS, DICT) }, 'dsh-soup: dictionaries')
|
|
2416
|
+
if (typeof ctx.locale.bind === 'function') T = ctx.locale.bind(NS)
|
|
2417
|
+
}
|
|
2233
2418
|
// 自动刷新心跳:1s 固定心跳 + 到期才探测(基础 3s;宿主不可达时指数退避,封顶 60s)。
|
|
2234
2419
|
// 面板关且无预览时 tick 内直接返回(零请求);首个 tick 只建基线。
|
|
2235
2420
|
if (timerRef && typeof timerRef.interval === 'function') {
|
|
@@ -2238,7 +2423,7 @@ window.__ModuleLoader__.load({
|
|
|
2238
2423
|
}
|
|
2239
2424
|
slots.inject('conversation.session.header.utilities', function () {
|
|
2240
2425
|
disposers.push(slots.register(
|
|
2241
|
-
{ name: 'conversation.session.header.utilities', id: 'dsh-soup-toggle', order: 10, label: '
|
|
2426
|
+
{ name: 'conversation.session.header.utilities', id: 'dsh-soup-toggle', order: 10, locale: NS, label: function () { return T('explorer.label') } },
|
|
2242
2427
|
function (props) { return create(HeaderAction, props) },
|
|
2243
2428
|
))
|
|
2244
2429
|
})
|
|
@@ -2246,7 +2431,7 @@ window.__ModuleLoader__.load({
|
|
|
2246
2431
|
disposers.push(slots.register(
|
|
2247
2432
|
// `details` is a single slot occupied by the shell at priority 0.
|
|
2248
2433
|
// Lower priorities render first, so this intentionally shadows it.
|
|
2249
|
-
{ name: 'details', priority: -1 },
|
|
2434
|
+
{ name: 'details', priority: -1, locale: NS },
|
|
2250
2435
|
function (props) { return create(Panel, props) },
|
|
2251
2436
|
))
|
|
2252
2437
|
})
|
|
@@ -2254,7 +2439,7 @@ window.__ModuleLoader__.load({
|
|
|
2254
2439
|
// 槽位 entry 是静态的,多文件由 FilesView 内部子 tab 条管理。
|
|
2255
2440
|
slots.inject('conversation.view', function () {
|
|
2256
2441
|
disposers.push(slots.register(
|
|
2257
|
-
{ name: 'conversation.view', id: 'dsh-soup-files', order: 20, label:
|
|
2442
|
+
{ name: 'conversation.view', id: 'dsh-soup-files', order: 20, locale: NS, label: function () { return T('files.tab') } },
|
|
2258
2443
|
function (props) { return create(FilesView, props) },
|
|
2259
2444
|
))
|
|
2260
2445
|
})
|
|
@@ -2274,7 +2459,7 @@ window.__ModuleLoader__.load({
|
|
|
2274
2459
|
}
|
|
2275
2460
|
slots.inject('conversation.input.dock', function () {
|
|
2276
2461
|
disposers.push(slots.register(
|
|
2277
|
-
{ name: 'conversation.input.dock', id: 'dsh-soup-hero-toggle', order: -10 },
|
|
2462
|
+
{ name: 'conversation.input.dock', id: 'dsh-soup-hero-toggle', order: -10, locale: NS },
|
|
2278
2463
|
function (props) { return create(HeroAction, props) },
|
|
2279
2464
|
))
|
|
2280
2465
|
// GoalBar 多行版:同 id 'goal'、更低 priority (-1) -> 遮蔽默认单行实现。
|
|
@@ -2310,7 +2495,7 @@ window.__ModuleLoader__.load({
|
|
|
2310
2495
|
function (props) { return create(GoalDock, props) },
|
|
2311
2496
|
))
|
|
2312
2497
|
disposers.push(slots.register(
|
|
2313
|
-
{ name: 'conversation.input.dock', id: 'dsh-speed-badge', order: 30, label: '
|
|
2498
|
+
{ name: 'conversation.input.dock', id: 'dsh-speed-badge', order: 30, locale: NS, label: function () { return T('speed.label') } },
|
|
2314
2499
|
function (props) { return create(SpeedBadge, props) },
|
|
2315
2500
|
))
|
|
2316
2501
|
})
|
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
|
+
"version": "0.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|