@zhangfengshun/dsh-remote-ssh 2.3.1 → 2.3.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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  本文件的版本号与 `package.json` 的 `version` 保持一致。每个版本对应一个 Cordis Package 快照(`pkg-N`)。
4
4
 
5
+ ## [2.3.2] — 远程工作区图标改为单个「地球角标文件夹」
6
+ ### 变更
7
+ - **工作区标题去掉 🌐 前缀**:新建远程工作区的原生标题不再带 🌐(此前标题 emoji + 壳层文件夹图标造成「文件夹 + 地球」双重标识);启动自愈同步剥掉既有工作区标题中的旧 🌐 前缀(仅限已知旧形态,不覆盖自定义标题)。
8
+ - **客户端图标替换**:按工作区标题文本定位侧边栏工作区行,隐藏壳层默认纯文件夹 svg,插入内联「地球角标文件夹」SVG(文件夹描边 + 主题色圆形地球角标,跟随明暗主题);复用 2.3.1 的增量观察器(只扫新增子树、30s 刷新标题集、data 标记防重入),稳态成本≈0。
9
+
5
10
  ## [2.3.1] — 修复 2.3.0 上线后的界面卡顿(GC 压力 + 客户端全页扫描)
6
11
  ### 修复
7
12
  - **读缓存字节预算(review m1)**:单条结果 >1MiB 不再缓存(结果照常返回),缓存总量上限 32MB 超限逐出最旧条目——消除大文件驻留字符串造成的 GC 压力(最坏 ~270MB → ≤32MB)。
package/lib/client.js CHANGED
@@ -1136,9 +1136,67 @@ window.__ModuleLoader__.load({
1136
1136
  for (var i = 0; i < buttons.length; i++) checkCell(buttons[i]);
1137
1137
  } catch (e) {}
1138
1138
  }
1139
+ // ---- 远程工作区图标:单个「地球角标文件夹」SVG 替换壳层默认纯文件夹图标 ----
1140
+ // 壳层工作区行的文件夹图标是固定原语(IconFolderOpen16/Close16),无 per-workspace
1141
+ // 图标 API;旧方案在标题里塞 🌐 emoji 造成「文件夹 + 地球」双重标识。现按标题文本
1142
+ // 定位远程工作区行,隐藏原 svg 并插入内联「地球角标文件夹」svg(data 标记防重入)。
1143
+ var remoteTitles = new Set();
1144
+ function refreshRemoteTitles() {
1145
+ api("listWorkspaces").then(function (r) {
1146
+ if (!(r && r.ok && Array.isArray(r.workspaces))) return;
1147
+ var next = new Set();
1148
+ for (var i = 0; i < r.workspaces.length; i++) {
1149
+ var t = String(r.workspaces[i].title || "").trim();
1150
+ if (t) { next.add(t); next.add("🌐 " + t); } // 兼容自愈前的旧前缀标题
1151
+ }
1152
+ remoteTitles = next;
1153
+ swapWorkspaceIconsIn(document.body); // 标题集就绪后补一次全量替换
1154
+ }).catch(function () {});
1155
+ }
1156
+ function folderGlobeSvg() {
1157
+ var NS = "http://www.w3.org/2000/svg";
1158
+ var svg = document.createElementNS(NS, "svg");
1159
+ svg.setAttribute("viewBox", "0 0 20 16");
1160
+ svg.setAttribute("width", "16");
1161
+ svg.setAttribute("height", "16");
1162
+ svg.setAttribute("aria-hidden", "true");
1163
+ svg.setAttribute("data-rssh-folder-globe", "1");
1164
+ svg.innerHTML =
1165
+ '<path d="M2.4 4.2c0-1.05.85-1.9 1.9-1.9h3.1c.52 0 1.02.21 1.39.58l1.05 1.05c.28.28.66.44 1.06.44h6.3c1.05 0 1.9.85 1.9 1.9v6.53c0 1.05-.85 1.9-1.9 1.9H4.3c-1.05 0-1.9-.85-1.9-1.9V4.2z" fill="none" stroke="currentColor" stroke-width="1.2"/>' +
1166
+ '<circle cx="6.3" cy="12.1" r="3.1" fill="var(--dsw-alias-state-business-primary, #4c6ef5)" stroke="var(--dsw-alias-bg-layer-1, #1b1b1b)" stroke-width="0.8"/>' +
1167
+ '<g fill="none" stroke="#ffffff" stroke-width="0.75"><circle cx="6.3" cy="12.1" r="1.95"/><ellipse cx="6.3" cy="12.1" rx="0.95" ry="1.95"/><path d="M4.35 12.1h3.9"/></g>';
1168
+ return svg;
1169
+ }
1170
+ function swapWorkspaceIconsIn(root) {
1171
+ if (!remoteTitles.size) return;
1172
+ try {
1173
+ var nodes = root.querySelectorAll("span,div,button,a,p");
1174
+ for (var i = 0; i < nodes.length; i++) {
1175
+ var el = nodes[i];
1176
+ if (el.getAttribute && el.getAttribute("data-rssh-title-scan") === "1") continue;
1177
+ var t = (el.textContent || "").trim();
1178
+ if (!t || !remoteTitles.has(t) || el.childElementCount > 2) continue;
1179
+ el.setAttribute("data-rssh-title-scan", "1");
1180
+ // 向上找最近的「直接子级含 svg」的容器(壳层 slot.folder 槽位)
1181
+ var p = el, slot = null, guard = 0;
1182
+ while (p && p.nodeType === 1 && p !== document.body && guard++ < 6) {
1183
+ if (p.querySelector(":scope > svg")) { slot = p; break; }
1184
+ p = p.parentElement;
1185
+ }
1186
+ if (!slot) continue;
1187
+ var old = slot.querySelector(":scope > svg");
1188
+ if (old && old.getAttribute("data-rssh-folder-globe") === "1") continue;
1189
+ if (old) old.style.display = "none";
1190
+ var mine = folderGlobeSvg();
1191
+ if (old) slot.insertBefore(mine, old); else slot.appendChild(mine);
1192
+ }
1193
+ } catch (e) {}
1194
+ }
1139
1195
  ctx.effect(function () {
1140
1196
  if (typeof document === "undefined") return;
1141
1197
  hideRemoteGearIcon();
1198
+ refreshRemoteTitles();
1199
+ var titleTimer = setInterval(refreshRemoteTitles, 30000);
1142
1200
  var timer = null;
1143
1201
  var pendingRoots = null;
1144
1202
  var schedule = function (root) {
@@ -1152,10 +1210,11 @@ window.__ModuleLoader__.load({
1152
1210
  if (roots && roots.length) {
1153
1211
  // 只扫新增子树:流式聊天期间不做全文档扫描(稳态成本≈0)
1154
1212
  for (var i = 0; i < roots.length; i++) {
1155
- if (roots[i].isConnected) checkCellIn(roots[i]);
1213
+ if (roots[i].isConnected) { checkCellIn(roots[i]); swapWorkspaceIconsIn(roots[i]); }
1156
1214
  }
1157
1215
  } else {
1158
1216
  hideRemoteGearIcon(); // 语言切换等无根场景全量扫一次
1217
+ swapWorkspaceIconsIn(document.body);
1159
1218
  }
1160
1219
  }, 500);
1161
1220
  };
@@ -1171,8 +1230,9 @@ window.__ModuleLoader__.load({
1171
1230
  mo.disconnect();
1172
1231
  if (off) off();
1173
1232
  if (timer) clearTimeout(timer);
1233
+ clearInterval(titleTimer);
1174
1234
  };
1175
- }, "dsh-remote-ssh: hide settings nav gear icon");
1235
+ }, "dsh-remote-ssh: settings gear hide + remote workspace folder-globe icon");
1176
1236
  }
