@grapecity-software/dsh-spreadjs-editor 0.1.3 → 0.2.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/CHANGELOG.md +70 -36
- package/README.md +223 -141
- package/THIRD_PARTY_NOTICES.md +12 -10
- package/lib/client.js +2 -2
- package/lib/index.js +200 -4
- package/package.json +113 -112
package/CHANGELOG.md
CHANGED
|
@@ -1,36 +1,70 @@
|
|
|
1
|
-
# Release Notes / 版本记录
|
|
2
|
-
|
|
3
|
-
## 0.
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
1
|
+
# Release Notes / 版本记录
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
> 破坏性变更:本版要求 DSH `>=0.1.5-rc.3`,并改用 DSH 自带的右侧栏,不再需要任何第三方侧边栏插件;升级前请先升级 DSH。仓库里准备过的 `0.1.5` 从未发布,其内容已并入本版。
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
|
|
9
|
+
- **编辑后可以直接保存回原文件**:Designer「文件」菜单里点「保存到工作区」即可。写回限定在会话工作区内,带冲突检测(文件被外部改过会拒绝并说明原因),按下即写入、不再二次确认。「文件」菜单里只保留这一个动作,其余分类(新建、打开、导入、导出、打印、信息)已移除,需要副本时用 DSH 自己的文件下载。
|
|
10
|
+
- **浅色/深色完整跟随系统**:此前只有工具栏和面板变深、工作表区域仍是白底,现在一起切换。
|
|
11
|
+
- **界面更干净**:去掉面板顶部的文件名标题栏(DSH 标签页已有)和底部状态栏,保存与冲突提示改为右下角浮层。
|
|
12
|
+
- **新增「关于」**:Designer「设置」标签里可查看插件版本、内置 SpreadJS 与 Designer 版本、授权状态和版权信息。
|
|
13
|
+
- **可以让 AI 直接改你正在编辑的表**:另装配套的 `@grapecity-software/dsh-spreadjs-driver` 后,可通过对话让 Agent 修改当前工作簿;未安装时插件行为不变。
|
|
14
|
+
- 内置 SpreadJS 升级到 19.2.0;新增配置项 `maxSaveBytes`(默认 64 MB)与 `trustedHosts`。
|
|
15
|
+
|
|
16
|
+
### English
|
|
17
|
+
|
|
18
|
+
- **Save your edits straight back to the file you opened**: press `保存到工作区` (save to workspace) in the Designer's File tab. The write stays inside the session workspace and is guarded against conflicts (a file changed on disk is refused, with a reason), and it writes immediately with no second prompt. That is now the File tab's only action — new, open, import, export, print and info are gone, so use the harness's own file download for a copy.
|
|
19
|
+
- **Light and dark follow the system all the way**: previously only the toolbars and panels darkened while the sheet stayed white.
|
|
20
|
+
- **A cleaner panel**: the file-name title bar (the harness tab already shows it) and the status bar are gone, and save or conflict messages appear as a floating notice in the corner.
|
|
21
|
+
- **A new About dialog**: the Designer's settings tab shows the plugin and bundled versions, the licence state and the copyright.
|
|
22
|
+
- **Let an agent edit the sheet you are looking at**: with the companion `@grapecity-software/dsh-spreadjs-driver` installed, you can ask an agent to change the open workbook in conversation; without it the plugin behaves as before.
|
|
23
|
+
- Bundled SpreadJS moved to 19.2.0; new configuration `maxSaveBytes` (default 64 MB) and `trustedHosts`.
|
|
24
|
+
|
|
25
|
+
## 0.1.4
|
|
26
|
+
|
|
27
|
+
### 中文
|
|
28
|
+
|
|
29
|
+
- 移除安装期 `prepare` 钩子。npm 包继续以预构建方式分发,发布打包改用 `prepack`,用户安装时不会执行任何脚本。
|
|
30
|
+
- 打包校验新增检查:禁止 `preinstall`、`install`、`postinstall`、`prepare` 等安装期钩子。
|
|
31
|
+
|
|
32
|
+
### English
|
|
33
|
+
|
|
34
|
+
- Remove the `prepare` install hook. The npm package stays prebuilt; release packing now uses `prepack`, so no script runs during user installation.
|
|
35
|
+
- Add a package check that rejects `preinstall`, `install`, `postinstall`, or `prepare` hooks.
|
|
36
|
+
|
|
37
|
+
## 0.1.3
|
|
38
|
+
|
|
39
|
+
- 精简安装、配置和版本说明,方便查阅。
|
|
40
|
+
- Simplify installation, configuration, and release documentation.
|
|
41
|
+
|
|
42
|
+
## 0.1.2
|
|
43
|
+
|
|
44
|
+
### 中文
|
|
45
|
+
|
|
46
|
+
- 修复 DSH 启动失败和网页中编辑器无法加载的问题。
|
|
47
|
+
- 简化安装:内置 SpreadJS 19.1.4 及配套 Designer,无需本地编译或安装旧的 web-editors 前置插件。
|
|
48
|
+
- 编辑器底部显示 SpreadJS 版本,便于确认文件兼容性。
|
|
49
|
+
- 支持分别配置 SpreadJS 和 Designer 许可证。
|
|
50
|
+
|
|
51
|
+
升级要求:DSH `>=0.1.2-rc.1`。旧用户需移除 `dsh-plugin-web-editors` 及其 profile patch;已有 SpreadJS 许可证配置保持不变,Designer 许可证需单独配置。详见 [安装说明](README.md#安装)和[配置说明](README.md#配置)。
|
|
52
|
+
|
|
53
|
+
### English
|
|
54
|
+
|
|
55
|
+
- Fix issues that prevented DSH from starting or the editor from loading in the browser.
|
|
56
|
+
- Simplify installation: bundle SpreadJS 19.1.4 and its matching Designer, with no local build or former web-editors prerequisite required.
|
|
57
|
+
- Show the SpreadJS version in the editor status bar to help identify file compatibility.
|
|
58
|
+
- Support separate SpreadJS and Designer license configuration.
|
|
59
|
+
|
|
60
|
+
Upgrade requirements: DSH `>=0.1.2-rc.1`. Existing users should remove `dsh-plugin-web-editors` and its profile patch. Existing SpreadJS license configuration stays the same; configure the Designer license separately. See [Installation](README.md#installation) and [Configuration](README.md#configuration).
|
|
61
|
+
|
|
62
|
+
## 0.1.1
|
|
63
|
+
|
|
64
|
+
- 更新 README 中的编辑器截图。
|
|
65
|
+
- Update the editor screenshot in the README.
|
|
66
|
+
|
|
67
|
+
## 0.1.0
|
|
68
|
+
|
|
69
|
+
- 首次发布:支持从 DSH 文件树打开、查看和编辑 `.xlsx`、`.xlsm`、`.csv`、`.sjs` 和 `.ssjson` 文件。
|
|
70
|
+
- Initial release: open, view, and edit `.xlsx`, `.xlsm`, `.csv`, `.sjs`, and `.ssjson` files from the DSH file tree.
|
package/README.md
CHANGED
|
@@ -1,141 +1,223 @@
|
|
|
1
|
-
# SpreadJS Editor for DeepSeek Harness
|
|
2
|
-
|
|
3
|
-
[GitHub](https://github.com/GrapeCityXA/dsh-plugin-spreadjs-editor) · npm: `@grapecity-software/dsh-spreadjs-editor`
|
|
4
|
-
|
|
5
|
-
[版本记录 / Release Notes](CHANGELOG.md)
|
|
6
|
-
|
|
7
|
-
在 DeepSeek Harness Web UI 里,直接在右侧文件树中打开、查看和编辑 Excel / SpreadJS 文件。
|
|
8
|
-
|
|
9
|
-
A DeepSeek Harness Web UI plugin that opens, views, and edits Excel / SpreadJS files directly from the right-side file tree.
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 中文说明
|
|
16
|
-
|
|
17
|
-
### 功能
|
|
18
|
-
|
|
19
|
-
- 支持 `.xlsx`、`.xlsm`、`.csv`、`.sjs`、`.ssjson` 文件。
|
|
20
|
-
- 从文件树打开工作簿,使用 SpreadJS Designer 编辑表格。
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
###
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
1
|
+
# SpreadJS Editor for DeepSeek Harness
|
|
2
|
+
|
|
3
|
+
[GitHub](https://github.com/GrapeCityXA/dsh-plugin-spreadjs-editor) · npm: `@grapecity-software/dsh-spreadjs-editor`
|
|
4
|
+
|
|
5
|
+
[版本记录 / Release Notes](CHANGELOG.md)
|
|
6
|
+
|
|
7
|
+
在 DeepSeek Harness Web UI 里,直接在右侧文件树中打开、查看和编辑 Excel / SpreadJS 文件。
|
|
8
|
+
|
|
9
|
+
A DeepSeek Harness Web UI plugin that opens, views, and edits Excel / SpreadJS files directly from the right-side file tree.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 中文说明
|
|
16
|
+
|
|
17
|
+
### 功能
|
|
18
|
+
|
|
19
|
+
- 支持 `.xlsx`、`.xlsm`、`.csv`、`.sjs`、`.ssjson` 文件。
|
|
20
|
+
- 从文件树打开工作簿,使用 SpreadJS Designer 编辑表格。
|
|
21
|
+
- 编辑后可直接保存回原文件:原子写入、限制在会话工作区内、带冲突检测。
|
|
22
|
+
- 编辑器跟随系统切换浅色和深色主题,工具栏与工作表区域一起变。
|
|
23
|
+
- 「设置」标签里有「关于」按钮:内置版本、授权状态与版权信息在弹窗里。
|
|
24
|
+
|
|
25
|
+
### 内置版本与文件兼容性
|
|
26
|
+
|
|
27
|
+
| 组件 | 内置版本 |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| SpreadJS | 19.2.0 |
|
|
30
|
+
| SpreadJS Designer | 19.2.0 |
|
|
31
|
+
|
|
32
|
+
内置版本、授权状态与版权信息在 Designer「设置」标签的「关于」弹窗里。低版本 SpreadJS 可能无法打开本版本保存的 `.sjs` 或 `.ssjson` 文件;与旧版系统交换文件时,建议保留原文件并确认兼容性。
|
|
33
|
+
|
|
34
|
+
### 安装
|
|
35
|
+
|
|
36
|
+
需要 DSH `>=0.1.5-rc.3`。插件使用 DSH 自带的右侧栏,**不需要安装任何第三方侧边栏插件**。
|
|
37
|
+
|
|
38
|
+
插件内置 SpreadJS 和 Designer,无需编译。使用已安装的 DSH CLI 安装并启动:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web add @grapecity-software/dsh-spreadjs-editor
|
|
42
|
+
dsh --profile web
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
启动后,在 Web UI 右侧文件树中打开 `.xlsx`、`.xlsm`、`.csv`、`.sjs` 或 `.ssjson` 文件即可。
|
|
46
|
+
|
|
47
|
+
也可以通过 npx 安装并启动:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npx --yes @deepseek-ai/dsh@latest plugin --profile web add @grapecity-software/dsh-spreadjs-editor
|
|
51
|
+
npx --yes @deepseek-ai/dsh@latest --profile web
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
旧版本用户请参阅[升级说明](CHANGELOG.md)。
|
|
55
|
+
|
|
56
|
+
### 保存
|
|
57
|
+
|
|
58
|
+
保存只有一处入口:Designer「文件」菜单里的「保存到工作区」,就在原本内置"另存为 .sjs"那颗按钮的位置上。改动写回你打开的那个文件,不会再问你"要不要保存",因为按下保存本身就是你的意图。
|
|
59
|
+
|
|
60
|
+
- **「文件」菜单只剩一个保存动作。** Designer 原本的保存是「弹框下载到本地」;本插件把它换成写回你打开的那个文件,菜单里只保留这一个动作,新建、打开、导入、导出、打印、信息等分类一并移除。需要一份副本时,用 DSH 自己的文件下载即可。
|
|
61
|
+
- **工作簿还没载入完时不让保存。** 载入中的工作簿是空的,此时保存会用空表覆盖原文件;载入完成前保存命令会拒绝并说明。
|
|
62
|
+
- **不会冲掉别人的修改。** 打开文件时插件记下其内容哈希;保存前重新比对,若磁盘上的文件在此期间被改过(Agent 改过、你在别处存过),保存会被拒绝并说明原因,而不是覆盖掉那些改动。
|
|
63
|
+
- **不会写出半个文件。** 同目录临时文件写完后 rename 覆盖,其他程序任何时候读到的都是完整文件。
|
|
64
|
+
- **写不出工作区。** 目标必须在当前会话的工作区内,越界(含 `..` 逃逸)直接拒绝。
|
|
65
|
+
- **只有循环回环地址可用。** 保存接口与 DSH 其它接口一样只服务本机来源;通过局域网地址访问时,需要把该地址加进 `trustedHosts`。
|
|
66
|
+
|
|
67
|
+
### 配合 AI 使用:让 Agent 改你正在看的表
|
|
68
|
+
|
|
69
|
+
再装上 [`@grapecity-software/dsh-spreadjs-driver`](https://www.npmjs.com/package/@grapecity-software/dsh-spreadjs-driver),就可以直接对 Agent 说"把金额列改成红色"——改动**立刻出现在你正看着的这张表上**。
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
dsh plugin --profile web add @grapecity-software/dsh-spreadjs-driver
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **改的是同一个对象,不是文件副本。** 编辑器把当前工作簿交给该插件的 `spreadjsHostBridge` 服务;两个插件的浏览器半跑在同一个页面里,所以改动直接落在 Designer 正在渲染的那份文档上——**不会覆盖你尚未保存的编辑**(这一点是按文件走的路子给不了的)。
|
|
76
|
+
- **磁盘上的文件不动。** 改动只活在编辑器里,除非你明确要求保存——所以你有机会先看清楚,再决定要不要落盘。
|
|
77
|
+
- **只有它拿得到。** 工作簿是编辑器**主动交出去**的,不存在"查找别人的工作簿"这类入口,同页面上的其他插件够不到。
|
|
78
|
+
- 该插件本身还带一整套表格工具(新建、导入、导出 `.xlsx` / `.csv` / `.pdf`、截图),**不装编辑器也能单独使用**。
|
|
79
|
+
|
|
80
|
+
> 需要本插件 **0.2.0 或更高版本**——更早的版本里没有这个桥,装上 driver 也不会有联动。
|
|
81
|
+
|
|
82
|
+
### 配置
|
|
83
|
+
|
|
84
|
+
如已获得许可证,在 web profile 的 `cordis.patch.yml` 中添加以下配置:
|
|
85
|
+
|
|
86
|
+
| Key | 默认值 | 说明 |
|
|
87
|
+
| --- | --- | --- |
|
|
88
|
+
| `licenseKey` | `''` | SpreadJS 部署许可证。留空以带水印的试用模式运行。 |
|
|
89
|
+
| `designerLicenseKey` | `''` | 独立的 SpreadJS Designer 部署许可证。留空保持 Designer 试用状态。 |
|
|
90
|
+
| `maxSaveBytes` | `67108864` | 单次保存允许的最大字节数;超出直接拒绝,不做截断。 |
|
|
91
|
+
| `trustedHosts` | `[]` | 除本机回环地址外,额外允许调用保存接口的地址(局域网部署时使用)。 |
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
- id: spreadjs-editor
|
|
95
|
+
config:
|
|
96
|
+
licenseKey: 'YOUR_SPREADJS_KEY'
|
|
97
|
+
designerLicenseKey: 'YOUR_DESIGNER_KEY'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
修改后重启 DSH 并刷新网页。
|
|
101
|
+
|
|
102
|
+
### 验证
|
|
103
|
+
|
|
104
|
+
| 命令 | 验证内容 |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `npm run typecheck`、`npm test` | 类型契约,以及 53 个单元测试(保存路由的每条守卫、文件地址解析,以及保存命令重定向的每条分支) |
|
|
107
|
+
| `node scripts/smoke-client.mjs` | 构建后的浏览器半结构:loader 包装、内联的 SpreadJS 与样式、外部依赖只有 react |
|
|
108
|
+
| `node scripts/smoke-node.mjs` | 构建后的宿主半:路由注册、许可证配置,以及保存路径的真实写盘、冲突与越界拒绝 |
|
|
109
|
+
| `npm run probe:live` | **对着正在运行的 DSH** 跑一遍真实保存:真实 `ctx.fs` 的解析、落盘、冲突守卫、工作区越界 |
|
|
110
|
+
|
|
111
|
+
`npm run probe:live` 要求 DSH 已重启到本版本;如果它报 404,说明进程里还是旧的宿主半。可选参数:`--url`(默认 `http://127.0.0.1:3080`)、`--session <id>`、`--home <DSH_HOME>`。探针会在会话工作区内建一个临时文件,结束时删除。
|
|
112
|
+
|
|
113
|
+
### 许可
|
|
114
|
+
|
|
115
|
+
插件代码免费提供,采用 [MIT 许可证](LICENSE)。内置的 SpreadJS 和 Designer 是葡萄城商业产品,未配置许可证时可按其试用条款体验;插件免费不包含这两个产品的商业授权。
|
|
116
|
+
|
|
117
|
+
如用于正式部署或 SaaS 服务,请根据使用场景获取适用的 SpreadJS 和 Designer 授权。
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## English
|
|
122
|
+
|
|
123
|
+
### Features
|
|
124
|
+
|
|
125
|
+
- Opens `.xlsx`, `.xlsm`, `.csv`, `.sjs`, and `.ssjson` files.
|
|
126
|
+
- Open workbooks from the file tree and edit them with SpreadJS Designer.
|
|
127
|
+
- Save edits straight back to the file: atomic, confined to the session workspace, with conflict detection.
|
|
128
|
+
- Follows the system's light or dark theme, toolbars and worksheet alike.
|
|
129
|
+
- An **About** button in the settings tab reports the bundled versions, the licence state and the copyright.
|
|
130
|
+
|
|
131
|
+
### Bundled Versions and File Compatibility
|
|
132
|
+
|
|
133
|
+
| Component | Bundled version |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| SpreadJS | 19.2.0 |
|
|
136
|
+
| SpreadJS Designer | 19.2.0 |
|
|
137
|
+
|
|
138
|
+
The **About** button in the Designer's settings tab shows the bundled versions, the licence state and the copyright. Older SpreadJS versions may not open `.sjs` or `.ssjson` files saved by this version. When exchanging files with older systems, keep the original files and check compatibility.
|
|
139
|
+
|
|
140
|
+
### Installation
|
|
141
|
+
|
|
142
|
+
Requires DSH `>=0.1.5-rc.3`. The plugin uses the harness's own right Sidebar and needs **no third-party sidebar plugin**.
|
|
143
|
+
|
|
144
|
+
The plugin includes SpreadJS and Designer, with no build step required. With the DSH CLI installed:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
dsh plugin --profile web add @grapecity-software/dsh-spreadjs-editor
|
|
148
|
+
dsh --profile web
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
After startup, open `.xlsx`, `.xlsm`, `.csv`, `.sjs`, or `.ssjson` from the right-side file tree.
|
|
152
|
+
|
|
153
|
+
Alternatively, install and start through npx:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
npx --yes @deepseek-ai/dsh@latest plugin --profile web add @grapecity-software/dsh-spreadjs-editor
|
|
157
|
+
npx --yes @deepseek-ai/dsh@latest --profile web
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For upgrades, see the [release notes](CHANGELOG.md).
|
|
161
|
+
|
|
162
|
+
### Saving
|
|
163
|
+
|
|
164
|
+
Save has a single entry point: the **保存到工作区** button in the Designer's File tab, in the place the built-in "save as .sjs" button used to occupy. It writes your changes back to the file you opened, and there is no second prompt, because pressing Save *is* the intent.
|
|
165
|
+
|
|
166
|
+
- **The File tab is reduced to one Save action.** The Designer's own Save is a dialog that downloads a file; this plugin replaces it with a write-back to the file you opened, and the File tab keeps only that one action — new, open, import, export, print and info are removed outright. A copy is what the harness's own file download is for.
|
|
167
|
+
- **A workbook that is still loading cannot be saved.** It has no content yet, so saving would overwrite the file with an empty book; the save command refuses, with an explanation, until the load finishes.
|
|
168
|
+
- **It will not clobber someone else's edit.** The plugin records the file's content hash when it opens it and re-checks before writing; if the file changed in the meantime (an agent edited it, you saved it elsewhere), the save is refused with an explanation instead of overwriting those changes.
|
|
169
|
+
- **It never leaves half a file.** A same-directory temporary file is renamed over the target, so any other reader always sees a complete workbook.
|
|
170
|
+
- **It cannot write outside the workspace.** The target must sit inside the current session's workspace; anything else — including `..` escapes — is refused.
|
|
171
|
+
- **Loopback only.** The save endpoint serves the local machine like the rest of DSH. If you reach DSH through a LAN address, add that address to `trustedHosts`.
|
|
172
|
+
|
|
173
|
+
### Let the agent edit the sheet you are looking at
|
|
174
|
+
|
|
175
|
+
Install [`@grapecity-software/dsh-spreadjs-driver`](https://www.npmjs.com/package/@grapecity-software/dsh-spreadjs-driver) alongside this plugin, and you can simply ask the agent — "make the amount column red" — and **watch the change land on the sheet you have open**.
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
dsh plugin --profile web add @grapecity-software/dsh-spreadjs-driver
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- **It edits the same object, not a copy of the file.** The editor hands its current workbook to that plugin's `spreadjsHostBridge` service. Both plugins' browser halves run in the same page, so the change lands on the very document the Designer is rendering — and it **does not clobber edits you have not saved**, which a file-based route cannot promise.
|
|
182
|
+
- **The file on disk is not touched.** The change lives in the editor until you explicitly ask for a save, so you get to look before deciding.
|
|
183
|
+
- **Only that plugin can reach it.** The workbook is handed over *by the editor*; there is no way to go looking for somebody else's workbook, so other plugins on the page cannot touch it.
|
|
184
|
+
- That plugin also brings a full set of spreadsheet tools of its own (create, import, export `.xlsx` / `.csv` / `.pdf`, screenshot) and **works on its own without the editor**.
|
|
185
|
+
|
|
186
|
+
> Requires this plugin **0.2.0 or later** — earlier versions have no bridge, so installing the driver alongside them changes nothing.
|
|
187
|
+
|
|
188
|
+
### Configuration
|
|
189
|
+
|
|
190
|
+
If you have licenses, add the following to the web profile's `cordis.patch.yml`:
|
|
191
|
+
|
|
192
|
+
| Key | Default | Description |
|
|
193
|
+
| --- | --- | --- |
|
|
194
|
+
| `licenseKey` | `''` | SpreadJS deployment key. Empty runs in evaluation mode with a watermark. |
|
|
195
|
+
| `designerLicenseKey` | `''` | Separate SpreadJS Designer deployment key. Empty keeps Designer in evaluation mode. |
|
|
196
|
+
| `maxSaveBytes` | `67108864` | Largest payload one save may carry; a bigger one is refused, never truncated. |
|
|
197
|
+
| `trustedHosts` | `[]` | Extra authorities allowed to call the save endpoint, beyond loopback (for LAN deployments). |
|
|
198
|
+
|
|
199
|
+
```yaml
|
|
200
|
+
- id: spreadjs-editor
|
|
201
|
+
config:
|
|
202
|
+
licenseKey: 'YOUR_SPREADJS_KEY'
|
|
203
|
+
designerLicenseKey: 'YOUR_DESIGNER_KEY'
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Restart DSH and refresh the browser after changing the configuration.
|
|
207
|
+
|
|
208
|
+
### Verification
|
|
209
|
+
|
|
210
|
+
| Command | What it proves |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| `npm run typecheck`, `npm test` | The type contract, and 53 unit tests: every save guard, file-address parsing, and every branch of the Save-command redirect |
|
|
213
|
+
| `node scripts/smoke-client.mjs` | The built browser half's structure: loader wrapper, inlined SpreadJS and styles, react as the only runtime external |
|
|
214
|
+
| `node scripts/smoke-node.mjs` | The built host half: route registration, license config, and the save path writing real bytes plus refusing conflicts and escapes |
|
|
215
|
+
| `npm run probe:live` | A real save against a **running DSH**: the actual `ctx.fs` resolving, writing, refusing a stale base hash, and refusing a workspace escape |
|
|
216
|
+
|
|
217
|
+
`npm run probe:live` needs DSH restarted onto this version; a 404 from it means the running process still carries the old host half. Options: `--url` (default `http://127.0.0.1:3080`), `--session <id>`, `--home <DSH_HOME>`. The probe creates one temporary file inside the session workspace and deletes it.
|
|
218
|
+
|
|
219
|
+
### License
|
|
220
|
+
|
|
221
|
+
The plugin code is free under the [MIT license](LICENSE). Bundled SpreadJS and Designer are GrapeCity commercial products available for evaluation under their trial terms without license keys. The free plugin does not include commercial licenses for these products.
|
|
222
|
+
|
|
223
|
+
For production or SaaS deployments, obtain SpreadJS and Designer licenses appropriate to your use.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# Bundled Software
|
|
2
|
-
|
|
3
|
-
The plugin's MIT license covers its own code, not the bundled SpreadJS and
|
|
4
|
-
SpreadJS Designer commercial components. Those components remain subject to
|
|
5
|
-
GrapeCity's applicable evaluation and commercial license terms.
|
|
6
|
-
|
|
7
|
-
The prebuilt client contains the SpreadJS
|
|
8
|
-
package.json
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
# Bundled Software
|
|
2
|
+
|
|
3
|
+
The plugin's MIT license covers its own code, not the bundled SpreadJS and
|
|
4
|
+
SpreadJS Designer commercial components. Those components remain subject to
|
|
5
|
+
GrapeCity's applicable evaluation and commercial license terms.
|
|
6
|
+
|
|
7
|
+
The prebuilt client contains the SpreadJS and SpreadJS Designer components
|
|
8
|
+
declared in package.json — the versions recorded there are authoritative, and
|
|
9
|
+
this file deliberately does not repeat them. Original license and notice files
|
|
10
|
+
supplied by those packages are preserved under lib/licenses. Copyright notices
|
|
11
|
+
embedded in the client bundle are retained. React and DSH are supplied by the
|
|
12
|
+
host rather than bundled here.
|
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/lib/client.js
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 51960527 bytes
|
|
4
|
+
sha256: 755ed8c0ded6f8614655c3fa528f535c2d80092201ce8b62eb0ea0c7c44d7655
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,57 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
1
4
|
//#region src/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* dsh-spreadjs-editor — node half.
|
|
7
|
+
*
|
|
8
|
+
* Two responsibilities:
|
|
9
|
+
*
|
|
10
|
+
* 1. Supply the SpreadJS license/config handshake on the `/spreadjs` prefix.
|
|
11
|
+
* 2. Save an edited workbook back to the file it came from.
|
|
12
|
+
*
|
|
13
|
+
* Why this plugin carries the write at all: the harness filesystem seam reads
|
|
14
|
+
* bytes but mutates text only (`ctx.fs.writeText`/`editText`, and
|
|
15
|
+
* `writeFileAtomic` takes a string), so neither the harness nor an agent can
|
|
16
|
+
* write a workbook. The capability therefore has to live in the document's own
|
|
17
|
+
* plugin — and this is the shape to keep when the platform grows a byte write:
|
|
18
|
+
* every decision below is already delegated to harness services, and only the
|
|
19
|
+
* final byte replacement is local.
|
|
20
|
+
*
|
|
21
|
+
* Policy is never invented here:
|
|
22
|
+
*
|
|
23
|
+
* - the session's workspace root is resolved exactly as
|
|
24
|
+
* `@deepseek-ai/dsh-api-workspace-files` resolves it: live session header cwd,
|
|
25
|
+
* persistent header as fallback, deployment sandbox root last;
|
|
26
|
+
* - containment is decided by `ctx.fs.contains` on resolved targets, so a write
|
|
27
|
+
* can never leave that root;
|
|
28
|
+
* - the freshness guard uses the complete current bytes, so a file edited
|
|
29
|
+
* elsewhere between open and save is reported instead of overwritten;
|
|
30
|
+
* - every missing service refuses the write. There is no unguarded path.
|
|
31
|
+
*
|
|
32
|
+
* The request fence mirrors the harness browser-trust posture: a request is
|
|
33
|
+
* served only when its Host header names a loopback authority (or one the
|
|
34
|
+
* deployment lists in `trustedHosts`).
|
|
35
|
+
*/
|
|
2
36
|
const name = "dsh-spreadjs-editor";
|
|
3
37
|
/** Required service: the web server whose route table this plugin extends. */
|
|
4
38
|
const inject = ["webServer"];
|
|
39
|
+
const DEFAULT_MAX_SAVE_BYTES = 67108864;
|
|
40
|
+
/** A failure with an HTTP status, so handlers stay flat. */
|
|
41
|
+
var HttpError = class extends Error {
|
|
42
|
+
status;
|
|
43
|
+
code;
|
|
44
|
+
constructor(status, code, message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.status = status;
|
|
47
|
+
this.code = code;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function serviceOf(ctx, serviceName) {
|
|
51
|
+
const bag = ctx;
|
|
52
|
+
if (typeof bag.get !== "function") return void 0;
|
|
53
|
+
return bag.get(serviceName) ?? void 0;
|
|
54
|
+
}
|
|
5
55
|
function sendJson(res, status, body) {
|
|
6
56
|
const data = JSON.stringify(body);
|
|
7
57
|
res.writeHead(status, {
|
|
@@ -11,16 +61,164 @@ function sendJson(res, status, body) {
|
|
|
11
61
|
});
|
|
12
62
|
res.end(data);
|
|
13
63
|
}
|
|
64
|
+
function sendError(res, error) {
|
|
65
|
+
const failure = error instanceof HttpError ? error : new HttpError(500, "internal", error instanceof Error ? error.message : String(error));
|
|
66
|
+
if (!res.headersSent) sendJson(res, failure.status, {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: {
|
|
69
|
+
code: failure.code,
|
|
70
|
+
message: failure.message
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
else res.destroy();
|
|
74
|
+
}
|
|
14
75
|
function readPathname(req) {
|
|
15
76
|
return new URL(req.url ?? "/", "http://localhost").pathname;
|
|
16
77
|
}
|
|
78
|
+
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
79
|
+
"localhost",
|
|
80
|
+
"127.0.0.1",
|
|
81
|
+
"::1",
|
|
82
|
+
"[::1]"
|
|
83
|
+
]);
|
|
84
|
+
/**
|
|
85
|
+
* The harness browser-trust fence: serve only a request whose Host header names
|
|
86
|
+
* a loopback authority (any port), or one the deployment lists explicitly.
|
|
87
|
+
*
|
|
88
|
+
* @param req - the incoming request.
|
|
89
|
+
* @param trustedHosts - extra allowed authorities from the plugin config.
|
|
90
|
+
* @returns whether the request may be served.
|
|
91
|
+
*/
|
|
92
|
+
function isTrustedRequest(req, trustedHosts) {
|
|
93
|
+
const host = req.headers.host;
|
|
94
|
+
if (host === void 0 || host === "") return false;
|
|
95
|
+
const authority = host.toLowerCase();
|
|
96
|
+
const hostname = authority.startsWith("[") ? authority.slice(0, authority.indexOf("]") + 1 || void 0) : authority.split(":")[0] ?? "";
|
|
97
|
+
if (LOOPBACK_HOSTS.has(hostname)) return true;
|
|
98
|
+
return trustedHosts.some((allowed) => allowed.toLowerCase() === authority || allowed.toLowerCase() === hostname);
|
|
99
|
+
}
|
|
100
|
+
/** Read a request body, refusing anything above `limit` instead of truncating. */
|
|
101
|
+
async function readBody(req, limit) {
|
|
102
|
+
const chunks = [];
|
|
103
|
+
let total = 0;
|
|
104
|
+
for await (const chunk of req) {
|
|
105
|
+
const buffer = chunk;
|
|
106
|
+
total += buffer.length;
|
|
107
|
+
if (total > limit) throw new HttpError(413, "too_large", `Workbook exceeds the ${limit} byte save limit`);
|
|
108
|
+
chunks.push(buffer);
|
|
109
|
+
}
|
|
110
|
+
const body = Buffer.concat(chunks);
|
|
111
|
+
if (body.length === 0) throw new HttpError(400, "empty_body", "Save request carried no workbook bytes");
|
|
112
|
+
return body;
|
|
113
|
+
}
|
|
114
|
+
function sha256Hex(bytes) {
|
|
115
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
116
|
+
}
|
|
117
|
+
function required(params, key) {
|
|
118
|
+
const value = params.get(key);
|
|
119
|
+
if (value === null || value === "") throw new HttpError(400, "bad_request", `Missing "${key}"`);
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve the workspace root of one session, exactly as the harness's own
|
|
124
|
+
* workspace-file service does: the live header's cwd wins, the persisted header
|
|
125
|
+
* is consulted when the session is not live, and the deployment sandbox root is
|
|
126
|
+
* the last resort.
|
|
127
|
+
*
|
|
128
|
+
* @param ctx - the plugin context.
|
|
129
|
+
* @param sessionId - the session the document belongs to.
|
|
130
|
+
* @returns the workspace root, or undefined when nothing can establish one.
|
|
131
|
+
*/
|
|
132
|
+
async function resolveWorkspaceRoot(ctx, sessionId) {
|
|
133
|
+
const live = serviceOf(ctx, "sessions")?.get(sessionId)?.header;
|
|
134
|
+
if (live?.cwd !== void 0 && live.cwd !== "") return live.cwd;
|
|
135
|
+
if (live === void 0) {
|
|
136
|
+
const cwd = (await serviceOf(ctx, "sessionPersistence")?.stat(sessionId).catch(() => void 0))?.header?.cwd;
|
|
137
|
+
if (cwd !== void 0 && cwd !== "") return cwd;
|
|
138
|
+
}
|
|
139
|
+
const root = serviceOf(ctx, "sandboxPolicy")?.workspaceRoot;
|
|
140
|
+
return root === void 0 || root === "" ? void 0 : root;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Write `bytes` over `target` atomically.
|
|
144
|
+
*
|
|
145
|
+
* The harness's own atomic writer takes text, so the binary case is done here in
|
|
146
|
+
* the same shape: an exclusively created random-suffix sibling, then a rename
|
|
147
|
+
* over the target, so a reader never observes a partial workbook.
|
|
148
|
+
*
|
|
149
|
+
* @param targetPath - the canonical absolute path to replace.
|
|
150
|
+
* @param bytes - the complete new file contents.
|
|
151
|
+
*/
|
|
152
|
+
async function replaceFile(targetPath, bytes) {
|
|
153
|
+
const directory = dirname(targetPath);
|
|
154
|
+
await mkdir(directory, { recursive: true });
|
|
155
|
+
const temporary = join(directory, `.${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.dsh-save`);
|
|
156
|
+
try {
|
|
157
|
+
await writeFile(temporary, bytes, { flag: "wx" });
|
|
158
|
+
await rename(temporary, targetPath);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
await rm(temporary, { force: true }).catch(() => void 0);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Save one workbook posted by the document panel.
|
|
166
|
+
*
|
|
167
|
+
* Guards, in order: trusted request, complete parameters, a resolvable session
|
|
168
|
+
* workspace, an available filesystem, a target inside that workspace, a body
|
|
169
|
+
* whose hash matches what the caller claimed, and — when the file already exists
|
|
170
|
+
* — a base hash matching the file's current contents.
|
|
171
|
+
*
|
|
172
|
+
* @param req - the POST request carrying raw workbook bytes.
|
|
173
|
+
* @param res - the response.
|
|
174
|
+
* @param ctx - the plugin context.
|
|
175
|
+
* @param config - resolved plugin configuration.
|
|
176
|
+
*/
|
|
177
|
+
async function handleSave(req, res, ctx, config) {
|
|
178
|
+
if (req.method !== "POST") throw new HttpError(405, "method_not_allowed", "Save accepts POST only");
|
|
179
|
+
if (!isTrustedRequest(req, config.trustedHosts ?? [])) throw new HttpError(403, "forbidden", "Untrusted Host header");
|
|
180
|
+
const limit = config.maxSaveBytes ?? DEFAULT_MAX_SAVE_BYTES;
|
|
181
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
182
|
+
const sessionId = required(url.searchParams, "sessionId");
|
|
183
|
+
const path = required(url.searchParams, "path");
|
|
184
|
+
const claimedHash = required(url.searchParams, "body");
|
|
185
|
+
const baseHash = url.searchParams.get("base");
|
|
186
|
+
const fs = serviceOf(ctx, "fs");
|
|
187
|
+
if (fs === void 0) throw new HttpError(503, "unavailable", "The harness filesystem service is unavailable");
|
|
188
|
+
const workspaceRoot = await resolveWorkspaceRoot(ctx, sessionId);
|
|
189
|
+
if (workspaceRoot === void 0) throw new HttpError(503, "unavailable", `Cannot resolve a workspace root for session "${sessionId}"`);
|
|
190
|
+
const rootTarget = await fs.resolve(workspaceRoot);
|
|
191
|
+
const target = await fs.resolve(path, { cwd: workspaceRoot });
|
|
192
|
+
if (!fs.contains(rootTarget, target)) throw new HttpError(403, "outside_workspace", `Refusing to write outside the session workspace: ${target.displayPath}`);
|
|
193
|
+
const bytes = await readBody(req, limit);
|
|
194
|
+
const hash = sha256Hex(bytes);
|
|
195
|
+
if (hash !== claimedHash.toLowerCase()) throw new HttpError(400, "hash_mismatch", "The uploaded bytes do not match the declared body hash");
|
|
196
|
+
const existing = await fs.stat(target).catch(() => void 0);
|
|
197
|
+
if (existing !== void 0) {
|
|
198
|
+
if (existing.type !== "file") throw new HttpError(400, "not_a_file", `${target.displayPath} is not a regular file`);
|
|
199
|
+
if (baseHash === null || baseHash === "") throw new HttpError(409, "conflict", "The file already exists but the editor did not state what it opened");
|
|
200
|
+
if (sha256Hex(await fs.readBytes(target, void 0, limit)) !== baseHash.toLowerCase()) throw new HttpError(409, "conflict", "The file changed on disk since it was opened; nothing was written");
|
|
201
|
+
} else if (baseHash !== null && baseHash !== "") throw new HttpError(409, "conflict", "The editor expected an existing file but none is there");
|
|
202
|
+
await replaceFile(fs.processPath(target), bytes);
|
|
203
|
+
sendJson(res, 200, {
|
|
204
|
+
ok: true,
|
|
205
|
+
hash,
|
|
206
|
+
path: target.displayPath
|
|
207
|
+
});
|
|
208
|
+
}
|
|
17
209
|
function apply(ctx, config = {}) {
|
|
18
210
|
const disposer = ctx.webServer.register({
|
|
19
211
|
kind: "prefix",
|
|
20
212
|
path: "/spreadjs",
|
|
21
213
|
handler: (req, res) => {
|
|
214
|
+
const pathname = readPathname(req);
|
|
215
|
+
if (pathname === "/spreadjs/api/save") {
|
|
216
|
+
handleSave(req, res, ctx, config).catch((error) => {
|
|
217
|
+
sendError(res, error);
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
22
221
|
try {
|
|
23
|
-
const pathname = readPathname(req);
|
|
24
222
|
if (pathname === "/spreadjs/api/health") {
|
|
25
223
|
sendJson(res, 200, { ok: true });
|
|
26
224
|
return;
|
|
@@ -34,9 +232,7 @@ function apply(ctx, config = {}) {
|
|
|
34
232
|
}
|
|
35
233
|
sendJson(res, 404, { error: "unknown endpoint" });
|
|
36
234
|
} catch (error) {
|
|
37
|
-
|
|
38
|
-
if (!res.headersSent) sendJson(res, 500, { error: message });
|
|
39
|
-
else res.destroy();
|
|
235
|
+
sendError(res, error);
|
|
40
236
|
}
|
|
41
237
|
}
|
|
42
238
|
});
|
package/package.json
CHANGED
|
@@ -1,112 +1,113 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@grapecity-software/dsh-spreadjs-editor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DeepSeek Harness Web UI plugin: SpreadJS
|
|
5
|
-
"keywords": [
|
|
6
|
-
"deepseek-harness",
|
|
7
|
-
"dsh",
|
|
8
|
-
"webui",
|
|
9
|
-
"plugin",
|
|
10
|
-
"spreadjs",
|
|
11
|
-
"excel"
|
|
12
|
-
],
|
|
13
|
-
"type": "module",
|
|
14
|
-
"main": "lib/index.js",
|
|
15
|
-
"exports": {
|
|
16
|
-
".": {
|
|
17
|
-
"default": "./lib/index.js"
|
|
18
|
-
},
|
|
19
|
-
"./client": {
|
|
20
|
-
"default": "./lib/client.js"
|
|
21
|
-
},
|
|
22
|
-
"./package.json": "./package.json"
|
|
23
|
-
},
|
|
24
|
-
"files": [
|
|
25
|
-
"lib/*.js",
|
|
26
|
-
"lib/licenses",
|
|
27
|
-
"cordis.patch.yml",
|
|
28
|
-
"README.md",
|
|
29
|
-
"CHANGELOG.md",
|
|
30
|
-
"LICENSE",
|
|
31
|
-
"THIRD_PARTY_NOTICES.md"
|
|
32
|
-
],
|
|
33
|
-
"scripts": {
|
|
34
|
-
"build": "node scripts/build.mjs && node scripts/copy-notices.mjs",
|
|
35
|
-
"grapecity:update": "node scripts/update-grapecity.mjs",
|
|
36
|
-
"
|
|
37
|
-
"prepublishOnly": "npm run typecheck && npm test && npm run build && node scripts/smoke-client.mjs && node scripts/smoke-node.mjs && npm run verify:package",
|
|
38
|
-
"rebuild": "node scripts/build.mjs",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"dsh-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"@grapecity-software/spread-
|
|
79
|
-
"@grapecity-software/spread-sheets
|
|
80
|
-
"@grapecity-software/spread-sheets-
|
|
81
|
-
"@grapecity-software/spread-sheets-
|
|
82
|
-
"@grapecity-software/spread-sheets-designer
|
|
83
|
-
"@grapecity-software/spread-sheets-
|
|
84
|
-
"@grapecity-software/spread-sheets-
|
|
85
|
-
"@grapecity-software/spread-sheets-
|
|
86
|
-
"@grapecity-software/spread-sheets-
|
|
87
|
-
"@grapecity-software/spread-sheets-
|
|
88
|
-
"@grapecity-software/spread-sheets-
|
|
89
|
-
"@grapecity-software/spread-sheets-
|
|
90
|
-
"@grapecity-software/spread-sheets-
|
|
91
|
-
"@grapecity-software/spread-sheets-
|
|
92
|
-
"@grapecity-software/spread-sheets-
|
|
93
|
-
"@grapecity-software/spread-sheets-
|
|
94
|
-
"@grapecity-software/spread-sheets-
|
|
95
|
-
"@grapecity-software/spread-sheets-
|
|
96
|
-
"@grapecity-software/spread-sheets-
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"@deepseek-ai/
|
|
102
|
-
"@deepseek-ai/dsh-client-ui-
|
|
103
|
-
"@deepseek-ai/dsh-client-ui-
|
|
104
|
-
"@deepseek-ai/dsh-client-ui-
|
|
105
|
-
"@deepseek-ai/dsh-
|
|
106
|
-
"@
|
|
107
|
-
"@types/
|
|
108
|
-
"react": "^18.
|
|
109
|
-
"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@grapecity-software/dsh-spreadjs-editor",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "DeepSeek Harness Web UI plugin: SpreadJS workbook editor for the harness's own right Sidebar, with binary save.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"deepseek-harness",
|
|
7
|
+
"dsh",
|
|
8
|
+
"webui",
|
|
9
|
+
"plugin",
|
|
10
|
+
"spreadjs",
|
|
11
|
+
"excel"
|
|
12
|
+
],
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"default": "./lib/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./client": {
|
|
20
|
+
"default": "./lib/client.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib/*.js",
|
|
26
|
+
"lib/licenses",
|
|
27
|
+
"cordis.patch.yml",
|
|
28
|
+
"README.md",
|
|
29
|
+
"CHANGELOG.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"THIRD_PARTY_NOTICES.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "node scripts/build.mjs && node scripts/copy-notices.mjs",
|
|
35
|
+
"grapecity:update": "node scripts/update-grapecity.mjs",
|
|
36
|
+
"prepack": "npm run build",
|
|
37
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build && node scripts/smoke-client.mjs && node scripts/smoke-node.mjs && npm run verify:package",
|
|
38
|
+
"rebuild": "node scripts/build.mjs",
|
|
39
|
+
"probe:live": "node scripts/probe-live-save.mjs",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"verify:package": "node scripts/verify-package.mjs"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://github.com/GrapeCityXA/dsh-plugin-spreadjs-editor.git"
|
|
53
|
+
},
|
|
54
|
+
"dsh": {
|
|
55
|
+
"engines": {
|
|
56
|
+
"dsh": ">=0.1.5-rc.3"
|
|
57
|
+
},
|
|
58
|
+
"bundle": {
|
|
59
|
+
"patch": "./cordis.patch.yml"
|
|
60
|
+
},
|
|
61
|
+
"client": {
|
|
62
|
+
"platform": "web",
|
|
63
|
+
"inject": [
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-sidebar-documentpreview"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@deepseek-ai/cordis": ">=4.0.0",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.3",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-sidebar-documentpreview": ">=0.1.5-rc.3",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.3",
|
|
75
|
+
"react": "^18.0.0"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@grapecity-software/spread-excelio": "19.2.0",
|
|
79
|
+
"@grapecity-software/spread-sheets": "19.2.0",
|
|
80
|
+
"@grapecity-software/spread-sheets-io": "19.2.0",
|
|
81
|
+
"@grapecity-software/spread-sheets-resources-zh": "19.2.0",
|
|
82
|
+
"@grapecity-software/spread-sheets-designer": "19.2.0",
|
|
83
|
+
"@grapecity-software/spread-sheets-designer-resources-cn": "19.2.0",
|
|
84
|
+
"@grapecity-software/spread-sheets-shapes": "19.2.0",
|
|
85
|
+
"@grapecity-software/spread-sheets-charts": "19.2.0",
|
|
86
|
+
"@grapecity-software/spread-sheets-slicers": "19.2.0",
|
|
87
|
+
"@grapecity-software/spread-sheets-sparklines": "19.2.0",
|
|
88
|
+
"@grapecity-software/spread-sheets-print": "19.2.0",
|
|
89
|
+
"@grapecity-software/spread-sheets-pdf": "19.2.0",
|
|
90
|
+
"@grapecity-software/spread-sheets-barcode": "19.2.0",
|
|
91
|
+
"@grapecity-software/spread-sheets-formula-panel": "19.2.0",
|
|
92
|
+
"@grapecity-software/spread-sheets-pivot-addon": "19.2.0",
|
|
93
|
+
"@grapecity-software/spread-sheets-tablesheet": "19.2.0",
|
|
94
|
+
"@grapecity-software/spread-sheets-datacharts-addon": "19.2.0",
|
|
95
|
+
"@grapecity-software/spread-sheets-ganttsheet": "19.2.0",
|
|
96
|
+
"@grapecity-software/spread-sheets-reportsheet-addon": "19.2.0",
|
|
97
|
+
"@grapecity-software/spread-sheets-languagepackages": "19.2.0",
|
|
98
|
+
"tsdown": "^0.22.0",
|
|
99
|
+
"typescript": "^5.9.0",
|
|
100
|
+
"unrun": "^0.3.1",
|
|
101
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.3",
|
|
103
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.3",
|
|
104
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.5-rc.3",
|
|
105
|
+
"@deepseek-ai/dsh-client-ui-sidebar-documentpreview": "0.1.5-rc.3",
|
|
106
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.3",
|
|
107
|
+
"@types/node": "^22.0.0",
|
|
108
|
+
"@types/react": "^18.3.0",
|
|
109
|
+
"react": "^18.2.0",
|
|
110
|
+
"vitest": "^3.0.0"
|
|
111
|
+
},
|
|
112
|
+
"license": "MIT"
|
|
113
|
+
}
|