@zhangfengshun/dsh-remote-ssh 1.3.1 → 1.4.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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  本文件的版本号与 `package.json` 的 `version` 保持一致。每个版本对应一个 Cordis Package 快照(`pkg-N`)。
4
4
 
5
+ ## [1.4.0] — 远程文件单开一栏预览
6
+ ### 新增
7
+ - 点击「远程文件」文件树中的文件,改为在 better-sidebar 中**单开一栏(独立页签)预览/编辑**,与内置「文件」页签一致,不再在文件树下方内嵌预览。新增隐藏页签类型 `remssh:editor`,同一远程文件按 (profileId, path) 去重复用。
8
+
5
9
  ## [1.3.1] — 修复远程文件树滚动
6
10
  ### 修复
7
11
  - 「远程文件」页签改为 flex 布局:文件树在文件很多时占据剩余高度并内部滚动(`flex:1; overflow-y:auto`),不再因父容器 `overflow:hidden` 而无法往下拉。
package/lib/client.js CHANGED
@@ -96,6 +96,7 @@ window.__ModuleLoader__.load({
96
96
  "files.errRead": "读取失败",
97
97
  "files.errSave": "保存失败",
98
98
  "files.errSaveDefaultDir": "保存默认目录失败",
99
+ "files.binary": "二进制文件,无法预览",
99
100
  "term.title": "终端 {id}",
100
101
  "term.exited": " (已退出)",
101
102
  "term.connecting": "正在连接…",
@@ -160,6 +161,7 @@ window.__ModuleLoader__.load({
160
161
  "files.errRead": "Failed to read",
161
162
  "files.errSave": "Failed to save",
162
163
  "files.errSaveDefaultDir": "Failed to save default directory",
164
+ "files.binary": "Binary file — preview not available",
163
165
  "term.title": "Terminal {id}",
164
166
  "term.exited": " (exited)",
165
167
  "term.connecting": "Connecting…",
@@ -255,6 +257,7 @@ window.__ModuleLoader__.load({
255
257
  function FilesTab(props) {
256
258
  var t = useT();
257
259
  var sessionId = props.sessionId;
260
+ var betterSidebar = props.betterSidebar;
258
261
  var [workspaces, setWorkspaces] = React.useState([]);
259
262
  var profiles = useProfiles();
260
263
  var [wsId, setWsId] = useSessionMemory(sessionId, "wsId", sel.workspaceId);
@@ -263,7 +266,6 @@ window.__ModuleLoader__.load({
263
266
  var [entries, setEntries] = useSessionMemory(sessionId, "entries", []);
264
267
  var [err, setErr] = React.useState(null);
265
268
  var [loading, setLoading] = React.useState(false);
266
- var [openFile, setOpenFile] = useSessionMemory(sessionId, "openFile", null);
267
269
  var [defaultDir, setDefaultDir] = useSessionMemory(sessionId, "defaultDir", "");
268
270
 
269
271
  React.useEffect(function () {
@@ -316,11 +318,16 @@ window.__ModuleLoader__.load({
316
318
  setPath(base);
317
319
  load(profileId, base);
318
320
  } else {
319
- setOpenFile({ path: base, content: "", loading: true });
320
- api("readFile", { profileId: profileId, path: base }).then(function (r) {
321
- if (r && r.ok) setOpenFile({ path: base, content: r.content, binary: r.binary, dirty: false, loading: false });
322
- else setOpenFile({ path: base, content: "", error: (r && r.error) || t("files.errRead"), loading: false });
323
- });
321
+ // 用独立页签预览远程文件(与内置「文件」页签一致),而非在文件树下方预览。
322
+ if (betterSidebar && betterSidebar.openTab) {
323
+ betterSidebar.openTab({
324
+ type: "remssh:editor",
325
+ id: "remssh:editor:" + profileId + ":" + base,
326
+ title: e.name,
327
+ path: base,
328
+ meta: { profileId: profileId, remotePath: base }
329
+ }, { sessionId: sessionId });
330
+ }
324
331
  }
325
332
  }
326
333
 
@@ -333,14 +340,6 @@ window.__ModuleLoader__.load({
333
340
  load(profileId, up);
334
341
  }
335
342
 
336
- function saveFile() {
337
- if (!openFile) return;
338
- api("writeFile", { profileId: profileId, path: openFile.path, content: openFile.content }).then(function (r) {
339
- if (r && r.ok) setOpenFile(Object.assign({}, openFile, { dirty: false }));
340
- else setErr((r && r.error) || t("files.errSave"));
341
- });
342
- }
343
-
344
343
  return h("div", { style: { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, overflow: "hidden" } },
345
344
  h("div", { style: { flex: "none" } },
346
345
  h("div", { className: "rssh-row" },
@@ -379,18 +378,56 @@ window.__ModuleLoader__.load({
379
378
  e.type === "file" ? h("span", { className: "rssh-tree-size" }, String(e.size)) : null
380
379
  );
381
380
  })
381
+ )
382
+ );
383
+ }
384
+
385
+ // ======================================================================
386
+ // 远程文件编辑器页签:点击文件树中的文件后单开一栏预览/编辑(与内置「文件」页签一致)
387
+ // ======================================================================
388
+ function RemoteEditor(props) {
389
+ var t = useT();
390
+ var tab = props.tab || {};
391
+ var ctx = props.ctx;
392
+ var meta = tab.meta || {};
393
+ var profileId = meta.profileId || "";
394
+ var remotePath = tab.path || meta.remotePath || "";
395
+ var [data, setData] = React.useState({ loading: true, content: "", binary: false, dirty: false, error: null });
396
+
397
+ React.useEffect(function () {
398
+ var alive = true;
399
+ setData({ loading: true, content: "", binary: false, dirty: false, error: null });
400
+ api("readFile", { profileId: profileId, path: remotePath }).then(function (r) {
401
+ if (!alive) return;
402
+ if (r && r.ok) setData({ loading: false, content: r.content || "", binary: !!r.binary, dirty: false, error: null });
403
+ else setData({ loading: false, content: "", binary: false, dirty: false, error: (r && r.error) || t("files.errRead") });
404
+ });
405
+ return function () { alive = false; };
406
+ }, [profileId, remotePath]);
407
+
408
+ function save() {
409
+ api("writeFile", { profileId: profileId, path: remotePath, content: data.content }).then(function (r) {
410
+ if (r && r.ok) setData(Object.assign({}, data, { dirty: false, error: null }));
411
+ else setData(Object.assign({}, data, { error: (r && r.error) || t("files.errSave") }));
412
+ });
413
+ }
414
+
415
+ function close() {
416
+ if (ctx && ctx.betterSidebar && tab.id) ctx.betterSidebar.closeTab(tab.id);
417
+ }
418
+
419
+ if (data.loading) return h("div", { className: "rssh-muted", style: { padding: 8 } }, t("files.reading"));
420
+ if (data.error) return h("div", { className: "rssh-err", style: { padding: 8, whiteSpace: "pre-wrap" } }, data.error);
421
+ if (data.binary) return h("div", { className: "rssh-muted", style: { padding: 8 } }, t("files.binary"));
422
+
423
+ return h("div", { style: { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, padding: 8, overflow: "hidden" } },
424
+ h("div", { className: "rssh-pathbar", style: { flex: "none" } },
425
+ h("span", { className: "rssh-title", style: { flex: 1, wordBreak: "break-all" } }, remotePath),
426
+ h("button", { className: "rssh-btn", onClick: save }, t("common.save")),
427
+ h("button", { className: "rssh-btn", onClick: close }, t("common.close"))
382
428
  ),
383
- openFile ? h("div", { style: { flex: "none" } },
384
- h("div", { className: "rssh-pathbar", style: { marginTop: 8 } },
385
- h("span", { className: "rssh-title", style: { flex: 1, wordBreak: "break-all" } }, openFile.path),
386
- h("button", { className: "rssh-btn", onClick: saveFile }, t("common.save")),
387
- h("button", { className: "rssh-btn", onClick: function () { setOpenFile(null); } }, t("common.close"))
388
- ),
389
- openFile.loading ? h("div", { className: "rssh-muted" }, t("files.reading")) :
390
- openFile.error ? h("div", { className: "rssh-err" }, openFile.error) :
391
- h("textarea", { className: "rssh-content", style: { width: "100%", boxSizing: "border-box", minHeight: 200, background: "var(--dsw-alias-markdown-code-block)", color: "var(--dsw-alias-label-primary)", border: "1px solid var(--dsw-alias-border-l2)" },
392
- value: openFile.content, onChange: function (e) { setOpenFile(Object.assign({}, openFile, { content: e.target.value, dirty: true })); } })
393
- ) : null
429
+ h("textarea", { className: "rssh-content", style: { flex: 1, minHeight: 0, maxHeight: "none", width: "100%", boxSizing: "border-box", background: "var(--dsw-alias-markdown-code-block)", color: "var(--dsw-alias-label-primary)", border: "1px solid var(--dsw-alias-border-l2)" },
430
+ value: data.content, onChange: function (e) { setData(Object.assign({}, data, { content: e.target.value, dirty: true })); } })
394
431
  );
395
432
  }
396
433
 
@@ -728,7 +765,20 @@ window.__ModuleLoader__.load({
728
765
  icon: function () { return h("span", null, "🗂️"); },
729
766
  component: function (props) {
730
767
  var sid = props && props.scope ? props.scope.sessionId : null;
731
- return h(FilesTab, { key: "files:" + (sid || "global"), sessionId: sid });
768
+ return h(FilesTab, { key: "files:" + (sid || "global"), sessionId: sid, betterSidebar: props && props.ctx ? props.ctx.betterSidebar : null });
769
+ }
770
+ });
771
+ });
772
+ // 远程文件编辑器页签(隐藏页签,仅由文件树点击打开,每个远程文件单开一栏)。
773
+ ctx.effect(function () {
774
+ return betterSidebar.registerTab({
775
+ id: "remssh:editor",
776
+ title: function () { return i18n.t("tabs.files"); },
777
+ hidden: true,
778
+ dedupeKey: function (tab) { return (tab && tab.meta && tab.meta.profileId || "") + "\u0000" + (tab && tab.path || ""); },
779
+ icon: function () { return h("span", null, "📄"); },
780
+ component: function (props) {
781
+ return h(RemoteEditor, { tab: props && props.tab, ctx: props && props.ctx, scope: props && props.scope });
732
782
  }
733
783
  });
734
784
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangfengshun/dsh-remote-ssh",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
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",