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 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
+
@@ -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', disabled: deleting === file.name, onClick: () => remove(file.name) },
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
- @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%}.dsh-upload-actions a,.dsh-upload-actions button{flex:1;text-align:center}.dsh-upload-chip{min-width:160px}.dsh-upload-settings-head{flex-direction:column;align-items:stretch;gap:10px}.dsh-upload-head-actions{width:100%;justify-content:flex-end}.dsh-upload-head-actions .dsh-upload-refresh{flex:1;text-align:center;padding:7px 8px;max-width:none}}
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: () => openPreview(f.path) }, '预览'),
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: delStyle, disabled: busy, onClick: () => doDelete(f.path) }, '删除'),
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) }, '关闭'),