@linxin666/dsh-ssh 0.4.2 → 0.4.3
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/lib/client.js +303 -459
- package/lib/client.js.map +1 -1
- package/lib/index.js +707 -466
- package/lib/types/client/api.d.ts +27 -4
- package/lib/types/client/native-panel.d.ts +70 -0
- package/lib/types/client/panel/SshPanel.d.ts +1 -3
- package/lib/types/client/panel/TerminalTab.d.ts +6 -1
- package/lib/types/client/panel/controller.d.ts +71 -4
- package/lib/types/engine/terminal-sessions.d.ts +67 -0
- package/lib/types/mount-once.d.ts +38 -7
- package/lib/types/protocol.d.ts +12 -1
- package/lib/types/routes.d.ts +2 -0
- package/package.json +5 -2
- package/src/client/api.ts +52 -22
- package/src/client/index.ts +30 -8
- package/src/client/native-panel.tsx +141 -0
- package/src/client/panel/SshPanel.tsx +25 -28
- package/src/client/panel/TerminalTab.tsx +54 -14
- package/src/client/panel/controller.ts +126 -7
- package/src/client/panel/panel.module.css +8 -128
- package/src/engine/terminal-sessions.ts +280 -0
- package/src/index.ts +4 -1
- package/src/mount-once.ts +107 -13
- package/src/protocol.ts +6 -1
- package/src/routes.ts +26 -107
- package/lib/types/client/body-mutations.d.ts +0 -45
- package/lib/types/client/mount.d.ts +0 -27
- package/lib/types/client/panel-mount-core.d.ts +0 -56
- package/lib/types/client/sidebar-entry-core.d.ts +0 -55
- package/lib/types/client/sidebar-entry.d.ts +0 -26
- package/src/client/body-mutations.ts +0 -133
- package/src/client/mount.tsx +0 -47
- package/src/client/panel-mount-core.ts +0 -155
- package/src/client/sidebar-entry-core.ts +0 -237
- package/src/client/sidebar-entry.ts +0 -51
package/lib/client.js
CHANGED
|
@@ -9,7 +9,6 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
//#endregion
|
|
10
10
|
let react = require("react");
|
|
11
11
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
12
|
-
let react_dom_client = require("react-dom/client");
|
|
13
12
|
//#region src/protocol.ts
|
|
14
13
|
const SSH_API = {
|
|
15
14
|
hosts: "/api/dsh-ssh/hosts",
|
|
@@ -644,39 +643,77 @@ window.__ModuleLoader__.load({
|
|
|
644
643
|
bytes: received
|
|
645
644
|
};
|
|
646
645
|
}
|
|
647
|
-
/**
|
|
646
|
+
/**
|
|
647
|
+
* Open a new host terminal session for the alias.
|
|
648
|
+
* @param alias - the configured host alias.
|
|
649
|
+
* @param cols - initial PTY width.
|
|
650
|
+
* @param rows - initial PTY height.
|
|
651
|
+
* @returns the live connection; its ready callback carries the session id
|
|
652
|
+
* {@link attachTerminal} uses after a view detach.
|
|
653
|
+
*/
|
|
648
654
|
openTerminal(alias, cols, rows) {
|
|
649
|
-
|
|
655
|
+
return this.terminalSocket(query({
|
|
650
656
|
alias,
|
|
651
657
|
cols,
|
|
652
658
|
rows
|
|
653
|
-
});
|
|
659
|
+
}));
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Reattach to a host-side session that outlived its view (a panel switch, a
|
|
663
|
+
* page navigation). The host replays its scrollback before the ready frame.
|
|
664
|
+
* @param sessionId - the id the previous connection reported on ready.
|
|
665
|
+
* @param cols - the view's current width.
|
|
666
|
+
* @param rows - the view's current height.
|
|
667
|
+
*/
|
|
668
|
+
attachTerminal(sessionId, cols, rows) {
|
|
669
|
+
return this.terminalSocket(query({
|
|
670
|
+
session: sessionId,
|
|
671
|
+
cols,
|
|
672
|
+
rows
|
|
673
|
+
}));
|
|
674
|
+
}
|
|
675
|
+
/** One terminal socket over either an alias (open) or a session id (attach). */
|
|
676
|
+
terminalSocket(search) {
|
|
677
|
+
const url = (window.location.protocol === "https:" ? "wss" : "ws") + "://" + window.location.host + SSH_API.terminal + search;
|
|
654
678
|
const socket = new WebSocket(url);
|
|
679
|
+
let leaving = false;
|
|
680
|
+
const sendFrame = (frame) => {
|
|
681
|
+
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(frame));
|
|
682
|
+
};
|
|
655
683
|
const connection = {
|
|
656
684
|
onReady: void 0,
|
|
657
685
|
onOutput: void 0,
|
|
658
686
|
onExit: void 0,
|
|
659
687
|
onAuthPrompt: void 0,
|
|
660
688
|
send: (data) => {
|
|
661
|
-
|
|
689
|
+
sendFrame({
|
|
662
690
|
type: "input",
|
|
663
691
|
data
|
|
664
|
-
})
|
|
692
|
+
});
|
|
665
693
|
},
|
|
666
694
|
resize: (cols, rows) => {
|
|
667
|
-
|
|
695
|
+
sendFrame({
|
|
668
696
|
type: "resize",
|
|
669
697
|
cols,
|
|
670
698
|
rows
|
|
671
|
-
})
|
|
699
|
+
});
|
|
672
700
|
},
|
|
673
701
|
sendAuthResponse: (responses) => {
|
|
674
|
-
|
|
702
|
+
sendFrame({
|
|
675
703
|
type: "auth_response",
|
|
676
704
|
responses
|
|
677
|
-
})
|
|
705
|
+
});
|
|
706
|
+
},
|
|
707
|
+
detach: () => {
|
|
708
|
+
leaving = true;
|
|
709
|
+
sendFrame({ type: "detach" });
|
|
710
|
+
try {
|
|
711
|
+
socket.close();
|
|
712
|
+
} catch {}
|
|
678
713
|
},
|
|
679
714
|
close: () => {
|
|
715
|
+
leaving = true;
|
|
716
|
+
sendFrame({ type: "close" });
|
|
680
717
|
try {
|
|
681
718
|
socket.close();
|
|
682
719
|
} catch {}
|
|
@@ -689,23 +726,127 @@ window.__ModuleLoader__.load({
|
|
|
689
726
|
} catch {
|
|
690
727
|
return;
|
|
691
728
|
}
|
|
692
|
-
if (frame.type === "ready") connection.onReady?.();
|
|
729
|
+
if (frame.type === "ready") connection.onReady?.(frame.sessionId, frame.alias);
|
|
693
730
|
else if (frame.type === "output") connection.onOutput?.(frame.data);
|
|
694
731
|
else if (frame.type === "exit") connection.onExit?.(frame.code, frame.error);
|
|
695
732
|
else if (frame.type === "auth_prompt") connection.onAuthPrompt?.(frame.name, frame.instructions, frame.prompts);
|
|
696
733
|
};
|
|
697
734
|
socket.onclose = () => {
|
|
698
|
-
connection.onExit?.(null, "connection closed");
|
|
735
|
+
if (!leaving) connection.onExit?.(null, "connection closed");
|
|
699
736
|
};
|
|
700
737
|
socket.onerror = () => {
|
|
701
|
-
connection.onExit?.(null, "connection error");
|
|
738
|
+
if (!leaving) connection.onExit?.(null, "connection error");
|
|
702
739
|
};
|
|
703
740
|
return connection;
|
|
704
741
|
}
|
|
705
742
|
};
|
|
743
|
+
/** The panel state owner the sidebar row and the view both read. */
|
|
744
|
+
var PanelController = class {
|
|
745
|
+
deps;
|
|
746
|
+
panelOpen = false;
|
|
747
|
+
activeTab = "hosts";
|
|
748
|
+
connectRequest = null;
|
|
749
|
+
terminalSessionId;
|
|
750
|
+
listeners = /* @__PURE__ */ new Set();
|
|
751
|
+
/** Cached so getSnapshot is referentially stable between changes. */
|
|
752
|
+
snapshot = {
|
|
753
|
+
panelOpen: false,
|
|
754
|
+
activeTab: "hosts",
|
|
755
|
+
connectRequest: null,
|
|
756
|
+
terminalSessionId: void 0
|
|
757
|
+
};
|
|
758
|
+
constructor(deps = {}) {
|
|
759
|
+
this.deps = deps;
|
|
760
|
+
}
|
|
761
|
+
getSnapshot() {
|
|
762
|
+
return this.snapshot;
|
|
763
|
+
}
|
|
764
|
+
subscribe(fn) {
|
|
765
|
+
this.listeners.add(fn);
|
|
766
|
+
return () => {
|
|
767
|
+
this.listeners.delete(fn);
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Show the panel. The layout owns which panel the column renders, so the
|
|
772
|
+
* state flip and the selection travel together; the selection is requested
|
|
773
|
+
* after the snapshot flips, so a subscriber never observes "open" while the
|
|
774
|
+
* shell still shows the conversation.
|
|
775
|
+
*/
|
|
776
|
+
open() {
|
|
777
|
+
if (this.panelOpen) return;
|
|
778
|
+
this.panelOpen = true;
|
|
779
|
+
this.commit();
|
|
780
|
+
this.selectPanel("ssh");
|
|
781
|
+
}
|
|
782
|
+
/** Return the column to the conversation, asking the layout explicitly. */
|
|
783
|
+
close() {
|
|
784
|
+
if (!this.panelOpen) return;
|
|
785
|
+
this.panelOpen = false;
|
|
786
|
+
this.commit();
|
|
787
|
+
this.selectPanel(null);
|
|
788
|
+
}
|
|
789
|
+
toggle() {
|
|
790
|
+
if (this.panelOpen) this.close();
|
|
791
|
+
else this.open();
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Reflect a selection made outside this controller (the user clicked another
|
|
795
|
+
* sidebar row, or the layout dropped the panel id).
|
|
796
|
+
* @param panelId - the layout's current panel id, or null for the conversation.
|
|
797
|
+
*/
|
|
798
|
+
syncPanelSelection(panelId) {
|
|
799
|
+
const open = panelId === "ssh";
|
|
800
|
+
if (open === this.panelOpen) return;
|
|
801
|
+
this.panelOpen = open;
|
|
802
|
+
this.commit();
|
|
803
|
+
}
|
|
804
|
+
/** Switch tabs, keeping every other piece of view state. */
|
|
805
|
+
setActiveTab(tab) {
|
|
806
|
+
if (this.activeTab === tab) return;
|
|
807
|
+
this.activeTab = tab;
|
|
808
|
+
this.commit();
|
|
809
|
+
}
|
|
810
|
+
/** Hand the terminal tab a host to connect to (hosts-tab "connect" action). */
|
|
811
|
+
requestConnect(alias) {
|
|
812
|
+
this.connectRequest = {
|
|
813
|
+
alias,
|
|
814
|
+
nonce: Date.now()
|
|
815
|
+
};
|
|
816
|
+
this.activeTab = "terminal";
|
|
817
|
+
this.commit();
|
|
818
|
+
}
|
|
819
|
+
/** Record the host-side session so a later page mount can reattach. */
|
|
820
|
+
setTerminalSession(sessionId) {
|
|
821
|
+
if (this.terminalSessionId === sessionId) return;
|
|
822
|
+
this.terminalSessionId = sessionId;
|
|
823
|
+
this.commit();
|
|
824
|
+
}
|
|
825
|
+
/** Forget the session (it exited, or the user disconnected). */
|
|
826
|
+
clearTerminalSession() {
|
|
827
|
+
if (this.terminalSessionId === void 0) return;
|
|
828
|
+
this.terminalSessionId = void 0;
|
|
829
|
+
this.commit();
|
|
830
|
+
}
|
|
831
|
+
commit() {
|
|
832
|
+
this.snapshot = {
|
|
833
|
+
panelOpen: this.panelOpen,
|
|
834
|
+
activeTab: this.activeTab,
|
|
835
|
+
connectRequest: this.connectRequest,
|
|
836
|
+
terminalSessionId: this.terminalSessionId
|
|
837
|
+
};
|
|
838
|
+
for (const fn of [...this.listeners]) fn();
|
|
839
|
+
}
|
|
840
|
+
/** Ask the layout to select a panel; a shell without the face is a no-op. */
|
|
841
|
+
selectPanel(panelId) {
|
|
842
|
+
try {
|
|
843
|
+
this.deps.panel?.select(panelId);
|
|
844
|
+
} catch {}
|
|
845
|
+
}
|
|
846
|
+
};
|
|
706
847
|
//#endregion
|
|
707
848
|
//#region \0dsh-css:packages/dsh-ssh/src/client/panel/panel.module.css.mjs
|
|
708
|
-
const css = "[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-ssh-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-dsh-ssh-view]{display:block}html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [data-pane=conversation]>:not([data-dsh-ssh-view]),html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*=centerCol]>:not([data-dsh-ssh-view]){display:none!important}.mL8Uca_entry{box-sizing:border-box;min-height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;text-align:left;white-space:nowrap;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;margin:0 2px;padding:7px 8px;font-size:14px;line-height:22px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;width:16px;height:16px;display:inline-flex}.mL8Uca_entryIcon svg{width:16px;height:16px;display:block}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry,[data-sidebar-collapsed] .mL8Uca_entry{border-radius:12px;justify-content:center;width:36px;min-height:36px;margin:0 auto 12px;padding:0}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryIcon,[data-sidebar-collapsed] .mL8Uca_entryIcon,[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryIcon svg,[data-sidebar-collapsed] .mL8Uca_entryIcon svg{width:18px;height:18px}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entryLabel,[data-sidebar-collapsed] .mL8Uca_entryLabel{display:none}.mL8Uca_view{overflow:hidden}.mL8Uca_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex}.mL8Uca_panelHeader{flex:none;align-items:center;gap:10px;display:flex}.mL8Uca_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;flex:1;margin:0;font-size:16px;font-weight:700}.mL8Uca_backButton{align-items:center;gap:4px;display:inline-flex}.mL8Uca_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;gap:2px;display:flex}.mL8Uca_tab{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;border-radius:6px 6px 0 0;padding:7px 14px;font-size:13px}.mL8Uca_tab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_tab[data-active]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary);font-weight:600}.mL8Uca_panelContent{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.mL8Uca_tabBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}.mL8Uca_fillBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow:hidden}.mL8Uca_toolbar,.mL8Uca_controls{flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.mL8Uca_controls .mL8Uca_input{flex:0 260px;min-width:150px}.mL8Uca_toolbarSpacer{flex:1}.mL8Uca_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}.mL8Uca_search::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_groupBySelect{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:6px 8px;font-size:13px}.mL8Uca_groupSection+.mL8Uca_groupSection{border-top:1px solid var(--dsw-alias-border-l1)}.mL8Uca_groupHeader{background:var(--dsw-alias-bg-base,var(--dsw-specific-input-major));align-items:center;gap:8px;padding:4px 8px;display:flex;position:sticky;top:0}.mL8Uca_groupToggle{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;flex:1;align-items:center;gap:6px;padding:4px 6px;font-size:12.5px;font-weight:500;display:flex}.mL8Uca_groupToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_groupChevron{border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);flex:none;width:7px;height:7px;margin-bottom:2px;transition:transform .12s;transform:rotate(45deg)}.mL8Uca_groupChevron[data-collapsed]{margin-bottom:0;transform:rotate(-45deg)}.mL8Uca_groupName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mL8Uca_groupCount{color:var(--dsw-alias-label-tertiary);flex:none;font-weight:400}.mL8Uca_tableWrap{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex:1;min-height:0;overflow:auto}.mL8Uca_table{border-collapse:collapse;width:100%;font-size:12.5px}.mL8Uca_table th{z-index:1;text-align:left;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;padding:8px 10px;font-weight:600;position:sticky;top:0}.mL8Uca_table td{border-bottom:1px solid var(--dsw-alias-separator-primary);vertical-align:top;padding:7px 10px}.mL8Uca_table tbody tr:last-child td{border-bottom:none}.mL8Uca_table tbody tr:hover td{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellMuted{color:var(--dsw-alias-label-tertiary)}.mL8Uca_actions{white-space:nowrap;align-items:center;gap:8px;display:flex}.mL8Uca_inlineTest{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:11.5px;display:inline-flex}.mL8Uca_inlineTest[data-status=ok]{color:var(--dsw-alias-state-success-primary)}.mL8Uca_inlineTest[data-status=fail]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.6;display:inline-block}.mL8Uca_badge[data-kind=key]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_badge[data-kind=password]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_badge[data-kind=agent]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-kind=proxy]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);margin-left:6px}.mL8Uca_badge[data-status=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-status=fail]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge[data-status=timeout]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}.mL8Uca_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}.mL8Uca_primaryButton:disabled{opacity:.5;cursor:default}.mL8Uca_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}.mL8Uca_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_ghostButton:disabled{opacity:.45;cursor:default}.mL8Uca_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}.mL8Uca_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}.mL8Uca_linkButton:hover:not(:disabled){text-decoration:underline}.mL8Uca_linkButton:disabled{opacity:.45;cursor:default}.mL8Uca_linkButton[data-danger]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_spinner{border:2px solid var(--dsw-alias-state-business-primary);vertical-align:-1px;border-top-color:#0000;border-radius:50%;flex:none;width:11px;height:11px;animation:.8s linear infinite mL8Uca_dshSshSpin;display:inline-block}@keyframes mL8Uca_dshSshSpin{to{transform:rotate(360deg)}}.mL8Uca_banner{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;border-radius:8px;padding:8px 12px;font-size:12.5px;line-height:1.5}.mL8Uca_importSkips{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-direction:column;gap:4px;margin:6px 0 0;padding:8px 12px;list-style:none;display:flex}.mL8Uca_importSkipRow{justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;display:flex}.mL8Uca_banner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_banner[data-kind=error]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_banner[data-kind=info]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_empty,.mL8Uca_loading{text-align:center;color:var(--dsw-alias-label-tertiary);padding:28px 12px;font-size:12.5px}.mL8Uca_modalBackdrop{z-index:40;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.mL8Uca_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(560px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}.mL8Uca_modalTitle{margin:0;font-size:15px;font-weight:700}.mL8Uca_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}.mL8Uca_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.mL8Uca_hint{color:var(--dsw-alias-label-tertiary);font-size:11.5px}.mL8Uca_field{flex-direction:column;gap:5px;display:flex}.mL8Uca_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.mL8Uca_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}.mL8Uca_input:focus{border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_input::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_input:disabled{opacity:.55}.mL8Uca_formRow{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 12px;display:grid}.mL8Uca_radioRow{align-items:center;gap:16px;display:flex}.mL8Uca_radioLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:13px;display:inline-flex}.mL8Uca_termBody{flex-direction:column;flex:1;gap:8px;min-height:0;display:flex;overflow:hidden}.mL8Uca_termWrap{border:1px solid var(--dsw-alias-border-l1);background:#0b0e14;border-radius:10px;flex:1;min-height:0;position:relative;overflow:hidden}.mL8Uca_termContainer{position:absolute;inset:8px 10px}.mL8Uca_termPlaceholder{z-index:2;text-align:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);justify-content:center;align-items:center;padding:0 24px;font-size:12.5px;display:flex;position:absolute;inset:0}.mL8Uca_hiddenFile{display:none}.mL8Uca_browsePanel{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;display:flex;overflow:hidden}.mL8Uca_browseHeader{background:var(--dsw-alias-bg-layer-2);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:8px 10px;display:flex}.mL8Uca_browsePath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.mL8Uca_browseList{max-height:240px;overflow-y:auto}.mL8Uca_dirRow{text-align:left;width:100%;font:inherit;color:var(--dsw-alias-label-primary);border:none;border-bottom:1px solid var(--dsw-alias-separator-primary);cursor:pointer;background:0 0;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;padding:5px 10px;font-size:12.5px;display:grid}.mL8Uca_dirRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_dirRow[data-type=dir] .mL8Uca_dirName{color:var(--dsw-alias-state-business-primary)}.mL8Uca_dirRow[data-up]{color:var(--dsw-alias-label-secondary)}.mL8Uca_dirName{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.mL8Uca_dirType{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.mL8Uca_dirSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-align:right}.mL8Uca_transferBlock{flex-direction:column;flex:none;gap:6px;display:flex}.mL8Uca_progressMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.mL8Uca_progressTrack{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;height:8px;overflow:hidden}.mL8Uca_progressBar{background:var(--dsw-alias-state-business-primary);border-radius:999px;height:100%;transition:width .12s linear}.mL8Uca_tunnelList{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_tunnelRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;align-items:center;gap:10px;padding:8px 12px;display:flex}.mL8Uca_tunnelRow[data-state=forwarding]{border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_tunnelRow[data-state=connecting]{border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_tunnelRow[data-state=failed]{border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_tunnelLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;overflow:hidden}.mL8Uca_formCard{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.mL8Uca_clusterForm{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_clusterFilters{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;display:grid}.mL8Uca_commandInput{min-height:96px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellDetails summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px}.mL8Uca_cellPre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;word-break:break-all;border-radius:6px;max-height:180px;margin:4px 0 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow:auto}";
|
|
849
|
+
const css = ".mL8Uca_view{height:100%;min-height:0;overflow:hidden}.mL8Uca_panel{background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:10px;padding:14px 16px 16px;display:flex}.mL8Uca_panelHeader{flex:none;align-items:center;gap:10px;display:flex}.mL8Uca_panelTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;flex:1;margin:0;font-size:16px;font-weight:700}.mL8Uca_backButton{align-items:center;gap:4px;display:inline-flex}.mL8Uca_tabBar{border-bottom:1px solid var(--dsw-alias-border-l1);flex:none;gap:2px;display:flex}.mL8Uca_tab{color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;border-radius:6px 6px 0 0;padding:7px 14px;font-size:13px}.mL8Uca_tab:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_tab[data-active]{color:var(--dsw-alias-label-primary);border-bottom-color:var(--dsw-alias-state-business-primary);font-weight:600}.mL8Uca_panelContent{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.mL8Uca_tabBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}.mL8Uca_fillBody{flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow:hidden}.mL8Uca_toolbar,.mL8Uca_controls{flex-wrap:wrap;flex:none;align-items:center;gap:8px;display:flex}.mL8Uca_controls .mL8Uca_input{flex:0 260px;min-width:150px}.mL8Uca_toolbarSpacer{flex:1}.mL8Uca_search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 260px;padding:6px 10px;font-size:13px}.mL8Uca_search::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_groupBySelect{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;padding:6px 8px;font-size:13px}.mL8Uca_groupSection+.mL8Uca_groupSection{border-top:1px solid var(--dsw-alias-border-l1)}.mL8Uca_groupHeader{background:var(--dsw-alias-bg-base,var(--dsw-specific-input-major));align-items:center;gap:8px;padding:4px 8px;display:flex;position:sticky;top:0}.mL8Uca_groupToggle{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:6px;flex:1;align-items:center;gap:6px;padding:4px 6px;font-size:12.5px;font-weight:500;display:flex}.mL8Uca_groupToggle:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_groupChevron{border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);flex:none;width:7px;height:7px;margin-bottom:2px;transition:transform .12s;transform:rotate(45deg)}.mL8Uca_groupChevron[data-collapsed]{margin-bottom:0;transform:rotate(-45deg)}.mL8Uca_groupName{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mL8Uca_groupCount{color:var(--dsw-alias-label-tertiary);flex:none;font-weight:400}.mL8Uca_tableWrap{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex:1;min-height:0;overflow:auto}.mL8Uca_table{border-collapse:collapse;width:100%;font-size:12.5px}.mL8Uca_table th{z-index:1;text-align:left;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;padding:8px 10px;font-weight:600;position:sticky;top:0}.mL8Uca_table td{border-bottom:1px solid var(--dsw-alias-separator-primary);vertical-align:top;padding:7px 10px}.mL8Uca_table tbody tr:last-child td{border-bottom:none}.mL8Uca_table tbody tr:hover td{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellMuted{color:var(--dsw-alias-label-tertiary)}.mL8Uca_actions{white-space:nowrap;align-items:center;gap:8px;display:flex}.mL8Uca_inlineTest{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:11.5px;display:inline-flex}.mL8Uca_inlineTest[data-status=ok]{color:var(--dsw-alias-state-success-primary)}.mL8Uca_inlineTest[data-status=fail]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.6;display:inline-block}.mL8Uca_badge[data-kind=key]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_badge[data-kind=password]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_badge[data-kind=agent]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-kind=proxy]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);margin-left:6px}.mL8Uca_badge[data-status=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_badge[data-status=fail]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_badge[data-status=timeout]{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_primaryButton{color:var(--dsw-alias-label-primary-foreground);background:var(--dsw-alias-button-info-fill);cursor:pointer;white-space:nowrap;border:none;border-radius:8px;padding:6px 14px;font-size:13px;font-weight:600}.mL8Uca_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-info-hover)}.mL8Uca_primaryButton:disabled{opacity:.5;cursor:default}.mL8Uca_ghostButton{color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;padding:5px 12px;font-size:12px}.mL8Uca_ghostButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_ghostButton:disabled{opacity:.45;cursor:default}.mL8Uca_iconButton{width:26px;height:26px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0;font-size:13px;display:inline-flex}.mL8Uca_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_linkButton{color:var(--dsw-alias-state-business-primary);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0;font-size:12px}.mL8Uca_linkButton:hover:not(:disabled){text-decoration:underline}.mL8Uca_linkButton:disabled{opacity:.45;cursor:default}.mL8Uca_linkButton[data-danger]{color:var(--dsw-alias-state-error-primary)}.mL8Uca_spinner{border:2px solid var(--dsw-alias-state-business-primary);vertical-align:-1px;border-top-color:#0000;border-radius:50%;flex:none;width:11px;height:11px;animation:.8s linear infinite mL8Uca_dshSshSpin;display:inline-block}@keyframes mL8Uca_dshSshSpin{to{transform:rotate(360deg)}}.mL8Uca_banner{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;border-radius:8px;padding:8px 12px;font-size:12.5px;line-height:1.5}.mL8Uca_importSkips{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-direction:column;gap:4px;margin:6px 0 0;padding:8px 12px;list-style:none;display:flex}.mL8Uca_importSkipRow{justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;display:flex}.mL8Uca_banner[data-kind=ok]{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_banner[data-kind=error]{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_banner[data-kind=info]{color:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_empty,.mL8Uca_loading{text-align:center;color:var(--dsw-alias-label-tertiary);padding:28px 12px;font-size:12.5px}.mL8Uca_modalBackdrop{z-index:40;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.mL8Uca_modal{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);width:min(560px,100vw - 48px);max-height:calc(100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);border-radius:14px;flex-direction:column;gap:12px;padding:18px;display:flex;overflow-y:auto}.mL8Uca_modalTitle{margin:0;font-size:15px;font-weight:700}.mL8Uca_modalFooter{justify-content:flex-end;gap:10px;margin-top:4px;display:flex}.mL8Uca_formError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.mL8Uca_hint{color:var(--dsw-alias-label-tertiary);font-size:11.5px}.mL8Uca_field{flex-direction:column;gap:5px;display:flex}.mL8Uca_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.mL8Uca_input{color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);resize:vertical;border-radius:8px;outline:none;padding:7px 10px;font-family:inherit;font-size:13px}.mL8Uca_input:focus{border-color:var(--dsw-alias-state-business-primary)}.mL8Uca_input::placeholder{color:var(--dsw-alias-label-tertiary)}.mL8Uca_input:disabled{opacity:.55}.mL8Uca_formRow{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px 12px;display:grid}.mL8Uca_radioRow{align-items:center;gap:16px;display:flex}.mL8Uca_radioLabel{color:var(--dsw-alias-label-primary);cursor:pointer;align-items:center;gap:6px;font-size:13px;display:inline-flex}.mL8Uca_termBody{flex-direction:column;flex:1;gap:8px;min-height:0;display:flex;overflow:hidden}.mL8Uca_termWrap{border:1px solid var(--dsw-alias-border-l1);background:#0b0e14;border-radius:10px;flex:1;min-height:0;position:relative;overflow:hidden}.mL8Uca_termContainer{position:absolute;inset:8px 10px}.mL8Uca_termPlaceholder{z-index:2;text-align:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-base);justify-content:center;align-items:center;padding:0 24px;font-size:12.5px;display:flex;position:absolute;inset:0}.mL8Uca_hiddenFile{display:none}.mL8Uca_browsePanel{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;display:flex;overflow:hidden}.mL8Uca_browseHeader{background:var(--dsw-alias-bg-layer-2);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:8px 10px;display:flex}.mL8Uca_browsePath{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.mL8Uca_browseList{max-height:240px;overflow-y:auto}.mL8Uca_dirRow{text-align:left;width:100%;font:inherit;color:var(--dsw-alias-label-primary);border:none;border-bottom:1px solid var(--dsw-alias-separator-primary);cursor:pointer;background:0 0;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;padding:5px 10px;font-size:12.5px;display:grid}.mL8Uca_dirRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.mL8Uca_dirRow[data-type=dir] .mL8Uca_dirName{color:var(--dsw-alias-state-business-primary)}.mL8Uca_dirRow[data-up]{color:var(--dsw-alias-label-secondary)}.mL8Uca_dirName{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.mL8Uca_dirType{color:var(--dsw-alias-label-tertiary);white-space:nowrap}.mL8Uca_dirSize{color:var(--dsw-alias-label-tertiary);white-space:nowrap;text-align:right}.mL8Uca_transferBlock{flex-direction:column;flex:none;gap:6px;display:flex}.mL8Uca_progressMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:12px;font-size:12px;display:flex}.mL8Uca_progressTrack{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px;height:8px;overflow:hidden}.mL8Uca_progressBar{background:var(--dsw-alias-state-business-primary);border-radius:999px;height:100%;transition:width .12s linear}.mL8Uca_tunnelList{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_tunnelRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;align-items:center;gap:10px;padding:8px 12px;display:flex}.mL8Uca_tunnelRow[data-state=forwarding]{border-color:var(--dsw-alias-state-success-primary)}.mL8Uca_tunnelRow[data-state=connecting]{border-color:var(--dsw-alias-state-warn-primary)}.mL8Uca_tunnelRow[data-state=failed]{border-color:var(--dsw-alias-state-error-primary)}.mL8Uca_tunnelLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;overflow:hidden}.mL8Uca_formCard{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;flex-direction:column;flex:none;gap:10px;padding:12px;display:flex}.mL8Uca_clusterForm{flex-direction:column;flex:none;gap:8px;display:flex}.mL8Uca_clusterFilters{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;display:grid}.mL8Uca_commandInput{min-height:96px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.mL8Uca_cellDetails summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px}.mL8Uca_cellPre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;word-break:break-all;border-radius:6px;max-height:180px;margin:4px 0 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;overflow:auto}";
|
|
709
850
|
const tagId = "@linxin666/dsh-ssh/packages/dsh-ssh/src/client/panel/panel.module.css";
|
|
710
851
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
711
852
|
const tag = document.createElement("style");
|
|
@@ -736,9 +877,6 @@ window.__ModuleLoader__.load({
|
|
|
736
877
|
"dirType": "mL8Uca_dirType",
|
|
737
878
|
"dshSshSpin": "mL8Uca_dshSshSpin",
|
|
738
879
|
"empty": "mL8Uca_empty",
|
|
739
|
-
"entry": "mL8Uca_entry",
|
|
740
|
-
"entryIcon": "mL8Uca_entryIcon",
|
|
741
|
-
"entryLabel": "mL8Uca_entryLabel",
|
|
742
880
|
"field": "mL8Uca_field",
|
|
743
881
|
"fieldLabel": "mL8Uca_fieldLabel",
|
|
744
882
|
"fillBody": "mL8Uca_fillBody",
|
|
@@ -14893,7 +15031,7 @@ window.__ModuleLoader__.load({
|
|
|
14893
15031
|
subscribe: () => () => void 0
|
|
14894
15032
|
};
|
|
14895
15033
|
/** The xterm terminal view. */
|
|
14896
|
-
function TerminalTab({ api, presetAlias, requestId, terminalFont }) {
|
|
15034
|
+
function TerminalTab({ api, controller, sessionId, presetAlias, requestId, terminalFont }) {
|
|
14897
15035
|
const [hosts, setHosts] = (0, react.useState)([]);
|
|
14898
15036
|
const [alias, setAlias] = (0, react.useState)(presetAlias ?? "");
|
|
14899
15037
|
const [status, setStatus] = (0, react.useState)({ kind: "idle" });
|
|
@@ -14938,7 +15076,12 @@ window.__ModuleLoader__.load({
|
|
|
14938
15076
|
(0, react.useEffect)(() => {
|
|
14939
15077
|
if (presetAlias !== void 0) setAlias(presetAlias);
|
|
14940
15078
|
}, [presetAlias, requestId]);
|
|
14941
|
-
|
|
15079
|
+
/**
|
|
15080
|
+
* Tear the view down.
|
|
15081
|
+
* @param leave - `detach` leaves the host session alive for a reattach
|
|
15082
|
+
* (panel switch, unmount); `close` ends it (the disconnect control).
|
|
15083
|
+
*/
|
|
15084
|
+
const teardown = (leave) => {
|
|
14942
15085
|
setAuthPrompt(void 0);
|
|
14943
15086
|
setAuthInputs([]);
|
|
14944
15087
|
const connection = connRef.current;
|
|
@@ -14948,7 +15091,8 @@ window.__ModuleLoader__.load({
|
|
|
14948
15091
|
connection.onOutput = void 0;
|
|
14949
15092
|
connection.onExit = void 0;
|
|
14950
15093
|
connection.onAuthPrompt = void 0;
|
|
14951
|
-
connection.
|
|
15094
|
+
if (leave === "detach") connection.detach();
|
|
15095
|
+
else connection.close();
|
|
14952
15096
|
}
|
|
14953
15097
|
dataSubRef.current?.dispose();
|
|
14954
15098
|
dataSubRef.current = null;
|
|
@@ -14957,7 +15101,7 @@ window.__ModuleLoader__.load({
|
|
|
14957
15101
|
fitRef.current = null;
|
|
14958
15102
|
};
|
|
14959
15103
|
(0, react.useEffect)(() => () => {
|
|
14960
|
-
teardown();
|
|
15104
|
+
teardown("detach");
|
|
14961
15105
|
}, []);
|
|
14962
15106
|
(0, react.useEffect)(() => {
|
|
14963
15107
|
let lastCols = -1;
|
|
@@ -14988,12 +15132,15 @@ window.__ModuleLoader__.load({
|
|
|
14988
15132
|
window.removeEventListener("resize", sync);
|
|
14989
15133
|
};
|
|
14990
15134
|
}, []);
|
|
14991
|
-
|
|
14992
|
-
|
|
15135
|
+
/**
|
|
15136
|
+
* Build the xterm view and bind one connection to it.
|
|
15137
|
+
* @param open - opens the transport once the terminal has a size.
|
|
15138
|
+
*/
|
|
15139
|
+
const startSession = (open) => {
|
|
14993
15140
|
const container = containerRef.current;
|
|
14994
|
-
if (
|
|
15141
|
+
if (container === null) return;
|
|
14995
15142
|
if (status.kind === "connecting" || status.kind === "connected") return;
|
|
14996
|
-
teardown();
|
|
15143
|
+
teardown("detach");
|
|
14997
15144
|
setStatus({ kind: "connecting" });
|
|
14998
15145
|
const term = new import_xterm.Terminal({
|
|
14999
15146
|
convertEol: false,
|
|
@@ -15010,11 +15157,12 @@ window.__ModuleLoader__.load({
|
|
|
15010
15157
|
term.loadAddon(fit);
|
|
15011
15158
|
term.open(container);
|
|
15012
15159
|
fit.fit();
|
|
15013
|
-
const connection =
|
|
15160
|
+
const connection = open(term.cols, term.rows);
|
|
15014
15161
|
termRef.current = term;
|
|
15015
15162
|
fitRef.current = fit;
|
|
15016
15163
|
connRef.current = connection;
|
|
15017
15164
|
let settled = false;
|
|
15165
|
+
let connectedAlias = "";
|
|
15018
15166
|
dataSubRef.current = term.onData((data) => {
|
|
15019
15167
|
connection.send(data);
|
|
15020
15168
|
});
|
|
@@ -15026,12 +15174,14 @@ window.__ModuleLoader__.load({
|
|
|
15026
15174
|
});
|
|
15027
15175
|
setAuthInputs(prompts.map(() => ""));
|
|
15028
15176
|
};
|
|
15029
|
-
connection.onReady = () => {
|
|
15177
|
+
connection.onReady = (id, readyAlias) => {
|
|
15178
|
+
connectedAlias = readyAlias;
|
|
15179
|
+
controller.setTerminalSession(id);
|
|
15030
15180
|
setAuthPrompt(void 0);
|
|
15031
15181
|
setAuthInputs([]);
|
|
15032
15182
|
setStatus({
|
|
15033
15183
|
kind: "connected",
|
|
15034
|
-
alias:
|
|
15184
|
+
alias: readyAlias
|
|
15035
15185
|
});
|
|
15036
15186
|
};
|
|
15037
15187
|
connection.onOutput = (data) => {
|
|
@@ -15046,15 +15196,29 @@ window.__ModuleLoader__.load({
|
|
|
15046
15196
|
dataSubRef.current = null;
|
|
15047
15197
|
term.options.disableStdin = true;
|
|
15048
15198
|
connRef.current = null;
|
|
15199
|
+
controller.clearTerminalSession();
|
|
15049
15200
|
setStatus({
|
|
15050
15201
|
kind: "exited",
|
|
15051
|
-
alias:
|
|
15202
|
+
alias: connectedAlias !== "" ? connectedAlias : alias,
|
|
15052
15203
|
detail: error
|
|
15053
15204
|
});
|
|
15054
15205
|
};
|
|
15055
15206
|
};
|
|
15207
|
+
const connect = () => {
|
|
15208
|
+
const target = alias;
|
|
15209
|
+
if (target === "") return;
|
|
15210
|
+
startSession((cols, rows) => api.openTerminal(target, cols, rows));
|
|
15211
|
+
};
|
|
15212
|
+
const reattachedRef = (0, react.useRef)(void 0);
|
|
15213
|
+
(0, react.useEffect)(() => {
|
|
15214
|
+
if (sessionId === void 0 || connRef.current !== null) return;
|
|
15215
|
+
if (reattachedRef.current === sessionId) return;
|
|
15216
|
+
reattachedRef.current = sessionId;
|
|
15217
|
+
startSession((cols, rows) => api.attachTerminal(sessionId, cols, rows));
|
|
15218
|
+
}, [sessionId]);
|
|
15056
15219
|
const disconnect = () => {
|
|
15057
|
-
teardown();
|
|
15220
|
+
teardown("close");
|
|
15221
|
+
controller.clearTerminalSession();
|
|
15058
15222
|
setStatus({ kind: "idle" });
|
|
15059
15223
|
};
|
|
15060
15224
|
const submitAuth = (e) => {
|
|
@@ -15830,10 +15994,14 @@ window.__ModuleLoader__.load({
|
|
|
15830
15994
|
//#region src/client/panel/SshPanel.tsx
|
|
15831
15995
|
/**
|
|
15832
15996
|
* The SSH operations panel shell: a header with a close control, a five-tab
|
|
15833
|
-
* bar, and the active tab's content.
|
|
15834
|
-
*
|
|
15835
|
-
*
|
|
15836
|
-
*
|
|
15997
|
+
* bar, and the active tab's content.
|
|
15998
|
+
*
|
|
15999
|
+
* The active tab, the pending connect request and the live terminal session id
|
|
16000
|
+
* live in the controller, not in component state: the layout mounts this page
|
|
16001
|
+
* only while the panel is selected, so local state would reset the tab on
|
|
16002
|
+
* every panel switch and lose the id the terminal tab needs to reattach its
|
|
16003
|
+
* host-side session. Inactive tabs unmount, so each tab fetches its own data
|
|
16004
|
+
* on activation.
|
|
15837
16005
|
*/
|
|
15838
16006
|
/** The tab bar definition (labels resolved at render time). */
|
|
15839
16007
|
const TABS = [
|
|
@@ -15860,16 +16028,7 @@ window.__ModuleLoader__.load({
|
|
|
15860
16028
|
];
|
|
15861
16029
|
/** The tabbed SSH panel. */
|
|
15862
16030
|
function SshPanel({ controller, api, terminalFont }) {
|
|
15863
|
-
const panelOpen = (0, react.useSyncExternalStore)((0, react.useCallback)((listener) => controller.subscribe(listener), [controller]), (0, react.useCallback)(() => controller.getSnapshot()
|
|
15864
|
-
const [activeTab, setActiveTab] = (0, react.useState)("hosts");
|
|
15865
|
-
const [connectRequest, setConnectRequest] = (0, react.useState)(null);
|
|
15866
|
-
const handleConnect = (alias) => {
|
|
15867
|
-
setActiveTab("terminal");
|
|
15868
|
-
setConnectRequest((prev) => ({
|
|
15869
|
-
alias,
|
|
15870
|
-
nonce: (prev?.nonce ?? 0) + 1
|
|
15871
|
-
}));
|
|
15872
|
-
};
|
|
16031
|
+
const { panelOpen, activeTab, connectRequest, terminalSessionId } = (0, react.useSyncExternalStore)((0, react.useCallback)((listener) => controller.subscribe(listener), [controller]), (0, react.useCallback)(() => controller.getSnapshot(), [controller]));
|
|
15873
16032
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15874
16033
|
className: panel_module_css_default.panel,
|
|
15875
16034
|
"data-dsh-plugin": "ssh",
|
|
@@ -15905,7 +16064,7 @@ window.__ModuleLoader__.load({
|
|
|
15905
16064
|
"data-dsh-part": "tab",
|
|
15906
16065
|
className: panel_module_css_default.tab,
|
|
15907
16066
|
onClick: () => {
|
|
15908
|
-
setActiveTab(tab.id);
|
|
16067
|
+
controller.setActiveTab(tab.id);
|
|
15909
16068
|
},
|
|
15910
16069
|
children: tab.label()
|
|
15911
16070
|
}, tab.id))
|
|
@@ -15915,12 +16074,16 @@ window.__ModuleLoader__.load({
|
|
|
15915
16074
|
children: [
|
|
15916
16075
|
activeTab === "hosts" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostsTab, {
|
|
15917
16076
|
api,
|
|
15918
|
-
onConnect:
|
|
16077
|
+
onConnect: (alias) => {
|
|
16078
|
+
controller.requestConnect(alias);
|
|
16079
|
+
}
|
|
15919
16080
|
}),
|
|
15920
16081
|
activeTab === "terminal" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TerminalTab, {
|
|
15921
16082
|
api,
|
|
16083
|
+
controller,
|
|
15922
16084
|
presetAlias: connectRequest?.alias,
|
|
15923
16085
|
requestId: connectRequest?.nonce,
|
|
16086
|
+
sessionId: terminalSessionId,
|
|
15924
16087
|
terminalFont
|
|
15925
16088
|
}),
|
|
15926
16089
|
activeTab === "transfer" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransferTab, { api }),
|
|
@@ -15935,261 +16098,101 @@ window.__ModuleLoader__.load({
|
|
|
15935
16098
|
});
|
|
15936
16099
|
}
|
|
15937
16100
|
//#endregion
|
|
15938
|
-
//#region src/client/
|
|
15939
|
-
/**
|
|
15940
|
-
const
|
|
15941
|
-
const INVALIDATION_ONLY = Symbol.for("dsh-web.body-mutation-invalidation");
|
|
15942
|
-
function needsRecords(subscribers) {
|
|
15943
|
-
for (const listener of subscribers) if (!listener[INVALIDATION_ONLY]) return true;
|
|
15944
|
-
return false;
|
|
15945
|
-
}
|
|
16101
|
+
//#region src/client/native-panel.tsx
|
|
16102
|
+
/** Row order among the shell's global panel rows (Plugins 0, Schedule 10, board 20, skill center 30). */
|
|
16103
|
+
const PANEL_ORDER = 40;
|
|
15946
16104
|
/**
|
|
15947
|
-
*
|
|
15948
|
-
* The
|
|
15949
|
-
*
|
|
15950
|
-
*/
|
|
15951
|
-
function subscribeBodyInvalidations(subscriber) {
|
|
15952
|
-
const listener = () => {
|
|
15953
|
-
subscriber();
|
|
15954
|
-
};
|
|
15955
|
-
listener[INVALIDATION_ONLY] = true;
|
|
15956
|
-
return subscribeBodyMutations(listener);
|
|
15957
|
-
}
|
|
15958
|
-
/**
|
|
15959
|
-
* Subscribe to body-level childList mutations.
|
|
15960
|
-
* @param subscriber - called at most once per animation frame with the records
|
|
15961
|
-
* collected since the previous flush; must be safe to run repeatedly.
|
|
15962
|
-
* @returns the disposer removing this subscriber (and the observer when it was
|
|
15963
|
-
* the last one).
|
|
15964
|
-
*/
|
|
15965
|
-
function subscribeBodyMutations(subscriber) {
|
|
15966
|
-
if (typeof globalThis === "undefined" || typeof document === "undefined") return () => {};
|
|
15967
|
-
if (typeof MutationObserver !== "function") return () => {};
|
|
15968
|
-
const registry = globalThis;
|
|
15969
|
-
let hub = registry[HUB_KEY];
|
|
15970
|
-
if (hub === void 0) {
|
|
15971
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
15972
|
-
const created = {
|
|
15973
|
-
observer: void 0,
|
|
15974
|
-
subscribers,
|
|
15975
|
-
pending: [],
|
|
15976
|
-
scheduled: false
|
|
15977
|
-
};
|
|
15978
|
-
const flush = () => {
|
|
15979
|
-
created.frame = void 0;
|
|
15980
|
-
created.scheduled = false;
|
|
15981
|
-
const batch = created.pending;
|
|
15982
|
-
created.pending = [];
|
|
15983
|
-
for (const listener of [...subscribers]) {
|
|
15984
|
-
if (!subscribers.has(listener)) continue;
|
|
15985
|
-
try {
|
|
15986
|
-
listener(batch);
|
|
15987
|
-
} catch {}
|
|
15988
|
-
}
|
|
15989
|
-
};
|
|
15990
|
-
const schedule = () => {
|
|
15991
|
-
if (created.scheduled) return;
|
|
15992
|
-
created.scheduled = true;
|
|
15993
|
-
if (typeof requestAnimationFrame === "function") created.frame = requestAnimationFrame(flush);
|
|
15994
|
-
else flush();
|
|
15995
|
-
};
|
|
15996
|
-
created.observer = new MutationObserver((records) => {
|
|
15997
|
-
if (needsRecords(subscribers)) for (const record of records) created.pending.push(record);
|
|
15998
|
-
schedule();
|
|
15999
|
-
});
|
|
16000
|
-
created.observer.observe(document.body ?? document.documentElement, {
|
|
16001
|
-
childList: true,
|
|
16002
|
-
subtree: true
|
|
16003
|
-
});
|
|
16004
|
-
registry[HUB_KEY] = created;
|
|
16005
|
-
hub = created;
|
|
16006
|
-
}
|
|
16007
|
-
const active = hub;
|
|
16008
|
-
active.subscribers.add(subscriber);
|
|
16009
|
-
let subscribed = true;
|
|
16010
|
-
return () => {
|
|
16011
|
-
if (!subscribed) return;
|
|
16012
|
-
subscribed = false;
|
|
16013
|
-
active.subscribers.delete(subscriber);
|
|
16014
|
-
if (!needsRecords(active.subscribers)) active.pending = [];
|
|
16015
|
-
if (active.subscribers.size === 0 && registry[HUB_KEY] === active) {
|
|
16016
|
-
active.observer.disconnect();
|
|
16017
|
-
if (active.frame !== void 0 && typeof cancelAnimationFrame === "function") cancelAnimationFrame(active.frame);
|
|
16018
|
-
active.frame = void 0;
|
|
16019
|
-
active.pending = [];
|
|
16020
|
-
active.scheduled = false;
|
|
16021
|
-
delete registry[HUB_KEY];
|
|
16022
|
-
}
|
|
16023
|
-
};
|
|
16024
|
-
}
|
|
16025
|
-
//#endregion
|
|
16026
|
-
//#region src/client/panel-mount-core.ts
|
|
16027
|
-
/**
|
|
16028
|
-
* Center-column panel takeover lifecycle.
|
|
16105
|
+
* The sidebar row glyph the shell asks for at its own size and active state.
|
|
16106
|
+
* The shell owns the button, label, tooltip and rail geometry; this component
|
|
16107
|
+
* draws only the glyph, like every other panel row.
|
|
16029
16108
|
*
|
|
16030
|
-
* The `
|
|
16031
|
-
*
|
|
16032
|
-
*
|
|
16033
|
-
*
|
|
16034
|
-
*
|
|
16035
|
-
*
|
|
16036
|
-
* manages, and a stylesheet rule hides the conversation content while the
|
|
16037
|
-
* panel is active. Toggling is a data attribute on <html> — no React
|
|
16038
|
-
* involvement, so the conversation subtree underneath stays mounted and
|
|
16039
|
-
* stateful.
|
|
16040
|
-
*
|
|
16041
|
-
* Consuming plugins keep a thin wrapper that supplies the panel tree,
|
|
16042
|
-
* container attribute names, and stylesheet class; those names are pinned by
|
|
16043
|
-
* each package's CSS, skins, and the semantic-attributes contract. The
|
|
16044
|
-
* sidebar row toggling the panel shares its core the same way
|
|
16045
|
-
* (shared/client/sidebar-entry-core.ts, synced copy).
|
|
16109
|
+
* The glyph carries `data-dsh-panel-entry` because it is the only DOM this
|
|
16110
|
+
* panel's own code owns inside that shell-owned row: the L2 contract (skins)
|
|
16111
|
+
* resolves which row belongs to which plugin through it, since the shell
|
|
16112
|
+
* stamps no per-entry hook of its own (see contracts/semantic-attrs-v1.md).
|
|
16113
|
+
* @param props - the shell's icon share: square edge and selection state.
|
|
16114
|
+
* @returns the decorative terminal glyph.
|
|
16046
16115
|
*/
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
|
|
16116
|
+
function SshPanelIcon({ size }) {
|
|
16117
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
16118
|
+
"data-dsh-panel-entry": "ssh",
|
|
16119
|
+
viewBox: "0 0 16 16",
|
|
16120
|
+
width: size,
|
|
16121
|
+
height: size,
|
|
16122
|
+
fill: "none",
|
|
16123
|
+
stroke: "currentColor",
|
|
16124
|
+
strokeWidth: "1.5",
|
|
16125
|
+
strokeLinecap: "round",
|
|
16126
|
+
strokeLinejoin: "round",
|
|
16127
|
+
"aria-hidden": "true",
|
|
16128
|
+
children: [
|
|
16129
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
16130
|
+
x: "1.75",
|
|
16131
|
+
y: "2.25",
|
|
16132
|
+
width: "12.5",
|
|
16133
|
+
height: "11.5",
|
|
16134
|
+
rx: "1.75"
|
|
16135
|
+
}),
|
|
16136
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4.25 5.25l2.75 2.75-2.75 2.75" }),
|
|
16137
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8.5 10.75h3.25" })
|
|
16138
|
+
]
|
|
16139
|
+
});
|
|
16054
16140
|
}
|
|
16055
16141
|
/**
|
|
16056
|
-
*
|
|
16057
|
-
*
|
|
16058
|
-
*
|
|
16142
|
+
* The main-slot page. The layout mounts it only while this panel is selected;
|
|
16143
|
+
* the wrapper carries the pinned `data-dsh-ssh-view` semantic anchor (L2
|
|
16144
|
+
* contract, skins) the takeover container used to own.
|
|
16145
|
+
* @param props - the framework main-slot share plus this entry's injected face.
|
|
16146
|
+
* @returns the SSH panel page.
|
|
16059
16147
|
*/
|
|
16060
|
-
function
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
if (container !== void 0 && !container.isConnected) {
|
|
16071
|
-
root?.unmount();
|
|
16072
|
-
root = void 0;
|
|
16073
|
-
container.remove();
|
|
16074
|
-
container = void 0;
|
|
16075
|
-
}
|
|
16076
|
-
if (container === void 0) {
|
|
16077
|
-
const column = conversationColumn();
|
|
16078
|
-
if (column === void 0) return;
|
|
16079
|
-
container = document.createElement("div");
|
|
16080
|
-
container.dataset[options.viewDatasetKey] = "";
|
|
16081
|
-
container.dataset.dshPlugin = options.pluginName;
|
|
16082
|
-
container.className = options.viewClassName;
|
|
16083
|
-
column.appendChild(container);
|
|
16084
|
-
}
|
|
16085
|
-
if (root !== void 0 || !options.isOpen()) return;
|
|
16086
|
-
root = (0, react_dom_client.createRoot)(container);
|
|
16087
|
-
options.render(root);
|
|
16088
|
-
};
|
|
16089
|
-
const unsubscribeBody = subscribeBodyInvalidations(() => {
|
|
16090
|
-
ensure();
|
|
16148
|
+
function SshPanelPage({ controller, api, terminalFont }) {
|
|
16149
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
16150
|
+
className: panel_module_css_default.view,
|
|
16151
|
+
"data-dsh-ssh-view": "",
|
|
16152
|
+
"data-dsh-plugin": "ssh",
|
|
16153
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SshPanel, {
|
|
16154
|
+
controller,
|
|
16155
|
+
api,
|
|
16156
|
+
terminalFont
|
|
16157
|
+
})
|
|
16091
16158
|
});
|
|
16092
|
-
const applyActive = () => {
|
|
16093
|
-
if (options.isOpen()) {
|
|
16094
|
-
ensure();
|
|
16095
|
-
document.documentElement.removeAttribute(options.siblingActiveAttribute);
|
|
16096
|
-
document.documentElement.setAttribute(options.activeAttribute, "");
|
|
16097
|
-
document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: options.panelName }));
|
|
16098
|
-
} else document.documentElement.removeAttribute(options.activeAttribute);
|
|
16099
|
-
};
|
|
16100
|
-
const onOtherActivate = (event) => {
|
|
16101
|
-
if (event.detail === options.siblingPanelName && options.isOpen()) options.close();
|
|
16102
|
-
};
|
|
16103
|
-
const onClickSidebarRow = (event) => {
|
|
16104
|
-
if (!options.isOpen()) return;
|
|
16105
|
-
const target = event.target;
|
|
16106
|
-
if (target === null) return;
|
|
16107
|
-
if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) options.close();
|
|
16108
|
-
};
|
|
16109
|
-
document.addEventListener("click", onClickSidebarRow, true);
|
|
16110
|
-
document.addEventListener(ACTIVATE_EVENT, onOtherActivate);
|
|
16111
|
-
const unsubscribe = options.subscribe(applyActive);
|
|
16112
|
-
applyActive();
|
|
16113
|
-
ensure();
|
|
16114
|
-
return () => {
|
|
16115
|
-
document.removeEventListener("click", onClickSidebarRow, true);
|
|
16116
|
-
document.removeEventListener(ACTIVATE_EVENT, onOtherActivate);
|
|
16117
|
-
unsubscribeBody();
|
|
16118
|
-
unsubscribe();
|
|
16119
|
-
unsubscribeLocale?.();
|
|
16120
|
-
document.documentElement.removeAttribute(options.activeAttribute);
|
|
16121
|
-
root?.unmount();
|
|
16122
|
-
root = void 0;
|
|
16123
|
-
container?.remove();
|
|
16124
|
-
container = void 0;
|
|
16125
|
-
};
|
|
16126
16159
|
}
|
|
16127
|
-
//#endregion
|
|
16128
|
-
//#region src/client/mount.tsx
|
|
16129
16160
|
/**
|
|
16130
|
-
*
|
|
16131
|
-
*
|
|
16132
|
-
*
|
|
16133
|
-
*
|
|
16161
|
+
* Register the SSH panel's sidebar row and center-column page.
|
|
16162
|
+
*
|
|
16163
|
+
* Both seats are declared by shell plugins this package does not depend on at
|
|
16164
|
+
* runtime, so each registration is wrapped in `ctx.slots.inject`: the callback
|
|
16165
|
+
* runs only after the owning entry declares the seat, and a shell that never
|
|
16166
|
+
* declares it leaves the panel simply absent instead of failing boot.
|
|
16167
|
+
* @param ctx - client root context (services: slots).
|
|
16168
|
+
* @param controller - the controller the page and the row drive.
|
|
16169
|
+
* @param api - the SSH API client the page operates through.
|
|
16134
16170
|
* @param terminalFont - live terminal-font setting source (issue #577).
|
|
16135
|
-
* @
|
|
16136
|
-
* on a Language switch.
|
|
16137
|
-
* @returns disposer unmounting the tree and restoring the column.
|
|
16171
|
+
* @returns disposer releasing both registrations.
|
|
16138
16172
|
*/
|
|
16139
|
-
function
|
|
16140
|
-
|
|
16141
|
-
|
|
16173
|
+
function registerSshPanel(ctx, controller, api, terminalFont) {
|
|
16174
|
+
const slots = ctx.slots;
|
|
16175
|
+
const disposers = [];
|
|
16176
|
+
disposers.push(slots.inject("sidebar.panellist", () => slots.register({
|
|
16177
|
+
name: "sidebar.panellist",
|
|
16178
|
+
id: "ssh",
|
|
16179
|
+
order: PANEL_ORDER,
|
|
16180
|
+
label: () => tt("entry.label")
|
|
16181
|
+
}, SshPanelIcon)));
|
|
16182
|
+
disposers.push(slots.inject("main", () => slots.register({
|
|
16183
|
+
name: "main",
|
|
16184
|
+
key: "ssh",
|
|
16185
|
+
inject: () => ({
|
|
16142
16186
|
controller,
|
|
16143
16187
|
api,
|
|
16144
16188
|
terminalFont
|
|
16145
|
-
})
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
siblingActiveAttribute: "data-dsh-taskboard-active",
|
|
16151
|
-
panelName: "ssh",
|
|
16152
|
-
siblingPanelName: "taskboard",
|
|
16153
|
-
isOpen: () => controller.getSnapshot().panelOpen,
|
|
16154
|
-
close: () => controller.close(),
|
|
16155
|
-
subscribe: (listener) => controller.subscribe(listener),
|
|
16156
|
-
locale
|
|
16157
|
-
});
|
|
16189
|
+
})
|
|
16190
|
+
}, SshPanelPage)));
|
|
16191
|
+
return () => {
|
|
16192
|
+
for (const dispose of disposers.splice(0)) dispose();
|
|
16193
|
+
};
|
|
16158
16194
|
}
|
|
16159
16195
|
//#endregion
|
|
16160
|
-
//#region src/client/panel/controller.ts
|
|
16161
|
-
/** The panel state owner the sidebar entry toggles and the view renders from. */
|
|
16162
|
-
var PanelController = class {
|
|
16163
|
-
panelOpen = false;
|
|
16164
|
-
listeners = /* @__PURE__ */ new Set();
|
|
16165
|
-
getSnapshot() {
|
|
16166
|
-
return { panelOpen: this.panelOpen };
|
|
16167
|
-
}
|
|
16168
|
-
subscribe(fn) {
|
|
16169
|
-
this.listeners.add(fn);
|
|
16170
|
-
return () => {
|
|
16171
|
-
this.listeners.delete(fn);
|
|
16172
|
-
};
|
|
16173
|
-
}
|
|
16174
|
-
open() {
|
|
16175
|
-
if (this.panelOpen) return;
|
|
16176
|
-
this.panelOpen = true;
|
|
16177
|
-
this.notify();
|
|
16178
|
-
}
|
|
16179
|
-
close() {
|
|
16180
|
-
if (!this.panelOpen) return;
|
|
16181
|
-
this.panelOpen = false;
|
|
16182
|
-
this.notify();
|
|
16183
|
-
}
|
|
16184
|
-
toggle() {
|
|
16185
|
-
if (this.panelOpen) this.close();
|
|
16186
|
-
else this.open();
|
|
16187
|
-
}
|
|
16188
|
-
notify() {
|
|
16189
|
-
for (const fn of [...this.listeners]) fn();
|
|
16190
|
-
}
|
|
16191
|
-
};
|
|
16192
|
-
//#endregion
|
|
16193
16196
|
//#region src/client/settings-binding.ts
|
|
16194
16197
|
/**
|
|
16195
16198
|
* The snapshot a reader reports before the Host has answered with this
|
|
@@ -16292,175 +16295,6 @@ window.__ModuleLoader__.load({
|
|
|
16292
16295
|
return new SharedFormsReader(ctx.configForms, field);
|
|
16293
16296
|
}
|
|
16294
16297
|
//#endregion
|
|
16295
|
-
//#region src/client/sidebar-entry-core.ts
|
|
16296
|
-
/**
|
|
16297
|
-
* Shared sidebar entry injection core.
|
|
16298
|
-
*
|
|
16299
|
-
* dsh's sidebar shell exposes no slot an external plugin can register into,
|
|
16300
|
-
* so the entry row is injected between the shell's New Session button and the
|
|
16301
|
-
* workspace browser. The injection self-heals: a MutationObserver watches the
|
|
16302
|
-
* sidebar root and re-inserts the row whenever a React re-render displaces it
|
|
16303
|
-
* (re-insertion happens in the same frame, before paint, so no flicker).
|
|
16304
|
-
*
|
|
16305
|
-
* The row is plain DOM (no React tree) so it can never disturb the shell's
|
|
16306
|
-
* reconciliation; the view it toggles is a separate root owned by the caller.
|
|
16307
|
-
*
|
|
16308
|
-
* Packages receive this file as a generated copy via scripts/sync-shared.mjs;
|
|
16309
|
-
* edit the shared source and re-run the sync instead of editing a copy.
|
|
16310
|
-
*/
|
|
16311
|
-
/** Find the sidebar shell root element, or undefined while not yet mounted. */
|
|
16312
|
-
function sidebarRoot() {
|
|
16313
|
-
const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
|
|
16314
|
-
if (column === null) return void 0;
|
|
16315
|
-
return column.querySelector("[class*=\"logoRow\"]")?.parentElement ?? column.firstElementChild;
|
|
16316
|
-
}
|
|
16317
|
-
/** The New Session button: nested in the logo row on current shells, a direct child on legacy shells. */
|
|
16318
|
-
function newSessionButton(root) {
|
|
16319
|
-
const nested = root.querySelector("button[class*=\"newSession\"]");
|
|
16320
|
-
if (nested !== null) return nested;
|
|
16321
|
-
for (const child of root.children) if (child.tagName === "BUTTON") return child;
|
|
16322
|
-
}
|
|
16323
|
-
/** Build the entry row (detached; inserted once the shell is up). */
|
|
16324
|
-
function createEntry(options) {
|
|
16325
|
-
const entry = document.createElement("button");
|
|
16326
|
-
entry.type = "button";
|
|
16327
|
-
entry.setAttribute(options.rowAttribute, "");
|
|
16328
|
-
if (options.plugin !== void 0) {
|
|
16329
|
-
entry.setAttribute("data-dsh-plugin", options.plugin);
|
|
16330
|
-
entry.setAttribute("data-dsh-part", "sidebar-entry");
|
|
16331
|
-
}
|
|
16332
|
-
entry.className = options.css["entry"] ?? "";
|
|
16333
|
-
const labelSpan = document.createElement("span");
|
|
16334
|
-
labelSpan.className = options.css["entryLabel"] ?? "";
|
|
16335
|
-
const iconSpan = document.createElement("span");
|
|
16336
|
-
iconSpan.className = options.css["entryIcon"] ?? "";
|
|
16337
|
-
iconSpan.innerHTML = options.icon;
|
|
16338
|
-
entry.append(iconSpan, labelSpan);
|
|
16339
|
-
const applyLabel = () => {
|
|
16340
|
-
entry.setAttribute("aria-label", options.label());
|
|
16341
|
-
if (options.tooltip !== void 0) entry.setAttribute("title", options.tooltip());
|
|
16342
|
-
labelSpan.textContent = options.label();
|
|
16343
|
-
};
|
|
16344
|
-
applyLabel();
|
|
16345
|
-
entry.addEventListener("click", options.onToggle);
|
|
16346
|
-
return {
|
|
16347
|
-
entry,
|
|
16348
|
-
applyLabel
|
|
16349
|
-
};
|
|
16350
|
-
}
|
|
16351
|
-
/** Re-insert the entry after the New Session row (before the browser region). */
|
|
16352
|
-
function placeEntry(root, entry, options) {
|
|
16353
|
-
const button = newSessionButton(root);
|
|
16354
|
-
if (button === void 0) return false;
|
|
16355
|
-
if (entry.parentElement !== root) {
|
|
16356
|
-
const row = button.closest("[class*=\"logoRow\"]");
|
|
16357
|
-
const base = row !== null && row.parentElement === root ? row : button;
|
|
16358
|
-
const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches(options.familySelectors.join(", ")));
|
|
16359
|
-
const anchor = options.position === "before" ? family.length > 0 ? family[0] : base.nextElementSibling : family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling;
|
|
16360
|
-
root.insertBefore(entry, anchor);
|
|
16361
|
-
}
|
|
16362
|
-
return true;
|
|
16363
|
-
}
|
|
16364
|
-
/**
|
|
16365
|
-
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
16366
|
-
* on later React re-renders.
|
|
16367
|
-
* @param options - the row's attribute/icon/copy/action/ordering configuration.
|
|
16368
|
-
* @returns disposer removing the entry and its observers.
|
|
16369
|
-
*/
|
|
16370
|
-
function mountSidebarEntry$1(options) {
|
|
16371
|
-
if (typeof document !== "undefined" && document.querySelector(options.rowSelector) !== null) return () => {};
|
|
16372
|
-
const { entry, applyLabel } = createEntry(options);
|
|
16373
|
-
let root;
|
|
16374
|
-
let placed = false;
|
|
16375
|
-
let unsubscribeRefresh;
|
|
16376
|
-
if (options.refresh !== void 0) try {
|
|
16377
|
-
unsubscribeRefresh = options.refresh.subscribe(applyLabel);
|
|
16378
|
-
} catch {}
|
|
16379
|
-
const tryPlace = () => {
|
|
16380
|
-
if (root !== void 0 && !root.isConnected) {
|
|
16381
|
-
rootObserver.disconnect();
|
|
16382
|
-
root = void 0;
|
|
16383
|
-
placed = false;
|
|
16384
|
-
}
|
|
16385
|
-
if (placed) {
|
|
16386
|
-
if (document.body.contains(entry)) return;
|
|
16387
|
-
rootObserver.disconnect();
|
|
16388
|
-
root = void 0;
|
|
16389
|
-
placed = false;
|
|
16390
|
-
}
|
|
16391
|
-
root ??= sidebarRoot();
|
|
16392
|
-
if (root === void 0) return;
|
|
16393
|
-
placed = placeEntry(root, entry, options);
|
|
16394
|
-
if (placed) rootObserver.observe(root, {
|
|
16395
|
-
childList: true,
|
|
16396
|
-
subtree: true
|
|
16397
|
-
});
|
|
16398
|
-
};
|
|
16399
|
-
const unsubscribeBody = subscribeBodyInvalidations(() => {
|
|
16400
|
-
tryPlace();
|
|
16401
|
-
});
|
|
16402
|
-
const rootObserver = new MutationObserver(() => {
|
|
16403
|
-
if (root === void 0 || !root.isConnected) {
|
|
16404
|
-
placed = false;
|
|
16405
|
-
tryPlace();
|
|
16406
|
-
return;
|
|
16407
|
-
}
|
|
16408
|
-
if (!root.contains(entry)) placed = placeEntry(root, entry, options);
|
|
16409
|
-
});
|
|
16410
|
-
const unsubscribeActive = options.active === void 0 ? void 0 : (() => {
|
|
16411
|
-
const syncActive = () => {
|
|
16412
|
-
if (options.active.isOpen()) entry.dataset.active = "true";
|
|
16413
|
-
else delete entry.dataset.active;
|
|
16414
|
-
};
|
|
16415
|
-
const unsubscribe = options.active.subscribe(syncActive);
|
|
16416
|
-
syncActive();
|
|
16417
|
-
return unsubscribe;
|
|
16418
|
-
})();
|
|
16419
|
-
tryPlace();
|
|
16420
|
-
return () => {
|
|
16421
|
-
unsubscribeBody();
|
|
16422
|
-
rootObserver.disconnect();
|
|
16423
|
-
unsubscribeRefresh?.();
|
|
16424
|
-
unsubscribeActive?.();
|
|
16425
|
-
entry.remove();
|
|
16426
|
-
};
|
|
16427
|
-
}
|
|
16428
|
-
//#endregion
|
|
16429
|
-
//#region src/client/sidebar-entry.ts
|
|
16430
|
-
/** Stable data attribute identifying the injected entry row. */
|
|
16431
|
-
const ENTRY_SELECTOR = "[data-dsh-ssh-entry]";
|
|
16432
|
-
/** Inline terminal glyph sized to the shell's panel-row navigation icons. */
|
|
16433
|
-
const ICON = "<svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"1.75\" y=\"2.25\" width=\"12.5\" height=\"11.5\" rx=\"1.75\"/><path d=\"M4.25 5.25l2.75 2.75-2.75 2.75\"/><path d=\"M8.5 10.75h3.25\"/></svg>";
|
|
16434
|
-
/**
|
|
16435
|
-
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
16436
|
-
* on later React re-renders.
|
|
16437
|
-
* @param controller - the panel controller the entry toggles.
|
|
16438
|
-
* @param locale - locale-change source; when given, re-applies the label on
|
|
16439
|
-
* a Language switch (the plain-DOM row otherwise keeps the mount-time copy).
|
|
16440
|
-
* @returns disposer removing the entry and its observers.
|
|
16441
|
-
*/
|
|
16442
|
-
function mountSidebarEntry(controller, locale) {
|
|
16443
|
-
return mountSidebarEntry$1({
|
|
16444
|
-
rowAttribute: "data-dsh-ssh-entry",
|
|
16445
|
-
rowSelector: ENTRY_SELECTOR,
|
|
16446
|
-
plugin: "ssh",
|
|
16447
|
-
icon: ICON,
|
|
16448
|
-
css: panel_module_css_default,
|
|
16449
|
-
label: () => tt("entry.label"),
|
|
16450
|
-
tooltip: () => tt("entry.tooltip"),
|
|
16451
|
-
refresh: locale === void 0 ? void 0 : { subscribe: (listener) => locale.subscribe(listener) },
|
|
16452
|
-
onToggle: () => {
|
|
16453
|
-
controller.toggle();
|
|
16454
|
-
},
|
|
16455
|
-
position: "after",
|
|
16456
|
-
familySelectors: ["[data-dsh-taskboard-entry]", "[data-dsh-ssh-entry]"],
|
|
16457
|
-
active: {
|
|
16458
|
-
subscribe: (listener) => controller.subscribe(listener),
|
|
16459
|
-
isOpen: () => controller.getSnapshot().panelOpen
|
|
16460
|
-
}
|
|
16461
|
-
});
|
|
16462
|
-
}
|
|
16463
|
-
//#endregion
|
|
16464
16298
|
//#region src/client/telemetry.ts
|
|
16465
16299
|
const VISITOR_KEY = "dsh-web-ui-telemetry-visitor";
|
|
16466
16300
|
const DAY_KEY_PREFIX = "dsh-web-ui-telemetry-day:";
|
|
@@ -16468,7 +16302,7 @@ window.__ModuleLoader__.load({
|
|
|
16468
16302
|
/** The building package's version, when the bundle carries it. */
|
|
16469
16303
|
function bakedVersion() {
|
|
16470
16304
|
try {
|
|
16471
|
-
return "0.4.
|
|
16305
|
+
return "0.4.3";
|
|
16472
16306
|
} catch {
|
|
16473
16307
|
return;
|
|
16474
16308
|
}
|
|
@@ -16566,7 +16400,9 @@ window.__ModuleLoader__.load({
|
|
|
16566
16400
|
try {
|
|
16567
16401
|
setRuntimeTranslate(ctx.locale.bind(NS));
|
|
16568
16402
|
} catch {}
|
|
16569
|
-
const controller = new PanelController()
|
|
16403
|
+
const controller = new PanelController({ panel: { select: (panelId) => {
|
|
16404
|
+
ctx.get("layout")?.selectPanel?.(panelId);
|
|
16405
|
+
} } });
|
|
16570
16406
|
const api = new SshApi();
|
|
16571
16407
|
const settings = bindSettingsReader(ctx, SETTINGS_NS, TERMINAL_FONT_FIELD);
|
|
16572
16408
|
const terminalFont = {
|
|
@@ -16581,10 +16417,18 @@ window.__ModuleLoader__.load({
|
|
|
16581
16417
|
}, "dsh-ssh: settings binding");
|
|
16582
16418
|
const disposers = [];
|
|
16583
16419
|
try {
|
|
16584
|
-
disposers.push(
|
|
16585
|
-
|
|
16420
|
+
disposers.push(registerSshPanel(ctx, controller, api, terminalFont));
|
|
16421
|
+
const layoutFace = ctx.get("layout");
|
|
16422
|
+
if (layoutFace?.panelInfo !== void 0) {
|
|
16423
|
+
const sync = () => {
|
|
16424
|
+
const active = layoutFace.panelInfo.getSnapshot().activePanelId;
|
|
16425
|
+
controller.syncPanelSelection(active === "ssh" ? "ssh" : null);
|
|
16426
|
+
};
|
|
16427
|
+
sync();
|
|
16428
|
+
disposers.push(layoutFace.panelInfo.subscribe(sync));
|
|
16429
|
+
}
|
|
16586
16430
|
} catch (error) {
|
|
16587
|
-
console.warn("[dsh-ssh]
|
|
16431
|
+
console.warn("[dsh-ssh] panel registration failed:", error);
|
|
16588
16432
|
}
|
|
16589
16433
|
ctx.effect(() => () => {
|
|
16590
16434
|
for (const dispose of disposers.splice(0)) dispose();
|