@guowenzhang/dsh-whiteboard 0.2.0 → 0.2.2
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 +23 -14
- package/README.zh.md +23 -14
- package/docs/images/editor.png +0 -0
- package/docs/images/gallery.png +0 -0
- package/docs/images/new-board.png +0 -0
- package/lib/client.js +63 -5
- package/package.json +64 -20
package/README.md
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
# dsh-whiteboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
## Why I built this plugin
|
|
6
|
+
|
|
7
|
+
I often need somewhere to jot down drafts while thinking. I used to do that in documents, but now that AI tools are part of my everyday workflow, I wanted that space right inside DeepSeek Harness. A whiteboard is more flexible for sketching ideas and mapping out processes. It uses the open `.drawio` file format, so boards can also be edited in draw.io and other compatible tools.
|
|
8
|
+
|
|
9
|
+
## Screenshots
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
The editor screenshot shows an earlier version. The current version keeps the gallery visible beside the editor.
|
|
4
18
|
|
|
5
19
|
## Install
|
|
6
20
|
|
|
7
|
-
Install
|
|
21
|
+
Install the editor dependency first, then the whiteboard plugin:
|
|
8
22
|
|
|
9
23
|
```sh
|
|
10
24
|
npx @deepseek-ai/dsh plugin --profile web add @guowenzhang/dsh-drawioedit
|
|
11
25
|
npx @deepseek-ai/dsh plugin --profile web add @guowenzhang/dsh-whiteboard
|
|
12
26
|
```
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Restart DSH Web and open **Whiteboards** in the left navigation. **New whiteboard** first opens a dialog with an editable timestamp-based name. Only **Create** writes the `.drawio` file under the plugin's `files/` directory and opens it in drawioedit. Drawioedit autosaves changes. Each card shows its name, a first-page preview, and last modification time. Click the card to edit, or click the top-right X and confirm to delete the file. The persistent search box in the toolbar filters the loaded cards by name and shows an empty-result hint when nothing matches. Returning to the **Whiteboards** page goes straight back to the board whose editor tab is still open — collapsing the Sidebar or switching pages keeps it — and only an explicit tab close, a deleted board or a replaced file leaves you on the gallery. Oversized or unreadable diagrams show a preview placeholder. On desktop, opening a board expands the editor column to fullscreen so draw.io can show its own left shapes palette and right format panel; collapse the Sidebar or leave fullscreen to get back to the gallery and click a card to continue editing.
|
|
28
|
+
Restart DSH Web, open **Whiteboards** in the left navigation, and click **New whiteboard** to confirm a name and get started. Click an existing card to continue editing.
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
## Notes
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
- The editor saves your drawings automatically; no manual saving is needed.
|
|
33
|
+
- Boards are stored in the plugin's `files/` directory, not the current workspace. Back up that directory before updating, replacing, or uninstalling the plugin.
|
|
34
|
+
- Deleting a board removes its file, so check before confirming.
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
npm ci --legacy-peer-deps
|
|
24
|
-
npm run typecheck
|
|
25
|
-
npm run build
|
|
26
|
-
npm test
|
|
27
|
-
```
|
|
36
|
+
## License
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
[Apache-2.0](LICENSE). See [NOTICE](NOTICE) for third-party notices.
|
package/README.zh.md
CHANGED
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
# dsh-whiteboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
## 为什么做这个插件
|
|
6
|
+
|
|
7
|
+
我在思考时,经常需要一个地方记录草稿,以前一般会写在文档里。现在全面使用 AI 工具,就把这个空间直接集成到了 DeepSeek Harness 中。白板比文档更灵活,适合随手记录想法、梳理流程;采用开放的 `.drawio` 文件格式,也方便在 draw.io 等兼容工具中继续编辑。
|
|
8
|
+
|
|
9
|
+
## 截图
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
编辑器截图来自旧版;当前版本在编辑器旁保留白板列表。
|
|
4
18
|
|
|
5
19
|
## 安装
|
|
6
20
|
|
|
7
|
-
|
|
21
|
+
先安装编辑器依赖,再安装白板插件:
|
|
8
22
|
|
|
9
23
|
```sh
|
|
10
24
|
npx @deepseek-ai/dsh plugin --profile web add @guowenzhang/dsh-drawioedit
|
|
11
25
|
npx @deepseek-ai/dsh plugin --profile web add @guowenzhang/dsh-whiteboard
|
|
12
26
|
```
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
重启 DSH Web 后,打开左侧导航中的“白板”。点击“新建白板”会先显示命名对话框,默认填入时间名称,可直接修改;只有点击“确认创建”才会在插件 `files/` 目录创建 `.drawio` 文件并交给 drawioedit 打开。绘图内容由 drawioedit 自动保存。卡片显示名称、第一页缩略图和最近修改时间;点击卡片可继续编辑,点击右上角叉号并确认可删除文件。工具条上的搜索框按名称筛选当前卡片列表,不匹配时显示空结果提示。再次进入“白板”页时,如果上一块白板的编辑标签仍开着(只是收起过侧边栏或切到过别的页面),会直接回到那块白板;只有主动关闭标签、或该文件已被删除或替换,才会停在白板列表。过大或无法解析的图表显示预览占位内容。桌面端打开白板时右侧编辑列展开为全屏,编辑器可以完整显示自己的左侧形状面板和右侧格式面板;收起侧边栏或退出全屏即可回到白板列表,再点卡片可继续编辑。
|
|
28
|
+
重启 DSH Web,打开左侧“白板”,点击“新建白板”并确认名称即可开始;点击已有卡片可继续编辑。
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
## 注意事项
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
- 绘图内容由编辑器自动保存,无需手动保存。
|
|
33
|
+
- 白板保存在插件的 `files/` 目录,不在当前工作区;更新、替换或卸载插件前请备份该目录。
|
|
34
|
+
- 删除白板会删除对应文件,请确认后再操作。
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
npm ci --legacy-peer-deps
|
|
24
|
-
npm run typecheck
|
|
25
|
-
npm run build
|
|
26
|
-
npm test
|
|
27
|
-
```
|
|
36
|
+
## 许可
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
[Apache-2.0](LICENSE),第三方组件声明见 [NOTICE](NOTICE)。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -187,7 +187,7 @@ window.__ModuleLoader__.load({
|
|
|
187
187
|
};
|
|
188
188
|
//#endregion
|
|
189
189
|
//#region src/client/last-board.ts
|
|
190
|
-
const STORAGE_KEY = "dsh.whiteboard.v1.last-board";
|
|
190
|
+
const STORAGE_KEY$1 = "dsh.whiteboard.v1.last-board";
|
|
191
191
|
function storage() {
|
|
192
192
|
try {
|
|
193
193
|
return typeof window === "undefined" ? void 0 : window.localStorage;
|
|
@@ -200,7 +200,7 @@ window.__ModuleLoader__.load({
|
|
|
200
200
|
const store = storage();
|
|
201
201
|
if (!store) return void 0;
|
|
202
202
|
try {
|
|
203
|
-
const raw = store.getItem(STORAGE_KEY);
|
|
203
|
+
const raw = store.getItem(STORAGE_KEY$1);
|
|
204
204
|
if (raw === null) return void 0;
|
|
205
205
|
const value = JSON.parse(raw);
|
|
206
206
|
if (typeof value !== "object" || value === null) return void 0;
|
|
@@ -220,7 +220,7 @@ window.__ModuleLoader__.load({
|
|
|
220
220
|
const store = storage();
|
|
221
221
|
if (!store) return;
|
|
222
222
|
try {
|
|
223
|
-
store.setItem(STORAGE_KEY, JSON.stringify({
|
|
223
|
+
store.setItem(STORAGE_KEY$1, JSON.stringify({
|
|
224
224
|
id: board.id,
|
|
225
225
|
title: board.title,
|
|
226
226
|
updatedAt: board.updatedAt
|
|
@@ -232,7 +232,7 @@ window.__ModuleLoader__.load({
|
|
|
232
232
|
const store = storage();
|
|
233
233
|
if (!store) return;
|
|
234
234
|
try {
|
|
235
|
-
store.removeItem(STORAGE_KEY);
|
|
235
|
+
store.removeItem(STORAGE_KEY$1);
|
|
236
236
|
} catch {}
|
|
237
237
|
}
|
|
238
238
|
/**
|
|
@@ -30894,6 +30894,60 @@ segment: 30 };
|
|
|
30894
30894
|
});
|
|
30895
30895
|
}
|
|
30896
30896
|
//#endregion
|
|
30897
|
+
//#region src/client/board-session.ts
|
|
30898
|
+
const STORAGE_KEY = "dsh.whiteboard.v1.session";
|
|
30899
|
+
/** The dedicated editor Session survives reloads; it is not a chat destination. */
|
|
30900
|
+
function readBoardSession() {
|
|
30901
|
+
try {
|
|
30902
|
+
return window.localStorage?.getItem(STORAGE_KEY) || void 0;
|
|
30903
|
+
} catch {
|
|
30904
|
+
return;
|
|
30905
|
+
}
|
|
30906
|
+
}
|
|
30907
|
+
function writeBoardSession(sessionId) {
|
|
30908
|
+
try {
|
|
30909
|
+
window.localStorage?.setItem(STORAGE_KEY, sessionId);
|
|
30910
|
+
} catch {}
|
|
30911
|
+
}
|
|
30912
|
+
/**
|
|
30913
|
+
* Replace only a known whiteboard Session's central occupant, not its navigation
|
|
30914
|
+
* or rightbar binding. Selecting the global whiteboard panel here would hide the
|
|
30915
|
+
* rightbar: the Host only exposes it while the Conversation main key is selected.
|
|
30916
|
+
* The normal Conversation registration stays installed underneath this shadow.
|
|
30917
|
+
*/
|
|
30918
|
+
function installBoardSessionPanel(ctx, owned, face) {
|
|
30919
|
+
let reconcile = () => {};
|
|
30920
|
+
ctx.slots.inject("main", () => {
|
|
30921
|
+
let release;
|
|
30922
|
+
reconcile = () => {
|
|
30923
|
+
const sessionId = ctx.sidebarRight.mounted.getSnapshot();
|
|
30924
|
+
const showList = sessionId !== void 0 && owned(sessionId);
|
|
30925
|
+
if (showList && !release) release = ctx.slots.register({
|
|
30926
|
+
name: "main",
|
|
30927
|
+
key: "conversation",
|
|
30928
|
+
priority: -1,
|
|
30929
|
+
locale: NS,
|
|
30930
|
+
inject: () => ({
|
|
30931
|
+
...face,
|
|
30932
|
+
restore: async () => "none"
|
|
30933
|
+
})
|
|
30934
|
+
}, WhiteboardPanel);
|
|
30935
|
+
else if (!showList && release) {
|
|
30936
|
+
release();
|
|
30937
|
+
release = void 0;
|
|
30938
|
+
}
|
|
30939
|
+
};
|
|
30940
|
+
const unsubscribe = ctx.sidebarRight.mounted.subscribe(() => reconcile());
|
|
30941
|
+
reconcile();
|
|
30942
|
+
return () => {
|
|
30943
|
+
unsubscribe();
|
|
30944
|
+
release?.();
|
|
30945
|
+
reconcile = () => {};
|
|
30946
|
+
};
|
|
30947
|
+
});
|
|
30948
|
+
return () => reconcile();
|
|
30949
|
+
}
|
|
30950
|
+
//#endregion
|
|
30897
30951
|
//#region src/client/index.tsx
|
|
30898
30952
|
async function unwrap(call) {
|
|
30899
30953
|
const result = await call;
|
|
@@ -30941,7 +30995,8 @@ segment: 30 };
|
|
|
30941
30995
|
return service;
|
|
30942
30996
|
};
|
|
30943
30997
|
const sessions = ctx.sessions;
|
|
30944
|
-
let lastSessionId;
|
|
30998
|
+
let lastSessionId = readBoardSession();
|
|
30999
|
+
let syncBoardPanel = () => {};
|
|
30945
31000
|
/** The drawio editor tab open for the mounted session: the active tab or a background one. */
|
|
30946
31001
|
const openEditorTab = (sessionId) => {
|
|
30947
31002
|
const known = ctx.sidebarRight.openTabs.getSnapshot().find((tab) => tab.sessionId === sessionId && tab.kind === "drawio-edit");
|
|
@@ -30968,6 +31023,8 @@ segment: 30 };
|
|
|
30968
31023
|
if (!sessionId) sessionId = await sessions.create({ cwd: board.directory });
|
|
30969
31024
|
await unwrap(remote().grantBoardSession({ sessionId }));
|
|
30970
31025
|
lastSessionId = sessionId;
|
|
31026
|
+
writeBoardSession(sessionId);
|
|
31027
|
+
syncBoardPanel();
|
|
30971
31028
|
writeLastBoard(board);
|
|
30972
31029
|
if (ctx.sidebarRight.mounted.getSnapshot() !== sessionId) {
|
|
30973
31030
|
ctx.uiWorkspace.openSession(sessionId);
|
|
@@ -30995,6 +31052,7 @@ segment: 30 };
|
|
|
30995
31052
|
return "reopened";
|
|
30996
31053
|
}
|
|
30997
31054
|
};
|
|
31055
|
+
syncBoardPanel = installBoardSessionPanel(ctx, (id) => id === lastSessionId, face);
|
|
30998
31056
|
ctx.slots.inject("main", () => ctx.slots.register({
|
|
30999
31057
|
name: "main",
|
|
31000
31058
|
key: "whiteboard",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guowenzhang/dsh-whiteboard",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Whiteboards for DeepSeek Harness: a board gallery in the main panel and draw.io editing in the Sidebar, with boards stored inside the plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"main": "lib/index.mjs",
|
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
"./package.json": "./package.json",
|
|
12
12
|
"./cordis.patch.yml": "./cordis.patch.yml"
|
|
13
13
|
},
|
|
14
|
-
"engines": {
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": "^22.19.0 || >=24.0.0",
|
|
16
|
+
"dsh": ">=0.2.0-rc.1"
|
|
17
|
+
},
|
|
15
18
|
"dsh": {
|
|
16
|
-
"bundle": {
|
|
19
|
+
"bundle": {
|
|
20
|
+
"patch": "./cordis.patch.yml"
|
|
21
|
+
},
|
|
17
22
|
"client": {
|
|
18
23
|
"inject": [
|
|
19
24
|
"@deepseek-ai/dsh-api-remotes",
|
|
@@ -29,7 +34,16 @@
|
|
|
29
34
|
"platform": "web"
|
|
30
35
|
}
|
|
31
36
|
},
|
|
32
|
-
"files": [
|
|
37
|
+
"files": [
|
|
38
|
+
"lib",
|
|
39
|
+
"docs",
|
|
40
|
+
"files/README.md",
|
|
41
|
+
"cordis.patch.yml",
|
|
42
|
+
"README.md",
|
|
43
|
+
"README.zh.md",
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"NOTICE"
|
|
46
|
+
],
|
|
33
47
|
"scripts": {
|
|
34
48
|
"build": "tsdown -c tsdown.config.ts && node build-client.mjs",
|
|
35
49
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
@@ -54,21 +68,51 @@
|
|
|
54
68
|
"react": "*"
|
|
55
69
|
},
|
|
56
70
|
"peerDependenciesMeta": {
|
|
57
|
-
"@deepseek-ai/cordis": {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"@deepseek-ai/dsh-client-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"@deepseek-ai/dsh-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
"@deepseek-ai/cordis": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@deepseek-ai/dsh-api-remotes": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@deepseek-ai/dsh-api-session-controller": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-layout": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-sidebar": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@deepseek-ai/dsh-client-ui-workspace": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"@deepseek-ai/dsh-sandbox-policy": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@deepseek-ai/dsh-session": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@deepseek-ai/dsh-util-workspace-path": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@deepseek-ai/dsh-typert-protocol": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"react": {
|
|
114
|
+
"optional": true
|
|
115
|
+
}
|
|
72
116
|
},
|
|
73
117
|
"devDependencies": {
|
|
74
118
|
"@deepseek-ai/cordis": "^4.0.4",
|