@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 +2 -2
- package/README.md +4 -4
- package/README.zh.md +2 -2
- package/lib/client.js +67 -26
- package/lib/client.js.map +1 -1
- package/lib/index.js +26 -22
- package/lib/types/client/sidebar-entry-core.d.ts +60 -0
- package/lib/types/client/sidebar-entry.d.ts +7 -11
- package/package.json +1 -1
- package/src/client/mount.tsx +1 -0
- package/src/client/panel/SshPanel.tsx +1 -1
- package/src/client/panel/TerminalTab.tsx +1 -1
- package/src/client/sidebar-entry-core.ts +208 -0
- package/src/client/sidebar-entry.ts +23 -138
- package/src/engine/connection-pool.ts +27 -21
- package/src/routes.ts +8 -3
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:
|
|
6
|
-
README.zh.md:
|
|
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
|
-
###
|
|
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(
|
|
15717
|
+
function createEntry(options) {
|
|
15715
15718
|
const entry = document.createElement("button");
|
|
15716
15719
|
entry.type = "button";
|
|
15717
|
-
entry.
|
|
15718
|
-
|
|
15719
|
-
|
|
15720
|
-
|
|
15721
|
-
|
|
15722
|
-
entry.
|
|
15723
|
-
|
|
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("
|
|
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
|
|
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(
|
|
15747
|
-
|
|
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
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
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
|
-
|
|
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";
|