@linxin666/dsh-ssh 0.1.20 → 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 +5 -5
- package/README.zh.md +3 -3
- package/lib/client.js +291 -119
- package/lib/client.js.map +1 -1
- package/lib/index.js +51 -33
- package/lib/types/client/locales.d.ts +135 -127
- package/lib/types/client/panel/HostsTab.d.ts +16 -0
- package/lib/types/client/sidebar-entry-core.d.ts +60 -0
- package/lib/types/client/sidebar-entry.d.ts +7 -11
- package/lib/types/protocol.d.ts +1 -1
- package/package.json +12 -12
- package/src/client/locales.ts +16 -0
- package/src/client/mount.tsx +1 -0
- package/src/client/panel/HostsTab.tsx +152 -48
- package/src/client/panel/SshPanel.tsx +1 -1
- package/src/client/panel/TerminalTab.tsx +1 -1
- package/src/client/panel/panel.module.css +75 -0
- 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 +33 -8
package/lib/client.js
CHANGED
|
@@ -323,6 +323,14 @@ window.__ModuleLoader__.load({
|
|
|
323
323
|
"hosts.add": "新增主机",
|
|
324
324
|
"hosts.import": "导入 ~/.ssh/config",
|
|
325
325
|
"hosts.search": "搜索别名 / 描述 / 标签…",
|
|
326
|
+
"hosts.groupBy.label": "分组方式",
|
|
327
|
+
"hosts.groupBy.none": "不分组",
|
|
328
|
+
"hosts.groupBy.environment": "按环境分组",
|
|
329
|
+
"hosts.groupBy.tags": "按标签分组",
|
|
330
|
+
"hosts.group.ungrouped": "未设置环境",
|
|
331
|
+
"hosts.group.noTags": "无标签",
|
|
332
|
+
"hosts.group.count": "{count} 台",
|
|
333
|
+
"hosts.group.testAll": "测试全部",
|
|
326
334
|
"hosts.col.alias": "别名",
|
|
327
335
|
"hosts.col.host": "主机",
|
|
328
336
|
"hosts.col.user": "用户",
|
|
@@ -452,6 +460,14 @@ window.__ModuleLoader__.load({
|
|
|
452
460
|
"hosts.add": "Add host",
|
|
453
461
|
"hosts.import": "Import ~/.ssh/config",
|
|
454
462
|
"hosts.search": "Search alias / description / tags…",
|
|
463
|
+
"hosts.groupBy.label": "Group by",
|
|
464
|
+
"hosts.groupBy.none": "No grouping",
|
|
465
|
+
"hosts.groupBy.environment": "Group by environment",
|
|
466
|
+
"hosts.groupBy.tags": "Group by tags",
|
|
467
|
+
"hosts.group.ungrouped": "No environment",
|
|
468
|
+
"hosts.group.noTags": "No tags",
|
|
469
|
+
"hosts.group.count": "{count} hosts",
|
|
470
|
+
"hosts.group.testAll": "Test all",
|
|
455
471
|
"hosts.col.alias": "Alias",
|
|
456
472
|
"hosts.col.host": "Host",
|
|
457
473
|
"hosts.col.user": "User",
|
|
@@ -595,7 +611,7 @@ window.__ModuleLoader__.load({
|
|
|
595
611
|
}
|
|
596
612
|
//#endregion
|
|
597
613
|
//#region \0dsh-css:packages/dsh-ssh/src/client/panel/panel.module.css.mjs
|
|
598
|
-
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{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][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_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-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_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{padding:8px 10px;position:absolute;inset:0}.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}";
|
|
614
|
+
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{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}.mL8Uca_entry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}.mL8Uca_entry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}.mL8Uca_entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.mL8Uca_entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .mL8Uca_entry{justify-content:center;width:100%;padding:0}[data-dsh-frame][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-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_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{padding:8px 10px;position:absolute;inset:0}.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}";
|
|
599
615
|
const tagId = "@linxin666/dsh-ssh/panel.module.css";
|
|
600
616
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
601
617
|
const tag = document.createElement("style");
|
|
@@ -636,6 +652,13 @@ window.__ModuleLoader__.load({
|
|
|
636
652
|
"formError": "mL8Uca_formError",
|
|
637
653
|
"formRow": "mL8Uca_formRow",
|
|
638
654
|
"ghostButton": "mL8Uca_ghostButton",
|
|
655
|
+
"groupBySelect": "mL8Uca_groupBySelect",
|
|
656
|
+
"groupChevron": "mL8Uca_groupChevron",
|
|
657
|
+
"groupCount": "mL8Uca_groupCount",
|
|
658
|
+
"groupHeader": "mL8Uca_groupHeader",
|
|
659
|
+
"groupName": "mL8Uca_groupName",
|
|
660
|
+
"groupSection": "mL8Uca_groupSection",
|
|
661
|
+
"groupToggle": "mL8Uca_groupToggle",
|
|
639
662
|
"hiddenFile": "mL8Uca_hiddenFile",
|
|
640
663
|
"hint": "mL8Uca_hint",
|
|
641
664
|
"iconButton": "mL8Uca_iconButton",
|
|
@@ -1238,6 +1261,31 @@ window.__ModuleLoader__.load({
|
|
|
1238
1261
|
* add/edit/delete/test actions, ~/.ssh/config import, and a connect action
|
|
1239
1262
|
* that hands the alias to the terminal tab via onConnect.
|
|
1240
1263
|
*/
|
|
1264
|
+
/**
|
|
1265
|
+
* Bucket hosts into collapsible groups (#379). Grouping by tags places a
|
|
1266
|
+
* multi-tag host in every one of its tag groups (folder view); hosts without
|
|
1267
|
+
* the grouping key land in the '' bucket, which always sorts last. Groups
|
|
1268
|
+
* sort alphabetically; host order inside a group follows the API listing.
|
|
1269
|
+
*/
|
|
1270
|
+
function groupHosts(hosts, groupBy) {
|
|
1271
|
+
if (groupBy === "none") return [{
|
|
1272
|
+
key: "",
|
|
1273
|
+
hosts
|
|
1274
|
+
}];
|
|
1275
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1276
|
+
const push = (key, host) => {
|
|
1277
|
+
const bucket = buckets.get(key);
|
|
1278
|
+
if (bucket === void 0) buckets.set(key, [host]);
|
|
1279
|
+
else bucket.push(host);
|
|
1280
|
+
};
|
|
1281
|
+
for (const host of hosts) if (groupBy === "environment") push(host.environment ?? "", host);
|
|
1282
|
+
else if (host.tags.length === 0) push("", host);
|
|
1283
|
+
else for (const tag of host.tags) push(tag, host);
|
|
1284
|
+
return [...buckets.entries()].sort(([a], [b]) => a === "" ? 1 : b === "" ? -1 : a.localeCompare(b)).map(([key, group]) => ({
|
|
1285
|
+
key,
|
|
1286
|
+
hosts: group
|
|
1287
|
+
}));
|
|
1288
|
+
}
|
|
1241
1289
|
/** The hosts table plus its toolbar and dialogs. */
|
|
1242
1290
|
function HostsTab({ api, onConnect }) {
|
|
1243
1291
|
const [hosts, setHosts] = (0, react.useState)(null);
|
|
@@ -1248,6 +1296,9 @@ window.__ModuleLoader__.load({
|
|
|
1248
1296
|
const [importing, setImporting] = (0, react.useState)(false);
|
|
1249
1297
|
const [notice, setNotice] = (0, react.useState)(null);
|
|
1250
1298
|
const [dialog, setDialog] = (0, react.useState)(null);
|
|
1299
|
+
const [groupBy, setGroupBy] = (0, react.useState)("none");
|
|
1300
|
+
const [collapsed, setCollapsed] = (0, react.useState)({});
|
|
1301
|
+
const [testingGroup, setTestingGroup] = (0, react.useState)(null);
|
|
1251
1302
|
const seqRef = (0, react.useRef)(0);
|
|
1252
1303
|
const load = (0, react.useCallback)(async (query) => {
|
|
1253
1304
|
const seq = ++seqRef.current;
|
|
@@ -1302,6 +1353,14 @@ window.__ModuleLoader__.load({
|
|
|
1302
1353
|
setError(errorMessage(cause));
|
|
1303
1354
|
}
|
|
1304
1355
|
};
|
|
1356
|
+
const testGroup = async (group) => {
|
|
1357
|
+
setTestingGroup(group.key);
|
|
1358
|
+
try {
|
|
1359
|
+
await Promise.all(group.hosts.map((host) => runTest(host.alias)));
|
|
1360
|
+
} finally {
|
|
1361
|
+
setTestingGroup(null);
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1305
1364
|
const importConfig = async () => {
|
|
1306
1365
|
setImporting(true);
|
|
1307
1366
|
try {
|
|
@@ -1318,6 +1377,106 @@ window.__ModuleLoader__.load({
|
|
|
1318
1377
|
setImporting(false);
|
|
1319
1378
|
}
|
|
1320
1379
|
};
|
|
1380
|
+
const renderHostRow = (host) => {
|
|
1381
|
+
const test = testResults[host.alias];
|
|
1382
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1383
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1384
|
+
className: panel_module_css_default.mono,
|
|
1385
|
+
children: host.alias
|
|
1386
|
+
}),
|
|
1387
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", {
|
|
1388
|
+
className: panel_module_css_default.mono,
|
|
1389
|
+
children: [
|
|
1390
|
+
host.host,
|
|
1391
|
+
":",
|
|
1392
|
+
host.port
|
|
1393
|
+
]
|
|
1394
|
+
}),
|
|
1395
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: host.user }),
|
|
1396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1397
|
+
className: panel_module_css_default.badge,
|
|
1398
|
+
"data-kind": host.auth,
|
|
1399
|
+
children: host.auth === "key" ? tt("form.auth.key") : tt("form.auth.password")
|
|
1400
|
+
}) }),
|
|
1401
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1402
|
+
className: panel_module_css_default.cellMuted,
|
|
1403
|
+
children: host.environment ?? ""
|
|
1404
|
+
}),
|
|
1405
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1406
|
+
className: panel_module_css_default.cellMuted,
|
|
1407
|
+
children: host.tags.join(", ")
|
|
1408
|
+
}),
|
|
1409
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1410
|
+
className: panel_module_css_default.cellMuted,
|
|
1411
|
+
children: host.description ?? ""
|
|
1412
|
+
}),
|
|
1413
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1414
|
+
className: panel_module_css_default.actions,
|
|
1415
|
+
children: [
|
|
1416
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1417
|
+
type: "button",
|
|
1418
|
+
className: panel_module_css_default.linkButton,
|
|
1419
|
+
disabled: testingAlias === host.alias,
|
|
1420
|
+
onClick: () => {
|
|
1421
|
+
runTest(host.alias);
|
|
1422
|
+
},
|
|
1423
|
+
children: testingAlias === host.alias ? tt("hosts.testing") : tt("hosts.test")
|
|
1424
|
+
}),
|
|
1425
|
+
testingAlias === host.alias && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1426
|
+
className: panel_module_css_default.spinner,
|
|
1427
|
+
"aria-hidden": "true"
|
|
1428
|
+
}),
|
|
1429
|
+
test !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1430
|
+
className: panel_module_css_default.inlineTest,
|
|
1431
|
+
"data-status": test.ok ? "ok" : "fail",
|
|
1432
|
+
children: test.ok ? tt("hosts.testOk", { latency: test.latencyMs ?? 0 }) : tt("hosts.testFail", { error: test.error ?? "" })
|
|
1433
|
+
}),
|
|
1434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1435
|
+
type: "button",
|
|
1436
|
+
className: panel_module_css_default.linkButton,
|
|
1437
|
+
onClick: () => {
|
|
1438
|
+
setDialog({
|
|
1439
|
+
mode: "edit",
|
|
1440
|
+
host
|
|
1441
|
+
});
|
|
1442
|
+
},
|
|
1443
|
+
children: tt("hosts.edit")
|
|
1444
|
+
}),
|
|
1445
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1446
|
+
type: "button",
|
|
1447
|
+
className: panel_module_css_default.linkButton,
|
|
1448
|
+
"data-danger": true,
|
|
1449
|
+
onClick: () => {
|
|
1450
|
+
deleteHost(host.alias);
|
|
1451
|
+
},
|
|
1452
|
+
children: tt("hosts.delete")
|
|
1453
|
+
}),
|
|
1454
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1455
|
+
type: "button",
|
|
1456
|
+
className: panel_module_css_default.ghostButton,
|
|
1457
|
+
onClick: () => {
|
|
1458
|
+
onConnect(host.alias);
|
|
1459
|
+
},
|
|
1460
|
+
children: tt("hosts.connected")
|
|
1461
|
+
})
|
|
1462
|
+
]
|
|
1463
|
+
}) })
|
|
1464
|
+
] }, host.alias);
|
|
1465
|
+
};
|
|
1466
|
+
const renderHostTable = (rows) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1467
|
+
className: panel_module_css_default.table,
|
|
1468
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1469
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.alias") }),
|
|
1470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.host") }),
|
|
1471
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.user") }),
|
|
1472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.auth") }),
|
|
1473
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.environment") }),
|
|
1474
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.tags") }),
|
|
1475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.description") }),
|
|
1476
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: tt("hosts.col.actions") })
|
|
1477
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: rows.map(renderHostRow) })]
|
|
1478
|
+
});
|
|
1479
|
+
const groups = hosts === null ? [] : groupHosts(hosts, groupBy);
|
|
1321
1480
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1322
1481
|
className: panel_module_css_default.fillBody,
|
|
1323
1482
|
children: [
|
|
@@ -1333,6 +1492,28 @@ window.__ModuleLoader__.load({
|
|
|
1333
1492
|
setSearch(event.target.value);
|
|
1334
1493
|
}
|
|
1335
1494
|
}),
|
|
1495
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1496
|
+
className: panel_module_css_default.groupBySelect,
|
|
1497
|
+
"aria-label": tt("hosts.groupBy.label"),
|
|
1498
|
+
value: groupBy,
|
|
1499
|
+
onChange: (event) => {
|
|
1500
|
+
setGroupBy(event.target.value);
|
|
1501
|
+
},
|
|
1502
|
+
children: [
|
|
1503
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1504
|
+
value: "none",
|
|
1505
|
+
children: tt("hosts.groupBy.none")
|
|
1506
|
+
}),
|
|
1507
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1508
|
+
value: "environment",
|
|
1509
|
+
children: tt("hosts.groupBy.environment")
|
|
1510
|
+
}),
|
|
1511
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1512
|
+
value: "tags",
|
|
1513
|
+
children: tt("hosts.groupBy.tags")
|
|
1514
|
+
})
|
|
1515
|
+
]
|
|
1516
|
+
}),
|
|
1336
1517
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: panel_module_css_default.toolbarSpacer }),
|
|
1337
1518
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1338
1519
|
type: "button",
|
|
@@ -1371,105 +1552,55 @@ window.__ModuleLoader__.load({
|
|
|
1371
1552
|
className: panel_module_css_default.empty,
|
|
1372
1553
|
children: tt("hosts.empty")
|
|
1373
1554
|
}),
|
|
1374
|
-
hosts !== null && hosts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1555
|
+
hosts !== null && hosts.length > 0 && groupBy === "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1375
1556
|
className: panel_module_css_default.tableWrap,
|
|
1376
|
-
children:
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
host.host,
|
|
1398
|
-
":",
|
|
1399
|
-
host.port
|
|
1400
|
-
]
|
|
1401
|
-
}),
|
|
1402
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: host.user }),
|
|
1403
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1404
|
-
className: panel_module_css_default.badge,
|
|
1405
|
-
"data-kind": host.auth,
|
|
1406
|
-
children: host.auth === "key" ? tt("form.auth.key") : tt("form.auth.password")
|
|
1407
|
-
}) }),
|
|
1408
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1409
|
-
className: panel_module_css_default.cellMuted,
|
|
1410
|
-
children: host.environment ?? ""
|
|
1411
|
-
}),
|
|
1412
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1413
|
-
className: panel_module_css_default.cellMuted,
|
|
1414
|
-
children: host.tags.join(", ")
|
|
1415
|
-
}),
|
|
1416
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
1417
|
-
className: panel_module_css_default.cellMuted,
|
|
1418
|
-
children: host.description ?? ""
|
|
1419
|
-
}),
|
|
1420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1421
|
-
className: panel_module_css_default.actions,
|
|
1557
|
+
children: renderHostTable(hosts)
|
|
1558
|
+
}),
|
|
1559
|
+
hosts !== null && hosts.length > 0 && groupBy !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1560
|
+
className: panel_module_css_default.tableWrap,
|
|
1561
|
+
children: groups.map((group) => {
|
|
1562
|
+
const isCollapsed = collapsed[group.key] === true;
|
|
1563
|
+
const label = group.key === "" ? groupBy === "tags" ? tt("hosts.group.noTags") : tt("hosts.group.ungrouped") : group.key;
|
|
1564
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1565
|
+
className: panel_module_css_default.groupSection,
|
|
1566
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1567
|
+
className: panel_module_css_default.groupHeader,
|
|
1568
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1569
|
+
type: "button",
|
|
1570
|
+
className: panel_module_css_default.groupToggle,
|
|
1571
|
+
"aria-expanded": !isCollapsed,
|
|
1572
|
+
onClick: () => {
|
|
1573
|
+
setCollapsed((prev) => ({
|
|
1574
|
+
...prev,
|
|
1575
|
+
[group.key]: !isCollapsed
|
|
1576
|
+
}));
|
|
1577
|
+
},
|
|
1422
1578
|
children: [
|
|
1423
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
disabled: testingAlias === host.alias,
|
|
1427
|
-
onClick: () => {
|
|
1428
|
-
runTest(host.alias);
|
|
1429
|
-
},
|
|
1430
|
-
children: testingAlias === host.alias ? tt("hosts.testing") : tt("hosts.test")
|
|
1431
|
-
}),
|
|
1432
|
-
testingAlias === host.alias && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1433
|
-
className: panel_module_css_default.spinner,
|
|
1579
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1580
|
+
className: panel_module_css_default.groupChevron,
|
|
1581
|
+
"data-collapsed": isCollapsed || void 0,
|
|
1434
1582
|
"aria-hidden": "true"
|
|
1435
1583
|
}),
|
|
1436
|
-
|
|
1437
|
-
className: panel_module_css_default.
|
|
1438
|
-
|
|
1439
|
-
children: test.ok ? tt("hosts.testOk", { latency: test.latencyMs ?? 0 }) : tt("hosts.testFail", { error: test.error ?? "" })
|
|
1584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1585
|
+
className: panel_module_css_default.groupName,
|
|
1586
|
+
children: label
|
|
1440
1587
|
}),
|
|
1441
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
onClick: () => {
|
|
1445
|
-
setDialog({
|
|
1446
|
-
mode: "edit",
|
|
1447
|
-
host
|
|
1448
|
-
});
|
|
1449
|
-
},
|
|
1450
|
-
children: tt("hosts.edit")
|
|
1451
|
-
}),
|
|
1452
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1453
|
-
type: "button",
|
|
1454
|
-
className: panel_module_css_default.linkButton,
|
|
1455
|
-
"data-danger": true,
|
|
1456
|
-
onClick: () => {
|
|
1457
|
-
deleteHost(host.alias);
|
|
1458
|
-
},
|
|
1459
|
-
children: tt("hosts.delete")
|
|
1460
|
-
}),
|
|
1461
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1462
|
-
type: "button",
|
|
1463
|
-
className: panel_module_css_default.ghostButton,
|
|
1464
|
-
onClick: () => {
|
|
1465
|
-
onConnect(host.alias);
|
|
1466
|
-
},
|
|
1467
|
-
children: tt("hosts.connected")
|
|
1588
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1589
|
+
className: panel_module_css_default.groupCount,
|
|
1590
|
+
children: tt("hosts.group.count", { count: group.hosts.length })
|
|
1468
1591
|
})
|
|
1469
1592
|
]
|
|
1470
|
-
})
|
|
1471
|
-
|
|
1472
|
-
|
|
1593
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1594
|
+
type: "button",
|
|
1595
|
+
className: panel_module_css_default.linkButton,
|
|
1596
|
+
disabled: testingGroup === group.key,
|
|
1597
|
+
onClick: () => {
|
|
1598
|
+
testGroup(group);
|
|
1599
|
+
},
|
|
1600
|
+
children: testingGroup === group.key ? tt("hosts.testing") : tt("hosts.group.testAll")
|
|
1601
|
+
})]
|
|
1602
|
+
}), !isCollapsed && renderHostTable(group.hosts)]
|
|
1603
|
+
}, group.key);
|
|
1473
1604
|
})
|
|
1474
1605
|
}),
|
|
1475
1606
|
dialog !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostFormDialog, {
|
|
@@ -14733,7 +14864,8 @@ window.__ModuleLoader__.load({
|
|
|
14733
14864
|
className: panel_module_css_default.termWrap,
|
|
14734
14865
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14735
14866
|
ref: containerRef,
|
|
14736
|
-
className: panel_module_css_default.termContainer
|
|
14867
|
+
className: panel_module_css_default.termContainer,
|
|
14868
|
+
"data-dsh-part": "terminal"
|
|
14737
14869
|
}), status.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14738
14870
|
className: panel_module_css_default.termPlaceholder,
|
|
14739
14871
|
children: hosts.length === 0 ? tt("hosts.empty") : tt("terminal.placeholder")
|
|
@@ -15403,6 +15535,7 @@ window.__ModuleLoader__.load({
|
|
|
15403
15535
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15404
15536
|
className: panel_module_css_default.tabBar,
|
|
15405
15537
|
role: "tablist",
|
|
15538
|
+
"data-dsh-part": "tab-bar",
|
|
15406
15539
|
children: TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15407
15540
|
type: "button",
|
|
15408
15541
|
role: "tab",
|
|
@@ -15483,6 +15616,7 @@ window.__ModuleLoader__.load({
|
|
|
15483
15616
|
if (column === void 0) return;
|
|
15484
15617
|
container = document.createElement("div");
|
|
15485
15618
|
container.dataset.dshSshView = "";
|
|
15619
|
+
container.dataset.dshPlugin = "ssh";
|
|
15486
15620
|
container.className = panel_module_css_default.view;
|
|
15487
15621
|
column.appendChild(container);
|
|
15488
15622
|
root = (0, react_dom_client.createRoot)(container);
|
|
@@ -15566,7 +15700,7 @@ window.__ModuleLoader__.load({
|
|
|
15566
15700
|
}
|
|
15567
15701
|
};
|
|
15568
15702
|
//#endregion
|
|
15569
|
-
//#region src/client/sidebar-entry.ts
|
|
15703
|
+
//#region src/client/sidebar-entry-core.ts
|
|
15570
15704
|
/** Find the sidebar shell root element, or undefined while not yet mounted. */
|
|
15571
15705
|
function sidebarRoot() {
|
|
15572
15706
|
const column = document.querySelector("[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]");
|
|
@@ -15580,28 +15714,30 @@ window.__ModuleLoader__.load({
|
|
|
15580
15714
|
for (const child of root.children) if (child.tagName === "BUTTON") return child;
|
|
15581
15715
|
}
|
|
15582
15716
|
/** Build the entry row (a detached button; insert once the shell is up). */
|
|
15583
|
-
function createEntry(
|
|
15717
|
+
function createEntry(options) {
|
|
15584
15718
|
const entry = document.createElement("button");
|
|
15585
15719
|
entry.type = "button";
|
|
15586
|
-
entry.
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
entry.
|
|
15592
|
-
|
|
15593
|
-
|
|
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);
|
|
15594
15730
|
return entry;
|
|
15595
15731
|
}
|
|
15596
15732
|
/** Re-insert the entry after the New Session row (before the browser region). */
|
|
15597
|
-
function placeEntry(root, entry) {
|
|
15733
|
+
function placeEntry(root, entry, options) {
|
|
15598
15734
|
const button = newSessionButton(root);
|
|
15599
15735
|
if (button === void 0) return false;
|
|
15600
15736
|
if (entry.parentElement !== root) {
|
|
15601
15737
|
const row = button.closest("[class*=\"logoRow\"]");
|
|
15602
15738
|
const base = row !== null && row.parentElement === root ? row : button;
|
|
15603
|
-
const family = Array.from(root.children).filter((el) => el instanceof HTMLElement && el.matches("
|
|
15604
|
-
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;
|
|
15605
15741
|
root.insertBefore(entry, anchor);
|
|
15606
15742
|
}
|
|
15607
15743
|
return true;
|
|
@@ -15609,11 +15745,12 @@ window.__ModuleLoader__.load({
|
|
|
15609
15745
|
/**
|
|
15610
15746
|
* Mount the sidebar entry, waiting for the shell to render and self-healing
|
|
15611
15747
|
* on later React re-renders.
|
|
15612
|
-
* @param
|
|
15748
|
+
* @param options - the row's attribute/icon/copy/action/ordering configuration.
|
|
15613
15749
|
* @returns disposer removing the entry and its observers.
|
|
15614
15750
|
*/
|
|
15615
|
-
function mountSidebarEntry(
|
|
15616
|
-
|
|
15751
|
+
function mountSidebarEntry$1(options) {
|
|
15752
|
+
if (typeof document !== "undefined" && document.querySelector(options.rowSelector) !== null) return () => {};
|
|
15753
|
+
const entry = createEntry(options);
|
|
15617
15754
|
let root;
|
|
15618
15755
|
let placed = false;
|
|
15619
15756
|
const tryPlace = () => {
|
|
@@ -15630,7 +15767,7 @@ window.__ModuleLoader__.load({
|
|
|
15630
15767
|
}
|
|
15631
15768
|
root ??= sidebarRoot();
|
|
15632
15769
|
if (root === void 0) return;
|
|
15633
|
-
placed = placeEntry(root, entry);
|
|
15770
|
+
placed = placeEntry(root, entry, options);
|
|
15634
15771
|
if (placed) rootObserver.observe(root, {
|
|
15635
15772
|
childList: true,
|
|
15636
15773
|
subtree: true
|
|
@@ -15649,23 +15786,58 @@ window.__ModuleLoader__.load({
|
|
|
15649
15786
|
tryPlace();
|
|
15650
15787
|
return;
|
|
15651
15788
|
}
|
|
15652
|
-
if (!root.contains(entry)) placed = placeEntry(root, entry);
|
|
15789
|
+
if (!root.contains(entry)) placed = placeEntry(root, entry, options);
|
|
15653
15790
|
});
|
|
15654
|
-
const
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
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
|
+
})();
|
|
15660
15800
|
tryPlace();
|
|
15661
15801
|
return () => {
|
|
15662
15802
|
waitObserver.disconnect();
|
|
15663
15803
|
rootObserver.disconnect();
|
|
15664
|
-
|
|
15804
|
+
unsubscribeActive?.();
|
|
15665
15805
|
entry.remove();
|
|
15666
15806
|
};
|
|
15667
15807
|
}
|
|
15668
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
|
|
15669
15841
|
//#region src/client/index.ts
|
|
15670
15842
|
/** Locale namespace this plugin owns. */
|
|
15671
15843
|
const NS = "dsh-ssh";
|