@zhangfengshun/dsh-remote-ssh 1.7.0 → 1.8.1
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 +11 -0
- package/README.md +1 -1
- package/lib/client.js +71 -33
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
本文件的版本号与 `package.json` 的 `version` 保持一致。每个版本对应一个 Cordis Package 快照(`pkg-N`)。
|
|
4
4
|
|
|
5
|
+
## [1.8.1] — 浏览按钮调用原生目录选择框
|
|
6
|
+
### 修复
|
|
7
|
+
- **「浏览」按钮**:本地模式下点击调用 `ctx.workspaces.pickDirectory()` 弹出操作系统原生目录选择框(默认打开用户主目录),选中后自动加载该目录的文件树并填入路径栏。原先该按钮只是按输入框路径刷新树,没有实际选择作用。远程模式无系统对话框,仍按输入路径刷新远端文件树。
|
|
8
|
+
|
|
9
|
+
## [1.8.0] — 重做「添加工作区」弹窗
|
|
10
|
+
### 改进
|
|
11
|
+
- **统一弹窗布局**:原先本地/远程是两个独立弹窗(各自标题),现改为**单个弹窗**:顶部「📁 本地目录 / 🌐 远程目录」分段切换按钮(当前激活的高亮),下方依次为连接选择(远程时)、路径栏(浏览按钮紧挨输入框右侧)、当前路径下的文件树(仅目录、可滚动)。
|
|
12
|
+
- 路径栏右侧的「浏览」按钮紧挨输入框,点击后按输入框路径加载文件树(原为「打开」)。
|
|
13
|
+
- 文件树无子目录时显示占位提示。
|
|
14
|
+
- 本地/远程切换不再关闭重开弹窗,在同一弹窗内即时切换。
|
|
15
|
+
|
|
5
16
|
## [1.7.0] — SSH 连接复用(大幅加速)
|
|
6
17
|
### 重大优化
|
|
7
18
|
- **持久 SSH 会话池**:`ls`/`cat`/`write`/`grep`/`glob`/`mkdir`/`delete`/`move` 等文件操作不再每次新建 ssh 子进程(每次都要完整 TCP 握手 + 密钥交换 + 认证,超算/跳板机单次 2-10 秒)。改为维护一条常驻 `ssh <host> bash` 进程,所有命令复用它,用哨兵标记(sentinel)分隔输出、解析退出码。首次连接后,后续操作近乎瞬时。
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ dsh plugin --profile <name> add /absolute/path/to/dsh-remote-ssh
|
|
|
30
30
|
### 发布后安装
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@1.
|
|
33
|
+
dsh plugin --profile <name> add @zhangfengshun/dsh-remote-ssh@1.8.1
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
> ⚠️ 安装后需**重启 DSH** 才生效;后续仅修改 Client 半边时刷新浏览器即可。
|
package/lib/client.js
CHANGED
|
@@ -47,6 +47,13 @@ window.__ModuleLoader__.load({
|
|
|
47
47
|
".rssh-ok{color:var(--dsw-alias-state-success-primary)}.rssh-err{color:var(--dsw-alias-state-error-primary)}.rssh-muted{color:var(--dsw-alias-label-tertiary);font-size:12px;padding:6px 0}" +
|
|
48
48
|
".rssh-empty{padding:12px;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px}" +
|
|
49
49
|
".rssh-flow-dialog{width:690px!important;max-width:94vw!important;min-width:460px;resize:horizontal;overflow:auto!important}" +
|
|
50
|
+
// 添加工作区弹窗:上方本地/远程分段切换 + 路径栏 + 文件树
|
|
51
|
+
".rssh-seg{display:flex;gap:0;margin-bottom:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;overflow:hidden}" +
|
|
52
|
+
".rssh-seg-btn{flex:1;padding:7px 12px;text-align:center;cursor:pointer;font-size:12px;font-weight:500;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border:none;transition:background .15s}" +
|
|
53
|
+
".rssh-seg-btn:hover{background:var(--dsw-alias-interactive-bg-hover)}" +
|
|
54
|
+
".rssh-seg-btn.active{background:var(--dsw-alias-state-business-primary);color:#fff}" +
|
|
55
|
+
".rssh-pick-tree{border:1px solid var(--dsw-alias-border-l1);border-radius:4px;max-height:240px;overflow-y:auto;margin-top:4px;background:var(--dsw-alias-bg-layer-2)}" +
|
|
56
|
+
".rssh-pick-empty{padding:12px;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:11px}" +
|
|
50
57
|
// 设置面板导航:隐藏「远程连接」栏目左侧壳层硬编码的齿轮图标(本栏目 order=200 位于末尾)。
|
|
51
58
|
".VOzbGW_navList .VOzbGW_navCell:last-child .VOzbGW_navIcon{display:none}";
|
|
52
59
|
|
|
@@ -84,6 +91,7 @@ window.__ModuleLoader__.load({
|
|
|
84
91
|
"common.save": "保存",
|
|
85
92
|
"common.open": "打开",
|
|
86
93
|
"common.close": "关闭",
|
|
94
|
+
"common.noEntries": "(无子目录)",
|
|
87
95
|
"common.delete": "删除",
|
|
88
96
|
"common.up": "⬆ 上级",
|
|
89
97
|
"common.loading": "加载中…",
|
|
@@ -114,6 +122,10 @@ window.__ModuleLoader__.load({
|
|
|
114
122
|
"flow.remoteTitle": "选择远程目录",
|
|
115
123
|
"flow.localDir": "本地目录",
|
|
116
124
|
"flow.remoteDir": "远程目录",
|
|
125
|
+
"flow.addWsTitle": "添加工作区",
|
|
126
|
+
"flow.localSeg": "📁 本地目录",
|
|
127
|
+
"flow.remoteSeg": "🌐 远程目录",
|
|
128
|
+
"flow.browse": "浏览",
|
|
117
129
|
"flow.chooseRemote": "选择远程目录…",
|
|
118
130
|
"flow.backLocal": "← 选择本地目录",
|
|
119
131
|
"flow.errCreate": "创建远程工作区失败",
|
|
@@ -178,6 +190,7 @@ window.__ModuleLoader__.load({
|
|
|
178
190
|
"common.save": "Save",
|
|
179
191
|
"common.open": "Open",
|
|
180
192
|
"common.close": "Close",
|
|
193
|
+
"common.noEntries": "(no subdirectories)",
|
|
181
194
|
"common.delete": "Delete",
|
|
182
195
|
"common.up": "⬆ Up",
|
|
183
196
|
"common.loading": "Loading…",
|
|
@@ -208,6 +221,10 @@ window.__ModuleLoader__.load({
|
|
|
208
221
|
"flow.remoteTitle": "Select Remote Directory",
|
|
209
222
|
"flow.localDir": "Local directory",
|
|
210
223
|
"flow.remoteDir": "Remote directory",
|
|
224
|
+
"flow.addWsTitle": "Add Workspace",
|
|
225
|
+
"flow.localSeg": "📁 Local",
|
|
226
|
+
"flow.remoteSeg": "🌐 Remote",
|
|
227
|
+
"flow.browse": "Browse",
|
|
211
228
|
"flow.chooseRemote": "Choose remote directory…",
|
|
212
229
|
"flow.backLocal": "← Choose local directory",
|
|
213
230
|
"flow.errCreate": "Failed to create remote workspace",
|
|
@@ -773,6 +790,8 @@ window.__ModuleLoader__.load({
|
|
|
773
790
|
|
|
774
791
|
var listFnRef = React.useRef(props.listFn);
|
|
775
792
|
listFnRef.current = props.listFn;
|
|
793
|
+
var pickRef = React.useRef(props.pickNative);
|
|
794
|
+
pickRef.current = props.pickNative;
|
|
776
795
|
|
|
777
796
|
function load(p) {
|
|
778
797
|
setLoading(true); setErr(null);
|
|
@@ -790,21 +809,43 @@ window.__ModuleLoader__.load({
|
|
|
790
809
|
if (up !== null) load(up);
|
|
791
810
|
}
|
|
792
811
|
|
|
793
|
-
|
|
812
|
+
// 浏览按钮:本地模式弹原生系统目录选择框(默认打开用户主目录),远程模式按输入路径刷新文件树。
|
|
813
|
+
function browse() {
|
|
814
|
+
if (pickRef.current) {
|
|
815
|
+
setLoading(true); setErr(null);
|
|
816
|
+
pickRef.current().then(function (picked) {
|
|
817
|
+
setLoading(false);
|
|
818
|
+
if (picked) load(picked);
|
|
819
|
+
}).catch(function (e) {
|
|
820
|
+
setLoading(false);
|
|
821
|
+
setErr(String(e && e.message ? e.message : e));
|
|
822
|
+
});
|
|
823
|
+
} else {
|
|
824
|
+
load(path);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// 路径栏:输入框 + 浏览按钮(紧挨在右侧)+ 上级
|
|
829
|
+
return h("div", null,
|
|
794
830
|
h("div", { className: "rssh-pathbar" },
|
|
795
831
|
h("input", { className: "rssh-input", value: path, onChange: function (e) { setPath(e.target.value); }, placeholder: props.placeholder || t("picker.dir") }),
|
|
796
|
-
h("button", { className: "rssh-btn", onClick:
|
|
832
|
+
h("button", { className: "rssh-btn", onClick: browse, disabled: loading }, t("flow.browse")),
|
|
797
833
|
h("button", { className: "rssh-btn", onClick: goUp }, t("common.up"))
|
|
798
834
|
),
|
|
799
835
|
loading ? h("div", { className: "rssh-muted" }, t("common.loading")) : null,
|
|
800
836
|
err ? h("div", { className: "rssh-err" }, err) : null,
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
837
|
+
// 当前路径下的文件树(仅目录可进入)
|
|
838
|
+
h("div", { className: "rssh-pick-tree" },
|
|
839
|
+
(entries.filter(function (e) { return e.type === "directory"; })).length === 0 && !loading
|
|
840
|
+
? h("div", { className: "rssh-pick-empty" }, t("common.noEntries"))
|
|
841
|
+
: entries.filter(function (e) { return e.type === "directory"; }).map(function (e) {
|
|
842
|
+
return h("div", { key: e.path || e.name, className: "rssh-tree-item", onClick: function () { enterDir(e); } },
|
|
843
|
+
h("span", { className: "rssh-tree-dir" }, "📁 " + e.name));
|
|
844
|
+
})
|
|
806
845
|
),
|
|
807
|
-
h("
|
|
846
|
+
h("div", { className: "rssh-actions", style: { marginTop: 8 } },
|
|
847
|
+
h("button", { className: "rssh-btn", disabled: !path || props.disabled, onClick: function () { props.onChoose(path); } }, t("picker.choose"))
|
|
848
|
+
)
|
|
808
849
|
);
|
|
809
850
|
}
|
|
810
851
|
|
|
@@ -834,33 +875,30 @@ window.__ModuleLoader__.load({
|
|
|
834
875
|
}
|
|
835
876
|
|
|
836
877
|
var canClose = !creating && !props.busy;
|
|
878
|
+
var isRemote = mode === "remote";
|
|
837
879
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
h("div", { style: { padding: 8 } },
|
|
841
|
-
creating || props.busy ? h("div", { className: "rssh-muted" }, t("common.processing")) : null,
|
|
842
|
-
h("div", { className: "rssh-row" },
|
|
843
|
-
h("span", { className: "rssh-label" }, t("common.profile")),
|
|
844
|
-
h("select", { className: "rssh-select", value: profileId, onChange: function (e) { setProfileId(e.target.value); } },
|
|
845
|
-
h("option", { value: "" }, t("common.selectProfile")),
|
|
846
|
-
profiles.map(function (p) { return h("option", { key: p.id, value: p.id }, p.name); })
|
|
847
|
-
)
|
|
848
|
-
),
|
|
849
|
-
profileId ? h(DirPicker, { resetKey: profileId, placeholder: t("flow.remoteDir"), listFn: function (p) { return remoteListFn(profileId, p); }, onChoose: chooseRemote }) : null,
|
|
850
|
-
h("div", { className: "rssh-actions", style: { marginTop: 8 } },
|
|
851
|
-
h("button", { className: "rssh-btn", onClick: function () { setMode("local"); }, disabled: !!props.busy }, t("flow.backLocal"))
|
|
852
|
-
)
|
|
853
|
-
)
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
return h(Modal, { open: true, onClose: function () { if (canClose) props.onCancel(); }, title: t("flow.localTitle"), closeLabel: t("common.close"), className: "rssh-flow-dialog" },
|
|
880
|
+
// 统一弹窗:上方本地/远程分段切换 → 远程时显示连接选择 → 路径栏(浏览按钮紧挨右侧)→ 文件树
|
|
881
|
+
return h(Modal, { open: true, onClose: function () { if (canClose) props.onCancel(); }, title: t("flow.addWsTitle"), closeLabel: t("common.close"), className: "rssh-flow-dialog" },
|
|
858
882
|
h("div", { style: { padding: 8 } },
|
|
859
|
-
props.busy ? h("div", { className: "rssh-muted" }, t("common.processing")) : null,
|
|
860
|
-
|
|
861
|
-
h("div", { className: "rssh-
|
|
862
|
-
h("button", { className: "rssh-btn", onClick: function () { setMode("
|
|
863
|
-
|
|
883
|
+
creating || props.busy ? h("div", { className: "rssh-muted" }, t("common.processing")) : null,
|
|
884
|
+
// 分段切换:本地 / 远程
|
|
885
|
+
h("div", { className: "rssh-seg" },
|
|
886
|
+
h("button", { className: "rssh-seg-btn" + (isRemote ? "" : " active"), onClick: function () { setMode("local"); }, disabled: !!props.busy || creating }, t("flow.localSeg")),
|
|
887
|
+
h("button", { className: "rssh-seg-btn" + (isRemote ? " active" : ""), onClick: function () { setMode("remote"); }, disabled: !!props.busy || creating }, t("flow.remoteSeg"))
|
|
888
|
+
),
|
|
889
|
+
// 远程模式:连接配置选择
|
|
890
|
+
isRemote ? h("div", { className: "rssh-row" },
|
|
891
|
+
h("span", { className: "rssh-label" }, t("common.profile")),
|
|
892
|
+
h("select", { className: "rssh-select", value: profileId, onChange: function (e) { setProfileId(e.target.value); }, disabled: creating },
|
|
893
|
+
h("option", { value: "" }, t("common.selectProfile")),
|
|
894
|
+
profiles.map(function (p) { return h("option", { key: p.id, value: p.id }, p.name); })
|
|
895
|
+
)
|
|
896
|
+
) : null,
|
|
897
|
+
// 目录选择器:远程需选好连接后才显示
|
|
898
|
+
isRemote
|
|
899
|
+
? (profileId ? h(DirPicker, { resetKey: profileId, placeholder: t("flow.remoteDir"), listFn: function (p) { return remoteListFn(profileId, p); }, onChoose: chooseRemote, disabled: creating })
|
|
900
|
+
: h("div", { className: "rssh-pick-empty" }, t("common.selectProfile")))
|
|
901
|
+
: h(DirPicker, { resetKey: "local", placeholder: t("flow.localDir"), listFn: function (p) { return localListFn(props.workspaces, p); }, pickNative: function () { return props.workspaces.pickDirectory(); }, onChoose: chooseLocal })
|
|
864
902
|
)
|
|
865
903
|
);
|
|
866
904
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhangfengshun/dsh-remote-ssh",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "DSH web plugin: VSCode Remote-SSH-like remote development (SSH to supercomputers/servers, remote workspace, file explorer, integrated terminal), integrated with dsh-better-sidebar and DSH settings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|