1177
1237
 
1178
1238
  // better-sidebar 0.15+ 的文件上传 UI 把原始字节流 POST 到 /sidebar/upload。
package/lib/index.js CHANGED
@@ -1273,7 +1273,7 @@ function apply(ctx, config) {
1273
1273
  let workspaceId = null;
1274
1274
  if (workspaceRegistry) {
1275
1275
  try {
1276
- const wsTitle = "🌐 " + title;
1276
+ const wsTitle = title; // 图标语义由客户端「地球角标文件夹」图标承担,标题不再带 🌐 前缀
1277
1277
  const native = await workspaceRegistry.create(mirrorDir, wsTitle);
1278
1278
  workspaceId = native.id;
1279
1279
  // workspaceRegistry.create 仅首次创建时应用 title;已存在的旧工作区
@@ -1856,10 +1856,11 @@ function apply(ctx, config) {
1856
1856
  }
1857
1857
  })();
1858
1858
 
1859
- // ---- 启动自愈:把既有远程工作区的原生标题统一为「🌐 + 远程路径最后一段」----
1859
+ // ---- 启动自愈:把既有远程工作区的原生标题统一为「远程路径最后一段」----
1860
1860
  // 早期版本 create 工作区时未传 title,DSH 用镜像目录 basename(wsId,如 wmt3tev5cdfge)
1861
1861
  // 作标题;workspaceRegistry.create 只对新建记录应用 title,旧记录会被原样返回。
1862
- // 这里仅在标题等于镜像目录 basename(明显是旧 bug 产物)时修复,不覆盖用户自定义标题。
1862
+ // 2.3.2 起标题不再带 🌐 前缀(图标由客户端地球角标文件夹 SVG 承担),自愈同时剥掉
1863
+ // 旧版遗留的 🌐 前缀。仅在标题等于已知旧形态时修复,不覆盖用户自定义标题。
1863
1864
  function healWorkspaceTitles() {
1864
1865
  const reg = workspaceRegistry;
1865
1866
  if (!reg || typeof reg.resolveByPath !== "function") return;
@@ -1868,13 +1869,14 @@ function apply(ctx, config) {
1868
1869
  if (!w || !w.mirrorPath || !w.remotePath) continue;
1869
1870
  const lastSeg = String(w.remotePath).replace(/\/+$/, "").split("/").pop();
1870
1871
  if (!lastSeg || lastSeg === "~" || lastSeg === ".") continue;
1871
- const desired = "🌐 " + lastSeg;
1872
+ const desired = lastSeg;
1872
1873
  const base = String(w.mirrorPath).split(/[\\/]/).filter(Boolean).pop();
1874
+ const legacy = new Set([base, "🌐 " + base, desired, "🌐 " + desired, "🌐 " + w.title]);
1873
1875
  (async () => {
1874
1876
  try {
1875
1877
  const ent = await reg.resolveByPath(w.mirrorPath);
1876
- // 只在标题仍卡在镜像目录名(旧 bug)时才改,尊重用户自定义标题。
1877
- if (ent && typeof ent.setTitle === "function" && (ent.title === base || ent.title === "🌐 " + base)) {
1878
+ // 只在标题仍是已知旧形态(镜像目录名旧 bug / 🌐 前缀旧版)时修复,尊重自定义标题。
1879
+ if (ent && typeof ent.setTitle === "function" && legacy.has(ent.title)) {
1878
1880
  await ent.setTitle(desired);
1879
1881
  }
1880
1882
  } catch (e) {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhangfengshun/dsh-remote-ssh",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
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",