dsh-vscode-workbench 0.1.1 → 0.1.3
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 +3 -11
- package/README.md +3 -12
- package/lib/client.js +192 -84
- package/lib/client.js.map +1 -1
- package/lib/types/client/MonacoEditor.d.ts +13 -0
- package/lib/types/client/Workbench.d.ts +9 -3
- package/lib/types/client/shortcuts.d.ts +3 -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 +40 -19
- package/src/client/index.tsx +6 -1
- package/src/client/shortcuts.ts +17 -0
- 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
|
|
@@ -55,12 +48,11 @@ dsh plugin --profile web add dsh-vscode-workbench
|
|
|
55
48
|
| `Ctrl+Shift+G` | Open Source Control |
|
|
56
49
|
| `Ctrl+S` | Save the active file |
|
|
57
50
|
| `Ctrl+Alt+B` | Toggle the DSH conversation panel |
|
|
51
|
+
| `Ctrl+\`` | Toggle the registered terminal panel |
|
|
52
|
+
| `Ctrl+Shift+\`` | Create a terminal and show its panel |
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Extension API
|
|
54
|
+
The terminal remains an independent plugin. This workbench only owns the shortcuts and invokes it through `registerBottomPanel()` and the `terminal.new` command.
|
|
62
55
|
|
|
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
56
|
|
|
65
57
|
## Security
|
|
66
58
|
|
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
|
```
|
|
@@ -54,12 +46,11 @@ dsh plugin --profile web add dsh-vscode-workbench
|
|
|
54
46
|
| `Ctrl+Shift+G` | 打开源代码管理 |
|
|
55
47
|
| `Ctrl+S` | 保存当前文件 |
|
|
56
48
|
| `Ctrl+Alt+B` | 显示或隐藏 DSH 对话栏 |
|
|
49
|
+
| `Ctrl+\`` | 显示或隐藏已注册的终端面板 |
|
|
50
|
+
| `Ctrl+Shift+\`` | 新建终端并显示终端面板 |
|
|
57
51
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
## 扩展
|
|
52
|
+
终端仍由独立插件提供;本工作台只注册快捷键,并通过 `registerBottomPanel()` 和 `terminal.new` 命令调用它。
|
|
61
53
|
|
|
62
|
-
浏览器端提供 `vscodeWorkbench` 服务,可注册底部面板和命令,并控制工作台与面板显隐。`dsh-vscode-plugin` 使用该接口挂载 `@xterm/xterm`。
|
|
63
54
|
|
|
64
55
|
## 安全
|
|
65
56
|
|
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) {
|
|
@@ -4753,20 +4794,33 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4753
4794
|
}
|
|
4754
4795
|
function graphRows(commits) {
|
|
4755
4796
|
const visible = new Set(commits.map((commit) => commit.hash));
|
|
4756
|
-
let
|
|
4797
|
+
let nextColor = 0;
|
|
4798
|
+
let lanes = commits[0] ? [{
|
|
4799
|
+
hash: commits[0].hash,
|
|
4800
|
+
color: nextColor++
|
|
4801
|
+
}] : [];
|
|
4757
4802
|
return commits.map((commit) => {
|
|
4758
|
-
let lane = lanes.
|
|
4803
|
+
let lane = lanes.findIndex((track) => track.hash === commit.hash);
|
|
4759
4804
|
if (lane < 0) {
|
|
4760
|
-
lanes = [
|
|
4805
|
+
lanes = [{
|
|
4806
|
+
hash: commit.hash,
|
|
4807
|
+
color: nextColor++
|
|
4808
|
+
}, ...lanes];
|
|
4761
4809
|
lane = 0;
|
|
4762
4810
|
}
|
|
4763
4811
|
const before = [...lanes];
|
|
4764
|
-
const
|
|
4812
|
+
const current = before[lane];
|
|
4813
|
+
const after = before.filter((track) => track.hash !== commit.hash);
|
|
4765
4814
|
const parents = commit.parents.filter((parent) => visible.has(parent));
|
|
4766
4815
|
parents.forEach((parent, index) => {
|
|
4767
|
-
if (
|
|
4816
|
+
if (after.some((track) => track.hash === parent)) return;
|
|
4817
|
+
const color = index === 0 ? current.color : nextColor++;
|
|
4818
|
+
after.splice(Math.min(lane + index, after.length), 0, {
|
|
4819
|
+
hash: parent,
|
|
4820
|
+
color
|
|
4821
|
+
});
|
|
4768
4822
|
});
|
|
4769
|
-
const parentLanes = parents.map((parent) => after.
|
|
4823
|
+
const parentLanes = parents.map((parent) => after.findIndex((track) => track.hash === parent)).filter((index) => index >= 0);
|
|
4770
4824
|
lanes = after;
|
|
4771
4825
|
return {
|
|
4772
4826
|
commit,
|
|
@@ -4784,15 +4838,23 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4784
4838
|
"#d9a441",
|
|
4785
4839
|
"#9b7bea"
|
|
4786
4840
|
];
|
|
4787
|
-
function graphColor(
|
|
4788
|
-
return GRAPH_COLORS[
|
|
4841
|
+
function graphColor(color) {
|
|
4842
|
+
return GRAPH_COLORS[color % GRAPH_COLORS.length];
|
|
4789
4843
|
}
|
|
4790
4844
|
function graphWidth(row) {
|
|
4791
4845
|
return Math.max(row.before.length, row.after.length, row.lane + 1, 1) * 14 + 8;
|
|
4792
4846
|
}
|
|
4847
|
+
/** Draw a fixed-radius, orthogonal transition between two graph lanes. */
|
|
4848
|
+
function graphTrackPath(from, to, startY, endY, bendY) {
|
|
4849
|
+
if (from === to) return `M ${from} ${startY} V ${endY}`;
|
|
4850
|
+
const direction = Math.sign(to - from);
|
|
4851
|
+
const radius = Math.min(5, Math.abs(to - from) / 2, Math.abs(bendY - startY), Math.abs(endY - bendY));
|
|
4852
|
+
return `M ${from} ${startY} V ${bendY - radius} Q ${from} ${bendY} ${from + direction * radius} ${bendY} H ${to - direction * radius} Q ${to} ${bendY} ${to} ${bendY + radius} V ${endY}`;
|
|
4853
|
+
}
|
|
4793
4854
|
function GraphTracks({ row }) {
|
|
4794
4855
|
const width = graphWidth(row);
|
|
4795
4856
|
const x = row.lane * 14 + 8;
|
|
4857
|
+
const current = row.before[row.lane];
|
|
4796
4858
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
4797
4859
|
className: "dvw-graph-svg",
|
|
4798
4860
|
width,
|
|
@@ -4800,41 +4862,44 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4800
4862
|
viewBox: `0 0 ${width} 38`,
|
|
4801
4863
|
"aria-hidden": "true",
|
|
4802
4864
|
children: [
|
|
4803
|
-
row.before.map((
|
|
4804
|
-
if (hash === row.commit.hash) return null;
|
|
4805
|
-
const target = row.after.
|
|
4865
|
+
row.before.map((track, lane) => {
|
|
4866
|
+
if (track.hash === row.commit.hash) return null;
|
|
4867
|
+
const target = row.after.findIndex((next) => next.hash === track.hash);
|
|
4806
4868
|
const nextLane = target >= 0 ? target : lane;
|
|
4807
|
-
const next = nextLane * 14 + 8;
|
|
4808
|
-
const start = lane * 14 + 8;
|
|
4809
4869
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4810
|
-
d:
|
|
4870
|
+
d: graphTrackPath(lane * 14 + 8, nextLane * 14 + 8, 0, 38, 19),
|
|
4811
4871
|
fill: "none",
|
|
4812
|
-
stroke: graphColor(
|
|
4813
|
-
strokeWidth: "2"
|
|
4814
|
-
|
|
4872
|
+
stroke: graphColor(track.color),
|
|
4873
|
+
strokeWidth: "2",
|
|
4874
|
+
vectorEffect: "non-scaling-stroke"
|
|
4875
|
+
}, `continue-${track.hash}`);
|
|
4815
4876
|
}),
|
|
4816
4877
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4817
4878
|
d: `M ${x} 0 V 19`,
|
|
4818
4879
|
fill: "none",
|
|
4819
|
-
stroke: graphColor(
|
|
4820
|
-
strokeWidth: "2"
|
|
4880
|
+
stroke: graphColor(current.color),
|
|
4881
|
+
strokeWidth: "2",
|
|
4882
|
+
vectorEffect: "non-scaling-stroke"
|
|
4821
4883
|
}),
|
|
4822
4884
|
row.parentLanes.map((parent, index) => {
|
|
4823
4885
|
const target = parent * 14 + 8;
|
|
4886
|
+
const track = row.after[parent];
|
|
4824
4887
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4825
|
-
d:
|
|
4888
|
+
d: graphTrackPath(x, target, 19, 38, 29),
|
|
4826
4889
|
fill: "none",
|
|
4827
|
-
stroke: graphColor(
|
|
4828
|
-
strokeWidth: "2"
|
|
4829
|
-
|
|
4890
|
+
stroke: graphColor(track.color),
|
|
4891
|
+
strokeWidth: "2",
|
|
4892
|
+
vectorEffect: "non-scaling-stroke"
|
|
4893
|
+
}, `parent-${track.hash}-${index}`);
|
|
4830
4894
|
}),
|
|
4831
4895
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
4832
4896
|
cx: x,
|
|
4833
4897
|
cy: "19",
|
|
4834
4898
|
r: "5",
|
|
4835
4899
|
fill: "var(--dvw-surface-1)",
|
|
4836
|
-
stroke: graphColor(
|
|
4837
|
-
strokeWidth: "2"
|
|
4900
|
+
stroke: graphColor(current.color),
|
|
4901
|
+
strokeWidth: "2",
|
|
4902
|
+
vectorEffect: "non-scaling-stroke"
|
|
4838
4903
|
})
|
|
4839
4904
|
]
|
|
4840
4905
|
});
|
|
@@ -4848,15 +4913,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4848
4913
|
viewBox: `0 0 ${width} 100`,
|
|
4849
4914
|
preserveAspectRatio: "none",
|
|
4850
4915
|
"aria-hidden": "true",
|
|
4851
|
-
children: row.after.map((
|
|
4916
|
+
children: row.after.map((track, lane) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
4852
4917
|
x1: lane * 14 + 8,
|
|
4853
4918
|
y1: "0",
|
|
4854
4919
|
x2: lane * 14 + 8,
|
|
4855
4920
|
y2: "100",
|
|
4856
|
-
stroke: graphColor(
|
|
4921
|
+
stroke: graphColor(track.color),
|
|
4857
4922
|
strokeWidth: "2",
|
|
4858
4923
|
vectorEffect: "non-scaling-stroke"
|
|
4859
|
-
}, hash))
|
|
4924
|
+
}, track.hash))
|
|
4860
4925
|
});
|
|
4861
4926
|
}
|
|
4862
4927
|
function CommitGraph({ commits, expanded, files, onToggle, onOpenFile }) {
|
|
@@ -5234,6 +5299,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5234
5299
|
const [chatWidth, setChatWidth] = (0, react.useState)(() => Math.max(400, Math.min(720, Math.round(Number(localStorage.getItem("dvw:dsh-chat-width")) || 420))));
|
|
5235
5300
|
const [chatVisible, setChatVisible] = (0, react.useState)(() => localStorage.getItem("dvw:dsh-chat-visible") !== "closed");
|
|
5236
5301
|
const [outlineVisible, setOutlineVisible] = (0, react.useState)(() => localStorage.getItem("dvw:outline") !== "closed");
|
|
5302
|
+
const tabsRef = (0, react.useRef)(tabs);
|
|
5303
|
+
tabsRef.current = tabs;
|
|
5304
|
+
const workspaceRequests = (0, react.useRef)(new WorkspaceRequestScope());
|
|
5237
5305
|
const dshDark = useDshDarkTheme();
|
|
5238
5306
|
const [theme, setTheme] = (0, react.useState)(() => {
|
|
5239
5307
|
const saved = localStorage.getItem("dvw:theme");
|
|
@@ -5256,9 +5324,69 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5256
5324
|
window.addEventListener("keydown", onKey, true);
|
|
5257
5325
|
return () => window.removeEventListener("keydown", onKey, true);
|
|
5258
5326
|
}, [snapshot.visible]);
|
|
5327
|
+
const openForWorkspace = async (targetSessionId, epoch, path, line, column) => {
|
|
5328
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5329
|
+
if (tabsRef.current.find((tab) => tab.path === path)) {
|
|
5330
|
+
setActive(path);
|
|
5331
|
+
setTabs((rows) => rows.map((tab) => tab.path === path ? {
|
|
5332
|
+
...tab,
|
|
5333
|
+
line,
|
|
5334
|
+
column
|
|
5335
|
+
} : tab));
|
|
5336
|
+
return;
|
|
5337
|
+
}
|
|
5338
|
+
if (/\.(?:avif|bmp|gif|ico|jpe?g|png|svg|webp)$/i.test(path)) {
|
|
5339
|
+
const imageUrl = `/dsh-vscode/file?sessionId=${encodeURIComponent(targetSessionId)}&path=${encodeURIComponent(path)}`;
|
|
5340
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5341
|
+
setTabs((rows) => workspaceRequests.current.isCurrent(epoch) ? [...rows.filter((tab) => tab.path !== path), {
|
|
5342
|
+
path,
|
|
5343
|
+
content: "",
|
|
5344
|
+
draft: "",
|
|
5345
|
+
revision: "",
|
|
5346
|
+
size: 0,
|
|
5347
|
+
binary: false,
|
|
5348
|
+
tooLarge: false,
|
|
5349
|
+
dirty: false,
|
|
5350
|
+
imageUrl
|
|
5351
|
+
}] : rows);
|
|
5352
|
+
setActive(path);
|
|
5353
|
+
return;
|
|
5354
|
+
}
|
|
5355
|
+
try {
|
|
5356
|
+
const doc = await api("fs.read", {
|
|
5357
|
+
sessionId: targetSessionId,
|
|
5358
|
+
path
|
|
5359
|
+
});
|
|
5360
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5361
|
+
if (doc.binary || doc.tooLarge) {
|
|
5362
|
+
setError(doc.binary ? "二进制文件不能在文本编辑器中打开" : `文件超过 5 MiB:${path}`);
|
|
5363
|
+
return;
|
|
5364
|
+
}
|
|
5365
|
+
setTabs((rows) => workspaceRequests.current.isCurrent(epoch) ? [...rows.filter((tab) => tab.path !== path), {
|
|
5366
|
+
...doc,
|
|
5367
|
+
draft: doc.content,
|
|
5368
|
+
dirty: false,
|
|
5369
|
+
line,
|
|
5370
|
+
column,
|
|
5371
|
+
preview: /\.md$/i.test(path)
|
|
5372
|
+
}] : rows);
|
|
5373
|
+
setActive(path);
|
|
5374
|
+
} catch (error) {
|
|
5375
|
+
if (workspaceRequests.current.isCurrent(epoch)) setError(String(error));
|
|
5376
|
+
}
|
|
5377
|
+
};
|
|
5259
5378
|
(0, react.useEffect)(() => {
|
|
5260
5379
|
if (!snapshot.visible || sessionId === void 0) return;
|
|
5261
|
-
|
|
5380
|
+
const epoch = workspaceRequests.current.begin();
|
|
5381
|
+
const cleared = resetWorkspaceDocuments();
|
|
5382
|
+
setWorkspace(void 0);
|
|
5383
|
+
setTabs(cleared.tabs);
|
|
5384
|
+
tabsRef.current = cleared.tabs;
|
|
5385
|
+
setActive(cleared.active);
|
|
5386
|
+
setError(cleared.error);
|
|
5387
|
+
const controller = new AbortController();
|
|
5388
|
+
api("workspace", { sessionId }, controller.signal).then((value) => {
|
|
5389
|
+
if (!workspaceRequests.current.isCurrent(epoch)) return;
|
|
5262
5390
|
setWorkspace(value);
|
|
5263
5391
|
const saved = localStorage.getItem(`dvw:${value.cwd}`);
|
|
5264
5392
|
if (saved) try {
|
|
@@ -5266,9 +5394,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5266
5394
|
if (parsed.view) service.show(parsed.view);
|
|
5267
5395
|
if (typeof parsed.panelHeight === "number") setPanelHeight(parsed.panelHeight);
|
|
5268
5396
|
if (typeof parsed.sideWidth === "number") setSideWidth(Math.max(210, Math.min(640, Math.round(parsed.sideWidth))));
|
|
5269
|
-
for (const path of parsed.paths ?? [])
|
|
5397
|
+
for (const path of parsed.paths ?? []) openForWorkspace(sessionId, epoch, path);
|
|
5270
5398
|
} catch {}
|
|
5271
|
-
}).catch((error) =>
|
|
5399
|
+
}).catch((error) => {
|
|
5400
|
+
if (!controller.signal.aborted && workspaceRequests.current.isCurrent(epoch)) setError(String(error));
|
|
5401
|
+
});
|
|
5402
|
+
return () => {
|
|
5403
|
+
controller.abort();
|
|
5404
|
+
workspaceRequests.current.invalidate(epoch);
|
|
5405
|
+
};
|
|
5272
5406
|
}, [snapshot.visible, sessionId]);
|
|
5273
5407
|
(0, react.useEffect)(() => {
|
|
5274
5408
|
if (workspace) localStorage.setItem(`dvw:${workspace.cwd}`, JSON.stringify({
|
|
@@ -5299,52 +5433,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
5299
5433
|
}, [snapshot.visible, sessionId]);
|
|
5300
5434
|
const open = async (path, line, column) => {
|
|
5301
5435
|
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
|
-
}
|
|
5436
|
+
await openForWorkspace(sessionId, workspaceRequests.current.current(), path, line, column);
|
|
5348
5437
|
};
|
|
5349
5438
|
(0, react.useEffect)(() => {
|
|
5350
5439
|
const listener = (event) => void open(event.detail);
|
|
@@ -5963,11 +6052,29 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
|
|
|
5963
6052
|
};
|
|
5964
6053
|
}
|
|
5965
6054
|
//#endregion
|
|
6055
|
+
//#region src/client/shortcuts.ts
|
|
6056
|
+
/** Handle terminal shortcuts through contributions registered by a terminal adapter. */
|
|
6057
|
+
function handleTerminalShortcut(event, workbench) {
|
|
6058
|
+
if (event.isComposing || !event.ctrlKey || event.altKey || event.code !== "Backquote") return false;
|
|
6059
|
+
if (!workbench.getPanels().some((panel) => panel.id === "terminal")) return false;
|
|
6060
|
+
event.preventDefault();
|
|
6061
|
+
event.stopImmediatePropagation();
|
|
6062
|
+
workbench.show();
|
|
6063
|
+
if (event.shiftKey) {
|
|
6064
|
+
if (workbench.getSnapshot().bottomPanel !== "terminal") workbench.toggleBottomPanel("terminal");
|
|
6065
|
+
queueMicrotask(() => workbench.runCommand("terminal.new"));
|
|
6066
|
+
} else workbench.toggleBottomPanel("terminal");
|
|
6067
|
+
return true;
|
|
6068
|
+
}
|
|
6069
|
+
//#endregion
|
|
5966
6070
|
//#region src/client/index.tsx
|
|
5967
|
-
const Overlay = ({ service, useSessions }) =>
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
6071
|
+
const Overlay = ({ service, useSessions }) => {
|
|
6072
|
+
const sessionId = useSessions((state) => state.current);
|
|
6073
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Workbench, {
|
|
6074
|
+
service,
|
|
6075
|
+
useSessions
|
|
6076
|
+
}, sessionId ?? "no-session");
|
|
6077
|
+
};
|
|
5971
6078
|
const Footer = ({ service, wide }) => {
|
|
5972
6079
|
const marker = (0, react.useRef)(null);
|
|
5973
6080
|
const [target, setTarget] = (0, react.useState)();
|
|
@@ -6018,6 +6125,7 @@ body[data-ds-dark-theme] .dvw-overlay,.dvw-overlay[data-dvw-theme=dark]{color-sc
|
|
|
6018
6125
|
}, Overlay));
|
|
6019
6126
|
ctx.effect(() => {
|
|
6020
6127
|
const onKey = (event) => {
|
|
6128
|
+
if (handleTerminalShortcut(event, service)) return;
|
|
6021
6129
|
if (event.isComposing || !event.ctrlKey || event.altKey) return;
|
|
6022
6130
|
const view = event.shiftKey && event.code === "KeyE" ? "explorer" : event.shiftKey && event.code === "KeyF" ? "search" : event.shiftKey && event.code === "KeyG" ? "git" : void 0;
|
|
6023
6131
|
if (view !== void 0) {
|