dsh-vscode-workbench 0.1.1 → 0.1.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.en.md +0 -10
- package/README.md +0 -11
- package/lib/client.js +124 -56
- package/lib/client.js.map +1 -1
- package/lib/types/client/MonacoEditor.d.ts +13 -0
- package/lib/types/client/workspaceState.d.ts +20 -0
- package/package.json +1 -1
- package/src/client/MonacoEditor.tsx +11 -2
- package/src/client/Workbench.tsx +6 -2
- package/src/client/index.tsx +4 -1
- package/src/client/workspaceState.ts +37 -0
package/README.en.md
CHANGED
|
@@ -34,13 +34,6 @@ The workbench mounts through the official `sidebar.footer.action` and `shell.ove
|
|
|
34
34
|
|
|
35
35
|
## Installation
|
|
36
36
|
|
|
37
|
-
Install the aggregate package for the complete workbench with terminal integration:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
dsh plugin --profile web add dsh-vscode-plugin
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Install only the workbench and its extension API:
|
|
44
37
|
|
|
45
38
|
```bash
|
|
46
39
|
dsh plugin --profile web add dsh-vscode-workbench
|
|
@@ -58,9 +51,6 @@ dsh plugin --profile web add dsh-vscode-workbench
|
|
|
58
51
|
|
|
59
52
|
Terminal shortcuts are registered by `dsh-vscode-plugin`.
|
|
60
53
|
|
|
61
|
-
## Extension API
|
|
62
|
-
|
|
63
|
-
The browser exposes a `vscodeWorkbench` service for registering bottom panels and commands and controlling workbench visibility. `dsh-vscode-plugin` uses this API to mount `@xterm/xterm`.
|
|
64
54
|
|
|
65
55
|
## Security
|
|
66
56
|
|
package/README.md
CHANGED
|
@@ -33,14 +33,6 @@
|
|
|
33
33
|
|
|
34
34
|
## 安装
|
|
35
35
|
|
|
36
|
-
推荐安装包含终端适配器的聚合包:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
dsh plugin --profile web add dsh-vscode-plugin
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
仅使用工作台和扩展 API:
|
|
43
|
-
|
|
44
36
|
```bash
|
|
45
37
|
dsh plugin --profile web add dsh-vscode-workbench
|
|
46
38
|
```
|
|
@@ -57,9 +49,6 @@ dsh plugin --profile web add dsh-vscode-workbench
|
|
|
57
49
|
|
|
58
50
|
终端快捷键由 `dsh-vscode-plugin` 注册。
|
|
59
51
|
|
|
60
|
-
## 扩展
|
|
61
|
-
|
|
62
|
-
浏览器端提供 `vscodeWorkbench` 服务,可注册底部面板和命令,并控制工作台与面板显隐。`dsh-vscode-plugin` 使用该接口挂载 `@xterm/xterm`。
|
|
63
52
|
|
|
64
53
|
## 安全
|
|
65
54
|
|
package/lib/client.js
CHANGED
|
@@ -4191,6 +4191,17 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4191
4191
|
//#endregion
|
|
4192
4192
|
//#region src/client/MonacoEditor.tsx
|
|
4193
4193
|
let loading;
|
|
4194
|
+
/** Monaco chrome used by both editors so only the narrow native scrollbar remains. */
|
|
4195
|
+
const WORKBENCH_EDITOR_CHROME = {
|
|
4196
|
+
minimap: { enabled: false },
|
|
4197
|
+
renderOverviewRuler: false,
|
|
4198
|
+
overviewRulerLanes: 0,
|
|
4199
|
+
hideCursorInOverviewRuler: true,
|
|
4200
|
+
scrollbar: {
|
|
4201
|
+
verticalScrollbarSize: 10,
|
|
4202
|
+
horizontalScrollbarSize: 10
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4194
4205
|
function loadMonaco() {
|
|
4195
4206
|
loading ??= new Promise((resolve, reject) => {
|
|
4196
4207
|
const start = () => {
|
|
@@ -4233,9 +4244,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4233
4244
|
renderSideBySide: props.mode === "split",
|
|
4234
4245
|
theme: props.theme === "dark" ? "vs-dark" : "vs",
|
|
4235
4246
|
fontSize: 14,
|
|
4236
|
-
|
|
4247
|
+
...WORKBENCH_EDITOR_CHROME,
|
|
4237
4248
|
originalEditable: false,
|
|
4238
|
-
renderOverviewRuler: true,
|
|
4239
4249
|
ignoreTrimWhitespace: false
|
|
4240
4250
|
});
|
|
4241
4251
|
instance.setModel({
|
|
@@ -4300,7 +4310,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4300
4310
|
automaticLayout: true,
|
|
4301
4311
|
theme: props.theme === "dark" ? "vs-dark" : "vs",
|
|
4302
4312
|
fontSize: 14,
|
|
4303
|
-
|
|
4313
|
+
...WORKBENCH_EDITOR_CHROME,
|
|
4304
4314
|
wordWrap: "off"
|
|
4305
4315
|
});
|
|
4306
4316
|
editor.current = instance;
|
|
@@ -4427,6 +4437,37 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4427
4437
|
});
|
|
4428
4438
|
}
|
|
4429
4439
|
//#endregion
|
|
4440
|
+
//#region src/client/workspaceState.ts
|
|
4441
|
+
/** Remove editor state that belongs to the previous workspace. */
|
|
4442
|
+
function resetWorkspaceDocuments() {
|
|
4443
|
+
return {
|
|
4444
|
+
tabs: [],
|
|
4445
|
+
active: void 0,
|
|
4446
|
+
error: void 0
|
|
4447
|
+
};
|
|
4448
|
+
}
|
|
4449
|
+
/** Identifies responses that still belong to the active workspace request. */
|
|
4450
|
+
var WorkspaceRequestScope = class {
|
|
4451
|
+
epoch = 0;
|
|
4452
|
+
/** Begin loading another workspace and invalidate earlier work. */
|
|
4453
|
+
begin() {
|
|
4454
|
+
this.epoch += 1;
|
|
4455
|
+
return this.epoch;
|
|
4456
|
+
}
|
|
4457
|
+
/** Return the active workspace request identifier. */
|
|
4458
|
+
current() {
|
|
4459
|
+
return this.epoch;
|
|
4460
|
+
}
|
|
4461
|
+
/** Invalidate an active request while preserving any newer request. */
|
|
4462
|
+
invalidate(epoch) {
|
|
4463
|
+
if (this.epoch === epoch) this.epoch += 1;
|
|
4464
|
+
}
|
|
4465
|
+
/** Return whether a response may update the current workspace. */
|
|
4466
|
+
isCurrent(epoch) {
|
|
4467
|
+
return epoch === this.epoch;
|
|
4468
|
+
}
|
|
4469
|
+
};
|
|
4470
|
+
//#endregion
|
|
4430
4471
|
//#region src/client/Workbench.tsx
|
|
4431
4472
|
/** Return the basename shown in a tab while preserving its full internal path. */
|
|
4432
4473
|
function tabLabel(tab) {
|
|
@@ -5234,6 +5275,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5234
5275
|
const [chatWidth, setChatWidth] = (0, react.useState)(() => Math.max(400, Math.min(720, Math.round(Number(localStorage.getItem("dvw:dsh-chat-width")) || 420))));
|
|
5235
5276
|
const [chatVisible, setChatVisible] = (0, react.useState)(() => localStorage.getItem("dvw:dsh-chat-visible") !== "closed");
|
|
5236
5277
|
const [outlineVisible, setOutlineVisible] = (0, react.useState)(() => localStorage.getItem("dvw:outline") !== "closed");
|
|
5278
|
+
const tabsRef = (0, react.useRef)(tabs);
|
|
5279
|
+
tabsRef.current = tabs;
|
|
5280
|
+
const workspaceRequests = (0, react.useRef)(new WorkspaceRequestScope());
|
|
5237
5281
|
const dshDark = useDshDarkTheme();
|
|
5238
5282
|
const [theme, setTheme] = (0, react.useState)(() => {
|
|
5239
5283
|
const saved = localStorage.getItem("dvw:theme");
|
|
@@ -5256,9 +5300,69 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5256
5300
|
window.addEventListener("keydown", onKey, true);
|
|
5257
5301
|
return () => window.removeEventListener("keydown", onKey, true);
|
|
5258
5302
|
}, [snapshot.visible]);
|
|
5303
|
+
const openForWorkspace = async (targetSessionId, epoch, path, line, column) => {
|
|
5304
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5305
|
+
if (tabsRef.current.find((tab) => tab.path === path)) {
|
|
5306
|
+
setActive(path);
|
|
5307
|
+
setTabs((rows) => rows.map((tab) => tab.path === path ? {
|
|
5308
|
+
...tab,
|
|
5309
|
+
line,
|
|
5310
|
+
column
|
|
5311
|
+
} : tab));
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
if (/\.(?:avif|bmp|gif|ico|jpe?g|png|svg|webp)$/i.test(path)) {
|
|
5315
|
+
const imageUrl = `/dsh-vscode/file?sessionId=${encodeURIComponent(targetSessionId)}&path=${encodeURIComponent(path)}`;
|
|
5316
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5317
|
+
setTabs((rows) => workspaceRequests.current.isCurrent(epoch) ? [...rows.filter((tab) => tab.path !== path), {
|
|
5318
|
+
path,
|
|
5319
|
+
content: "",
|
|
5320
|
+
draft: "",
|
|
5321
|
+
revision: "",
|
|
5322
|
+
size: 0,
|
|
5323
|
+
binary: false,
|
|
5324
|
+
tooLarge: false,
|
|
5325
|
+
dirty: false,
|
|
5326
|
+
imageUrl
|
|
5327
|
+
}] : rows);
|
|
5328
|
+
setActive(path);
|
|
5329
|
+
return;
|
|
5330
|
+
}
|
|
5331
|
+
try {
|
|
5332
|
+
const doc = await api("fs.read", {
|
|
5333
|
+
sessionId: targetSessionId,
|
|
5334
|
+
path
|
|
5335
|
+
});
|
|
5336
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5337
|
+
if (doc.binary || doc.tooLarge) {
|
|
5338
|
+
setError(doc.binary ? "二进制文件不能在文本编辑器中打开" : `文件超过 5 MiB:${path}`);
|
|
5339
|
+
return;
|
|
5340
|
+
}
|
|
5341
|
+
setTabs((rows) => workspaceRequests.current.isCurrent(epoch) ? [...rows.filter((tab) => tab.path !== path), {
|
|
5342
|
+
...doc,
|
|
5343
|
+
draft: doc.content,
|
|
5344
|
+
dirty: false,
|
|
5345
|
+
line,
|
|
5346
|
+
column,
|
|
5347
|
+
preview: /\.md$/i.test(path)
|
|
5348
|
+
}] : rows);
|
|
5349
|
+
setActive(path);
|
|
5350
|
+
} catch (error) {
|
|
5351
|
+
if (workspaceRequests.current.isCurrent(epoch)) setError(String(error));
|
|
5352
|
+
}
|
|
5353
|
+
};
|
|
5259
5354
|
(0, react.useEffect)(() => {
|
|
5260
5355
|
if (!snapshot.visible || sessionId === void 0) return;
|
|
5261
|
-
|
|
5356
|
+
const epoch = workspaceRequests.current.begin();
|
|
5357
|
+
const cleared = resetWorkspaceDocuments();
|
|
5358
|
+
setWorkspace(void 0);
|
|
5359
|
+
setTabs(cleared.tabs);
|
|
5360
|
+
tabsRef.current = cleared.tabs;
|
|
5361
|
+
setActive(cleared.active);
|
|
5362
|
+
setError(cleared.error);
|
|
5363
|
+
const controller = new AbortController();
|
|
5364
|
+
api("workspace", { sessionId }, controller.signal).then((value) => {
|
|
5365
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5262
5366
|
setWorkspace(value);
|
|
5263
5367
|
const saved = localStorage.getItem(`dvw:${value.cwd}`);
|
|
5264
5368
|
if (saved) try {
|
|
@@ -5266,9 +5370,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5266
5370
|
if (parsed.view) service.show(parsed.view);
|
|
5267
5371
|
if (typeof parsed.panelHeight === "number") setPanelHeight(parsed.panelHeight);
|
|
5268
5372
|
if (typeof parsed.sideWidth === "number") setSideWidth(Math.max(210, Math.min(640, Math.round(parsed.sideWidth))));
|
|
5269
|
-
for (const path of parsed.paths ?? [])
|
|
5373
|
+
for (const path of parsed.paths ?? []) openForWorkspace(sessionId, epoch, path);
|
|
5270
5374
|
} catch {}
|
|
5271
|
-
}).catch((error) =>
|
|
5375
|
+
}).catch((error) => {
|
|
5376
|
+
if (!controller.signal.aborted && workspaceRequests.current.isCurrent(epoch)) setError(String(error));
|
|
5377
|
+
});
|
|
5378
|
+
return () => {
|
|
5379
|
+
controller.abort();
|
|
5380
|
+
workspaceRequests.current.invalidate(epoch);
|
|
5381
|
+
};
|
|
5272
5382
|
}, [snapshot.visible, sessionId]);
|
|
5273
5383
|
(0, react.useEffect)(() => {
|
|
5274
5384
|
if (workspace) localStorage.setItem(`dvw:${workspace.cwd}`, JSON.stringify({
|
|
@@ -5299,52 +5409,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5299
5409
|
}, [snapshot.visible, sessionId]);
|
|
5300
5410
|
const open = async (path, line, column) => {
|
|
5301
5411
|
if (sessionId === void 0) return;
|
|
5302
|
-
|
|
5303
|
-
setActive(path);
|
|
5304
|
-
setTabs((rows) => rows.map((t) => t.path === path ? {
|
|
5305
|
-
...t,
|
|
5306
|
-
line,
|
|
5307
|
-
column
|
|
5308
|
-
} : t));
|
|
5309
|
-
return;
|
|
5310
|
-
}
|
|
5311
|
-
if (/\.(?:avif|bmp|gif|ico|jpe?g|png|svg|webp)$/i.test(path)) {
|
|
5312
|
-
const imageUrl = `/dsh-vscode/file?sessionId=${encodeURIComponent(sessionId)}&path=${encodeURIComponent(path)}`;
|
|
5313
|
-
setTabs((rows) => [...rows, {
|
|
5314
|
-
path,
|
|
5315
|
-
content: "",
|
|
5316
|
-
draft: "",
|
|
5317
|
-
revision: "",
|
|
5318
|
-
size: 0,
|
|
5319
|
-
binary: false,
|
|
5320
|
-
tooLarge: false,
|
|
5321
|
-
dirty: false,
|
|
5322
|
-
imageUrl
|
|
5323
|
-
}]);
|
|
5324
|
-
setActive(path);
|
|
5325
|
-
return;
|
|
5326
|
-
}
|
|
5327
|
-
try {
|
|
5328
|
-
const doc = await api("fs.read", {
|
|
5329
|
-
sessionId,
|
|
5330
|
-
path
|
|
5331
|
-
});
|
|
5332
|
-
if (doc.binary || doc.tooLarge) {
|
|
5333
|
-
setError(doc.binary ? "二进制文件不能在文本编辑器中打开" : `文件超过 5 MiB:${path}`);
|
|
5334
|
-
return;
|
|
5335
|
-
}
|
|
5336
|
-
setTabs((rows) => [...rows, {
|
|
5337
|
-
...doc,
|
|
5338
|
-
draft: doc.content,
|
|
5339
|
-
dirty: false,
|
|
5340
|
-
line,
|
|
5341
|
-
column,
|
|
5342
|
-
preview: /\.md$/i.test(path)
|
|
5343
|
-
}]);
|
|
5344
|
-
setActive(path);
|
|
5345
|
-
} catch (error) {
|
|
5346
|
-
setError(String(error));
|
|
5347
|
-
}
|
|
5412
|
+
await openForWorkspace(sessionId, workspaceRequests.current.current(), path, line, column);
|
|
5348
5413
|
};
|
|
5349
5414
|
(0, react.useEffect)(() => {
|
|
5350
5415
|
const listener = (event) => void open(event.detail);
|
|
@@ -5964,10 +6029,13 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
|
|
|
5964
6029
|
}
|
|
5965
6030
|
//#endregion
|
|
5966
6031
|
//#region src/client/index.tsx
|
|
5967
|
-
const Overlay = ({ service, useSessions }) =>
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
6032
|
+
const Overlay = ({ service, useSessions }) => {
|
|
6033
|
+
const sessionId = useSessions((state) => state.current);
|
|
6034
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Workbench, {
|
|
6035
|
+
service,
|
|
6036
|
+
useSessions
|
|
6037
|
+
}, sessionId ?? "no-session");
|
|
6038
|
+
};
|
|
5971
6039
|
const Footer = ({ service, wide }) => {
|
|
5972
6040
|
const marker = (0, react.useRef)(null);
|
|
5973
6041
|
const [target, setTarget] = (0, react.useState)();
|