mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 18:31:33 +00:00
fix: unify connection toolbar visibility (#1357)
This commit is contained in:
+1
-1
@@ -111,7 +111,7 @@ export interface HostFeatureFlags {
|
|||||||
enableFileManager: boolean; // SSH only
|
enableFileManager: boolean; // SSH only
|
||||||
enableDocker: boolean; // SSH only
|
enableDocker: boolean; // SSH only
|
||||||
enableTmuxMonitor: boolean; // SSH only
|
enableTmuxMonitor: boolean; // SSH only
|
||||||
enableTerminalToolbar: boolean; // SSH only
|
enableTerminalToolbar: boolean; // SSH, RDP, VNC, and Telnet
|
||||||
enableRemoteDesktop: boolean; // RDP, VNC only
|
enableRemoteDesktop: boolean; // RDP, VNC only
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ interface GuacamoleAppInnerProps {
|
|||||||
| "rdpPassword"
|
| "rdpPassword"
|
||||||
| "vncUser"
|
| "vncUser"
|
||||||
| "vncPassword"
|
| "vncPassword"
|
||||||
>;
|
> & { enableTerminalToolbar?: boolean };
|
||||||
hostName: string;
|
hostName: string;
|
||||||
tabId?: string;
|
tabId?: string;
|
||||||
protocol?: "rdp" | "vnc" | "telnet";
|
protocol?: "rdp" | "vnc" | "telnet";
|
||||||
@@ -208,6 +208,7 @@ const GuacamoleAppInner = React.forwardRef<
|
|||||||
const [displayZoom, setDisplayZoom] = useState(1);
|
const [displayZoom, setDisplayZoom] = useState(1);
|
||||||
const [filesystem, setFilesystem] = useState<Guacamole.Object | null>(null);
|
const [filesystem, setFilesystem] = useState<Guacamole.Object | null>(null);
|
||||||
const [fileBrowserOpen, setFileBrowserOpen] = useState(false);
|
const [fileBrowserOpen, setFileBrowserOpen] = useState(false);
|
||||||
|
const [toolbarHidden, setToolbarHidden] = useState(false);
|
||||||
const [pendingUploads, setPendingUploads] = useState<File[]>([]);
|
const [pendingUploads, setPendingUploads] = useState<File[]>([]);
|
||||||
|
|
||||||
const guacConfig = parseGuacamoleConfig(hostConfig.guacamoleConfig);
|
const guacConfig = parseGuacamoleConfig(hostConfig.guacamoleConfig);
|
||||||
@@ -586,16 +587,19 @@ const GuacamoleAppInner = React.forwardRef<
|
|||||||
onClose={() => setFileBrowserOpen(false)}
|
onClose={() => setFileBrowserOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<GuacamoleToolbar
|
{hostConfig.enableTerminalToolbar !== false && !toolbarHidden && (
|
||||||
displayRef={displayRef}
|
<GuacamoleToolbar
|
||||||
protocol={resolvedProtocol}
|
displayRef={displayRef}
|
||||||
touchMode={touchMode}
|
protocol={resolvedProtocol}
|
||||||
hasFilesystem={filesystem !== null}
|
touchMode={touchMode}
|
||||||
fileBrowserOpen={fileBrowserOpen}
|
hasFilesystem={filesystem !== null}
|
||||||
onToggleFileBrowser={() => setFileBrowserOpen((open) => !open)}
|
fileBrowserOpen={fileBrowserOpen}
|
||||||
onTouchModeChange={setTouchMode}
|
onToggleFileBrowser={() => setFileBrowserOpen((open) => !open)}
|
||||||
zoom={displayZoom}
|
onTouchModeChange={setTouchMode}
|
||||||
/>
|
zoom={displayZoom}
|
||||||
|
onHide={() => setToolbarHidden(true)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{shareModalOpen && guacamoleConnectionId && (
|
{shareModalOpen && guacamoleConnectionId && (
|
||||||
<ShareSessionModal
|
<ShareSessionModal
|
||||||
open={shareModalOpen}
|
open={shareModalOpen}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
Scan,
|
Scan,
|
||||||
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -42,6 +43,7 @@ interface GuacamoleToolbarProps {
|
|||||||
onToggleFileBrowser?: () => void;
|
onToggleFileBrowser?: () => void;
|
||||||
onTouchModeChange?: (mode: GuacamoleTouchMode) => void;
|
onTouchModeChange?: (mode: GuacamoleTouchMode) => void;
|
||||||
zoom?: number;
|
zoom?: number;
|
||||||
|
onHide?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MODIFIER_KEYSYMS = {
|
const MODIFIER_KEYSYMS = {
|
||||||
@@ -130,6 +132,7 @@ export const GuacamoleToolbar: React.FC<GuacamoleToolbarProps> = ({
|
|||||||
onToggleFileBrowser,
|
onToggleFileBrowser,
|
||||||
onTouchModeChange,
|
onTouchModeChange,
|
||||||
zoom = 1,
|
zoom = 1,
|
||||||
|
onHide,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [position, setPosition] = useState({ x: 0, y: 12 });
|
const [position, setPosition] = useState({ x: 0, y: 12 });
|
||||||
@@ -569,6 +572,14 @@ export const GuacamoleToolbar: React.FC<GuacamoleToolbarProps> = ({
|
|||||||
{t("guacamole.toolbar.collapse")}
|
{t("guacamole.toolbar.collapse")}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{onHide && (
|
||||||
|
<TipIconBtn
|
||||||
|
tooltip={t("guacamole.toolbar.hide")}
|
||||||
|
onClick={onHide}
|
||||||
|
>
|
||||||
|
<X className="size-3.5" />
|
||||||
|
</TipIconBtn>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1142,6 +1142,9 @@
|
|||||||
"enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar",
|
"enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar",
|
||||||
"enableTerminalToolbar": "Enable Terminal Toolbar",
|
"enableTerminalToolbar": "Enable Terminal Toolbar",
|
||||||
"enableTerminalToolbarDesc": "Show a quick-access toolbar docked to the active terminal for this host's tools and live stats",
|
"enableTerminalToolbarDesc": "Show a quick-access toolbar docked to the active terminal for this host's tools and live stats",
|
||||||
|
"connectionToolbar": "Connection Toolbar",
|
||||||
|
"showConnectionToolbar": "Show connection toolbar",
|
||||||
|
"showConnectionToolbarDesc": "Show the toolbar in this host's SSH, RDP, VNC, and Telnet sessions. You can also hide it temporarily from the toolbar itself.",
|
||||||
"tabProxmox": "Proxmox",
|
"tabProxmox": "Proxmox",
|
||||||
"proxmoxIntegration": "Proxmox Integration",
|
"proxmoxIntegration": "Proxmox Integration",
|
||||||
"enableProxmox": "Enable Proxmox",
|
"enableProxmox": "Enable Proxmox",
|
||||||
@@ -2043,6 +2046,7 @@
|
|||||||
"collapse": "Collapse toolbar",
|
"collapse": "Collapse toolbar",
|
||||||
"expand": "Expand toolbar",
|
"expand": "Expand toolbar",
|
||||||
"dragHandle": "Drag to reposition",
|
"dragHandle": "Drag to reposition",
|
||||||
|
"hide": "Hide for this session. To disable it permanently, open Host Settings → General → Connection Toolbar.",
|
||||||
"zoomIn": "Zoom in",
|
"zoomIn": "Zoom in",
|
||||||
"zoomOut": "Zoom out",
|
"zoomOut": "Zoom out",
|
||||||
"resetZoom": "Reset zoom",
|
"resetZoom": "Reset zoom",
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ function hostToSSHHost(h: Host): SSHHost {
|
|||||||
enableTunnel: h.enableTunnel ?? false,
|
enableTunnel: h.enableTunnel ?? false,
|
||||||
enableFileManager: h.enableFileManager ?? false,
|
enableFileManager: h.enableFileManager ?? false,
|
||||||
enableDocker: h.enableDocker ?? false,
|
enableDocker: h.enableDocker ?? false,
|
||||||
|
enableTerminalToolbar: h.enableTerminalToolbar ?? true,
|
||||||
dockerConfig: h.dockerConfig ?? null,
|
dockerConfig: h.dockerConfig ?? null,
|
||||||
showTerminalInSidebar: true,
|
showTerminalInSidebar: true,
|
||||||
showFileManagerInSidebar: true,
|
showFileManagerInSidebar: true,
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
Globe,
|
Globe,
|
||||||
Info,
|
Info,
|
||||||
Layers, // --- tmux-monitor ---
|
Layers, // --- tmux-monitor ---
|
||||||
LayoutGrid,
|
|
||||||
Network,
|
Network,
|
||||||
Palette,
|
Palette,
|
||||||
Pencil,
|
Pencil,
|
||||||
@@ -2193,22 +2192,6 @@ export function HostEditor({
|
|||||||
</SettingRow>
|
</SettingRow>
|
||||||
</div>
|
</div>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
<SectionCard
|
|
||||||
title={t("terminalToolbar.title")}
|
|
||||||
icon={<LayoutGrid className="size-3.5" />}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col gap-4 py-3">
|
|
||||||
<SettingRow
|
|
||||||
label={t("hosts.enableTerminalToolbar")}
|
|
||||||
description={t("hosts.enableTerminalToolbarDesc")}
|
|
||||||
>
|
|
||||||
<FakeSwitch
|
|
||||||
checked={form.enableTerminalToolbar}
|
|
||||||
onChange={(v) => setField("enableTerminalToolbar", v)}
|
|
||||||
/>
|
|
||||||
</SettingRow>
|
|
||||||
</div>
|
|
||||||
</SectionCard>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { FakeSwitch, SectionCard, SettingRow } from "@/components/section-card";
|
|||||||
import type { Host } from "@/types/ui-types";
|
import type { Host } from "@/types/ui-types";
|
||||||
import {
|
import {
|
||||||
Globe,
|
Globe,
|
||||||
|
LayoutGrid,
|
||||||
Monitor,
|
Monitor,
|
||||||
MousePointerClick,
|
MousePointerClick,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -172,6 +173,23 @@ export function HostEditorGeneralTab({
|
|||||||
</div>
|
</div>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
|
|
||||||
|
<SectionCard
|
||||||
|
title={t("hosts.connectionToolbar")}
|
||||||
|
icon={<LayoutGrid className="size-3.5" />}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-4 py-3">
|
||||||
|
<SettingRow
|
||||||
|
label={t("hosts.showConnectionToolbar")}
|
||||||
|
description={t("hosts.showConnectionToolbarDesc")}
|
||||||
|
>
|
||||||
|
<FakeSwitch
|
||||||
|
checked={form.enableTerminalToolbar}
|
||||||
|
onChange={(value) => setField("enableTerminalToolbar", value)}
|
||||||
|
/>
|
||||||
|
</SettingRow>
|
||||||
|
</div>
|
||||||
|
</SectionCard>
|
||||||
|
|
||||||
<SectionCard
|
<SectionCard
|
||||||
title={t("hosts.connectionDetails")}
|
title={t("hosts.connectionDetails")}
|
||||||
icon={<Globe className="size-3.5" />}
|
icon={<Globe className="size-3.5" />}
|
||||||
|
|||||||
@@ -78,4 +78,32 @@ describe("GuacamoleToolbar Windows key", () => {
|
|||||||
expect(zoomIn).toHaveBeenCalledOnce();
|
expect(zoomIn).toHaveBeenCalledOnce();
|
||||||
expect(resetZoom).toHaveBeenCalledOnce();
|
expect(resetZoom).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("offers an explicit session-only hide action", () => {
|
||||||
|
const onHide = vi.fn();
|
||||||
|
const displayRef = {
|
||||||
|
current: {
|
||||||
|
disconnect: vi.fn(),
|
||||||
|
isConnected: () => true,
|
||||||
|
sendKey: vi.fn(),
|
||||||
|
sendMouse: vi.fn(),
|
||||||
|
setClipboard: vi.fn(),
|
||||||
|
getFilesystem: () => null,
|
||||||
|
zoomIn: vi.fn(() => 1.25),
|
||||||
|
zoomOut: vi.fn(() => 0.75),
|
||||||
|
resetZoom: vi.fn(() => 1),
|
||||||
|
} satisfies GuacamoleDisplayHandle,
|
||||||
|
} as React.RefObject<GuacamoleDisplayHandle>;
|
||||||
|
|
||||||
|
const { getByLabelText } = render(
|
||||||
|
<GuacamoleToolbar
|
||||||
|
displayRef={displayRef}
|
||||||
|
protocol="rdp"
|
||||||
|
onHide={onHide}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(getByLabelText("guacamole.toolbar.hide"));
|
||||||
|
expect(onHide).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user