Skip to content

Commit 4f73613

Browse files
committed
consoles: Redesign and reimplement
- A ToggleGroup in the Card header is used to switch consoles - The console switcher is also there for shut-off machines. This gives us a place type-specific actions that also make sense for a shut-off machine, like editing VNC server settings. - The DesktopViewer is gone, but there is a footer with a "Launch viewer" button and a "How to connect" popover. - Actions for the Graphics console are collected into a kebab menu, with a separate one for "Send keys". - The expanded console has less UI around it, and it keeps the type that was active in the collapsed view. - When there is no actual console for a given type, there is now a EmptyState component where you can activate it. (VNC and serial) - It is possible to change VNC server settings via the "How to connect" popup. - The SPICE console invites you to replace it with VNC.
1 parent 5f15cbb commit 4f73613

13 files changed

+1328
-234
lines changed

src/components/common/needsShutdown.jsx

+49
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,47 @@ export function needsShutdownSpice(vm) {
8585
return vm.hasSpice !== vm.inactiveXML.hasSpice;
8686
}
8787

88+
export function needsShutdownVnc(vm) {
89+
function find_vnc(v) {
90+
return v.displays && v.displays.find(d => d.type == "vnc");
91+
}
92+
93+
const active_vnc = find_vnc(vm);
94+
const inactive_vnc = find_vnc(vm.inactiveXML);
95+
96+
if (inactive_vnc) {
97+
if (!active_vnc)
98+
return true;
99+
100+
// The active_vnc.port value is the actual port allocated at
101+
// machine start, it is never -1. Thus, we can't just compare
102+
// inactive_vnc.port with active_vnc.port here when
103+
// inactive_vnc.port is -1. Also, when inactive_vnc.port _is_
104+
// -1, we can't tell whether active_vnc.port has been
105+
// allocated based on some old fixed port in inactive_vnc.port
106+
// (in which case we might want to shutdown and restart), or
107+
// whether it was allocated dynamically (in which case we
108+
// don't want to). But luckily that doesn't really matter and
109+
// a shutdown would not have any useful effect anyway, so we
110+
// don't have to worry that we are missing a notification for
111+
// a pending shutdown.
112+
//
113+
if (inactive_vnc.port != -1 && active_vnc.port != inactive_vnc.port)
114+
return true;
115+
116+
if (active_vnc.password != inactive_vnc.password)
117+
return true;
118+
}
119+
120+
return false;
121+
}
122+
123+
export function needsShutdownSerialConsole(vm) {
124+
const serials = vm.displays && vm.displays.filter(display => display.type == 'pty');
125+
const inactive_serials = vm.inactiveXML.displays && vm.inactiveXML.displays.filter(display => display.type == 'pty');
126+
return serials.length != inactive_serials.length;
127+
}
128+
88129
export function getDevicesRequiringShutdown(vm) {
89130
if (!vm.persistent)
90131
return [];
@@ -125,6 +166,14 @@ export function getDevicesRequiringShutdown(vm) {
125166
if (needsShutdownSpice(vm))
126167
devices.push(_("SPICE"));
127168

169+
// VNC
170+
if (needsShutdownVnc(vm))
171+
devices.push(_("VNC"));
172+
173+
// Serial console
174+
if (needsShutdownSerialConsole(vm))
175+
devices.push(_("Text console"));
176+
128177
// TPM
129178
if (needsShutdownTpm(vm))
130179
devices.push(_("TPM"));

src/components/vm/consoles/common.jsx

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* This file is part of Cockpit.
3+
*
4+
* Copyright (C) 2025 Red Hat, Inc.
5+
*
6+
* Cockpit is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU Lesser General Public License as published by
8+
* the Free Software Foundation; either version 2.1 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Cockpit is distributed in the hope that it will be useful, but
12+
* WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
import React from 'react';
21+
import cockpit from 'cockpit';
22+
23+
import { Text, TextContent, TextVariants, TextList, TextListItem, TextListItemVariants, TextListVariants } from "@patternfly/react-core/dist/esm/components/Text";
24+
import { ClipboardCopy } from "@patternfly/react-core/dist/esm/components/ClipboardCopy/index.js";
25+
import { fmt_to_fragments } from 'utils.jsx';
26+
27+
const _ = cockpit.gettext;
28+
29+
export const RemoteConnectionInfo = ({ url, editor }) => {
30+
function TLI(term, description) {
31+
// What is this? Java?
32+
return (
33+
<>
34+
<TextListItem component={TextListItemVariants.dt}>{term}</TextListItem>
35+
<TextListItem component={TextListItemVariants.dd}>{description}</TextListItem>
36+
</>
37+
);
38+
}
39+
40+
return (
41+
<TextContent>
42+
<Text component={TextVariants.p}>
43+
{fmt_to_fragments(_('Clicking "Launch viewer" will download a $0 file and launch the Remote Viewer application on your system.'), <code>.vv</code>)}
44+
</Text>
45+
<Text component={TextVariants.p}>
46+
{_('Remote Viewer is available for most operating systems. To install it, search for "Remote Viewer" in GNOME Software, KDE Discover, or run the following:')}
47+
</Text>
48+
<TextList component={TextListVariants.dl}>
49+
{TLI(_("RHEL, CentOS"), <code>sudo yum install virt-viewer</code>)}
50+
{TLI(_("Fedora"), <code>sudo dnf install virt-viewer</code>)}
51+
{TLI(_("Ubuntu, Debian"), <code>sudo apt-get install virt-viewer</code>)}
52+
{TLI(_("Windows"),
53+
fmt_to_fragments(
54+
_("Download the MSI from $0"),
55+
<a href="https://virt-manager.org/download" target="_blank" rel="noopener noreferrer">
56+
virt-manager.org
57+
</a>))}
58+
</TextList>
59+
{ url &&
60+
<>
61+
<Text component={TextVariants.p}>
62+
{_('Other remote viewer applications can connect to the following address:')}
63+
</Text>
64+
<Text component={TextVariants.p}>
65+
<ClipboardCopy
66+
hoverTip={_("Copy to clipboard")}
67+
clickTip={_("Successfully copied to clipboard!")}
68+
variant="inline-compact"
69+
isCode>
70+
{url}
71+
</ClipboardCopy>
72+
</Text>
73+
</>
74+
}
75+
{ editor }
76+
</TextContent>
77+
);
78+
};

src/components/vm/consoles/consoles.css

+14-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,25 @@
1818
grid-template-rows: min-content 1fr;
1919
}
2020

21+
.vm-console-footer {
22+
grid-area: 3 / 1 / 4 / 3;
23+
}
24+
2125
.consoles-page-expanded .actions-pagesection .pf-v5-c-page__main-body {
2226
padding-block-end: 0;
2327
}
2428

25-
/* Hide send key button - there is not way to do that from the JS
29+
/* Hide standard VNC actions - there is not way to do that from the JS
2630
* https://github.com/patternfly/patternfly-react/issues/3689
2731
*/
28-
#pf-v5-c-console__send-shortcut {
32+
.pf-v5-c-console__actions-vnc {
2933
display: none;
3034
}
35+
36+
.consoles-card .pf-v5-c-empty-state__icon {
37+
color: var(--pf-v5-global--custom-color--200);
38+
}
39+
40+
.ct-remote-viewer-popover {
41+
max-inline-size: 60ch;
42+
}

0 commit comments

Comments
 (0)