fix docker availability flicker and add openEuler icon

This commit is contained in:
陈大猫
2026-06-13 12:28:07 +08:00
committed by GitHub
parent d57dd664a2
commit bb02f8e162
10 changed files with 85 additions and 11 deletions

View File

@@ -485,6 +485,7 @@ export const enVaultMessages: Messages = {
'hostDetails.distro.option.redhat': 'Red Hat / RHEL',
'hostDetails.distro.option.almalinux': 'AlmaLinux',
'hostDetails.distro.option.alinux': 'Alibaba Cloud Linux',
'hostDetails.distro.option.openeuler': 'openEuler',
'hostDetails.distro.option.oracle': 'Oracle Linux',
'hostDetails.distro.option.kali': 'Kali Linux',
'hostDetails.distro.option.cisco': 'Cisco',

View File

@@ -520,6 +520,7 @@ export const ruVaultMessages: Messages = {
'hostDetails.distro.option.redhat': 'Red Hat / RHEL',
'hostDetails.distro.option.almalinux': 'AlmaLinux',
'hostDetails.distro.option.alinux': 'Alibaba Cloud Linux',
'hostDetails.distro.option.openeuler': 'openEuler',
'hostDetails.distro.option.oracle': 'Oracle Linux',
'hostDetails.distro.option.kali': 'Kali Linux',
'hostDetails.distro.option.cisco': 'Cisco',

View File

@@ -66,6 +66,7 @@ export const zhCNVaultMessages: Messages = {
'hostDetails.distro.option.redhat': 'Red Hat / RHEL',
'hostDetails.distro.option.almalinux': 'AlmaLinux',
'hostDetails.distro.option.alinux': '阿里云 Linux',
'hostDetails.distro.option.openeuler': 'openEuler',
'hostDetails.distro.option.oracle': 'Oracle Linux',
'hostDetails.distro.option.kali': 'Kali Linux',
'hostDetails.distro.option.cisco': '思科',

View File

@@ -19,6 +19,7 @@ export const DISTRO_LOGOS: Record<string, string> = {
kali: "/distro/kali.svg",
almalinux: "/distro/almalinux.svg",
alinux: "/distro/alinux.svg",
openeuler: "/distro/openeuler.svg",
// OS-level logos (used by local terminal tab icons)
macos: "/distro/macos.svg",
windows: "/distro/windows.svg",
@@ -50,6 +51,7 @@ export const DISTRO_COLORS: Record<string, string> = {
kali: "bg-[#0F6DB3]",
almalinux: "bg-[#173B66]",
alinux: "bg-[#FF6A00]",
openeuler: "bg-[#002FA7]",
// OS-level colors
macos: "bg-[#333333]",
windows: "bg-[#0078D4]",

View File

@@ -5,6 +5,7 @@ import { useSystemManagerBackend } from '../../application/state/useSystemManage
import type { TerminalSettings } from '../../domain/models';
import type { Host } from '../../domain/models/connection';
import type { SystemManagerSubTab } from '../../domain/systemManager/types';
import { resolveCapabilityPanelState } from '../../domain/systemManagerPanelState';
import { buildSystemManagerTabs } from '../../domain/systemManager/systemTarget';
import type { Snippet, TerminalSession } from '../../types';
import { cn } from '../../lib/utils';
@@ -52,7 +53,7 @@ export const SystemManagerSidePanel = memo(function SystemManagerSidePanel({
const capabilitiesTtlMs = terminalSettings.systemManagerProcessRefreshInterval * 1000;
const { capabilities, probing, refreshCapabilities } = useSessionCapabilities(sessionId, isConnected, backend, isVisible, capabilitiesTtlMs);
const { capabilities, refreshCapabilities } = useSessionCapabilities(sessionId, isConnected, backend, isVisible, capabilitiesTtlMs);
const availableTabs = useMemo(
() => buildSystemManagerTabs(sessionHost, capabilities, session),
@@ -158,10 +159,16 @@ export const SystemManagerSidePanel = memo(function SystemManagerSidePanel({
const tmuxReady = capabilities?.hasTmux === true;
const dockerReady = capabilities?.hasDocker === true;
const tmuxUnavailable = !probing && capabilities !== undefined && !tmuxReady;
const dockerUnavailable = !probing && capabilities !== undefined && !dockerReady;
const tmuxChecking = resolvedTab === 'tmux' && !tmuxReady && !tmuxUnavailable;
const dockerChecking = resolvedTab === 'docker' && !dockerReady && !dockerUnavailable;
const tmuxPanelState = resolveCapabilityPanelState({
isActive: resolvedTab === 'tmux',
ready: tmuxReady,
capabilitiesKnown: capabilities !== undefined,
});
const dockerPanelState = resolveCapabilityPanelState({
isActive: resolvedTab === 'docker',
ready: dockerReady,
capabilitiesKnown: capabilities !== undefined,
});
return (
<SystemPanelShell section="system-manager-panel">
@@ -194,15 +201,15 @@ export const SystemManagerSidePanel = memo(function SystemManagerSidePanel({
refreshIntervalSec={terminalSettings.systemManagerProcessRefreshInterval}
/>
</div>
{tmuxUnavailable && resolvedTab === 'tmux' ? (
{tmuxPanelState === 'unavailable' ? (
<div className="flex-1 min-h-0">
<SystemPanelEmpty icon={TerminalSquare} message={t('systemManager.tmux.unavailable')} />
</div>
) : tmuxChecking ? (
) : tmuxPanelState === 'checking' ? (
<div className="flex-1 min-h-0">
<SystemPanelChecking message={t('systemManager.common.checkingAvailability')} />
</div>
) : tmuxReady ? (
) : tmuxPanelState === 'ready' ? (
<div className={cn('flex-1 min-h-0 flex flex-col', resolvedTab !== 'tmux' && 'hidden')}>
<TmuxManagerTab
sessionId={sessionId}
@@ -215,15 +222,15 @@ export const SystemManagerSidePanel = memo(function SystemManagerSidePanel({
/>
</div>
) : null}
{dockerUnavailable && resolvedTab === 'docker' ? (
{dockerPanelState === 'unavailable' ? (
<div className="flex-1 min-h-0">
<SystemPanelEmpty icon={Box} message={t('systemManager.docker.unavailable')} />
</div>
) : dockerChecking ? (
) : dockerPanelState === 'checking' ? (
<div className="flex-1 min-h-0">
<SystemPanelChecking message={t('systemManager.common.checkingAvailability')} />
</div>
) : dockerReady ? (
) : dockerPanelState === 'ready' ? (
<div className={cn('flex-1 min-h-0 flex flex-col', resolvedTab !== 'docker' && 'hidden')}>
<DockerManagerTab
sessionId={sessionId}

View File

@@ -243,6 +243,12 @@ test("normalizeDistroId matches Alibaba Cloud Linux PRETTY_NAME/NAME fallback",
);
});
test("normalizeDistroId maps openEuler before the generic Linux fallback", () => {
assert.equal(normalizeDistroId("openeuler"), "openeuler");
assert.equal(normalizeDistroId("openEuler"), "openeuler");
assert.notEqual(normalizeDistroId("openeuler"), "linux");
});
test("shouldProbeSessionCwd allows the probe on a plain Linux host", () => {
assert.equal(
shouldProbeSessionCwd({ isNetworkDevice: false, remoteSshVersion: "OpenSSH_9.6" }),

View File

@@ -47,6 +47,7 @@ export const LINUX_DISTRO_OPTIONS = [
'oracle',
'kali',
'alinux',
'openeuler',
] as const;
/**
@@ -86,6 +87,7 @@ export const normalizeDistroId = (value?: string) => {
if (v.includes('almalinux')) return 'almalinux';
if (v.includes('oracle')) return 'oracle';
if (v.includes('kali')) return 'kali';
if (v.includes('openeuler') || v.includes('open euler')) return 'openeuler';
// Alibaba Cloud Linux: os-release ID is `alinux` (older branding: Aliyun
// Linux / `aliyun`). Must come before the generic `linux` fallback because
// 'alinux'.includes('linux') is true and would otherwise resolve to 'linux'.

View File

@@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import test from "node:test";
import { resolveCapabilityPanelState } from "./systemManagerPanelState.ts";
test("keeps unavailable state visible while a known-missing capability is refreshed", () => {
assert.equal(
resolveCapabilityPanelState({
isActive: true,
ready: false,
capabilitiesKnown: true,
}),
"unavailable",
);
});
test("shows checking only before capabilities are known", () => {
assert.equal(
resolveCapabilityPanelState({
isActive: true,
ready: false,
capabilitiesKnown: false,
}),
"checking",
);
});
test("hides inactive capability panels", () => {
assert.equal(
resolveCapabilityPanelState({
isActive: false,
ready: false,
capabilitiesKnown: true,
}),
"hidden",
);
});

View File

@@ -0,0 +1,16 @@
export type CapabilityPanelState = "hidden" | "checking" | "unavailable" | "ready";
export function resolveCapabilityPanelState({
isActive,
ready,
capabilitiesKnown,
}: {
isActive: boolean;
ready: boolean;
capabilitiesKnown: boolean;
}): CapabilityPanelState {
if (!isActive) return "hidden";
if (ready) return "ready";
if (capabilitiesKnown) return "unavailable";
return "checking";
}

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="80 45 93 105" xmlns="http://www.w3.org/2000/svg"><title>openEuler</title><path d="m170.54,69.83l-42.27-24.4c-1.13-.65-2.53-.65-3.66.02l-42.28,24.4c-1.12.65-1.82,1.85-1.83,3.15v48.82c0,1.31.69,2.52,1.83,3.18l42.28,24.41c1.13.65,2.53.65,3.66,0l42.27-24.41c1.14-.65,1.83-1.86,1.83-3.17v-48.83c0-1.31-.7-2.52-1.83-3.17Zm-33.72,49.99c-3.3,4.2-9.89,5.42-14.3,2.66-4.19-2.59-4.52-7.61-1.19-11.18,3.39-3.46,8.57-4.45,13-2.49.65.28,1.26.67,1.8,1.13,2.92,2.54,3.23,6.96.69,9.88Zm7.89-39.11c-2.14,2.21-6.79,3.15-10.14,2.09-1.92-.6-2.94-1.87-3-2.87-.04-.8-1.24-1.53-2.57-1.93-1.45-.34-2.94-.39-4.41-.15-.59.09-1.18.22-1.75.4-1.33.39-2.58,1.02-3.68,1.86-1.02.95-1.62,2.25-1.67,3.64.05.98.95,1.82,2.33,2.34,1.52.51,3.14.62,4.71.34,2.01-.58,4.14-.55,6.13.07,3.36,1.19,4.22,4.22,1.67,6.82-3,2.77-7.31,3.64-11.15,2.23-1.54-.61-2.58-2.07-2.65-3.73-.08-1.09-1.05-1.96-2.38-2.51-1.5-.49-3.09-.62-4.65-.38-.23.02-1.27.23-1.91.4-1.57.35-3.02,1.12-4.19,2.22-.85.87-1.53,1.88-2,3-.2.5-.33,1.02-.4,1.55.02,1.31.8,2.48,2,3,1.55.73,3.28.95,4.96.65,2.14-.66,4.45-.53,6.51.35,3.39,1.65,3.79,5.59.6,8.84-3.38,3.4-9.02,4.5-12.38,2.4-1.68-1.02-2.53-2.98-2.12-4.9,0-.25-.03-.5-.07-.74-.05-.32-.15-.62-.3-.91-.38-.72-.97-1.32-1.68-1.71-1.5-.76-3.2-1-4.85-.68-1.95.67-4.08.53-5.93-.38-2.48-1.55-1.73-4.83,1.45-7.31,1.62-1.25,3.52-2.09,5.53-2.45,1.86-.37,3.61-1.17,5.1-2.34,1.19-.79,2-2.05,2.23-3.46.01-.39-.01-.78-.08-1.17-.14-1.02.16-2.06.83-2.84.82-.8,1.87-1.33,3-1.5.88-.12,1.76-.17,2.64-.16.63-.03,1.25-.12,1.86-.25,1.1-.31,2.12-.85,3-1.58.83-.55,1.43-1.39,1.69-2.36.04-.33.06-.67.06-1-.32-1.75,2.13-3.68,5.47-4.3,1.49-.32,3.04-.26,4.51.15h.12c.98.15,1.83.75,2.3,1.63v.06c.05.13.09.26.1.4.5.96,1.43,1.63,2.5,1.81,1.47.36,3,.41,4.49.13,1.89-.51,3.87-.55,5.77-.11,3.3.85,4.54,3.13,2.4,5.34Zm18.23,5.26c-2.02,2.56-7.23,3.6-11.37,2.28-3.94-1.26-5.14-4.28-3.08-6.43,2.06-2.15,6.67-3.15,10.51-2.15,4,1.04,5.96,3.74,3.94,6.3Z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB