dsh-long-plugins 1.3.8 → 1.3.10
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 +14 -1
- package/THIRD_PARTY_NOTICES.md +7 -0
- package/client/client.js +53 -7
- package/client/vendor/xlsx.full.min.js +22 -0
- package/dsh.plugin.json +1 -1
- package/lib/index.js +407 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ upload manager, workspace **输出文件** section, top-bar **文件** browser w
|
|
|
5
5
|
**技能文档** (skill docs) browser, DeepSeek account balance, and **turn ruler (session navigation)** — all in one installable plugin.
|
|
6
6
|
Also ships an auto-repair install that relinks DSH core patches (reverse-proxy WebSocket heartbeat, upgrade relink).
|
|
7
7
|
|
|
8
|
-
一个插件整合 DSH Web 的常用增强:上传管理、顶栏「📂文件」工作区浏览 + 文件预览(Word、PPT 格式渲染)、工作区「输出文件」面板、技能文档浏览、账户余额显示。并自带修复安装,自动重连 DSH 核心补丁(反代 WebSocket 心跳、升级重连)。会话记录导航。
|
|
8
|
+
一个插件整合 DSH Web 的常用增强:上传管理、顶栏「📂文件」工作区浏览 + 文件预览(Word、PPT、Excel 格式渲染)、工作区「输出文件」面板、技能文档浏览、账户余额显示。并自带修复安装,自动重连 DSH 核心补丁(反代 WebSocket 心跳、升级重连)。会话记录导航。
|
|
9
9
|
|
|
10
10
|
## Features / 功能
|
|
11
11
|
|
|
@@ -95,6 +95,8 @@ Agent 可直接调用 `md2docx` 工具,把 Markdown 转成带页码页脚的 W
|
|
|
95
95
|
/api/dsh-uploads/workspace-file/save save edited content (POST)
|
|
96
96
|
/api/dsh-uploads/docx-preview Word (.docx) real render page (browser-side docx-preview)
|
|
97
97
|
/api/dsh-uploads/docx-preview-asset serve docx-preview / jszip vendor libs
|
|
98
|
+
/api/dsh-uploads/xlsx-preview Excel (.xlsx) static render page (browser-side SheetJS → table)
|
|
99
|
+
/api/dsh-uploads/xlsx-preview-asset serve SheetJS (xlsx) vendor lib
|
|
98
100
|
/dsh-skill-docs/skill-docs skill docs list (grouped by skill)
|
|
99
101
|
/dsh-skill-docs/skill-doc preview / download
|
|
100
102
|
/dsh-skill-docs/skill-doc/save save (POST)
|
|
@@ -115,7 +117,18 @@ MIT
|
|
|
115
117
|
- `JSZip` — 解压 Office 包 — **MIT**(或 GPLv3,本项目按 MIT 使用)
|
|
116
118
|
- `Chart.js` — PPT 内图表 — **MIT**
|
|
117
119
|
- `PptxViewJS` — PowerPoint 真实预览 — **MIT**
|
|
120
|
+
- `SheetJS`(xlsx.full.min.js) — Excel 真实预览 — **Apache-2.0**
|
|
118
121
|
- npm 依赖:`mammoth`(BSD-2-Clause)、`exceljs`(MIT)
|
|
119
122
|
|
|
120
123
|
完整的版权声明与来源见 [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md)。
|
|
121
124
|
|
|
125
|
+
## Changelog / 更新记录
|
|
126
|
+
|
|
127
|
+
### v1.3.10
|
|
128
|
+
- 新增 `.xlsx` 浏览器端预览:用 SheetJS 解析并显示为表格(行列标头、合并单元格、多工作表切换),预览支持 − / + / 适合宽度缩放,默认整表适配;浏览器端处理,不占用 NAS 本地资源。
|
|
129
|
+
- 工作区浏览、工作区文件、上传文件列表中的 `.xlsx` 均接入该预览页。
|
|
130
|
+
- `.csv` / `.tsv` 保留表格预览(首行作表头)。
|
|
131
|
+
- 文件列表按钮顺序调整:工作区浏览页为 复制路径、重命名、下载、预览;上传/输出文件面板为 复制路径、删除、下载、预览。
|
|
132
|
+
- 「上传文件」「输出文件」新增「开启删除」开关:关闭时删除按钮置灰禁用,开启后可删除;上传文件的「复制路径」不再使用删除红色。
|
|
133
|
+
- 手机端(≤640px)文件列表顶部操作行保持单行:搜索/日期等宽、刷新/开关靠右。
|
|
134
|
+
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -34,6 +34,13 @@ copyright notices and sources are listed here.
|
|
|
34
34
|
- npm: <https://www.npmjs.com/package/pptxviewjs>
|
|
35
35
|
- CDN / source build: <https://cdn.jsdelivr.net/npm/pptxviewjs@1.1.9>
|
|
36
36
|
|
|
37
|
+
### SheetJS (`xlsx.full.min.js`)
|
|
38
|
+
- Used for: real browser-side Excel (`.xlsx`) preview
|
|
39
|
+
- License: **Apache-2.0**
|
|
40
|
+
- Copyright: SheetJS Community Edition
|
|
41
|
+
- npm: <https://www.npmjs.com/package/xlsx>
|
|
42
|
+
- CDN / source build: <https://cdn.jsdelivr.net/npm/xlsx@0.18.5>
|
|
43
|
+
|
|
37
44
|
## Runtime npm dependencies (not vendored into the package)
|
|
38
45
|
|
|
39
46
|
These are declared as `dependencies` and resolved on the host at install time;
|
package/client/client.js
CHANGED
|
@@ -95,6 +95,21 @@ window.__ModuleLoader__.load({
|
|
|
95
95
|
t._timer = setTimeout(() => { t.className = 'dsh-long-toast' }, 2800)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/** 复制文本到剪贴板 + 轻提示。 */
|
|
99
|
+
function copyText(text) {
|
|
100
|
+
try {
|
|
101
|
+
const done = () => showToast(`已复制:${text}`)
|
|
102
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
103
|
+
navigator.clipboard.writeText(text).then(done, () => showToast('复制失败', 'error'))
|
|
104
|
+
} else {
|
|
105
|
+
const ta = document.createElement('textarea')
|
|
106
|
+
ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'
|
|
107
|
+
document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta)
|
|
108
|
+
done()
|
|
109
|
+
}
|
|
110
|
+
} catch (e) { showToast('复制失败', 'error') }
|
|
111
|
+
}
|
|
112
|
+
|
|
98
113
|
/** Trigger a browser download for a URL (no navigation, keeps the page). */
|
|
99
114
|
function triggerDownload(url, name) {
|
|
100
115
|
const a = document.createElement('a')
|
|
@@ -703,6 +718,7 @@ window.__ModuleLoader__.load({
|
|
|
703
718
|
const [previewMaximized, setPreviewMaximized] = React.useState(false)
|
|
704
719
|
const [dayFilter, setDayFilter] = React.useState('')
|
|
705
720
|
const [search, setSearch] = React.useState('')
|
|
721
|
+
const [deleteEnabled, setDeleteEnabled] = React.useState(false)
|
|
706
722
|
|
|
707
723
|
async function refresh() {
|
|
708
724
|
setState((current) => ({ ...current, loading: true, error: '' }))
|
|
@@ -837,6 +853,12 @@ window.__ModuleLoader__.load({
|
|
|
837
853
|
{ type: 'button', className: 'dsh-upload-refresh', disabled: state.loading, onClick: refresh },
|
|
838
854
|
state.loading ? '刷新中…' : '刷新',
|
|
839
855
|
),
|
|
856
|
+
React.createElement(
|
|
857
|
+
'label',
|
|
858
|
+
{ className: 'dsh-upload-del-toggle' },
|
|
859
|
+
React.createElement('input', { type: 'checkbox', checked: deleteEnabled, onChange: (e) => setDeleteEnabled(e.target.checked) }),
|
|
860
|
+
'开启删除',
|
|
861
|
+
),
|
|
840
862
|
),
|
|
841
863
|
),
|
|
842
864
|
React.createElement(
|
|
@@ -926,13 +948,22 @@ window.__ModuleLoader__.load({
|
|
|
926
948
|
React.createElement(
|
|
927
949
|
'div',
|
|
928
950
|
{ className: 'dsh-upload-actions' },
|
|
929
|
-
React.createElement('button', { type: 'button', className: 'dsh-upload-preview', onClick: () => previewFile(file.name) }, '预览'),
|
|
930
|
-
React.createElement('a', { href: downloadUrl(file.name), download: file.name }, '下载'),
|
|
931
951
|
React.createElement(
|
|
932
952
|
'button',
|
|
933
|
-
{ type: 'button',
|
|
953
|
+
{ type: 'button', className: 'dsh-upload-copy', onClick: () => {
|
|
954
|
+
const s = String(file.path || file.name)
|
|
955
|
+
const base = state.root ? String(state.root).replace(/\/[^/]*$/, '') : ''
|
|
956
|
+
copyText(base && s.indexOf(base) === 0 ? s.slice(base.length).replace(/^\/+/, '') : s)
|
|
957
|
+
} },
|
|
958
|
+
'复制路径',
|
|
959
|
+
),
|
|
960
|
+
React.createElement(
|
|
961
|
+
'button',
|
|
962
|
+
{ type: 'button', disabled: !deleteEnabled || deleting === file.name, onClick: () => remove(file.name) },
|
|
934
963
|
deleting === file.name ? '删除中…' : '删除',
|
|
935
964
|
),
|
|
965
|
+
React.createElement('a', { href: downloadUrl(file.name), download: file.name }, '下载'),
|
|
966
|
+
React.createElement('button', { type: 'button', className: 'dsh-upload-preview', onClick: () => previewFile(file.name) }, '预览'),
|
|
936
967
|
),
|
|
937
968
|
)),
|
|
938
969
|
)),
|
|
@@ -1000,7 +1031,13 @@ window.__ModuleLoader__.load({
|
|
|
1000
1031
|
.dsh-upload-file-meta{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}
|
|
1001
1032
|
.dsh-upload-actions{display:flex;flex:none;gap:7px}
|
|
1002
1033
|
.dsh-upload-actions button{color:var(--dsw-alias-state-error-primary)}
|
|
1003
|
-
|
|
1034
|
+
.dsh-upload-actions button.dsh-upload-copy{color:var(--dsw-alias-label-primary)}
|
|
1035
|
+
.dsh-upload-actions button:disabled{opacity:.5;cursor:not-allowed;color:var(--dsw-alias-label-tertiary)}
|
|
1036
|
+
.dsh-upload-del-toggle{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;user-select:none}
|
|
1037
|
+
.dsh-upload-del-toggle input{accent-color:var(--dsw-alias-state-business-primary);cursor:pointer}
|
|
1038
|
+
.dsh-ws-del:disabled{opacity:.5;cursor:not-allowed;color:var(--dsw-alias-label-tertiary)!important}
|
|
1039
|
+
.dsh-upload-actions a,.dsh-upload-actions button{white-space:nowrap}
|
|
1040
|
+
@media (max-width:640px){.dsh-upload-row{align-items:stretch;flex-direction:column;gap:4px}.dsh-upload-file-name{white-space:normal;word-break:break-all}.dsh-upload-actions{width:100%;display:flex;gap:6px}.dsh-upload-actions a,.dsh-upload-actions button{flex:1;text-align:center;white-space:nowrap;padding:6px 4px}.dsh-upload-chip{min-width:160px}.dsh-upload-settings-head{flex-direction:column;align-items:stretch;gap:10px}.dsh-upload-head-actions{width:100%;flex-wrap:nowrap;justify-content:flex-start}.dsh-upload-head-actions .dsh-searchpop,.dsh-upload-head-actions .dsh-datefilter{flex:1 1 0;min-width:0}.dsh-upload-head-actions .dsh-upload-refresh{flex:none;text-align:center;padding:7px 8px;max-width:none}.dsh-upload-head-actions .dsh-upload-del-toggle{flex:none;white-space:nowrap;padding:0 4px}}
|
|
1004
1041
|
.dsh-ws-folder:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
1005
1042
|
.dsh-ws-file-type{flex:none;font-size:10px;line-height:14px;padding:1px 6px;border-radius:5px;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);white-space:nowrap}
|
|
1006
1043
|
.dsh-upload-preview-del{color:var(--dsw-alias-state-error-primary)!important}
|
|
@@ -1043,6 +1080,7 @@ window.__ModuleLoader__.load({
|
|
|
1043
1080
|
const [collapsed, setCollapsed] = React.useState({})
|
|
1044
1081
|
const [dayFilter, setDayFilter] = React.useState('')
|
|
1045
1082
|
const [search, setSearch] = React.useState('')
|
|
1083
|
+
const [deleteEnabled, setDeleteEnabled] = React.useState(false)
|
|
1046
1084
|
const [copied, setCopied] = React.useState(false)
|
|
1047
1085
|
const [editing, setEditing] = React.useState(false)
|
|
1048
1086
|
const [edited, setEdited] = React.useState('')
|
|
@@ -1104,6 +1142,9 @@ window.__ModuleLoader__.load({
|
|
|
1104
1142
|
} else if (/\.pptx$/i.test(path)) {
|
|
1105
1143
|
// pptx:走 PptxViewJS 真实渲染页(浏览器端 Canvas 渲染,所见即所得)。
|
|
1106
1144
|
setPreview({ path, name: data.name, url: '/api/dsh-uploads/pptx-preview?path=' + encodeURIComponent(path) })
|
|
1145
|
+
} else if (/\.xlsx$/i.test(path)) {
|
|
1146
|
+
// xlsx:走 x-spreadsheet 真实渲染页(浏览器端解析 xlsx,所见即所得)。
|
|
1147
|
+
setPreview({ path, name: data.name, url: '/api/dsh-uploads/xlsx-preview?path=' + encodeURIComponent(path) })
|
|
1107
1148
|
} else {
|
|
1108
1149
|
setPreview(data)
|
|
1109
1150
|
}
|
|
@@ -1242,6 +1283,10 @@ window.__ModuleLoader__.load({
|
|
|
1242
1283
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', minWidth: 0 } },
|
|
1243
1284
|
React.createElement(SearchPopup, { value: search, onChange: setSearch, placeholder: '搜索文件名…' }),
|
|
1244
1285
|
React.createElement(DateFilter, { value: dayFilter, onChange: setDayFilter, placeholder: '选择日期' }),
|
|
1286
|
+
React.createElement('label', { className: 'dsh-upload-del-toggle' },
|
|
1287
|
+
React.createElement('input', { type: 'checkbox', checked: deleteEnabled, onChange: (e) => setDeleteEnabled(e.target.checked) }),
|
|
1288
|
+
'开启删除',
|
|
1289
|
+
),
|
|
1245
1290
|
),
|
|
1246
1291
|
error !== null && React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, error),
|
|
1247
1292
|
groups === null && error === null && React.createElement('div', { style: metaStyle }, '加载中…'),
|
|
@@ -1274,9 +1319,10 @@ window.__ModuleLoader__.load({
|
|
|
1274
1319
|
React.createElement(
|
|
1275
1320
|
'div',
|
|
1276
1321
|
{ className: 'dsh-ws-actions', style: { display: 'flex', gap: 6, flex: 'none' } },
|
|
1277
|
-
React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () =>
|
|
1322
|
+
React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () => copyText(f.path) }, '复制路径'),
|
|
1323
|
+
React.createElement('button', { type: 'button', className: 'dsh-ws-del', style: delStyle, disabled: !deleteEnabled || busy, onClick: () => doDelete(f.path) }, '删除'),
|
|
1278
1324
|
React.createElement('a', { href: '/api/dsh-uploads/workspace-file?path=' + encodeURIComponent(f.path) + '&download=1', download: f.name, style: btnStyle }, '下载'),
|
|
1279
|
-
React.createElement('button', { type: 'button', style:
|
|
1325
|
+
React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () => openPreview(f.path) }, '预览'),
|
|
1280
1326
|
),
|
|
1281
1327
|
)),
|
|
1282
1328
|
)),
|
|
@@ -1294,7 +1340,7 @@ window.__ModuleLoader__.load({
|
|
|
1294
1340
|
editing && React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: doSave }, savedFlash ? '已保存' : '保存'),
|
|
1295
1341
|
React.createElement('button', { type: 'button', style: btnStyle, onClick: copyContent }, copied ? '已复制' : '复制全部'),
|
|
1296
1342
|
(preview.url !== void 0 || preview.officeHtml !== void 0 || preview.mdHtml !== void 0) && preview.loading !== true && React.createElement('a', { href: '/api/dsh-uploads/workspace-file?path=' + encodeURIComponent(preview.path) + '&download=1', download: preview.name, style: { ...btnStyle, color: 'var(--dsw-alias-state-business-primary)' } }, '下载'),
|
|
1297
|
-
(preview.url !== void 0 || preview.officeHtml !== void 0 || preview.mdHtml !== void 0) && preview.loading !== true && React.createElement('a', { href: '/api/dsh-uploads/workspace-preview?path=' + encodeURIComponent(preview.path), target: '_blank', rel: 'noopener noreferrer', style: { ...btnStyle, color: 'var(--dsw-alias-state-business-primary)' } }, '打开'),
|
|
1343
|
+
(preview.url !== void 0 || preview.officeHtml !== void 0 || preview.mdHtml !== void 0) && preview.loading !== true && React.createElement('a', { href: preview.url !== void 0 ? preview.url : '/api/dsh-uploads/workspace-preview?path=' + encodeURIComponent(preview.path), target: '_blank', rel: 'noopener noreferrer', style: { ...btnStyle, color: 'var(--dsw-alias-state-business-primary)' } }, '打开'),
|
|
1298
1344
|
React.createElement('button', { type: 'button', style: delStyle, disabled: busy, onClick: () => doDelete(preview.path) }, '删除'),
|
|
1299
1345
|
React.createElement('button', { type: 'button', style: btnStyle, onClick: () => setMaximized((m) => !m) }, maximized ? '还原' : '放大'),
|
|
1300
1346
|
React.createElement('button', { type: 'button', style: btnStyle, onClick: () => setPreview(null) }, '关闭'),
|