Merge pull request #8088 from SivanCola/feature/compact-ssh-statusbar

Compact idle SSH status / 精简空闲 SSH 状态栏
This commit is contained in:
SivanCola authored and GitHub committed 2026-08-10 01:49:11 +08:00
commit ea57e9bf35
3 files changed
+33 -10

No files matched your search

@@ -52,7 +52,9 @@ console.log("\nstatus bar workspace");
{ id: "demo", label: "demo", host: "192.0.2.10", port: 22, user: "dev", identityFile: "", proxyJump: "", defaultWorkspace: "~/app", serveInstall: "auto", useSSHConfig: false },
];
const stopped = renderStatusBar({ workspacePath: "/workspace/repo", workspaceName: "repo", remoteHosts });
ok(stopped.includes("SSH · Disconnected"), "configured SSH entry remains visible while disconnected");
ok(stopped.includes("SSH · Disconnected"), "disconnected SSH entry keeps its full accessible status");
ok(stopped.includes('statusbar__remote--idle'), "disconnected SSH entry uses the compact idle treatment");
ok(stopped.includes('<span class="statusbar__remote-label">SSH</span>'), "disconnected SSH entry renders only the compact SSH label");
ok(stopped.indexOf("SSH · Disconnected") < stopped.indexOf("workspace/repo"), "window-level SSH entry leads the status bar");
const connected = renderStatusBar({
@@ -62,6 +64,8 @@ console.log("\nstatus bar workspace");
remoteStatuses: { demo: { hostId: "demo", state: "connected" } },
});
ok(connected.includes("demo · Connected"), "SSH entry includes host and connected state text");
ok(connected.includes('statusbar__remote-state-dot'), "connected SSH entry renders a state dot");
ok(connected.includes('<span class="statusbar__remote-label">demo</span>'), "connected SSH entry renders the host without redundant state text");
const failed = renderStatusBar({
workspacePath: "/workspace/repo",
@@ -69,6 +73,7 @@ console.log("\nstatus bar workspace");
remoteStatuses: { demo: { hostId: "demo", state: "stopped", error: "handshake failed" } },
});
ok(failed.includes("demo · Connection failed"), "SSH entry keeps a recoverable failure summary visible");
ok(failed.includes('<span class="statusbar__remote-label">demo · Connection failed</span>'), "failed SSH entry keeps the failure visible in the status bar");
ok(!failed.includes("handshake failed"), "status entry keeps raw connection diagnostics out of primary chrome");
const degraded = renderStatusBar({
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState, type ReactNode } from "react";
import { Activity, ChevronsUpDown, CircleDollarSign, CircleGauge, Database, FileOutput, Folder, Gauge, GitBranch, HardDrive, Layers, Percent, Puzzle, RefreshCw, Server, Settings, Square, Unplug, Wallet, Zap } from "lucide-react";
import { Activity, CircleDollarSign, CircleGauge, Database, FileOutput, Folder, Gauge, GitBranch, HardDrive, Layers, Percent, Puzzle, RefreshCw, Server, Settings, Square, Unplug, Wallet, Zap } from "lucide-react";
import { AnchoredPopover } from "./AnchoredPopover";
import { RemoteConnectionErrorDialog } from "./RemoteConnectionErrorDialog";
import { Tooltip } from "./Tooltip";
@@ -673,25 +673,24 @@ function RemoteStatusBarChip({
const worstHost = hosts.find((host) => host.id === worst.hostId) ?? hosts[0];
const triggerState = isRemoteTerminalFailure(worst) ? "error" : worst.state;
const triggerStatus = isRemoteTerminalFailure(worst) ? t("remote.status.failed") : t(`remote.status.${worst.state}`);
const triggerLabel = worst.state === "stopped" && !worst.error
? t("remote.statusBar.disconnected")
: t("remote.statusBar.summary", { host: worstHost.label, status: triggerStatus });
const idleDisconnected = worst.state === "stopped" && !worst.error;
const triggerLabel = idleDisconnected ? t("remote.statusBar.disconnected") : t("remote.statusBar.summary", { host: worstHost.label, status: triggerStatus });
const triggerText = idleDisconnected ? "SSH" : triggerState === "connected" ? worstHost.label : triggerLabel;
return (
<span className="statusbar__remote-wrap">
<button
ref={triggerRef}
type="button"
className={`statusbar__remote remote-chip remote-chip--${triggerState}`}
className={`statusbar__remote remote-chip remote-chip--${triggerState}${idleDisconnected ? " statusbar__remote--idle" : ""}`}
onClick={() => setOpen((value) => !value)}
aria-label={triggerLabel}
aria-haspopup="dialog"
aria-expanded={open}
title={triggerLabel}
>
<Server size={11} aria-hidden="true" />
<span>{triggerLabel}</span>
<ChevronsUpDown size={10} aria-hidden="true" />
{triggerState === "connected" ? <span className="statusbar__remote-state-dot" aria-hidden="true" /> : <Server size={11} aria-hidden="true" />}
<span className="statusbar__remote-label">{triggerText}</span>
</button>
<AnchoredPopover
open={open}
+20 -1
View File
@@ -37349,6 +37349,7 @@ body > .mermaid-diagram--fullscreen {
display: inline-flex;
align-items: center;
max-width: 190px;
min-height: 24px;
gap: 4px;
padding: 1px 7px;
cursor: pointer;
@@ -37356,15 +37357,33 @@ body > .mermaid-diagram--fullscreen {
font: inherit;
white-space: nowrap;
}
.statusbar__remote > span {
.statusbar__remote-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.statusbar__remote-state-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: currentColor;
flex: 0 0 7px;
}
.statusbar__remote-wrap {
display: inline-flex;
min-width: 0;
}
@media (max-width: 820px) {
.statusbar__remote--idle {
width: 24px;
min-width: 24px;
justify-content: center;
padding-inline: 4px;
}
.statusbar__remote--idle .statusbar__remote-label {
display: none;
}
}
.remote-switcher {
z-index: var(--z-local-popover);
width: min(390px, calc(100vw - 16px));