dsh-long-plugins 1.3.9 → 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
@@ -718,6 +718,7 @@ window.__ModuleLoader__.load({
718
718
  const [previewMaximized, setPreviewMaximized] = React.useState(false)
719
719
  const [dayFilter, setDayFilter] = React.useState('')
720
720
  const [search, setSearch] = React.useState('')
721
+ const [deleteEnabled, setDeleteEnabled] = React.useState(false)
721
722
 
722
723
  async function refresh() {
723
724
  setState((current) => ({ ...current, loading: true, error: '' }))
@@ -852,6 +853,12 @@ window.__ModuleLoader__.load({
852
853
  { type: 'button', className: 'dsh-upload-refresh', disabled: state.loading, onClick: refresh },
853
854
  state.loading ? '刷新中…' : '刷新',
854
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
+ ),
855
862
  ),
856
863
  ),
857
864
  React.createElement(
@@ -941,11 +948,9 @@ window.__ModuleLoader__.load({
941
948
  React.createElement(
942
949
  'div',
943
950
  { className: 'dsh-upload-actions' },
944
- React.createElement('button', { type: 'button', className: 'dsh-upload-preview', onClick: () => previewFile(file.name) }, '预览'),
945
- React.createElement('a', { href: downloadUrl(file.name), download: file.name }, '下载'),
946
951
  React.createElement(
947
952
  'button',
948
- { type: 'button', onClick: () => {
953
+ { type: 'button', className: 'dsh-upload-copy', onClick: () => {
949
954
  const s = String(file.path || file.name)
950
955
  const base = state.root ? String(state.root).replace(/\/[^/]*$/, '') : ''
951
956
  copyText(base && s.indexOf(base) === 0 ? s.slice(base.length).replace(/^\/+/, '') : s)
@@ -954,9 +959,11 @@ window.__ModuleLoader__.load({
954
959
  ),
955
960
  React.createElement(
956
961
  'button',
957
- { type: 'button', disabled: deleting === file.name, onClick: () => remove(file.name) },
962
+ { type: 'button', disabled: !deleteEnabled || deleting === file.name, onClick: () => remove(file.name) },
958
963
  deleting === file.name ? '删除中…' : '删除',
959
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) }, '预览'),
960
967
  ),
961
968
  )),
962
969
  )),
@@ -1024,8 +1031,13 @@ window.__ModuleLoader__.load({
1024
1031
  .dsh-upload-file-meta{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px}
1025
1032
  .dsh-upload-actions{display:flex;flex:none;gap:7px}
1026
1033
  .dsh-upload-actions button{color:var(--dsw-alias-state-error-primary)}
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}
1027
1039
  .dsh-upload-actions a,.dsh-upload-actions button{white-space:nowrap}
1028
- @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%;justify-content:flex-end}.dsh-upload-head-actions .dsh-upload-refresh{flex:1;text-align:center;padding:7px 8px;max-width:none}}
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}}
1029
1041
  .dsh-ws-folder:hover{background:var(--dsw-alias-interactive-bg-hover)}
1030
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}
1031
1043
  .dsh-upload-preview-del{color:var(--dsw-alias-state-error-primary)!important}
@@ -1068,6 +1080,7 @@ window.__ModuleLoader__.load({
1068
1080
  const [collapsed, setCollapsed] = React.useState({})
1069
1081
  const [dayFilter, setDayFilter] = React.useState('')
1070
1082
  const [search, setSearch] = React.useState('')
1083
+ const [deleteEnabled, setDeleteEnabled] = React.useState(false)
1071
1084
  const [copied, setCopied] = React.useState(false)
1072
1085
  const [editing, setEditing] = React.useState(false)
1073
1086
  const [edited, setEdited] = React.useState('')
@@ -1129,6 +1142,9 @@ window.__ModuleLoader__.load({
1129
1142
  } else if (/\.pptx$/i.test(path)) {
1130
1143
  // pptx:走 PptxViewJS 真实渲染页(浏览器端 Canvas 渲染,所见即所得)。
1131
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) })
1132
1148
  } else {
1133
1149
  setPreview(data)
1134
1150
  }
@@ -1267,6 +1283,10 @@ window.__ModuleLoader__.load({
1267
1283
  React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', minWidth: 0 } },
1268
1284
  React.createElement(SearchPopup, { value: search, onChange: setSearch, placeholder: '搜索文件名…' }),
1269
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
+ ),
1270
1290
  ),
1271
1291
  error !== null && React.createElement('div', { style: { fontSize: 12, color: 'var(--dsw-alias-state-error-primary)' } }, error),
1272
1292
  groups === null && error === null && React.createElement('div', { style: metaStyle }, '加载中…'),
@@ -1299,10 +1319,10 @@ window.__ModuleLoader__.load({
1299
1319
  React.createElement(
1300
1320
  'div',
1301
1321
  { className: 'dsh-ws-actions', style: { display: 'flex', gap: 6, flex: 'none' } },
1302
- React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () => openPreview(f.path) }, '预览'),
1303
- React.createElement('a', { href: '/api/dsh-uploads/workspace-file?path=' + encodeURIComponent(f.path) + '&download=1', download: f.name, style: btnStyle }, '下载'),
1304
1322
  React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () => copyText(f.path) }, '复制路径'),
1305
- React.createElement('button', { type: 'button', style: delStyle, disabled: busy, onClick: () => doDelete(f.path) }, '删除'),
1323
+ React.createElement('button', { type: 'button', className: 'dsh-ws-del', style: delStyle, disabled: !deleteEnabled || busy, onClick: () => doDelete(f.path) }, '删除'),
1324
+ React.createElement('a', { href: '/api/dsh-uploads/workspace-file?path=' + encodeURIComponent(f.path) + '&download=1', download: f.name, style: btnStyle }, '下载'),
1325
+ React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: () => openPreview(f.path) }, '预览'),
1306
1326
  ),
1307
1327
  )),
1308
1328
  )),
@@ -1320,7 +1340,7 @@ window.__ModuleLoader__.load({
1320
1340
  editing && React.createElement('button', { type: 'button', style: btnStyle, disabled: busy, onClick: doSave }, savedFlash ? '已保存' : '保存'),
1321
1341
  React.createElement('button', { type: 'button', style: btnStyle, onClick: copyContent }, copied ? '已复制' : '复制全部'),
1322
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)' } }, '下载'),
1323
- (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)' } }, '打开'),
1324
1344
  React.createElement('button', { type: 'button', style: delStyle, disabled: busy, onClick: () => doDelete(preview.path) }, '删除'),
1325
1345
  React.createElement('button', { type: 'button', style: btnStyle, onClick: () => setMaximized((m) => !m) }, maximized ? '还原' : '放大'),
1326
1346
  React.createElement('button', { type: 'button', style: btnStyle, onClick: () => setPreview(null) }, '关闭'),