@linxin666/dsh-ssh 0.2.0 → 0.2.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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: 4ea116d416ac311ffde1f1052d9857e46b9c4849
6
- README.zh.md: d5bec45d8054b999d24832a4a8d103269b2c816d
5
+ README.md: 4f2f068da6af190caeadf8deeffce24d138c6b37
6
+ README.zh.md: d0090dd49663cf397e4980b70127659cb041fea9
package/README.md CHANGED
@@ -33,10 +33,10 @@ Built on the capability list of [badseal/ssh-skill](https://github.com/badseal/s
33
33
  Install the family aggregate package `@linxin666/dsh-web-ui-all` (all plugins and skins in one) or this plugin alone:
34
34
 
35
35
  ```sh
36
- ### 从 npm 安装(推荐)
37
- dsh plugin --profile web add @linxin666/dsh-ssh
36
+ ### From npm (recommended)
37
+ dsh plugin --profile web add @linxin666/dsh-ssh@latest
38
38
 
39
- ### 从仓库安装(开发调试)
39
+ ### From the repository (development)
40
40
  git clone https://github.com/zhu1090093659/dsh-web-ui.git
41
41
  cd dsh-web-ui
42
42
  pnpm install && pnpm -r build
@@ -53,7 +53,7 @@ The settings panel (plugin config) toggles `announceToAgent` (whether to announc
53
53
  ## Data
54
54
 
55
55
  - Host config: `~/.dsh/dsh-ssh.json` (versioned JSON, atomic write)
56
- - Transfer staging: `os.tmpdir()/dsh-ssh-uploads/`
56
+ - Transfer staging: `os.tmpdir()/dsh-ssh-uploads/` (0700 directory, 0600 in-flight files)
57
57
 
58
58
  ## Development
59
59
 
package/README.zh.md CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ```sh
36
36
  ### 从 npm 安装(推荐)
37
- dsh plugin --profile web add @linxin666/dsh-ssh
37
+ dsh plugin --profile web add @linxin666/dsh-ssh@latest
38
38
 
39
39
  ### 从仓库安装(开发调试)
40
40
  git clone https://github.com/zhu1090093659/dsh-web-ui.git
@@ -53,7 +53,7 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-ssh
53
53
  ## 数据
54
54
 
55
55
  - 主机配置:`~/.dsh/dsh-ssh.json`(版本化 JSON,原子写入)
56
- - 传输暂存:`os.tmpdir()/dsh-ssh-uploads/`
56
+ - 传输暂存:`os.tmpdir()/dsh-ssh-uploads/`(目录 0700,传输中的文件 0600)
57
57
 
58
58
  ## 开发
59
59
 
package/lib/client.js CHANGED
@@ -14864,7 +14864,8 @@ window.__ModuleLoader__.load({
14864
14864
  className: panel_module_css_default.termWrap,
14865
14865
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14866
14866
  ref: containerRef,
14867
- className: panel_module_css_default.termContainer
14867
+ className: panel_module_css_default.termContainer,
14868
+ "data-dsh-part": "terminal"
14868
14869
  }), status.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14869
14870
  className: panel_module_css_default.termPlaceholder,
14870
14871
  children: hosts.length === 0 ? tt("hosts.empty") : tt("terminal.placeholder")
@@ -15534,6 +15535,7 @@ window.__ModuleLoader__.load({
15534
15535
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15535
15536
  className: panel_module_css_default.tabBar,
15536
15537
  role: "tablist",
15538
+ "data-dsh-part": "tab-bar",
15537
15539
  children: TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15538
15540
  type: "button",
15539
15541
  role: "tab",
@@ -15614,6 +15616,7 @@ window.__ModuleLoader__.load({
15614
15616
  if (column === void 0) return;
15615
15617
  container = document.createElement("div");
15616
15618
  container.dataset.dshSshView = "";
15619
+ container.dataset.dshPlugin = "ssh";
15617
15620
  container.className = panel_module_css_default.view;
15618
15621
  column.appendChild(container);
15619
15622
  root = (0, react_dom_client.createRoot)(container);
@@ -15697,7 +15700,7 @@ window.__ModuleLoader__.load({
15697
15700
  }
15698
15701
  };
15699
15702
  //#endregion
15700
- //#region src/client/sidebar-entry.ts
15703
+ //#region src/client/sidebar-entry-core.ts
15701
15704
  /** Find the sidebar shell root element, or undefined while not yet mounted. */
15702
15705
  function sidebarRoot() {
15703
15706
  const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
@@ -15711,28 +15714,30 @@ window.__ModuleLoader__.load({
15711
15714
  for (const child of root.children) if (child.tagName === "BUTTON") return child;
15712
15715
  }
15713
15716
  /** Build the entry row (a detached button; insert once the shell is up). */
15714
- function createEntry(controller) {
15717
+ function createEntry(options) {
15715
15718
  const entry = document.createElement("button");
15716
15719
  entry.type = "button";
15717
- entry.dataset.dshSshEntry = "";
15718
- entry.className = panel_module_css_default.entry;
15719
- entry.setAttribute("aria-label", tt("entry.label"));
15720
- entry.setAttribute("title", tt("entry.tooltip"));
15721
- entry.innerHTML = "<span class=\"" + panel_module_css_default.entryIcon + "\"><svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2.5\" width=\"12\" height=\"11\" rx=\"1.5\"/><path d=\"M4.5 5.5l2.5 2.5-2.5 2.5\"/><path d=\"M8.5 10.5h3\"/></svg></span><span class=\"" + panel_module_css_default.entryLabel + "\">" + tt("entry.label") + "</span>";
15722
- entry.addEventListener("click", () => {
15723
- controller.toggle();
15724
- });
15720
+ entry.setAttribute(options.rowAttribute, "");
15721
+ if (options.plugin !== void 0) {
15722
+ entry.setAttribute("data-dsh-plugin", options.plugin);
15723
+ entry.setAttribute("data-dsh-part", "sidebar-entry");
15724
+ }
15725
+ entry.className = options.css["entry"] ?? "";
15726
+ entry.setAttribute("aria-label", options.label());
15727
+ if (options.tooltip !== void 0) entry.setAttribute("title", options.tooltip());
15728
+ entry.innerHTML = "<span class=\"" + (options.css["entryIcon"] ?? "") + "\">" + options.icon + "</span><span class=\"" + (options.css["entryLabel"] ?? "") + "\">" + options.label() + "</span>";
15729
+ entry.addEventListener("click", options.onToggle);
15725
15730
  return entry;
15726
15731
  }
15727
15732
  /** Re-insert the entry after the New Session row (before the browser region). */
15728
- function placeEntry(root, entry) {
15733
+ function placeEntry(root, entry, options) {
15729
15734
  const button = newSessionButton(root);
15730
15735
  if (button === void 0) return false;
15731
15736
  if (entry.parentElement !== root) {
15732
15737
  const row = button.closest("[class*=\"logoRow\"]");
15733
15738
  const base = row !== null && row.parentElement === root ? row : button;
15734
- const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches("[data-dsh-taskboard-entry], [data-dsh-ssh-entry]"));
15735
- const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling;
15739
+ const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches(options.familySelectors.join(", ")));
15740
+ const anchor = options.position === "before" ? family.length > 0 ? family[0] : base.nextElementSibling : family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling;
15736
15741
  root.insertBefore(entry, anchor);
15737
15742
  }
15738
15743
  return true;
@@ -15740,11 +15745,12 @@ window.__ModuleLoader__.load({
15740
15745
  /**
15741
15746
  * Mount the sidebar entry, waiting for the shell to render and self-healing
15742
15747
  * on later React re-renders.
15743
- * @param controller - the panel controller the entry toggles.
15748
+ * @param options - the row's attribute/icon/copy/action/ordering configuration.
15744
15749
  * @returns disposer removing the entry and its observers.
15745
15750
  */
15746
- function mountSidebarEntry(controller) {
15747
- const entry = createEntry(controller);
15751
+ function mountSidebarEntry$1(options) {
15752
+ if (typeof document !== "undefined" && document.querySelector(options.rowSelector) !== null) return () => {};
15753
+ const entry = createEntry(options);
15748
15754
  let root;
15749
15755
  let placed = false;
15750
15756
  const tryPlace = () => {
@@ -15761,7 +15767,7 @@ window.__ModuleLoader__.load({
15761
15767
  }
15762
15768
  root ??= sidebarRoot();
15763
15769
  if (root === void 0) return;
15764
- placed = placeEntry(root, entry);
15770
+ placed = placeEntry(root, entry, options);
15765
15771
  if (placed) rootObserver.observe(root, {
15766
15772
  childList: true,
15767
15773
  subtree: true
@@ -15780,23 +15786,58 @@ window.__ModuleLoader__.load({
15780
15786
  tryPlace();
15781
15787
  return;
15782
15788
  }
15783
- if (!root.contains(entry)) placed = placeEntry(root, entry);
15789
+ if (!root.contains(entry)) placed = placeEntry(root, entry, options);
15784
15790
  });
15785
- const syncActive = () => {
15786
- if (controller.getSnapshot().panelOpen) entry.dataset.active = "true";
15787
- else delete entry.dataset.active;
15788
- };
15789
- const unsubscribe = controller.subscribe(syncActive);
15790
- syncActive();
15791
+ const unsubscribeActive = options.active === void 0 ? void 0 : (() => {
15792
+ const syncActive = () => {
15793
+ if (options.active.isOpen()) entry.dataset.active = "true";
15794
+ else delete entry.dataset.active;
15795
+ };
15796
+ const unsubscribe = options.active.subscribe(syncActive);
15797
+ syncActive();
15798
+ return unsubscribe;
15799
+ })();
15791
15800
  tryPlace();
15792
15801
  return () => {
15793
15802
  waitObserver.disconnect();
15794
15803
  rootObserver.disconnect();
15795
- unsubscribe();
15804
+ unsubscribeActive?.();
15796
15805
  entry.remove();
15797
15806
  };
15798
15807
  }
15799
15808
  //#endregion
15809
+ //#region src/client/sidebar-entry.ts
15810
+ /** Stable data attribute identifying the injected entry row. */
15811
+ const ENTRY_SELECTOR = "[data-dsh-ssh-entry]";
15812
+ /** Inline icon (matches the shell's 16px nav-icon look): a terminal prompt glyph. */
15813
+ const ICON = "<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2\" y=\"2.5\" width=\"12\" height=\"11\" rx=\"1.5\"/><path d=\"M4.5 5.5l2.5 2.5-2.5 2.5\"/><path d=\"M8.5 10.5h3\"/></svg>";
15814
+ /**
15815
+ * Mount the sidebar entry, waiting for the shell to render and self-healing
15816
+ * on later React re-renders.
15817
+ * @param controller - the panel controller the entry toggles.
15818
+ * @returns disposer removing the entry and its observers.
15819
+ */
15820
+ function mountSidebarEntry(controller) {
15821
+ return mountSidebarEntry$1({
15822
+ rowAttribute: "data-dsh-ssh-entry",
15823
+ rowSelector: ENTRY_SELECTOR,
15824
+ plugin: "ssh",
15825
+ icon: ICON,
15826
+ css: panel_module_css_default,
15827
+ label: () => tt("entry.label"),
15828
+ tooltip: () => tt("entry.tooltip"),
15829
+ onToggle: () => {
15830
+ controller.toggle();
15831
+ },
15832
+ position: "after",
15833
+ familySelectors: ["[data-dsh-taskboard-entry]", "[data-dsh-ssh-entry]"],
15834
+ active: {
15835
+ subscribe: (listener) => controller.subscribe(listener),
15836
+ isOpen: () => controller.getSnapshot().panelOpen
15837
+ }
15838
+ });
15839
+ }
15840
+ //#endregion
15800
15841
  //#region src/client/index.ts
15801
15842
  /** Locale namespace this plugin owns. */
15802
15843
  const NS = "dsh-ssh";